{"diffoscope-json-version": 1, "source1": "/srv/reproducible-results/rbuild-debian/r-b-build.HOGf5I07/b1/openlayers_2.13.1+ds2-8_i386.changes", "source2": "/srv/reproducible-results/rbuild-debian/r-b-build.HOGf5I07/b2/openlayers_2.13.1+ds2-8_i386.changes", "unified_diff": null, "details": [{"source1": "Files", "source2": "Files", "unified_diff": "@@ -1,2 +1,2 @@\n \n- e21444c04e2734caefa85c1971ba09a1 722640 javascript optional libjs-openlayers_2.13.1+ds2-8_all.deb\n+ 53222143dab782bac4f67f3b67544ea3 716516 javascript optional libjs-openlayers_2.13.1+ds2-8_all.deb\n"}, {"source1": "libjs-openlayers_2.13.1+ds2-8_all.deb", "source2": "libjs-openlayers_2.13.1+ds2-8_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 2020-04-18 18:00:46.000000 debian-binary\n -rw-r--r-- 0 0 0 3684 2020-04-18 18:00:46.000000 control.tar.xz\n--rw-r--r-- 0 0 0 718764 2020-04-18 18:00:46.000000 data.tar.xz\n+-rw-r--r-- 0 0 0 712640 2020-04-18 18:00:46.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": "@@ -52,37 +52,14 @@\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \n * POSSIBILITY OF SUCH DAMAGE.\n */\n /* ======================================================================\n- Rico/license.js\n- ====================================================================== */\n-\n-/**\n- * @license Apache 2 \n- * \n- * Contains portions of Rico \n- * \n- * Copyright 2005 Sabre Airline Solutions \n- *\n- * Licensed under the Apache License, Version 2.0 (the \"License\"); you\n- * may not use this file except in compliance with the License. You\n- * 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\n- * implied. See the License for the specific language governing\n- * permissions and limitations under the License. \n- */\n-/* ======================================================================\n OpenLayers/SingleFile.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -286,268 +263,14 @@\n source.hasOwnProperty && source.hasOwnProperty(\"toString\")) {\n destination.toString = source.toString;\n }\n }\n return destination;\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/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@@ -1866,14 +1589,207 @@\n \n opp += (quadrant.charAt(0) == 't') ? 'b' : 't';\n opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';\n \n return opp;\n };\n /* ======================================================================\n+ OpenLayers/BaseTypes/Element.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/BaseTypes.js\n+ */\n+\n+/**\n+ * Namespace: OpenLayers.Element\n+ */\n+OpenLayers.Element = {\n+\n+ /**\n+ * APIFunction: visible\n+ * \n+ * Parameters: \n+ * element - {DOMElement}\n+ * \n+ * Returns:\n+ * {Boolean} Is the element visible?\n+ */\n+ visible: function(element) {\n+ return OpenLayers.Util.getElement(element).style.display != 'none';\n+ },\n+\n+ /**\n+ * APIFunction: toggle\n+ * Toggle the visibility of element(s) passed in\n+ * \n+ * Parameters:\n+ * element - {DOMElement} Actually user can pass any number of elements\n+ */\n+ toggle: function() {\n+ for (var i = 0, len = arguments.length; i < len; i++) {\n+ var element = OpenLayers.Util.getElement(arguments[i]);\n+ var display = OpenLayers.Element.visible(element) ? 'none' :\n+ '';\n+ element.style.display = display;\n+ }\n+ },\n+\n+ /**\n+ * APIFunction: remove\n+ * Remove the specified element from the DOM.\n+ * \n+ * Parameters:\n+ * element - {DOMElement}\n+ */\n+ remove: function(element) {\n+ element = OpenLayers.Util.getElement(element);\n+ element.parentNode.removeChild(element);\n+ },\n+\n+ /**\n+ * APIFunction: getHeight\n+ * \n+ * Parameters:\n+ * element - {DOMElement}\n+ * \n+ * Returns:\n+ * {Integer} The offset height of the element passed in\n+ */\n+ getHeight: function(element) {\n+ element = OpenLayers.Util.getElement(element);\n+ return element.offsetHeight;\n+ },\n+\n+ /**\n+ * Function: hasClass\n+ * Tests if an element has the given CSS class name.\n+ *\n+ * Parameters:\n+ * element - {DOMElement} A DOM element node.\n+ * name - {String} The CSS class name to search for.\n+ *\n+ * Returns:\n+ * {Boolean} The element has the given class name.\n+ */\n+ hasClass: function(element, name) {\n+ var names = element.className;\n+ return (!!names && new RegExp(\"(^|\\\\s)\" + name + \"(\\\\s|$)\").test(names));\n+ },\n+\n+ /**\n+ * Function: addClass\n+ * Add a CSS class name to an element. Safe where element already has\n+ * the class name.\n+ *\n+ * Parameters:\n+ * element - {DOMElement} A DOM element node.\n+ * name - {String} The CSS class name to add.\n+ *\n+ * Returns:\n+ * {DOMElement} The element.\n+ */\n+ addClass: function(element, name) {\n+ if (!OpenLayers.Element.hasClass(element, name)) {\n+ element.className += (element.className ? \" \" : \"\") + name;\n+ }\n+ return element;\n+ },\n+\n+ /**\n+ * Function: removeClass\n+ * Remove a CSS class name from an element. Safe where element does not\n+ * have the class name.\n+ *\n+ * Parameters:\n+ * element - {DOMElement} A DOM element node.\n+ * name - {String} The CSS class name to remove.\n+ *\n+ * Returns:\n+ * {DOMElement} The element.\n+ */\n+ removeClass: function(element, name) {\n+ var names = element.className;\n+ if (names) {\n+ element.className = OpenLayers.String.trim(\n+ names.replace(\n+ new RegExp(\"(^|\\\\s+)\" + name + \"(\\\\s+|$)\"), \" \"\n+ )\n+ );\n+ }\n+ return element;\n+ },\n+\n+ /**\n+ * Function: toggleClass\n+ * Remove a CSS class name from an element if it exists. Add the class name\n+ * if it doesn't exist.\n+ *\n+ * Parameters:\n+ * element - {DOMElement} A DOM element node.\n+ * name - {String} The CSS class name to toggle.\n+ *\n+ * Returns:\n+ * {DOMElement} The element.\n+ */\n+ toggleClass: function(element, name) {\n+ if (OpenLayers.Element.hasClass(element, name)) {\n+ OpenLayers.Element.removeClass(element, name);\n+ } else {\n+ OpenLayers.Element.addClass(element, name);\n+ }\n+ return element;\n+ },\n+\n+ /**\n+ * APIFunction: getStyle\n+ * \n+ * Parameters:\n+ * element - {DOMElement}\n+ * style - {?}\n+ * \n+ * Returns:\n+ * {?}\n+ */\n+ getStyle: function(element, style) {\n+ element = OpenLayers.Util.getElement(element);\n+\n+ var value = null;\n+ if (element && element.style) {\n+ value = element.style[OpenLayers.String.camelize(style)];\n+ if (!value) {\n+ if (document.defaultView &&\n+ document.defaultView.getComputedStyle) {\n+\n+ var css = document.defaultView.getComputedStyle(element, null);\n+ value = css ? css.getPropertyValue(style) : null;\n+ } else if (element.currentStyle) {\n+ value = element.currentStyle[OpenLayers.String.camelize(style)];\n+ }\n+ }\n+\n+ var positions = ['left', 'top', 'right', 'bottom'];\n+ if (window.opera &&\n+ (OpenLayers.Util.indexOf(positions, style) != -1) &&\n+ (OpenLayers.Element.getStyle(element, 'position') == 'static')) {\n+ value = 'auto';\n+ }\n+ }\n+\n+ return value == 'auto' ? null : value;\n+ }\n+\n+};\n+/* ======================================================================\n OpenLayers/BaseTypes/LonLat.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -2327,14 +2243,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@@ -4256,4012 +4426,8469 @@\n } else {\n str += coordinate < 0 ? OpenLayers.i18n(\"S\") : OpenLayers.i18n(\"N\");\n }\n return str;\n };\n \n /* ======================================================================\n- OpenLayers/BaseTypes/Element.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- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/BaseTypes.js\n+ * @requires OpenLayers/SingleFile.js\n */\n \n+OpenLayers.Util = OpenLayers.Util || {};\n /**\n- * Namespace: OpenLayers.Element\n+ * Namespace: OpenLayers.Util.vendorPrefix\n+ * A collection of utility functions to detect vendor prefixed features\n */\n-OpenLayers.Element = {\n-\n- /**\n- * APIFunction: visible\n- * \n- * Parameters: \n- * element - {DOMElement}\n- * \n- * Returns:\n- * {Boolean} Is the element visible?\n- */\n- visible: function(element) {\n- return OpenLayers.Util.getElement(element).style.display != 'none';\n- },\n-\n- /**\n- * APIFunction: toggle\n- * Toggle the visibility of element(s) passed in\n- * \n- * Parameters:\n- * element - {DOMElement} Actually user can pass any number of elements\n- */\n- toggle: function() {\n- for (var i = 0, len = arguments.length; i < len; i++) {\n- var element = OpenLayers.Util.getElement(arguments[i]);\n- var display = OpenLayers.Element.visible(element) ? 'none' :\n- '';\n- element.style.display = display;\n- }\n- },\n+OpenLayers.Util.vendorPrefix = (function() {\n+ \"use strict\";\n \n- /**\n- * APIFunction: remove\n- * Remove the specified element from the DOM.\n- * \n- * Parameters:\n- * element - {DOMElement}\n- */\n- remove: function(element) {\n- element = OpenLayers.Util.getElement(element);\n- element.parentNode.removeChild(element);\n- },\n+ var VENDOR_PREFIXES = [\"\", \"O\", \"ms\", \"Moz\", \"Webkit\"],\n+ divStyle = document.createElement(\"div\").style,\n+ cssCache = {},\n+ jsCache = {};\n \n- /**\n- * APIFunction: getHeight\n- * \n- * Parameters:\n- * element - {DOMElement}\n- * \n- * Returns:\n- * {Integer} The offset height of the element passed in\n- */\n- getHeight: function(element) {\n- element = OpenLayers.Util.getElement(element);\n- return element.offsetHeight;\n- },\n \n /**\n- * Function: hasClass\n- * Tests if an element has the given CSS class name.\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- * element - {DOMElement} A DOM element node.\n- * name - {String} The CSS class name to search for.\n+ * prefixedDom - {String} The property to convert\n *\n * Returns:\n- * {Boolean} The element has the given class name.\n+ * {String} The CSS property\n */\n- hasClass: function(element, name) {\n- var names = element.className;\n- return (!!names && new RegExp(\"(^|\\\\s)\" + name + \"(\\\\s|$)\").test(names));\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- * Function: addClass\n- * Add a CSS class name to an element. Safe where element already has\n- * the class name.\n+ * APIMethod: css\n+ * Detect which property is used for a CSS property\n *\n * Parameters:\n- * element - {DOMElement} A DOM element node.\n- * name - {String} The CSS class name to add.\n+ * property - {String} The standard (unprefixed) CSS property name\n *\n * Returns:\n- * {DOMElement} The element.\n+ * {String} The standard CSS property, prefixed property or null if not\n+ * supported\n */\n- addClass: function(element, name) {\n- if (!OpenLayers.Element.hasClass(element, name)) {\n- element.className += (element.className ? \" \" : \"\") + name;\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 element;\n- },\n+ return cssCache[property];\n+ }\n \n /**\n- * Function: removeClass\n- * Remove a CSS class name from an element. Safe where element does not\n- * have the class name.\n+ * APIMethod: js\n+ * Detect which property is used for a JS property/method\n *\n * Parameters:\n- * element - {DOMElement} A DOM element node.\n- * name - {String} The CSS class name to remove.\n+ * obj - {Object} The object to test on\n+ * property - {String} The standard (unprefixed) JS property name\n *\n * Returns:\n- * {DOMElement} The element.\n+ * {String} The standard JS property, prefixed property or null if not\n+ * supported\n */\n- removeClass: function(element, name) {\n- var names = element.className;\n- if (names) {\n- element.className = OpenLayers.String.trim(\n- names.replace(\n- new RegExp(\"(^|\\\\s+)\" + name + \"(\\\\s+|$)\"), \" \"\n- )\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 element;\n- },\n+ return jsCache[property];\n+ }\n \n /**\n- * Function: toggleClass\n- * Remove a CSS class name from an element if it exists. Add the class name\n- * if it doesn't exist.\n+ * APIMethod: style\n+ * Detect which property is used for a DOM style property\n *\n * Parameters:\n- * element - {DOMElement} A DOM element node.\n- * name - {String} The CSS class name to toggle.\n+ * property - {String} The standard (unprefixed) style property name\n *\n * Returns:\n- * {DOMElement} The element.\n- */\n- toggleClass: function(element, name) {\n- if (OpenLayers.Element.hasClass(element, name)) {\n- OpenLayers.Element.removeClass(element, name);\n- } else {\n- OpenLayers.Element.addClass(element, name);\n- }\n- return element;\n- },\n-\n- /**\n- * APIFunction: getStyle\n- * \n- * Parameters:\n- * element - {DOMElement}\n- * style - {?}\n- * \n- * Returns:\n- * {?}\n+ * {String} The standard style property, prefixed property or null if not\n+ * supported\n */\n- getStyle: function(element, style) {\n- element = OpenLayers.Util.getElement(element);\n-\n- var value = null;\n- if (element && element.style) {\n- value = element.style[OpenLayers.String.camelize(style)];\n- if (!value) {\n- if (document.defaultView &&\n- document.defaultView.getComputedStyle) {\n-\n- var css = document.defaultView.getComputedStyle(element, null);\n- value = css ? css.getPropertyValue(style) : null;\n- } else if (element.currentStyle) {\n- value = element.currentStyle[OpenLayers.String.camelize(style)];\n- }\n- }\n-\n- var positions = ['left', 'top', 'right', 'bottom'];\n- if (window.opera &&\n- (OpenLayers.Util.indexOf(positions, style) != -1) &&\n- (OpenLayers.Element.getStyle(element, 'position') == 'static')) {\n- value = 'auto';\n- }\n- }\n-\n- return value == 'auto' ? null : value;\n+ function style(property) {\n+ return js(divStyle, property);\n }\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- Rico/Color.js\n+ OpenLayers/Events.js\n ====================================================================== */\n \n-/** \n- * @requires Rico/license.js\n- * @requires OpenLayers/Console.js\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/BaseTypes/Element.js\n- */\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-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- * This file has been edited substantially from the Rico-released version by\n- * the OpenLayers development team.\n+/**\n+ * @requires OpenLayers/Util.js\n */\n \n-OpenLayers.Console.warn(\"OpenLayers.Rico is deprecated\");\n-\n-OpenLayers.Rico = OpenLayers.Rico || {};\n-OpenLayers.Rico.Color = OpenLayers.Class({\n-\n- initialize: function(red, green, blue) {\n- this.rgb = {\n- r: red,\n- g: green,\n- b: blue\n- };\n- },\n-\n- setRed: function(r) {\n- this.rgb.r = r;\n- },\n+/**\n+ * Namespace: OpenLayers.Event\n+ * Utility functions for event handling.\n+ */\n+OpenLayers.Event = {\n \n- setGreen: function(g) {\n- this.rgb.g = g;\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- setBlue: function(b) {\n- this.rgb.b = b;\n- },\n+ /**\n+ * Constant: KEY_SPACE\n+ * {int}\n+ */\n+ KEY_SPACE: 32,\n \n- setHue: function(h) {\n+ /** \n+ * Constant: KEY_BACKSPACE \n+ * {int} \n+ */\n+ KEY_BACKSPACE: 8,\n \n- // get an HSB model, and set the new hue...\n- var hsb = this.asHSB();\n- hsb.h = h;\n+ /** \n+ * Constant: KEY_TAB \n+ * {int} \n+ */\n+ KEY_TAB: 9,\n \n- // convert back to RGB...\n- this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);\n- },\n+ /** \n+ * Constant: KEY_RETURN \n+ * {int} \n+ */\n+ KEY_RETURN: 13,\n \n- setSaturation: function(s) {\n- // get an HSB model, and set the new hue...\n- var hsb = this.asHSB();\n- hsb.s = s;\n+ /** \n+ * Constant: KEY_ESC \n+ * {int} \n+ */\n+ KEY_ESC: 27,\n \n- // convert back to RGB and set values...\n- this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);\n- },\n+ /** \n+ * Constant: KEY_LEFT \n+ * {int} \n+ */\n+ KEY_LEFT: 37,\n \n- setBrightness: function(b) {\n- // get an HSB model, and set the new hue...\n- var hsb = this.asHSB();\n- hsb.b = b;\n+ /** \n+ * Constant: KEY_UP \n+ * {int} \n+ */\n+ KEY_UP: 38,\n \n- // convert back to RGB and set values...\n- this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);\n- },\n+ /** \n+ * Constant: KEY_RIGHT \n+ * {int} \n+ */\n+ KEY_RIGHT: 39,\n \n- darken: function(percent) {\n- var hsb = this.asHSB();\n- this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent, 0));\n- },\n+ /** \n+ * Constant: KEY_DOWN \n+ * {int} \n+ */\n+ KEY_DOWN: 40,\n \n- brighten: function(percent) {\n- var hsb = this.asHSB();\n- this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent, 1));\n- },\n+ /** \n+ * Constant: KEY_DELETE \n+ * {int} \n+ */\n+ KEY_DELETE: 46,\n \n- blend: function(other) {\n- this.rgb.r = Math.floor((this.rgb.r + other.rgb.r) / 2);\n- this.rgb.g = Math.floor((this.rgb.g + other.rgb.g) / 2);\n- this.rgb.b = Math.floor((this.rgb.b + other.rgb.b) / 2);\n- },\n \n- isBright: function() {\n- var hsb = this.asHSB();\n- return this.asHSB().b > 0.5;\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- isDark: function() {\n- return !this.isBright();\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- asRGB: function() {\n- return \"rgb(\" + this.rgb.r + \",\" + this.rgb.g + \",\" + this.rgb.b + \")\";\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- asHex: function() {\n- return \"#\" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();\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- asHSB: function() {\n- return OpenLayers.Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);\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- toString: function() {\n- return this.asHex();\n- }\n-\n-});\n-\n-OpenLayers.Rico.Color.createFromHex = function(hexCode) {\n- if (hexCode.length == 4) {\n- var shortHexCode = hexCode;\n- var hexCode = '#';\n- for (var i = 1; i < 4; i++) {\n- hexCode += (shortHexCode.charAt(i) +\n- shortHexCode.charAt(i));\n- }\n- }\n- if (hexCode.indexOf('#') == 0) {\n- hexCode = hexCode.substring(1);\n- }\n- var red = hexCode.substring(0, 2);\n- var green = hexCode.substring(2, 4);\n- var blue = hexCode.substring(4, 6);\n- return new OpenLayers.Rico.Color(parseInt(red, 16), parseInt(green, 16), parseInt(blue, 16));\n-};\n-\n-/**\n- * Factory method for creating a color from the background of\n- * an HTML element.\n- */\n-OpenLayers.Rico.Color.createColorFromBackground = function(elem) {\n-\n- var actualColor =\n- OpenLayers.Element.getStyle(OpenLayers.Util.getElement(elem),\n- \"backgroundColor\");\n-\n- if (actualColor == \"transparent\" && elem.parentNode) {\n- return OpenLayers.Rico.Color.createColorFromBackground(elem.parentNode);\n- }\n- if (actualColor == null) {\n- return new OpenLayers.Rico.Color(255, 255, 255);\n- }\n- if (actualColor.indexOf(\"rgb(\") == 0) {\n- var colors = actualColor.substring(4, actualColor.length - 1);\n- var colorArray = colors.split(\",\");\n- return new OpenLayers.Rico.Color(parseInt(colorArray[0]),\n- parseInt(colorArray[1]),\n- parseInt(colorArray[2]));\n-\n- } else if (actualColor.indexOf(\"#\") == 0) {\n- return OpenLayers.Rico.Color.createFromHex(actualColor);\n- } else {\n- return new OpenLayers.Rico.Color(255, 255, 255);\n- }\n-};\n-\n-OpenLayers.Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {\n-\n- var red = 0;\n- var green = 0;\n- var blue = 0;\n-\n- if (saturation == 0) {\n- red = parseInt(brightness * 255.0 + 0.5);\n- green = red;\n- blue = red;\n- } else {\n- var h = (hue - Math.floor(hue)) * 6.0;\n- var f = h - Math.floor(h);\n- var p = brightness * (1.0 - saturation);\n- var q = brightness * (1.0 - saturation * f);\n- var t = brightness * (1.0 - (saturation * (1.0 - f)));\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- switch (parseInt(h)) {\n- case 0:\n- red = (brightness * 255.0 + 0.5);\n- green = (t * 255.0 + 0.5);\n- blue = (p * 255.0 + 0.5);\n- break;\n- case 1:\n- red = (q * 255.0 + 0.5);\n- green = (brightness * 255.0 + 0.5);\n- blue = (p * 255.0 + 0.5);\n- break;\n- case 2:\n- red = (p * 255.0 + 0.5);\n- green = (brightness * 255.0 + 0.5);\n- blue = (t * 255.0 + 0.5);\n- break;\n- case 3:\n- red = (p * 255.0 + 0.5);\n- green = (q * 255.0 + 0.5);\n- blue = (brightness * 255.0 + 0.5);\n- break;\n- case 4:\n- red = (t * 255.0 + 0.5);\n- green = (p * 255.0 + 0.5);\n- blue = (brightness * 255.0 + 0.5);\n- break;\n- case 5:\n- red = (brightness * 255.0 + 0.5);\n- green = (p * 255.0 + 0.5);\n- blue = (q * 255.0 + 0.5);\n- break;\n+ if (!allowDefault) {\n+ OpenLayers.Event.preventDefault(event);\n }\n- }\n-\n- return {\n- r: parseInt(red),\n- g: parseInt(green),\n- b: parseInt(blue)\n- };\n-};\n-\n-OpenLayers.Rico.Color.RGBtoHSB = function(r, g, b) {\n-\n- var hue;\n- var saturation;\n- var brightness;\n-\n- var cmax = (r > g) ? r : g;\n- if (b > cmax) {\n- cmax = b;\n- }\n- var cmin = (r < g) ? r : g;\n- if (b < cmin) {\n- cmin = b;\n- }\n- brightness = cmax / 255.0;\n- if (cmax != 0) {\n- saturation = (cmax - cmin) / cmax;\n- } else {\n- saturation = 0;\n- }\n- if (saturation == 0) {\n- hue = 0;\n- } else {\n- var redc = (cmax - r) / (cmax - cmin);\n- var greenc = (cmax - g) / (cmax - cmin);\n- var bluec = (cmax - b) / (cmax - cmin);\n \n- if (r == cmax) {\n- hue = bluec - greenc;\n- } else if (g == cmax) {\n- hue = 2.0 + redc - bluec;\n+ if (event.stopPropagation) {\n+ event.stopPropagation();\n } else {\n- hue = 4.0 + greenc - redc;\n- }\n- hue = hue / 6.0;\n- if (hue < 0) {\n- hue = hue + 1.0;\n- }\n- }\n-\n- return {\n- h: hue,\n- s: saturation,\n- b: brightness\n- };\n-};\n-\n-/* ======================================================================\n- Rico/Corner.js\n- ====================================================================== */\n-\n-/**\n- * @requires OpenLayers/Console.js\n- * @requires Rico/Color.js\n- */\n-\n-\n-/*\n- * This file has been edited substantially from the Rico-released\n- * version by the OpenLayers development team.\n- * \n- * Copyright 2005 Sabre Airline Solutions \n- * \n- * Licensed under the Apache License, Version 2.0 (the \"License\");\n- * you may not use this file except in compliance with the\n- * License. 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\n- * implied. See the License for the specific language governing\n- * permissions * and limitations under the License.\n- *\n- */\n-\n-OpenLayers.Console.warn(\"OpenLayers.Rico is deprecated\");\n-\n-OpenLayers.Rico = OpenLayers.Rico || {};\n-OpenLayers.Rico.Corner = {\n-\n- round: function(e, options) {\n- e = OpenLayers.Util.getElement(e);\n- this._setOptions(options);\n-\n- var color = this.options.color;\n- if (this.options.color == \"fromElement\") {\n- color = this._background(e);\n- }\n- var bgColor = this.options.bgColor;\n- if (this.options.bgColor == \"fromParent\") {\n- bgColor = this._background(e.offsetParent);\n+ event.cancelBubble = true;\n }\n- this._roundCornersImpl(e, color, bgColor);\n },\n \n- /** This is a helper function to change the background\n- * color of
that has had Rico rounded corners added.\n- *\n- * It seems we cannot just set the background color for the\n- * outer
so each element used to create the\n- * corners must have its background color set individually.\n- *\n- * @param {DOM} theDiv - A child of the outer
that was\n- * supplied to the `round` method.\n+ /**\n+ * Method: preventDefault\n+ * Cancels the event if it is cancelable, without stopping further\n+ * propagation of the event.\n *\n- * @param {String} newColor - The new background color to use.\n+ * Parameters:\n+ * event - {Event}\n */\n- changeColor: function(theDiv, newColor) {\n-\n- theDiv.style.backgroundColor = newColor;\n-\n- var spanElements = theDiv.parentNode.getElementsByTagName(\"span\");\n-\n- for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {\n- spanElements[currIdx].style.backgroundColor = newColor;\n+ preventDefault: function(event) {\n+ if (event.preventDefault) {\n+ event.preventDefault();\n+ } else {\n+ event.returnValue = false;\n }\n },\n \n-\n- /** This is a helper function to change the background\n- * opacity of
that has had Rico rounded corners added.\n- *\n- * See changeColor (above) for algorithm explanation\n- *\n- * @param {DOM} theDiv A child of the outer
that was\n- * supplied to the `round` method.\n- *\n- * @param {int} newOpacity The new opacity to use (0-1).\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- changeOpacity: function(theDiv, newOpacity) {\n-\n- var mozillaOpacity = newOpacity;\n- var ieOpacity = 'alpha(opacity=' + newOpacity * 100 + ')';\n-\n- theDiv.style.opacity = mozillaOpacity;\n- theDiv.style.filter = ieOpacity;\n-\n- var spanElements = theDiv.parentNode.getElementsByTagName(\"span\");\n-\n- for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {\n- spanElements[currIdx].style.opacity = mozillaOpacity;\n- spanElements[currIdx].style.filter = ieOpacity;\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-\n+ return element;\n },\n \n- /** this function takes care of redoing the rico cornering\n- * \n- * you can't just call updateRicoCorners() again and pass it a \n- * new options string. you have to first remove the divs that \n- * rico puts on top and below the content div.\n- *\n- * @param {DOM} theDiv - A child of the outer
that was\n- * supplied to the `round` method.\n- *\n- * @param {Object} options - list of options\n+ /** \n+ * Method: observe\n+ * \n+ * Parameters:\n+ * elementParam - {DOMElement || String} \n+ * name - {String} \n+ * observer - {function} \n+ * useCapture - {Boolean} \n */\n- reRound: function(theDiv, options) {\n-\n- var topRico = theDiv.parentNode.childNodes[0];\n- //theDiv would be theDiv.parentNode.childNodes[1]\n- var bottomRico = theDiv.parentNode.childNodes[2];\n-\n- theDiv.parentNode.removeChild(topRico);\n- theDiv.parentNode.removeChild(bottomRico);\n-\n- this.round(theDiv.parentNode, options);\n- },\n+ observe: function(elementParam, name, observer, useCapture) {\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ useCapture = useCapture || false;\n \n- _roundCornersImpl: function(e, color, bgColor) {\n- if (this.options.border) {\n- this._renderBorder(e, bgColor);\n- }\n- if (this._isTopRounded()) {\n- this._roundTopCorners(e, color, bgColor);\n- }\n- if (this._isBottomRounded()) {\n- this._roundBottomCorners(e, color, bgColor);\n+ if (name == 'keypress' &&\n+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n+ element.attachEvent)) {\n+ name = 'keydown';\n }\n- },\n \n- _renderBorder: function(el, bgColor) {\n- var borderValue = \"1px solid \" + this._borderColor(bgColor);\n- var borderL = \"border-left: \" + borderValue;\n- var borderR = \"border-right: \" + borderValue;\n- var style = \"style='\" + borderL + \";\" + borderR + \"'\";\n- el.innerHTML = \"
\" + el.innerHTML + \"
\";\n- },\n-\n- _roundTopCorners: function(el, color, bgColor) {\n- var corner = this._createCorner(bgColor);\n- for (var i = 0; i < this.options.numSlices; i++) {\n- corner.appendChild(this._createCornerSlice(color, bgColor, i, \"top\"));\n+ //if observers cache has not yet been created, create it\n+ if (!this.observers) {\n+ this.observers = {};\n }\n- el.style.paddingTop = 0;\n- el.insertBefore(corner, el.firstChild);\n- },\n \n- _roundBottomCorners: function(el, color, bgColor) {\n- var corner = this._createCorner(bgColor);\n- for (var i = (this.options.numSlices - 1); i >= 0; i--) {\n- corner.appendChild(this._createCornerSlice(color, bgColor, i, \"bottom\"));\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- el.style.paddingBottom = 0;\n- el.appendChild(corner);\n- },\n \n- _createCorner: function(bgColor) {\n- var corner = document.createElement(\"div\");\n- corner.style.backgroundColor = (this._isTransparent() ? \"transparent\" : bgColor);\n- return corner;\n- },\n-\n- _createCornerSlice: function(color, bgColor, n, position) {\n- var slice = document.createElement(\"span\");\n-\n- var inStyle = slice.style;\n- inStyle.backgroundColor = color;\n- inStyle.display = \"block\";\n- inStyle.height = \"1px\";\n- inStyle.overflow = \"hidden\";\n- inStyle.fontSize = \"1px\";\n+ var cacheID = element._eventCacheID;\n \n- var borderColor = this._borderColor(color, bgColor);\n- if (this.options.border && n == 0) {\n- inStyle.borderTopStyle = \"solid\";\n- inStyle.borderTopWidth = \"1px\";\n- inStyle.borderLeftWidth = \"0px\";\n- inStyle.borderRightWidth = \"0px\";\n- inStyle.borderBottomWidth = \"0px\";\n- inStyle.height = \"0px\"; // assumes css compliant box model\n- inStyle.borderColor = borderColor;\n- } else if (borderColor) {\n- inStyle.borderColor = borderColor;\n- inStyle.borderStyle = \"solid\";\n- inStyle.borderWidth = \"0px 1px\";\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- if (!this.options.compact && (n == (this.options.numSlices - 1))) {\n- inStyle.height = \"2px\";\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- this._setMargin(slice, n, position);\n- this._setBorder(slice, n, position);\n- return slice;\n },\n \n- _setOptions: function(options) {\n- this.options = {\n- corners: \"all\",\n- color: \"fromElement\",\n- bgColor: \"fromParent\",\n- blend: true,\n- border: false,\n- compact: false\n- };\n- OpenLayers.Util.extend(this.options, options || {});\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.options.numSlices = this.options.compact ? 2 : 4;\n- if (this._isTransparent()) {\n- this.options.blend = false;\n- }\n+ this._removeElementObservers(OpenLayers.Event.observers[cacheID]);\n },\n \n- _whichSideTop: function() {\n- if (this._hasString(this.options.corners, \"all\", \"top\")) {\n- return \"\";\n- }\n- if (this.options.corners.indexOf(\"tl\") >= 0 && this.options.corners.indexOf(\"tr\") >= 0) {\n- return \"\";\n- }\n- if (this.options.corners.indexOf(\"tl\") >= 0) {\n- return \"left\";\n- } else if (this.options.corners.indexOf(\"tr\") >= 0) {\n- return \"right\";\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- return \"\";\n },\n \n- _whichSideBottom: function() {\n- if (this._hasString(this.options.corners, \"all\", \"bottom\")) {\n- return \"\";\n- }\n- if (this.options.corners.indexOf(\"bl\") >= 0 && this.options.corners.indexOf(\"br\") >= 0) {\n- return \"\";\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- if (this.options.corners.indexOf(\"bl\") >= 0) {\n- return \"left\";\n- } else if (this.options.corners.indexOf(\"br\") >= 0) {\n- return \"right\";\n- }\n- return \"\";\n- },\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ var cacheID = element._eventCacheID;\n \n- _borderColor: function(color, bgColor) {\n- if (color == \"transparent\") {\n- return bgColor;\n- } else if (this.options.border) {\n- return this.options.border;\n- } else if (this.options.blend) {\n- return this._blend(bgColor, color);\n- } else {\n- return \"\";\n+ if (name == 'keypress') {\n+ if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n+ element.detachEvent) {\n+ name = 'keydown';\n+ }\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- _setMargin: function(el, n, corners) {\n- var marginSize = this._marginSize(n);\n- var whichSide = corners == \"top\" ? this._whichSideTop() : this._whichSideBottom();\n-\n- if (whichSide == \"left\") {\n- el.style.marginLeft = marginSize + \"px\";\n- el.style.marginRight = \"0px\";\n- } else if (whichSide == \"right\") {\n- el.style.marginRight = marginSize + \"px\";\n- el.style.marginLeft = \"0px\";\n- } else {\n- el.style.marginLeft = marginSize + \"px\";\n- el.style.marginRight = marginSize + \"px\";\n- }\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- _setBorder: function(el, n, corners) {\n- var borderSize = this._borderSize(n);\n- var whichSide = corners == \"top\" ? this._whichSideTop() : this._whichSideBottom();\n- if (whichSide == \"left\") {\n- el.style.borderLeftWidth = borderSize + \"px\";\n- el.style.borderRightWidth = \"0px\";\n- } else if (whichSide == \"right\") {\n- el.style.borderRightWidth = borderSize + \"px\";\n- el.style.borderLeftWidth = \"0px\";\n- } else {\n- el.style.borderLeftWidth = borderSize + \"px\";\n- el.style.borderRightWidth = borderSize + \"px\";\n- }\n- if (this.options.border != false) {\n- el.style.borderLeftWidth = borderSize + \"px\";\n- el.style.borderRightWidth = borderSize + \"px\";\n- }\n- },\n+ if ((cacheEntry.name == name) &&\n+ (cacheEntry.observer == observer) &&\n+ (cacheEntry.useCapture == useCapture)) {\n \n- _marginSize: function(n) {\n- if (this._isTransparent()) {\n- return 0;\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- var marginSizes = [5, 3, 2, 1];\n- var blendedMarginSizes = [3, 2, 1, 0];\n- var compactMarginSizes = [2, 1];\n- var smBlendedMarginSizes = [1, 0];\n \n- if (this.options.compact && this.options.blend) {\n- return smBlendedMarginSizes[n];\n- } else if (this.options.compact) {\n- return compactMarginSizes[n];\n- } else if (this.options.blend) {\n- return blendedMarginSizes[n];\n- } else {\n- return marginSizes[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- _borderSize: function(n) {\n- var transparentBorderSizes = [5, 3, 2, 1];\n- var blendedBorderSizes = [2, 1, 1, 1];\n- var compactBorderSizes = [1, 0];\n- var actualBorderSizes = [0, 2, 0, 0];\n-\n- if (this.options.compact && (this.options.blend || this._isTransparent())) {\n- return 1;\n- } else if (this.options.compact) {\n- return compactBorderSizes[n];\n- } else if (this.options.blend) {\n- return blendedBorderSizes[n];\n- } else if (this.options.border) {\n- return actualBorderSizes[n];\n- } else if (this._isTransparent()) {\n- return transparentBorderSizes[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- return 0;\n },\n \n- _hasString: function(str) {\n- for (var i = 1; i < arguments.length; i++)\n- if (str.indexOf(arguments[i]) >= 0) {\n- return true;\n- } return false;\n- },\n- _blend: function(c1, c2) {\n- var cc1 = OpenLayers.Rico.Color.createFromHex(c1);\n- cc1.blend(OpenLayers.Rico.Color.createFromHex(c2));\n- return cc1;\n- },\n- _background: function(el) {\n- try {\n- return OpenLayers.Rico.Color.createColorFromBackground(el).asHex();\n- } catch (err) {\n- return \"#ffffff\";\n- }\n- },\n- _isTransparent: function() {\n- return this.options.color == \"transparent\";\n- },\n- _isTopRounded: function() {\n- return this._hasString(this.options.corners, \"all\", \"top\", \"tl\", \"tr\");\n- },\n- _isBottomRounded: function() {\n- return this._hasString(this.options.corners, \"all\", \"bottom\", \"bl\", \"br\");\n- },\n- _hasSingleTextChild: function(el) {\n- return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3;\n- }\n+ CLASS_NAME: \"OpenLayers.Event\"\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+/* prevent memory leaks in IE */\n+OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);\n \n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n+ * Class: OpenLayers.Events\n */\n+OpenLayers.Events = OpenLayers.Class({\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 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 method, you must\n- * explicitly include the OpenLayers.Format.WKT in your build.\n- */\n-OpenLayers.Geometry = OpenLayers.Class({\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- * Property: id\n- * {String} A unique identifier for this geometry.\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 namespace using\n+ * , and named after the event they provide.\n+ * The constructor receives the target instance as\n+ * argument. Extensions that need to capture browser events before they\n+ * propagate can register their listeners events using , 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- id: null,\n+ extensions: null,\n \n /**\n- * Property: parent\n- * {}This is set when a Geometry is added as component\n- * of another geometry\n+ * Property: extensionCount\n+ * {Object} Keys are event types (like in ), values are the\n+ * number of extension listeners for each event type.\n */\n- parent: null,\n+ extensionCount: null,\n \n /**\n- * Property: bounds \n- * {} The bounds of this geometry\n+ * Method: clearMouseListener\n+ * A version of that is bound to this instance so that\n+ * it can be used with and\n+ * .\n */\n- bounds: null,\n+ clearMouseListener: null,\n \n /**\n- * Constructor: OpenLayers.Geometry\n- * Creates a geometry object. \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() {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\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- * Method: destroy\n- * Destroy this geometry.\n+ * APIMethod: destroy\n */\n destroy: function() {\n- this.id = null;\n- this.bounds = null;\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: clone\n- * Create a clone of this geometry. Does not set any non-standard\n- * properties of the cloned geometry.\n+ * APIMethod: addEventType\n+ * Deprecated. Any event can be triggered without adding it first.\n * \n- * Returns:\n- * {} An exact clone of this geometry.\n+ * Parameters:\n+ * eventName - {String}\n */\n- clone: function() {\n- return new OpenLayers.Geometry();\n- },\n+ addEventType: function(eventName) {},\n \n /**\n- * Method: setBounds\n- * Set the bounds for this Geometry.\n- * \n+ * Method: attachToElement\n+ *\n * Parameters:\n- * bounds - {} \n+ * element - {HTMLDOMElement} a DOM element to attach browser events to\n */\n- setBounds: function(bounds) {\n- if (bounds) {\n- this.bounds = bounds.clone();\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- * Method: clearBounds\n- * Nullify this components bounds and that of its parent as well.\n+ * APIMethod: on\n+ * Convenience method for registering listeners with a common scope.\n+ * Internally, this method calls 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- clearBounds: function() {\n- this.bounds = null;\n- if (this.parent) {\n- this.parent.clearBounds();\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- * 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+ * 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- * newBounds - {} \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- extendBounds: function(newBounds) {\n- var bounds = this.getBounds();\n- if (!bounds) {\n- this.setBounds(newBounds);\n- } else {\n- this.bounds.extend(newBounds);\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: 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- * {}\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- getBounds: function() {\n- if (this.bounds == null) {\n- this.calculateBounds();\n- }\n- return this.bounds;\n+ registerPriority: function(type, obj, func) {\n+ this.register(type, obj, func, true);\n },\n \n- /** \n- * APIMethod: calculateBounds\n- * Recalculate the bounds for the geometry. \n+ /**\n+ * APIMethod: un\n+ * Convenience method for unregistering listeners with a common scope.\n+ * Internally, this method calls 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- calculateBounds: function() {\n- //\n- // This should be overridden by subclasses.\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: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n+ * APIMethod: unregister\n *\n * Parameters:\n- * 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+ * type - {String} \n+ * obj - {Object} If none specified, defaults to this.object\n+ * func - {Function} \n */\n- distanceTo: function(geometry, options) {},\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- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\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- * 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+ * type - {String} \n */\n- getVertices: function(nodes) {},\n+ remove: function(type) {\n+ if (this.listeners[type] != null) {\n+ this.listeners[type] = [];\n+ }\n+ },\n \n /**\n- * Method: atPoint\n- * Note - This is only an approximation based on the bounds of the \n- * geometry.\n+ * APIMethod: triggerEvent\n+ * Trigger a specified registered event. \n * \n * Parameters:\n- * 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+ * type - {String} \n+ * evt - {Event || Object} will be passed to the listeners.\n+ *\n * Returns:\n- * {Boolean} Whether or not the geometry is at the specified location\n+ * {Boolean} The last listener return. If a listener returns false, the\n+ * chain of listeners will stop getting called.\n */\n- atPoint: function(lonlat, toleranceLon, toleranceLat) {\n- var atPoint = false;\n- var bounds = this.getBounds();\n- if ((bounds != null) && (lonlat != null)) {\n+ triggerEvent: function(type, evt) {\n+ var listeners = this.listeners[type];\n \n- var dX = (toleranceLon != null) ? toleranceLon : 0;\n- var dY = (toleranceLat != null) ? toleranceLat : 0;\n+ // fast path\n+ if (!listeners || listeners.length == 0) {\n+ return undefined;\n+ }\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+ // 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- atPoint = toleranceBounds.containsLonLat(lonlat);\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- return atPoint;\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: 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+ * 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- getLength: function() {\n- //to be overridden by geometries that actually have a length\n- //\n- return 0.0;\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: getArea\n- * Calculate the area of this geometry. This method is defined in subclasses.\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- * {Float} The area of the collection by summing its parts\n+ * {Object} An object with only clientX and clientY properties with the\n+ * calculated values.\n */\n- getArea: function() {\n- //to be overridden by geometries that actually have an area\n- //\n- return 0.0;\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: getCentroid\n- * Calculate the centroid of this geometry. This method is defined in subclasses.\n- *\n- * Returns:\n- * {} The centroid of the collection\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- getCentroid: function() {\n- return null;\n+ clearMouseCache: function() {\n+ this.element.scrolls = null;\n+ this.element.lefttop = null;\n+ this.element.offsets = 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+ * Method: getMousePosition\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ * \n * Returns:\n- * {String} String representation of this geometry.\n+ * {} The current xy coordinate of the mouse, adjusted\n+ * for offsets\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+ 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- 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- * {} 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+ 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- 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 | } 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+ if (!this.element.lefttop) {\n+ this.element.lefttop = [\n+ (document.documentElement.clientLeft || 0),\n+ (document.documentElement.clientTop || 0)\n+ ];\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+ if (!this.element.offsets) {\n+ this.element.offsets = OpenLayers.Util.pagePosition(this.element);\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+ 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- }\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- 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/Feature.js\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-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: 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-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\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-/**\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+ e.touches = touches.slice();\n+ handler(e);\n+ };\n \n- /** \n- * Property: layer \n- * {} \n- */\n- layer: null,\n+ OpenLayers.Event.observe(element, 'MSPointerDown', cb);\n \n- /** \n- * Property: id \n- * {String} \n- */\n- id: null,\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- * Property: lonlat \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- lonlat: null,\n+ addMsTouchListenerMove: function(element, type, handler) {\n+ var touches = this._msTouches;\n+ var cb = function(e) {\n \n- /** \n- * Property: data \n- * {Object} \n- */\n- data: null,\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- /** \n- * Property: marker \n- * {} \n- */\n- marker: null,\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- /**\n- * APIProperty: popupClass\n- * {} The class which will be used to instantiate\n- * a new Popup. Default is .\n- */\n- popupClass: null,\n+ e.touches = touches.slice();\n+ handler(e);\n+ };\n \n- /** \n- * Property: popup \n- * {} \n- */\n- popup: null,\n+ OpenLayers.Event.observe(element, 'MSPointerMove', cb);\n+ },\n \n- /** \n- * Constructor: OpenLayers.Feature\n- * Constructor for features.\n+ /**\n+ * Method: addMsTouchListenerEnd\n *\n * Parameters:\n- * layer - {} \n- * lonlat - {} \n- * data - {Object} \n- * \n- * Returns:\n- * {}\n+ * element - {DOMElement} The DOM element to register the listener on\n+ * type - {String} The event type\n+ * handler - {Function} the handler\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+ addMsTouchListenerEnd: function(element, type, handler) {\n+ var touches = this._msTouches;\n \n- /** \n- * Method: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n+ var cb = function(e) {\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+ 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- // 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+ e.touches = touches.slice();\n+ handler(e);\n+ };\n+\n+ OpenLayers.Event.observe(element, 'MSPointerUp', cb);\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+ CLASS_NAME: \"OpenLayers.Events\"\n+});\n+/* ======================================================================\n+ OpenLayers/Animation.js\n+ ====================================================================== */\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+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD 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- * Method: createMarker\n- * Based on the data associated with the Feature, create and return a marker object.\n- *\n- * Returns: \n- * {} 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+ * Property: isNative\n+ * {Boolean} true if a native requestAnimationFrame function is available\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+ var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n+ var isNative = !!(requestAnimationFrame);\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+ * 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- destroyMarker: function() {\n- this.marker.destroy();\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- * 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+ * Function: start\n+ * Executes a method with 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- * 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- * {} 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 .\n- * \n+ * {Number} Identifier for the animation loop. Used to stop animations with\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+ 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- this.popup.feature = this;\n- }\n- return this.popup;\n- },\n-\n+ };\n+ requestFrame(loops[id], element);\n+ return id;\n+ }\n \n /**\n- * Method: destroyPopup\n- * Destroys the popup created via createPopup.\n+ * Function: stop\n+ * Terminates an animation loop started with .\n *\n- * As with the marker, if user overrides the createPopup() function, s/he \n- * should also be able to override the destruction\n+ * Parameters:\n+ * id - {Number} Identifier returned from .\n */\n- destroyPopup: function() {\n- if (this.popup) {\n- this.popup.feature = null;\n- this.popup.destroy();\n- this.popup = null;\n- }\n- },\n+ function stop(id) {\n+ delete loops[id];\n+ }\n \n- CLASS_NAME: \"OpenLayers.Feature\"\n-});\n+ return {\n+ isNative: isNative,\n+ requestFrame: requestFrame,\n+ start: start,\n+ stop: stop\n+ };\n+\n+})(window);\n /* ======================================================================\n- OpenLayers/Feature/Vector.js\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-// 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+ * @requires OpenLayers/Animation.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- * objects.\n- * \n- * Inherits from:\n- * - \n+ * Namespace: OpenLayers.Tween\n */\n-OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {\n+OpenLayers.Tween = OpenLayers.Class({\n \n- /** \n- * Property: fid \n- * {String} \n+ /**\n+ * APIProperty: easing\n+ * {(Function)} Easing equation used for the animation\n+ * Defaultly set to OpenLayers.Easing.Expo.easeOut\n */\n- fid: null,\n+ easing: null,\n \n- /** \n- * APIProperty: geometry \n- * {} \n+ /**\n+ * APIProperty: begin\n+ * {Object} Values to start the animation with\n */\n- geometry: null,\n+ begin: null,\n \n- /** \n- * APIProperty: attributes \n- * {Object} This object holds arbitrary, serializable properties that\n- * describe the feature.\n+ /**\n+ * APIProperty: finish\n+ * {Object} Values to finish the animation with\n */\n- attributes: null,\n+ finish: null,\n \n /**\n- * Property: bounds\n- * {} The box bounding that feature's geometry, that\n- * property can be set by an object when\n- * deserializing the feature, so in most cases it represents an\n- * information set by the server. \n+ * APIProperty: duration\n+ * {int} duration of the tween (number of steps)\n */\n- bounds: null,\n+ duration: null,\n \n- /** \n- * Property: state \n- * {String} \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- state: null,\n+ callbacks: null,\n \n- /** \n- * APIProperty: style \n- * {Object} \n+ /**\n+ * Property: time\n+ * {int} Step counter\n */\n- style: null,\n+ time: null,\n \n /**\n- * APIProperty: url\n- * {String} If this property is set it will be taken into account by\n- * {} when upadting or deleting the feature.\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- url: null,\n+ minFrameRate: null,\n \n /**\n- * Property: renderIntent\n- * {String} rendering intent currently being used\n+ * Property: startTime\n+ * {Number} The timestamp of the first execution step. Used for skipping\n+ * frames\n */\n- renderIntent: \"default\",\n+ startTime: null,\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 , and written by\n- * , 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 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+ * Property: animationId\n+ * {int} Loop id returned by OpenLayers.Animation.start\n */\n- modified: null,\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.Feature.Vector\n- * Create a vector feature. \n+ * Constructor: OpenLayers.Tween\n+ * Creates a Tween.\n+ *\n+ * Parameters:\n+ * easing - {(Function)} easing function method to use\n+ */\n+ initialize: function(easing) {\n+ this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;\n+ },\n+\n+ /**\n+ * APIMethod: start\n+ * Plays the Tween, and calls the callback method on each step\n * \n * Parameters:\n- * geometry - {} The geometry that this feature\n- * represents.\n- * attributes - {Object} An optional object that will be mapped to the\n- * property. \n- * style - {Object} An optional style object.\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- 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+ 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.style = style ? style : null;\n+ this.animationId = OpenLayers.Animation.start(\n+ OpenLayers.Function.bind(this.play, this)\n+ );\n },\n \n- /** \n- * Method: destroy\n- * nullify references to prevent circular references and memory leaks\n+ /**\n+ * APIMethod: stop\n+ * Stops the Tween, and calls the done callback\n+ * Doesn't do anything if animation is already finished\n */\n- destroy: function() {\n- if (this.layer) {\n- this.layer.removeFeatures(this);\n- this.layer = null;\n+ stop: function() {\n+ if (!this.playing) {\n+ return;\n }\n \n- this.geometry = null;\n- this.modified = null;\n- OpenLayers.Feature.prototype.destroy.apply(this, arguments);\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: clone\n- * Create a clone of this vector feature. Does not set any non-standard\n- * properties.\n- *\n- * Returns:\n- * {} An exact clone of this vector feature.\n+ * Method: play\n+ * Calls the appropriate easing method\n */\n- clone: function() {\n- return new OpenLayers.Feature.Vector(\n- this.geometry ? this.geometry.clone() : null,\n- this.attributes,\n- this.style);\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 },\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+ * Create empty functions for all easing methods.\n+ */\n+ CLASS_NAME: \"OpenLayers.Tween\"\n+});\n+\n+/**\n+ * Namespace: OpenLayers.Easing\n+ * \n+ * Credits:\n+ * Easing Equations by Robert Penner, \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+ * 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- * {Boolean} The feature is currently visible on screen (optionally\n- * based on its bounds if boundsOnly is true).\n+ * {Float}\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+ easeIn: function(t, b, c, d) {\n+ return c * t / d + b;\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+ * 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- * {Boolean} The feature is currently displayed.\n+ * {Float}\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+ easeOut: function(t, b, c, d) {\n+ return c * t / d + b;\n },\n \n /**\n- * Method: createMarker\n- * HACK - we need to decide if all vector features should be able to\n- * create markers\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- * {} For now just returns null\n+ * {Float}\n */\n- createMarker: function() {\n- return null;\n+ easeInOut: function(t, b, c, d) {\n+ return c * t / d + b;\n },\n \n+ CLASS_NAME: \"OpenLayers.Easing.Linear\"\n+};\n+\n+/**\n+ * Namespace: OpenLayers.Easing.Expo\n+ */\n+OpenLayers.Easing.Expo = {\n+\n /**\n- * Method: destroyMarker\n- * HACK - we need to decide if all vector features should be able to\n- * delete markers\n+ * Function: easeIn\n * \n- * If user overrides the createMarker() function, s/he should be able\n- * to also specify an alternative function for destroying it\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- destroyMarker: function() {\n- // pass\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: createPopup\n- * HACK - we need to decide if all vector features should be able to\n- * create popups\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- * {} For now just returns null\n+ * {Float}\n */\n- createPopup: function() {\n- return null;\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- * Method: atPoint\n- * Determins whether the feature intersects with the specified location.\n- * \n- * Parameters: \n- * 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+ * 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- * {Boolean} Whether or not the feature is at the specified location\n+ * {Float}\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+ 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- * Method: destroyPopup\n- * HACK - we need to decide if all vector features should be able to\n- * delete popups\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- destroyPopup: function() {\n- // pass\n+ easeIn: function(t, b, c, d) {\n+ return c * (t /= d) * t + b;\n },\n \n /**\n- * Method: move\n- * Moves the feature and redraws it at its new location\n- *\n+ * Function: easeOut\n+ * \n * Parameters:\n- * location - { or } the\n- * location to which to move the feature.\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- 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+ easeOut: function(t, b, c, d) {\n+ return -c * (t /= d) * (t - 2) + b;\n },\n \n /**\n- * Method: toState\n- * Sets the new state\n- *\n+ * Function: easeInOut\n+ * \n * Parameters:\n- * state - {String} \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- 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+ 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- '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+ CLASS_NAME: \"OpenLayers.Easing.Quad\"\n };\n /* ======================================================================\n- OpenLayers/Format.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 * @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+ * 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 method for details\n+ * on usage.\n+ *\n+ * Additional transforms may be added by using the \n+ * library. If the proj4js library is included, the 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 \n+ * method to register a custom transform method.\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- * {} 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- * {} for more information on\n- * custom transformations.\n- */\n- externalProjection: null,\n+OpenLayers.Projection = OpenLayers.Class({\n \n /**\n- * APIProperty: internalProjection\n- * {} 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- * {} for more information on\n- * custom transformations.\n+ * Property: proj\n+ * {Object} Proj4js.Proj instance.\n */\n- internalProjection: null,\n+ proj: null,\n \n /**\n- * APIProperty: data\n- * {Object} When is true, this is the parsed string sent to\n- * .\n+ * Property: projCode\n+ * {String}\n */\n- data: null,\n+ projCode: null,\n \n /**\n- * APIProperty: keepData\n- * {Object} Maintain a reference () to the most recently read data.\n- * Default is false.\n+ * Property: titleRegEx\n+ * {RegExp} regular expression to strip the title from a proj4js definition\n */\n- keepData: false,\n+ titleRegEx: /\\+title=[^\\+]*/,\n \n /**\n- * Constructor: OpenLayers.Format\n- * Instances of this class are not useful. See one of the subclasses.\n+ * Constructor: OpenLayers.Projection\n+ * This class offers several methods for interacting with a wrapped \n+ * pro4js projection object. \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 , the data property will be\n- * set to the parsed object (e.g. the json or xml object).\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- * An instance of OpenLayers.Format\n+ * {} A projection object.\n */\n- initialize: function(options) {\n+ initialize: function(projCode, options) {\n OpenLayers.Util.extend(this, options);\n- this.options = options;\n+ this.projCode = projCode;\n+ if (typeof Proj4js == \"object\") {\n+ this.proj = new Proj4js.Proj(projCode);\n+ }\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+ * APIMethod: getCode\n+ * Get the string SRS code.\n *\n * Returns:\n- * Depends on the subclass\n+ * {String} The SRS code.\n */\n- read: function(data) {\n- throw new Error('Read not implemented.');\n+ getCode: function() {\n+ return this.proj ? this.proj.srsCode : this.projCode;\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+ * APIMethod: getUnits\n+ * Get the units string for the projection -- returns null if \n+ * proj4js is not available.\n *\n * Returns:\n- * {String} A string representation of the object.\n+ * {String} The units abbreviation.\n */\n- write: function(object) {\n- throw new Error('Write not implemented.');\n+ getUnits: function() {\n+ return this.proj ? this.proj.units : null;\n },\n \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- * @requires OpenLayers/Geometry.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.Point\n- * Point geometry class. \n- * \n- * Inherits from:\n- * - \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+ * Method: toString\n+ * Convert projection to string (getCode wrapper).\n *\n- * Parameters:\n- * x - {float} \n- * y - {float}\n- * \n+ * Returns:\n+ * {String} The projection code.\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+ toString: function() {\n+ return this.getCode();\n },\n \n /**\n- * APIMethod: clone\n- * \n+ * Method: equals\n+ * Test equality of two projection instances. Determines equality based\n+ * soley on the projection code.\n+ *\n * Returns:\n- * {} An exact clone of this OpenLayers.Geometry.Point\n+ * {Boolean} The two projections are equivalent.\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Geometry.Point(this.x, this.y);\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-\n- // catch any randomly tagged-on properties\n- OpenLayers.Util.applyDefaults(obj, this);\n-\n- return obj;\n+ return equals;\n },\n \n- /** \n- * Method: calculateBounds\n- * Create a new Bounds based on the lon/lat\n+ /* Method: destroy\n+ * Destroy projection object.\n */\n- calculateBounds: function() {\n- this.bounds = new OpenLayers.Bounds(this.x, this.y,\n- this.x, this.y);\n+ destroy: function() {\n+ delete this.proj;\n+ delete this.projCode;\n },\n \n- /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n- * Parameters:\n- * 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+ 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 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 - { | 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- 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+ 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- return result;\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 constructor.\n+ * \n+ * On their own maps do not provide much functionality. To extend a map\n+ * it's necessary to add controls () and \n+ * layers () 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+ * {}\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 - {} 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 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+ * 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+ * {} 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 or 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 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 method.\n+ * \n+ * Note:\n+ * If you are calling after map construction, do not use\n+ * auto. Instead, divide your 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+ * {} 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+ * {} 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()} Ordered list of layers in the map\n+ */\n+ layers: null,\n+\n+ /**\n+ * APIProperty: controls\n+ * {Array()} 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+ * - or \n+ * - or \n+ * - \n+ * - \n+ */\n+ controls: null,\n+\n+ /**\n+ * Property: popups\n+ * {Array()} List of popups associated with the map\n+ */\n+ popups: null,\n+\n+ /**\n+ * APIProperty: baseLayer\n+ * {} The currently selected base layer. This determines\n+ * min/max zoom level, projection, etc.\n+ */\n+ baseLayer: null,\n+\n+ /**\n+ * Property: center\n+ * {} 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+ * {} 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 and ).\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 .\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+ * {|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 instead.\n+ * The value for will change calculations for tile URLs.\n+ */\n+ maxExtent: null,\n+\n+ /**\n+ * APIProperty: minExtent\n+ * {|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+ * {|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- * 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+ * APIProperty: displayProjection\n+ * {} 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+ * {|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 {}.\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 . 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+ * {} 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+ * {} 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+ * {} 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- * geom - {} The geometry to test. \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
option is\n+ * provided or if you intend to call the method later.\n+ * options - {Object} Optional object with properties to tag onto the map.\n *\n- * Returns:\n- * {Boolean} The supplied geometry is equivalent to this geometry.\n+ * Valid options (in addition to the listed API properties):\n+ * center - {|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 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+ * 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 - {|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
and 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- 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+ 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- return equals;\n },\n \n- /**\n- * Method: toShortString\n+ /** \n+ * APIMethod: getViewport\n+ * Get the DOMElement representing the view port.\n *\n * Returns:\n- * {String} Shortened String representation of Point object. \n- * (ex. \"5, 42\")\n+ * {DOMElement}\n */\n- toShortString: function() {\n- return (this.x + \", \" + this.y);\n+ getViewport: function() {\n+ return this.viewPortDiv;\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+ * APIMethod: render\n+ * Render the map to a specified container.\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+ * 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- move: function(x, y) {\n- this.x = this.x + x;\n- this.y = this.y + y;\n- this.clearBounds();\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- * APIMethod: rotate\n- * Rotate a point around another.\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- * angle - {Float} Rotation angle in degrees (measured counterclockwise\n- * from the positive x-axis)\n- * origin - {} Center point for the rotation\n+ * options - {Object} Hashtable of options to tag to the map\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+ 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: getCentroid\n+ * APIMethod: getTileSize\n+ * Get the tile size for the map\n *\n * Returns:\n- * {} The centroid of the collection\n+ * {}\n */\n- getCentroid: function() {\n- return new OpenLayers.Geometry.Point(this.x, this.y);\n+ getTileSize: function() {\n+ return this.tileSize;\n },\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+ * APIMethod: getBy\n+ * Get a list of objects given a property and a match item.\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 - {} Point of origin for resizing\n- * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n- * \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- * {} - The current geometry. \n+ * {Array} An array of items where the given property matches the given\n+ * criteria.\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+ 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: intersects\n- * Determine if the input geometry intersects this one.\n+ * APIMethod: getLayersBy\n+ * Get a list of layers with properties matching the given criteria.\n *\n * Parameters:\n- * geometry - {} Any type of geometry.\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- * {Boolean} The input geometry intersects this one.\n+ * {Array()} A list of layers matching the given criteria.\n+ * An empty array is returned if no matches are found.\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+ getLayersBy: function(property, match) {\n+ return this.getBy(\"layers\", property, match);\n },\n \n /**\n- * APIMethod: transform\n- * Translate the x,y properties of the point from source to dest.\n- * \n+ * APIMethod: getLayersByName\n+ * Get a list of layers with names matching the given name.\n+ *\n * Parameters:\n- * source - {} \n- * dest - {}\n- * \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- * {} \n+ * {Array()} A list of layers matching the given name.\n+ * An empty array is returned if no matches are found.\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+ getLayersByName: function(match) {\n+ return this.getLayersBy(\"name\", match);\n },\n \n /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n+ * APIMethod: getLayersByClass\n+ * Get a list of layers of a given class (CLASS_NAME).\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+ * 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} A list of all vertices in the geometry.\n+ * {Array()} A list of layers matching the given class.\n+ * An empty array is returned if no matches are found.\n */\n- getVertices: function(nodes) {\n- return [this];\n+ getLayersByClass: function(match) {\n+ return this.getLayersBy(\"CLASS_NAME\", match);\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 (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 and functions here merely iterate through\n- * the components, summing their respective areas and lengths.\n- *\n- * Create a new instance with the constructor.\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {\n-\n /**\n- * APIProperty: components\n- * {Array()} The component parts of this geometry\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()} A list of controls matching the given\n+ * criteria. An empty array is returned if no matches are found.\n */\n- components: null,\n+ getControlsBy: function(property, match) {\n+ return this.getBy(\"controls\", property, match);\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+ * 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()} A list of controls matching the given class.\n+ * An empty array is returned if no matches are found.\n */\n- componentTypes: null,\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- * Constructor: OpenLayers.Geometry.Collection\n- * Creates a Geometry Collection -- a list of geoms.\n+ * APIMethod: getLayer\n+ * Get a layer based on its id\n *\n- * Parameters: \n- * components - {Array()} Optional array of geometries\n+ * Parameters:\n+ * id - {String} A layer id\n *\n+ * Returns:\n+ * {} The Layer with the corresponding id from the map's \n+ * layer collection, or null if not found.\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+ 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- * APIMethod: destroy\n- * Destroy this geometry.\n+ * Method: setLayerZIndex\n+ * \n+ * Parameters:\n+ * layer - {} \n+ * zIdx - {int} \n */\n- destroy: function() {\n- this.components.length = 0;\n- this.components = null;\n- OpenLayers.Geometry.prototype.destroy.apply(this, arguments);\n+ setLayerZIndex: function(layer, zIdx) {\n+ layer.setZIndex(\n+ this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] +\n+ zIdx * 5);\n },\n \n /**\n- * APIMethod: clone\n- * Clone this geometry.\n- *\n- * Returns:\n- * {} An exact clone of this collection\n+ * Method: resetLayersZIndex\n+ * Reset each layer's z-index based on layer's array index\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+ 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- // 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+ * APIMethod: addLayer\n+ *\n+ * Parameters:\n+ * layer - {} \n+ *\n * Returns:\n- * {String} A string representation of the components of this geometry\n+ * {Boolean} True if the layer has been added to the map.\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+ 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- 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+ 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- // 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+ 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: addComponents\n- * Add components to this geometry.\n+ * APIMethod: addLayers \n *\n * Parameters:\n- * components - {Array()} An array of geometries to add\n+ * layers - {Array()} \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+ addLayers: function(layers) {\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ this.addLayer(layers[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+ * 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- * component - {} 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+ * layer - {} \n+ * setNewBaseLayer - {Boolean} Default is true\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+ 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 (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+ 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- component.parent = this;\n- this.clearBounds();\n- added = true;\n }\n }\n- return added;\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: removeComponents\n- * Remove components from this geometry.\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- * components - {Array()} The components to be removed\n+ * layer - {}\n *\n- * Returns: \n- * {Boolean} A component was removed.\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- removeComponents: function(components) {\n- var removed = false;\n+ getLayerIndex: function(layer) {\n+ return OpenLayers.Util.indexOf(this.layers, layer);\n+ },\n \n- if (!(OpenLayers.Util.isArray(components))) {\n- components = [components];\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 - {} \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- for (var i = components.length - 1; i >= 0; --i) {\n- removed = this.removeComponent(components[i]) || removed;\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- return removed;\n },\n \n- /**\n- * Method: removeComponent\n- * Remove a component from this geometry.\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 - {} \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- * component - {} \n- *\n- * Returns: \n- * {Boolean} The component was removed.\n+ * newBaseLayer - {}\n */\n- removeComponent: function(component) {\n+ setBaseLayer: function(newBaseLayer) {\n \n- OpenLayers.Util.removeItem(this.components, component);\n+ if (newBaseLayer != this.baseLayer) {\n \n- // clearBounds() so that it gets recalculated on the next call\n- // to this.getBounds();\n- this.clearBounds();\n- return true;\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: getLength\n- * Calculate the length of this geometry\n- *\n- * Returns:\n- * {Float} The length of the geometry\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 - {}\n+ * px - {}\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+ addControl: function(control, px) {\n+ this.controls.push(control);\n+ this.addControlToMap(control, px);\n },\n \n /**\n- * APIMethod: getArea\n- * Calculate the area of this geometry. Note how this function is overridden\n- * in .\n- *\n- * Returns:\n- * {Float} The area of the collection by summing its parts\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()}\n+ * pixels - {Array()}\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+ 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- 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+ /**\n+ * Method: addControlToMap\n+ * \n * Parameters:\n- * 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+ * control - {}\n+ * px - {}\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+ 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- return area;\n },\n \n /**\n- * APIMethod: getCentroid\n- *\n- * Compute the centroid for this geometry collection.\n- *\n+ * APIMethod: getControl\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+ * id - {String} ID of the control to return.\n+ * \n * Returns:\n- * {} The centroid of the collection\n+ * {} The control from the map's list of controls \n+ * which has a matching 'id'. If none found, \n+ * returns null.\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+ 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- 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+ * 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 - {} 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- areas.push(area);\n- areaSum += area;\n- minArea = (area < minArea && area > 0) ? area : minArea;\n- centroids.push(centroid);\n+ OpenLayers.Util.removeItem(this.controls, control);\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+\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 - {}\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- 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+ 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- return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum);\n+ /** \n+ * APIMethod: removePopup\n+ * \n+ * Parameters:\n+ * 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: getGeodesicLength\n- * Calculate the approximate length of the geometry were it projected onto\n- * the earth.\n- *\n- * projection - {} The spatial reference system\n- * for the geometry coordinates. If not provided, Geographic/WGS84 is\n- * assumed.\n+ * APIMethod: getSize\n * \n * Returns:\n- * {Float} The appoximate geodesic length of the geometry in meters.\n+ * {} An 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- 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+ getSize: function() {\n+ var size = null;\n+ if (this.size != null) {\n+ size = this.size.clone();\n }\n- return length;\n+ return size;\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+ * 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- move: function(x, y) {\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- this.components[i].move(x, y);\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- * APIMethod: rotate\n- * Rotate a geometry around some origin\n- *\n+ * Method: getCurrentSize\n+ * \n+ * Returns:\n+ * {} A new 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- * angle - {Float} Rotation angle in degrees (measured counterclockwise\n- * from the positive x-axis)\n- * origin - {} Center point for the rotation\n+ * center - {} Default is this.getCenter()\n+ * resolution - {float} Default is this.getResolution() \n+ * \n+ * Returns:\n+ * {} A bounds based on resolution, center, and \n+ * current mapsize.\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+ 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: 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 - {} Point of origin for resizing\n- * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n+ * APIMethod: getCenter\n * \n * Returns:\n- * {} - The current geometry. \n+ * {}\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+ getCenter: function() {\n+ var center = null;\n+ var cachedCenter = this.getCachedCenter();\n+ if (cachedCenter) {\n+ center = cachedCenter.clone();\n }\n- return this;\n+ return center;\n },\n \n /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n- * Parameters:\n- * 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+ * Method: getCachedCenter\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 */\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+ 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 best;\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: equals\n- * Determine whether another geometry is equivalent to this one. Geometries\n- * are considered equivalent if all components have the same coordinates.\n+ * APIMethod: pan\n+ * Allows user to pan by a value of screen pixels\n * \n * Parameters:\n- * geometry - {} The geometry to test. \n- *\n- * Returns:\n- * {Boolean} The supplied geometry is equivalent to this geometry.\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- 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+ 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- 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+ // 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- return equivalent;\n+\n },\n \n- /**\n- * APIMethod: transform\n- * Reproject the components geometry from source to dest.\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- * source - {} \n- * dest - {}\n- * \n- * Returns:\n- * {} \n+ * lonlat - {}\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+ 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- this.bounds = null;\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- return this;\n },\n \n /**\n- * APIMethod: intersects\n- * Determine if the input geometry intersects this one.\n- *\n+ * APIMethod: setCenter\n+ * Set the map center (and optionally, the zoom level).\n+ * \n * Parameters:\n- * geometry - {} Any type of geometry.\n+ * 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- * Returns:\n- * {Boolean} The input geometry intersects this one.\n+ * TBD: reconsider forceZoomChange in 3.0\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+ 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- return intersect;\n },\n \n /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n+ * Method: adjustZoom\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+ * zoom - {Number} The zoom level to adjust\n *\n * Returns:\n- * {Array} A list of all vertices in the geometry.\n+ * {Integer} Adjusted zoom level that shows a map not wider than its\n+ * 's maxExtent.\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+ 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 vertices;\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 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+ * 's maxExtent. This is an Integer value, unless the map is\n+ * configured with set to true.\n+ */\n+ getMinZoom: function() {\n+ return this.adjustZoom(0);\n+ },\n \n- CLASS_NAME: \"OpenLayers.Geometry.Collection\"\n-});\n-/* ======================================================================\n- OpenLayers/Geometry/MultiPoint.js\n- ====================================================================== */\n+ /**\n+ * Method: moveTo\n+ *\n+ * Parameters:\n+ * 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-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under 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.getCachedCenter() && !this.isValidLonLat(lonlat)) {\n+ lonlat = this.maxExtent.getCenterLonLat();\n+ this.center = lonlat.clone();\n+ }\n \n-/**\n- * @requires OpenLayers/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/Point.js\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-/**\n- * Class: OpenLayers.Geometry.MultiPoint\n- * MultiPoint is a collection of Points. Create a new instance with the\n- * constructor.\n- *\n- * Inherits from:\n- * - \n- * - \n- */\n-OpenLayers.Geometry.MultiPoint = OpenLayers.Class(\n- OpenLayers.Geometry.Collection, {\n+ var zoomChanged = forceZoomChange || (\n+ (this.isValidZoomLevel(zoom)) &&\n+ (zoom != this.getZoom()));\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+ var centerChanged = (this.isValidLonLat(lonlat)) &&\n+ (!lonlat.equals(this.center));\n \n- /**\n- * Constructor: OpenLayers.Geometry.MultiPoint\n- * Create a new MultiPoint Geometry\n- *\n- * Parameters:\n- * components - {Array()} \n- *\n- * Returns:\n- * {}\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- /**\n- * APIMethod: addPoint\n- * Wrapper for \n- *\n- * Parameters:\n- * point - {} Point to be added\n- * index - {Integer} Optional index\n- */\n- addPoint: function(point, index) {\n- this.addComponent(point, index);\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- /**\n- * APIMethod: removePoint\n- * Wrapper for \n- *\n- * Parameters:\n- * point - {} Point to be removed\n- */\n- removePoint: function(point) {\n- this.removeComponent(point);\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- CLASS_NAME: \"OpenLayers.Geometry.MultiPoint\"\n- });\n-/* ======================================================================\n- OpenLayers/Geometry/Curve.js\n- ====================================================================== */\n+ if (zoomChanged) {\n+ this.zoom = zoom;\n+ this.resolution = res;\n+ }\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under 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 bounds = this.getExtent();\n \n-/**\n- * @requires OpenLayers/Geometry/MultiPoint.js\n- */\n+ //send the move call to the baselayer and all the overlays \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- * - \n- */\n-OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {\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- /**\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+ 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 - {}\n */\n- componentTypes: [\"OpenLayers.Geometry.Point\"],\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- * Constructor: OpenLayers.Geometry.Curve\n+ * Method: isValidZoomLevel\n * \n * Parameters:\n- * point - {Array()}\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- * APIMethod: getLength\n+ * Method: isValidLonLat\n+ * \n+ * Parameters:\n+ * lonlat - {}\n * \n * Returns:\n- * {Float} The length of the curve\n+ * {Boolean} Whether or not the lonlat passed in is non-null and within\n+ * the maxExtent bounds\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+ 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 length;\n+ return valid;\n },\n \n+ /********************************************************/\n+ /* */\n+ /* Layer Options */\n+ /* */\n+ /* Accessor functions to Layer Options parameters */\n+ /* */\n+ /********************************************************/\n+\n /**\n- * APIMethod: getGeodesicLength\n- * Calculate the approximate length of the geometry were it projected onto\n- * the earth.\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- * projection - {} The spatial reference system\n- * for the geometry coordinates. If not provided, Geographic/WGS84 is\n- * assumed.\n+ * FIXME: In 3.0, we will remove getProjectionObject, and instead\n+ * return a Projection object from this function. \n * \n * Returns:\n- * {Float} The appoximate geodesic length of the geometry in meters.\n+ * {String} The Projection string from the base layer or null. \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+ getProjection: function() {\n+ var projection = this.getProjectionObject();\n+ return projection ? projection.getCode() : null;\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+ * APIMethod: getProjectionObject\n+ * Returns the projection obect from the baselayer.\n+ *\n+ * Returns:\n+ * {} 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- * 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- * - \n- */\n-OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {\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- * Constructor: OpenLayers.Geometry.LineString\n- * Create a new LineString geometry\n+ * APIMethod: getMaxExtent\n *\n * Parameters:\n- * points - {Array()} An array of points used to\n- * generate the linestring\n+ * options - {Object} \n+ * \n+ * Allowed Options:\n+ * restricted - {Boolean} If true, returns restricted extent (if it is \n+ * available.)\n *\n+ * Returns:\n+ * {} 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: 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 - {} The point to be removed\n- *\n- * Returns: \n- * {Boolean} The component was removed.\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- 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+ getNumZoomLevels: function() {\n+ var numZoomLevels = null;\n+ if (this.baseLayer != null) {\n+ numZoomLevels = this.baseLayer.numZoomLevels;\n }\n- return removed;\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: 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 - {}\n- *\n+ * APIMethod: getExtent\n+ * \n * Returns:\n- * {Boolean} The input geometry intersects this geometry.\n+ * {} A Bounds object which represents the lon/lat \n+ * bounds of the current viewPort. \n+ * If no baselayer is set, returns null.\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+ getExtent: function() {\n+ var extent = null;\n+ if (this.baseLayer != null) {\n+ extent = this.baseLayer.getExtent();\n }\n- return intersect;\n+ return extent;\n },\n \n /**\n- * Method: getSortedSegments\n- *\n+ * APIMethod: getResolution\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+ * {Float} The current resolution of the map. \n+ * If no baselayer is set, returns null.\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+ 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- // more efficient to define this somewhere static\n- function byX1(seg1, seg2) {\n- return seg1.x1 - seg2.x1;\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 segments.sort(byX1);\n+ return units;\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+ * APIMethod: getScale\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+ * {Float} The current scale denominator of the map. \n+ * If no baselayer is set, returns null.\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+ 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- if (split) {\n- points.push(vert2.clone());\n- lines.push(new OpenLayers.Geometry.LineString(points));\n+ return scale;\n+ },\n+\n+\n+ /**\n+ * APIMethod: getZoomForExtent\n+ * \n+ * Parameters: \n+ * 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- 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+ 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 result;\n+ return resolution;\n },\n \n /**\n- * Method: split\n- * Use this geometry (the source) to attempt to split a target geometry.\n+ * APIMethod: getZoomForResolution\n * \n * Parameters:\n- * target - {} 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+ * 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- * {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+ * {Integer} A suitable zoom level for the specified resolution.\n+ * If no baselayer is set, returns 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- // 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+ 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+ * without a lonlat argument.\n+ * \n+ * Parameters:\n+ * zoom - {Integer}\n+ */\n+ zoomTo: function(zoom, xy) {\n+ // non-API arguments:\n+ // xy - {} 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 }\n+ } else {\n+ var center = xy ?\n+ map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) :\n+ null;\n+ map.setCenter(center, zoom);\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+\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 - {|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- if (targetSplit || sourceSplit) {\n- if (mutual) {\n- results = [sourceParts, targetParts];\n- } else {\n- results = targetParts;\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- return results;\n+ this.setCenter(center, this.getZoomForExtent(bounds, closest));\n },\n \n- /**\n- * Method: splitWith\n- * Split this geometry (the target) with the given geometry (the source).\n+ /** \n+ * APIMethod: zoomToMaxExtent\n+ * Zoom to the full extent and recenter.\n *\n * Parameters:\n- * 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+ * options - {Object}\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+ * Allowed Options:\n+ * restricted - {Boolean} True to zoom to restricted extent if it is \n+ * set. Defaults to true.\n */\n- splitWith: function(geometry, options) {\n- return geometry.split(this, options);\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- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n- *\n+ * Method: getLonLatFromViewPortPx\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+ * viewPortPx - {|Object} An OpenLayers.Pixel or\n+ * an object with a 'x'\n+ * and 'y' properties.\n+ * \n * Returns:\n- * {Array} A list of all vertices in the geometry.\n+ * {} An OpenLayers.LonLat which is the passed-in view \n+ * port , translated into lon/lat\n+ * by the current base layer.\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+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ if (this.baseLayer != null) {\n+ lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);\n }\n- return vertices;\n+ return lonlat;\n },\n \n /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n+ * APIMethod: getViewPortPxFromLonLat\n+ * \n * Parameters:\n- * 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+ * lonlat - {}\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+ * {} An OpenLayers.Pixel which is the passed-in \n+ * , translated into view port \n+ * pixels by the current base layer.\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+ getViewPortPxFromLonLat: function(lonlat) {\n+ var px = null;\n+ if (this.baseLayer != null) {\n+ px = this.baseLayer.getViewPortPxFromLonLat(lonlat);\n }\n- return best;\n+ return px;\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+ * Method: getZoomTargetCenter\n *\n * Parameters:\n- * tolerance - {number} threshhold for simplification in map units\n+ * xy - {} 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.Geometry.LineString} the simplified LineString\n+ * {} The location of the map center after the\n+ * transformation described by the origin xy and the target resolution.\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+ 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- var compareNumbers = function(a, b) {\n- return (a - b);\n- };\n+ //\n+ // CONVENIENCE TRANSLATION FUNCTIONS FOR API\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+ * APIMethod: getLonLatFromPixel\n+ * \n+ * Parameters:\n+ * px - {|Object} An OpenLayers.Pixel or an object with\n+ * a 'x' and 'y' properties.\n+ *\n+ * Returns:\n+ * {} 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- 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+ * 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 - {} A map location.\n+ * \n+ * Returns: \n+ * {} An OpenLayers.Pixel corresponding to the \n+ * 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- 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+ * Method: getGeodesicPixelSize\n+ * \n+ * Parameters:\n+ * px - {} 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+ * {} 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- /**\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+ return new OpenLayers.Size(\n+ OpenLayers.Util.distVincenty(left, right),\n+ OpenLayers.Util.distVincenty(bottom, top)\n+ );\n+ },\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+ // TRANSLATION: ViewPortPx <-> LayerPx\n+ //\n \n- //Add the first and last index to the keepers\n- pointIndexsToKeep.push(firstPoint);\n- pointIndexsToKeep.push(lastPoint);\n+ /**\n+ * APIMethod: getViewPortPxFromLayerPx\n+ * \n+ * Parameters:\n+ * layerPx - {}\n+ * \n+ * Returns:\n+ * {} 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- //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+ * APIMethod: getLayerPxFromViewPortPx\n+ * \n+ * Parameters:\n+ * viewPortPx - {}\n+ * \n+ * Returns:\n+ * {} 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- 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+ // TRANSLATION: LonLat <-> LayerPx\n+ //\n+\n+ /**\n+ * Method: getLonLatFromLayerPx\n+ * \n+ * Parameters:\n+ * px - {}\n+ *\n+ * Returns:\n+ * {}\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 - {} lonlat\n+ *\n+ * Returns:\n+ * {} An OpenLayers.Pixel which is the passed-in \n+ * , 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 . 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 \n+ * y - {Number} y parameter for the translation. Defaults to the y value of\n+ * the map's \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- return new OpenLayers.Geometry.LineString(returnPoints);\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- return this;\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.Geometry.LineString\"\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/Geometry/MultiLineString.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/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Geometry.MultiLineString\n- * A MultiLineString is a geometry with multiple \n- * components.\n+ * Class: OpenLayers.Icon\n+ * \n+ * The icon represents a graphical icon on the screen. Typically used in\n+ * conjunction with a 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- * Inherits from:\n- * - \n- * - \n */\n-OpenLayers.Geometry.MultiLineString = OpenLayers.Class(\n- OpenLayers.Geometry.Collection, {\n+OpenLayers.Icon = OpenLayers.Class({\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+ * Property: url \n+ * {String} image url\n+ */\n+ url: null,\n \n- /**\n- * Constructor: OpenLayers.Geometry.MultiLineString\n- * Constructor for a MultiLineString Geometry.\n- *\n- * Parameters: \n- * components - {Array()} \n- *\n- */\n+ /** \n+ * Property: size \n+ * {|Object} An OpenLayers.Size or\n+ * an object with a 'w' and 'h' properties.\n+ */\n+ size: null,\n \n- /**\n- * Method: split\n- * Use this geometry (the source) to attempt to split a target geometry.\n- * \n- * Parameters:\n- * 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+ /** \n+ * Property: offset \n+ * {|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+ * {|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 - {|Object} An OpenLayers.Size or an\n+ * object with a 'w' and 'h'\n+ * properties.\n+ * offset - {|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+ * {} 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 - {|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 - {|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 - {|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/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 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 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+ * {}This is set when a Geometry is added as component\n+ * of another geometry\n+ */\n+ parent: null,\n+\n+ /**\n+ * Property: bounds \n+ * {} 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+ * {} 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 - {} \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 - {} \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+ * {}\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 - {} 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 - {|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+ * {} 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+ * {} 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 | } 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/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+ * {} \n+ */\n+ layer: null,\n+\n+ /** \n+ * Property: id \n+ * {String} \n+ */\n+ id: null,\n+\n+ /** \n+ * Property: lonlat \n+ * {} \n+ */\n+ lonlat: null,\n+\n+ /** \n+ * Property: data \n+ * {Object} \n+ */\n+ data: null,\n+\n+ /** \n+ * Property: marker \n+ * {} \n+ */\n+ marker: null,\n+\n+ /**\n+ * APIProperty: popupClass\n+ * {} The class which will be used to instantiate\n+ * a new Popup. Default is .\n+ */\n+ popupClass: null,\n+\n+ /** \n+ * Property: popup \n+ * {} \n+ */\n+ popup: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Feature\n+ * Constructor for features.\n+ *\n+ * Parameters:\n+ * layer - {} \n+ * lonlat - {} \n+ * data - {Object} \n+ * \n+ * Returns:\n+ * {}\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+ * {} 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+ * {} 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 .\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+ * objects.\n+ * \n+ * Inherits from:\n+ * - \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+ * {} \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+ * {} The box bounding that feature's geometry, that\n+ * property can be set by an 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+ * {} 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 , and written by\n+ * , 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 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 - {} The geometry that this feature\n+ * represents.\n+ * attributes - {Object} An optional object that will be mapped to the\n+ * 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+ * {} 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+ * {} 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+ * {} 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 - {|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 - { or } 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/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+ * {} 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+ * {} for more information on\n+ * custom transformations.\n+ */\n+ externalProjection: null,\n+\n+ /**\n+ * APIProperty: internalProjection\n+ * {} 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+ * {} for more information on\n+ * custom transformations.\n+ */\n+ internalProjection: null,\n+\n+ /**\n+ * APIProperty: data\n+ * {Object} When is true, this is the parsed string sent to\n+ * .\n+ */\n+ data: null,\n+\n+ /**\n+ * APIProperty: keepData\n+ * {Object} Maintain a reference () 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 , 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/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+ * - \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+ * {} 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 - {} 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 - {} 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. \"5, 42\")\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 - {} 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+ * {} 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 - {} Point of origin for resizing\n+ * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n+ * \n+ * Returns:\n+ * {} - 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 - {} 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 - {} \n+ * dest - {}\n+ * \n+ * Returns:\n+ * {} \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 (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 and functions here merely iterate through\n+ * the components, summing their respective areas and lengths.\n+ *\n+ * Create a new instance with the constructor.\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {\n+\n+ /**\n+ * APIProperty: components\n+ * {Array()} 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()} 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+ * {} 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()} 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 - {} 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()} 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 - {} \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 .\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 - {} 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+ * {} 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 - {} 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 - {} 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 - {} Point of origin for resizing\n+ * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n+ * \n+ * Returns:\n+ * {} - 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 - {} 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 - {} 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 - {} \n+ * dest - {}\n+ * \n+ * Returns:\n+ * {} \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 - {} 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+ * constructor.\n+ *\n+ * Inherits from:\n+ * - \n+ * - \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()} \n+ *\n+ * Returns:\n+ * {}\n+ */\n+\n+ /**\n+ * APIMethod: addPoint\n+ * Wrapper for \n+ *\n+ * Parameters:\n+ * 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 \n+ *\n+ * Parameters:\n+ * 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+ * - \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()}\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 - {} 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+ * - \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()} 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 - {} 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 - {}\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 - {} 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 - {} 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 - {} 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 \n+ * components.\n+ * \n+ * Inherits from:\n+ * - \n+ * - \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()} \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 - {} 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@@ -18049,1523 +22676,347 @@\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+ OpenLayers/Request/XMLHttpRequest.js\n ====================================================================== */\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\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/Util.js\n+ * @requires OpenLayers/Request.js\n */\n \n-/**\n- * Namespace: OpenLayers.Event\n- * Utility functions for event handling.\n- */\n-OpenLayers.Event = {\n+(function() {\n \n- /** \n- * Property: observers \n- * {Object} A hashtable cache of the event observers. Keyed by\n- * element._eventCacheID \n- */\n- observers: false,\n+ // Save reference to earlier defined object implementation (if any)\n+ var oXMLHttpRequest = window.XMLHttpRequest;\n \n- /**\n- * Constant: KEY_SPACE\n- * {int}\n- */\n- KEY_SPACE: 32,\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- /** \n- * Constant: KEY_BACKSPACE \n- * {int} \n- */\n- KEY_BACKSPACE: 8,\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- /** \n- * Constant: KEY_TAB \n- * {int} \n- */\n- KEY_TAB: 9,\n+ // Constructor\n+ function cXMLHttpRequest() {\n+ return new fXMLHttpRequest;\n+ };\n+ cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;\n \n- /** \n- * Constant: KEY_RETURN \n- * {int} \n- */\n- KEY_RETURN: 13,\n+ // BUGFIX: Firefox with Firebug installed would break pages if not executed\n+ if (bGecko && oXMLHttpRequest.wrapped)\n+ cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;\n \n- /** \n- * Constant: KEY_ESC \n- * {int} \n- */\n- KEY_ESC: 27,\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- /** \n- * Constant: KEY_LEFT \n- * {int} \n- */\n- KEY_LEFT: 37,\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- /** \n- * Constant: KEY_UP \n- * {int} \n- */\n- KEY_UP: 38,\n+ // Priority proposal\n+ cXMLHttpRequest.prototype.priority = \"NORMAL\";\n \n- /** \n- * Constant: KEY_RIGHT \n- * {int} \n- */\n- KEY_RIGHT: 39,\n+ // Instance-level Events Handlers\n+ cXMLHttpRequest.prototype.onreadystatechange = null;\n \n- /** \n- * Constant: KEY_DOWN \n- * {int} \n- */\n- KEY_DOWN: 40,\n+ // Class-level Events Handlers\n+ cXMLHttpRequest.onreadystatechange = null;\n+ cXMLHttpRequest.onopen = null;\n+ cXMLHttpRequest.onsend = null;\n+ cXMLHttpRequest.onabort = null;\n \n- /** \n- * Constant: KEY_DELETE \n- * {int} \n- */\n- KEY_DELETE: 46,\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- /**\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+ // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests\n+ this._async = bAsync;\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+ // Set the onreadystatechange handler\n+ var oRequest = this,\n+ nState = this.readyState,\n+ fOnUnload;\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+ // 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- /**\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+ // Add method sniffer\n+ if (cXMLHttpRequest.onopen)\n+ cXMLHttpRequest.onopen.apply(this, arguments);\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+ 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- /**\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+ this.readyState = cXMLHttpRequest.OPENED;\n+ fReadyStateChange(this);\n \n- if (!allowDefault) {\n- OpenLayers.Event.preventDefault(event);\n- }\n+ this._object.onreadystatechange = function() {\n+ if (bGecko && !bAsync)\n+ return;\n \n- if (event.stopPropagation) {\n- event.stopPropagation();\n- } else {\n- event.cancelBubble = true;\n- }\n- },\n+ // Synchronize state\n+ oRequest.readyState = oRequest._object.readyState;\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+ fSynchronizeValues(oRequest);\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+ // BUGFIX: Firefox fires unnecessary DONE when aborting\n+ if (oRequest._aborted) {\n+ // Reset readyState to UNSENT\n+ oRequest.readyState = cXMLHttpRequest.UNSENT;\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+ // Return now\n+ return;\n+ }\n \n- if (name == 'keypress' &&\n- (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n- element.attachEvent)) {\n- name = 'keydown';\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- //if observers cache has not yet been created, create it\n- if (!this.observers) {\n- this.observers = {};\n- }\n+ // Instantiate a new transport object\n+ cXMLHttpRequest.call(oRequest);\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+ // 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- var cacheID = element._eventCacheID;\n+ oRequest._object.onreadystatechange = function() {\n+ // Synchronize state\n+ oRequest.readyState = oRequest._object.readyState;\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+ if (oRequest._aborted) {\n+ //\n+ oRequest.readyState = cXMLHttpRequest.UNSENT;\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+ // Return\n+ return;\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+ if (oRequest.readyState == cXMLHttpRequest.DONE) {\n+ // Clean Object\n+ fCleanTransport(oRequest);\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+ // get cached request\n+ if (oRequest.status == 304)\n+ oRequest._object = oRequest._cached;\n \n- this._removeElementObservers(OpenLayers.Event.observers[cacheID]);\n- },\n+ //\n+ delete oRequest._cached;\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+ fSynchronizeValues(oRequest);\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+ fReadyStateChange(oRequest);\n \n- var element = OpenLayers.Util.getElement(elementParam);\n- var cacheID = element._eventCacheID;\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- if (name == 'keypress') {\n- if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n- element.detachEvent) {\n- name = 'keydown';\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- // 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+ function fXMLHttpRequest_send(oRequest) {\n+ oRequest._object.send(oRequest._data);\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+ // BUGFIX: Gecko - missing readystatechange calls in synchronous requests\n+ if (bGecko && !oRequest._async) {\n+ oRequest.readyState = cXMLHttpRequest.OPENED;\n \n- if ((cacheEntry.name == name) &&\n- (cacheEntry.observer == observer) &&\n- (cacheEntry.useCapture == useCapture)) {\n+ // Synchronize state\n+ fSynchronizeValues(oRequest);\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+ // 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- //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+ if (!arguments.length)\n+ vData = null;\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+ // 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 \n- CLASS_NAME: \"OpenLayers.Event\"\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-/* prevent memory leaks in IE */\n-OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);\n+ // BUGFIX: Gecko - unnecessary DONE when aborting\n+ if (this.readyState > cXMLHttpRequest.UNSENT)\n+ this._aborted = true;\n \n-/**\n- * Class: OpenLayers.Events\n- */\n-OpenLayers.Events = OpenLayers.Class({\n+ this._object.abort();\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+ // BUGFIX: IE - memory leak\n+ fCleanTransport(this);\n \n- /** \n- * Property: listeners \n- * {Object} Hashtable of Array(Function): events listener functions \n- */\n- listeners: null,\n+ this.readyState = cXMLHttpRequest.UNSENT;\n \n- /** \n- * Property: object \n- * {Object} the code object issuing application events \n- */\n- object: null,\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- /** \n- * Property: element \n- * {DOMElement} the DOM element receiving browser events \n- */\n- element: null,\n+ return this._object.setRequestHeader(sName, sValue);\n+ };\n \n- /** \n- * Property: eventHandler \n- * {Function} bound event handler attached to elements \n- */\n- eventHandler: null,\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- /** \n- * APIProperty: fallThrough \n- * {Boolean} \n- */\n- fallThrough: null,\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- /** \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 namespace using\n- * , and named after the event they provide.\n- * The constructor receives the target instance as\n- * argument. Extensions that need to capture browser events before they\n- * propagate can register their listeners events using , 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 ), 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 that is bound to this instance so that\n- * it can be used with and\n- * .\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 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 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- * {} 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+ 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@@ -20655,5038 +24106,1705 @@\n OpenLayers.Util.extend(this, options);\n },\n \n CLASS_NAME: \"OpenLayers.WPSProcess.ChainLink\"\n \n });\n /* ======================================================================\n- OpenLayers/Symbolizer.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/BaseTypes/Class.js\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n */\n \n /**\n- * Class: OpenLayers.Symbolizer\n- * Base class representing a symbolizer used for feature rendering.\n+ * Class: OpenLayers.Format.WPSDescribeProcess\n+ * Read WPS DescribeProcess responses. \n+ *\n+ * Inherits from:\n+ * - \n */\n-OpenLayers.Symbolizer = OpenLayers.Class({\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- * 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+ * 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- * 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+ * Property: defaultPrefix\n+ */\n+ defaultPrefix: \"wps\",\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+ * 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- CLASS_NAME: \"OpenLayers.Symbolizer\"\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+ /**\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/Projection.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/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/SingleFile.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 method for details\n- * on usage.\n- *\n- * Additional transforms may be added by using the \n- * library. If the proj4js library is included, the 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 \n- * method to register a custom transform method.\n+ * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/WPSProcess.js\n+ * @requires OpenLayers/Format/WPSDescribeProcess.js\n+ * @requires OpenLayers/Request.js\n */\n-OpenLayers.Projection = OpenLayers.Class({\n \n- /**\n- * Property: proj\n- * {Object} Proj4js.Proj instance.\n- */\n- proj: null,\n+/**\n+ * Class: OpenLayers.WPSClient\n+ * High level API for interaction with Web Processing Services (WPS).\n+ * An instance is used to create \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: projCode\n- * {String}\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- projCode: null,\n+ servers: null,\n \n /**\n- * Property: titleRegEx\n- * {RegExp} regular expression to strip the title from a proj4js definition\n+ * Property: version\n+ * {String} The default WPS version to use if none is configured. Default\n+ * is '1.0.0'.\n */\n- titleRegEx: /\\+title=[^\\+]*/,\n+ version: '1.0.0',\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- * {} A projection object.\n+ * Property: lazy\n+ * {Boolean} Should the DescribeProcess be deferred until a process is\n+ * fully configured? Default is false.\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+ lazy: false,\n \n /**\n- * APIMethod: getCode\n- * Get the string SRS code.\n+ * Property: events\n+ * {}\n *\n- * Returns:\n- * {String} The SRS code.\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- getCode: function() {\n- return this.proj ? this.proj.srsCode : this.projCode;\n- },\n+ events: null,\n \n /**\n- * APIMethod: getUnits\n- * Get the units string for the projection -- returns null if \n- * proj4js is not available.\n+ * Constructor: OpenLayers.WPSClient\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+ * Parameters:\n+ * options - {Object} Object whose properties will be set on the instance.\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+ * 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- * Returns:\n- * {Boolean} The two projections are equivalent.\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- 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+ 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- 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 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 - { | 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/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+ * APIMethod: execute\n+ * Shortcut to execute a process with a single function call. This is\n+ * equivalent to using and then calling execute on the\n+ * process.\n *\n * Parameters:\n- * prefixedDom - {String} The property to convert\n+ * options - {Object} Options for the execute operation.\n *\n- * Returns:\n- * {String} The CSS property\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+ * , an 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 or an\n+ * array of features.\n+ * scope - {Object} Optional scope for the success callback.\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+ 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: css\n- * Detect which property is used for a CSS property\n+ * APIMethod: getProcess\n+ * Creates an .\n *\n * Parameters:\n- * property - {String} The standard (unprefixed) CSS property name\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- * {String} The standard CSS property, prefixed property or null if not\n- * supported\n+ * {}\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+ 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 cssCache[property];\n- }\n+ return process;\n+ },\n \n /**\n- * APIMethod: js\n- * Detect which property is used for a JS property/method\n+ * Method: describeProcess\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+ * 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- 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+ 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- 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+ } else {\n+ window.setTimeout(function() {\n+ callback.call(scope, server.processDescription[processID]);\n+ }, 0);\n }\n- return jsCache[property];\n- }\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+ * Method: destroy\n */\n- function style(property) {\n- return js(divStyle, property);\n- }\n+ destroy: function() {\n+ this.events.destroy();\n+ this.events = null;\n+ this.servers = null;\n+ },\n \n- return {\n- css: css,\n- js: js,\n- style: style,\n+ CLASS_NAME: 'OpenLayers.WPSClient'\n \n- // used for testing\n- cssCache: cssCache,\n- jsCache: jsCache\n- };\n-}());\n+});\n /* ======================================================================\n- OpenLayers/Animation.js\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/SingleFile.js\n- * @requires OpenLayers/Util/vendorPrefix.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Style.js\n+ * @requires OpenLayers/Feature/Vector.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+ * Class: OpenLayers.StyleMap\n */\n-OpenLayers.Animation = (function(window) {\n+OpenLayers.StyleMap = OpenLayers.Class({\n \n /**\n- * Property: isNative\n- * {Boolean} true if a native requestAnimationFrame function is available\n+ * Property: styles\n+ * {Object} Hash of {}, keyed by names of well known\n+ * rendering intents (e.g. \"default\", \"temporary\", \"select\", \"delete\").\n */\n- var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n- var isNative = !!(requestAnimationFrame);\n+ styles: null,\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+ * 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- 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+ extendDefault: true,\n \n /**\n- * Function: start\n- * Executes a method with in series for some \n- * duration.\n- *\n+ * Constructor: OpenLayers.StyleMap\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- * .\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- 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+ 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- requestFrame(loops[id], element);\n- return id;\n- }\n-\n- /**\n- * Function: stop\n- * Terminates an animation loop started with .\n- *\n- * Parameters:\n- * id - {Number} Identifier returned from .\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/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- * {(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-\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+ // 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- * 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- * easing - {(Function)} easing function method to use\n+ * Method: destroy\n */\n- initialize: function(easing) {\n- this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;\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: start\n- * Plays the Tween, and calls the callback method on each step\n+ * Method: createSymbolizer\n+ * Creates the symbolizer for a feature for a render intent.\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+ * 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- 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+ createSymbolizer: function(feature, intent) {\n+ if (!feature) {\n+ feature = new OpenLayers.Feature.Vector();\n }\n- this.animationId = OpenLayers.Animation.start(\n- OpenLayers.Function.bind(this.play, this)\n- );\n- },\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- */\n- stop: function() {\n- if (!this.playing) {\n- return;\n+ if (!this.styles[intent]) {\n+ intent = \"default\";\n }\n-\n- if (this.callbacks && this.callbacks.done) {\n- this.callbacks.done.call(this, this.finish);\n+ feature.renderIntent = intent;\n+ var defaultSymbolizer = {};\n+ if (this.extendDefault && intent != \"default\") {\n+ defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature);\n }\n- OpenLayers.Animation.stop(this.animationId);\n- this.animationId = null;\n- this.playing = false;\n+ return OpenLayers.Util.extend(defaultSymbolizer,\n+ this.styles[intent].createSymbolizer(feature));\n },\n \n /**\n- * Method: play\n- * Calls the appropriate easing method\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- 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+ 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- /**\n- * Create empty functions for all easing methods.\n- */\n- CLASS_NAME: \"OpenLayers.Tween\"\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- * Namespace: OpenLayers.Easing\n- * \n- * Credits:\n- * Easing Equations by Robert Penner, \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+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Map.js\n+ * @requires OpenLayers/Projection.js\n */\n-OpenLayers.Easing.Linear = {\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 + 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 + 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- return c * t / d + b;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Easing.Linear\"\n-};\n \n /**\n- * Namespace: OpenLayers.Easing.Expo\n+ * Class: OpenLayers.Layer\n */\n-OpenLayers.Easing.Expo = {\n+OpenLayers.Layer = OpenLayers.Class({\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+ * APIProperty: id\n+ * {String}\n */\n- easeIn: function(t, b, c, d) {\n- return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;\n- },\n+ id: 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- *\n- * Returns:\n- * {Float}\n+ /** \n+ * APIProperty: name\n+ * {String}\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+ name: null,\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+ * APIProperty: div\n+ * {DOMElement}\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+ div: null,\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+ * APIProperty: opacity\n+ * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default\n+ * is 1.\n */\n- easeIn: function(t, b, c, d) {\n- return c * (t /= d) * t + b;\n- },\n+ opacity: 1,\n \n /**\n- * Function: easeOut\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- * 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+ * 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- * 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+ * See #987 for more info.\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/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 constructor.\n- * \n- * On their own maps do not provide much functionality. To extend a map\n- * it's necessary to add controls () and \n- * layers () to the map. \n- */\n-OpenLayers.Map = OpenLayers.Class({\n+ alwaysInRange: null,\n \n /**\n- * Constant: Z_INDEX_BASE\n- * {Object} Base z-indexes for different classes of thing \n+ * Constant: RESOLUTION_PROPERTIES\n+ * {Array} The properties that are used for calculating resolutions\n+ * information.\n */\n- Z_INDEX_BASE: {\n- BaseLayer: 100,\n- Overlay: 325,\n- Feature: 725,\n- Popup: 750,\n- Control: 1000\n- },\n+ RESOLUTION_PROPERTIES: [\n+ 'scales', 'resolutions',\n+ 'maxScale', 'minScale',\n+ 'maxResolution', 'minResolution',\n+ 'numZoomLevels', 'maxZoomLevel'\n+ ],\n \n /**\n * APIProperty: events\n * {}\n *\n * Register a listener for a particular event with the following syntax:\n * (code)\n- * map.events.register(type, obj, listener);\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 map.events.object.\n- * element - {DOMElement} A reference to map.events.element.\n- *\n- * Browser events have the following additional properties:\n- * xy - {} The pixel location of the event (relative\n- * to the the map viewport).\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- * 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+ * 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 ). 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- * 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 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- * 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- * {} An events object that handles all \n- * events on the map\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: 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 or 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 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 method.\n- * \n- * Note:\n- * If you are calling after map construction, do not use\n- * auto. Instead, divide your 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- * {} 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- * {} The lonlat at which the later container was\n- * re-initialized (on-zoom)\n+ * APIProperty: map\n+ * {} This variable is set when the layer is added to \n+ * the map, via the accessor function setMap().\n */\n- layerContainerOrigin: null,\n+ map: null,\n \n /**\n- * Property: layerContainerDiv\n- * {HTMLDivElement} The element that contains the layers.\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- layerContainerDiv: null,\n+ isBaseLayer: false,\n \n /**\n- * APIProperty: layers\n- * {Array()} Ordered list of layers in the map\n+ * Property: alpha\n+ * {Boolean} The layer's images have an alpha channel. Default is false.\n */\n- layers: null,\n+ alpha: false,\n \n- /**\n- * APIProperty: controls\n- * {Array()} 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- * - or \n- * - or \n- * - \n- * - \n+ /** \n+ * APIProperty: displayInLayerSwitcher\n+ * {Boolean} Display the layer's name in the layer switcher. Default is\n+ * true.\n */\n- controls: null,\n+ displayInLayerSwitcher: true,\n \n /**\n- * Property: popups\n- * {Array()} List of popups associated with the map\n+ * APIProperty: visibility\n+ * {Boolean} The layer should be displayed in the map. Default is true.\n */\n- popups: null,\n+ visibility: true,\n \n /**\n- * APIProperty: baseLayer\n- * {} The currently selected base layer. This determines\n- * min/max zoom level, projection, etc.\n+ * APIProperty: attribution\n+ * {String} Attribution string, displayed when an \n+ * has been added to the map.\n */\n- baseLayer: null,\n+ attribution: null,\n \n- /**\n- * Property: center\n- * {} The current center of the map\n+ /** \n+ * Property: inRange\n+ * {Boolean} The current map resolution is within the layer's min/max \n+ * range. This is set in whenever the zoom \n+ * changes.\n */\n- center: null,\n+ inRange: false,\n \n /**\n- * Property: resolution\n- * {Float} The resolution of the map.\n+ * Propery: imageSize\n+ * {} For layers with a gutter, the image is larger than \n+ * the tile by twice the gutter in each dimension.\n */\n- resolution: null,\n+ imageSize: null,\n \n- /**\n- * Property: zoom\n- * {Integer} The current zoom level of the map\n- */\n- zoom: 0,\n+ // OPTIONS\n \n- /**\n- * Property: panRatio\n- * {Float} The ratio of the current extent within\n- * which panning will tween.\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- panRatio: 1.5,\n+ options: null,\n \n /**\n- * APIProperty: options\n- * {Object} The options object passed to the class constructor. Read-only.\n+ * APIProperty: eventListeners\n+ * {Object} If set as an option at construction, the eventListeners\n+ * object will be registered with . Object\n+ * structure must be a listeners object as shown in the example for\n+ * the events.on method.\n */\n- options: null,\n-\n- // Options\n+ eventListeners: null,\n \n /**\n- * APIProperty: tileSize\n- * {} Set in the map options to override the default tile\n- * size for this map.\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- tileSize: null,\n+ gutter: 0,\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 and ).\n+ * {} or {} 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 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: \"EPSG:4326\",\n+ projection: null,\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+ * {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+ * 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 .\n- */\n- maxResolution: null,\n-\n- /**\n- * APIProperty: minResolution\n- * {Float}\n- */\n- minResolution: null,\n-\n- /**\n- * APIProperty: maxScale\n- * {Float}\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 instead wherever possible.\n */\n- maxScale: null,\n+ scales: null,\n \n /**\n- * APIProperty: minScale\n- * {Float}\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- minScale: null,\n+ resolutions: null,\n \n /**\n * APIProperty: maxExtent\n * {|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 instead.\n- * The value for will change calculations for tile URLs.\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+ * 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 * {|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+ * The minimum extent for the layer. Defaults to null.\n */\n minExtent: null,\n \n /**\n- * APIProperty: restrictedExtent\n- * {|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+ * 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 \n+ * and displaying the whole world.\n */\n- restrictedExtent: null,\n+ maxResolution: 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+ * APIProperty: minResolution\n+ * {Float}\n */\n- numZoomLevels: 16,\n+ minResolution: null,\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- * {} 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+ * APIProperty: numZoomLevels\n+ * {Integer}\n */\n- displayProjection: null,\n+ numZoomLevels: null,\n \n /**\n- * APIProperty: tileManager\n- * {|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 {}.\n+ * APIProperty: minScale\n+ * {Float}\n */\n+ minScale: null,\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+ * APIProperty: maxScale\n+ * {Float}\n */\n- fallThrough: false,\n+ maxScale: null,\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+ * APIProperty: displayOutsideMaxExtent\n+ * {Boolean} Request map tiles that are completely outside of the max \n+ * extent for this layer. Defaults to false.\n */\n- autoUpdateSize: true,\n+ displayOutsideMaxExtent: false,\n \n /**\n- * APIProperty: eventListeners\n- * {Object} If set as an option at construction, the eventListeners\n- * object will be registered with . Object\n- * structure must be a listeners object as shown in the example for\n- * the events.on method.\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- eventListeners: null,\n+ wrapDateLine: false,\n \n /**\n- * Property: panTween\n- * {} Animated panning tween object, see panTo()\n+ * Property: metadata\n+ * {Object} This object can be used to store additional information on a\n+ * layer object.\n */\n- panTween: null,\n+ metadata: 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+ * 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- panMethod: OpenLayers.Easing.Expo.easeOut,\n+ initialize: function(name, options) {\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+ this.metadata = {};\n \n- /**\n- * Property: zoomTween\n- * {} Animated zooming tween object, see zoomTo()\n- */\n- zoomTween: null,\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- /**\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+ this.name = name;\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+ if (this.id == null) {\n \n- /**\n- * Property: paddingForPopups\n- * {} Outside margin of the popup. Used to prevent \n- * the popup from getting too close to the map border.\n- */\n- paddingForPopups: null,\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n \n- /**\n- * Property: layerContainerOriginPx\n- * {Object} Cached object representing the layer container origin (in pixels).\n- */\n- layerContainerOriginPx: null,\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- /**\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+ 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- * 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+ * 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- maxPx: null,\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- * 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+ * Method: clone\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
option is\n- * provided or if you intend to call the 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 - {|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 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- * 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 - {|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
and 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+ * obj - {} The layer to be cloned\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+ * Returns:\n+ * {} An exact clone of this \n */\n- initialize: function(div, options) {\n+ clone: function(obj) {\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+ if (obj == null) {\n+ obj = new OpenLayers.Layer(this.name, this.getOptions());\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+ // catch any randomly tagged-on properties\n+ OpenLayers.Util.applyDefaults(obj, this);\n \n- // now override default options \n- OpenLayers.Util.extend(this, options);\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- var projCode = this.projection instanceof OpenLayers.Projection ?\n- this.projection.projCode : this.projection;\n- OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);\n+ return obj;\n+ },\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+ * 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 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- // 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+ * 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- OpenLayers.Element.addClass(this.div, 'olMap');\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- // 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+ if (this.options == null) {\n+ this.options = {};\n+ }\n \n- this.events = new OpenLayers.Events(\n- this, this.viewPortDiv, null, this.fallThrough, {\n- includeXY: true\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- );\n-\n- if (OpenLayers.TileManager && this.tileManager !== null) {\n- if (!(this.tileManager instanceof OpenLayers.TileManager)) {\n- this.tileManager = new OpenLayers.TileManager(this.tileManager);\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- 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+ // update our copy for clone\n+ OpenLayers.Util.extend(this.options, newOptions);\n \n- this.viewPortDiv.appendChild(this.layerContainerDiv);\n+ // add new options to this\n+ OpenLayers.Util.extend(this, newOptions);\n \n- this.updateSize();\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners);\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- 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+ // 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- // 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+ 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- // 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 \n- this.popups = [];\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- this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);\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- // always call map.destroy()\n- OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);\n+ // map's center might not yet be set\n+ var extent = this.getExtent();\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+ 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- 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+ return redrawn;\n },\n \n /**\n- * APIMethod: render\n- * Render the map to a specified container.\n+ * Method: moveTo\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+ * 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- 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+ 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: 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+ * 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- unloadDestroy: null,\n+ moveByPx: function(dx, dy) {},\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+ * 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 - {}\n */\n- updateSizeDestroy: null,\n+ setMap: function(map) {\n+ if (this.map == 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+ this.map = map;\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+ // 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- // map has been destroyed. dont do it again!\n- OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);\n- this.unloadDestroy = null;\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 (this.updateSizeDestroy) {\n- OpenLayers.Event.stopObserving(window, 'resize',\n- this.updateSizeDestroy);\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.paddingForPopups = null;\n+ this.initResolutions();\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+ 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.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+ // deal with gutters\n+ this.setTileSize();\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+ * 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- * options - {Object} Hashtable of options to tag to the map\n+ * 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+ removeMap: function(map) {\n+ //to be overridden by subclasses\n },\n \n /**\n- * APIMethod: getTileSize\n- * Get the tile size for the map\n+ * APIMethod: getImageSize\n *\n+ * Parameters:\n+ * 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- * {}\n+ * {} The size that the image should be, taking into \n+ * account gutters.\n */\n- getTileSize: function() {\n- return this.tileSize;\n+ getImageSize: function(bounds) {\n+ return (this.imageSize || this.tileSize);\n },\n \n-\n /**\n- * APIMethod: getBy\n- * Get a list of objects given a property and a match item.\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- * 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+ * size - {}\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+ 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: 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+ * APIMethod: getVisibility\n+ * \n * Returns:\n- * {Array()} A list of layers matching the given criteria.\n- * An empty array is returned if no matches are found.\n+ * {Boolean} The layer should be displayed (if in range).\n */\n- getLayersBy: function(property, match) {\n- return this.getBy(\"layers\", property, match);\n+ getVisibility: function() {\n+ return this.visibility;\n },\n \n- /**\n- * APIMethod: getLayersByName\n- * Get a list of layers with names matching the given name.\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- * 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()} A list of layers matching the given name.\n- * An empty array is returned if no matches are found.\n+ * visibility - {Boolean} Whether or not to display the layer (if in range)\n */\n- getLayersByName: function(match) {\n- return this.getLayersBy(\"name\", match);\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: getLayersByClass\n- * Get a list of layers of a given class (CLASS_NAME).\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- * 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()} A list of layers matching the given class.\n- * An empty array is returned if no matches are found.\n+ * display - {Boolean}\n */\n- getLayersByClass: function(match) {\n- return this.getLayersBy(\"CLASS_NAME\", match);\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: 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+ * APIMethod: calculateInRange\n+ * \n * Returns:\n- * {Array()} A list of controls matching the given\n- * criteria. An empty array is returned if no matches are found.\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- getControlsBy: function(property, match) {\n- return this.getBy(\"controls\", property, match);\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: getControlsByClass\n- * Get a list of controls of a given class (CLASS_NAME).\n- *\n+ /** \n+ * APIMethod: setIsBaseLayer\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()} A list of controls matching the given class.\n- * An empty array is returned if no matches are found.\n+ * isBaseLayer - {Boolean}\n */\n- getControlsByClass: function(match) {\n- return this.getControlsBy(\"CLASS_NAME\", match);\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- /* Layer Functions */\n- /* */\n- /* The following functions deal with adding and */\n- /* removing Layers to and from the Map */\n+ /* Baselayer Functions */\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- * {} The Layer with the corresponding id from the map's \n- * layer collection, or null if not found.\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- 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+ 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- return foundLayer;\n- },\n+ if (this.options.alwaysInRange == null) {\n+ this.alwaysInRange = alwaysInRange;\n+ }\n \n- /**\n- * Method: setLayerZIndex\n- * \n- * Parameters:\n- * 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+ // 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- /**\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+ // 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 \n- /**\n- * APIMethod: addLayer\n- *\n- * Parameters:\n- * 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+ // 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- if (this.events.triggerEvent(\"preaddlayer\", {\n- layer: layer\n- }) === false) {\n- return false;\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.allOverlays) {\n- layer.isBaseLayer = false;\n+ if (this.options.minScale) {\n+ maxResolution = OpenLayers.Util.getResolutionFromScale(\n+ this.options.minScale, this.units);\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+ // 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- 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+ 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- } 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+ // 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- return true;\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- * APIMethod: addLayers \n+ * Method: resolutionsFromScales\n+ * Derive resolutions from scales.\n *\n * Parameters:\n- * layers - {Array()} \n+ * scales - {Array(Number)} Scales\n+ *\n+ * Returns\n+ * {Array(Number)} Resolutions\n */\n- addLayers: function(layers) {\n- for (var i = 0, len = layers.length; i < len; i++) {\n- this.addLayer(layers[i]);\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- * 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+ /**\n+ * Method: calculateResolutions\n+ * Calculate resolutions based on the provided properties.\n+ *\n * Parameters:\n- * layer - {} \n- * setNewBaseLayer - {Boolean} Default is true\n+ * props - {Object} Properties\n+ *\n+ * Returns:\n+ * {Array({Number})} Array of resolutions.\n */\n- removeLayer: function(layer, setNewBaseLayer) {\n- if (this.events.triggerEvent(\"preremovelayer\", {\n- layer: layer\n- }) === false) {\n- return;\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 (setNewBaseLayer == null) {\n- setNewBaseLayer = true;\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 (layer.isFixed) {\n- this.viewPortDiv.removeChild(layer.div);\n- } else {\n- this.layerContainerDiv.removeChild(layer.div);\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- 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+ // 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- this.resetLayersZIndex();\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- this.events.triggerEvent(\"removelayer\", {\n- layer: layer\n- });\n- layer.events.triggerEvent(\"removed\", {\n- map: this,\n- layer: layer\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: getNumLayers\n+ * APIMethod: getResolution\n * \n * Returns:\n- * {Int} The number of layers attached to the map.\n+ * {Float} The currently selected resolution of the map, taken from the\n+ * resolutions array, indexed by current zoom level.\n */\n- getNumLayers: function() {\n- return this.layers.length;\n+ getResolution: function() {\n+ var zoom = this.map.getZoom();\n+ return this.getResolutionForZoom(zoom);\n },\n \n /** \n- * APIMethod: getLayerIndex\n- *\n- * Parameters:\n- * layer - {}\n- *\n+ * APIMethod: getExtent\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+ * {} A Bounds object which represents the lon/lat \n+ * bounds of the current viewPort.\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 - {} \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 - {} \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 - {}\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 - {}\n- * px - {}\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()}\n- * pixels - {Array()}\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 - {}\n- * px - {}\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- * {} 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 - {} 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 - {}\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 - {}\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- * {} An 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- * {} A new 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 - {} Default is this.getCenter()\n- * resolution - {float} Default is this.getResolution() \n- * \n- * Returns:\n- * {} 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- * {}\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- * {}\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 - {}\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 - {|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- * '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 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- * 's maxExtent. This is an Integer value, unless the map is\n- * configured with set to true.\n- */\n- getMinZoom: function() {\n- return this.adjustZoom(0);\n- },\n-\n- /**\n- * Method: moveTo\n- *\n- * Parameters:\n- * 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 - {}\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 - {}\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- * {} 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- * {} 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- * {} 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 - {}\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- * without a lonlat argument.\n- * \n- * Parameters:\n- * zoom - {Integer}\n- */\n- zoomTo: function(zoom, xy) {\n- // non-API arguments:\n- // xy - {} 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 - {|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 - {|Object} An OpenLayers.Pixel or\n- * an object with a 'x'\n- * and 'y' properties.\n- * \n- * Returns:\n- * {} An OpenLayers.LonLat which is the passed-in view \n- * port , 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 - {}\n- * \n- * Returns:\n- * {} An OpenLayers.Pixel which is the passed-in \n- * , 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 - {} The zoom origin pixel location on the screen\n- * resolution - {Float} The resolution we want to get the center for\n- *\n- * Returns:\n- * {} 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 - {|Object} An OpenLayers.Pixel or an object with\n- * a 'x' and 'y' properties.\n- *\n- * Returns:\n- * {} 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 - {} A map location.\n- * \n- * Returns: \n- * {} An OpenLayers.Pixel corresponding to the \n- * 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 - {} 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- * {} 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 - {}\n- * \n- * Returns:\n- * {} 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 - {}\n- * \n- * Returns:\n- * {} 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 - {}\n- *\n- * Returns:\n- * {}\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 - {} lonlat\n- *\n- * Returns:\n- * {} An OpenLayers.Pixel which is the passed-in \n- * , 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 . 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 \n- * y - {Number} y parameter for the translation. Defaults to the y value of\n- * the map's \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- * {}\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 ). 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- * {} 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- * 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 whenever the zoom \n- * changes.\n- */\n- inRange: false,\n-\n- /**\n- * Propery: imageSize\n- * {} 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 . 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- * {} or {} 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 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 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- * {|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- * 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- * {|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 \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 - {} The layer to be cloned\n- *\n- * Returns:\n- * {} An exact clone of this \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 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 - {}\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 - {}\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 - {}\n- */\n- removeMap: function(map) {\n- //to be overridden by subclasses\n- },\n-\n- /**\n- * APIMethod: getImageSize\n- *\n- * Parameters:\n- * 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- * {} 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 - {}\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- * {} 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+ 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 - {}\n@@ -25952,1262 +26070,14 @@\n }\n return bounds;\n },\n \n CLASS_NAME: \"OpenLayers.Layer\"\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 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- * {|Object} An OpenLayers.Size or\n- * an object with a 'w' and 'h' properties.\n- */\n- size: null,\n-\n- /** \n- * Property: offset \n- * {|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- * {|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 - {|Object} An OpenLayers.Size or an\n- * object with a 'w' and 'h'\n- * properties.\n- * offset - {|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- * {} 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 - {|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 - {|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 - {|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- * and an . \n- *\n- * Markers are generally added to a special layer called\n- * .\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- * {} The icon used by this marker.\n- */\n- icon: null,\n-\n- /** \n- * Property: lonlat \n- * {} location of object\n- */\n- lonlat: null,\n-\n- /** \n- * Property: events \n- * {} the event handler.\n- */\n- events: null,\n-\n- /** \n- * Property: map \n- * {} the map this marker is attached to\n- */\n- map: null,\n-\n- /** \n- * Constructor: OpenLayers.Marker\n- *\n- * Parameters:\n- * lonlat - {} the position of this marker\n- * 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 - {}\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 - {|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 .\n- * \n- * Returns:\n- * {} 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- * {}. 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- * {}\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- * . 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 - {} 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 is set, this always\n- * returns true. If a 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 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 , 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 , 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 , 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 , 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 , returns false if Cmd is down.\n- */\n-OpenLayers.Handler.MOD_META = 8;\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- * - \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 instance is used to create \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- * {}\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 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- * , an 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 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 .\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- * {}\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/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@@ -30087,75 +28957,1924 @@\n this.tileCache = null;\n this.tileCacheIndex = null;\n this._destroyed = true;\n }\n \n });\n /* ======================================================================\n- OpenLayers/Popup.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/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Style.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 . Popup's don't require their own\n- * layer and are added the the map using the \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+ * Class: OpenLayers.Rule\n+ * This class represents an SLD Rule, as being used for rule-based SLD styling.\n */\n-OpenLayers.Popup = OpenLayers.Class({\n+OpenLayers.Rule = OpenLayers.Class({\n \n- /** \n- * Property: events \n- * {} custom event manager \n+ /**\n+ * Property: id\n+ * {String} A unique id for this session.\n */\n- events: null,\n+ id: null,\n \n- /** Property: id\n- * {String} the unique identifier assigned to this popup.\n+ /**\n+ * APIProperty: name\n+ * {String} name of this rule\n */\n- id: \"\",\n+ name: null,\n \n- /** \n- * Property: lonlat \n- * {} the position of this popup on the map\n+ /**\n+ * Property: title\n+ * {String} Title of this rule (set if included in SLD)\n */\n- lonlat: null,\n+ title: null,\n \n- /** \n- * Property: div \n- * {DOMElement} the div that contains this popup.\n+ /**\n+ * Property: description\n+ * {String} Description of this rule (set if abstract is included in SLD)\n */\n- div: null,\n+ description: null,\n \n- /** \n- * Property: contentSize \n- * {} the width and height of the content.\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+ * {} 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+ * {}\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 - {} 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 - {} 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+ * {} 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/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/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+\n+/**\n+ * @requires OpenLayers/Symbolizer.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Symbolizer.Point\n+ * A symbolizer used to render point features.\n+ */\n+OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {\n+\n+ /**\n+ * APIProperty: strokeColor\n+ * {String} Color for line stroke. This is a RGB hex value (e.g. \"#ff0000\"\n+ * for red).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: strokeOpacity\n+ * {Number} Stroke opacity (0-1).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: strokeWidth\n+ * {Number} Pixel stroke width.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: strokeLinecap\n+ * {String} Stroke cap type (\"butt\", \"round\", or \"square\").\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * Property: strokeDashstyle\n+ * {String} Stroke dash style according to the SLD spec. Note that the\n+ * OpenLayers values for strokeDashstyle (\"dot\", \"dash\", \"dashdot\",\n+ * \"longdash\", \"longdashdot\", or \"solid\") will not work in SLD, but\n+ * most SLD patterns will render correctly in OpenLayers.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: fillColor\n+ * {String} RGB hex fill color (e.g. \"#ff0000\" for red).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: fillOpacity\n+ * {Number} Fill opacity (0-1).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: pointRadius\n+ * {Number} Pixel point radius.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: externalGraphic\n+ * {String} Url to an external graphic that will be used for rendering \n+ * points.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: graphicWidth\n+ * {Number} Pixel width for sizing an external graphic.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: graphicHeight\n+ * {Number} Pixel height for sizing an external graphic.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: graphicOpacity\n+ * {Number} Opacity (0-1) for an external graphic.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: graphicXOffset\n+ * {Number} Pixel offset along the positive x axis for displacing an \n+ * external graphic.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: graphicYOffset\n+ * {Number} Pixel offset along the positive y axis for displacing an \n+ * external graphic.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: rotation\n+ * {Number} The rotation of a graphic in the clockwise direction about its \n+ * center point (or any point off center as specified by \n+ * and ).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: graphicName\n+ * {String} Named graphic to use when rendering points. Supported values \n+ * include \"circle\", \"square\", \"star\", \"x\", \"cross\", and \"triangle\".\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * Constructor: OpenLayers.Symbolizer.Point\n+ * Create a symbolizer for rendering points.\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 point symbolizer.\n+ */\n+ initialize: function(config) {\n+ OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Symbolizer.Point\"\n+\n+});\n+\n+/* ======================================================================\n+ OpenLayers/Symbolizer/Line.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Symbolizer.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Symbolizer.Line\n+ * A symbolizer used to render line features.\n+ */\n+OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, {\n+\n+ /**\n+ * APIProperty: strokeColor\n+ * {String} Color for line stroke. This is a RGB hex value (e.g. \"#ff0000\"\n+ * for red). \n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: strokeOpacity\n+ * {Number} Stroke opacity (0-1).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: strokeWidth\n+ * {Number} Pixel stroke width.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: strokeLinecap\n+ * {String} Stroke cap type (\"butt\", \"round\", or \"square\").\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * Property: strokeDashstyle\n+ * {String} Stroke dash style according to the SLD spec. Note that the\n+ * OpenLayers values for strokeDashstyle (\"dot\", \"dash\", \"dashdot\",\n+ * \"longdash\", \"longdashdot\", or \"solid\") will not work in SLD, but\n+ * most SLD patterns will render correctly in OpenLayers.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * Constructor: OpenLayers.Symbolizer.Line\n+ * Create a symbolizer for rendering lines.\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 line symbolizer.\n+ */\n+ initialize: function(config) {\n+ OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Symbolizer.Line\"\n+\n+});\n+\n+/* ======================================================================\n+ OpenLayers/Symbolizer/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/Symbolizer.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Symbolizer.Polygon\n+ * A symbolizer used to render line features.\n+ */\n+OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, {\n+\n+ /**\n+ * APIProperty: strokeColor\n+ * {String} Color for line stroke. This is a RGB hex value (e.g. \"#ff0000\"\n+ * for red).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: strokeOpacity\n+ * {Number} Stroke opacity (0-1).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: strokeWidth\n+ * {Number} Pixel stroke width.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: strokeLinecap\n+ * {String} Stroke cap type (\"butt\", \"round\", or \"square\").\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * Property: strokeDashstyle\n+ * {String} Stroke dash style according to the SLD spec. Note that the\n+ * OpenLayers values for strokeDashstyle (\"dot\", \"dash\", \"dashdot\",\n+ * \"longdash\", \"longdashdot\", or \"solid\") will not work in SLD, but\n+ * most SLD patterns will render correctly in OpenLayers.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: fillColor\n+ * {String} RGB hex fill color (e.g. \"#ff0000\" for red).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * APIProperty: fillOpacity\n+ * {Number} Fill opacity (0-1).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * Constructor: OpenLayers.Symbolizer.Polygon\n+ * Create a symbolizer for rendering polygons.\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 polygon symbolizer.\n+ */\n+ initialize: function(config) {\n+ OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Symbolizer.Polygon\"\n+\n+});\n+\n+/* ======================================================================\n+ OpenLayers/Symbolizer/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/Symbolizer.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Symbolizer.Text\n+ * A symbolizer used to render text labels for features.\n+ */\n+OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, {\n+\n+ /** \n+ * APIProperty: label\n+ * {String} The text for the label.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /** \n+ * APIProperty: fontFamily\n+ * {String} The font family for the label.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /** \n+ * APIProperty: fontSize\n+ * {String} The font size for the label.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /** \n+ * APIProperty: fontWeight\n+ * {String} The font weight for the label.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * Property: fontStyle\n+ * {String} The font style for the label.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ */\n+\n+ /**\n+ * Constructor: OpenLayers.Symbolizer.Text\n+ * Create a symbolizer for rendering text labels.\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 text symbolizer.\n+ */\n+ initialize: function(config) {\n+ OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Symbolizer.Text\"\n+\n+});\n+\n+/* ======================================================================\n+ OpenLayers/Symbolizer/Raster.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Symbolizer.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Symbolizer.Raster\n+ * A symbolizer used to render raster images.\n+ */\n+OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, {\n+\n+ /**\n+ * Constructor: OpenLayers.Symbolizer.Raster\n+ * Create a symbolizer for rendering rasters.\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 raster symbolizer.\n+ */\n+ initialize: function(config) {\n+ OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Symbolizer.Raster\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Style2.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD 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/Rule.js\n+ * @requires OpenLayers/Symbolizer/Point.js\n+ * @requires OpenLayers/Symbolizer/Line.js\n+ * @requires OpenLayers/Symbolizer/Polygon.js\n+ * @requires OpenLayers/Symbolizer/Text.js\n+ * @requires OpenLayers/Symbolizer/Raster.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Style2\n+ * This class represents a collection of rules for rendering features.\n+ */\n+OpenLayers.Style2 = 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} Style identifier.\n+ */\n+ name: null,\n+\n+ /**\n+ * APIProperty: title\n+ * {String} Title of this style.\n+ */\n+ title: null,\n+\n+ /**\n+ * APIProperty: description\n+ * {String} Description of this style.\n+ */\n+ description: null,\n+\n+ /**\n+ * APIProperty: layerName\n+ * {} 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+ * APIProperty: rules \n+ * {Array()} Collection of rendering rules.\n+ */\n+ rules: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Style2\n+ * Creates a style representing a collection of rendering rules.\n+ *\n+ * Parameters:\n+ * config - {Object} An object containing properties to be set on the \n+ * style. Any documented properties may be set at construction.\n+ *\n+ * Returns:\n+ * {} A new style object.\n+ */\n+ initialize: function(config) {\n+ OpenLayers.Util.extend(this, config);\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+ }\n+ delete this.rules;\n+ },\n+\n+ /**\n+ * APIMethod: clone\n+ * Clones this style.\n+ * \n+ * Returns:\n+ * {} Clone of this style.\n+ */\n+ clone: function() {\n+ var config = OpenLayers.Util.extend({}, this);\n+ // clone rules\n+ if (this.rules) {\n+ config.rules = [];\n+ for (var i = 0, len = this.rules.length; i < len; ++i) {\n+ config.rules.push(this.rules[i].clone());\n+ }\n+ }\n+ return new OpenLayers.Style2(config);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Style2\"\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+ * {}\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+ * {} \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+ * {} 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+ * ).\n+ */\n+ featureDx: 0,\n+\n+ /**\n+ * Constructor: OpenLayers.Renderer \n+ *\n+ * Parameters:\n+ * containerID - {} \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 - {}\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 - {} \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 - {} \n+ * style - {}\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 - {} Bounds of the feature\n+ * worldBounds - {} 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 - {} \n+ * style - {Object} \n+ * featureId - {} \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 - {}\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 - {} \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()} \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 - {} \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 - {} 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/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 - {} or {Object} Starting point. A LonLat or\n+ * a JavaScript literal with lon lat properties.\n+ * to - {} 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 - {} or {Object} Starting point. A LonLat or\n+ * a JavaScript literal with lon lat properties.\n+ * to - {} 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/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+ * {} 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+ * {} 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 - {}\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+ * {} An \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({})} or\n+ * {}\n+ * options - {Object} Optional object for configuring the request.\n+ *\n+ * Returns:\n+ * {} An \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({})} or\n+ * {}\n+ * options - {Object} Optional object for configuring the request.\n+ *\n+ * Returns:\n+ * {} An \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 - {}\n+ * options - {Object} Optional object for configuring the request.\n+ *\n+ * Returns:\n+ * {} An \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({})}\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({})} An array of\n+ * objects.\n+ */\n+ commit: function() {},\n+\n+ /**\n+ * Method: abort\n+ * Abort an ongoing request.\n+ *\n+ * Parameters:\n+ * 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 - {} 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({})} or {}\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({})} or {}\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/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 - {} 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 - {} 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/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 . Popup's don't require their own\n+ * layer and are added the the map using the \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+ * {} 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+ * {} 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+ * {} the width and height of the content.\n+ */\n contentSize: null,\n \n /** \n * Property: size \n * {} the width and height of the popup.\n */\n size: null,\n@@ -31160,14 +31879,718 @@\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/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+ * {} 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 property.\n+ *\n+ * Parameters:\n+ * layer - {}\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/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+ * and an . \n+ *\n+ * Markers are generally added to a special layer called\n+ * .\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+ * {} The icon used by this marker.\n+ */\n+ icon: null,\n+\n+ /** \n+ * Property: lonlat \n+ * {} location of object\n+ */\n+ lonlat: null,\n+\n+ /** \n+ * Property: events \n+ * {} the event handler.\n+ */\n+ events: null,\n+\n+ /** \n+ * Property: map \n+ * {} the map this marker is attached to\n+ */\n+ map: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Marker\n+ *\n+ * Parameters:\n+ * lonlat - {} the position of this marker\n+ * 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 - {}\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 - {|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 .\n+ * \n+ * Returns:\n+ * {} 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+ * {}. 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+ * {}\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+ * . 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 - {} 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 is set, this always\n+ * returns true. If a 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 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 , 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 , 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 , 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 , 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 , returns false if Cmd is down.\n+ */\n+OpenLayers.Handler.MOD_META = 8;\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@@ -31535,24724 +32958,22797 @@\n OpenLayers.Control.TYPE_TOGGLE = 2;\n \n /**\n * Constant: OpenLayers.Control.TYPE_TOOL\n */\n OpenLayers.Control.TYPE_TOOL = 3;\n /* ======================================================================\n- OpenLayers/Rule.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 /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Style.js\n+ * @requires OpenLayers/Events.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.Events.buttonclick\n+ * Extension event type for handling buttons on top of a dom element. This\n+ * event type fires \"buttonclick\" on its 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 .\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.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+OpenLayers.Events.buttonclick = OpenLayers.Class({\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+ * Property: target\n+ * {} The events instance that the buttonclick event will\n+ * be triggered on.\n */\n- context: null,\n+ target: null,\n \n /**\n- * Property: filter\n- * {} Optional filter for the rule.\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- filter: null,\n+ events: [\n+ 'mousedown', 'mouseup', 'click', 'dblclick',\n+ 'touchstart', 'touchmove', 'touchend', 'keydown'\n+ ],\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+ * Property: startRegEx\n+ * {RegExp} Regular expression to test Event.type for events that start\n+ * a buttonclick sequence.\n */\n- elseFilter: false,\n+ startRegEx: /^mousedown|touchstart$/,\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+ * Property: cancelRegEx\n+ * {RegExp} Regular expression to test Event.type for events that cancel\n+ * a buttonclick sequence.\n */\n- symbolizer: null,\n+ cancelRegEx: /^touchmove$/,\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+ * Property: completeRegEx\n+ * {RegExp} Regular expression to test Event.type for events that complete\n+ * a buttonclick sequence.\n */\n- symbolizers: null,\n+ completeRegEx: /^mouseup|touchend$/,\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: startEvt\n+ * {Event} The event that started the click sequence\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+ * 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+ * instances.\n *\n * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * rule\n- * \n- * Returns:\n- * {}\n+ * target - {} The events instance that the buttonclick\n+ * event will be triggered on.\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(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- 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+ * Method: destroy\n */\n destroy: function() {\n- for (var i in this.symbolizer) {\n- this.symbolizer[i] = null;\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.unregister(this.events[i], this, this.buttonClick);\n }\n- this.symbolizer = null;\n- delete this.symbolizers;\n+ delete this.target;\n },\n \n /**\n- * APIMethod: evaluate\n- * evaluates this rule for a specific feature\n- * \n- * Parameters:\n- * feature - {} feature to apply the rule to.\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- * {Boolean} true if the rule applies, false if it does not.\n- * This rule is the default rule and always returns true.\n+ * {DOMElement} The button element, or undefined.\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+ 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-\n- return applies;\n+ element = element.parentNode;\n+ } while (--depth > 0 && element);\n+ return button;\n },\n \n /**\n- * Method: getContext\n- * Gets the context for evaluating this rule\n- * \n- * Paramters:\n- * feature - {} feature to take the context from if\n- * none is specified.\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- 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+ 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- * APIMethod: clone\n- * Clones this rule.\n- * \n- * Returns:\n- * {} Clone of this rule.\n+ * Method: buttonClick\n+ * Check if a button was clicked, and fire the buttonclick event\n+ *\n+ * Parameters:\n+ * evt - {Event}\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+ 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- // 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+ return propagate;\n+ }\n \n- CLASS_NAME: \"OpenLayers.Rule\"\n });\n /* ======================================================================\n- OpenLayers/Renderer.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/BaseTypes/Class.js\n+ * @requires OpenLayers/Handler.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+ * 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 , controls can also ignore clicks\n+ * that include a drag. Create a new instance with the\n+ * constructor.\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+ * Inherits from:\n+ * - \n */\n-OpenLayers.Renderer = OpenLayers.Class({\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: container\n- * {DOMElement} \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- container: null,\n+ single: true,\n \n /**\n- * Property: root\n- * {DOMElement}\n+ * APIProperty: double\n+ * {Boolean} Handle double-clicks. Default is false.\n */\n- root: null,\n+ 'double': false,\n \n- /** \n- * Property: extent\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- extent: null,\n+ pixelTolerance: 0,\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: 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- locked: false,\n+ dblclickTolerance: 13,\n \n- /** \n- * Property: size\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- size: null,\n+ stopSingle: false,\n \n /**\n- * Property: resolution\n- * {Float} cache of current map resolution\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- resolution: null,\n+ stopDouble: false,\n \n /**\n- * Property: map \n- * {} Reference to the map -- this is set in Vector's setMap()\n+ * Property: timerId\n+ * {Number} The id of the timeout waiting to clear the .\n */\n- map: null,\n+ timerId: 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- * ).\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- featureDx: 0,\n+ down: null,\n \n /**\n- * Constructor: OpenLayers.Renderer \n- *\n- * Parameters:\n- * containerID - {} \n- * options - {Object} options for this renderer. See sublcasses for\n- * supported options.\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- initialize: function(containerID, options) {\n- this.container = OpenLayers.Util.getElement(containerID);\n- OpenLayers.Util.extend(this, options);\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- * APIMethod: destroy\n+ * Property: rightclickTimerId\n+ * {Number} The id of the right mouse timeout waiting to clear the \n+ * .\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+ rightclickTimerId: null,\n \n /**\n- * APIMethod: supported\n- * This should be overridden by specific subclasses\n+ * Constructor: OpenLayers.Handler.Click\n+ * Create a new click handler.\n * \n+ * Parameters:\n+ * 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} Whether or not the browser supports the renderer class\n+ * {Boolean} Continue propagating this event.\n */\n- supported: function() {\n- return false;\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: 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+ * Method: touchmove\n+ * Store position of last move, because touchend event can have\n+ * an empty \"touches\" property.\n *\n- * Parameters:\n- * extent - {}\n- * resolutionChanged - {Boolean}\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} 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} Continue propagating this event.\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+ 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: 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: mousedown\n+ * Handle mousedown.\n *\n- * Parameters:\n- * size - {} \n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n */\n- setSize: function(size) {\n- this.size = size.clone();\n- this.resolution = null;\n+ mousedown: function(evt) {\n+ this.down = this.getEventInfo(evt);\n+ this.last = this.getEventInfo(evt);\n+ return true;\n },\n \n- /** \n- * Method: getResolution\n- * Uses cached copy of resolution if available to minimize computing\n+ /**\n+ * Method: mouseup\n+ * Handle mouseup. Installed to support collection of right mouse events.\n * \n * Returns:\n- * {Float} The current map's resolution\n+ * {Boolean} Continue propagating this event.\n */\n- getResolution: function() {\n- this.resolution = this.resolution || this.map.getResolution();\n- return this.resolution;\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: 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 - {} \n- * style - {}\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} true if the feature has been drawn completely, false if not,\n- * undefined if the feature had no geometry\n+ * {Boolean} Continue propagating this event.\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+ 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 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+ 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: 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 - {} Bounds of the feature\n- * worldBounds - {} Bounds of the world\n+ * Method: delayedRightCall\n+ * Sets to null. And optionally triggers the \n+ * rightclick callback if evt is set.\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+ delayedRightCall: function(evt) {\n+ this.rightclickTimerId = null;\n+ if (evt) {\n+ this.callback('rightclick', [evt]);\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+ * 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- * geometry - {} \n- * style - {Object} \n- * featureId - {} \n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n */\n- drawGeometry: function(geometry, style, featureId) {},\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: 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 - {}\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- drawText: function(featureId, style, location) {},\n+ dblclick: function(evt) {\n+ this.handleDouble(evt);\n+ return !this.stopDouble;\n+ },\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+ * Method: handleDouble\n+ * Handle double-click sequence.\n */\n- removeText: function(featureId) {},\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: clear\n- * Clear all vectors from the renderer.\n- * virtual function.\n+ /** \n+ * Method: handleSingle\n+ * Handle single click sequence.\n */\n- clear: function() {},\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+ /** \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- * 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 - {} \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 will have no effect here (this method will always\n+ * return true).\n *\n * Returns:\n- * {String} A feature id or undefined.\n+ * {Boolean} The click is within the pixel tolerance (if specified).\n */\n- getFeatureIdFromEvent: function(evt) {},\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: eraseFeatures \n- * This is called by the layer to erase features\n- * \n- * Parameters:\n- * features - {Array()} \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- 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+ 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: eraseGeometry\n- * Remove a geometry from the renderer (by id).\n- * virtual function.\n- * \n- * Parameters:\n- * geometry - {} \n- * featureId - {String}\n+ * Method: clearTimer\n+ * Clear the timer and set to null.\n */\n- eraseGeometry: function(geometry, featureId) {},\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: 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 - {} target renderer for the moved root\n+ * Method: delayedCall\n+ * Sets to null. And optionally triggers the click callback if\n+ * evt is set.\n */\n- moveRoot: function(renderer) {},\n+ delayedCall: function(evt) {\n+ this.timerId = null;\n+ if (evt) {\n+ this.callback(\"click\", [evt]);\n+ }\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+ * 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- * {String} the id of the output layer.\n+ * {Object} An object with event related info.\n */\n- getRenderLayerId: function() {\n- return this.container.id;\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- * Method: applyDefaultSymbolizer\n- * \n- * Parameters:\n- * symbolizer - {Object}\n- * \n+ * APIMethod: deactivate\n+ * Deactivate the handler.\n+ *\n * Returns:\n- * {Object}\n+ * {Boolean} The handler was successfully deactivated.\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+ 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- OpenLayers.Util.extend(result, symbolizer);\n- return result;\n+ return deactivated;\n },\n \n- CLASS_NAME: \"OpenLayers.Renderer\"\n+ CLASS_NAME: \"OpenLayers.Handler.Click\"\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/Symbolizer/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- * @requires OpenLayers/Symbolizer.js\n+ * @requires OpenLayers/Handler.js\n */\n \n /**\n- * Class: OpenLayers.Symbolizer.Point\n- * A symbolizer used to render point features.\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 constructor.\n+ *\n+ * Inherits from:\n+ * - \n */\n-OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {\n-\n- /**\n- * APIProperty: strokeColor\n- * {String} Color for line stroke. This is a RGB hex value (e.g. \"#ff0000\"\n- * for red).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n- */\n-\n- /**\n- * APIProperty: strokeOpacity\n- * {Number} Stroke opacity (0-1).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n- */\n+OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n \n- /**\n- * APIProperty: strokeWidth\n- * {Number} Pixel stroke width.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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: strokeLinecap\n- * {String} Stroke cap type (\"butt\", \"round\", or \"square\").\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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: strokeDashstyle\n- * {String} Stroke dash style according to the SLD spec. Note that the\n- * OpenLayers values for strokeDashstyle (\"dot\", \"dash\", \"dashdot\",\n- * \"longdash\", \"longdashdot\", or \"solid\") will not work in SLD, but\n- * most SLD patterns will render correctly in OpenLayers.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ /** \n+ * Property: dragging \n+ * {Boolean} \n */\n+ dragging: false,\n \n- /**\n- * APIProperty: fillColor\n- * {String} RGB hex fill color (e.g. \"#ff0000\" for red).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ /** \n+ * Property: last\n+ * {} The last pixel location of the drag.\n */\n+ last: null,\n \n- /**\n- * APIProperty: fillOpacity\n- * {Number} Fill opacity (0-1).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ /** \n+ * Property: start\n+ * {} The first pixel location of the drag.\n */\n+ start: null,\n \n /**\n- * APIProperty: pointRadius\n- * {Number} Pixel point radius.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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 \n /**\n- * APIProperty: externalGraphic\n- * {String} Url to an external graphic that will be used for rendering \n- * points.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Property: oldOnselectstart\n+ * {Function}\n */\n+ oldOnselectstart: null,\n \n /**\n- * APIProperty: graphicWidth\n- * {Number} Pixel width for sizing an external graphic.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ interval: 0,\n \n /**\n- * APIProperty: graphicHeight\n- * {Number} Pixel height for sizing an external graphic.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ timeoutId: null,\n \n /**\n- * APIProperty: graphicOpacity\n- * {Number} Opacity (0-1) for an external graphic.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ documentDrag: false,\n \n /**\n- * APIProperty: graphicXOffset\n- * {Number} Pixel offset along the positive x axis for displacing an \n- * external graphic.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Property: documentEvents\n+ * {Boolean} Are we currently observing document events?\n */\n+ documentEvents: null,\n \n /**\n- * APIProperty: graphicYOffset\n- * {Number} Pixel offset along the positive y axis for displacing an \n- * external graphic.\n+ * Constructor: OpenLayers.Handler.Drag\n+ * Returns OpenLayers.Handler.Drag\n * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Parameters:\n+ * 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+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n \n- /**\n- * APIProperty: rotation\n- * {Number} The rotation of a graphic in the clockwise direction about its \n- * center point (or any point off center as specified by \n- * and ).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- /**\n- * APIProperty: graphicName\n- * {String} Named graphic to use when rendering points. Supported values \n- * include \"circle\", \"square\", \"star\", \"x\", \"cross\", and \"triangle\".\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n- */\n \n /**\n- * Constructor: OpenLayers.Symbolizer.Point\n- * Create a symbolizer for rendering points.\n+ * Method: dragstart\n+ * This private method is factorized from mousedown and touchstart methods\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+ * evt - {Event} The event\n *\n * Returns:\n- * A new point symbolizer.\n+ * {Boolean} Let the event propagate.\n */\n- initialize: function(config) {\n- OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Symbolizer.Point\"\n-\n-});\n-\n-/* ======================================================================\n- OpenLayers/Symbolizer/Line.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Symbolizer.js\n- */\n-\n-/**\n- * Class: OpenLayers.Symbolizer.Line\n- * A symbolizer used to render line features.\n- */\n-OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, {\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- * APIProperty: strokeColor\n- * {String} Color for line stroke. This is a RGB hex value (e.g. \"#ff0000\"\n- * for red). \n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n- */\n+ // prevent document dragging\n+ OpenLayers.Event.preventDefault(evt);\n \n- /**\n- * APIProperty: strokeOpacity\n- * {Number} Stroke opacity (0-1).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- /**\n- * APIProperty: strokeWidth\n- * {Number} Pixel stroke width.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: strokeLinecap\n- * {String} Stroke cap type (\"butt\", \"round\", or \"square\").\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ 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- /**\n- * Property: strokeDashstyle\n- * {String} Stroke dash style according to the SLD spec. Note that the\n- * OpenLayers values for strokeDashstyle (\"dot\", \"dash\", \"dashdot\",\n- * \"longdash\", \"longdashdot\", or \"solid\") will not work in SLD, but\n- * most SLD patterns will render correctly in OpenLayers.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * Constructor: OpenLayers.Symbolizer.Line\n- * Create a symbolizer for rendering lines.\n+ * Method: dragend\n+ * This private method is factorized from mouseup and touchend methods\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+ * evt - {Event} The event\n *\n * Returns:\n- * A new line symbolizer.\n+ * {Boolean} Let the event propagate.\n */\n- initialize: function(config) {\n- OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\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- CLASS_NAME: \"OpenLayers.Symbolizer.Line\"\n-\n-});\n-\n-/* ======================================================================\n- OpenLayers/Symbolizer/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/Symbolizer.js\n- */\n-\n-/**\n- * Class: OpenLayers.Symbolizer.Polygon\n- * A symbolizer used to render line features.\n- */\n-OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, {\n-\n /**\n- * APIProperty: strokeColor\n- * {String} Color for line stroke. This is a RGB hex value (e.g. \"#ff0000\"\n- * for red).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: strokeOpacity\n- * {Number} Stroke opacity (0-1).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: strokeWidth\n- * {Number} Pixel stroke width.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: strokeLinecap\n- * {String} Stroke cap type (\"butt\", \"round\", or \"square\").\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ up: function(evt) {},\n \n /**\n- * Property: strokeDashstyle\n- * {String} Stroke dash style according to the SLD spec. Note that the\n- * OpenLayers values for strokeDashstyle (\"dot\", \"dash\", \"dashdot\",\n- * \"longdash\", \"longdashdot\", or \"solid\") will not work in SLD, but\n- * most SLD patterns will render correctly in OpenLayers.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ out: function(evt) {},\n \n /**\n- * APIProperty: fillColor\n- * {String} RGB hex fill color (e.g. \"#ff0000\" for red).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: fillOpacity\n- * {Number} Fill opacity (0-1).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ },\n \n /**\n- * Constructor: OpenLayers.Symbolizer.Polygon\n- * Create a symbolizer for rendering polygons.\n+ * Method: touchstart\n+ * Handle touchstart events\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+ * evt - {Event}\n *\n * Returns:\n- * A new polygon symbolizer.\n+ * {Boolean} Let the event propagate.\n */\n- initialize: function(config) {\n- OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return this.dragstart(evt);\n },\n \n- CLASS_NAME: \"OpenLayers.Symbolizer.Polygon\"\n-\n-});\n-\n-/* ======================================================================\n- OpenLayers/Symbolizer/Text.js\n- ====================================================================== */\n+ /**\n+ * Method: mousemove\n+ * Handle mousemove events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n+ */\n+ mousemove: function(evt) {\n+ return this.dragmove(evt);\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: touchmove\n+ * Handle touchmove events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n+ */\n+ touchmove: function(evt) {\n+ return this.dragmove(evt);\n+ },\n \n-/**\n- * @requires OpenLayers/Symbolizer.js\n- */\n+ /**\n+ * Method: removeTimeout\n+ * Private. Called by mousemove() to remove the drag timeout.\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+ },\n \n-/**\n- * Class: OpenLayers.Symbolizer.Text\n- * A symbolizer used to render text labels for features.\n- */\n-OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, {\n+ /**\n+ * Method: mouseup\n+ * Handle mouseup events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n+ */\n+ mouseup: function(evt) {\n+ return this.dragend(evt);\n+ },\n \n- /** \n- * APIProperty: label\n- * {String} The text for the label.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: fontFamily\n- * {String} The font family for the label.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ /**\n+ * Method: mouseout\n+ * Handle mouseout events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\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+ }\n+ }\n+ }\n+ return true;\n+ },\n \n- /** \n- * APIProperty: fontSize\n- * {String} The font size for the label.\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- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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 \n- /** \n- * APIProperty: fontWeight\n- * {String} The font weight for the label.\n+ /**\n+ * Method: activate\n+ * Activate the handler.\n * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * Property: fontStyle\n- * {String} The font style for the label.\n+ * Method: deactivate \n+ * Deactivate the handler.\n * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * Constructor: OpenLayers.Symbolizer.Text\n- * Create a symbolizer for rendering text labels.\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- * 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 text symbolizer.\n+ * evt - {Object}\n */\n- initialize: function(config) {\n- OpenLayers.Symbolizer.prototype.initialize.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.Symbolizer.Text\"\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+ /**\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/Symbolizer/Raster.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- * @requires OpenLayers/Symbolizer.js\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 \n /**\n- * Class: OpenLayers.Symbolizer.Raster\n- * A symbolizer used to render raster images.\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+ * constructor.\n+ *\n+ * Inherits from:\n+ * - \n */\n-OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, {\n+OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Constructor: OpenLayers.Symbolizer.Raster\n- * Create a symbolizer for rendering rasters.\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 raster symbolizer.\n+ * Property: element\n+ * {DOMElement} The DOM element that contains the overview map\n */\n- initialize: function(config) {\n- OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n+ element: null,\n+\n+ /**\n+ * APIProperty: ovmap\n+ * {} A reference to the overview map itself.\n+ */\n+ ovmap: null,\n+\n+ /**\n+ * APIProperty: size\n+ * {} 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- CLASS_NAME: \"OpenLayers.Symbolizer.Raster\"\n+ /**\n+ * APIProperty: layers\n+ * {Array()} 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-});\n-/* ======================================================================\n- OpenLayers/Style2.js\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+ * property. Default is 15 pixels.\n+ */\n+ minRectSize: 15,\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: minRectDisplayClass\n+ * {String} Replacement style class name for the extent rectangle when\n+ * 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-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Rule.js\n- * @requires OpenLayers/Symbolizer/Point.js\n- * @requires OpenLayers/Symbolizer/Line.js\n- * @requires OpenLayers/Symbolizer/Polygon.js\n- * @requires OpenLayers/Symbolizer/Text.js\n- * @requires OpenLayers/Symbolizer/Raster.js\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-/**\n- * Class: OpenLayers.Style2\n- * This class represents a collection of rules for rendering features.\n- */\n-OpenLayers.Style2 = OpenLayers.Class({\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 \n /**\n- * Property: id\n- * {String} A unique id for this session.\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- id: null,\n+ mapOptions: null,\n \n /**\n- * APIProperty: name\n- * {String} Style identifier.\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- name: null,\n+ autoPan: false,\n \n /**\n- * APIProperty: title\n- * {String} Title of this style.\n+ * Property: handlers\n+ * {Object}\n */\n- title: null,\n+ handlers: null,\n \n /**\n- * APIProperty: description\n- * {String} Description of this style.\n+ * Property: resolutionFactor\n+ * {Object}\n */\n- description: null,\n+ resolutionFactor: 1,\n \n /**\n- * APIProperty: layerName\n- * {} Name of the layer that this style belongs to, usually\n- * according to the NamedLayer attribute of an SLD document.\n+ * APIProperty: maximized\n+ * {Boolean} Start as maximized (visible). Defaults to false.\n */\n- layerName: null,\n+ maximized: false,\n \n /**\n- * APIProperty: isDefault\n- * {Boolean}\n+ * APIProperty: maximizeTitle\n+ * {String} This property is used for showing a tooltip over the \n+ * maximize div. Defaults to \"\" (no title).\n */\n- isDefault: false,\n+ maximizeTitle: \"\",\n \n- /** \n- * APIProperty: rules \n- * {Array()} Collection of rendering rules.\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- rules: null,\n+ minimizeTitle: \"\",\n \n- /** \n- * Constructor: OpenLayers.Style2\n- * Creates a style representing a collection of rendering rules.\n+ /**\n+ * Constructor: OpenLayers.Control.OverviewMap\n+ * Create a new overview map\n *\n * Parameters:\n- * config - {Object} An object containing properties to be set on the \n- * style. Any documented properties may be set at construction.\n- *\n- * Returns:\n- * {} A new style object.\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 as one of the options properties.\n */\n- initialize: function(config) {\n- OpenLayers.Util.extend(this, config);\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ initialize: function(options) {\n+ this.layers = [];\n+ this.handlers = {};\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n },\n \n- /** \n+ /**\n * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n+ * Deconstruct the control\n */\n destroy: function() {\n- for (var i = 0, len = this.rules.length; i < len; i++) {\n- this.rules[i].destroy();\n+ if (!this.mapDiv) { // we've already been destroyed\n+ return;\n }\n- delete this.rules;\n- },\n-\n- /**\n- * APIMethod: clone\n- * Clones this style.\n- * \n- * Returns:\n- * {} Clone of this style.\n- */\n- clone: function() {\n- var config = OpenLayers.Util.extend({}, this);\n- // clone rules\n- if (this.rules) {\n- config.rules = [];\n- for (var i = 0, len = this.rules.length; i < len; ++i) {\n- config.rules.push(this.rules[i].clone());\n- }\n+ if (this.handlers.click) {\n+ this.handlers.click.destroy();\n+ }\n+ if (this.handlers.drag) {\n+ this.handlers.drag.destroy();\n }\n- return new OpenLayers.Style2(config);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Style2\"\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+ this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle);\n+ this.extentRectangle = null;\n \n-OpenLayers.Spherical = OpenLayers.Spherical || {};\n+ if (this.rectEvents) {\n+ this.rectEvents.destroy();\n+ this.rectEvents = null;\n+ }\n \n-OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;\n+ if (this.ovmap) {\n+ this.ovmap.destroy();\n+ this.ovmap = null;\n+ }\n \n-/**\n- * APIFunction: computeDistanceBetween\n- * Computes the distance between two LonLats.\n- *\n- * Parameters:\n- * from - {} or {Object} Starting point. A LonLat or\n- * a JavaScript literal with lon lat properties.\n- * to - {} 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+ this.element.removeChild(this.mapDiv);\n+ this.mapDiv = null;\n \n+ this.div.removeChild(this.element);\n+ this.element = null;\n \n-/**\n- * APIFunction: computeHeading\n- * Computes the heading from one LonLat to another LonLat.\n- *\n- * Parameters:\n- * from - {} or {Object} Starting point. A LonLat or\n- * a JavaScript literal with lon lat properties.\n- * to - {} 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/Protocol.js\n- ====================================================================== */\n+ if (this.maximizeDiv) {\n+ this.div.removeChild(this.maximizeDiv);\n+ this.maximizeDiv = null;\n+ }\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under 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.minimizeDiv) {\n+ this.div.removeChild(this.minimizeDiv);\n+ this.minimizeDiv = null;\n+ }\n \n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n+ this.map.events.un({\n+ buttonclick: this.onButtonClick,\n+ moveend: this.update,\n+ changebaselayer: this.baseLayerDraw,\n+ scope: this\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+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n \n /**\n- * Property: format\n- * {} The format used by this protocol.\n+ * Method: draw\n+ * Render the control in the browser.\n */\n- format: null,\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- /**\n- * Property: options\n- * {Object} Any options sent to the constructor.\n- */\n- options: null,\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- /**\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+ 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- /**\n- * Property: defaultFilter\n- * {} Optional default filter to read requests\n- */\n- defaultFilter: null,\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- /**\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+ this.element.appendChild(this.mapDiv);\n \n- /**\n- * Method: mergeWithDefaultFilter\n- * Merge filter passed to the read method with the default one\n- *\n- * Parameters:\n- * 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+ 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- merged = filter || this.defaultFilter || undefined;\n+ // show the overview map\n+ this.element.style.display = '';\n }\n- return merged;\n+ if (this.map.getExtent()) {\n+ this.update();\n+ }\n+\n+ this.map.events.on({\n+ buttonclick: this.onButtonClick,\n+ moveend: this.update,\n+ scope: this\n+ });\n+\n+ if (this.maximized) {\n+ this.maximizeControl();\n+ }\n+ return this.div;\n },\n \n /**\n- * APIMethod: destroy\n- * Clean up the protocol.\n+ * Method: baseLayerDraw\n+ * Draw the base layer - called if unable to complete in the initial draw\n */\n- destroy: function() {\n- this.options = null;\n- this.format = null;\n+ baseLayerDraw: function() {\n+ this.draw();\n+ this.map.events.unregister(\"changebaselayer\", this, this.baseLayerDraw);\n },\n \n /**\n- * APIMethod: read\n- * Construct a request for reading new features.\n+ * Method: rectDrag\n+ * Handle extent rectangle drag\n *\n * Parameters:\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {} An \n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n+ * px - {} The pixel location of the drag.\n */\n- read: function(options) {\n- options = options || {};\n- options.filter = this.mergeWithDefaultFilter(options.filter);\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 /**\n- * APIMethod: create\n- * Construct a request for writing newly created features.\n+ * Method: mapDivClick\n+ * Handle browser events\n *\n * Parameters:\n- * features - {Array({})} or\n- * {}\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {} An \n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n+ * evt - {} evt\n */\n- create: function() {},\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: update\n- * Construct a request updating modified features.\n+ * Method: onButtonClick\n *\n * Parameters:\n- * features - {Array({})} or\n- * {}\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {} An \n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n+ * evt - {Event}\n */\n- update: function() {},\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+ },\n \n /**\n- * APIMethod: delete\n- * Construct a request deleting a removed feature.\n+ * Method: maximizeControl\n+ * Unhide the control. Called when the control is in the map viewport.\n *\n * Parameters:\n- * feature - {}\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {} An \n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n+ * e - {}\n */\n- \"delete\": function() {},\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- * 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+ * Method: minimizeControl\n+ * Hide all the contents of the control, shrink the size, \n+ * add the maximize icon\n+ * \n * Parameters:\n- * features - {Array({})}\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({})} An array of\n- * objects.\n+ * e - {}\n */\n- commit: function() {},\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+ },\n \n /**\n- * Method: abort\n- * Abort an ongoing request.\n+ * Method: showToggle\n+ * Hide/Show the toggle depending on whether the control is minimized\n *\n * Parameters:\n- * response - {}\n+ * minimize - {Boolean} \n */\n- abort: function(response) {},\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: 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 - {} The protocol response object.\n- * options - {Object} Options sent to the protocol method\n+ * Method: update\n+ * Update the overview map after layers move.\n */\n- createCallback: function(method, response, options) {\n- return OpenLayers.Function.bind(function() {\n- method.apply(this, [response, options]);\n- }, this);\n- },\n+ update: function() {\n+ if (this.ovmap == null) {\n+ this.createMap();\n+ }\n \n- CLASS_NAME: \"OpenLayers.Protocol\"\n-});\n+ if (this.autoPan || !this.isSuitableOverview()) {\n+ this.updateOverview();\n+ }\n+\n+ // update extent rectangle\n+ this.updateRectToMap();\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+ * Method: isSuitableOverview\n+ * Determines if the overview map is suitable given the extent and\n+ * resolution of the main map.\n */\n- code: null,\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- * Property: requestType\n- * {String} The type of request this response corresponds to. Either\n- * \"create\", \"read\", \"update\" or \"delete\".\n+ * Method updateOverview\n+ * Called by if returns true\n */\n- requestType: null,\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- * Property: last\n- * {Boolean} - true if this is the last response expected in a commit,\n- * false otherwise, defaults to true.\n+ * Method: createMap\n+ * Construct the map that this control contains\n */\n- last: true,\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+ // 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+ 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+ 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- * Property: features\n- * {Array({})} or {}\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+ * Method: updateRectToMap\n+ * Updates the extent rectangle position and size to match the map extent\n */\n- features: null,\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- * 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+ * Method: updateMapToRect\n+ * Updates the map extent to match the extent rectangle position and size\n */\n- data: null,\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- * Property: reqFeatures\n- * {Array({})} or {}\n- * The features provided by the user and placed in the request by the\n- * protocol.\n+ * Method: setRectPxBounds\n+ * Set extent rectangle pixel bounds.\n+ *\n+ * Parameters:\n+ * pxBounds - {}\n */\n- reqFeatures: null,\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- * Property: priv\n+ * Method: getRectBoundsFromMapBounds\n+ * Get the rect bounds from the map bounds.\n+ *\n+ * Parameters:\n+ * lonLatBounds - {}\n+ *\n+ * Returns:\n+ * {}A bounds which is the passed-in map lon/lat extent\n+ * translated into pixel bounds for the overview map\n */\n- priv: null,\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- * Property: error\n- * {Object} The error object in case a service exception was encountered.\n+ * Method: getMapBoundsFromRectBounds\n+ * Get the map bounds from the rect bounds.\n+ *\n+ * Parameters:\n+ * pxBounds - {}\n+ *\n+ * Returns:\n+ * {} Bounds which is the passed-in overview rect bounds\n+ * translated into lon/lat bounds for the overview map\n */\n- error: null,\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- * Constructor: OpenLayers.Protocol.Response\n+ * Method: getLonLatFromOverviewPx\n+ * Get a map location from a pixel location\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * overviewMapPx - {|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- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\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: success\n+ * Method: getOverviewPxFromLonLat\n+ * Get a pixel location from a map location\n+ *\n+ * Parameters:\n+ * lonlat - {|Object} OpenLayers.LonLat or an\n+ * object with a 'lon' and 'lat' properties.\n *\n * Returns:\n- * {Boolean} - true on success, false otherwise\n+ * {Object} Location which is the passed-in OpenLayers.LonLat, \n+ * translated into overview map pixels\n */\n- success: function() {\n- return this.code > 0;\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.Protocol.Response\"\n+ CLASS_NAME: 'OpenLayers.Control.OverviewMap'\n });\n-\n-OpenLayers.Protocol.Response.SUCCESS = 1;\n-OpenLayers.Protocol.Response.FAILURE = 0;\n /* ======================================================================\n- OpenLayers/Strategy.js\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/BaseTypes/Class.js\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.Strategy\n- * Abstract vector layer strategy class. Not to be instantiated directly. Use\n- * one of the strategy subclasses instead.\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+ * constructor.\n+ *\n+ * Inherits from:\n+ * - \n */\n-OpenLayers.Strategy = OpenLayers.Class({\n+OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n \n /**\n- * Property: layer\n- * {} The layer this strategy belongs to.\n+ * APIProperty: events\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 map event types (in addition to those from ):\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- layer: null,\n \n /**\n- * Property: options\n- * {Object} Any options sent to the constructor.\n+ * APIProperty: isBaseLayer\n+ * {Boolean} The layer is a base layer. Default is false. Set this property\n+ * in the layer options.\n */\n- options: null,\n+ isBaseLayer: false,\n \n /** \n- * Property: active \n- * {Boolean} The control is active.\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- active: null,\n+ isFixed: false,\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+ * APIProperty: features\n+ * {Array()} \n */\n- autoActivate: true,\n+ features: 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+ /** \n+ * Property: filter\n+ * {} The filter set in this layer,\n+ * a strategy launching read requests can combined\n+ * this filter with its own filter.\n */\n- autoDestroy: true,\n+ filter: null,\n+\n+ /** \n+ * Property: selectedFeatures\n+ * {Array()} \n+ */\n+ selectedFeatures: null,\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+ * Property: unrenderedFeatures\n+ * {Object} hash of features, keyed by feature.id, that the renderer\n+ * failed to draw\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+ unrenderedFeatures: null,\n \n /**\n- * APIMethod: destroy\n- * Clean up the strategy.\n+ * APIProperty: reportError\n+ * {Boolean} report friendly error message when loading of renderer\n+ * fails.\n */\n- destroy: function() {\n- this.deactivate();\n- this.layer = null;\n- this.options = null;\n- },\n+ reportError: true,\n+\n+ /** \n+ * APIProperty: style\n+ * {Object} Default style for the layer\n+ */\n+ style: null,\n \n /**\n- * Method: setLayer\n- * Called to set the property.\n- *\n- * Parameters:\n- * layer - {}\n+ * Property: styleMap\n+ * {}\n */\n- setLayer: function(layer) {\n- this.layer = layer;\n- },\n+ styleMap: null,\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+ * Property: strategies\n+ * {Array(})} Optional list of strategies for the layer.\n */\n- activate: function() {\n- if (!this.active) {\n- this.active = true;\n- return true;\n- }\n- return false;\n- },\n+ strategies: null,\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+ * Property: protocol\n+ * {} Optional protocol for the layer.\n */\n- deactivate: function() {\n- if (this.active) {\n- this.active = false;\n- return true;\n- }\n- return false;\n- },\n+ protocol: null,\n \n- CLASS_NAME: \"OpenLayers.Strategy\"\n-});\n-/* ======================================================================\n- OpenLayers/StyleMap.js\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-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: renderer\n+ * {}\n+ */\n+ renderer: null,\n \n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Style.js\n- * @requires OpenLayers/Feature/Vector.js\n- */\n+ /**\n+ * APIProperty: rendererOptions\n+ * {Object} Options for the renderer. See {} for\n+ * supported options.\n+ */\n+ rendererOptions: null,\n \n-/**\n- * Class: OpenLayers.StyleMap\n- */\n-OpenLayers.StyleMap = OpenLayers.Class({\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: styles\n- * {Object} Hash of {}, keyed by names of well known\n- * rendering intents (e.g. \"default\", \"temporary\", \"select\", \"delete\").\n+ /** \n+ * Property: drawn\n+ * {Boolean} Whether the Vector Layer features have been drawn yet.\n */\n- styles: null,\n+ drawn: false,\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+ * 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- extendDefault: true,\n+ ratio: 1,\n \n /**\n- * Constructor: OpenLayers.StyleMap\n- * \n+ * Constructor: OpenLayers.Layer.Vector\n+ * Create a new vector layer\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+ * 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+ * {} A new vector layer\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+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\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+ // 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- OpenLayers.Util.extend(this, options);\n+\n },\n \n /**\n- * Method: destroy\n+ * APIMethod: destroy\n+ * Destroy this layer\n */\n destroy: function() {\n- for (var key in this.styles) {\n- this.styles[key].destroy();\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- this.styles = null;\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: createSymbolizer\n- * Creates the symbolizer for a feature for a render intent.\n- * \n- * Parameters:\n- * 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+ * Method: clone\n+ * Create a clone of this layer.\n * \n+ * Note: Features of the layer are also cloned.\n+ *\n * Returns:\n- * {Object} symbolizer hash\n+ * {} An exact clone of this layer\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+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());\n }\n- feature.renderIntent = intent;\n- var defaultSymbolizer = {};\n- if (this.extendDefault && intent != \"default\") {\n- defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature);\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- return OpenLayers.Util.extend(defaultSymbolizer,\n- this.styles[intent].createSymbolizer(feature));\n+ obj.features = clonedFeatures;\n+\n+ return obj;\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+ * 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- * 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+ * obj - {Object} Optional object with properties for any listener of\n+ * the refresh event.\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+ refresh: function(obj) {\n+ if (this.calculateInRange() && this.visibility) {\n+ this.events.triggerEvent(\"refresh\", obj);\n }\n- this.styles[renderIntent].addRules(rules);\n },\n \n- CLASS_NAME: \"OpenLayers.StyleMap\"\n-});\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+ * Method: assignRenderer\n+ * Iterates through the available renderer implementations and selects \n+ * and assigns the first one whose \"supported()\" function returns true.\n */\n- threshold: 0,\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: deceleration\n- * {Float} the deseleration in px/ms\u00b2, default to 0.0035.\n+ /** \n+ * Method: displayError \n+ * Let the user know their browser isn't supported.\n */\n- deceleration: 0.0035,\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: nbPoints\n- * {Integer} the number of points we use to calculate the kinetic\n- * initial values.\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 - {} \n */\n- nbPoints: 100,\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\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+ if (!this.renderer) {\n+ this.map.removeLayer(this);\n+ } else {\n+ this.renderer.map = this.map;\n \n- /**\n- * Property: points\n- * List of points use to calculate the kinetic initial values.\n- */\n- points: undefined,\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: timerId\n- * ID of the timer.\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- timerId: undefined,\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- * Constructor: OpenLayers.Kinetic\n+ * Method: removeMap\n+ * The layer has been removed from the map.\n *\n * Parameters:\n- * options - {Object}\n+ * map - {}\n */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\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: begin\n- * Begins the dragging.\n+ * Method: onMapResize\n+ * Notify the renderer of the change in size. \n+ * \n */\n- begin: function() {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = undefined;\n- this.points = [];\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: update\n- * Updates during the dragging.\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- * xy - {} The new position.\n+ * bounds - {} \n+ * zoomChanged - {Boolean} \n+ * dragging - {Boolean} \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+ 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- * Method: end\n- * Ends the dragging, start the kinetic.\n- *\n+ /** \n+ * APIMethod: display\n+ * Hide or show the Layer\n+ * \n * Parameters:\n- * xy - {} 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+ * display - {Boolean}\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+ 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- return {\n- speed: speed,\n- theta: theta\n- };\n },\n \n /**\n- * Method: move\n- * Launch the kinetic move pan.\n+ * APIMethod: addFeatures\n+ * Add Features to the layer.\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+ * features - {Array()} \n+ * options - {Object}\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+ addFeatures: function(features, options) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n \n- var timerCallback = function() {\n- if (this.timerId == null) {\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- var t = new Date().getTime() - initialTime;\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- var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;\n- var x = p * fx;\n- var y = p * fy;\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- var args = {};\n- args.end = false;\n- var v = -this.deceleration * t + v0;\n+ //give feature reference to its layer\n+ feature.layer = this;\n \n- if (v <= 0) {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = null;\n- args.end = true;\n+ if (!feature.style && this.style) {\n+ feature.style = OpenLayers.Util.extend({}, this.style);\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+ if (notify) {\n+ if (this.events.triggerEvent(\"beforefeatureadded\", {\n+ feature: feature\n+ }) === false) {\n+ continue;\n+ }\n+ this.preFeatureInsert(feature);\n+ }\n \n- CLASS_NAME: \"OpenLayers.Kinetic\"\n-});\n-/* ======================================================================\n- OpenLayers/Events/featureclick.js\n- ====================================================================== */\n+ featuresAdded.push(feature);\n+ this.features.push(feature);\n+ this.drawFeature(feature);\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under 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 (notify) {\n+ this.events.triggerEvent(\"featureadded\", {\n+ feature: feature\n+ });\n+ this.onFeatureInsert(feature);\n+ }\n+ }\n \n-/**\n- * @requires OpenLayers/Events.js\n- */\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresadded\", {\n+ features: featuresAdded\n+ });\n+ }\n+ },\n \n-/**\n- * Class: OpenLayers.Events.featureclick\n- *\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.Events.featureclick = OpenLayers.Class({\n \n /**\n- * Property: cache\n- * {Object} A cache of features under the mouse.\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()} 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- cache: null,\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- /**\n- * Property: map\n- * {} The map to register browser events on.\n- */\n- map: null,\n+ var notify = !options || !options.silent;\n \n- /**\n- * Property: provides\n- * {Array(String)} The event types provided by this extension.\n- */\n- provides: [\"featureclick\", \"nofeatureclick\", \"featureover\", \"featureout\"],\n+ if (notify) {\n+ this.events.triggerEvent(\n+ \"beforefeaturesremoved\", {\n+ features: features\n+ }\n+ );\n+ }\n \n- /**\n- * Constructor: OpenLayers.Events.featureclick\n- * Create a new featureclick event type.\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- * target - {} The events instance to create the events\n- * for.\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- 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+ 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- } 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+ 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: setMap\n+ * APIMethod: destroyFeatures\n+ * Erase and destroy features on the layer.\n *\n * Parameters:\n- * map - {} The map to register browser events on.\n+ * features - {Array()} 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- 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+ 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: start\n- * Sets startEvt = evt.\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 - {}\n+ * Parameters: \n+ * feature - {} \n+ * style - {String | Object} Named render intent or full symbolizer object.\n */\n- start: function(evt) {\n- this.startEvt = evt;\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: cancel\n- * Deletes the start event.\n+ * Method: eraseFeatures\n+ * Erase features from the layer.\n *\n * Parameters:\n- * evt - {}\n+ * features - {Array()} \n */\n- cancel: function(evt) {\n- delete this.startEvt;\n+ eraseFeatures: function(features) {\n+ this.renderer.eraseFeatures(features);\n },\n \n /**\n- * Method: onClick\n- * Listener for the click event.\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 - {}\n+ * evt - {Event} \n+ *\n+ * Returns:\n+ * {} A feature if one was under the event.\n */\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- if (more === false) {\n- break;\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- // 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+ 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: onMousemove\n- * Listener for the mousemove event.\n+ * APIMethod: getFeatureBy\n+ * Given a property value, return the feature if it exists in the features array\n *\n * Parameters:\n- * evt - {}\n+ * property - {String}\n+ * value - {String}\n+ *\n+ * Returns:\n+ * {} A feature corresponding to the given\n+ * property value or null if there is no such feature.\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+ 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: triggerEvent\n- * Determines where to trigger the event and triggers it.\n+ * APIMethod: getFeatureById\n+ * Given a feature id, return the feature if it exists in the features array\n *\n * Parameters:\n- * type - {String} The event type to trigger\n- * evt - {Object} The listener argument\n+ * featureId - {String}\n *\n * Returns:\n- * {Boolean} The last listener return.\n+ * {} A feature corresponding to the given\n+ * featureId or null if there is no such feature.\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+ getFeatureById: function(featureId) {\n+ return this.getFeatureBy('id', featureId);\n },\n \n /**\n- * Method: getFeatures\n- * Get all features at the given screen location.\n+ * APIMethod: getFeatureByFid\n+ * Given a feature fid, return the feature if it exists in the features array\n *\n * Parameters:\n- * evt - {Object} Event object.\n+ * featureFid - {String}\n *\n * Returns:\n- * {Array()} List of features at the given point.\n+ * {} A feature corresponding to the given\n+ * featureFid or null if there is no such feature.\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+ 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({}) 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- // 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 foundFeatures;\n },\n \n /**\n- * APIMethod: destroy\n- * Clean up.\n- */\n- destroy: function() {\n- for (var i = this.provides.length - 1; i >= 0; --i) {\n- delete this.target.extensions[this.provides[i]];\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.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+ this.map.featureSelection = [];\n+ },\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+ * 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 - {} \n+ */\n+ onFeatureInsert: function(feature) {},\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+ * 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 - {} \n+ */\n+ preFeatureInsert: function(feature) {},\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+ * APIMethod: getDataExtent\n+ * Calculates the max extent which includes all of the features.\n+ * \n+ * Returns:\n+ * {} 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/Events/buttonclick.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- * @requires OpenLayers/Events.js\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.Events.buttonclick\n- * Extension event type for handling buttons on top of a dom element. This\n- * event type fires \"buttonclick\" on its 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 .\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+ * Class: OpenLayers.Control.Graticule\n+ * The Graticule displays a grid of latitude/longitude lines reprojected on\n+ * the map. \n+ * \n+ * Inherits from:\n+ * - \n+ * \n */\n-OpenLayers.Events.buttonclick = OpenLayers.Class({\n+OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Property: target\n- * {} The events instance that the buttonclick event will\n- * be triggered on.\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true. \n */\n- target: null,\n+ autoActivate: true,\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+ * APIProperty: intervals\n+ * {Array(Float)} A list of possible graticule widths in degrees.\n */\n- events: [\n- 'mousedown', 'mouseup', 'click', 'dblclick',\n- 'touchstart', 'touchmove', 'touchend', 'keydown'\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: startRegEx\n- * {RegExp} Regular expression to test Event.type for events that start\n- * a buttonclick sequence.\n+ * APIProperty: displayInLayerSwitcher\n+ * {Boolean} Allows the Graticule control to be switched on and off by \n+ * LayerSwitcher control. Defaults is true.\n */\n- startRegEx: /^mousedown|touchstart$/,\n+ displayInLayerSwitcher: true,\n \n /**\n- * Property: cancelRegEx\n- * {RegExp} Regular expression to test Event.type for events that cancel\n- * a buttonclick sequence.\n+ * APIProperty: visible\n+ * {Boolean} should the graticule be initially visible (default=true)\n */\n- cancelRegEx: /^touchmove$/,\n+ visible: true,\n \n /**\n- * Property: completeRegEx\n- * {RegExp} Regular expression to test Event.type for events that complete\n- * a buttonclick sequence.\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- completeRegEx: /^mouseup|touchend$/,\n+ numPoints: 50,\n \n /**\n- * Property: startEvt\n- * {Event} The event that started the click sequence\n+ * APIProperty: targetSize\n+ * {Integer} The maximum size of the grid in pixels on the map\n */\n+ targetSize: 200,\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- * instances.\n- *\n+ * APIProperty: layerName\n+ * {String} The name to be displayed in the layer switcher, default is set \n+ * by {}.\n+ */\n+ layerName: null,\n+\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+ * 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+ * APIProperty: labelSymbolizer\n+ * {symbolizer} the symbolizer used to render labels\n+ */\n+ labelSymbolizer: {},\n+\n+ /**\n+ * Property: gratLayer\n+ * {} 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- * target - {} The events instance that the buttonclick\n- * event will be triggered on.\n+ * options - {Object} An optional object whose properties will be used\n+ * to extend the control.\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+ initialize: function(options) {\n+ options = options || {};\n+ options.layerName = options.layerName || OpenLayers.i18n(\"Graticule\");\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\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 /**\n- * Method: destroy\n+ * APIMethod: 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+ this.deactivate();\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ if (this.gratLayer) {\n+ this.gratLayer.destroy();\n+ this.gratLayer = null;\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+ * Method: draw\n *\n+ * initializes the graticule layer and does the initial update\n+ * \n * Returns:\n- * {DOMElement} The button element, or undefined.\n+ * {DOMElement}\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+ 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 /**\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+ * APIMethod: activate\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+ 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: buttonClick\n- * Check if a button was clicked, and fire the buttonclick event\n+ * APIMethod: deactivate\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+ },\n+ /**\n+ * Method: update\n *\n- * Parameters:\n- * evt - {Event}\n+ * calculates the grid to be displayed and actually draws it\n+ * \n+ * Returns:\n+ * {DOMElement}\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+ 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- 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+ //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+\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+ //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+ /* 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+ //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+ /* 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+ //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- if (this.startRegEx.test(evt.type)) {\n- this.startEvt = evt;\n- OpenLayers.Event.stop(evt);\n- propagate = false;\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- } else {\n- propagate = !this.ignore(OpenLayers.Event.element(evt));\n- delete this.startEvt;\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- return propagate;\n- }\n+ this.gratLayer.addFeatures(lines);\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Control.Graticule\"\n });\n+\n /* ======================================================================\n- OpenLayers/Format/WMSGetFeatureInfo.js\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/Format/XML.js\n+ * @requires OpenLayers/Handler.js\n */\n \n /**\n- * Class: OpenLayers.Format.WMSGetFeatureInfo\n- * Class to read GetFeatureInfo responses from Web Mapping Services\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- * - \n+ * - \n */\n-OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, {\n+OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {\n \n /**\n- * APIProperty: layerIdentifier\n- * {String} All xml nodes containing this search criteria will populate an\n- * internal array of layer nodes.\n+ * APIProperty: delay\n+ * {Integer} - Number of milliseconds between mousemoves before\n+ * the event is considered a hover. Default is 500.\n */\n- layerIdentifier: '_layer',\n+ delay: 500,\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+ * APIProperty: pixelTolerance\n+ * {Integer} - Maximum number of pixels between mousemoves for\n+ * an event to be considered a hover. Default is null.\n */\n- featureIdentifier: '_feature',\n+ pixelTolerance: null,\n \n /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n+ * APIProperty: stopMove\n+ * {Boolean} - Stop other listeners from being notified on mousemoves.\n+ * Default is false.\n */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n+ stopMove: false,\n \n /**\n- * Property: gmlFormat\n- * {} internal GML format for parsing geometries\n- * in msGMLOutput\n+ * Property: px\n+ * {} - The location of the last mousemove, expressed\n+ * in pixels.\n */\n- gmlFormat: null,\n+ px: null,\n \n /**\n- * Constructor: OpenLayers.Format.WMSGetFeatureInfo\n- * Create a new parser for WMS GetFeatureInfo responses\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 - {} 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- * this instance.\n+ * the handler.\n */\n \n /**\n- * APIMethod: read\n- * Read WMS GetFeatureInfo data from a string, and return an array of features\n+ * Method: mousemove\n+ * Called when the mouse moves on the map.\n *\n * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n+ * evt - {}\n *\n * Returns:\n- * {Array()} An array of features.\n+ * {Boolean} Continue propagating this event.\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+ 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 result;\n+ return !this.stopMove;\n },\n \n-\n /**\n- * Method: read_msGMLOutput\n- * Parse msGMLOutput nodes.\n+ * Method: mouseout\n+ * Called when the mouse goes out of the map.\n *\n * Parameters:\n- * data - {DOMElement}\n+ * evt - {}\n *\n * Returns:\n- * {Array}\n+ * {Boolean} Continue propagating this event.\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+ mouseout: function(evt) {\n+ if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n+ this.clearTimer();\n+ this.callback('move', [evt]);\n }\n- return response;\n+ return true;\n },\n \n /**\n- * Method: read_FeatureInfoResponse\n- * Parse FeatureInfoResponse nodes.\n+ * Method: passesTolerance\n+ * Determine whether the mouse move is within the optional pixel tolerance.\n *\n * Parameters:\n- * data - {DOMElement}\n+ * px - {}\n *\n * Returns:\n- * {Array}\n+ * {Boolean} The mouse move is within the pixel tolerance.\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+ 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 response;\n+ return passes;\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+ * Method: clearTimer\n+ * Clear the timer and set to null.\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+ clearTimer: function() {\n+ if (this.timerId != null) {\n+ window.clearTimeout(this.timerId);\n+ this.timerId = null;\n }\n- return nodes;\n },\n \n /**\n- * Method: parseAttributes\n+ * Method: delayedCall\n+ * Triggers pause callback.\n *\n * Parameters:\n- * node - {}\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+ * evt - {}\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+ delayedCall: function(evt) {\n+ this.callback('pause', [evt]);\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 - {}\n+ * APIMethod: deactivate\n+ * Deactivate the handler.\n *\n * Returns:\n- * {Object} An object containing the geometry and the feature bounds\n+ * {Boolean} The handler was successfully deactivated.\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+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.clearTimer();\n+ deactivated = true;\n }\n- return {\n- geometry: geometry,\n- bounds: bounds\n- };\n+ return deactivated;\n },\n \n- CLASS_NAME: \"OpenLayers.Format.WMSGetFeatureInfo\"\n-\n+ CLASS_NAME: \"OpenLayers.Handler.Hover\"\n });\n /* ======================================================================\n- OpenLayers/Format/WMSDescribeLayer.js\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/Format/XML/VersionedOGC.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Hover.js\n+ * @requires OpenLayers/Handler/Click.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+ * 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- * - \n+ * - \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+OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, {\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 {} 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+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n */\n+ autoActivate: true,\n \n- CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer\"\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- * - \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+ * 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} \n */\n- layerOptions: null,\n+ layers: 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+ /* Property: defaultHandlerOptions\n+ * The default opts passed to the handler constructors\n */\n- layerParams: 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 \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+ /* APIProperty: handlerMode\n+ * Defaults to 'click'. Can be 'hover' or 'move'.\n */\n+ handlerMode: 'click',\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+ * APIMethod: setHandler\n+ * sets this.handlerMode and calls resetHandler()\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- * , the layers from the context document will be added\n- * to the map.\n- *\n- * Returns:\n- * {} A map based on the context.\n+ * hm - {String} Handler Mode string; 'click', 'hover' or 'move'.\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+ setHandler: function(hm) {\n+ this.handlerMode = hm;\n+ this.resetHandler();\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+ * Method: resetHandler\n+ * Deactivates the old hanlder and creates a new\n+ * based on the mode specified in\n+ * this.handlerMode\n *\n- * Returns:\n- * {} 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+ resetHandler: function() {\n+ if (this.handler) {\n+ this.handler.deactivate();\n+ this.handler.destroy();\n+ this.handler = null;\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+ 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 (layerContext.features) {\n- // inline GML or KML features\n- layer = new OpenLayers.Layer.Vector(\n- layerContext.title || layerContext.name,\n- options\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- 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+ } 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- return layer;\n+ if (this.handler) {\n+ return true;\n+ } else {\n+ return false;\n+ }\n },\n \n /**\n- * Method: getLayersFromContext\n- * Create an array of layers from an array of layerContext objects.\n+ * Constructor: \n *\n * Parameters:\n- * layersContext - {Array(Object)} An array of objects representing layers.\n- *\n- * Returns:\n- * {Array()} An array of layers.\n+ * options - {Object} \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+ 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: contextToMap\n- * Create a map given a context object.\n+ * Method: handleEvent\n+ * Internal method called when specified event is triggered.\n+ * \n+ * This method does several things:\n *\n- * Parameters:\n- * context - {Object} The context object.\n- * options - {Object} Default map options.\n+ * Gets the lonLat of the event.\n *\n- * Returns:\n- * {} A map based on the context object.\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 - {} \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+ handleEvent: function(evt) {\n+ if (evt == null) {\n+ this.reset();\n+ return;\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+ var lonLat = this.map.getLonLatFromPixel(evt.xy);\n+ if (!lonLat) {\n+ return;\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+ 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- * Method: mergeContextToMap\n- * Add layers from a context object to a map.\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- * context - {Object} The context object.\n- * map - {} The map.\n- *\n- * Returns:\n- * {} The same map with layers added.\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- mergeContextToMap: function(context, map) {\n- map.addLayers(this.getLayersFromContext(context.layersContext));\n- return map;\n+ callback: function(infoLookup) {\n+ // to be provided in the constructor\n },\n \n /**\n- * APIMethod: write\n- * Write a context document given a map.\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- * obj - { | Object} A map or context object.\n- * options - {Object} Optional configuration object.\n+ * None\n *\n * Returns:\n- * {String} A context document string.\n+ * {Array} Layers to handle on each event\n */\n- write: function(obj, options) {\n- obj = this.toContext(obj);\n- return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this,\n- arguments);\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.Format.Context\"\n+ CLASS_NAME: \"OpenLayers.Control.UTFGrid\"\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/KML.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/BaseTypes/Date.js\n- * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Control.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 \n- * constructor. \n- * \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- * - \n+ * - \n */\n-OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {\n+OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n+ * Property: layer\n+ * {}\n */\n- namespaces: {\n- kml: \"http://www.opengis.net/kml/2.2\",\n- gx: \"http://www.google.com/kml/ext/2.2\"\n- },\n+ layer: null,\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+ * Property: callbacks\n+ * {Object} The functions that are sent to the handler for callback\n */\n- foldersName: \"OpenLayers export\",\n+ callbacks: null,\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+ * APIProperty: events\n+ * {} 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 ):\n+ * featureadded - Triggered when a feature is added\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+ * APIProperty: multi\n+ * {Boolean} Cast features to multi-part geometries before passing to the\n+ * layer. Default is false.\n */\n- kvpAttributes: false,\n+ multi: 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+ * APIProperty: featureAdded\n+ * {Function} Called after each feature is added\n */\n- extractStyles: false,\n+ featureAdded: function() {},\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+ * APIProperty: handlerOptions\n+ * {Object} Used to set non-default properties on the control's handler\n */\n- extractTracks: false,\n \n /**\n- * APIProperty: trackAttributes\n- * {Array} If 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 .\n+ * Constructor: OpenLayers.Control.DrawFeature\n+ * \n+ * Parameters:\n+ * layer - {} \n+ * handler - {} \n+ * options - {Object} \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+ 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 \n /**\n- * Property: features\n- * {Array} Array of features\n- * \n+ * Method: drawFeature\n */\n- features: null,\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 \n /**\n- * Property: styles\n- * {Object} Storage of style objects\n- * \n+ * APIMethod: insertXY\n+ * Insert a point in the current sketch given x & y coordinates.\n+ *\n+ * Parameters:\n+ * x - {Number} The x-coordinate of the point.\n+ * y - {Number} The y-coordinate of the point.\n */\n- styles: null,\n+ insertXY: function(x, y) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertXY(x, y);\n+ }\n+ },\n \n /**\n- * Property: styleBaseUrl\n- * {String}\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 */\n- styleBaseUrl: \"\",\n+ insertDeltaXY: function(dx, dy) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertDeltaXY(dx, dy);\n+ }\n+ },\n \n /**\n- * Property: fetched\n- * {Object} Storage of KML URLs that have been fetched before\n- * in order to prevent reloading them.\n+ * APIMethod: 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- fetched: null,\n+ insertDirectionLength: function(direction, length) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertDirectionLength(direction, length);\n+ }\n+ },\n \n /**\n- * APIProperty: maxDepth\n- * {Integer} Maximum depth for recursive loading external KML URLs \n- * Defaults to 0: do no external fetching\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+ * deflection - {Number} Degrees clockwise from the previous segment.\n+ * length - {Number} Distance from the previously drawn point.\n */\n- maxDepth: 0,\n+ insertDeflectionLength: function(deflection, length) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertDeflectionLength(deflection, length);\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Format.KML\n- * Create a new parser for KML.\n+ * APIMethod: undo\n+ * Remove the most recently added point in the current sketch geometry.\n *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n+ * Returns: \n+ * {Boolean} An edit was undone.\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+ undo: function() {\n+ return this.handler.undo && this.handler.undo();\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+ * APIMethod: redo\n+ * Reinsert the most recently removed point resulting from an call.\n+ * The undo stack is deleted whenever a point is added by other means.\n *\n- * Returns:\n- * {Array()} List of features.\n+ * Returns: \n+ * {Boolean} An edit was redone.\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+ redo: function() {\n+ return this.handler.redo && this.handler.redo();\n+ },\n \n- return this.parseData(data, options);\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+ */\n+ finishSketch: function() {\n+ this.handler.finishGeometry();\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()} List of features.\n+ * APIMethod: cancel\n+ * Cancel the current sketch. This removes the current sketch and keeps\n+ * the drawing control active.\n */\n- parseData: function(data, options) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n+ cancel: function() {\n+ this.handler.cancel();\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+ CLASS_NAME: \"OpenLayers.Control.DrawFeature\"\n+});\n+/* ======================================================================\n+ OpenLayers/Handler/Keyboard.js\n+ ====================================================================== */\n \n- var nodes = this.getElementsByTagNameNS(data, \"*\", type);\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- // skip to next type if no nodes are found\n- if (nodes.length == 0) {\n- continue;\n- }\n+/**\n+ * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Events.js\n+ */\n \n- switch (type.toLowerCase()) {\n+/**\n+ * Class: OpenLayers.handler.Keyboard\n+ * A handler for keyboard events. Create a new instance with the\n+ * constructor.\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, {\n \n- // Fetch external links \n- case \"link\":\n- case \"networklink\":\n- this.parseLinks(nodes, options);\n- break;\n+ /* http://www.quirksmode.org/js/keys.html explains key x-browser\n+ key handling quirks in pretty nice detail */\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+ * Constant: KEY_EVENTS\n+ * keydown, keypress, keyup\n+ */\n+ KEY_EVENTS: [\"keydown\", \"keyup\"],\n \n- // parse features\n- case \"placemark\":\n- this.parseFeatures(nodes, options);\n- break;\n- }\n- }\n+ /** \n+ * Property: eventListener\n+ * {Function}\n+ */\n+ eventListener: null,\n \n- return this.features;\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- * 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+ * Constructor: OpenLayers.Handler.Keyboard\n+ * Returns a new keyboard handler.\n * \n+ * Parameters:\n+ * 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- parseLinks: function(nodes, options) {\n-\n- // Fetch external links and \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+ 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: fetchLink\n- * Fetches a URL and returns the result\n- * \n- * Parameters: \n- * href - {String} url to be fetched\n- * \n+ * Method: destroy\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+ destroy: function() {\n+ this.deactivate();\n+ this.eventListener = null;\n+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: parseStyles\n- * Parses