{"diffoscope-json-version": 1, "source1": "/srv/reproducible-results/rbuild-debian/r-b-build.59zYmMjR/b1/openlayers_2.13.1+ds2-10_amd64.changes", "source2": "/srv/reproducible-results/rbuild-debian/r-b-build.59zYmMjR/b2/openlayers_2.13.1+ds2-10_amd64.changes", "unified_diff": null, "details": [{"source1": "Files", "source2": "Files", "unified_diff": "@@ -1,2 +1,2 @@\n \n- b8f862d3ec74ba06200c117bb86bd490 715748 javascript optional libjs-openlayers_2.13.1+ds2-10_all.deb\n+ ed0d68076b09c8e93a24f9653872d0fa 706992 javascript optional libjs-openlayers_2.13.1+ds2-10_all.deb\n"}, {"source1": "libjs-openlayers_2.13.1+ds2-10_all.deb", "source2": "libjs-openlayers_2.13.1+ds2-10_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 2023-01-14 13:27:41.000000 debian-binary\n--rw-r--r-- 0 0 0 3684 2023-01-14 13:27:41.000000 control.tar.xz\n--rw-r--r-- 0 0 0 711872 2023-01-14 13:27:41.000000 data.tar.xz\n+-rw-r--r-- 0 0 0 3680 2023-01-14 13:27:41.000000 control.tar.xz\n+-rw-r--r-- 0 0 0 703120 2023-01-14 13:27:41.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": "@@ -136,14 +136,394 @@\n * (code)\n * \n * (end code)\n */\n ImgPath: ''\n };\n /* ======================================================================\n+ OpenLayers/BaseTypes/Class.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD 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+ * Constructor: OpenLayers.Class\n+ * Base class used to construct all other classes. Includes support for \n+ * multiple inheritance. \n+ * \n+ * This constructor is new in OpenLayers 2.5. At OpenLayers 3.0, the old \n+ * syntax for creating classes and dealing with inheritance \n+ * will be removed.\n+ * \n+ * To create a new OpenLayers-style class, use the following syntax:\n+ * (code)\n+ * var MyClass = OpenLayers.Class(prototype);\n+ * (end)\n+ *\n+ * To create a new OpenLayers-style class with multiple inheritance, use the\n+ * following syntax:\n+ * (code)\n+ * var MyClass = OpenLayers.Class(Class1, Class2, prototype);\n+ * (end)\n+ * \n+ * Note that instanceof reflection will only reveal Class1 as superclass.\n+ *\n+ */\n+OpenLayers.Class = function() {\n+ var len = arguments.length;\n+ var P = arguments[0];\n+ var F = arguments[len - 1];\n+\n+ var C = typeof F.initialize == \"function\" ?\n+ F.initialize :\n+ function() {\n+ P.prototype.initialize.apply(this, arguments);\n+ };\n+\n+ if (len > 1) {\n+ var newArgs = [C, P].concat(\n+ Array.prototype.slice.call(arguments).slice(1, len - 1), F);\n+ OpenLayers.inherit.apply(null, newArgs);\n+ } else {\n+ C.prototype = F;\n+ }\n+ return C;\n+};\n+\n+/**\n+ * Function: OpenLayers.inherit\n+ *\n+ * Parameters:\n+ * C - {Object} the class that inherits\n+ * P - {Object} the superclass to inherit from\n+ *\n+ * In addition to the mandatory C and P parameters, an arbitrary number of\n+ * objects can be passed, which will extend C.\n+ */\n+OpenLayers.inherit = function(C, P) {\n+ var F = function() {};\n+ F.prototype = P.prototype;\n+ C.prototype = new F;\n+ var i, l, o;\n+ for (i = 2, l = arguments.length; i < l; i++) {\n+ o = arguments[i];\n+ if (typeof o === \"function\") {\n+ o = o.prototype;\n+ }\n+ OpenLayers.Util.extend(C.prototype, o);\n+ }\n+};\n+\n+/**\n+ * APIFunction: extend\n+ * Copy all properties of a source object to a destination object. Modifies\n+ * the passed in destination object. Any properties on the source object\n+ * that are set to undefined will not be (re)set on the destination object.\n+ *\n+ * Parameters:\n+ * destination - {Object} The object that will be modified\n+ * source - {Object} The object with properties to be set on the destination\n+ *\n+ * Returns:\n+ * {Object} The destination object.\n+ */\n+OpenLayers.Util = OpenLayers.Util || {};\n+OpenLayers.Util.extend = function(destination, source) {\n+ destination = destination || {};\n+ if (source) {\n+ for (var property in source) {\n+ var value = source[property];\n+ if (value !== undefined) {\n+ destination[property] = value;\n+ }\n+ }\n+\n+ /**\n+ * IE doesn't include the toString property when iterating over an object's\n+ * properties with the for(property in object) syntax. Explicitly check if\n+ * the source has its own toString property.\n+ */\n+\n+ /*\n+ * FF/Windows < 2.0.0.13 reports \"Illegal operation on WrappedNative\n+ * prototype object\" when calling hawOwnProperty if the source object\n+ * is an instance of window.Event.\n+ */\n+\n+ var sourceIsEvt = typeof window.Event == \"function\" &&\n+ source instanceof window.Event;\n+\n+ if (!sourceIsEvt &&\n+ source.hasOwnProperty && source.hasOwnProperty(\"toString\")) {\n+ destination.toString = source.toString;\n+ }\n+ }\n+ return destination;\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/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@@ -383,645 +763,377 @@\n requestFrame: requestFrame,\n start: start,\n stop: stop\n };\n \n })(window);\n /* ======================================================================\n- OpenLayers/BaseTypes/Class.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 /**\n- * @requires OpenLayers/SingleFile.js\n- */\n-\n-/**\n- * Constructor: OpenLayers.Class\n- * Base class used to construct all other classes. Includes support for \n- * multiple inheritance. \n- * \n- * This constructor is new in OpenLayers 2.5. At OpenLayers 3.0, the old \n- * syntax for creating classes and dealing with inheritance \n- * will be removed.\n- * \n- * To create a new OpenLayers-style class, use the following syntax:\n- * (code)\n- * var MyClass = OpenLayers.Class(prototype);\n- * (end)\n- *\n- * To create a new OpenLayers-style class with multiple inheritance, use the\n- * following syntax:\n- * (code)\n- * var MyClass = OpenLayers.Class(Class1, Class2, prototype);\n- * (end)\n- * \n- * Note that instanceof reflection will only reveal Class1 as superclass.\n- *\n- */\n-OpenLayers.Class = function() {\n- var len = arguments.length;\n- var P = arguments[0];\n- var F = arguments[len - 1];\n-\n- var C = typeof F.initialize == \"function\" ?\n- F.initialize :\n- function() {\n- P.prototype.initialize.apply(this, arguments);\n- };\n-\n- if (len > 1) {\n- var newArgs = [C, P].concat(\n- Array.prototype.slice.call(arguments).slice(1, len - 1), F);\n- OpenLayers.inherit.apply(null, newArgs);\n- } else {\n- C.prototype = F;\n- }\n- return C;\n-};\n-\n-/**\n- * Function: OpenLayers.inherit\n- *\n- * Parameters:\n- * C - {Object} the class that inherits\n- * P - {Object} the superclass to inherit from\n- *\n- * In addition to the mandatory C and P parameters, an arbitrary number of\n- * objects can be passed, which will extend C.\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Animation.js\n */\n-OpenLayers.inherit = function(C, P) {\n- var F = function() {};\n- F.prototype = P.prototype;\n- C.prototype = new F;\n- var i, l, o;\n- for (i = 2, l = arguments.length; i < l; i++) {\n- o = arguments[i];\n- if (typeof o === \"function\") {\n- o = o.prototype;\n- }\n- OpenLayers.Util.extend(C.prototype, o);\n- }\n-};\n \n /**\n- * APIFunction: extend\n- * Copy all properties of a source object to a destination object. Modifies\n- * the passed in destination object. Any properties on the source object\n- * that are set to undefined will not be (re)set on the destination object.\n- *\n- * Parameters:\n- * destination - {Object} The object that will be modified\n- * source - {Object} The object with properties to be set on the destination\n- *\n- * Returns:\n- * {Object} The destination object.\n+ * Namespace: OpenLayers.Tween\n */\n-OpenLayers.Util = OpenLayers.Util || {};\n-OpenLayers.Util.extend = function(destination, source) {\n- destination = destination || {};\n- if (source) {\n- for (var property in source) {\n- var value = source[property];\n- if (value !== undefined) {\n- destination[property] = value;\n- }\n- }\n-\n- /**\n- * IE doesn't include the toString property when iterating over an object's\n- * properties with the for(property in object) syntax. Explicitly check if\n- * the source has its own toString property.\n- */\n-\n- /*\n- * FF/Windows < 2.0.0.13 reports \"Illegal operation on WrappedNative\n- * prototype object\" when calling hawOwnProperty if the source object\n- * is an instance of window.Event.\n- */\n-\n- var sourceIsEvt = typeof window.Event == \"function\" &&\n- source instanceof window.Event;\n+OpenLayers.Tween = OpenLayers.Class({\n \n- if (!sourceIsEvt &&\n- source.hasOwnProperty && source.hasOwnProperty(\"toString\")) {\n- destination.toString = source.toString;\n- }\n- }\n- return destination;\n-};\n-/* ======================================================================\n- OpenLayers/Geometry.js\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-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: begin\n+ * {Object} Values to start the animation with\n+ */\n+ begin: null,\n \n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n+ /**\n+ * APIProperty: finish\n+ * {Object} Values to finish the animation with\n+ */\n+ finish: null,\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+ * APIProperty: duration\n+ * {int} duration of the tween (number of steps)\n+ */\n+ duration: null,\n \n /**\n- * Property: id\n- * {String} A unique identifier for this geometry.\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- id: null,\n+ callbacks: null,\n \n /**\n- * Property: parent\n- * {}This is set when a Geometry is added as component\n- * of another geometry\n+ * Property: time\n+ * {int} Step counter\n */\n- parent: null,\n+ time: null,\n \n /**\n- * Property: bounds \n- * {} The bounds of this geometry\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- bounds: null,\n+ minFrameRate: null,\n \n /**\n- * Constructor: OpenLayers.Geometry\n- * Creates a geometry object. \n+ * Property: startTime\n+ * {Number} The timestamp of the first execution step. Used for skipping\n+ * frames\n */\n- initialize: function() {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n+ startTime: null,\n \n /**\n- * Method: destroy\n- * Destroy this geometry.\n+ * Property: animationId\n+ * {int} Loop id returned by OpenLayers.Animation.start\n */\n- destroy: function() {\n- this.id = null;\n- this.bounds = null;\n- },\n+ animationId: null,\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+ * Property: playing\n+ * {Boolean} Tells if the easing is currently playing\n */\n- clone: function() {\n- return new OpenLayers.Geometry();\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+ */\n+ initialize: function(easing) {\n+ this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;\n },\n \n /**\n- * Method: setBounds\n- * Set the bounds for this Geometry.\n+ * APIMethod: start\n+ * Plays the Tween, and calls the callback method on each step\n * \n * Parameters:\n- * bounds - {} \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- setBounds: function(bounds) {\n- if (bounds) {\n- this.bounds = bounds.clone();\n+ start: function(begin, finish, duration, options) {\n+ this.playing = true;\n+ this.begin = begin;\n+ this.finish = finish;\n+ this.duration = duration;\n+ this.callbacks = options.callbacks;\n+ this.minFrameRate = options.minFrameRate || 30;\n+ this.time = 0;\n+ this.startTime = new Date().getTime();\n+ OpenLayers.Animation.stop(this.animationId);\n+ this.animationId = null;\n+ if (this.callbacks && this.callbacks.start) {\n+ this.callbacks.start.call(this, this.begin);\n }\n+ this.animationId = OpenLayers.Animation.start(\n+ OpenLayers.Function.bind(this.play, this)\n+ );\n },\n \n /**\n- * Method: clearBounds\n- * Nullify this components bounds and that of its parent as well.\n+ * APIMethod: stop\n+ * Stops the Tween, and calls the done callback\n+ * Doesn't do anything if animation is already finished\n */\n- clearBounds: function() {\n- this.bounds = null;\n- if (this.parent) {\n- this.parent.clearBounds();\n+ stop: function() {\n+ if (!this.playing) {\n+ return;\n+ }\n+\n+ if (this.callbacks && this.callbacks.done) {\n+ this.callbacks.done.call(this, this.finish);\n }\n+ OpenLayers.Animation.stop(this.animationId);\n+ this.animationId = null;\n+ this.playing = false;\n },\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+ * Method: play\n+ * Calls the appropriate easing method\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+ 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- * 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+ * Create empty functions for all easing methods.\n */\n- getBounds: function() {\n- if (this.bounds == null) {\n- this.calculateBounds();\n- }\n- return this.bounds;\n- },\n+ CLASS_NAME: \"OpenLayers.Tween\"\n+});\n \n- /** \n- * APIMethod: calculateBounds\n- * Recalculate the bounds for the geometry. \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- calculateBounds: function() {\n- //\n- // This should be overridden by subclasses.\n- //\n- },\n+ CLASS_NAME: \"OpenLayers.Easing\"\n+};\n+\n+/**\n+ * Namespace: OpenLayers.Easing.Linear\n+ */\n+OpenLayers.Easing.Linear = {\n \n /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n+ * Function: easeIn\n+ * \n * Parameters:\n- * geometry - {} The target geometry.\n- * options - {Object} Optional properties for configuring the distance\n- * calculation.\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\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+ * {Float}\n */\n- distanceTo: function(geometry, options) {},\n+ easeIn: function(t, b, c, d) {\n+ return c * t / d + b;\n+ },\n \n /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n- *\n+ * Function: easeOut\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+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n *\n * Returns:\n- * {Array} A list of all vertices in the geometry.\n+ * {Float}\n */\n- getVertices: function(nodes) {},\n+ easeOut: function(t, b, c, d) {\n+ return c * t / d + b;\n+ },\n \n /**\n- * Method: atPoint\n- * Note - This is only an approximation based on the bounds of the \n- * geometry.\n+ * Function: easeInOut\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+ * 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 geometry is at the specified location\n+ * {Float}\n */\n- atPoint: function(lonlat, toleranceLon, toleranceLat) {\n- var atPoint = false;\n- var bounds = this.getBounds();\n- if ((bounds != null) && (lonlat != null)) {\n+ easeInOut: function(t, b, c, d) {\n+ return c * t / d + b;\n+ },\n \n- var dX = (toleranceLon != null) ? toleranceLon : 0;\n- var dY = (toleranceLat != null) ? toleranceLat : 0;\n+ CLASS_NAME: \"OpenLayers.Easing.Linear\"\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+/**\n+ * Namespace: OpenLayers.Easing.Expo\n+ */\n+OpenLayers.Easing.Expo = {\n \n- atPoint = toleranceBounds.containsLonLat(lonlat);\n- }\n- return atPoint;\n+ /**\n+ * Function: easeIn\n+ * \n+ * Parameters:\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n+ *\n+ * Returns:\n+ * {Float}\n+ */\n+ easeIn: function(t, b, c, d) {\n+ return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;\n },\n \n /**\n- * Method: getLength\n- * Calculate the length of this geometry. This method is defined in\n- * subclasses.\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} The length of the collection by summing its parts\n+ * {Float}\n */\n- getLength: function() {\n- //to be overridden by geometries that actually have a length\n- //\n- return 0.0;\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: getArea\n- * Calculate the area of this geometry. This method is defined in subclasses.\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} The area of the collection by summing its parts\n+ * {Float}\n */\n- getArea: function() {\n- //to be overridden by geometries that actually have an area\n- //\n- return 0.0;\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- * APIMethod: getCentroid\n- * Calculate the centroid of this geometry. This method is defined in subclasses.\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- * {} The centroid of the collection\n+ * {Float}\n */\n- getCentroid: function() {\n- return null;\n+ easeIn: function(t, b, c, d) {\n+ return c * (t /= d) * t + b;\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+ * 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- * {String} String representation of this geometry.\n+ * {Float}\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+ easeOut: function(t, b, c, d) {\n+ return -c * (t /= d) * (t - 2) + b;\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+ * Function: easeInOut\n+ * \n+ * Parameters:\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n+ *\n+ * Returns:\n+ * {Float}\n+ */\n+ easeInOut: function(t, b, c, d) {\n+ if ((t /= d / 2) < 1) return c / 2 * t * t + b;\n+ return -c / 2 * ((--t) * (t - 2) - 1) + b;\n+ },\n \n-/**\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+ CLASS_NAME: \"OpenLayers.Easing.Quad\"\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@@ -5922,12905 +6034,10200 @@\n \n },\n 'delete': {\n display: \"none\"\n }\n };\n /* ======================================================================\n- OpenLayers/Format.js\n+ OpenLayers/Style.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n * @requires OpenLayers/BaseTypes/Class.js\n * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Feature/Vector.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+ * Class: OpenLayers.Style\n+ * This class represents a UserStyle obtained\n+ * from a SLD, containing styling rules.\n */\n-OpenLayers.Format = OpenLayers.Class({\n+OpenLayers.Style = OpenLayers.Class({\n \n /**\n- * Property: options\n- * {Object} A reference to options passed to the constructor.\n+ * Property: id\n+ * {String} A unique id for this session.\n */\n- options: null,\n+ id: 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+ * APIProperty: name\n+ * {String}\n */\n- externalProjection: null,\n+ name: 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+ * Property: title\n+ * {String} Title of this style (set if included in SLD)\n */\n- internalProjection: null,\n+ title: null,\n \n /**\n- * APIProperty: data\n- * {Object} When is true, this is the parsed string sent to\n- * .\n+ * Property: description\n+ * {String} Description of this style (set if abstract is included in SLD)\n */\n- data: null,\n+ description: null,\n \n /**\n- * APIProperty: keepData\n- * {Object} Maintain a reference () to the most recently read data.\n- * Default is false.\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- keepData: false,\n+ layerName: null,\n \n /**\n- * Constructor: OpenLayers.Format\n- * Instances of this class are not useful. See one of the subclasses.\n+ * APIProperty: isDefault\n+ * {Boolean}\n+ */\n+ isDefault: false,\n+\n+ /** \n+ * Property: rules \n+ * {Array()}\n+ */\n+ rules: null,\n+\n+ /**\n+ * APIProperty: context\n+ * {Object} An optional object with properties that symbolizers' property\n+ * values should be evaluated against. If no context is specified,\n+ * feature.attributes will be used\n+ */\n+ context: null,\n+\n+ /**\n+ * Property: defaultStyle\n+ * {Object} hash of style properties to use as default for merging\n+ * rule-based style symbolizers onto. If no rules are defined,\n+ * createSymbolizer will return this style. If is set to\n+ * true, the defaultStyle will only be taken into account if there are\n+ * rules defined.\n+ */\n+ defaultStyle: null,\n+\n+ /**\n+ * Property: defaultsPerSymbolizer\n+ * {Boolean} If set to true, the will extend the symbolizer\n+ * of every rule. Properties of the will also be used to set\n+ * missing symbolizer properties if the symbolizer has stroke, fill or\n+ * graphic set to true. Default is false.\n+ */\n+ defaultsPerSymbolizer: false,\n+\n+ /**\n+ * Property: propertyStyles\n+ * {Hash of Boolean} cache of style properties that need to be parsed for\n+ * propertyNames. Property names are keys, values won't be used.\n+ */\n+ propertyStyles: null,\n+\n+\n+ /** \n+ * Constructor: OpenLayers.Style\n+ * Creates a UserStyle.\n *\n * Parameters:\n+ * style - {Object} Optional hash of style properties that will be\n+ * used as default style for this style object. This style\n+ * applies if no rules are specified. Symbolizers defined in\n+ * rules will extend this default style.\n * options - {Object} An optional object with properties to set on the\n- * format\n+ * style.\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+ * rules - {Array()} List of rules to be added to the\n+ * style.\n+ * \n * Returns:\n- * An instance of OpenLayers.Format\n+ * {}\n */\n- initialize: function(options) {\n+ initialize: function(style, options) {\n+\n OpenLayers.Util.extend(this, options);\n- this.options = options;\n+ this.rules = [];\n+ if (options && options.rules) {\n+ this.addRules(options.rules);\n+ }\n+\n+ // use the default style from OpenLayers.Feature.Vector if no style\n+ // was given in the constructor\n+ this.setDefaultStyle(style ||\n+ OpenLayers.Feature.Vector.style[\"default\"]);\n+\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n },\n \n- /**\n+ /** \n * APIMethod: destroy\n- * Clean up.\n+ * nullify references to prevent circular references and memory leaks\n */\n- destroy: function() {},\n+ destroy: function() {\n+ for (var i = 0, len = this.rules.length; i < len; i++) {\n+ this.rules[i].destroy();\n+ this.rules[i] = null;\n+ }\n+ this.rules = null;\n+ this.defaultStyle = null;\n+ },\n \n /**\n- * Method: read\n- * Read data from a string, and return an object whose type depends on the\n- * subclass. \n+ * Method: createSymbolizer\n+ * creates a style by applying all feature-dependent rules to the base\n+ * style.\n * \n * Parameters:\n- * data - {string} Data to read/parse.\n- *\n+ * feature - {} feature to evaluate rules for\n+ * \n * Returns:\n- * Depends on the subclass\n+ * {Object} symbolizer hash\n */\n- read: function(data) {\n- throw new Error('Read not implemented.');\n+ createSymbolizer: function(feature) {\n+ var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(\n+ OpenLayers.Util.extend({}, this.defaultStyle), feature);\n+\n+ var rules = this.rules;\n+\n+ var rule, context;\n+ var elseRules = [];\n+ var appliedRules = false;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ rule = rules[i];\n+ // does the rule apply?\n+ var applies = rule.evaluate(feature);\n+\n+ if (applies) {\n+ if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n+ elseRules.push(rule);\n+ } else {\n+ appliedRules = true;\n+ this.applySymbolizer(rule, style, feature);\n+ }\n+ }\n+ }\n+\n+ // if no other rules apply, apply the rules with else filters\n+ if (appliedRules == false && elseRules.length > 0) {\n+ appliedRules = true;\n+ for (var i = 0, len = elseRules.length; i < len; i++) {\n+ this.applySymbolizer(elseRules[i], style, feature);\n+ }\n+ }\n+\n+ // don't display if there were rules but none applied\n+ if (rules.length > 0 && appliedRules == false) {\n+ style.display = \"none\";\n+ }\n+\n+ if (style.label != null && typeof style.label !== \"string\") {\n+ style.label = String(style.label);\n+ }\n+\n+ return style;\n },\n \n /**\n- * Method: write\n- * Accept an object, and return a string. \n+ * Method: applySymbolizer\n *\n * Parameters:\n- * object - {Object} Object to be serialized\n+ * rule - {}\n+ * style - {Object}\n+ * feature - {}\n *\n * Returns:\n- * {String} A string representation of the object.\n+ * {Object} A style with new symbolizer applied.\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+ applySymbolizer: function(rule, style, feature) {\n+ var symbolizerPrefix = feature.geometry ?\n+ this.getSymbolizerPrefix(feature.geometry) :\n+ OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under 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 symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n \n-/**\n- * @requires OpenLayers/Geometry.js\n- */\n+ if (this.defaultsPerSymbolizer === true) {\n+ var defaults = this.defaultStyle;\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: defaults.pointRadius\n+ });\n+ if (symbolizer.stroke === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ strokeWidth: defaults.strokeWidth,\n+ strokeColor: defaults.strokeColor,\n+ strokeOpacity: defaults.strokeOpacity,\n+ strokeDashstyle: defaults.strokeDashstyle,\n+ strokeLinecap: defaults.strokeLinecap\n+ });\n+ }\n+ if (symbolizer.fill === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ fillColor: defaults.fillColor,\n+ fillOpacity: defaults.fillOpacity\n+ });\n+ }\n+ if (symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: this.defaultStyle.pointRadius,\n+ externalGraphic: this.defaultStyle.externalGraphic,\n+ graphicName: this.defaultStyle.graphicName,\n+ graphicOpacity: this.defaultStyle.graphicOpacity,\n+ graphicWidth: this.defaultStyle.graphicWidth,\n+ graphicHeight: this.defaultStyle.graphicHeight,\n+ graphicXOffset: this.defaultStyle.graphicXOffset,\n+ graphicYOffset: this.defaultStyle.graphicYOffset\n+ });\n+ }\n+ }\n \n-/**\n- * Class: OpenLayers.Geometry.Point\n- * Point geometry class. \n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {\n+ // merge the style with the current style\n+ return this.createLiterals(\n+ OpenLayers.Util.extend(style, symbolizer), feature);\n+ },\n \n- /** \n- * APIProperty: x \n- * {float} \n+ /**\n+ * Method: createLiterals\n+ * creates literals for all style properties that have an entry in\n+ * .\n+ * \n+ * Parameters:\n+ * style - {Object} style to create literals for. Will be modified\n+ * inline.\n+ * feature - {Object}\n+ * \n+ * Returns:\n+ * {Object} the modified style\n */\n- x: null,\n+ createLiterals: function(style, feature) {\n+ var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n+ OpenLayers.Util.extend(context, this.context);\n \n- /** \n- * APIProperty: y \n- * {float} \n- */\n- y: null,\n+ for (var i in this.propertyStyles) {\n+ style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);\n+ }\n+ return style;\n+ },\n \n /**\n- * Constructor: OpenLayers.Geometry.Point\n- * Construct a point geometry.\n- *\n- * Parameters:\n- * x - {float} \n- * y - {float}\n+ * Method: findPropertyStyles\n+ * Looks into all rules for this style and the defaultStyle to collect\n+ * all the style hash property names containing ${...} strings that have\n+ * to be replaced using the createLiteral method before returning them.\n * \n+ * Returns:\n+ * {Object} hash of property names that need createLiteral parsing. The\n+ * name of the property is the key, and the value is true;\n */\n- initialize: function(x, y) {\n- OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n+ findPropertyStyles: function() {\n+ var propertyStyles = {};\n \n- this.x = parseFloat(x);\n- this.y = parseFloat(y);\n+ // check the default style\n+ var style = this.defaultStyle;\n+ this.addPropertyStyles(propertyStyles, style);\n+\n+ // walk through all rules to check for properties in their symbolizer\n+ var rules = this.rules;\n+ var symbolizer, value;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ symbolizer = rules[i].symbolizer;\n+ for (var key in symbolizer) {\n+ value = symbolizer[key];\n+ if (typeof value == \"object\") {\n+ // symbolizer key is \"Point\", \"Line\" or \"Polygon\"\n+ this.addPropertyStyles(propertyStyles, value);\n+ } else {\n+ // symbolizer is a hash of style properties\n+ this.addPropertyStyles(propertyStyles, symbolizer);\n+ break;\n+ }\n+ }\n+ }\n+ return propertyStyles;\n },\n \n /**\n- * APIMethod: clone\n+ * Method: addPropertyStyles\n+ * \n+ * Parameters:\n+ * propertyStyles - {Object} hash to add new property styles to. Will be\n+ * modified inline\n+ * symbolizer - {Object} search this symbolizer for property styles\n * \n * Returns:\n- * {} An exact clone of this OpenLayers.Geometry.Point\n+ * {Object} propertyStyles hash\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Geometry.Point(this.x, this.y);\n+ addPropertyStyles: function(propertyStyles, symbolizer) {\n+ var property;\n+ for (var key in symbolizer) {\n+ property = symbolizer[key];\n+ if (typeof property == \"string\" &&\n+ property.match(/\\$\\{\\w+\\}/)) {\n+ propertyStyles[key] = true;\n+ }\n }\n+ return propertyStyles;\n+ },\n \n- // catch any randomly tagged-on properties\n- OpenLayers.Util.applyDefaults(obj, this);\n-\n- return obj;\n+ /**\n+ * APIMethod: addRules\n+ * Adds rules to this style.\n+ * \n+ * Parameters:\n+ * rules - {Array()}\n+ */\n+ addRules: function(rules) {\n+ Array.prototype.push.apply(this.rules, rules);\n+ this.propertyStyles = this.findPropertyStyles();\n },\n \n- /** \n- * Method: calculateBounds\n- * Create a new Bounds based on the lon/lat\n+ /**\n+ * APIMethod: setDefaultStyle\n+ * Sets the default style for this style object.\n+ * \n+ * Parameters:\n+ * style - {Object} Hash of style properties\n */\n- calculateBounds: function() {\n- this.bounds = new OpenLayers.Bounds(this.x, this.y,\n- this.x, this.y);\n+ setDefaultStyle: function(style) {\n+ this.defaultStyle = style;\n+ this.propertyStyles = this.findPropertyStyles();\n },\n \n /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n+ * Method: getSymbolizerPrefix\n+ * Returns the correct symbolizer prefix according to the\n+ * geometry type of the passed geometry\n+ * \n * Parameters:\n- * geometry - {} 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+ * geometry - {}\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+ * {String} key of the according symbolizer\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+ getSymbolizerPrefix: function(geometry) {\n+ var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n+ for (var i = 0, len = prefixes.length; i < len; i++) {\n+ if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n+ return prefixes[i];\n }\n }\n- 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+ * APIMethod: clone\n+ * Clones this style.\n * \n- * Parameters:\n- * geom - {} The geometry to test. \n- *\n * Returns:\n- * {Boolean} The supplied geometry is equivalent to this geometry.\n+ * {} Clone of this style.\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+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ // clone rules\n+ if (this.rules) {\n+ options.rules = [];\n+ for (var i = 0, len = this.rules.length; i < len; ++i) {\n+ options.rules.push(this.rules[i].clone());\n+ }\n }\n- return equals;\n+ // clone context\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ //clone default style\n+ var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n+ return new OpenLayers.Style(defaultStyle, options);\n },\n \n+ CLASS_NAME: \"OpenLayers.Style\"\n+});\n+\n+\n+/**\n+ * Function: createLiteral\n+ * converts a style value holding a combination of PropertyName and Literal\n+ * into a Literal, taking the property values from the passed features.\n+ * \n+ * Parameters:\n+ * value - {String} value to parse. If this string contains a construct like\n+ * \"foo ${bar}\", then \"foo \" will be taken as literal, and \"${bar}\"\n+ * will be replaced by the value of the \"bar\" attribute of the passed\n+ * feature.\n+ * context - {Object} context to take attribute values from\n+ * feature - {} optional feature to pass to\n+ * for evaluating functions in the\n+ * context.\n+ * property - {String} optional, name of the property for which the literal is\n+ * being created for evaluating functions in the context.\n+ * \n+ * Returns:\n+ * {String} the parsed value. In the example of the value parameter above, the\n+ * result would be \"foo valueOfBar\", assuming that the passed feature has an\n+ * attribute named \"bar\" with the value \"valueOfBar\".\n+ */\n+OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n+ if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n+ value = OpenLayers.String.format(value, context, [feature, property]);\n+ value = (isNaN(value) || !value) ? value : parseFloat(value);\n+ }\n+ return value;\n+};\n+\n+/**\n+ * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES\n+ * {Array} prefixes of the sld symbolizers. These are the\n+ * same as the main geometry types\n+ */\n+OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',\n+ 'Raster'\n+];\n+/* ======================================================================\n+ OpenLayers/Rule.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Style.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Rule\n+ * This class represents an SLD Rule, as being used for rule-based SLD styling.\n+ */\n+OpenLayers.Rule = OpenLayers.Class({\n+\n /**\n- * Method: toShortString\n- *\n- * Returns:\n- * {String} Shortened String representation of Point object. \n- * (ex. \"5, 42\")\n+ * Property: id\n+ * {String} A unique id for this session.\n */\n- toShortString: function() {\n- return (this.x + \", \" + this.y);\n- },\n+ id: null,\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+ * APIProperty: name\n+ * {String} name of this rule\n */\n- move: function(x, y) {\n- this.x = this.x + x;\n- this.y = this.y + y;\n- this.clearBounds();\n- },\n+ name: null,\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+ * Property: title\n+ * {String} Title of this rule (set if included in SLD)\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+ title: null,\n \n /**\n- * APIMethod: getCentroid\n- *\n- * Returns:\n- * {} The centroid of the collection\n+ * Property: description\n+ * {String} Description of this rule (set if abstract is included in SLD)\n */\n- getCentroid: function() {\n- return new OpenLayers.Geometry.Point(this.x, this.y);\n- },\n+ description: null,\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+ * 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- * 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+ * options - {Object} An optional object with properties to set on the\n+ * rule\n * \n * Returns:\n- * {} - The current geometry. \n+ * {}\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+ 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: intersects\n- * Determine if the input geometry intersects this one.\n- *\n+ * APIMethod: evaluate\n+ * evaluates this rule for a specific feature\n+ * \n * Parameters:\n- * geometry - {} Any type of geometry.\n- *\n+ * feature - {} feature to apply the rule to.\n+ * \n * Returns:\n- * {Boolean} The input geometry intersects this one.\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- 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+ evaluate: function(feature) {\n+ var context = this.getContext(feature);\n+ var applies = true;\n+\n+ if (this.minScaleDenominator || this.maxScaleDenominator) {\n+ var scale = feature.layer.map.getScale();\n }\n- return intersect;\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- * APIMethod: transform\n- * Translate the x,y properties of the point from source to dest.\n+ * Method: getContext\n+ * Gets the context for evaluating this rule\n * \n- * Parameters:\n- * source - {} \n- * dest - {}\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- * {} \n+ * {} Clone of this rule.\n */\n- transform: function(source, dest) {\n- if ((source && dest)) {\n- OpenLayers.Projection.transform(\n- this, source, dest);\n- this.bounds = null;\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- return this;\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- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\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- * 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+ * 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- * {Array} A list of all vertices in the geometry.\n+ * A new symbolizer.\n */\n- getVertices: function(nodes) {\n- return [this];\n+ initialize: function(config) {\n+ OpenLayers.Util.extend(this, config);\n },\n \n- CLASS_NAME: \"OpenLayers.Geometry.Point\"\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/Geometry/Collection.js\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/Geometry.js\n+ * @requires OpenLayers/Symbolizer.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+ * Class: OpenLayers.Symbolizer.Point\n+ * A symbolizer used to render point features.\n */\n-OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {\n+OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {\n \n /**\n- * APIProperty: components\n- * {Array()} The component parts of this geometry\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- 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+ * APIProperty: strokeOpacity\n+ * {Number} Stroke opacity (0-1).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * APIProperty: strokeWidth\n+ * {Number} Pixel stroke width.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * 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- 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+ * 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- 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+ * 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- return geometry;\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- * Method: getComponentsString\n- * Get a string representing the components for this collection\n+ * APIProperty: pointRadius\n+ * {Number} Pixel point radius.\n * \n- * Returns:\n- * {String} A string representation of the components of this geometry\n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * 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- 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+ * 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- 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+ * APIProperty: graphicHeight\n+ * {Number} Pixel height for sizing an external graphic.\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+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * 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- * 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+ * 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- 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+ * 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- * Method: removeComponent\n- * Remove a component from this geometry.\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- * component - {} \n+ * config - {Object} An object containing properties to be set on the \n+ * symbolizer. Any documented symbolizer property can be set at \n+ * construction.\n *\n- * Returns: \n- * {Boolean} The component was removed.\n+ * Returns:\n+ * A new point symbolizer.\n */\n- removeComponent: function(component) {\n+ initialize: function(config) {\n+ OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n+ },\n \n- OpenLayers.Util.removeItem(this.components, component);\n+ CLASS_NAME: \"OpenLayers.Symbolizer.Point\"\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+/* ======================================================================\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- * APIMethod: getLength\n- * Calculate the length of this geometry\n- *\n- * Returns:\n- * {Float} The length of the geometry\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- 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+ * APIProperty: strokeOpacity\n+ * {Number} Stroke opacity (0-1).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * APIProperty: strokeWidth\n+ * {Number} Pixel stroke width.\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+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * 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- * weighted - {Boolean} Perform the getCentroid computation recursively,\n- * returning an area weighted average of all geometries in this collection.\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- * {} The centroid of the collection\n+ * A new line symbolizer.\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+ initialize: function(config) {\n+ OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\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+ CLASS_NAME: \"OpenLayers.Symbolizer.Line\"\n \n- var xSum = 0,\n- ySum = 0,\n- centroid, area;\n- for (var i = 0; i < len; ++i) {\n- centroid = centroids[i];\n- area = areas[i];\n- xSum += centroid.x * area;\n- ySum += centroid.y * area;\n- }\n+});\n \n- return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum);\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- * 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+ * APIProperty: strokeColor\n+ * {String} Color for line stroke. This is a RGB hex value (e.g. \"#ff0000\"\n+ * for red).\n * \n- * Returns:\n- * {Float} The appoximate geodesic length of the geometry in meters.\n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * APIProperty: strokeOpacity\n+ * {Number} Stroke opacity (0-1).\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * APIProperty: strokeWidth\n+ * {Number} Pixel stroke width.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * APIProperty: strokeLinecap\n+ * {String} Stroke cap type (\"butt\", \"round\", or \"square\").\n * \n- * Returns:\n- * {} - The current geometry. \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * 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- * 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+ * 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- * {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+ * A new polygon symbolizer.\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+ 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- * 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+ * APIProperty: label\n+ * {String} The text for the label.\n * \n- * Parameters:\n- * geometry - {} The geometry to test. \n- *\n- * Returns:\n- * {Boolean} The supplied geometry is equivalent to this geometry.\n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * APIProperty: fontFamily\n+ * {String} The font family for the label.\n * \n- * Parameters:\n- * source - {} \n- * dest - {}\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- * Returns:\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- 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+ * Property: fontStyle\n+ * {String} The font style for the label.\n+ * \n+ * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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+ * Constructor: OpenLayers.Symbolizer.Text\n+ * Create a symbolizer for rendering text labels.\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+ * 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- * {Array} A list of all vertices in the geometry.\n+ * A new text symbolizer.\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+ initialize: function(config) {\n+ OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n },\n \n+ CLASS_NAME: \"OpenLayers.Symbolizer.Text\"\n \n- CLASS_NAME: \"OpenLayers.Geometry.Collection\"\n });\n+\n /* ======================================================================\n- OpenLayers/Geometry/MultiPoint.js\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/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Symbolizer.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+ * Class: OpenLayers.Symbolizer.Raster\n+ * A symbolizer used to render raster images.\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+OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, {\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+ * 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- /**\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+ CLASS_NAME: \"OpenLayers.Symbolizer.Raster\"\n \n- CLASS_NAME: \"OpenLayers.Geometry.MultiPoint\"\n- });\n+});\n /* ======================================================================\n- OpenLayers/Geometry/Curve.js\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/Geometry/MultiPoint.js\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.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+ * Class: OpenLayers.Style2\n+ * This class represents a collection of rules for rendering features.\n */\n-OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {\n+OpenLayers.Style2 = 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 \n- * value means the component types are not restricted.\n+ * Property: id\n+ * {String} A unique id for this session.\n */\n- componentTypes: [\"OpenLayers.Geometry.Point\"],\n+ id: null,\n \n /**\n- * Constructor: OpenLayers.Geometry.Curve\n- * \n- * Parameters:\n- * point - {Array()}\n+ * APIProperty: name\n+ * {String} Style identifier.\n */\n+ name: null,\n \n /**\n- * APIMethod: getLength\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- * {Float} The length of the curve\n+ * {} A new style object.\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+ 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- return length;\n+ delete this.rules;\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: clone\n+ * Clones this style.\n * \n * Returns:\n- * {Float} The appoximate geodesic length of the geometry in meters.\n+ * {} Clone of this style.\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+ 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- // convert to m\n- return length * 1000;\n+ return new OpenLayers.Style2(config);\n },\n \n- CLASS_NAME: \"OpenLayers.Geometry.Curve\"\n+ CLASS_NAME: \"OpenLayers.Style2\"\n });\n /* ======================================================================\n- OpenLayers/Geometry/LineString.js\n+ OpenLayers/Kinetic.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Geometry/Curve.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Animation.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+OpenLayers.Kinetic = OpenLayers.Class({\n \n /**\n- * Constructor: OpenLayers.Geometry.LineString\n- * Create a new LineString geometry\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- * points - {Array()} An array of points used to\n- * generate the linestring\n- *\n+ * options - {Object}\n */\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\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+ * Method: begin\n+ * Begins the dragging.\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+ begin: function() {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = undefined;\n+ this.points = [];\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+ * Method: update\n+ * Updates during the dragging.\n *\n * Parameters:\n- * geometry - {}\n- *\n- * Returns:\n- * {Boolean} The input geometry intersects this geometry.\n+ * xy - {} The new position.\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+ update: function(xy) {\n+ this.points.unshift({\n+ xy: xy,\n+ tick: new Date().getTime()\n+ });\n+ if (this.points.length > this.nbPoints) {\n+ this.points.pop();\n }\n- return intersect;\n },\n \n /**\n- * Method: getSortedSegments\n+ * Method: end\n+ * Ends the dragging, start the kinetic.\n+ *\n+ * Parameters:\n+ * xy - {} The last position.\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+ * {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- 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+ 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- // more efficient to define this somewhere static\n- function byX1(seg1, seg2) {\n- return seg1.x1 - seg2.x1;\n+ if (!last) {\n+ return;\n }\n- return segments.sort(byX1);\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: splitWithSegment\n- * Split this geometry with the given segment.\n+ * Method: move\n+ * Launch the kinetic move pan.\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+ * 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- 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+ 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- 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+ 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/Events.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Util.js\n+ */\n+\n+/**\n+ * Namespace: OpenLayers.Event\n+ * Utility functions for event handling.\n+ */\n+OpenLayers.Event = {\n+\n+ /** \n+ * Property: observers \n+ * {Object} A hashtable cache of the event observers. Keyed by\n+ * element._eventCacheID \n+ */\n+ observers: false,\n+\n /**\n- * Method: split\n- * Use this geometry (the source) to attempt to split a target geometry.\n+ * Constant: KEY_SPACE\n+ * {int}\n+ */\n+ KEY_SPACE: 32,\n+\n+ /** \n+ * Constant: KEY_BACKSPACE \n+ * {int} \n+ */\n+ KEY_BACKSPACE: 8,\n+\n+ /** \n+ * Constant: KEY_TAB \n+ * {int} \n+ */\n+ KEY_TAB: 9,\n+\n+ /** \n+ * Constant: KEY_RETURN \n+ * {int} \n+ */\n+ KEY_RETURN: 13,\n+\n+ /** \n+ * Constant: KEY_ESC \n+ * {int} \n+ */\n+ KEY_ESC: 27,\n+\n+ /** \n+ * Constant: KEY_LEFT \n+ * {int} \n+ */\n+ KEY_LEFT: 37,\n+\n+ /** \n+ * Constant: KEY_UP \n+ * {int} \n+ */\n+ KEY_UP: 38,\n+\n+ /** \n+ * Constant: KEY_RIGHT \n+ * {int} \n+ */\n+ KEY_RIGHT: 39,\n+\n+ /** \n+ * Constant: KEY_DOWN \n+ * {int} \n+ */\n+ KEY_DOWN: 40,\n+\n+ /** \n+ * Constant: KEY_DELETE \n+ * {int} \n+ */\n+ KEY_DELETE: 46,\n+\n+\n+ /**\n+ * Method: element\n+ * Cross browser event element detection.\n * \n * Parameters:\n- * 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+ * event - {Event} \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+ * {DOMElement} The element that caused the event \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+ element: function(event) {\n+ return event.target || event.srcElement;\n },\n \n /**\n- * Method: splitWith\n- * Split this geometry (the target) with the given geometry (the source).\n+ * Method: isSingleTouch\n+ * Determine whether event was caused by a single touch\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+ * event - {Event}\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+ * {Boolean}\n */\n- splitWith: function(geometry, options) {\n- return geometry.split(this, options);\n-\n+ isSingleTouch: function(event) {\n+ return event.touches && event.touches.length == 1;\n },\n \n /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n+ * Method: isMultiTouch\n+ * Determine whether event was caused by a multi touch\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+ * event - {Event}\n *\n * Returns:\n- * {Array} A list of all vertices in the geometry.\n+ * {Boolean}\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+ isMultiTouch: function(event) {\n+ return event.touches && event.touches.length > 1;\n },\n \n /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n+ * Method: isLeftClick\n+ * Determine whether event was caused by a left click. \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+ * event - {Event} \n+ * \n+ * Returns:\n+ * {Boolean}\n+ */\n+ isLeftClick: function(event) {\n+ return (((event.which) && (event.which == 1)) ||\n+ ((event.button) && (event.button == 1)));\n+ },\n+\n+ /**\n+ * Method: isRightClick\n+ * Determine whether event was caused by a right mouse click. \n *\n+ * Parameters:\n+ * event - {Event} \n+ * \n * Returns:\n- * {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+ * {Boolean}\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+ isRightClick: function(event) {\n+ return (((event.which) && (event.which == 3)) ||\n+ ((event.button) && (event.button == 2)));\n+ },\n+\n+ /**\n+ * Method: stop\n+ * Stops an event from propagating. \n+ *\n+ * Parameters: \n+ * event - {Event} \n+ * allowDefault - {Boolean} If true, we stop the event chain but \n+ * still allow the default browser behaviour (text selection,\n+ * radio-button clicking, etc). Default is false.\n+ */\n+ stop: function(event, allowDefault) {\n+\n+ if (!allowDefault) {\n+ OpenLayers.Event.preventDefault(event);\n+ }\n+\n+ if (event.stopPropagation) {\n+ event.stopPropagation();\n } else {\n- 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+ event.cancelBubble = true;\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+ * Method: preventDefault\n+ * Cancels the event if it is cancelable, without stopping further\n+ * propagation of the event.\n *\n * Parameters:\n- * tolerance - {number} threshhold for simplification in map units\n- *\n+ * event - {Event}\n+ */\n+ preventDefault: function(event) {\n+ if (event.preventDefault) {\n+ event.preventDefault();\n+ } else {\n+ event.returnValue = false;\n+ }\n+ },\n+\n+ /** \n+ * Method: findElement\n+ * \n+ * Parameters:\n+ * event - {Event} \n+ * tagName - {String} \n+ * \n * Returns:\n- * {OpenLayers.Geometry.LineString} the simplified LineString\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- simplify: function(tolerance) {\n- if (this && this !== null) {\n- var points = this.getVertices();\n- if (points.length < 3) {\n- return this;\n+ findElement: function(event, tagName) {\n+ var element = OpenLayers.Event.element(event);\n+ while (element.parentNode && (!element.tagName ||\n+ (element.tagName.toUpperCase() != tagName.toUpperCase()))) {\n+ element = element.parentNode;\n+ }\n+ return element;\n+ },\n+\n+ /** \n+ * Method: observe\n+ * \n+ * Parameters:\n+ * elementParam - {DOMElement || String} \n+ * name - {String} \n+ * observer - {function} \n+ * useCapture - {Boolean} \n+ */\n+ observe: function(elementParam, name, observer, useCapture) {\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ useCapture = useCapture || false;\n+\n+ if (name == 'keypress' &&\n+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n+ element.attachEvent)) {\n+ name = 'keydown';\n+ }\n+\n+ //if observers cache has not yet been created, create it\n+ if (!this.observers) {\n+ this.observers = {};\n+ }\n+\n+ //if not already assigned, make a new unique cache ID\n+ if (!element._eventCacheID) {\n+ var idPrefix = \"eventCacheID_\";\n+ if (element.id) {\n+ idPrefix = element.id + \"_\" + idPrefix;\n }\n+ element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);\n+ }\n \n- var compareNumbers = function(a, b) {\n- return (a - b);\n- };\n+ var cacheID = element._eventCacheID;\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+ //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- 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+ //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- 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+ //add the actual browser event listener\n+ if (element.addEventListener) {\n+ element.addEventListener(name, observer, useCapture);\n+ } else if (element.attachEvent) {\n+ element.attachEvent('on' + name, observer);\n+ }\n+ },\n \n- /**\n- * 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+ * 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- 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+ this._removeElementObservers(OpenLayers.Event.observers[cacheID]);\n+ },\n \n- return height;\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- var firstPoint = 0;\n- var lastPoint = points.length - 1;\n- var pointIndexsToKeep = [];\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- //Add the first and last index to the keepers\n- pointIndexsToKeep.push(firstPoint);\n- pointIndexsToKeep.push(lastPoint);\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ var cacheID = element._eventCacheID;\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+ if (name == 'keypress') {\n+ if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n+ element.detachEvent) {\n+ name = 'keydown';\n }\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+ // find element's entry in this.observers cache and remove it\n+ var foundEntry = false;\n+ var elementObservers = OpenLayers.Event.observers[cacheID];\n+ if (elementObservers) {\n+\n+ // find the specific event type in the element's list\n+ var i = 0;\n+ while (!foundEntry && i < elementObservers.length) {\n+ var cacheEntry = elementObservers[i];\n+\n+ if ((cacheEntry.name == name) &&\n+ (cacheEntry.observer == observer) &&\n+ (cacheEntry.useCapture == useCapture)) {\n+\n+ elementObservers.splice(i, 1);\n+ if (elementObservers.length == 0) {\n+ delete OpenLayers.Event.observers[cacheID];\n+ }\n+ foundEntry = true;\n+ break;\n+ }\n+ i++;\n }\n- return new OpenLayers.Geometry.LineString(returnPoints);\n+ }\n \n- } else {\n- return this;\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- CLASS_NAME: \"OpenLayers.Geometry.LineString\"\n-});\n-/* ======================================================================\n- OpenLayers/Geometry/MultiLineString.js\n- ====================================================================== */\n+ /** \n+ * Method: unloadCache\n+ * Cycle through all the element entries in the events cache and call\n+ * stopObservingElement on each. \n+ */\n+ unloadCache: function() {\n+ // check for OpenLayers.Event before checking for observers, because\n+ // OpenLayers.Event may be undefined in IE if no map instance was\n+ // created\n+ if (OpenLayers.Event && OpenLayers.Event.observers) {\n+ for (var cacheID in OpenLayers.Event.observers) {\n+ var elementObservers = OpenLayers.Event.observers[cacheID];\n+ OpenLayers.Event._removeElementObservers.apply(this,\n+ [elementObservers]);\n+ }\n+ OpenLayers.Event.observers = false;\n+ }\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ CLASS_NAME: \"OpenLayers.Event\"\n+};\n \n-/**\n- * @requires OpenLayers/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/LineString.js\n- */\n+/* prevent memory leaks in IE */\n+OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);\n \n /**\n- * Class: OpenLayers.Geometry.MultiLineString\n- * A MultiLineString is a geometry with multiple \n- * components.\n- * \n- * Inherits from:\n- * - \n- * - \n+ * Class: OpenLayers.Events\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+OpenLayers.Events = OpenLayers.Class({\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- );\n- }\n- }\n- sourceSplit = true;\n- splits = splits[1];\n- }\n- if (splits.length) {\n- // splice in new target parts\n- splits.unshift(j, 1);\n- Array.prototype.splice.apply(targetParts, splits);\n- break;\n- }\n- }\n- }\n- if (!sourceSplit) {\n- // source line was not hit\n- if (sourceParts.length) {\n- // add line to existing multi\n- sourceParts[sourceParts.length - 1].addComponent(\n- sourceLine.clone()\n- );\n- } else {\n- // create a fresh multi\n- sourceParts = [\n- new OpenLayers.Geometry.MultiLineString(\n- sourceLine.clone()\n- )\n- ];\n- }\n- }\n- }\n- if (sourceParts && sourceParts.length > 1) {\n- sourceSplit = true;\n- } else {\n- sourceParts = [];\n- }\n- if (targetParts && targetParts.length > 1) {\n- targetSplit = true;\n- } else {\n- targetParts = [];\n- }\n- if (sourceSplit || targetSplit) {\n- if (mutual) {\n- results = [sourceParts, targetParts];\n- } else {\n- results = targetParts;\n- }\n- }\n- return results;\n- },\n-\n- /**\n- * Method: splitWith\n- * Split this geometry (the target) with the given geometry (the source).\n- *\n- * Parameters:\n- * geometry - {} A geometry used to split this\n- * geometry (the source).\n- * options - {Object} Properties of this object will be used to determine\n- * how the split is conducted.\n- *\n- * Valid options:\n- * mutual - {Boolean} Split the source geometry in addition to the target\n- * geometry. Default is false.\n- * edge - {Boolean} Allow splitting when only edges intersect. Default is\n- * true. If false, a vertex on the source must be within the tolerance\n- * distance of the intersection to be considered a split.\n- * tolerance - {Number} If a non-null value is provided, intersections\n- * within the tolerance distance of an existing vertex on the source\n- * will be assumed to occur at the vertex.\n- * \n- * Returns:\n- * {Array} A list of geometries (of this same type as the target) that\n- * result from splitting the target with the source geometry. The\n- * source and target geometry will remain unmodified. If no split\n- * results, null will be returned. If mutual is true and a split\n- * results, return will be an array of two arrays - the first will be\n- * all geometries that result from splitting the source geometry and\n- * the second will be all geometries that result from splitting the\n- * target geometry.\n- */\n- splitWith: function(geometry, options) {\n- var results = null;\n- var mutual = options && options.mutual;\n- var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;\n- if (geometry instanceof OpenLayers.Geometry.LineString) {\n- targetParts = [];\n- sourceParts = [geometry];\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- targetSplit = false;\n- targetLine = this.components[i];\n- for (var j = 0; j < sourceParts.length; ++j) {\n- splits = sourceParts[j].split(targetLine, options);\n- if (splits) {\n- if (mutual) {\n- sourceLines = splits[0];\n- if (sourceLines.length) {\n- // splice in new source parts\n- sourceLines.unshift(j, 1);\n- Array.prototype.splice.apply(sourceParts, sourceLines);\n- j += sourceLines.length - 2;\n- }\n- splits = splits[1];\n- if (splits.length === 0) {\n- splits = [targetLine.clone()];\n- }\n- }\n- for (var k = 0, klen = splits.length; k < klen; ++k) {\n- if (k === 0 && targetParts.length) {\n- targetParts[targetParts.length - 1].addComponent(\n- splits[k]\n- );\n- } else {\n- targetParts.push(\n- new OpenLayers.Geometry.MultiLineString([\n- splits[k]\n- ])\n- );\n- }\n- }\n- targetSplit = true;\n- }\n- }\n- if (!targetSplit) {\n- // target component was not hit\n- if (targetParts.length) {\n- // add it to any existing multi-line\n- targetParts[targetParts.length - 1].addComponent(\n- targetLine.clone()\n- );\n- } else {\n- // or start with a fresh multi-line\n- targetParts = [\n- new OpenLayers.Geometry.MultiLineString([\n- targetLine.clone()\n- ])\n- ];\n- }\n-\n- }\n- }\n- } else {\n- results = geometry.split(this);\n- }\n- if (sourceParts && sourceParts.length > 1) {\n- sourceSplit = true;\n- } else {\n- sourceParts = [];\n- }\n- if (targetParts && targetParts.length > 1) {\n- targetSplit = true;\n- } else {\n- targetParts = [];\n- }\n- if (sourceSplit || targetSplit) {\n- if (mutual) {\n- results = [sourceParts, targetParts];\n- } else {\n- results = targetParts;\n- }\n- }\n- return results;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.MultiLineString\"\n- });\n-/* ======================================================================\n- OpenLayers/Geometry/LinearRing.js\n- ====================================================================== */\n+ /** \n+ * 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-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: listeners \n+ * {Object} Hashtable of Array(Function): events listener functions \n+ */\n+ listeners: null,\n \n-/**\n- * @requires OpenLayers/Geometry/LineString.js\n- */\n+ /** \n+ * Property: object \n+ * {Object} the code object issuing application events \n+ */\n+ object: null,\n \n-/**\n- * Class: OpenLayers.Geometry.LinearRing\n- * \n- * A Linear Ring is a special LineString which is closed. It closes itself \n- * automatically on every addPoint/removePoint by adding a copy of the first\n- * point as the last point. \n- * \n- * Also, as it is the first in the line family to close itself, a getArea()\n- * function is defined to calculate the enclosed area of the linearRing\n- * \n- * Inherits:\n- * - \n- */\n-OpenLayers.Geometry.LinearRing = OpenLayers.Class(\n- OpenLayers.Geometry.LineString, {\n+ /** \n+ * Property: element \n+ * {DOMElement} the DOM element receiving browser events \n+ */\n+ element: 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 \n- * value means the component types are not restricted.\n- */\n- componentTypes: [\"OpenLayers.Geometry.Point\"],\n+ /** \n+ * Property: eventHandler \n+ * {Function} bound event handler attached to elements \n+ */\n+ eventHandler: null,\n \n- /**\n- * Constructor: OpenLayers.Geometry.LinearRing\n- * Linear rings are constructed with an array of points. This array\n- * can represent a closed or open ring. If the ring is open (the last\n- * point does not equal the first point), the constructor will close\n- * the ring. If the ring is already closed (the last point does equal\n- * the first point), it will be left closed.\n- * \n- * Parameters:\n- * points - {Array()} points\n- */\n+ /** \n+ * APIProperty: fallThrough \n+ * {Boolean} \n+ */\n+ fallThrough: null,\n \n- /**\n- * APIMethod: addComponent\n- * Adds a point to geometry components. If the point is to be added to\n- * the end of the components array and it is the same as the last point\n- * already in that array, the duplicate point is not added. This has \n- * the effect of closing the ring if it is not already closed, and \n- * doing the right thing if it is already closed. This behavior can \n- * be overridden by calling the method with a non-null index as the \n- * second argument.\n- *\n- * Parameters:\n- * point - {}\n- * index - {Integer} Index into the array to insert the component\n- * \n- * Returns:\n- * {Boolean} Was the Point successfully added?\n- */\n- addComponent: function(point, index) {\n- var added = false;\n+ /** \n+ * 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- //remove last point\n- var lastPoint = this.components.pop();\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- // given an index, add the point\n- // without an index only add non-duplicate points\n- if (index != null || !point.equals(lastPoint)) {\n- added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n- arguments);\n- }\n+ /**\n+ * 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- //append copy of first point\n- var firstPoint = this.components[0];\n- OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n- [firstPoint]);\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- return added;\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- /**\n- * APIMethod: removeComponent\n- * Removes a point from geometry components.\n- *\n- * Parameters:\n- * point - {}\n- *\n- * Returns: \n- * {Boolean} The component was removed.\n- */\n- removeComponent: function(point) {\n- var removed = this.components && (this.components.length > 3);\n- if (removed) {\n- //remove last point\n- this.components.pop();\n+ // 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- //remove our point\n- OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,\n- arguments);\n- //append copy of first point\n- var firstPoint = this.components[0];\n- OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n- [firstPoint]);\n+ /**\n+ * 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- return removed;\n- },\n-\n- /**\n- * APIMethod: move\n- * Moves a geometry by the given displacement along positive x and y axes.\n- * This modifies the position of the geometry and clears the cached\n- * bounds.\n- *\n- * Parameters:\n- * x - {Float} Distance to move geometry in positive x direction. \n- * y - {Float} Distance to move geometry in positive y direction.\n- */\n- move: function(x, y) {\n- for (var i = 0, len = this.components.length; i < len - 1; i++) {\n- this.components[i].move(x, y);\n+ }\n+ 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+ }\n+ this.element = null;\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 - 1; ++i) {\n- this.components[i].rotate(angle, origin);\n- }\n- },\n+ this.listeners = null;\n+ this.object = null;\n+ this.fallThrough = null;\n+ this.eventHandler = null;\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, len = this.components.length; i < len - 1; ++i) {\n- this.components[i].resize(scale, origin, ratio);\n- }\n- return this;\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- * 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 - 1; i++) {\n- var component = this.components[i];\n- component.transform(source, dest);\n- }\n- this.bounds = null;\n- }\n- return this;\n- },\n+ /**\n+ * 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- /**\n- * APIMethod: getCentroid\n- *\n- * Returns:\n- * {} The centroid of the collection\n- */\n- getCentroid: function() {\n- if (this.components) {\n- var len = this.components.length;\n- if (len > 0 && len <= 2) {\n- return this.components[0].clone();\n- } else if (len > 2) {\n- var sumX = 0.0;\n- var sumY = 0.0;\n- var x0 = this.components[0].x;\n- var y0 = this.components[0].y;\n- var area = -1 * this.getArea();\n- if (area != 0) {\n- for (var i = 0; i < len - 1; i++) {\n- var b = this.components[i];\n- var c = this.components[i + 1];\n- sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n- sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n- }\n- var x = x0 + sumX / (6 * area);\n- var y = y0 + sumY / (6 * area);\n- } else {\n- for (var i = 0; i < len - 1; i++) {\n- sumX += this.components[i].x;\n- sumY += this.components[i].y;\n- }\n- var x = sumX / (len - 1);\n- var y = sumY / (len - 1);\n- }\n- return new OpenLayers.Geometry.Point(x, y);\n- } else {\n- return null;\n- }\n+ // 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+ }\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: getArea\n- * Note - The area is positive if the ring is oriented CW, otherwise\n- * it will be negative.\n- * \n- * Returns:\n- * {Float} The signed area for a ring.\n- */\n- getArea: function() {\n- var area = 0.0;\n- if (this.components && (this.components.length > 2)) {\n- var sum = 0.0;\n- for (var i = 0, len = this.components.length; i < len - 1; i++) {\n- var b = this.components[i];\n- var c = this.components[i + 1];\n- sum += (b.x + c.x) * (c.y - b.y);\n- }\n- area = -sum / 2.0;\n+ /**\n+ * 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- return area;\n- },\n+ }\n+ },\n \n- /**\n- * APIMethod: getGeodesicArea\n- * Calculate the approximate area of the polygon were it projected onto\n- * the earth. Note that this area will be positive if ring is oriented\n- * clockwise, otherwise it will be negative.\n- *\n- * Parameters:\n- * projection - {} The spatial reference system\n- * for the geometry coordinates. If not provided, Geographic/WGS84 is\n- * assumed.\n- * \n- * Reference:\n- * Robert. G. Chamberlain and William H. Duquette, \"Some Algorithms for\n- * Polygons on a Sphere\", JPL Publication 07-03, Jet Propulsion\n- * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409\n- *\n- * Returns:\n- * {float} The approximate signed geodesic area of the polygon in square\n- * meters.\n- */\n- getGeodesicArea: function(projection) {\n- var ring = this; // so we can work with a clone if needed\n- if (projection) {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- if (!gg.equals(projection)) {\n- ring = this.clone().transform(projection, gg);\n- }\n- }\n- var area = 0.0;\n- var len = ring.components && ring.components.length;\n- if (len > 2) {\n- var p1, p2;\n- for (var i = 0; i < len - 1; i++) {\n- p1 = ring.components[i];\n- p2 = ring.components[i + 1];\n- area += OpenLayers.Util.rad(p2.x - p1.x) *\n- (2 + Math.sin(OpenLayers.Util.rad(p1.y)) +\n- Math.sin(OpenLayers.Util.rad(p2.y)));\n- }\n- area = area * 6378137.0 * 6378137.0 / 2.0;\n+ /**\n+ * 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- return area;\n- },\n-\n- /**\n- * Method: containsPoint\n- * Test if a point is inside a linear ring. For the case where a point\n- * is coincident with a linear ring edge, returns 1. Otherwise,\n- * returns boolean.\n- *\n- * Parameters:\n- * point - {}\n- *\n- * Returns:\n- * {Boolean | Number} The point is inside the linear ring. Returns 1 if\n- * the point is coincident with an edge. Returns boolean otherwise.\n- */\n- containsPoint: function(point) {\n- var approx = OpenLayers.Number.limitSigDigs;\n- var digs = 14;\n- var px = approx(point.x, digs);\n- var py = approx(point.y, digs);\n-\n- function getX(y, x1, y1, x2, y2) {\n- return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2;\n+ var listeners = this.listeners[type];\n+ if (!listeners) {\n+ listeners = [];\n+ this.listeners[type] = listeners;\n+ this.extensionCount[type] = 0;\n }\n- var numSeg = this.components.length - 1;\n- var start, end, x1, y1, x2, y2, cx, cy;\n- var crosses = 0;\n- for (var i = 0; i < numSeg; ++i) {\n- start = this.components[i];\n- x1 = approx(start.x, digs);\n- y1 = approx(start.y, digs);\n- end = this.components[i + 1];\n- x2 = approx(end.x, digs);\n- y2 = approx(end.y, digs);\n-\n- /**\n- * The following conditions enforce five edge-crossing rules:\n- * 1. points coincident with edges are considered contained;\n- * 2. an upward edge includes its starting endpoint, and\n- * excludes its final endpoint;\n- * 3. a downward edge excludes its starting endpoint, and\n- * includes its final endpoint;\n- * 4. horizontal edges are excluded; and\n- * 5. the edge-ray intersection point must be strictly right\n- * of the point P.\n- */\n- if (y1 == y2) {\n- // horizontal edge\n- if (py == y1) {\n- // point on horizontal line\n- if (x1 <= x2 && (px >= x1 && px <= x2) || // right or vert\n- x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert\n- // point on edge\n- crosses = -1;\n- break;\n- }\n- }\n- // ignore other horizontal edges\n- continue;\n- }\n- cx = approx(getX(py, x1, y1, x2, y2), digs);\n- if (cx == px) {\n- // point on line\n- if (y1 < y2 && (py >= y1 && py <= y2) || // upward\n- y1 > y2 && (py <= y1 && py >= y2)) { // downward\n- // point on edge\n- crosses = -1;\n- break;\n- }\n- }\n- if (cx <= px) {\n- // no crossing to the right\n- continue;\n- }\n- if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {\n- // no crossing\n- continue;\n- }\n- if (y1 < y2 && (py >= y1 && py < y2) || // upward\n- y1 > y2 && (py < y1 && py >= y2)) { // downward\n- ++crosses;\n+ 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- }\n- var contained = (crosses == -1) ?\n- // on edge\n- 1 :\n- // even (out) or odd (in)\n- !!(crosses & 1);\n-\n- return contained;\n- },\n-\n- /**\n- * APIMethod: intersects\n- * Determine if the input geometry intersects this one.\n- *\n- * Parameters:\n- * geometry - {} Any type of geometry.\n- *\n- * Returns:\n- * {Boolean} The input geometry intersects this one.\n- */\n- intersects: function(geometry) {\n- var intersect = false;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- intersect = this.containsPoint(geometry);\n- } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n- intersect = geometry.intersects(this);\n- } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(\n- this, [geometry]\n- );\n } else {\n- // check for component intersections\n- for (var i = 0, len = geometry.components.length; i < len; ++i) {\n- intersect = geometry.components[i].intersects(this);\n- if (intersect) {\n- break;\n- }\n- }\n- }\n- return intersect;\n- },\n-\n- /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n- *\n- * Parameters:\n- * nodes - {Boolean} For lines, only return vertices that are\n- * endpoints. If false, for lines, only vertices that are not\n- * endpoints will be returned. If not provided, all vertices will\n- * be returned.\n- *\n- * Returns:\n- * {Array} A list of all vertices in the geometry.\n- */\n- getVertices: function(nodes) {\n- return (nodes === true) ? [] : this.components.slice(0, this.components.length - 1);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.LinearRing\"\n- });\n-/* ======================================================================\n- OpenLayers/Geometry/Polygon.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/LinearRing.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.Polygon \n- * Polygon is a collection of Geometry.LinearRings. \n- * \n- * Inherits from:\n- * - \n- * - \n- */\n-OpenLayers.Geometry.Polygon = OpenLayers.Class(\n- OpenLayers.Geometry.Collection, {\n-\n- /**\n- * Property: componentTypes\n- * {Array(String)} An array of class names representing the types of\n- * components that the collection can include. A null value means the\n- * component types are not restricted.\n- */\n- componentTypes: [\"OpenLayers.Geometry.LinearRing\"],\n-\n- /**\n- * Constructor: OpenLayers.Geometry.Polygon\n- * Constructor for a Polygon geometry. \n- * The first ring (this.component[0])is the outer bounds of the polygon and \n- * all subsequent rings (this.component[1-n]) are internal holes.\n- *\n- *\n- * Parameters:\n- * components - {Array()} \n- */\n-\n- /** \n- * APIMethod: getArea\n- * Calculated by subtracting the areas of the internal holes from the \n- * area of the outer hole.\n- * \n- * Returns:\n- * {float} The area of the geometry\n- */\n- getArea: function() {\n- var area = 0.0;\n- if (this.components && (this.components.length > 0)) {\n- area += Math.abs(this.components[0].getArea());\n- for (var i = 1, len = this.components.length; i < len; i++) {\n- area -= Math.abs(this.components[i].getArea());\n- }\n+ listeners.push(listener);\n }\n- return area;\n- },\n+ }\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 polygon in square meters.\n- */\n- getGeodesicArea: function(projection) {\n- var area = 0.0;\n- if (this.components && (this.components.length > 0)) {\n- area += Math.abs(this.components[0].getGeodesicArea(projection));\n- for (var i = 1, len = this.components.length; i < len; i++) {\n- area -= Math.abs(this.components[i].getGeodesicArea(projection));\n- }\n- }\n- return area;\n- },\n+ /**\n+ * 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- * Method: containsPoint\n- * Test if a point is inside a polygon. Points on a polygon edge are\n- * considered inside.\n- *\n- * Parameters:\n- * point - {}\n- *\n- * Returns:\n- * {Boolean | Number} The point is inside the polygon. Returns 1 if the\n- * point is on an edge. Returns boolean otherwise.\n- */\n- containsPoint: function(point) {\n- var numRings = this.components.length;\n- var contained = false;\n- if (numRings > 0) {\n- // check exterior ring - 1 means on edge, boolean otherwise\n- contained = this.components[0].containsPoint(point);\n- if (contained !== 1) {\n- if (contained && numRings > 1) {\n- // check interior rings\n- var hole;\n- for (var i = 1; i < numRings; ++i) {\n- hole = this.components[i].containsPoint(point);\n- if (hole) {\n- if (hole === 1) {\n- // on edge\n- contained = 1;\n- } else {\n- // in hole\n- contained = false;\n- }\n- break;\n- }\n- }\n- }\n- }\n+ /**\n+ * 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- return contained;\n- },\n+ }\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- var i, len;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- intersect = this.containsPoint(geometry);\n- } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" ||\n- geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- // check if rings/linestrings intersect\n- for (i = 0, len = this.components.length; i < len; ++i) {\n- intersect = geometry.intersects(this.components[i]);\n- if (intersect) {\n- break;\n- }\n- }\n- if (!intersect) {\n- // check if this poly contains points of the ring/linestring\n- for (i = 0, len = geometry.components.length; i < len; ++i) {\n- intersect = this.containsPoint(geometry.components[i]);\n- if (intersect) {\n- break;\n- }\n- }\n- }\n- } else {\n- for (i = 0, len = geometry.components.length; i < len; ++i) {\n- intersect = this.intersects(geometry.components[i]);\n- if (intersect) {\n- break;\n- }\n- }\n- }\n- // check case where this poly is wholly contained by another\n- if (!intersect && geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n- // exterior ring points will be contained in the other geometry\n- var ring = this.components[0];\n- for (i = 0, len = ring.components.length; i < len; ++i) {\n- intersect = geometry.containsPoint(ring.components[i]);\n- if (intersect) {\n- break;\n- }\n+ /**\n+ * 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- return intersect;\n- },\n-\n- /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n- * Parameters:\n- * geometry - {} The target geometry.\n- * options - {Object} Optional properties for configuring the distance\n- * calculation.\n- *\n- * Valid options:\n- * details - {Boolean} Return details from the distance calculation.\n- * Default is false.\n- * edge - {Boolean} Calculate the distance from this geometry to the\n- * nearest edge of the target geometry. Default is true. If true,\n- * calling distanceTo from a geometry that is wholly contained within\n- * the target will result in a non-zero distance. If false, whenever\n- * geometries intersect, calling distanceTo will return 0. If false,\n- * details cannot be returned.\n- *\n- * Returns:\n- * {Number | Object} The distance between this geometry and the target.\n- * If details is true, the return will be an object with distance,\n- * x0, y0, x1, and y1 properties. The x0 and y0 properties represent\n- * the coordinates of the closest point on this geometry. The x1 and y1\n- * properties represent the coordinates of the closest point on the\n- * target geometry.\n- */\n- distanceTo: function(geometry, options) {\n- var edge = !(options && options.edge === false);\n- var result;\n- // this is the case where we might not be looking for distance to edge\n- if (!edge && this.intersects(geometry)) {\n- result = 0;\n- } else {\n- result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(\n- this, [geometry, options]\n- );\n- }\n- return result;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.Polygon\"\n- });\n-\n-/**\n- * APIMethod: createRegularPolygon\n- * Create a regular polygon around a radius. Useful for creating circles \n- * and the like.\n- *\n- * Parameters:\n- * origin - {} center of polygon.\n- * radius - {Float} distance to vertex, in map units.\n- * sides - {Integer} Number of sides. 20 approximates a circle.\n- * rotation - {Float} original angle of rotation, in degrees.\n- */\n-OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {\n- var angle = Math.PI * ((1 / sides) - (1 / 2));\n- if (rotation) {\n- angle += (rotation / 180) * Math.PI;\n- }\n- var rotatedAngle, x, y;\n- var points = [];\n- for (var i = 0; i < sides; ++i) {\n- rotatedAngle = angle + (i * 2 * Math.PI / sides);\n- x = origin.x + (radius * Math.cos(rotatedAngle));\n- y = origin.y + (radius * Math.sin(rotatedAngle));\n- points.push(new OpenLayers.Geometry.Point(x, y));\n- }\n- var ring = new OpenLayers.Geometry.LinearRing(points);\n- return new OpenLayers.Geometry.Polygon([ring]);\n-};\n-/* ======================================================================\n- OpenLayers/Geometry/MultiPolygon.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.MultiPolygon\n- * MultiPolygon is a geometry with multiple \n- * components. Create a new instance with the \n- * constructor.\n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(\n- OpenLayers.Geometry.Collection, {\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 value means the\n- * component types are not restricted.\n- */\n- componentTypes: [\"OpenLayers.Geometry.Polygon\"],\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- * Constructor: OpenLayers.Geometry.MultiPolygon\n- * Create a new MultiPolygon geometry\n- *\n- * Parameters:\n- * components - {Array()} An array of polygons\n- * used to generate the MultiPolygon\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- CLASS_NAME: \"OpenLayers.Geometry.MultiPolygon\"\n- });\n-/* ======================================================================\n- OpenLayers/Format/WKT.js\n- ====================================================================== */\n+ // fast path\n+ if (!listeners || listeners.length == 0) {\n+ return undefined;\n+ }\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\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-/**\n- * @requires OpenLayers/Format.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/MultiPoint.js\n- * @requires OpenLayers/Geometry/LineString.js\n- * @requires OpenLayers/Geometry/MultiLineString.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- * @requires OpenLayers/Geometry/MultiPolygon.js\n- */\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-/**\n- * Class: OpenLayers.Format.WKT\n- * Class for reading and writing Well-Known Text. Create a new instance\n- * with the constructor.\n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, {\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- * Constructor: OpenLayers.Format.WKT\n- * Create a new parser for WKT\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- * options - {Object} An optional object whose properties will be set on\n- * this instance\n- *\n- * Returns:\n- * {} A new WKT parser.\n+ * evt - {Event} \n */\n- initialize: function(options) {\n- this.regExes = {\n- 'typeStr': /^\\s*(\\w+)\\s*\\(\\s*(.*)\\s*\\)\\s*$/,\n- 'spaces': /\\s+/,\n- 'parenComma': /\\)\\s*,\\s*\\(/,\n- 'doubleParenComma': /\\)\\s*\\)\\s*,\\s*\\(\\s*\\(/, // can't use {2} here\n- 'trimParens': /^\\s*\\(?(.*?)\\)?\\s*$/\n- };\n- OpenLayers.Format.prototype.initialize.apply(this, [options]);\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- * APIMethod: read\n- * Deserialize a WKT string and return a vector feature or an\n- * array of vector features. Supports WKT for POINT, MULTIPOINT,\n- * LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and\n- * GEOMETRYCOLLECTION.\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- * wkt - {String} A WKT string\n- *\n+ * evt - {Touch} a Touch object from a TouchEvent\n+ * \n * Returns:\n- * {|Array} A feature or array of features for\n- * GEOMETRYCOLLECTION WKT.\n+ * {Object} An object with only clientX and clientY properties with the\n+ * calculated values.\n */\n- read: function(wkt) {\n- var features, type, str;\n- wkt = wkt.replace(/[\\n\\r]/g, \" \");\n- var matches = this.regExes.typeStr.exec(wkt);\n- if (matches) {\n- type = matches[1].toLowerCase();\n- str = matches[2];\n- if (this.parse[type]) {\n- features = this.parse[type].apply(this, [str]);\n- }\n- if (this.internalProjection && this.externalProjection) {\n- if (features &&\n- features.CLASS_NAME == \"OpenLayers.Feature.Vector\") {\n- features.geometry.transform(this.externalProjection,\n- this.internalProjection);\n- } else if (features &&\n- type != \"geometrycollection\" &&\n- typeof features == \"object\") {\n- for (var i = 0, len = features.length; i < len; i++) {\n- var component = features[i];\n- component.geometry.transform(this.externalProjection,\n- this.internalProjection);\n- }\n- }\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- return features;\n+\n+ evt.olClientX = x;\n+ evt.olClientY = y;\n+\n+ return {\n+ clientX: x,\n+ clientY: y\n+ };\n },\n \n /**\n- * APIMethod: write\n- * Serialize a feature or array of features into a WKT string.\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- * features - {|Array} A feature or array of\n- * features\n- *\n+ * evt - {Event} \n+ * \n * Returns:\n- * {String} The WKT string representation of the input geometries\n+ * {} The current xy coordinate of the mouse, adjusted\n+ * for offsets\n */\n- write: function(features) {\n- var collection, geometry, isCollection;\n- if (features.constructor == Array) {\n- collection = features;\n- isCollection = true;\n- } else {\n- collection = [features];\n- isCollection = false;\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- var pieces = [];\n- if (isCollection) {\n- pieces.push('GEOMETRYCOLLECTION(');\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- for (var i = 0, len = collection.length; i < len; ++i) {\n- if (isCollection && i > 0) {\n- pieces.push(',');\n- }\n- geometry = collection[i].geometry;\n- pieces.push(this.extractGeometry(geometry));\n+\n+ if (!this.element.lefttop) {\n+ this.element.lefttop = [\n+ (document.documentElement.clientLeft || 0),\n+ (document.documentElement.clientTop || 0)\n+ ];\n }\n- if (isCollection) {\n- pieces.push(')');\n+\n+ if (!this.element.offsets) {\n+ this.element.offsets = OpenLayers.Util.pagePosition(this.element);\n }\n- return pieces.join('');\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: extractGeometry\n- * Entry point to construct the WKT for a single Geometry object.\n+ * Method: addMsTouchListener\n *\n * Parameters:\n- * geometry - {}\n- *\n- * Returns:\n- * {String} A WKT string of representing the geometry\n+ * element - {DOMElement} The DOM element to register the listener on\n+ * type - {String} The event type\n+ * handler - {Function} the handler\n */\n- extractGeometry: function(geometry) {\n- var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();\n- if (!this.extract[type]) {\n- return null;\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- if (this.internalProjection && this.externalProjection) {\n- geometry = geometry.clone();\n- geometry.transform(this.internalProjection, this.externalProjection);\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- var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase();\n- var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')';\n- return data;\n },\n \n /**\n- * Object with properties corresponding to the geometry types.\n- * Property values are functions that do the actual data extraction.\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- extract: {\n- /**\n- * Return a space delimited string of point coordinates.\n- * @param {OpenLayers.Geometry.Point} point\n- * @returns {String} A string of coordinates representing the point\n- */\n- 'point': function(point) {\n- return point.x + ' ' + point.y;\n- },\n+ addMsTouchListenerStart: function(element, type, handler) {\n+ var touches = this._msTouches;\n \n- /**\n- * Return a comma delimited string of point coordinates from a multipoint.\n- * @param {OpenLayers.Geometry.MultiPoint} multipoint\n- * @returns {String} A string of point coordinate strings representing\n- * the multipoint\n- */\n- 'multipoint': function(multipoint) {\n- var array = [];\n- for (var i = 0, len = multipoint.components.length; i < len; ++i) {\n- array.push('(' +\n- this.extract.point.apply(this, [multipoint.components[i]]) +\n- ')');\n- }\n- return array.join(',');\n- },\n+ var cb = function(e) {\n \n- /**\n- * Return a comma delimited string of point coordinates from a line.\n- * @param {OpenLayers.Geometry.LineString} linestring\n- * @returns {String} A string of point coordinate strings representing\n- * the linestring\n- */\n- 'linestring': function(linestring) {\n- var array = [];\n- for (var i = 0, len = linestring.components.length; i < len; ++i) {\n- array.push(this.extract.point.apply(this, [linestring.components[i]]));\n+ 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- return array.join(',');\n- },\n-\n- /**\n- * Return a comma delimited string of linestring strings from a multilinestring.\n- * @param {OpenLayers.Geometry.MultiLineString} multilinestring\n- * @returns {String} A string of of linestring strings representing\n- * the multilinestring\n- */\n- 'multilinestring': function(multilinestring) {\n- var array = [];\n- for (var i = 0, len = multilinestring.components.length; i < len; ++i) {\n- array.push('(' +\n- this.extract.linestring.apply(this, [multilinestring.components[i]]) +\n- ')');\n+ if (!alreadyInArray) {\n+ touches.push(e);\n }\n- return array.join(',');\n- },\n \n- /**\n- * Return a comma delimited string of linear ring arrays from a polygon.\n- * @param {OpenLayers.Geometry.Polygon} polygon\n- * @returns {String} An array of linear ring arrays representing the polygon\n- */\n- 'polygon': function(polygon) {\n- var array = [];\n- for (var i = 0, len = polygon.components.length; i < len; ++i) {\n- array.push('(' +\n- this.extract.linestring.apply(this, [polygon.components[i]]) +\n- ')');\n- }\n- return array.join(',');\n- },\n+ e.touches = touches.slice();\n+ handler(e);\n+ };\n \n- /**\n- * Return an array of polygon arrays from a multipolygon.\n- * @param {OpenLayers.Geometry.MultiPolygon} multipolygon\n- * @returns {String} An array of polygon arrays representing\n- * the multipolygon\n- */\n- 'multipolygon': function(multipolygon) {\n- var array = [];\n- for (var i = 0, len = multipolygon.components.length; i < len; ++i) {\n- array.push('(' +\n- this.extract.polygon.apply(this, [multipolygon.components[i]]) +\n- ')');\n- }\n- return array.join(',');\n- },\n+ OpenLayers.Event.observe(element, 'MSPointerDown', cb);\n \n- /**\n- * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an \n- * @param {OpenLayers.Geometry.Collection} collection\n- * @returns {String} internal WKT representation of the collection\n- */\n- 'collection': function(collection) {\n- var array = [];\n- for (var i = 0, len = collection.components.length; i < len; ++i) {\n- array.push(this.extractGeometry.apply(this, [collection.components[i]]));\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- return array.join(',');\n- }\n-\n+ };\n+ OpenLayers.Event.observe(element, 'MSPointerUp', internalCb);\n },\n \n /**\n- * Object with properties corresponding to the geometry types.\n- * Property values are functions that do the actual parsing.\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- parse: {\n- /**\n- * Return point feature given a point WKT fragment.\n- * @param {String} str A WKT fragment representing the point\n- * @returns {OpenLayers.Feature.Vector} A point feature\n- * @private\n- */\n- 'point': function(str) {\n- var coords = OpenLayers.String.trim(str).split(this.regExes.spaces);\n- return new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.Point(coords[0], coords[1])\n- );\n- },\n+ addMsTouchListenerMove: function(element, type, handler) {\n+ var touches = this._msTouches;\n+ var cb = function(e) {\n \n- /**\n- * Return a multipoint feature given a multipoint WKT fragment.\n- * @param {String} str A WKT fragment representing the multipoint\n- * @returns {OpenLayers.Feature.Vector} A multipoint feature\n- * @private\n- */\n- 'multipoint': function(str) {\n- var point;\n- var points = OpenLayers.String.trim(str).split(',');\n- var components = [];\n- for (var i = 0, len = points.length; i < len; ++i) {\n- point = points[i].replace(this.regExes.trimParens, '$1');\n- components.push(this.parse.point.apply(this, [point]).geometry);\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- return new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.MultiPoint(components)\n- );\n- },\n \n- /**\n- * Return a linestring feature given a linestring WKT fragment.\n- * @param {String} str A WKT fragment representing the linestring\n- * @returns {OpenLayers.Feature.Vector} A linestring feature\n- * @private\n- */\n- 'linestring': function(str) {\n- var points = OpenLayers.String.trim(str).split(',');\n- var components = [];\n- for (var i = 0, len = points.length; i < len; ++i) {\n- components.push(this.parse.point.apply(this, [points[i]]).geometry);\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- return new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.LineString(components)\n- );\n- },\n-\n- /**\n- * Return a multilinestring feature given a multilinestring WKT fragment.\n- * @param {String} str A WKT fragment representing the multilinestring\n- * @returns {OpenLayers.Feature.Vector} A multilinestring feature\n- * @private\n- */\n- 'multilinestring': function(str) {\n- var line;\n- var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma);\n- var components = [];\n- for (var i = 0, len = lines.length; i < len; ++i) {\n- line = lines[i].replace(this.regExes.trimParens, '$1');\n- components.push(this.parse.linestring.apply(this, [line]).geometry);\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- return new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.MultiLineString(components)\n- );\n- },\n \n- /**\n- * Return a polygon feature given a polygon WKT fragment.\n- * @param {String} str A WKT fragment representing the polygon\n- * @returns {OpenLayers.Feature.Vector} A polygon feature\n- * @private\n- */\n- 'polygon': function(str) {\n- var ring, linestring, linearring;\n- var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma);\n- var components = [];\n- for (var i = 0, len = rings.length; i < len; ++i) {\n- ring = rings[i].replace(this.regExes.trimParens, '$1');\n- linestring = this.parse.linestring.apply(this, [ring]).geometry;\n- linearring = new OpenLayers.Geometry.LinearRing(linestring.components);\n- components.push(linearring);\n- }\n- return new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.Polygon(components)\n- );\n- },\n+ e.touches = touches.slice();\n+ handler(e);\n+ };\n \n- /**\n- * Return a multipolygon feature given a multipolygon WKT fragment.\n- * @param {String} str A WKT fragment representing the multipolygon\n- * @returns {OpenLayers.Feature.Vector} A multipolygon feature\n- * @private\n- */\n- 'multipolygon': function(str) {\n- var polygon;\n- var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);\n- var components = [];\n- for (var i = 0, len = polygons.length; i < len; ++i) {\n- polygon = polygons[i].replace(this.regExes.trimParens, '$1');\n- components.push(this.parse.polygon.apply(this, [polygon]).geometry);\n- }\n- return new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.MultiPolygon(components)\n- );\n- },\n+ OpenLayers.Event.observe(element, 'MSPointerMove', cb);\n+ },\n \n- /**\n- * Return an array of features given a geometrycollection WKT fragment.\n- * @param {String} str A WKT fragment representing the geometrycollection\n- * @returns {Array} An array of OpenLayers.Feature.Vector\n- * @private\n- */\n- 'geometrycollection': function(str) {\n- // separate components of the collection with |\n- str = str.replace(/,\\s*([A-Za-z])/g, '|$1');\n- var wktArray = OpenLayers.String.trim(str).split('|');\n- var components = [];\n- for (var i = 0, len = wktArray.length; i < len; ++i) {\n- components.push(OpenLayers.Format.WKT.prototype.read.apply(this, [wktArray[i]]));\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- return components;\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.Format.WKT\"\n+ CLASS_NAME: \"OpenLayers.Events\"\n });\n /* ======================================================================\n- OpenLayers/Format/JSON.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- * Note:\n- * This work draws heavily from the public domain JSON serializer/deserializer\n- * at http://www.json.org/json.js. Rewritten so that it doesn't modify\n- * basic data prototypes.\n- */\n-\n-/**\n- * @requires OpenLayers/Format.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Format.JSON\n- * A parser to read/write JSON safely. Create a new instance with the\n- * constructor.\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- * Inherits from:\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.JSON = OpenLayers.Class(OpenLayers.Format, {\n-\n- /**\n- * APIProperty: indent\n- * {String} For \"pretty\" printing, the indent string will be used once for\n- * each indentation level.\n- */\n- indent: \" \",\n-\n- /**\n- * APIProperty: space\n- * {String} For \"pretty\" printing, the space string will be used after\n- * the \":\" separating a name/value pair.\n- */\n- space: \" \",\n-\n- /**\n- * APIProperty: newline\n- * {String} For \"pretty\" printing, the newline string will be used at the\n- * end of each name/value pair or array item.\n- */\n- newline: \"\\n\",\n+OpenLayers.Projection = OpenLayers.Class({\n \n /**\n- * Property: level\n- * {Integer} For \"pretty\" printing, this is incremented/decremented during\n- * serialization.\n+ * Property: proj\n+ * {Object} Proj4js.Proj instance.\n */\n- level: 0,\n+ proj: null,\n \n /**\n- * Property: pretty\n- * {Boolean} Serialize with extra whitespace for structure. This is set\n- * by the method.\n+ * Property: projCode\n+ * {String}\n */\n- pretty: false,\n+ projCode: null,\n \n /**\n- * Property: nativeJSON\n- * {Boolean} Does the browser support native json?\n+ * Property: titleRegEx\n+ * {RegExp} regular expression to strip the title from a proj4js definition\n */\n- nativeJSON: (function() {\n- return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\");\n- })(),\n+ titleRegEx: /\\+title=[^\\+]*/,\n \n /**\n- * Constructor: OpenLayers.Format.JSON\n- * Create a new parser for JSON.\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 whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Deserialize a json string.\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- * Parameters:\n- * json - {String} A JSON string\n- * filter - {Function} A function which will be called for every key and\n- * value at every level of the final result. Each value will be\n- * replaced by the result of the filter function. This can be used to\n- * reform generic objects into instances of classes, or to transform\n- * date strings into Date objects.\n- * \n * Returns:\n- * {Object} An object, array, string, or number .\n+ * {} A projection object.\n */\n- read: function(json, filter) {\n- var object;\n- if (this.nativeJSON) {\n- object = JSON.parse(json, filter);\n- } else try {\n- /**\n- * Parsing happens in three stages. In the first stage, we run the\n- * text against a regular expression which looks for non-JSON\n- * characters. We are especially concerned with '()' and 'new'\n- * because they can cause invocation, and '=' because it can\n- * cause mutation. But just to be safe, we will reject all\n- * unexpected characters.\n- */\n- if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, '@').replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']').replace(/(?:^|:|,)(?:\\s*\\[)+/g, ''))) {\n-\n- /**\n- * In the second stage we use the eval function to compile the\n- * text into a JavaScript structure. The '{' operator is\n- * subject to a syntactic ambiguity in JavaScript - it can\n- * begin a block or an object literal. We wrap the text in\n- * parens to eliminate the ambiguity.\n- */\n- object = eval('(' + json + ')');\n-\n- /**\n- * In the optional third stage, we recursively walk the new\n- * structure, passing each name/value pair to a filter\n- * function for possible transformation.\n- */\n- if (typeof filter === 'function') {\n- function walk(k, v) {\n- if (v && typeof v === 'object') {\n- for (var i in v) {\n- if (v.hasOwnProperty(i)) {\n- v[i] = walk(i, v[i]);\n- }\n- }\n- }\n- return filter(k, v);\n- }\n- object = walk('', object);\n- }\n- }\n- } catch (e) {\n- // Fall through if the regexp test fails.\n- }\n-\n- if (this.keepData) {\n- this.data = object;\n+ 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- return object;\n },\n \n /**\n- * APIMethod: write\n- * Serialize an object into a JSON string.\n- *\n- * Parameters:\n- * value - {String} The object, array, string, number, boolean or date\n- * to be serialized.\n- * pretty - {Boolean} Structure the output with newlines and indentation.\n- * Default is false.\n+ * APIMethod: getCode\n+ * Get the string SRS code.\n *\n * Returns:\n- * {String} The JSON string representation of the input value.\n+ * {String} The SRS code.\n */\n- write: function(value, pretty) {\n- this.pretty = !!pretty;\n- var json = null;\n- var type = typeof value;\n- if (this.serialize[type]) {\n- try {\n- json = (!this.pretty && this.nativeJSON) ?\n- JSON.stringify(value) :\n- this.serialize[type].apply(this, [value]);\n- } catch (err) {\n- OpenLayers.Console.error(\"Trouble serializing: \" + err);\n- }\n- }\n- return json;\n+ getCode: function() {\n+ return this.proj ? this.proj.srsCode : this.projCode;\n },\n \n /**\n- * Method: writeIndent\n- * Output an indentation string depending on the indentation level.\n+ * APIMethod: getUnits\n+ * Get the units string for the projection -- returns null if \n+ * proj4js is not available.\n *\n * Returns:\n- * {String} An appropriate indentation string.\n+ * {String} The units abbreviation.\n */\n- writeIndent: function() {\n- var pieces = [];\n- if (this.pretty) {\n- for (var i = 0; i < this.level; ++i) {\n- pieces.push(this.indent);\n- }\n- }\n- return pieces.join('');\n+ getUnits: function() {\n+ return this.proj ? this.proj.units : null;\n },\n \n /**\n- * Method: writeNewline\n- * Output a string representing a newline if in pretty printing mode.\n+ * Method: toString\n+ * Convert projection to string (getCode wrapper).\n *\n * Returns:\n- * {String} A string representing a new line.\n+ * {String} The projection code.\n */\n- writeNewline: function() {\n- return (this.pretty) ? this.newline : '';\n+ toString: function() {\n+ return this.getCode();\n },\n \n /**\n- * Method: writeSpace\n- * Output a string representing a space if in pretty printing mode.\n+ * Method: equals\n+ * Test equality of two projection instances. Determines equality based\n+ * soley on the projection code.\n *\n * Returns:\n- * {String} A space.\n+ * {Boolean} The two projections are equivalent.\n */\n- writeSpace: function() {\n- return (this.pretty) ? this.space : '';\n+ equals: function(projection) {\n+ var p = projection,\n+ equals = false;\n+ if (p) {\n+ if (!(p instanceof OpenLayers.Projection)) {\n+ p = new OpenLayers.Projection(p);\n+ }\n+ if ((typeof Proj4js == \"object\") && this.proj.defData && p.proj.defData) {\n+ equals = this.proj.defData.replace(this.titleRegEx, \"\") ==\n+ p.proj.defData.replace(this.titleRegEx, \"\");\n+ } else if (p.getCode) {\n+ var source = this.getCode(),\n+ target = p.getCode();\n+ equals = source == target ||\n+ !!OpenLayers.Projection.transforms[source] &&\n+ OpenLayers.Projection.transforms[source][target] ===\n+ OpenLayers.Projection.nullTransform;\n+ }\n+ }\n+ return equals;\n },\n \n- /**\n- * Property: serialize\n- * Object with properties corresponding to the serializable data types.\n- * Property values are functions that do the actual serializing.\n+ /* Method: destroy\n+ * Destroy projection object.\n */\n- serialize: {\n- /**\n- * Method: serialize.object\n- * Transform an object into a JSON string.\n- *\n- * Parameters:\n- * object - {Object} The object to be serialized.\n- * \n- * Returns:\n- * {String} A JSON string representing the object.\n- */\n- 'object': function(object) {\n- // three special objects that we want to treat differently\n- if (object == null) {\n- return \"null\";\n- }\n- if (object.constructor == Date) {\n- return this.serialize.date.apply(this, [object]);\n- }\n- if (object.constructor == Array) {\n- return this.serialize.array.apply(this, [object]);\n- }\n- var pieces = ['{'];\n- this.level += 1;\n- var key, keyJSON, valueJSON;\n+ destroy: function() {\n+ delete this.proj;\n+ delete this.projCode;\n+ },\n \n- var addComma = false;\n- for (key in object) {\n- if (object.hasOwnProperty(key)) {\n- // recursive calls need to allow for sub-classing\n- keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n- [key, this.pretty]);\n- valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n- [object[key], this.pretty]);\n- if (keyJSON != null && valueJSON != null) {\n- if (addComma) {\n- pieces.push(',');\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(),\n- keyJSON, ':', this.writeSpace(), valueJSON);\n- addComma = true;\n- }\n- }\n- }\n+ CLASS_NAME: \"OpenLayers.Projection\"\n+});\n \n- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), '}');\n- return pieces.join('');\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- * Method: serialize.array\n- * Transform an array into a JSON string.\n- *\n- * Parameters:\n- * array - {Array} The array to be serialized\n- * \n- * Returns:\n- * {String} A JSON string representing the array.\n- */\n- 'array': function(array) {\n- var json;\n- var pieces = ['['];\n- this.level += 1;\n+/**\n+ * 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- for (var i = 0, len = array.length; i < len; ++i) {\n- // recursive calls need to allow for sub-classing\n- json = OpenLayers.Format.JSON.prototype.write.apply(this,\n- [array[i], this.pretty]);\n- if (json != null) {\n- if (i > 0) {\n- pieces.push(',');\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(), json);\n- }\n+/**\n+ * 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- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), ']');\n- return pieces.join('');\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- * Method: serialize.string\n- * Transform a string into a JSON string.\n- *\n- * Parameters:\n- * string - {String} The string to be serialized\n- * \n- * Returns:\n- * {String} A JSON string representing the string.\n- */\n- 'string': function(string) {\n- // If the string contains no control characters, no quote characters, and no\n- // backslash characters, then we can simply slap some quotes around it.\n- // Otherwise we must also replace the offending characters with safe\n- // sequences. \n- var m = {\n- '\\b': '\\\\b',\n- '\\t': '\\\\t',\n- '\\n': '\\\\n',\n- '\\f': '\\\\f',\n- '\\r': '\\\\r',\n- '\"': '\\\\\"',\n- '\\\\': '\\\\\\\\'\n- };\n- if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n- return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n- var c = m[b];\n- if (c) {\n- return c;\n- }\n- c = b.charCodeAt();\n- return '\\\\u00' +\n- Math.floor(c / 16).toString(16) +\n- (c % 16).toString(16);\n- }) + '\"';\n- }\n- return '\"' + string + '\"';\n- },\n+/**\n+ * 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- /**\n- * Method: serialize.number\n- * Transform a number into a JSON string.\n- *\n- * Parameters:\n- * number - {Number} The number to be serialized.\n- *\n- * Returns:\n- * {String} A JSON string representing the number.\n- */\n- 'number': function(number) {\n- return isFinite(number) ? String(number) : \"null\";\n- },\n+ var pole = 20037508.34;\n \n- /**\n- * Method: serialize.boolean\n- * Transform a boolean into a JSON string.\n- *\n- * Parameters:\n- * bool - {Boolean} The boolean to be serialized.\n- * \n- * Returns:\n- * {String} A JSON string representing the boolean.\n- */\n- 'boolean': function(bool) {\n- return String(bool);\n- },\n+ 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- /**\n- * Method: serialize.object\n- * Transform a date into a JSON string.\n- *\n- * Parameters:\n- * date - {Date} The date to be serialized.\n- * \n- * Returns:\n- * {String} A JSON string representing the date.\n- */\n- 'date': function(date) {\n- function format(number) {\n- // Format integers to have at least two digits.\n- return (number < 10) ? '0' + number : number;\n+ 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- return '\"' + date.getFullYear() + '-' +\n- format(date.getMonth() + 1) + '-' +\n- format(date.getDate()) + 'T' +\n- format(date.getHours()) + ':' +\n- format(date.getMinutes()) + ':' +\n- format(date.getSeconds()) + '\"';\n }\n- },\n+ }\n \n- CLASS_NAME: \"OpenLayers.Format.JSON\"\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 /* ======================================================================\n- OpenLayers/Format/GeoJSON.js\n+ OpenLayers/Map.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/JSON.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/MultiPoint.js\n- * @requires OpenLayers/Geometry/LineString.js\n- * @requires OpenLayers/Geometry/MultiLineString.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- * @requires OpenLayers/Geometry/MultiPolygon.js\n- * @requires OpenLayers/Console.js\n+ * @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.Format.GeoJSON\n- * Read and write GeoJSON. Create a new parser with the\n- * constructor.\n- *\n- * Inherits from:\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.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {\n+OpenLayers.Map = OpenLayers.Class({\n \n /**\n- * APIProperty: ignoreExtraDims\n- * {Boolean} Ignore dimensions higher than 2 when reading geometry\n- * coordinates.\n+ * Constant: Z_INDEX_BASE\n+ * {Object} Base z-indexes for different classes of thing \n */\n- ignoreExtraDims: false,\n+ Z_INDEX_BASE: {\n+ BaseLayer: 100,\n+ Overlay: 325,\n+ Feature: 725,\n+ Popup: 750,\n+ Control: 1000\n+ },\n \n /**\n- * Constructor: OpenLayers.Format.GeoJSON\n- * Create a new parser for GeoJSON.\n+ * APIProperty: events\n+ * {}\n *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Deserialize a GeoJSON string.\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * map.events.register(type, obj, listener);\n+ * (end)\n *\n- * Parameters:\n- * json - {String} A GeoJSON string\n- * type - {String} Optional string that determines the structure of\n- * the output. Supported values are \"Geometry\", \"Feature\", and\n- * \"FeatureCollection\". If absent or null, a default of\n- * \"FeatureCollection\" is assumed.\n- * filter - {Function} A function which will be called for every key and\n- * value at every level of the final result. Each value will be\n- * replaced by the result of the filter function. This can be used to\n- * reform generic objects into instances of classes, or to transform\n- * date strings into Date objects.\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n *\n- * Returns: \n- * {Object} The return depends on the value of the type argument. If type\n- * is \"FeatureCollection\" (the default), the return will be an array\n- * of . If type is \"Geometry\", the input json\n- * must represent a single geometry, and the return will be an\n- * . If type is \"Feature\", the input json must\n- * represent a single feature, and the return will be an\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- read: function(json, type, filter) {\n- type = (type) ? type : \"FeatureCollection\";\n- var results = null;\n- var obj = null;\n- if (typeof json == \"string\") {\n- obj = OpenLayers.Format.JSON.prototype.read.apply(this,\n- [json, filter]);\n- } else {\n- obj = json;\n- }\n- if (!obj) {\n- OpenLayers.Console.error(\"Bad JSON: \" + json);\n- } else if (typeof(obj.type) != \"string\") {\n- OpenLayers.Console.error(\"Bad GeoJSON - no type: \" + json);\n- } else if (this.isValidType(obj, type)) {\n- switch (type) {\n- case \"Geometry\":\n- try {\n- results = this.parseGeometry(obj);\n- } catch (err) {\n- OpenLayers.Console.error(err);\n- }\n- break;\n- case \"Feature\":\n- try {\n- results = this.parseFeature(obj);\n- results.type = \"Feature\";\n- } catch (err) {\n- OpenLayers.Console.error(err);\n- }\n- break;\n- case \"FeatureCollection\":\n- // for type FeatureCollection, we allow input to be any type\n- results = [];\n- switch (obj.type) {\n- case \"Feature\":\n- try {\n- results.push(this.parseFeature(obj));\n- } catch (err) {\n- results = null;\n- OpenLayers.Console.error(err);\n- }\n- break;\n- case \"FeatureCollection\":\n- for (var i = 0, len = obj.features.length; i < len; ++i) {\n- try {\n- results.push(this.parseFeature(obj.features[i]));\n- } catch (err) {\n- results = null;\n- OpenLayers.Console.error(err);\n- }\n- }\n- break;\n- default:\n- try {\n- var geom = this.parseGeometry(obj);\n- results.push(new OpenLayers.Feature.Vector(geom));\n- } catch (err) {\n- results = null;\n- OpenLayers.Console.error(err);\n- }\n- }\n- break;\n- }\n- }\n- return results;\n- },\n \n /**\n- * Method: isValidType\n- * Check if a GeoJSON object is a valid representative of the given type.\n- *\n- * Returns:\n- * {Boolean} The object is valid GeoJSON object of the given type.\n+ * Property: id\n+ * {String} Unique identifier for the map\n */\n- isValidType: function(obj, type) {\n- var valid = false;\n- switch (type) {\n- case \"Geometry\":\n- if (OpenLayers.Util.indexOf(\n- [\"Point\", \"MultiPoint\", \"LineString\", \"MultiLineString\",\n- \"Polygon\", \"MultiPolygon\", \"Box\", \"GeometryCollection\"\n- ],\n- obj.type) == -1) {\n- // unsupported geometry type\n- OpenLayers.Console.error(\"Unsupported geometry type: \" +\n- obj.type);\n- } else {\n- valid = true;\n- }\n- break;\n- case \"FeatureCollection\":\n- // allow for any type to be converted to a feature collection\n- valid = true;\n- break;\n- default:\n- // for Feature types must match\n- if (obj.type == type) {\n- valid = true;\n- } else {\n- OpenLayers.Console.error(\"Cannot convert types from \" +\n- obj.type + \" to \" + type);\n- }\n- }\n- return valid;\n- },\n+ id: null,\n \n /**\n- * Method: parseFeature\n- * Convert a feature object from GeoJSON into an\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- * Parameters:\n- * obj - {Object} An object created from a GeoJSON object\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- * Returns:\n- * {} A feature.\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- parseFeature: function(obj) {\n- var feature, geometry, attributes, bbox;\n- attributes = (obj.properties) ? obj.properties : {};\n- bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;\n- try {\n- geometry = this.parseGeometry(obj.geometry);\n- } catch (err) {\n- // deal with bad geometries\n- throw err;\n- }\n- feature = new OpenLayers.Feature.Vector(geometry, attributes);\n- if (bbox) {\n- feature.bounds = OpenLayers.Bounds.fromArray(bbox);\n- }\n- if (obj.id) {\n- feature.fid = obj.id;\n- }\n- return feature;\n- },\n+ fractionalZoom: false,\n \n /**\n- * Method: parseGeometry\n- * Convert a geometry object from GeoJSON into an .\n- *\n- * Parameters:\n- * obj - {Object} An object created from a GeoJSON object\n- *\n- * Returns: \n- * {} A geometry.\n+ * APIProperty: events\n+ * {} An events object that handles all \n+ * events on the map\n */\n- parseGeometry: function(obj) {\n- if (obj == null) {\n- return null;\n- }\n- var geometry, collection = false;\n- if (obj.type == \"GeometryCollection\") {\n- if (!(OpenLayers.Util.isArray(obj.geometries))) {\n- throw \"GeometryCollection must have geometries array: \" + obj;\n- }\n- var numGeom = obj.geometries.length;\n- var components = new Array(numGeom);\n- for (var i = 0; i < numGeom; ++i) {\n- components[i] = this.parseGeometry.apply(\n- this, [obj.geometries[i]]\n- );\n- }\n- geometry = new OpenLayers.Geometry.Collection(components);\n- collection = true;\n- } else {\n- if (!(OpenLayers.Util.isArray(obj.coordinates))) {\n- throw \"Geometry must have coordinates array: \" + obj;\n- }\n- if (!this.parseCoords[obj.type.toLowerCase()]) {\n- throw \"Unsupported geometry type: \" + obj.type;\n- }\n- try {\n- geometry = this.parseCoords[obj.type.toLowerCase()].apply(\n- this, [obj.coordinates]\n- );\n- } catch (err) {\n- // deal with bad coordinates\n- throw err;\n- }\n- }\n- // We don't reproject collections because the children are reprojected\n- // for us when they are created.\n- if (this.internalProjection && this.externalProjection && !collection) {\n- geometry.transform(this.externalProjection,\n- this.internalProjection);\n- }\n- return geometry;\n- },\n+ events: null,\n \n /**\n- * Property: parseCoords\n- * Object with properties corresponding to the GeoJSON geometry types.\n- * Property values are functions that do the actual parsing.\n+ * 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- parseCoords: {\n- /**\n- * Method: parseCoords.point\n- * Convert a coordinate array from GeoJSON into an\n- * .\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {} A geometry.\n- */\n- \"point\": function(array) {\n- if (this.ignoreExtraDims == false &&\n- array.length != 2) {\n- throw \"Only 2D points are supported: \" + array;\n- }\n- return new OpenLayers.Geometry.Point(array[0], array[1]);\n- },\n+ allOverlays: false,\n \n- /**\n- * Method: parseCoords.multipoint\n- * Convert a coordinate array from GeoJSON into an\n- * .\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {} A geometry.\n- */\n- \"multipoint\": function(array) {\n- var points = [];\n- var p = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- p = this.parseCoords[\"point\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- points.push(p);\n- }\n- return new OpenLayers.Geometry.MultiPoint(points);\n- },\n+ /**\n+ * 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- * Method: parseCoords.linestring\n- * Convert a coordinate array from GeoJSON into an\n- * .\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {} A geometry.\n- */\n- \"linestring\": function(array) {\n- var points = [];\n- var p = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- p = this.parseCoords[\"point\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- points.push(p);\n- }\n- return new OpenLayers.Geometry.LineString(points);\n- },\n+ /**\n+ * Property: dragging\n+ * {Boolean} The map is currently being dragged.\n+ */\n+ dragging: false,\n \n- /**\n- * Method: parseCoords.multilinestring\n- * Convert a coordinate array from GeoJSON into an\n- * .\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {} A geometry.\n- */\n- \"multilinestring\": function(array) {\n- var lines = [];\n- var l = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- l = this.parseCoords[\"linestring\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- lines.push(l);\n- }\n- return new OpenLayers.Geometry.MultiLineString(lines);\n- },\n+ /**\n+ * Property: size\n+ * {} Size of the main div (this.div)\n+ */\n+ size: null,\n \n- /**\n- * Method: parseCoords.polygon\n- * Convert a coordinate array from GeoJSON into an\n- * .\n- *\n- * Returns:\n- * {} A geometry.\n- */\n- \"polygon\": function(array) {\n- var rings = [];\n- var r, l;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- l = this.parseCoords[\"linestring\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- r = new OpenLayers.Geometry.LinearRing(l.components);\n- rings.push(r);\n- }\n- return new OpenLayers.Geometry.Polygon(rings);\n- },\n+ /**\n+ * Property: viewPortDiv\n+ * {HTMLDivElement} The element that represents the map viewport\n+ */\n+ viewPortDiv: null,\n \n- /**\n- * Method: parseCoords.multipolygon\n- * Convert a coordinate array from GeoJSON into an\n- * .\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {} A geometry.\n- */\n- \"multipolygon\": function(array) {\n- var polys = [];\n- var p = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- p = this.parseCoords[\"polygon\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- polys.push(p);\n- }\n- return new OpenLayers.Geometry.MultiPolygon(polys);\n- },\n+ /**\n+ * Property: layerContainerOrigin\n+ * {} The lonlat at which the later container was\n+ * re-initialized (on-zoom)\n+ */\n+ layerContainerOrigin: null,\n \n- /**\n- * Method: parseCoords.box\n- * Convert a coordinate array from GeoJSON into an\n- * .\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {} A geometry.\n- */\n- \"box\": function(array) {\n- if (array.length != 2) {\n- throw \"GeoJSON box coordinates must have 2 elements\";\n- }\n- return new OpenLayers.Geometry.Polygon([\n- new OpenLayers.Geometry.LinearRing([\n- new OpenLayers.Geometry.Point(array[0][0], array[0][1]),\n- new OpenLayers.Geometry.Point(array[1][0], array[0][1]),\n- new OpenLayers.Geometry.Point(array[1][0], array[1][1]),\n- new OpenLayers.Geometry.Point(array[0][0], array[1][1]),\n- new OpenLayers.Geometry.Point(array[0][0], array[0][1])\n- ])\n- ]);\n- }\n+ /**\n+ * Property: layerContainerDiv\n+ * {HTMLDivElement} The element that contains the layers.\n+ */\n+ layerContainerDiv: null,\n \n- },\n+ /**\n+ * APIProperty: layers\n+ * {Array()} Ordered list of layers in the map\n+ */\n+ layers: null,\n \n /**\n- * APIMethod: write\n- * Serialize a feature, geometry, array of features into a GeoJSON string.\n- *\n- * Parameters:\n- * obj - {Object} An , ,\n- * or an array of features.\n- * pretty - {Boolean} Structure the output with newlines and indentation.\n- * Default is false.\n+ * APIProperty: controls\n+ * {Array()} List of controls associated with the map.\n *\n- * Returns:\n- * {String} The GeoJSON string representation of the input geometry,\n- * features, or array of features.\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- write: function(obj, pretty) {\n- var geojson = {\n- \"type\": null\n- };\n- if (OpenLayers.Util.isArray(obj)) {\n- geojson.type = \"FeatureCollection\";\n- var numFeatures = obj.length;\n- geojson.features = new Array(numFeatures);\n- for (var i = 0; i < numFeatures; ++i) {\n- var element = obj[i];\n- if (!element instanceof OpenLayers.Feature.Vector) {\n- var msg = \"FeatureCollection only supports collections \" +\n- \"of features: \" + element;\n- throw msg;\n- }\n- geojson.features[i] = this.extract.feature.apply(\n- this, [element]\n- );\n- }\n- } else if (obj.CLASS_NAME.indexOf(\"OpenLayers.Geometry\") == 0) {\n- geojson = this.extract.geometry.apply(this, [obj]);\n- } else if (obj instanceof OpenLayers.Feature.Vector) {\n- geojson = this.extract.feature.apply(this, [obj]);\n- if (obj.layer && obj.layer.projection) {\n- geojson.crs = this.createCRSObject(obj);\n- }\n- }\n- return OpenLayers.Format.JSON.prototype.write.apply(this,\n- [geojson, pretty]);\n- },\n+ controls: null,\n \n /**\n- * Method: createCRSObject\n- * Create the CRS object for an object.\n- *\n- * Parameters:\n- * object - {} \n- *\n- * Returns:\n- * {Object} An object which can be assigned to the crs property\n- * of a GeoJSON object.\n+ * Property: popups\n+ * {Array()} List of popups associated with the map\n */\n- createCRSObject: function(object) {\n- var proj = object.layer.projection.toString();\n- var crs = {};\n- if (proj.match(/epsg:/i)) {\n- var code = parseInt(proj.substring(proj.indexOf(\":\") + 1));\n- if (code == 4326) {\n- crs = {\n- \"type\": \"name\",\n- \"properties\": {\n- \"name\": \"urn:ogc:def:crs:OGC:1.3:CRS84\"\n- }\n- };\n- } else {\n- crs = {\n- \"type\": \"name\",\n- \"properties\": {\n- \"name\": \"EPSG:\" + code\n- }\n- };\n- }\n- }\n- return crs;\n- },\n+ popups: null,\n \n /**\n- * Property: extract\n- * Object with properties corresponding to the GeoJSON types.\n- * Property values are functions that do the actual value extraction.\n+ * APIProperty: baseLayer\n+ * {} The currently selected base layer. This determines\n+ * min/max zoom level, projection, etc.\n */\n- extract: {\n- /**\n- * Method: extract.feature\n- * Return a partial GeoJSON object representing a single feature.\n- *\n- * Parameters:\n- * feature - {}\n- *\n- * Returns:\n- * {Object} An object representing the point.\n- */\n- 'feature': function(feature) {\n- var geom = this.extract.geometry.apply(this, [feature.geometry]);\n- var json = {\n- \"type\": \"Feature\",\n- \"properties\": feature.attributes,\n- \"geometry\": geom\n- };\n- if (feature.fid != null) {\n- json.id = feature.fid;\n- }\n- return json;\n- },\n+ baseLayer: null,\n \n- /**\n- * Method: extract.geometry\n- * Return a GeoJSON object representing a single geometry.\n- *\n- * Parameters:\n- * geometry - {}\n- *\n- * Returns:\n- * {Object} An object representing the geometry.\n- */\n- 'geometry': function(geometry) {\n- if (geometry == null) {\n- return null;\n- }\n- if (this.internalProjection && this.externalProjection) {\n- geometry = geometry.clone();\n- geometry.transform(this.internalProjection,\n- this.externalProjection);\n- }\n- var geometryType = geometry.CLASS_NAME.split('.')[2];\n- var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);\n- var json;\n- if (geometryType == \"Collection\") {\n- json = {\n- \"type\": \"GeometryCollection\",\n- \"geometries\": data\n- };\n- } else {\n- json = {\n- \"type\": geometryType,\n- \"coordinates\": data\n- };\n- }\n+ /**\n+ * Property: center\n+ * {} The current center of the map\n+ */\n+ center: null,\n \n- return json;\n- },\n+ /**\n+ * Property: resolution\n+ * {Float} The resolution of the map.\n+ */\n+ resolution: null,\n \n- /**\n- * Method: extract.point\n- * Return an array of coordinates from a point.\n- *\n- * Parameters:\n- * point - {}\n- *\n- * Returns: \n- * {Array} An array of coordinates representing the point.\n- */\n- 'point': function(point) {\n- return [point.x, point.y];\n- },\n+ /**\n+ * Property: zoom\n+ * {Integer} The current zoom level of the map\n+ */\n+ zoom: 0,\n \n- /**\n- * Method: extract.multipoint\n- * Return an array of point coordinates from a multipoint.\n- *\n- * Parameters:\n- * multipoint - {}\n- *\n- * Returns:\n- * {Array} An array of point coordinate arrays representing\n- * the multipoint.\n- */\n- 'multipoint': function(multipoint) {\n- var array = [];\n- for (var i = 0, len = multipoint.components.length; i < len; ++i) {\n- array.push(this.extract.point.apply(this, [multipoint.components[i]]));\n- }\n- return array;\n- },\n+ /**\n+ * Property: panRatio\n+ * {Float} The ratio of the current extent within\n+ * which panning will tween.\n+ */\n+ panRatio: 1.5,\n \n- /**\n- * Method: extract.linestring\n- * Return an array of coordinate arrays from a linestring.\n- *\n- * Parameters:\n- * linestring - {}\n- *\n- * Returns:\n- * {Array} An array of coordinate arrays representing\n- * the linestring.\n- */\n- 'linestring': function(linestring) {\n- var array = [];\n- for (var i = 0, len = linestring.components.length; i < len; ++i) {\n- array.push(this.extract.point.apply(this, [linestring.components[i]]));\n- }\n- return array;\n- },\n+ /**\n+ * APIProperty: options\n+ * {Object} The options object passed to the class constructor. Read-only.\n+ */\n+ options: null,\n \n- /**\n- * Method: extract.multilinestring\n- * Return an array of linestring arrays from a linestring.\n- * \n- * Parameters:\n- * multilinestring - {}\n- * \n- * Returns:\n- * {Array} An array of linestring arrays representing\n- * the multilinestring.\n- */\n- 'multilinestring': function(multilinestring) {\n- var array = [];\n- for (var i = 0, len = multilinestring.components.length; i < len; ++i) {\n- array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));\n- }\n- return array;\n- },\n+ // Options\n \n- /**\n- * Method: extract.polygon\n- * Return an array of linear ring arrays from a polygon.\n- *\n- * Parameters:\n- * polygon - {}\n- * \n- * Returns:\n- * {Array} An array of linear ring arrays representing the polygon.\n- */\n- 'polygon': function(polygon) {\n- var array = [];\n- for (var i = 0, len = polygon.components.length; i < len; ++i) {\n- array.push(this.extract.linestring.apply(this, [polygon.components[i]]));\n- }\n- return array;\n- },\n+ /**\n+ * 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- * Method: extract.multipolygon\n- * Return an array of polygon arrays from a multipolygon.\n- * \n- * Parameters:\n- * multipolygon - {}\n- * \n- * Returns:\n- * {Array} An array of polygon arrays representing\n- * the multipolygon\n- */\n- 'multipolygon': function(multipolygon) {\n- var array = [];\n- for (var i = 0, len = multipolygon.components.length; i < len; ++i) {\n- array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));\n- }\n- return array;\n- },\n+ /**\n+ * 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- * Method: extract.collection\n- * Return an array of geometries from a geometry collection.\n- * \n- * Parameters:\n- * collection - {}\n- * \n- * Returns:\n- * {Array} An array of geometry objects representing the geometry\n- * collection.\n- */\n- 'collection': function(collection) {\n- var len = collection.components.length;\n- var array = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- array[i] = this.extract.geometry.apply(\n- this, [collection.components[i]]\n- );\n- }\n- return array;\n- }\n+ /**\n+ * 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+ /**\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- CLASS_NAME: \"OpenLayers.Format.GeoJSON\"\n+ /**\n+ * APIProperty: minResolution\n+ * {Float}\n+ */\n+ minResolution: null,\n \n-});\n-/* ======================================================================\n- OpenLayers/Format/XML.js\n- ====================================================================== */\n+ /**\n+ * APIProperty: maxScale\n+ * {Float}\n+ */\n+ maxScale: null,\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * APIProperty: minScale\n+ * {Float}\n+ */\n+ minScale: null,\n \n-/**\n- * @requires OpenLayers/Format.js\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- * Class: OpenLayers.Format.XML\n- * Read and write XML. For cross-browser XML generation, use methods on an\n- * instance of the XML format class instead of on document.\n- * The DOM creation and traversing methods exposed here all mimic the\n- * W3C XML DOM methods. Create a new parser with the\n- * constructor.\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {\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- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs. Properties\n- * of this object should not be set individually. Read-only. All\n- * XML subclasses should have their own namespaces object. Use\n- * to add or set a namespace alias after construction.\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- namespaces: null,\n+ restrictedExtent: null,\n \n /**\n- * Property: namespaceAlias\n- * {Object} Mapping of namespace URI to namespace alias. This object\n- * is read-only. Use to add or set a namespace alias.\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- namespaceAlias: null,\n+ numZoomLevels: 16,\n \n /**\n- * Property: defaultPrefix\n- * {String} The default namespace alias for creating element nodes.\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- defaultPrefix: null,\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+ */\n+ displayProjection: null,\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+ * 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- readers: {},\n \n /**\n- * Property: writers\n- * As a compliment to the property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\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- writers: {},\n+ fallThrough: false,\n \n /**\n- * Property: xmldom\n- * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM\n- * object. It is not intended to be a browser sniffing property.\n- * Instead, the xmldom property is used instead of document\n- * where namespaced node creation methods are not supported. In all\n- * other browsers, this remains null.\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- xmldom: null,\n+ autoUpdateSize: true,\n \n /**\n- * Constructor: OpenLayers.Format.XML\n- * Construct an XML parser. The parser is used to read and write XML.\n- * Reading XML from a string returns a DOM element. Writing XML from\n- * a DOM element returns a string.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on\n- * the object.\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- initialize: function(options) {\n- if (window.ActiveXObject) {\n- this.xmldom = new ActiveXObject(\"Microsoft.XMLDOM\");\n- }\n- OpenLayers.Format.prototype.initialize.apply(this, [options]);\n- // clone the namespace object and set all namespace aliases\n- this.namespaces = OpenLayers.Util.extend({}, this.namespaces);\n- this.namespaceAlias = {};\n- for (var alias in this.namespaces) {\n- this.namespaceAlias[this.namespaces[alias]] = alias;\n- }\n- },\n+ eventListeners: null,\n \n /**\n- * APIMethod: destroy\n- * Clean up.\n+ * Property: panTween\n+ * {} Animated panning tween object, see panTo()\n */\n- destroy: function() {\n- this.xmldom = null;\n- OpenLayers.Format.prototype.destroy.apply(this, arguments);\n- },\n+ panTween: null,\n \n /**\n- * Method: setNamespace\n- * Set a namespace alias and URI for the format.\n- *\n- * Parameters:\n- * alias - {String} The namespace alias (prefix).\n- * uri - {String} The namespace URI.\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- setNamespace: function(alias, uri) {\n- this.namespaces[alias] = uri;\n- this.namespaceAlias[uri] = alias;\n- },\n+ panMethod: OpenLayers.Easing.Expo.easeOut,\n \n /**\n- * APIMethod: read\n- * Deserialize a XML string and return a DOM node.\n- *\n- * Parameters:\n- * text - {String} A XML string\n- \n- * Returns:\n- * {DOMElement} A DOM node\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- read: function(text) {\n- var index = text.indexOf('<');\n- if (index > 0) {\n- text = text.substring(index);\n- }\n- var node = OpenLayers.Util.Try(\n- OpenLayers.Function.bind((\n- function() {\n- var xmldom;\n- /**\n- * Since we want to be able to call this method on the prototype\n- * itself, this.xmldom may not exist even if in IE.\n- */\n- if (window.ActiveXObject && !this.xmldom) {\n- xmldom = new ActiveXObject(\"Microsoft.XMLDOM\");\n- } else {\n- xmldom = this.xmldom;\n+ panDuration: 50,\n \n- }\n- xmldom.loadXML(text);\n- return xmldom;\n- }\n- ), this),\n- function() {\n- return new DOMParser().parseFromString(text, 'text/xml');\n- },\n- function() {\n- var req = new XMLHttpRequest();\n- req.open(\"GET\", \"data:\" + \"text/xml\" +\n- \";charset=utf-8,\" + encodeURIComponent(text), false);\n- if (req.overrideMimeType) {\n- req.overrideMimeType(\"text/xml\");\n- }\n- req.send(null);\n- return req.responseXML;\n- }\n- );\n+ /**\n+ * Property: zoomTween\n+ * {} Animated zooming tween object, see zoomTo()\n+ */\n+ zoomTween: null,\n \n- if (this.keepData) {\n- this.data = node;\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- return node;\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- * APIMethod: write\n- * Serialize a DOM node into a XML string.\n- * \n- * Parameters:\n- * node - {DOMElement} A DOM node.\n- *\n- * Returns:\n- * {String} The XML string representation of the input node.\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- write: function(node) {\n- var data;\n- if (this.xmldom) {\n- data = node.xml;\n- } else {\n- var serializer = new XMLSerializer();\n- if (node.nodeType == 1) {\n- // Add nodes to a document before serializing. Everything else\n- // is serialized as is. This may need more work. See #1218 .\n- var doc = document.implementation.createDocument(\"\", \"\", null);\n- if (doc.importNode) {\n- node = doc.importNode(node, true);\n- }\n- doc.appendChild(node);\n- data = serializer.serializeToString(doc);\n- } else {\n- data = serializer.serializeToString(node);\n- }\n- }\n- return data;\n- },\n+ paddingForPopups: null,\n \n /**\n- * APIMethod: createElementNS\n- * Create a new element with namespace. This node can be appended to\n- * another node with the standard node.appendChild method. For\n- * cross-browser support, this method must be used instead of\n- * document.createElementNS.\n- *\n- * Parameters:\n- * uri - {String} Namespace URI for the element.\n- * name - {String} The qualified name of the element (prefix:localname).\n- * \n- * Returns:\n- * {Element} A DOM element with namespace.\n+ * Property: layerContainerOriginPx\n+ * {Object} Cached object representing the layer container origin (in pixels).\n */\n- createElementNS: function(uri, name) {\n- var element;\n- if (this.xmldom) {\n- if (typeof uri == \"string\") {\n- element = this.xmldom.createNode(1, name, uri);\n- } else {\n- element = this.xmldom.createNode(1, name, \"\");\n- }\n- } else {\n- element = document.createElementNS(uri, name);\n- }\n- return element;\n- },\n+ layerContainerOriginPx: null,\n \n /**\n- * APIMethod: createDocumentFragment\n- * Create a document fragment node that can be appended to another node\n- * created by createElementNS. This will call \n- * document.createDocumentFragment outside of IE. In IE, the ActiveX\n- * object's createDocumentFragment method is used.\n- *\n- * Returns:\n- * {Element} A document fragment.\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- createDocumentFragment: function() {\n- var element;\n- if (this.xmldom) {\n- element = this.xmldom.createDocumentFragment();\n- } else {\n- element = document.createDocumentFragment();\n- }\n- return element;\n- },\n+ minPx: null,\n \n /**\n- * APIMethod: createTextNode\n- * Create a text node. This node can be appended to another node with\n- * the standard node.appendChild method. For cross-browser support,\n- * this method must be used instead of document.createTextNode.\n- * \n- * Parameters:\n- * text - {String} The text of the node.\n- * \n- * Returns: \n- * {DOMElement} A DOM text node.\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- createTextNode: function(text) {\n- var node;\n- if (typeof text !== \"string\") {\n- text = String(text);\n- }\n- if (this.xmldom) {\n- node = this.xmldom.createTextNode(text);\n- } else {\n- node = document.createTextNode(text);\n- }\n- return node;\n- },\n+ maxPx: null,\n \n /**\n- * APIMethod: getElementsByTagNameNS\n- * Get a list of elements on a node given the namespace URI and local name.\n- * To return all nodes in a given namespace, use '*' for the name\n- * argument. To return all nodes of a given (local) name, regardless\n- * of namespace, use '*' for the uri argument.\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- * node - {Element} Node on which to search for other nodes.\n- * uri - {String} Namespace URI.\n- * name - {String} Local name of the tag (without the prefix).\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- * Returns:\n- * {NodeList} A node list or array of elements.\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- getElementsByTagNameNS: function(node, uri, name) {\n- var elements = [];\n- if (node.getElementsByTagNameNS) {\n- elements = node.getElementsByTagNameNS(uri, name);\n- } else {\n- // brute force method\n- var allNodes = node.getElementsByTagName(\"*\");\n- var potentialNode, fullName;\n- for (var i = 0, len = allNodes.length; i < len; ++i) {\n- potentialNode = allNodes[i];\n- fullName = (potentialNode.prefix) ?\n- (potentialNode.prefix + \":\" + name) : name;\n- if ((name == \"*\") || (fullName == potentialNode.nodeName)) {\n- if ((uri == \"*\") || (uri == potentialNode.namespaceURI)) {\n- elements.push(potentialNode);\n- }\n+ initialize: function(div, options) {\n+\n+ // If only one argument is provided, check if it is an object.\n+ if (arguments.length === 1 && typeof div === \"object\") {\n+ options = div;\n+ div = options && options.div;\n+ }\n+\n+ // Simple-type defaults are set in class definition. \n+ // Now set complex-type defaults \n+ this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,\n+ OpenLayers.Map.TILE_HEIGHT);\n+\n+ this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);\n+\n+ this.theme = OpenLayers._getScriptLocation() +\n+ 'theme/default/style.css';\n+\n+ // backup original options\n+ this.options = OpenLayers.Util.extend({}, options);\n+\n+ // now override default options \n+ OpenLayers.Util.extend(this, options);\n+\n+ var projCode = this.projection instanceof OpenLayers.Projection ?\n+ this.projection.projCode : this.projection;\n+ OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);\n+\n+ // allow extents and center to be arrays\n+ if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {\n+ this.maxExtent = new OpenLayers.Bounds(this.maxExtent);\n+ }\n+ if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {\n+ this.minExtent = new OpenLayers.Bounds(this.minExtent);\n+ }\n+ if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {\n+ this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);\n+ }\n+ if (this.center && !(this.center instanceof OpenLayers.LonLat)) {\n+ this.center = new OpenLayers.LonLat(this.center);\n+ }\n+\n+ // initialize layers array\n+ this.layers = [];\n+\n+ this.id = OpenLayers.Util.createUniqueID(\"OpenLayers.Map_\");\n+\n+ this.div = OpenLayers.Util.getElement(div);\n+ if (!this.div) {\n+ this.div = document.createElement(\"div\");\n+ this.div.style.height = \"1px\";\n+ this.div.style.width = \"1px\";\n+ }\n+\n+ OpenLayers.Element.addClass(this.div, 'olMap');\n+\n+ // the viewPortDiv is the outermost div we modify\n+ var id = this.id + \"_OpenLayers_ViewPort\";\n+ this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,\n+ \"relative\", null,\n+ \"hidden\");\n+ this.viewPortDiv.style.width = \"100%\";\n+ this.viewPortDiv.style.height = \"100%\";\n+ this.viewPortDiv.className = \"olMapViewport\";\n+ this.div.appendChild(this.viewPortDiv);\n+\n+ this.events = new OpenLayers.Events(\n+ this, this.viewPortDiv, null, this.fallThrough, {\n+ includeXY: true\n+ }\n+ );\n+\n+ if (OpenLayers.TileManager && this.tileManager !== null) {\n+ if (!(this.tileManager instanceof OpenLayers.TileManager)) {\n+ this.tileManager = new OpenLayers.TileManager(this.tileManager);\n+ }\n+ this.tileManager.addMap(this);\n+ }\n+\n+ // the layerContainerDiv is the one that holds all the layers\n+ id = this.id + \"_OpenLayers_Container\";\n+ this.layerContainerDiv = OpenLayers.Util.createDiv(id);\n+ this.layerContainerDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] - 1;\n+ this.layerContainerOriginPx = {\n+ x: 0,\n+ y: 0\n+ };\n+ this.applyTransform();\n+\n+ this.viewPortDiv.appendChild(this.layerContainerDiv);\n+\n+ this.updateSize();\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners);\n+ }\n+\n+ if (this.autoUpdateSize === true) {\n+ // updateSize on catching the window's resize\n+ // Note that this is ok, as updateSize() does nothing if the \n+ // map's size has not actually changed.\n+ this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize,\n+ this);\n+ OpenLayers.Event.observe(window, 'resize',\n+ this.updateSizeDestroy);\n+ }\n+\n+ // only append link stylesheet if the theme property is set\n+ if (this.theme) {\n+ // check existing links for equivalent url\n+ var addNode = true;\n+ var nodes = document.getElementsByTagName('link');\n+ for (var i = 0, len = nodes.length; i < len; ++i) {\n+ if (OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,\n+ this.theme)) {\n+ addNode = false;\n+ break;\n }\n }\n+ // only add a new node if one with an equivalent url hasn't already\n+ // been added\n+ if (addNode) {\n+ var cssNode = document.createElement('link');\n+ cssNode.setAttribute('rel', 'stylesheet');\n+ cssNode.setAttribute('type', 'text/css');\n+ cssNode.setAttribute('href', this.theme);\n+ document.getElementsByTagName('head')[0].appendChild(cssNode);\n+ }\n }\n- return elements;\n- },\n \n- /**\n- * APIMethod: getAttributeNodeNS\n- * Get an attribute node given the namespace URI and local name.\n- * \n- * Parameters:\n- * node - {Element} Node on which to search for attribute nodes.\n- * uri - {String} Namespace URI.\n- * name - {String} Local name of the attribute (without the prefix).\n- * \n- * Returns:\n- * {DOMElement} An attribute node or null if none found.\n- */\n- getAttributeNodeNS: function(node, uri, name) {\n- var attributeNode = null;\n- if (node.getAttributeNodeNS) {\n- attributeNode = node.getAttributeNodeNS(uri, name);\n- } else {\n- var attributes = node.attributes;\n- var potentialNode, fullName;\n- for (var i = 0, len = attributes.length; i < len; ++i) {\n- potentialNode = attributes[i];\n- if (potentialNode.namespaceURI == uri) {\n- fullName = (potentialNode.prefix) ?\n- (potentialNode.prefix + \":\" + name) : name;\n- if (fullName == potentialNode.nodeName) {\n- attributeNode = potentialNode;\n- break;\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- return attributeNode;\n- },\n \n- /**\n- * APIMethod: getAttributeNS\n- * Get an attribute value given the namespace URI and local name.\n- * \n- * Parameters:\n- * node - {Element} Node on which to search for an attribute.\n- * uri - {String} Namespace URI.\n- * name - {String} Local name of the attribute (without the prefix).\n- * \n- * Returns:\n- * {String} An attribute value or and empty string if none found.\n- */\n- getAttributeNS: function(node, uri, name) {\n- var attributeValue = \"\";\n- if (node.getAttributeNS) {\n- attributeValue = node.getAttributeNS(uri, name) || \"\";\n- } else {\n- var attributeNode = this.getAttributeNodeNS(node, uri, name);\n- if (attributeNode) {\n- attributeValue = attributeNode.nodeValue;\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- return attributeValue;\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: getChildValue\n- * Get the textual value of the node if it exists, or return an\n- * optional default string. Returns an empty string if no first child\n- * exists and no default value is supplied.\n- *\n- * Parameters:\n- * node - {DOMElement} The element used to look for a first child value.\n- * def - {String} Optional string to return in the event that no\n- * first child value exists.\n+ /** \n+ * APIMethod: getViewport\n+ * Get the DOMElement representing the view port.\n *\n * Returns:\n- * {String} The value of the first child of the given node.\n+ * {DOMElement}\n */\n- getChildValue: function(node, def) {\n- var value = def || \"\";\n- if (node) {\n- for (var child = node.firstChild; child; child = child.nextSibling) {\n- switch (child.nodeType) {\n- case 3: // text node\n- case 4: // cdata section\n- value += child.nodeValue;\n- }\n- }\n- }\n- return value;\n+ getViewport: function() {\n+ return this.viewPortDiv;\n },\n \n /**\n- * APIMethod: isSimpleContent\n- * Test if the given node has only simple content (i.e. no child element\n- * nodes).\n- *\n+ * APIMethod: render\n+ * Render the map to a specified container.\n+ * \n * Parameters:\n- * node - {DOMElement} An element node.\n- *\n- * Returns:\n- * {Boolean} The node has no child element nodes (nodes of type 1). \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- isSimpleContent: function(node) {\n- var simple = true;\n- for (var child = node.firstChild; child; child = child.nextSibling) {\n- if (child.nodeType === 1) {\n- simple = false;\n- break;\n- }\n- }\n- return simple;\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: contentType\n- * Determine the content type for a given node.\n- *\n- * Parameters:\n- * node - {DOMElement}\n- *\n- * Returns:\n- * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}\n- * if the node has no, simple, complex, or mixed content.\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- contentType: function(node) {\n- var simple = false,\n- complex = false;\n+ unloadDestroy: null,\n \n- var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;\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- for (var child = node.firstChild; child; child = child.nextSibling) {\n- switch (child.nodeType) {\n- case 1: // element\n- complex = true;\n- break;\n- case 8: // comment\n- break;\n- default:\n- simple = true;\n- }\n- if (complex && simple) {\n- break;\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- if (complex && simple) {\n- type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;\n- } else if (complex) {\n- return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;\n- } else if (simple) {\n- return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;\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- return type;\n- },\n \n- /**\n- * APIMethod: hasAttributeNS\n- * Determine whether a node has a particular attribute matching the given\n- * name and namespace.\n- * \n- * Parameters:\n- * node - {Element} Node on which to search for an attribute.\n- * uri - {String} Namespace URI.\n- * name - {String} Local name of the attribute (without the prefix).\n- * \n- * Returns:\n- * {Boolean} The node has an attribute matching the name and namespace.\n- */\n- hasAttributeNS: function(node, uri, name) {\n- var found = false;\n- if (node.hasAttributeNS) {\n- found = node.hasAttributeNS(uri, name);\n- } else {\n- found = !!this.getAttributeNodeNS(node, uri, name);\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- return found;\n- },\n \n- /**\n- * APIMethod: setAttributeNS\n- * Adds a new attribute or changes the value of an attribute with the given\n- * namespace and name.\n- *\n- * Parameters:\n- * node - {Element} Element node on which to set the attribute.\n- * uri - {String} Namespace URI for the attribute.\n- * name - {String} Qualified name (prefix:localname) for the attribute.\n- * value - {String} Attribute value.\n- */\n- setAttributeNS: function(node, uri, name, value) {\n- if (node.setAttributeNS) {\n- node.setAttributeNS(uri, name, value);\n- } else {\n- if (this.xmldom) {\n- if (uri) {\n- var attribute = node.ownerDocument.createNode(\n- 2, name, uri\n- );\n- attribute.nodeValue = value;\n- node.setAttributeNode(attribute);\n- } else {\n- node.setAttribute(name, value);\n- }\n- } else {\n- throw \"setAttributeNS not implemented\";\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- },\n-\n- /**\n- * Method: createElementNSPlus\n- * Shorthand for creating namespaced elements with optional attributes and\n- * child text nodes.\n- *\n- * Parameters:\n- * name - {String} The qualified node name.\n- * options - {Object} Optional object for node configuration.\n- *\n- * Valid options:\n- * uri - {String} Optional namespace uri for the element - supply a prefix\n- * instead if the namespace uri is a property of the format's namespace\n- * object.\n- * attributes - {Object} Optional attributes to be set using the\n- * method.\n- * value - {String} Optional text to be appended as a text node.\n- *\n- * Returns:\n- * {Element} An element node.\n- */\n- createElementNSPlus: function(name, options) {\n- options = options || {};\n- // order of prefix preference\n- // 1. in the uri option\n- // 2. in the prefix option\n- // 3. in the qualified name\n- // 4. from the defaultPrefix\n- var uri = options.uri || this.namespaces[options.prefix];\n- if (!uri) {\n- var loc = name.indexOf(\":\");\n- uri = this.namespaces[name.substring(0, loc)];\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 (!uri) {\n- uri = this.namespaces[this.defaultPrefix];\n+ if (this.viewPortDiv && this.viewPortDiv.parentNode) {\n+ this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);\n }\n- var node = this.createElementNS(uri, name);\n- if (options.attributes) {\n- this.setAttributes(node, options.attributes);\n+ this.viewPortDiv = null;\n+\n+ if (this.tileManager) {\n+ this.tileManager.removeMap(this);\n+ this.tileManager = null;\n }\n- var value = options.value;\n- if (value != null) {\n- node.appendChild(this.createTextNode(value));\n+\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n+ this.eventListeners = null;\n }\n- return node;\n+ this.events.destroy();\n+ this.events = null;\n+\n+ this.options = null;\n },\n \n /**\n- * Method: setAttributes\n- * Set multiple attributes given key value pairs from an object.\n+ * APIMethod: setOptions\n+ * Change the map options\n *\n * Parameters:\n- * node - {Element} An element node.\n- * obj - {Object || Array} An object whose properties represent attribute\n- * names and values represent attribute values. If an attribute name\n- * is a qualified name (\"prefix:local\"), the prefix will be looked up\n- * in the parsers {namespaces} object. If the prefix is found,\n- * setAttributeNS will be used instead of setAttribute.\n+ * options - {Object} Hashtable of options to tag to the map\n */\n- setAttributes: function(node, obj) {\n- var value, uri;\n- for (var name in obj) {\n- if (obj[name] != null && obj[name].toString) {\n- value = obj[name].toString();\n- // check for qualified attribute name (\"prefix:local\")\n- uri = this.namespaces[name.substring(0, name.indexOf(\":\"))] || null;\n- this.setAttributeNS(node, uri, name, value);\n- }\n- }\n+ setOptions: function(options) {\n+ var updatePxExtent = this.minPx &&\n+ options.restrictedExtent != this.restrictedExtent;\n+ OpenLayers.Util.extend(this, options);\n+ // force recalculation of minPx and maxPx\n+ updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {\n+ forceZoomChange: true\n+ });\n },\n \n /**\n- * Method: readNode\n- * Shorthand for applying one of the named readers given the node\n- * namespace and local name. Readers take two args (node, obj) and\n- * generally extend or modify the second.\n- *\n- * Parameters:\n- * node - {DOMElement} The node to be read (required).\n- * obj - {Object} The object to be modified (optional).\n+ * APIMethod: getTileSize\n+ * Get the tile size for the map\n *\n * Returns:\n- * {Object} The input object, modified (or a new one if none was provided).\n+ * {}\n */\n- readNode: function(node, obj) {\n- if (!obj) {\n- obj = {};\n- }\n- var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI] : this.defaultPrefix];\n- if (group) {\n- var local = node.localName || node.nodeName.split(\":\").pop();\n- var reader = group[local] || group[\"*\"];\n- if (reader) {\n- reader.apply(this, [node, obj]);\n- }\n- }\n- return obj;\n+ getTileSize: function() {\n+ return this.tileSize;\n },\n \n+\n /**\n- * Method: readChildNodes\n- * Shorthand for applying the named readers to all children of a node.\n- * For each child of type 1 (element), is called.\n+ * APIMethod: getBy\n+ * Get a list of objects given a property and a match item.\n *\n * Parameters:\n- * node - {DOMElement} The node to be read (required).\n- * obj - {Object} The object to be modified (optional).\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- * {Object} The input object, modified.\n+ * {Array} An array of items where the given property matches the given\n+ * criteria.\n */\n- readChildNodes: function(node, obj) {\n- if (!obj) {\n- obj = {};\n- }\n- var children = node.childNodes;\n- var child;\n- for (var i = 0, len = children.length; i < len; ++i) {\n- child = children[i];\n- if (child.nodeType == 1) {\n- this.readNode(child, obj);\n- }\n- }\n- return obj;\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- * Method: writeNode\n- * Shorthand for applying one of the named writers and appending the\n- * results to a node. If a qualified name is not provided for the\n- * second argument (and a local name is used instead), the namespace\n- * of the parent node will be assumed.\n+ * APIMethod: getLayersBy\n+ * Get a list of layers with properties matching the given criteria.\n *\n * Parameters:\n- * name - {String} The name of a node to generate. If a qualified name\n- * (e.g. \"pre:Name\") is used, the namespace prefix is assumed to be\n- * in the group. If a local name is used (e.g. \"Name\") then\n- * the namespace of the parent is assumed. If a local name is used\n- * and no parent is supplied, then the default namespace is assumed.\n- * obj - {Object} Structure containing data for the writer.\n- * parent - {DOMElement} Result will be appended to this node. If no parent\n- * is supplied, the node will not be appended to anything.\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- * {DOMElement} The child node.\n+ * {Array()} A list of layers matching the given criteria.\n+ * An empty array is returned if no matches are found.\n */\n- writeNode: function(name, obj, parent) {\n- var prefix, local;\n- var split = name.indexOf(\":\");\n- if (split > 0) {\n- prefix = name.substring(0, split);\n- local = name.substring(split + 1);\n- } else {\n- if (parent) {\n- prefix = this.namespaceAlias[parent.namespaceURI];\n- } else {\n- prefix = this.defaultPrefix;\n- }\n- local = name;\n- }\n- var child = this.writers[prefix][local].apply(this, [obj]);\n- if (parent) {\n- parent.appendChild(child);\n- }\n- return child;\n+ getLayersBy: function(property, match) {\n+ return this.getBy(\"layers\", property, match);\n },\n \n /**\n- * APIMethod: getChildEl\n- * Get the first child element. Optionally only return the first child\n- * if it matches the given name and namespace URI.\n+ * APIMethod: getLayersByName\n+ * Get a list of layers with names matching the given name.\n *\n * Parameters:\n- * node - {DOMElement} The parent node.\n- * name - {String} Optional node name (local) to search for.\n- * uri - {String} Optional namespace URI to search for.\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- * {DOMElement} The first child. Returns null if no element is found, if\n- * something significant besides an element is found, or if the element\n- * found does not match the optional name and uri.\n+ * {Array()} A list of layers matching the given name.\n+ * An empty array is returned if no matches are found.\n */\n- getChildEl: function(node, name, uri) {\n- return node && this.getThisOrNextEl(node.firstChild, name, uri);\n+ getLayersByName: function(match) {\n+ return this.getLayersBy(\"name\", match);\n },\n \n /**\n- * APIMethod: getNextEl\n- * Get the next sibling element. Optionally get the first sibling only\n- * if it matches the given local name and namespace URI.\n+ * APIMethod: getLayersByClass\n+ * Get a list of layers of a given class (CLASS_NAME).\n *\n * Parameters:\n- * node - {DOMElement} The node.\n- * name - {String} Optional local name of the sibling to search for.\n- * uri - {String} Optional namespace URI of the sibling to search for.\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- * {DOMElement} The next sibling element. Returns null if no element is\n- * found, something significant besides an element is found, or the\n- * found element does not match the optional name and uri.\n+ * {Array()} A list of layers matching the given class.\n+ * An empty array is returned if no matches are found.\n */\n- getNextEl: function(node, name, uri) {\n- return node && this.getThisOrNextEl(node.nextSibling, name, uri);\n+ getLayersByClass: function(match) {\n+ return this.getLayersBy(\"CLASS_NAME\", match);\n },\n \n /**\n- * Method: getThisOrNextEl\n- * Return this node or the next element node. Optionally get the first\n- * sibling with the given local name or namespace URI.\n+ * APIMethod: getControlsBy\n+ * Get a list of controls with properties matching the given criteria.\n *\n * Parameters:\n- * node - {DOMElement} The node.\n- * name - {String} Optional local name of the sibling to search for.\n- * uri - {String} Optional namespace URI of the sibling to search for.\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- * {DOMElement} The next sibling element. Returns null if no element is\n- * found, something significant besides an element is found, or the\n- * found element does not match the query.\n+ * {Array()} A list of controls matching the given\n+ * criteria. An empty array is returned if no matches are found.\n */\n- getThisOrNextEl: function(node, name, uri) {\n- outer: for (var sibling = node; sibling; sibling = sibling.nextSibling) {\n- switch (sibling.nodeType) {\n- case 1: // Element\n- if ((!name || name === (sibling.localName || sibling.nodeName.split(\":\").pop())) &&\n- (!uri || uri === sibling.namespaceURI)) {\n- // matches\n- break outer;\n- }\n- sibling = null;\n- break outer;\n- case 3: // Text\n- if (/^\\s*$/.test(sibling.nodeValue)) {\n- break;\n- }\n- case 4: // CDATA\n- case 6: // ENTITY_NODE\n- case 12: // NOTATION_NODE\n- case 10: // DOCUMENT_TYPE_NODE\n- case 11: // DOCUMENT_FRAGMENT_NODE\n- sibling = null;\n- break outer;\n- } // ignore comments and processing instructions\n- }\n- return sibling || null;\n+ getControlsBy: function(property, match) {\n+ return this.getBy(\"controls\", property, match);\n },\n \n /**\n- * APIMethod: lookupNamespaceURI\n- * Takes a prefix and returns the namespace URI associated with it on the given\n- * node if found (and null if not). Supplying null for the prefix will\n- * return the default namespace.\n- *\n- * For browsers that support it, this calls the native lookupNamesapceURI\n- * function. In other browsers, this is an implementation of\n- * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.\n+ * APIMethod: getControlsByClass\n+ * Get a list of controls of a given class (CLASS_NAME).\n *\n- * For browsers that don't support the attribute.ownerElement property, this\n- * method cannot be called on attribute nodes.\n- * \n * Parameters:\n- * node - {DOMElement} The node from which to start looking.\n- * prefix - {String} The prefix to lookup or null to lookup the default namespace.\n- * \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- * {String} The namespace URI for the given prefix. Returns null if the prefix\n- * cannot be found or the node is the wrong type.\n+ * {Array()} A list of controls matching the given class.\n+ * An empty array is returned if no matches are found.\n */\n- lookupNamespaceURI: function(node, prefix) {\n- var uri = null;\n- if (node) {\n- if (node.lookupNamespaceURI) {\n- uri = node.lookupNamespaceURI(prefix);\n- } else {\n- outer: switch (node.nodeType) {\n- case 1: // ELEMENT_NODE\n- if (node.namespaceURI !== null && node.prefix === prefix) {\n- uri = node.namespaceURI;\n- break outer;\n- }\n- var len = node.attributes.length;\n- if (len) {\n- var attr;\n- for (var i = 0; i < len; ++i) {\n- attr = node.attributes[i];\n- if (attr.prefix === \"xmlns\" && attr.name === \"xmlns:\" + prefix) {\n- uri = attr.value || null;\n- break outer;\n- } else if (attr.name === \"xmlns\" && prefix === null) {\n- uri = attr.value || null;\n- break outer;\n- }\n- }\n- }\n- uri = this.lookupNamespaceURI(node.parentNode, prefix);\n- break outer;\n- case 2: // ATTRIBUTE_NODE\n- uri = this.lookupNamespaceURI(node.ownerElement, prefix);\n- break outer;\n- case 9: // DOCUMENT_NODE\n- uri = this.lookupNamespaceURI(node.documentElement, prefix);\n- break outer;\n- case 6: // ENTITY_NODE\n- case 12: // NOTATION_NODE\n- case 10: // DOCUMENT_TYPE_NODE\n- case 11: // DOCUMENT_FRAGMENT_NODE\n- break outer;\n- default:\n- // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),\n- // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)\n- uri = this.lookupNamespaceURI(node.parentNode, prefix);\n- break outer;\n- }\n- }\n- }\n- return uri;\n+ getControlsByClass: function(match) {\n+ return this.getControlsBy(\"CLASS_NAME\", match);\n },\n \n+ /********************************************************/\n+ /* */\n+ /* Layer Functions */\n+ /* */\n+ /* The following functions deal with adding and */\n+ /* removing Layers to and from the Map */\n+ /* */\n+ /********************************************************/\n+\n /**\n- * Method: getXMLDoc\n- * Get an XML document for nodes that are not supported in HTML (e.g.\n- * createCDATASection). On IE, this will either return an existing or\n- * create a new on the instance. On other browsers, this will\n- * either return an existing or create a new shared document (see\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- * {XMLDocument}\n+ * {} The Layer with the corresponding id from the map's \n+ * layer collection, or null if not found.\n */\n- getXMLDoc: function() {\n- if (!OpenLayers.Format.XML.document && !this.xmldom) {\n- if (document.implementation && document.implementation.createDocument) {\n- OpenLayers.Format.XML.document =\n- document.implementation.createDocument(\"\", \"\", null);\n- } else if (!this.xmldom && window.ActiveXObject) {\n- this.xmldom = new ActiveXObject(\"Microsoft.XMLDOM\");\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 OpenLayers.Format.XML.document || this.xmldom;\n+ return foundLayer;\n },\n \n- CLASS_NAME: \"OpenLayers.Format.XML\"\n-\n-});\n-\n-OpenLayers.Format.XML.CONTENT_TYPE = {\n- EMPTY: 0,\n- SIMPLE: 1,\n- COMPLEX: 2,\n- MIXED: 3\n-};\n-\n-/**\n- * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI\n- * Takes a prefix and returns the namespace URI associated with it on the given\n- * node if found (and null if not). Supplying null for the prefix will\n- * return the default namespace.\n- *\n- * For browsers that support it, this calls the native lookupNamesapceURI\n- * function. In other browsers, this is an implementation of\n- * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.\n- *\n- * For browsers that don't support the attribute.ownerElement property, this\n- * method cannot be called on attribute nodes.\n- * \n- * Parameters:\n- * node - {DOMElement} The node from which to start looking.\n- * prefix - {String} The prefix to lookup or null to lookup the default namespace.\n- * \n- * Returns:\n- * {String} The namespace URI for the given prefix. Returns null if the prefix\n- * cannot be found or the node is the wrong type.\n- */\n-OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(\n- OpenLayers.Format.XML.prototype.lookupNamespaceURI,\n- OpenLayers.Format.XML.prototype\n-);\n-\n-/**\n- * Property: OpenLayers.Format.XML.document\n- * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes,\n- * like document.createCDATASection.\n- */\n-OpenLayers.Format.XML.document = null;\n-/* ======================================================================\n- OpenLayers/Format/OGCExceptionReport.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.OGCExceptionReport\n- * Class to read exception reports for various OGC services and versions.\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n+ * Method: setLayerZIndex\n+ * \n+ * Parameters:\n+ * layer - {} \n+ * zIdx - {int} \n */\n- namespaces: {\n- ogc: \"http://www.opengis.net/ogc\"\n+ setLayerZIndex: function(layer, zIdx) {\n+ layer.setZIndex(\n+ this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] +\n+ zIdx * 5);\n },\n \n /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n+ * Method: resetLayersZIndex\n+ * Reset each layer's z-index based on layer's array index\n */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n+ resetLayersZIndex: function() {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ this.setLayerZIndex(layer, i);\n+ }\n },\n \n /**\n- * Property: defaultPrefix\n- */\n- defaultPrefix: \"ogc\",\n-\n- /**\n- * Constructor: OpenLayers.Format.OGCExceptionReport\n- * Create a new parser for OGC exception reports.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read OGC exception report data from a string, and return an object with\n- * information about the exceptions.\n+ * APIMethod: addLayer\n *\n * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n+ * layer - {} \n *\n * Returns:\n- * {Object} Information about the exceptions that occurred.\n+ * {Boolean} True if the layer has been added to the map.\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- var exceptionInfo = {\n- exceptionReport: null\n- };\n- if (root) {\n- this.readChildNodes(data, exceptionInfo);\n- if (exceptionInfo.exceptionReport === null) {\n- // fall-back to OWSCommon since this is a common output format for exceptions\n- // we cannot easily use the ows readers directly since they differ for 1.0 and 1.1\n- exceptionInfo = new OpenLayers.Format.OWSCommon().read(data);\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- return exceptionInfo;\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- \"ogc\": {\n- \"ServiceExceptionReport\": function(node, obj) {\n- obj.exceptionReport = {\n- exceptions: []\n- };\n- this.readChildNodes(node, obj.exceptionReport);\n- },\n- \"ServiceException\": function(node, exceptionReport) {\n- var exception = {\n- code: node.getAttribute(\"code\"),\n- locator: node.getAttribute(\"locator\"),\n- text: this.getChildValue(node)\n- };\n- exceptionReport.exceptions.push(exception);\n- }\n+ if (this.events.triggerEvent(\"preaddlayer\", {\n+ layer: layer\n+ }) === false) {\n+ return false;\n+ }\n+ if (this.allOverlays) {\n+ layer.isBaseLayer = false;\n }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.OGCExceptionReport\"\n \n-});\n-/* ======================================================================\n- OpenLayers/Format/XML/VersionedOGC.js\n- ====================================================================== */\n+ layer.div.className = \"olLayerDiv\";\n+ layer.div.style.overflow = \"\";\n+ this.setLayerZIndex(layer, this.layers.length);\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under 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 (layer.isFixed) {\n+ this.viewPortDiv.appendChild(layer.div);\n+ } else {\n+ this.layerContainerDiv.appendChild(layer.div);\n+ }\n+ this.layers.push(layer);\n+ layer.setMap(this);\n \n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/OGCExceptionReport.js\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-/**\n- * Class: OpenLayers.Format.XML.VersionedOGC\n- * Base class for versioned formats, i.e. a format which supports multiple\n- * versions.\n- *\n- * To enable checking if parsing succeeded, you will need to define a property\n- * called errorProperty on the parser you want to check. The parser will then\n- * check the returned object to see if that property is present. If it is, it\n- * assumes the parsing was successful. If it is not present (or is null), it will\n- * pass the document through an OGCExceptionReport parser.\n- * \n- * If errorProperty is undefined for the parser, this error checking mechanism\n- * will be disabled.\n- *\n- *\n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, {\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- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found.\n- */\n- defaultVersion: null,\n+ return true;\n+ },\n \n /**\n- * APIProperty: version\n- * {String} Specify a version string if one is known.\n+ * APIMethod: addLayers \n+ *\n+ * Parameters:\n+ * layers - {Array()} \n */\n- version: null,\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- * APIProperty: profile\n- * {String} If provided, use a custom profile.\n+ /** \n+ * APIMethod: removeLayer\n+ * Removes a layer from the map by removing its visual element (the \n+ * layer.div property), then removing it from the map's internal list \n+ * of layers, setting the layer's map property to null. \n+ * \n+ * a \"removelayer\" event is triggered.\n+ * \n+ * very worthy of mention is that simply removing a layer from a map\n+ * will not cause the removal of any popups which may have been created\n+ * by the layer. this is due to the fact that it was decided at some\n+ * point that popups would not belong to layers. thus there is no way \n+ * for us to know here to which layer the popup belongs.\n+ * \n+ * A simple solution to this is simply to call destroy() on the layer.\n+ * the default OpenLayers.Layer class's destroy() function\n+ * automatically takes care to remove itself from whatever map it has\n+ * been attached to. \n+ * \n+ * The correct solution is for the layer itself to register an \n+ * event-handler on \"removelayer\" and when it is called, if it \n+ * recognizes itself as the layer being removed, then it cycles through\n+ * its own personal list of popups, removing them from the map.\n+ * \n+ * Parameters:\n+ * layer - {} \n+ * setNewBaseLayer - {Boolean} Default is true\n */\n- profile: null,\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- /**\n- * APIProperty: allowFallback\n- * {Boolean} If a profiled parser cannot be found for the returned version,\n- * use a non-profiled parser as the fallback. Application code using this\n- * should take into account that the return object structure might be\n- * missing the specifics of the profile. Defaults to false.\n- */\n- allowFallback: false,\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- /**\n- * Property: name\n- * {String} The name of this parser, this is the part of the CLASS_NAME\n- * except for \"OpenLayers.Format.\"\n- */\n- name: null,\n+ // if we removed the base layer, need to set a new one\n+ if (this.baseLayer == layer) {\n+ this.baseLayer = null;\n+ if (setNewBaseLayer) {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var iLayer = this.layers[i];\n+ if (iLayer.isBaseLayer || this.allOverlays) {\n+ this.setBaseLayer(iLayer);\n+ break;\n+ }\n+ }\n+ }\n+ }\n \n- /**\n- * APIProperty: stringifyOutput\n- * {Boolean} If true, write will return a string otherwise a DOMElement.\n- * Default is false.\n- */\n- stringifyOutput: false,\n+ this.resetLayersZIndex();\n \n- /**\n- * Property: parser\n- * {Object} Instance of the versioned parser. Cached for multiple read and\n- * write calls of the same version.\n- */\n- parser: null,\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- * Constructor: OpenLayers.Format.XML.VersionedOGC.\n- * Constructor.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on\n- * the object.\n+ * APIMethod: getNumLayers\n+ * \n+ * Returns:\n+ * {Int} The number of layers attached to the map.\n */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- var className = this.CLASS_NAME;\n- this.name = className.substring(className.lastIndexOf(\".\") + 1);\n+ getNumLayers: function() {\n+ return this.layers.length;\n },\n \n- /**\n- * Method: getVersion\n- * Returns the version to use. Subclasses can override this function\n- * if a different version detection is needed.\n+ /** \n+ * APIMethod: getLayerIndex\n *\n * Parameters:\n- * root - {DOMElement}\n- * options - {Object} Optional configuration object.\n+ * layer - {}\n *\n * Returns:\n- * {String} The version to use.\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- getVersion: function(root, options) {\n- var version;\n- // read\n- if (root) {\n- version = this.version;\n- if (!version) {\n- version = root.getAttribute(\"version\");\n- if (!version) {\n- version = this.defaultVersion;\n- }\n- }\n- } else { // write\n- version = (options && options.version) ||\n- this.version || this.defaultVersion;\n- }\n- return version;\n+ getLayerIndex: function(layer) {\n+ return OpenLayers.Util.indexOf(this.layers, layer);\n },\n \n- /**\n- * Method: getParser\n- * Get an instance of the cached parser if available, otherwise create one.\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- * version - {String}\n- *\n- * Returns:\n- * {}\n+ * layer - {} \n+ * idx - {int} \n */\n- getParser: function(version) {\n- version = version || this.defaultVersion;\n- var profile = this.profile ? \"_\" + this.profile : \"\";\n- if (!this.parser || this.parser.VERSION != version) {\n- var format = OpenLayers.Format[this.name][\n- \"v\" + version.replace(/\\./g, \"_\") + profile\n- ];\n- if (!format) {\n- if (profile !== \"\" && this.allowFallback) {\n- // fallback to the non-profiled version of the parser\n- profile = \"\";\n- format = OpenLayers.Format[this.name][\n- \"v\" + version.replace(/\\./g, \"_\")\n- ];\n- }\n- if (!format) {\n- throw \"Can't find a \" + this.name + \" parser for version \" +\n- version + profile;\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- this.parser = new format(this.options);\n }\n- return this.parser;\n },\n \n- /**\n- * APIMethod: write\n- * Write a document.\n- *\n- * Parameters:\n- * obj - {Object} An object representing the document.\n- * options - {Object} Optional configuration object.\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- * Returns:\n- * {String} The document as a string\n+ * Paremeters:\n+ * layer - {} \n+ * delta - {int} \n */\n- write: function(obj, options) {\n- var version = this.getVersion(null, options);\n- this.parser = this.getParser(version);\n- var root = this.parser.write(obj, options);\n- if (this.stringifyOutput === false) {\n- return root;\n- } else {\n- return OpenLayers.Format.XML.prototype.write.apply(this, [root]);\n- }\n+ raiseLayer: function(layer, delta) {\n+ var idx = this.getLayerIndex(layer) + delta;\n+ this.setLayerIndex(layer, idx);\n },\n \n- /**\n- * APIMethod: read\n- * Read a doc and return an object representing the document.\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- * data - {String | DOMElement} Data to read.\n- * options - {Object} Options for the reader.\n- *\n- * Returns:\n- * {Object} An object representing the document.\n+ * newBaseLayer - {}\n */\n- read: function(data, options) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- var root = data.documentElement;\n- var version = this.getVersion(root);\n- this.parser = this.getParser(version); // Select the parser\n- var obj = this.parser.read(data, options); // Parse the data\n+ setBaseLayer: function(newBaseLayer) {\n \n- var errorProperty = this.parser.errorProperty || null;\n- if (errorProperty !== null && obj[errorProperty] === undefined) {\n- // an error must have happened, so parse it and report back\n- var format = new OpenLayers.Format.OGCExceptionReport();\n- obj.error = format.read(data);\n- }\n- obj.version = version;\n- return obj;\n- },\n+ if (newBaseLayer != this.baseLayer) {\n \n- CLASS_NAME: \"OpenLayers.Format.XML.VersionedOGC\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/OWSCommon.js\n- ====================================================================== */\n+ // ensure newBaseLayer is already loaded\n+ if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ // 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-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n+ // make the old base layer invisible \n+ if (this.baseLayer != null && !this.allOverlays) {\n+ this.baseLayer.setVisibility(false);\n+ }\n \n-/**\n- * Class: OpenLayers.Format.OWSCommon\n- * Read OWSCommon. Create a new instance with the \n- * constructor.\n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.OWSCommon = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ // set new baselayer\n+ this.baseLayer = newBaseLayer;\n \n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.0.0\".\n- */\n- defaultVersion: \"1.0.0\",\n+ if (!this.allOverlays || this.baseLayer.visibility) {\n+ this.baseLayer.setVisibility(true);\n+ // Layer may previously have been visible but not in range.\n+ // In this case we need to redraw it to make it visible.\n+ if (this.baseLayer.inRange === false) {\n+ this.baseLayer.redraw();\n+ }\n+ }\n \n- /**\n- * Constructor: OpenLayers.Format.OWSCommon\n- * Create a new parser for OWSCommon.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\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- /**\n- * Method: getVersion\n- * Returns the version to use. Subclasses can override this function\n- * if a different version detection is needed.\n- *\n- * Parameters:\n- * root - {DOMElement}\n- * options - {Object} Optional configuration object.\n- *\n- * Returns:\n- * {String} The version to use.\n- */\n- getVersion: function(root, options) {\n- var version = this.version;\n- if (!version) {\n- // remember version does not correspond to the OWS version\n- // it corresponds to the WMS/WFS/WCS etc. request version\n- var uri = root.getAttribute(\"xmlns:ows\");\n- // the above will fail if the namespace prefix is different than\n- // ows and if the namespace is declared on a different element\n- if (uri && uri.substring(uri.lastIndexOf(\"/\") + 1) === \"1.1\") {\n- version = \"1.1.0\";\n- }\n- if (!version) {\n- version = this.defaultVersion;\n+ this.events.triggerEvent(\"changebaselayer\", {\n+ layer: this.baseLayer\n+ });\n }\n }\n- return version;\n },\n \n- /**\n- * APIMethod: read\n- * Read an OWSCommon document and return an object.\n- *\n- * Parameters:\n- * data - {String | DOMElement} Data to read.\n- * options - {Object} Options for the reader.\n- *\n- * Returns:\n- * {Object} An object representing the structure of the document.\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.OWSCommon\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/OWSCommon/v1.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n \n-/**\n- * @requires OpenLayers/Format/OWSCommon.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.OWSCommon.v1\n- * Common readers and writers for OWSCommon v1.X formats\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.OWSCommon.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\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- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\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- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n+ addControl: function(control, px) {\n+ this.controls.push(control);\n+ this.addControlToMap(control, px);\n },\n \n /**\n- * Method: read\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- * data - {DOMElement} An OWSCommon document element.\n- * options - {Object} Options for the reader.\n- *\n- * Returns:\n- * {Object} An object representing the OWSCommon document.\n+ * controls - {Array()}\n+ * pixels - {Array()}\n */\n- read: function(data, options) {\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var ows = {};\n- this.readChildNodes(data, ows);\n- return ows;\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- * 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+ * Method: addControlToMap\n+ * \n+ * Parameters:\n+ * \n+ * control - {}\n+ * px - {}\n */\n- readers: {\n- \"ows\": {\n- \"Exception\": function(node, exceptionReport) {\n- var exception = {\n- code: node.getAttribute('exceptionCode'),\n- locator: node.getAttribute('locator'),\n- texts: []\n- };\n- exceptionReport.exceptions.push(exception);\n- this.readChildNodes(node, exception);\n- },\n- \"ExceptionText\": function(node, exception) {\n- var text = this.getChildValue(node);\n- exception.texts.push(text);\n- },\n- \"ServiceIdentification\": function(node, obj) {\n- obj.serviceIdentification = {};\n- this.readChildNodes(node, obj.serviceIdentification);\n- },\n- \"Title\": function(node, obj) {\n- obj.title = this.getChildValue(node);\n- },\n- \"Abstract\": function(node, serviceIdentification) {\n- serviceIdentification[\"abstract\"] = this.getChildValue(node);\n- },\n- \"Keywords\": function(node, serviceIdentification) {\n- serviceIdentification.keywords = {};\n- this.readChildNodes(node, serviceIdentification.keywords);\n- },\n- \"Keyword\": function(node, keywords) {\n- keywords[this.getChildValue(node)] = true;\n- },\n- \"ServiceType\": function(node, serviceIdentification) {\n- serviceIdentification.serviceType = {\n- codeSpace: node.getAttribute('codeSpace'),\n- value: this.getChildValue(node)\n- };\n- },\n- \"ServiceTypeVersion\": function(node, serviceIdentification) {\n- serviceIdentification.serviceTypeVersion = this.getChildValue(node);\n- },\n- \"Fees\": function(node, serviceIdentification) {\n- serviceIdentification.fees = this.getChildValue(node);\n- },\n- \"AccessConstraints\": function(node, serviceIdentification) {\n- serviceIdentification.accessConstraints =\n- this.getChildValue(node);\n- },\n- \"ServiceProvider\": function(node, obj) {\n- obj.serviceProvider = {};\n- this.readChildNodes(node, obj.serviceProvider);\n- },\n- \"ProviderName\": function(node, serviceProvider) {\n- serviceProvider.providerName = this.getChildValue(node);\n- },\n- \"ProviderSite\": function(node, serviceProvider) {\n- serviceProvider.providerSite = this.getAttributeNS(node,\n- this.namespaces.xlink, \"href\");\n- },\n- \"ServiceContact\": function(node, serviceProvider) {\n- serviceProvider.serviceContact = {};\n- this.readChildNodes(node, serviceProvider.serviceContact);\n- },\n- \"IndividualName\": function(node, serviceContact) {\n- serviceContact.individualName = this.getChildValue(node);\n- },\n- \"PositionName\": function(node, serviceContact) {\n- serviceContact.positionName = this.getChildValue(node);\n- },\n- \"ContactInfo\": function(node, serviceContact) {\n- serviceContact.contactInfo = {};\n- this.readChildNodes(node, serviceContact.contactInfo);\n- },\n- \"Phone\": function(node, contactInfo) {\n- contactInfo.phone = {};\n- this.readChildNodes(node, contactInfo.phone);\n- },\n- \"Voice\": function(node, phone) {\n- phone.voice = this.getChildValue(node);\n- },\n- \"Address\": function(node, contactInfo) {\n- contactInfo.address = {};\n- this.readChildNodes(node, contactInfo.address);\n- },\n- \"DeliveryPoint\": function(node, address) {\n- address.deliveryPoint = this.getChildValue(node);\n- },\n- \"City\": function(node, address) {\n- address.city = this.getChildValue(node);\n- },\n- \"AdministrativeArea\": function(node, address) {\n- address.administrativeArea = this.getChildValue(node);\n- },\n- \"PostalCode\": function(node, address) {\n- address.postalCode = this.getChildValue(node);\n- },\n- \"Country\": function(node, address) {\n- address.country = this.getChildValue(node);\n- },\n- \"ElectronicMailAddress\": function(node, address) {\n- address.electronicMailAddress = this.getChildValue(node);\n- },\n- \"Role\": function(node, serviceContact) {\n- serviceContact.role = this.getChildValue(node);\n- },\n- \"OperationsMetadata\": function(node, obj) {\n- obj.operationsMetadata = {};\n- this.readChildNodes(node, obj.operationsMetadata);\n- },\n- \"Operation\": function(node, operationsMetadata) {\n- var name = node.getAttribute(\"name\");\n- operationsMetadata[name] = {};\n- this.readChildNodes(node, operationsMetadata[name]);\n- },\n- \"DCP\": function(node, operation) {\n- operation.dcp = {};\n- this.readChildNodes(node, operation.dcp);\n- },\n- \"HTTP\": function(node, dcp) {\n- dcp.http = {};\n- this.readChildNodes(node, dcp.http);\n- },\n- \"Get\": function(node, http) {\n- if (!http.get) {\n- http.get = [];\n- }\n- var obj = {\n- url: this.getAttributeNS(node, this.namespaces.xlink, \"href\")\n- };\n- this.readChildNodes(node, obj);\n- http.get.push(obj);\n- },\n- \"Post\": function(node, http) {\n- if (!http.post) {\n- http.post = [];\n- }\n- var obj = {\n- url: this.getAttributeNS(node, this.namespaces.xlink, \"href\")\n- };\n- this.readChildNodes(node, obj);\n- http.post.push(obj);\n- },\n- \"Parameter\": function(node, operation) {\n- if (!operation.parameters) {\n- operation.parameters = {};\n- }\n- var name = node.getAttribute(\"name\");\n- operation.parameters[name] = {};\n- this.readChildNodes(node, operation.parameters[name]);\n- },\n- \"Constraint\": function(node, obj) {\n- if (!obj.constraints) {\n- obj.constraints = {};\n- }\n- var name = node.getAttribute(\"name\");\n- obj.constraints[name] = {};\n- this.readChildNodes(node, obj.constraints[name]);\n- },\n- \"Value\": function(node, allowedValues) {\n- allowedValues[this.getChildValue(node)] = true;\n- },\n- \"OutputFormat\": function(node, obj) {\n- obj.formats.push({\n- value: this.getChildValue(node)\n- });\n- this.readChildNodes(node, obj);\n- },\n- \"WGS84BoundingBox\": function(node, obj) {\n- var boundingBox = {};\n- boundingBox.crs = node.getAttribute(\"crs\");\n- if (obj.BoundingBox) {\n- obj.BoundingBox.push(boundingBox);\n- } else {\n- obj.projection = boundingBox.crs;\n- boundingBox = obj;\n- }\n- this.readChildNodes(node, boundingBox);\n- },\n- \"BoundingBox\": function(node, obj) {\n- // FIXME: We consider that BoundingBox is the same as WGS84BoundingBox\n- // LowerCorner = \"min_x min_y\"\n- // UpperCorner = \"max_x max_y\"\n- // It should normally depend on the projection\n- this.readers['ows']['WGS84BoundingBox'].apply(this, [node, obj]);\n- },\n- \"LowerCorner\": function(node, obj) {\n- var str = this.getChildValue(node).replace(\n- this.regExes.trimSpace, \"\");\n- str = str.replace(this.regExes.trimComma, \",\");\n- var pointList = str.split(this.regExes.splitSpace);\n- obj.left = pointList[0];\n- obj.bottom = pointList[1];\n- },\n- \"UpperCorner\": function(node, obj) {\n- var str = this.getChildValue(node).replace(\n- this.regExes.trimSpace, \"\");\n- str = str.replace(this.regExes.trimComma, \",\");\n- var pointList = str.split(this.regExes.splitSpace);\n- obj.right = pointList[0];\n- obj.top = pointList[1];\n- obj.bounds = new OpenLayers.Bounds(obj.left, obj.bottom,\n- obj.right, obj.top);\n- delete obj.left;\n- delete obj.bottom;\n- delete obj.right;\n- delete obj.top;\n- },\n- \"Language\": function(node, obj) {\n- obj.language = this.getChildValue(node);\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 \n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"ows\": {\n- \"BoundingBox\": function(options, nodeName) {\n- var node = this.createElementNSPlus(nodeName || \"ows:BoundingBox\", {\n- attributes: {\n- crs: options.projection\n- }\n- });\n- this.writeNode(\"ows:LowerCorner\", options, node);\n- this.writeNode(\"ows:UpperCorner\", options, node);\n- return node;\n- },\n- \"LowerCorner\": function(options) {\n- var node = this.createElementNSPlus(\"ows:LowerCorner\", {\n- value: options.bounds.left + \" \" + options.bounds.bottom\n- });\n- return node;\n- },\n- \"UpperCorner\": function(options) {\n- var node = this.createElementNSPlus(\"ows:UpperCorner\", {\n- value: options.bounds.right + \" \" + options.bounds.top\n- });\n- return node;\n- },\n- \"Identifier\": function(identifier) {\n- var node = this.createElementNSPlus(\"ows:Identifier\", {\n- value: identifier\n- });\n- return node;\n- },\n- \"Title\": function(title) {\n- var node = this.createElementNSPlus(\"ows:Title\", {\n- value: title\n- });\n- return node;\n- },\n- \"Abstract\": function(abstractValue) {\n- var node = this.createElementNSPlus(\"ows:Abstract\", {\n- value: abstractValue\n- });\n- return node;\n- },\n- \"OutputFormat\": function(format) {\n- var node = this.createElementNSPlus(\"ows:OutputFormat\", {\n- value: format\n- });\n- return node;\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- CLASS_NAME: \"OpenLayers.Format.OWSCommon.v1\"\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- OpenLayers/Format/OWSCommon/v1_1_0.js\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-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /** \n+ * APIMethod: addPopup\n+ * \n+ * Parameters:\n+ * popup - {}\n+ * exclusive - {Boolean} If true, closes all other popups first\n+ */\n+ addPopup: function(popup, exclusive) {\n \n-/**\n- * @requires OpenLayers/Format/OWSCommon/v1.js\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-/**\n- * Class: OpenLayers.Format.OWSCommon.v1_1_0\n- * Parser for OWS Common version 1.1.0.\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {\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- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\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- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- xlink: \"http://www.w3.org/1999/xlink\"\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- * 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+ * 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- readers: {\n- \"ows\": OpenLayers.Util.applyDefaults({\n- \"ExceptionReport\": function(node, obj) {\n- obj.exceptionReport = {\n- version: node.getAttribute('version'),\n- language: node.getAttribute('xml:lang'),\n- exceptions: []\n- };\n- this.readChildNodes(node, obj.exceptionReport);\n- },\n- \"AllowedValues\": function(node, parameter) {\n- parameter.allowedValues = {};\n- this.readChildNodes(node, parameter.allowedValues);\n- },\n- \"AnyValue\": function(node, parameter) {\n- parameter.anyValue = true;\n- },\n- \"DataType\": function(node, parameter) {\n- parameter.dataType = this.getChildValue(node);\n- },\n- \"Range\": function(node, allowedValues) {\n- allowedValues.range = {};\n- this.readChildNodes(node, allowedValues.range);\n- },\n- \"MinimumValue\": function(node, range) {\n- range.minValue = this.getChildValue(node);\n- },\n- \"MaximumValue\": function(node, range) {\n- range.maxValue = this.getChildValue(node);\n- },\n- \"Identifier\": function(node, obj) {\n- obj.identifier = this.getChildValue(node);\n- },\n- \"SupportedCRS\": function(node, obj) {\n- obj.supportedCRS = this.getChildValue(node);\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- }, OpenLayers.Format.OWSCommon.v1.prototype.readers[\"ows\"])\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- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n+ * Method: getCurrentSize\n+ * \n+ * Returns:\n+ * {} A new object with the dimensions \n+ * of the map div\n */\n- writers: {\n- \"ows\": OpenLayers.Util.applyDefaults({\n- \"Range\": function(range) {\n- var node = this.createElementNSPlus(\"ows:Range\", {\n- attributes: {\n- 'ows:rangeClosure': range.closure\n- }\n- });\n- this.writeNode(\"ows:MinimumValue\", range.minValue, node);\n- this.writeNode(\"ows:MaximumValue\", range.maxValue, node);\n- return node;\n- },\n- \"MinimumValue\": function(minValue) {\n- var node = this.createElementNSPlus(\"ows:MinimumValue\", {\n- value: minValue\n- });\n- return node;\n- },\n- \"MaximumValue\": function(maxValue) {\n- var node = this.createElementNSPlus(\"ows:MaximumValue\", {\n- value: maxValue\n- });\n- return node;\n- },\n- \"Value\": function(value) {\n- var node = this.createElementNSPlus(\"ows:Value\", {\n- value: value\n- });\n- return node;\n- }\n- }, OpenLayers.Format.OWSCommon.v1.prototype.writers[\"ows\"])\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- CLASS_NAME: \"OpenLayers.Format.OWSCommon.v1_1_0\"\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-});\n-/* ======================================================================\n- OpenLayers/Format/WCSGetCoverage.js\n- ====================================================================== */\n+ var extent = null;\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ if (center == null) {\n+ center = this.getCachedCenter();\n+ }\n+ if (resolution == null) {\n+ resolution = this.getResolution();\n+ }\n \n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\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-/**\n- * Class: OpenLayers.Format.WCSGetCoverage version 1.1.0\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.WCSGetCoverage = OpenLayers.Class(OpenLayers.Format.XML, {\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- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n+ * APIMethod: getCenter\n+ * \n+ * Returns:\n+ * {}\n */\n- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- wcs: \"http://www.opengis.net/wcs/1.1\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ 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- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n+ * Method: getCachedCenter\n+ *\n+ * Returns:\n+ * {}\n */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\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- * Constant: VERSION\n- * {String} 1.1.2\n+ * APIMethod: getZoom\n+ * \n+ * Returns:\n+ * {Integer}\n */\n- VERSION: \"1.1.2\",\n+ getZoom: function() {\n+ return this.zoom;\n+ },\n \n- /**\n- * Property: schemaLocation\n- * {String} Schema location\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- schemaLocation: \"http://www.opengis.net/wcs/1.1 http://schemas.opengis.net/wcs/1.1/wcsGetCoverage.xsd\",\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- * Constructor: OpenLayers.Format.WCSGetCoverage\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- * options - {Object} An optional object whose properties will be set on\n- * this instance.\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: write\n+ * Method: adjustZoom\n *\n * Parameters:\n- * options - {Object} Optional object.\n+ * zoom - {Number} The zoom level to adjust\n *\n * Returns:\n- * {String} A WCS GetCoverage request XML string.\n+ * {Integer} Adjusted zoom level that shows a map not wider than its\n+ * 's maxExtent.\n */\n- write: function(options) {\n- var node = this.writeNode(\"wcs:GetCoverage\", options);\n- this.setAttributeNS(\n- node, this.namespaces.xsi,\n- \"xsi:schemaLocation\", this.schemaLocation\n- );\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n+ 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- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n+ * 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- writers: {\n- \"wcs\": {\n- \"GetCoverage\": function(options) {\n- var node = this.createElementNSPlus(\"wcs:GetCoverage\", {\n- attributes: {\n- version: options.version || this.VERSION,\n- service: 'WCS'\n- }\n- });\n- this.writeNode(\"ows:Identifier\", options.identifier, node);\n- this.writeNode(\"wcs:DomainSubset\", options.domainSubset, node);\n- this.writeNode(\"wcs:Output\", options.output, node);\n- return node;\n- },\n- \"DomainSubset\": function(domainSubset) {\n- var node = this.createElementNSPlus(\"wcs:DomainSubset\", {});\n- this.writeNode(\"ows:BoundingBox\", domainSubset.boundingBox, node);\n- if (domainSubset.temporalSubset) {\n- this.writeNode(\"wcs:TemporalSubset\", domainSubset.temporalSubset, node);\n- }\n- return node;\n- },\n- \"TemporalSubset\": function(temporalSubset) {\n- var node = this.createElementNSPlus(\"wcs:TemporalSubset\", {});\n- for (var i = 0, len = temporalSubset.timePeriods.length; i < len; ++i) {\n- this.writeNode(\"wcs:TimePeriod\", temporalSubset.timePeriods[i], node);\n- }\n- return node;\n- },\n- \"TimePeriod\": function(timePeriod) {\n- var node = this.createElementNSPlus(\"wcs:TimePeriod\", {});\n- this.writeNode(\"wcs:BeginPosition\", timePeriod.begin, node);\n- this.writeNode(\"wcs:EndPosition\", timePeriod.end, node);\n- if (timePeriod.resolution) {\n- this.writeNode(\"wcs:TimeResolution\", timePeriod.resolution, node);\n- }\n- return node;\n- },\n- \"BeginPosition\": function(begin) {\n- var node = this.createElementNSPlus(\"wcs:BeginPosition\", {\n- value: begin\n- });\n- return node;\n- },\n- \"EndPosition\": function(end) {\n- var node = this.createElementNSPlus(\"wcs:EndPosition\", {\n- value: end\n- });\n- return node;\n- },\n- \"TimeResolution\": function(resolution) {\n- var node = this.createElementNSPlus(\"wcs:TimeResolution\", {\n- value: resolution\n- });\n- return node;\n- },\n- \"Output\": function(output) {\n- var node = this.createElementNSPlus(\"wcs:Output\", {\n- attributes: {\n- format: output.format,\n- store: output.store\n- }\n- });\n- if (output.gridCRS) {\n- this.writeNode(\"wcs:GridCRS\", output.gridCRS, node);\n- }\n- return node;\n- },\n- \"GridCRS\": function(gridCRS) {\n- var node = this.createElementNSPlus(\"wcs:GridCRS\", {});\n- this.writeNode(\"wcs:GridBaseCRS\", gridCRS.baseCRS, node);\n- if (gridCRS.type) {\n- this.writeNode(\"wcs:GridType\", gridCRS.type, node);\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 (gridCRS.origin) {\n- this.writeNode(\"wcs:GridOrigin\", gridCRS.origin, node);\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- this.writeNode(\"wcs:GridOffsets\", gridCRS.offsets, node);\n- if (gridCRS.CS) {\n- this.writeNode(\"wcs:GridCS\", gridCRS.CS, node);\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- return node;\n- },\n- \"GridBaseCRS\": function(baseCRS) {\n- return this.createElementNSPlus(\"wcs:GridBaseCRS\", {\n- value: baseCRS\n- });\n- },\n- \"GridOrigin\": function(origin) {\n- return this.createElementNSPlus(\"wcs:GridOrigin\", {\n- value: origin\n- });\n- },\n- \"GridType\": function(type) {\n- return this.createElementNSPlus(\"wcs:GridType\", {\n- value: type\n- });\n- },\n- \"GridOffsets\": function(offsets) {\n- return this.createElementNSPlus(\"wcs:GridOffsets\", {\n- value: offsets\n- });\n- },\n- \"GridCS\": function(CS) {\n- return this.createElementNSPlus(\"wcs:GridCS\", {\n- value: CS\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- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows\n- },\n \n- CLASS_NAME: \"OpenLayers.Format.WCSGetCoverage\"\n+ if (zoomChanged) {\n+ this.zoom = zoom;\n+ this.resolution = res;\n+ }\n \n-});\n-/* ======================================================================\n- OpenLayers/Format/WFST.js\n- ====================================================================== */\n+ var bounds = this.getExtent();\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ //send the move call to the baselayer and all the overlays \n \n-/**\n- * @requires OpenLayers/Format.js\n- */\n+ if (this.baseLayer.visibility) {\n+ this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);\n+ options.dragging || this.baseLayer.events.triggerEvent(\n+ \"moveend\", {\n+ zoomChanged: zoomChanged\n+ }\n+ );\n+ }\n \n-/**\n- * Function: OpenLayers.Format.WFST\n- * Used to create a versioned WFS protocol. Default version is 1.0.0.\n- *\n- * Returns:\n- * {} A WFST format of the given version.\n- */\n-OpenLayers.Format.WFST = function(options) {\n- options = OpenLayers.Util.applyDefaults(\n- options, OpenLayers.Format.WFST.DEFAULTS\n- );\n- var cls = OpenLayers.Format.WFST[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported WFST version: \" + options.version;\n- }\n- return new cls(options);\n-};\n+ bounds = this.baseLayer.getExtent();\n \n-/**\n- * Constant: OpenLayers.Format.WFST.DEFAULTS\n- * {Object} Default properties for the WFST format.\n- */\n-OpenLayers.Format.WFST.DEFAULTS = {\n- \"version\": \"1.0.0\"\n-};\n-/* ======================================================================\n- OpenLayers/Style.js\n- ====================================================================== */\n+ 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-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\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- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Feature/Vector.js\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-/**\n- * Class: OpenLayers.Style\n- * This class represents a UserStyle obtained\n- * from a SLD, containing styling rules.\n- */\n-OpenLayers.Style = OpenLayers.Class({\n+ 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- * Property: id\n- * {String} A unique id for this session.\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- id: null,\n+ isValidZoomLevel: function(zoomLevel) {\n+ return ((zoomLevel != null) &&\n+ (zoomLevel >= 0) &&\n+ (zoomLevel < this.getNumZoomLevels()));\n+ },\n \n /**\n- * APIProperty: name\n- * {String}\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- name: null,\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- * Property: title\n- * {String} Title of this style (set if included in SLD)\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- title: null,\n+ getProjection: function() {\n+ var projection = this.getProjectionObject();\n+ return projection ? projection.getCode() : null;\n+ },\n \n /**\n- * Property: description\n- * {String} Description of this style (set if abstract is included in SLD)\n+ * APIMethod: getProjectionObject\n+ * Returns the projection obect from the baselayer.\n+ *\n+ * Returns:\n+ * {} The Projection of the base layer.\n */\n- description: null,\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- * APIProperty: layerName\n- * {} name of the layer that this style belongs to, usually\n- * according to the NamedLayer attribute of an SLD document.\n+ * APIMethod: getMaxResolution\n+ * \n+ * Returns:\n+ * {String} The Map's Maximum Resolution\n */\n- layerName: null,\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- * APIProperty: isDefault\n- * {Boolean}\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- isDefault: false,\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- * Property: rules \n- * {Array()}\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- rules: null,\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- * APIProperty: context\n- * {Object} An optional object with properties that symbolizers' property\n- * values should be evaluated against. If no context is specified,\n- * feature.attributes will be used\n+ * 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- context: null,\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- * Property: defaultStyle\n- * {Object} hash of style properties to use as default for merging\n- * rule-based style symbolizers onto. If no rules are defined,\n- * createSymbolizer will return this style. If is set to\n- * true, the defaultStyle will only be taken into account if there are\n- * rules defined.\n+ * APIMethod: getResolution\n+ * \n+ * Returns:\n+ * {Float} The current resolution of the map. \n+ * If no baselayer is set, returns null.\n */\n- defaultStyle: null,\n+ getResolution: function() {\n+ var resolution = null;\n+ if (this.baseLayer != null) {\n+ resolution = this.baseLayer.getResolution();\n+ } else if (this.allOverlays === true && this.layers.length > 0) {\n+ // while adding the 1st layer to the map in allOverlays mode,\n+ // this.baseLayer is not set yet when we need the resolution\n+ // for calculateInRange.\n+ resolution = this.layers[0].getResolution();\n+ }\n+ return resolution;\n+ },\n \n /**\n- * Property: defaultsPerSymbolizer\n- * {Boolean} If set to true, the will extend the symbolizer\n- * of every rule. Properties of the will also be used to set\n- * missing symbolizer properties if the symbolizer has stroke, fill or\n- * graphic set to true. Default is false.\n+ * APIMethod: getUnits\n+ * \n+ * Returns:\n+ * {Float} The current units of the map. \n+ * If no baselayer is set, returns null.\n */\n- defaultsPerSymbolizer: false,\n+ getUnits: function() {\n+ var units = null;\n+ if (this.baseLayer != null) {\n+ units = this.baseLayer.units;\n+ }\n+ return units;\n+ },\n \n /**\n- * Property: propertyStyles\n- * {Hash of Boolean} cache of style properties that need to be parsed for\n- * propertyNames. Property names are keys, values won't be used.\n+ * APIMethod: getScale\n+ * \n+ * Returns:\n+ * {Float} The current scale denominator of the map. \n+ * If no baselayer is set, returns null.\n */\n- propertyStyles: null,\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- * Constructor: OpenLayers.Style\n- * Creates a UserStyle.\n- *\n- * Parameters:\n- * style - {Object} Optional hash of style properties that will be\n- * used as default style for this style object. This style\n- * applies if no rules are specified. Symbolizers defined in\n- * rules will extend this default style.\n- * options - {Object} An optional object with properties to set on the\n- * style.\n- *\n- * Valid options:\n- * rules - {Array()} List of rules to be added to the\n- * style.\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- * {}\n+ * {Integer} A suitable zoom level for the specified bounds.\n+ * If no baselayer is set, returns null.\n */\n- initialize: function(style, options) {\n-\n- OpenLayers.Util.extend(this, options);\n- this.rules = [];\n- if (options && options.rules) {\n- this.addRules(options.rules);\n+ getZoomForExtent: function(bounds, closest) {\n+ var zoom = null;\n+ if (this.baseLayer != null) {\n+ zoom = this.baseLayer.getZoomForExtent(bounds, closest);\n }\n-\n- // use the default style from OpenLayers.Feature.Vector if no style\n- // was given in the constructor\n- this.setDefaultStyle(style ||\n- OpenLayers.Feature.Vector.style[\"default\"]);\n-\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ return zoom;\n },\n \n- /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\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- destroy: function() {\n- for (var i = 0, len = this.rules.length; i < len; i++) {\n- this.rules[i].destroy();\n- this.rules[i] = null;\n+ getResolutionForZoom: function(zoom) {\n+ var resolution = null;\n+ if (this.baseLayer) {\n+ resolution = this.baseLayer.getResolutionForZoom(zoom);\n }\n- this.rules = null;\n- this.defaultStyle = null;\n+ return resolution;\n },\n \n /**\n- * Method: createSymbolizer\n- * creates a style by applying all feature-dependent rules to the base\n- * style.\n+ * APIMethod: getZoomForResolution\n * \n * Parameters:\n- * feature - {} feature to evaluate rules for\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- * {Object} symbolizer hash\n+ * {Integer} A suitable zoom level for the specified resolution.\n+ * If no baselayer is set, returns null.\n */\n- createSymbolizer: function(feature) {\n- var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(\n- OpenLayers.Util.extend({}, this.defaultStyle), feature);\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- var rules = this.rules;\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- var rule, context;\n- var elseRules = [];\n- var appliedRules = false;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- rule = rules[i];\n- // does the rule apply?\n- var applies = rule.evaluate(feature);\n+ /** \n+ * 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- if (applies) {\n- if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n- elseRules.push(rule);\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- appliedRules = true;\n- this.applySymbolizer(rule, style, feature);\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- // if no other rules apply, apply the rules with else filters\n- if (appliedRules == false && elseRules.length > 0) {\n- appliedRules = true;\n- for (var i = 0, len = elseRules.length; i < len; i++) {\n- this.applySymbolizer(elseRules[i], style, feature);\n- }\n- }\n+ /**\n+ * APIMethod: zoomIn\n+ * \n+ */\n+ zoomIn: function() {\n+ this.zoomTo(this.getZoom() + 1);\n+ },\n \n- // don't display if there were rules but none applied\n- if (rules.length > 0 && appliedRules == false) {\n- style.display = \"none\";\n- }\n+ /**\n+ * APIMethod: zoomOut\n+ * \n+ */\n+ zoomOut: function() {\n+ this.zoomTo(this.getZoom() - 1);\n+ },\n \n- if (style.label != null && typeof style.label !== \"string\") {\n- style.label = String(style.label);\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- return style;\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- * Method: applySymbolizer\n+ /** \n+ * APIMethod: zoomToMaxExtent\n+ * Zoom to the full extent and recenter.\n *\n * Parameters:\n- * rule - {}\n- * style - {Object}\n- * feature - {}\n- *\n- * Returns:\n- * {Object} A style with new symbolizer applied.\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- applySymbolizer: function(rule, style, feature) {\n- var symbolizerPrefix = feature.geometry ?\n- this.getSymbolizerPrefix(feature.geometry) :\n- OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n+ zoomToMaxExtent: function(options) {\n+ //restricted is true by default\n+ var restricted = (options) ? options.restricted : true;\n \n- var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n+ var maxExtent = this.getMaxExtent({\n+ 'restricted': restricted\n+ });\n+ this.zoomToExtent(maxExtent);\n+ },\n \n- if (this.defaultsPerSymbolizer === true) {\n- var defaults = this.defaultStyle;\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: defaults.pointRadius\n- });\n- if (symbolizer.stroke === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- strokeWidth: defaults.strokeWidth,\n- strokeColor: defaults.strokeColor,\n- strokeOpacity: defaults.strokeOpacity,\n- strokeDashstyle: defaults.strokeDashstyle,\n- strokeLinecap: defaults.strokeLinecap\n- });\n- }\n- if (symbolizer.fill === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- fillColor: defaults.fillColor,\n- fillOpacity: defaults.fillOpacity\n- });\n- }\n- if (symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: this.defaultStyle.pointRadius,\n- externalGraphic: this.defaultStyle.externalGraphic,\n- graphicName: this.defaultStyle.graphicName,\n- graphicOpacity: this.defaultStyle.graphicOpacity,\n- graphicWidth: this.defaultStyle.graphicWidth,\n- graphicHeight: this.defaultStyle.graphicHeight,\n- graphicXOffset: this.defaultStyle.graphicXOffset,\n- graphicYOffset: this.defaultStyle.graphicYOffset\n- });\n- }\n- }\n+ /** \n+ * 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- // merge the style with the current style\n- return this.createLiterals(\n- OpenLayers.Util.extend(style, symbolizer), feature);\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: createLiterals\n- * creates literals for all style properties that have an entry in\n- * .\n+ * Method: getLonLatFromViewPortPx\n * \n * Parameters:\n- * style - {Object} style to create literals for. Will be modified\n- * inline.\n- * feature - {Object}\n+ * viewPortPx - {|Object} An OpenLayers.Pixel or\n+ * an object with a 'x'\n+ * and 'y' properties.\n * \n * Returns:\n- * {Object} the modified style\n+ * {} An OpenLayers.LonLat which is the passed-in view \n+ * port , translated into lon/lat\n+ * by the current base layer.\n */\n- createLiterals: function(style, feature) {\n- var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n- OpenLayers.Util.extend(context, this.context);\n-\n- for (var i in this.propertyStyles) {\n- style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ if (this.baseLayer != null) {\n+ lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);\n }\n- return style;\n+ return lonlat;\n },\n \n /**\n- * Method: findPropertyStyles\n- * Looks into all rules for this style and the defaultStyle to collect\n- * all the style hash property names containing ${...} strings that have\n- * to be replaced using the createLiteral method before returning them.\n+ * APIMethod: getViewPortPxFromLonLat\n+ * \n+ * Parameters:\n+ * lonlat - {}\n * \n * Returns:\n- * {Object} hash of property names that need createLiteral parsing. The\n- * name of the property is the key, and the value is true;\n+ * {} An OpenLayers.Pixel which is the passed-in \n+ * , translated into view port \n+ * pixels by the current base layer.\n */\n- findPropertyStyles: function() {\n- var propertyStyles = {};\n-\n- // check the default style\n- var style = this.defaultStyle;\n- this.addPropertyStyles(propertyStyles, style);\n-\n- // walk through all rules to check for properties in their symbolizer\n- var rules = this.rules;\n- var symbolizer, value;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- symbolizer = rules[i].symbolizer;\n- for (var key in symbolizer) {\n- value = symbolizer[key];\n- if (typeof value == \"object\") {\n- // symbolizer key is \"Point\", \"Line\" or \"Polygon\"\n- this.addPropertyStyles(propertyStyles, value);\n- } else {\n- // symbolizer is a hash of style properties\n- this.addPropertyStyles(propertyStyles, symbolizer);\n- break;\n- }\n- }\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ var px = null;\n+ if (this.baseLayer != null) {\n+ px = this.baseLayer.getViewPortPxFromLonLat(lonlat);\n }\n- return propertyStyles;\n+ return px;\n },\n \n /**\n- * Method: addPropertyStyles\n- * \n+ * Method: getZoomTargetCenter\n+ *\n * Parameters:\n- * propertyStyles - {Object} hash to add new property styles to. Will be\n- * modified inline\n- * symbolizer - {Object} search this symbolizer for property styles\n- * \n+ * 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- * {Object} propertyStyles hash\n+ * {} The location of the map center after the\n+ * transformation described by the origin xy and the target resolution.\n */\n- addPropertyStyles: function(propertyStyles, symbolizer) {\n- var property;\n- for (var key in symbolizer) {\n- property = symbolizer[key];\n- if (typeof property == \"string\" &&\n- property.match(/\\$\\{\\w+\\}/)) {\n- propertyStyles[key] = true;\n- }\n+ 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 propertyStyles;\n+ return lonlat;\n },\n \n+ //\n+ // CONVENIENCE TRANSLATION FUNCTIONS FOR API\n+ //\n+\n /**\n- * APIMethod: addRules\n- * Adds rules to this style.\n+ * APIMethod: getLonLatFromPixel\n * \n * Parameters:\n- * rules - {Array()}\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- addRules: function(rules) {\n- Array.prototype.push.apply(this.rules, rules);\n- this.propertyStyles = this.findPropertyStyles();\n+ getLonLatFromPixel: function(px) {\n+ return this.getLonLatFromViewPortPx(px);\n },\n \n /**\n- * APIMethod: setDefaultStyle\n- * Sets the default style for this style object.\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- * style - {Object} Hash of style properties\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- setDefaultStyle: function(style) {\n- this.defaultStyle = style;\n- this.propertyStyles = this.findPropertyStyles();\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: getSymbolizerPrefix\n- * Returns the correct symbolizer prefix according to the\n- * geometry type of the passed geometry\n+ * Method: getGeodesicPixelSize\n * \n * Parameters:\n- * geometry - {}\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- * {String} key of the according symbolizer\n+ * {} The geodesic size of the pixel in kilometers.\n */\n- getSymbolizerPrefix: function(geometry) {\n- var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n- for (var i = 0, len = prefixes.length; i < len; i++) {\n- if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n- return prefixes[i];\n- }\n+ 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: clone\n- * Clones this style.\n+ * APIMethod: getViewPortPxFromLayerPx\n+ * \n+ * Parameters:\n+ * layerPx - {}\n * \n * Returns:\n- * {} Clone of this style.\n+ * {} Layer Pixel translated into ViewPort Pixel \n+ * coordinates\n */\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- // clone rules\n- if (this.rules) {\n- options.rules = [];\n- for (var i = 0, len = this.rules.length; i < len; ++i) {\n- options.rules.push(this.rules[i].clone());\n- }\n+ 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- // clone context\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- //clone default style\n- var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n- return new OpenLayers.Style(defaultStyle, options);\n+ return viewPortPx;\n },\n \n- CLASS_NAME: \"OpenLayers.Style\"\n-});\n-\n-\n-/**\n- * Function: createLiteral\n- * converts a style value holding a combination of PropertyName and Literal\n- * into a Literal, taking the property values from the passed features.\n- * \n- * Parameters:\n- * value - {String} value to parse. If this string contains a construct like\n- * \"foo ${bar}\", then \"foo \" will be taken as literal, and \"${bar}\"\n- * will be replaced by the value of the \"bar\" attribute of the passed\n- * feature.\n- * context - {Object} context to take attribute values from\n- * feature - {} optional feature to pass to\n- * for evaluating functions in the\n- * context.\n- * property - {String} optional, name of the property for which the literal is\n- * being created for evaluating functions in the context.\n- * \n- * Returns:\n- * {String} the parsed value. In the example of the value parameter above, the\n- * result would be \"foo valueOfBar\", assuming that the passed feature has an\n- * attribute named \"bar\" with the value \"valueOfBar\".\n- */\n-OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n- if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n- value = OpenLayers.String.format(value, context, [feature, property]);\n- value = (isNaN(value) || !value) ? value : parseFloat(value);\n- }\n- return value;\n-};\n-\n-/**\n- * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES\n- * {Array} prefixes of the sld symbolizers. These are the\n- * same as the main geometry types\n- */\n-OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',\n- 'Raster'\n-];\n-/* ======================================================================\n- OpenLayers/Filter.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Style.js\n- */\n-\n-/**\n- * Class: OpenLayers.Filter\n- * This class represents an OGC Filter.\n- */\n-OpenLayers.Filter = OpenLayers.Class({\n-\n- /** \n- * Constructor: OpenLayers.Filter\n- * This class represents a generic filter.\n- *\n+ /**\n+ * APIMethod: getLayerPxFromViewPortPx\n+ * \n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * viewPortPx - {}\n * \n * Returns:\n- * {}\n+ * {} ViewPort Pixel translated into Layer Pixel \n+ * coordinates\n */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\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- * APIMethod: destroy\n- * Remove reference to anything added.\n- */\n- destroy: function() {},\n+ //\n+ // TRANSLATION: LonLat <-> LayerPx\n+ //\n \n /**\n- * APIMethod: evaluate\n- * Evaluates this filter in a specific context. Instances or subclasses\n- * are supposed to override this method.\n+ * Method: getLonLatFromLayerPx\n * \n * Parameters:\n- * context - {Object} Context to use in evaluating the filter. If a vector\n- * feature is provided, the feature.attributes will be used as context.\n- * \n+ * px - {}\n+ *\n * Returns:\n- * {Boolean} The filter applies.\n+ * {}\n */\n- evaluate: function(context) {\n- return true;\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: clone\n- * Clones this filter. Should be implemented by subclasses.\n+ * APIMethod: getLayerPxFromLonLat\n * \n+ * Parameters:\n+ * lonlat - {} lonlat\n+ *\n * Returns:\n- * {} Clone of this filter.\n+ * {} An OpenLayers.Pixel which is the passed-in \n+ * , translated into layer pixels \n+ * by the current base layer\n */\n- clone: function() {\n- return null;\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- * APIMethod: toString\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- * Returns:\n- * {String} Include in your build to get a CQL\n- * representation of the filter returned. Otherwise \"[Object object]\"\n- * will be returned.\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- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.CQL) {\n- string = OpenLayers.Format.CQL.prototype.write(this);\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- string = Object.prototype.toString.call(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- return string;\n },\n \n- CLASS_NAME: \"OpenLayers.Filter\"\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/Filter/Spatial.js\n+ OpenLayers/Layer.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Filter.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Map.js\n+ * @requires OpenLayers/Projection.js\n */\n \n /**\n- * Class: OpenLayers.Filter.Spatial\n- * This class represents a spatial filter.\n- * Currently implemented: BBOX, DWithin and Intersects\n- * \n- * Inherits from:\n- * - \n+ * Class: OpenLayers.Layer\n */\n-OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {\n+OpenLayers.Layer = OpenLayers.Class({\n \n /**\n- * APIProperty: type\n- * {String} Type of spatial filter.\n- *\n- * The type should be one of:\n- * - OpenLayers.Filter.Spatial.BBOX\n- * - OpenLayers.Filter.Spatial.INTERSECTS\n- * - OpenLayers.Filter.Spatial.DWITHIN\n- * - OpenLayers.Filter.Spatial.WITHIN\n- * - OpenLayers.Filter.Spatial.CONTAINS\n+ * APIProperty: id\n+ * {String}\n */\n- type: null,\n+ id: null,\n \n- /**\n- * APIProperty: property\n- * {String} Name of the context property to compare.\n+ /** \n+ * APIProperty: name\n+ * {String}\n */\n- property: null,\n+ name: null,\n+\n+ /** \n+ * APIProperty: div\n+ * {DOMElement}\n+ */\n+ div: null,\n \n /**\n- * APIProperty: value\n- * { || } The bounds or geometry\n- * to be used by the filter. Use bounds for BBOX filters and geometry\n- * for INTERSECTS or DWITHIN filters.\n+ * APIProperty: opacity\n+ * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default\n+ * is 1.\n */\n- value: null,\n+ opacity: 1,\n \n /**\n- * APIProperty: distance\n- * {Number} The distance to use in a DWithin spatial filter.\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- distance: null,\n+ alwaysInRange: null,\n \n /**\n- * APIProperty: distanceUnits\n- * {String} The units to use for the distance, e.g. 'm'.\n+ * Constant: RESOLUTION_PROPERTIES\n+ * {Array} The properties that are used for calculating resolutions\n+ * information.\n */\n- distanceUnits: null,\n+ RESOLUTION_PROPERTIES: [\n+ 'scales', 'resolutions',\n+ 'maxScale', 'minScale',\n+ 'maxResolution', 'minResolution',\n+ 'numZoomLevels', 'maxZoomLevel'\n+ ],\n \n- /** \n- * Constructor: OpenLayers.Filter.Spatial\n- * Creates a spatial filter.\n+ /**\n+ * APIProperty: events\n+ * {}\n *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * filter.\n- * \n- * Returns:\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- * Method: evaluate\n- * Evaluates this filter for a specific feature.\n- * \n- * Parameters:\n- * feature - {} feature to apply the filter to.\n- * \n- * Returns:\n- * {Boolean} The feature meets filter criteria.\n+ * APIProperty: map\n+ * {} This variable is set when the layer is added to \n+ * the map, via the accessor function setMap().\n */\n- evaluate: function(feature) {\n- var intersect = false;\n- switch (this.type) {\n- case OpenLayers.Filter.Spatial.BBOX:\n- case OpenLayers.Filter.Spatial.INTERSECTS:\n- if (feature.geometry) {\n- var geom = this.value;\n- if (this.value.CLASS_NAME == \"OpenLayers.Bounds\") {\n- geom = this.value.toGeometry();\n- }\n- if (feature.geometry.intersects(geom)) {\n- intersect = true;\n- }\n- }\n- break;\n- default:\n- throw new Error('evaluate is not implemented for this filter type.');\n- }\n- return intersect;\n- },\n+ map: null,\n \n /**\n- * APIMethod: clone\n- * Clones this filter.\n- * \n- * Returns:\n- * {} Clone of this filter.\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- clone: function() {\n- var options = OpenLayers.Util.applyDefaults({\n- value: this.value && this.value.clone && this.value.clone()\n- }, this);\n- return new OpenLayers.Filter.Spatial(options);\n- },\n- CLASS_NAME: \"OpenLayers.Filter.Spatial\"\n-});\n-\n-OpenLayers.Filter.Spatial.BBOX = \"BBOX\";\n-OpenLayers.Filter.Spatial.INTERSECTS = \"INTERSECTS\";\n-OpenLayers.Filter.Spatial.DWITHIN = \"DWITHIN\";\n-OpenLayers.Filter.Spatial.WITHIN = \"WITHIN\";\n-OpenLayers.Filter.Spatial.CONTAINS = \"CONTAINS\";\n-/* ======================================================================\n- OpenLayers/Filter/FeatureId.js\n- ====================================================================== */\n+ isBaseLayer: false,\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: 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- * @requires OpenLayers/Filter.js\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- * Class: OpenLayers.Filter.FeatureId\n- * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD\n- * styling\n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {\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- * APIProperty: fids\n- * {Array(String)} Feature Ids to evaluate this rule against. \n- * To be passed inside the params object.\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- fids: null,\n+ inRange: false,\n \n- /** \n- * Property: type\n- * {String} Type to identify this filter.\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- type: \"FID\",\n+ imageSize: null,\n+\n+ // OPTIONS\n \n /** \n- * Constructor: OpenLayers.Filter.FeatureId\n- * Creates an ogc:FeatureId rule.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * rule\n- * \n- * Returns:\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- initialize: function(options) {\n- this.fids = [];\n- OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n- },\n+ options: null,\n \n /**\n- * APIMethod: evaluate\n- * evaluates this rule for a specific feature\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- * Parameters:\n- * feature - {} feature to apply the rule to.\n- * For vector features, the check is run against the fid,\n- * for plain features against the id.\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- * Returns:\n- * {Boolean} true if the rule applies, false if it does not\n */\n- evaluate: function(feature) {\n- for (var i = 0, len = this.fids.length; i < len; i++) {\n- var fid = feature.fid || feature.id;\n- if (fid == this.fids[i]) {\n- return true;\n- }\n- }\n- return false;\n- },\n+ projection: null,\n \n /**\n- * APIMethod: clone\n- * Clones this filter.\n- * \n- * Returns:\n- * {} Clone of this filter.\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- clone: function() {\n- var filter = new OpenLayers.Filter.FeatureId();\n- OpenLayers.Util.extend(filter, this);\n- filter.fids = this.fids.slice();\n- return filter;\n- },\n+ units: null,\n \n- CLASS_NAME: \"OpenLayers.Filter.FeatureId\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/WFST/v1.js\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-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: 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- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/WFST.js\n- * @requires OpenLayers/Filter/Spatial.js\n- * @requires OpenLayers/Filter/FeatureId.js\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- * Class: OpenLayers.Format.WFST.v1\n- * Superclass for WFST parsers.\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\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- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\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- namespaces: {\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n- wfs: \"http://www.opengis.net/wfs\",\n- gml: \"http://www.opengis.net/gml\",\n- ogc: \"http://www.opengis.net/ogc\",\n- ows: \"http://www.opengis.net/ows\"\n- },\n+ maxResolution: null,\n \n /**\n- * Property: defaultPrefix\n+ * APIProperty: minResolution\n+ * {Float}\n */\n- defaultPrefix: \"wfs\",\n+ minResolution: null,\n \n /**\n- * Property: version\n- * {String} WFS version number.\n+ * APIProperty: numZoomLevels\n+ * {Integer}\n */\n- version: null,\n+ numZoomLevels: null,\n \n /**\n- * Property: schemaLocation\n- * {String} Schema location for a particular minor version.\n+ * APIProperty: minScale\n+ * {Float}\n */\n- schemaLocations: null,\n+ minScale: null,\n \n /**\n- * APIProperty: srsName\n- * {String} URI for spatial reference system.\n+ * APIProperty: maxScale\n+ * {Float}\n */\n- srsName: null,\n+ maxScale: null,\n \n /**\n- * APIProperty: extractAttributes\n- * {Boolean} Extract attributes from GML. 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- extractAttributes: true,\n+ displayOutsideMaxExtent: false,\n \n /**\n- * APIProperty: xy\n- * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)\n- * Changing is not recommended, a new Format should be instantiated.\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- xy: true,\n+ wrapDateLine: false,\n \n /**\n- * Property: stateName\n- * {Object} Maps feature states to node names.\n+ * Property: metadata\n+ * {Object} This object can be used to store additional information on a\n+ * layer object.\n */\n- stateName: null,\n+ metadata: null,\n \n /**\n- * Constructor: OpenLayers.Format.WFST.v1\n- * Instances of this class are not created directly. Use the\n- * or \n- * constructor instead.\n+ * Constructor: OpenLayers.Layer\n *\n * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n+ * name - {String} The layer name\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- initialize: function(options) {\n- // set state name mapping\n- this.stateName = {};\n- this.stateName[OpenLayers.State.INSERT] = \"wfs:Insert\";\n- this.stateName[OpenLayers.State.UPDATE] = \"wfs:Update\";\n- this.stateName[OpenLayers.State.DELETE] = \"wfs:Delete\";\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- },\n+ initialize: function(name, options) {\n \n- /**\n- * Method: getSrsName\n- */\n- getSrsName: function(feature, options) {\n- var srsName = options && options.srsName;\n- if (!srsName) {\n- if (feature && feature.layer) {\n- srsName = feature.layer.projection.getCode();\n- } else {\n- srsName = this.srsName;\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- return srsName;\n },\n \n /**\n- * APIMethod: read\n- * Parse the response from a transaction. Because WFS is split into\n- * Transaction requests (create, update, and delete) and GetFeature\n- * requests (read), this method handles parsing of both types of\n- * responses.\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- * data - {String | Document} The WFST document to read\n- * options - {Object} Options for the reader\n- *\n- * Valid options properties:\n- * output - {String} either \"features\" or \"object\". The default is\n- * \"features\", which means that the method will return an array of\n- * features. If set to \"object\", an object with a \"features\" property\n- * and other properties read by the parser will be returned.\n- *\n- * Returns:\n- * {Array | Object} Output depending on the output option.\n+ * setNewBaseLayer - {Boolean} Set a new base layer when this layer has\n+ * been destroyed. Default is true.\n */\n- read: function(data, options) {\n- options = options || {};\n- OpenLayers.Util.applyDefaults(options, {\n- output: \"features\"\n- });\n-\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var obj = {};\n- if (data) {\n- this.readNode(data, obj, true);\n+ destroy: function(setNewBaseLayer) {\n+ if (setNewBaseLayer == null) {\n+ setNewBaseLayer = true;\n }\n- if (obj.features && options.output === \"features\") {\n- obj = obj.features;\n+ if (this.map != null) {\n+ this.map.removeLayer(this, setNewBaseLayer);\n }\n- return obj;\n- },\n+ this.projection = null;\n+ this.map = null;\n+ this.name = null;\n+ this.div = null;\n+ this.options = null;\n \n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wfs\": {\n- \"FeatureCollection\": function(node, obj) {\n- obj.features = [];\n- this.readChildNodes(node, obj);\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: write\n- * Given an array of features, write a WFS transaction. This assumes\n- * the features have a state property that determines the operation\n- * type - insert, update, or delete.\n+ * Method: clone\n *\n * Parameters:\n- * features - {Array()} A list of features. See\n- * below for a more detailed description of the influence of the\n- * feature's *modified* property.\n- * options - {Object}\n- *\n- * feature.modified rules:\n- * If a feature has a modified property set, the following checks will be\n- * made before a feature's geometry or attribute is included in an Update\n- * transaction:\n- * - *modified* is not set at all: The geometry and all attributes will be\n- * included.\n- * - *modified.geometry* is set (null or a geometry): The geometry will be\n- * included. If *modified.attributes* is not set, all attributes will\n- * be included.\n- * - *modified.attributes* is set: Only the attributes set (i.e. to null or\n- * a value) in *modified.attributes* will be included. \n- * If *modified.geometry* is not set, the geometry will not be included.\n- *\n- * Valid options include:\n- * - *multi* {Boolean} If set to true, geometries will be casted to\n- * Multi geometries before writing.\n+ * obj - {} The layer to be cloned\n *\n * Returns:\n- * {String} A serialized WFS transaction.\n- */\n- write: function(features, options) {\n- var node = this.writeNode(\"wfs:Transaction\", {\n- features: features,\n- options: options\n- });\n- var value = this.schemaLocationAttr();\n- if (value) {\n- this.setAttributeNS(\n- node, this.namespaces[\"xsi\"], \"xsi:schemaLocation\", value\n- );\n- }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n- },\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n+ * {} An exact clone of this \n */\n- writers: {\n- \"wfs\": {\n- \"GetFeature\": function(options) {\n- var node = this.createElementNSPlus(\"wfs:GetFeature\", {\n- attributes: {\n- service: \"WFS\",\n- version: this.version,\n- handle: options && options.handle,\n- outputFormat: options && options.outputFormat,\n- maxFeatures: options && options.maxFeatures,\n- \"xsi:schemaLocation\": this.schemaLocationAttr(options)\n- }\n- });\n- if (typeof this.featureType == \"string\") {\n- this.writeNode(\"Query\", options, node);\n- } else {\n- for (var i = 0, len = this.featureType.length; i < len; i++) {\n- options.featureType = this.featureType[i];\n- this.writeNode(\"Query\", options, node);\n- }\n- }\n- return node;\n- },\n- \"Transaction\": function(obj) {\n- obj = obj || {};\n- var options = obj.options || {};\n- var node = this.createElementNSPlus(\"wfs:Transaction\", {\n- attributes: {\n- service: \"WFS\",\n- version: this.version,\n- handle: options.handle\n- }\n- });\n- var i, len;\n- var features = obj.features;\n- if (features) {\n- // temporarily re-assigning geometry types\n- if (options.multi === true) {\n- OpenLayers.Util.extend(this.geometryTypes, {\n- \"OpenLayers.Geometry.Point\": \"MultiPoint\",\n- \"OpenLayers.Geometry.LineString\": (this.multiCurve === true) ? \"MultiCurve\" : \"MultiLineString\",\n- \"OpenLayers.Geometry.Polygon\": (this.multiSurface === true) ? \"MultiSurface\" : \"MultiPolygon\"\n- });\n- }\n- var name, feature;\n- for (i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- name = this.stateName[feature.state];\n- if (name) {\n- this.writeNode(name, {\n- feature: feature,\n- options: options\n- }, node);\n- }\n- }\n- // switch back to original geometry types assignment\n- if (options.multi === true) {\n- this.setGeometryTypes();\n- }\n- }\n- if (options.nativeElements) {\n- for (i = 0, len = options.nativeElements.length; i < len; ++i) {\n- this.writeNode(\"wfs:Native\",\n- options.nativeElements[i], node);\n- }\n- }\n- return node;\n- },\n- \"Native\": function(nativeElement) {\n- var node = this.createElementNSPlus(\"wfs:Native\", {\n- attributes: {\n- vendorId: nativeElement.vendorId,\n- safeToIgnore: nativeElement.safeToIgnore\n- },\n- value: nativeElement.value\n- });\n- return node;\n- },\n- \"Insert\": function(obj) {\n- var feature = obj.feature;\n- var options = obj.options;\n- var node = this.createElementNSPlus(\"wfs:Insert\", {\n- attributes: {\n- handle: options && options.handle\n- }\n- });\n- this.srsName = this.getSrsName(feature);\n- this.writeNode(\"feature:_typeName\", feature, node);\n- return node;\n- },\n- \"Update\": function(obj) {\n- var feature = obj.feature;\n- var options = obj.options;\n- var node = this.createElementNSPlus(\"wfs:Update\", {\n- attributes: {\n- handle: options && options.handle,\n- typeName: (this.featureNS ? this.featurePrefix + \":\" : \"\") +\n- this.featureType\n- }\n- });\n- if (this.featureNS) {\n- node.setAttribute(\"xmlns:\" + this.featurePrefix, this.featureNS);\n- }\n+ clone: function(obj) {\n \n- // add in geometry\n- var modified = feature.modified;\n- if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) {\n- this.srsName = this.getSrsName(feature);\n- this.writeNode(\n- \"Property\", {\n- name: this.geometryName,\n- value: feature.geometry\n- }, node\n- );\n- }\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer(this.name, this.getOptions());\n+ }\n \n- // add in attributes\n- for (var key in feature.attributes) {\n- if (feature.attributes[key] !== undefined &&\n- (!modified || !modified.attributes ||\n- (modified.attributes && modified.attributes[key] !== undefined))) {\n- this.writeNode(\n- \"Property\", {\n- name: key,\n- value: feature.attributes[key]\n- }, node\n- );\n- }\n- }\n+ // catch any randomly tagged-on properties\n+ OpenLayers.Util.applyDefaults(obj, this);\n \n- // add feature id filter\n- this.writeNode(\"ogc:Filter\", new OpenLayers.Filter.FeatureId({\n- fids: [feature.fid]\n- }), node);\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 node;\n- },\n- \"Property\": function(obj) {\n- var node = this.createElementNSPlus(\"wfs:Property\");\n- this.writeNode(\"Name\", obj.name, node);\n- if (obj.value !== null) {\n- this.writeNode(\"Value\", obj.value, node);\n- }\n- return node;\n- },\n- \"Name\": function(name) {\n- return this.createElementNSPlus(\"wfs:Name\", {\n- value: name\n- });\n- },\n- \"Value\": function(obj) {\n- var node;\n- if (obj instanceof OpenLayers.Geometry) {\n- node = this.createElementNSPlus(\"wfs:Value\");\n- var geom = this.writeNode(\"feature:_geometry\", obj).firstChild;\n- node.appendChild(geom);\n- } else {\n- node = this.createElementNSPlus(\"wfs:Value\", {\n- value: obj\n- });\n- }\n- return node;\n- },\n- \"Delete\": function(obj) {\n- var feature = obj.feature;\n- var options = obj.options;\n- var node = this.createElementNSPlus(\"wfs:Delete\", {\n- attributes: {\n- handle: options && options.handle,\n- typeName: (this.featureNS ? this.featurePrefix + \":\" : \"\") +\n- this.featureType\n- }\n- });\n- if (this.featureNS) {\n- node.setAttribute(\"xmlns:\" + this.featurePrefix, this.featureNS);\n- }\n- this.writeNode(\"ogc:Filter\", new OpenLayers.Filter.FeatureId({\n- fids: [feature.fid]\n- }), node);\n- return node;\n- }\n- }\n+ return obj;\n },\n \n /**\n- * Method: schemaLocationAttr\n- * Generate the xsi:schemaLocation attribute value.\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- * {String} The xsi:schemaLocation attribute or undefined if none.\n+ * {Object} the of the layer, representing the current state.\n */\n- schemaLocationAttr: function(options) {\n- options = OpenLayers.Util.extend({\n- featurePrefix: this.featurePrefix,\n- schema: this.schema\n- }, options);\n- var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);\n- if (options.schema) {\n- schemaLocations[options.featurePrefix] = options.schema;\n+ getOptions: function() {\n+ var options = {};\n+ for (var o in this.options) {\n+ options[o] = this[o];\n }\n- var parts = [];\n- var uri;\n- for (var key in schemaLocations) {\n- uri = this.namespaces[key];\n- if (uri) {\n- parts.push(uri + \" \" + schemaLocations[key]);\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- var value = parts.join(\" \") || undefined;\n- return value;\n },\n \n /**\n- * Method: setFilterProperty\n- * Set the property of each spatial filter.\n- *\n+ * APIMethod: addOptions\n+ * \n * Parameters:\n- * filter - {}\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- setFilterProperty: function(filter) {\n- if (filter.filters) {\n- for (var i = 0, len = filter.filters.length; i < len; ++i) {\n- OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]);\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- } else {\n- if (filter instanceof OpenLayers.Filter.Spatial && !filter.property) {\n- // got a spatial filter without property, so set it\n- filter.property = this.geometryName;\n+ if (newOptions.projection) {\n+ // get maxResolution, units and maxExtent from projection defaults if\n+ // they are not defined already\n+ OpenLayers.Util.applyDefaults(newOptions,\n+ OpenLayers.Projection.defaults[newOptions.projection.getCode()]);\n+ }\n+ // allow array for extents\n+ if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent);\n+ }\n+ if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent);\n }\n }\n- },\n \n- CLASS_NAME: \"OpenLayers.Format.WFST.v1\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Filter/Logical.js\n- ====================================================================== */\n+ // update our copy for clone\n+ OpenLayers.Util.extend(this.options, newOptions);\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ // add new options to this\n+ OpenLayers.Util.extend(this, newOptions);\n \n+ // get the units from the projection, if we have a projection\n+ // and it it has units\n+ if (this.projection && this.projection.getUnits()) {\n+ this.units = this.projection.getUnits();\n+ }\n \n-/**\n- * @requires OpenLayers/Filter.js\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-/**\n- * Class: OpenLayers.Filter.Logical\n- * This class represents ogc:And, ogc:Or and ogc:Not rules.\n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {\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- * APIProperty: filters\n- * {Array()} Child filters for this filter.\n+ * APIMethod: onMapResize\n+ * This function can be implemented by subclasses\n */\n- filters: null,\n+ onMapResize: function() {\n+ //this function can be implemented by subclasses \n+ },\n \n /**\n- * APIProperty: type\n- * {String} type of logical operator. Available types are:\n- * - OpenLayers.Filter.Logical.AND = \"&&\";\n- * - OpenLayers.Filter.Logical.OR = \"||\";\n- * - OpenLayers.Filter.Logical.NOT = \"!\";\n- */\n- type: null,\n-\n- /** \n- * Constructor: OpenLayers.Filter.Logical\n- * Creates a logical filter (And, Or, Not).\n+ * APIMethod: redraw\n+ * Redraws the layer. Returns true if the layer was redrawn, false if not.\n *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * filter.\n- * \n * Returns:\n- * {}\n+ * {Boolean} The layer was redrawn.\n */\n- initialize: function(options) {\n- this.filters = [];\n- OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n- },\n+ redraw: function() {\n+ var redrawn = false;\n+ if (this.map) {\n \n- /** \n- * APIMethod: destroy\n- * Remove reference to child filters.\n- */\n- destroy: function() {\n- this.filters = null;\n- OpenLayers.Filter.prototype.destroy.apply(this);\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- * APIMethod: evaluate\n- * Evaluates this filter in a specific context.\n+ * Method: moveTo\n * \n * Parameters:\n- * context - {Object} Context to use in evaluating the filter. A vector\n- * feature may also be provided to evaluate feature attributes in \n- * comparison filters or geometries in spatial filters.\n- * \n- * Returns:\n- * {Boolean} The filter applies.\n+ * 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- evaluate: function(context) {\n- var i, len;\n- switch (this.type) {\n- case OpenLayers.Filter.Logical.AND:\n- for (i = 0, len = this.filters.length; i < len; i++) {\n- if (this.filters[i].evaluate(context) == false) {\n- return false;\n- }\n- }\n- return true;\n-\n- case OpenLayers.Filter.Logical.OR:\n- for (i = 0, len = this.filters.length; i < len; i++) {\n- if (this.filters[i].evaluate(context) == true) {\n- return true;\n- }\n- }\n- return false;\n-\n- case OpenLayers.Filter.Logical.NOT:\n- return (!this.filters[0].evaluate(context));\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ var display = this.visibility;\n+ if (!this.isBaseLayer) {\n+ display = display && this.inRange;\n }\n- return undefined;\n+ this.display(display);\n },\n \n /**\n- * APIMethod: clone\n- * Clones this filter.\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- * Returns:\n- * {} Clone of this filter.\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- clone: function() {\n- var filters = [];\n- for (var i = 0, len = this.filters.length; i < len; ++i) {\n- filters.push(this.filters[i].clone());\n- }\n- return new OpenLayers.Filter.Logical({\n- type: this.type,\n- filters: filters\n- });\n- },\n+ setMap: function(map) {\n+ if (this.map == null) {\n \n- CLASS_NAME: \"OpenLayers.Filter.Logical\"\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-OpenLayers.Filter.Logical.AND = \"&&\";\n-OpenLayers.Filter.Logical.OR = \"||\";\n-OpenLayers.Filter.Logical.NOT = \"!\";\n-/* ======================================================================\n- OpenLayers/Filter/Comparison.js\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-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\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-/**\n- * @requires OpenLayers/Filter.js\n- */\n+ this.initResolutions();\n \n-/**\n- * Class: OpenLayers.Filter.Comparison\n- * This class represents a comparison filter.\n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {\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- /**\n- * APIProperty: type\n- * {String} type: type of the comparison. This is one of\n- * - OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n- * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n- * - OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n- * - OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n- * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n- * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n- * - OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n- * - OpenLayers.Filter.Comparison.LIKE = \"~\";\n- * - OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n- */\n- type: null,\n+ // deal with gutters\n+ this.setTileSize();\n+ }\n+ },\n \n /**\n- * APIProperty: property\n- * {String}\n- * name of the context property to compare\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- property: null,\n+ afterAdd: function() {},\n \n /**\n- * APIProperty: value\n- * {Number} or {String}\n- * comparison value for binary comparisons. In the case of a String, this\n- * can be a combination of text and propertyNames in the form\n- * \"literal ${propertyName}\"\n+ * 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- value: null,\n+ removeMap: function(map) {\n+ //to be overridden by subclasses\n+ },\n \n /**\n- * Property: matchCase\n- * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO\n- * comparisons. The Filter Encoding 1.1 specification added a matchCase\n- * attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo\n- * elements. This property will be serialized with those elements only\n- * if using the v1.1.0 filter format. However, when evaluating filters\n- * here, the matchCase property will always be respected (for EQUAL_TO\n- * and NOT_EQUAL_TO). Default is true. \n+ * 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- matchCase: true,\n+ getImageSize: function(bounds) {\n+ return (this.imageSize || this.tileSize);\n+ },\n \n /**\n- * APIProperty: lowerBoundary\n- * {Number} or {String}\n- * lower boundary for between comparisons. In the case of a String, this\n- * can be a combination of text and propertyNames in the form\n- * \"literal ${propertyName}\"\n+ * 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- lowerBoundary: null,\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- * APIProperty: upperBoundary\n- * {Number} or {String}\n- * upper boundary for between comparisons. In the case of a String, this\n- * can be a combination of text and propertyNames in the form\n- * \"literal ${propertyName}\"\n- */\n- upperBoundary: null,\n-\n- /** \n- * Constructor: OpenLayers.Filter.Comparison\n- * Creates a comparison rule.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * rule\n+ * APIMethod: getVisibility\n * \n * Returns:\n- * {}\n+ * {Boolean} The layer should be displayed (if in range).\n */\n- initialize: function(options) {\n- OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n- // since matchCase on PropertyIsLike is not schema compliant, we only\n- // want to use this if explicitly asked for\n- if (this.type === OpenLayers.Filter.Comparison.LIKE &&\n- options.matchCase === undefined) {\n- this.matchCase = null;\n- }\n+ getVisibility: function() {\n+ return this.visibility;\n },\n \n- /**\n- * APIMethod: evaluate\n- * Evaluates this filter in a specific context.\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- * Parameters:\n- * context - {Object} Context to use in evaluating the filter. If a vector\n- * feature is provided, the feature.attributes will be used as context.\n+ * 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- * Returns:\n- * {Boolean} The filter applies.\n+ * Parameters:\n+ * visibility - {Boolean} Whether or not to display the layer (if in range)\n */\n- evaluate: function(context) {\n- if (context instanceof OpenLayers.Feature.Vector) {\n- context = context.attributes;\n- }\n- var result = false;\n- var got = context[this.property];\n- var exp;\n- switch (this.type) {\n- case OpenLayers.Filter.Comparison.EQUAL_TO:\n- exp = this.value;\n- if (!this.matchCase &&\n- typeof got == \"string\" && typeof exp == \"string\") {\n- result = (got.toUpperCase() == exp.toUpperCase());\n- } else {\n- result = (got == exp);\n- }\n- break;\n- case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:\n- exp = this.value;\n- if (!this.matchCase &&\n- typeof got == \"string\" && typeof exp == \"string\") {\n- result = (got.toUpperCase() != exp.toUpperCase());\n- } else {\n- result = (got != exp);\n- }\n- break;\n- case OpenLayers.Filter.Comparison.LESS_THAN:\n- result = got < this.value;\n- break;\n- case OpenLayers.Filter.Comparison.GREATER_THAN:\n- result = got > this.value;\n- break;\n- case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:\n- result = got <= this.value;\n- break;\n- case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:\n- result = got >= this.value;\n- break;\n- case OpenLayers.Filter.Comparison.BETWEEN:\n- result = (got >= this.lowerBoundary) &&\n- (got <= this.upperBoundary);\n- break;\n- case OpenLayers.Filter.Comparison.LIKE:\n- var regexp = new RegExp(this.value, \"gi\");\n- result = regexp.test(got);\n- break;\n- case OpenLayers.Filter.Comparison.IS_NULL:\n- result = (got === null);\n- break;\n+ 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- return result;\n },\n \n- /**\n- * APIMethod: value2regex\n- * Converts the value of this rule into a regular expression string,\n- * according to the wildcard characters specified. This method has to\n- * be called after instantiation of this class, if the value is not a\n- * regular expression already.\n+ /** \n+ * 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- * wildCard - {Char} wildcard character in the above value, default\n- * is \"*\"\n- * singleChar - {Char} single-character wildcard in the above value\n- * default is \".\"\n- * escapeChar - {Char} escape character in the above value, default is\n- * \"!\"\n- * \n- * Returns:\n- * {String} regular expression string\n+ * display - {Boolean}\n */\n- value2regex: function(wildCard, singleChar, escapeChar) {\n- if (wildCard == \".\") {\n- throw new Error(\"'.' is an unsupported wildCard character for \" +\n- \"OpenLayers.Filter.Comparison\");\n+ 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- // set UMN MapServer defaults for unspecified parameters\n- wildCard = wildCard ? wildCard : \"*\";\n- singleChar = singleChar ? singleChar : \".\";\n- escapeChar = escapeChar ? escapeChar : \"!\";\n-\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\" + escapeChar + \"(.|$)\", \"g\"), \"\\\\$1\");\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\" + singleChar, \"g\"), \".\");\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\" + wildCard, \"g\"), \".*\");\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\\\\.\\\\*\", \"g\"), \"\\\\\" + wildCard);\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\\\\\\\\.\", \"g\"), \"\\\\\" + singleChar);\n-\n- return this.value;\n },\n \n /**\n- * Method: regex2value\n- * Convert the value of this rule from a regular expression string into an\n- * ogc literal string using a wildCard of *, a singleChar of ., and an\n- * escape of !. Leaves the property unmodified.\n+ * APIMethod: calculateInRange\n * \n * Returns:\n- * {String} A string value.\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- regex2value: function() {\n-\n- var value = this.value;\n-\n- // replace ! with !!\n- value = value.replace(/!/g, \"!!\");\n-\n- // replace \\. with !. (watching out for \\\\.)\n- value = value.replace(/(\\\\)?\\\\\\./g, function($0, $1) {\n- return $1 ? $0 : \"!.\";\n- });\n-\n- // replace \\* with #* (watching out for \\\\*)\n- value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n- return $1 ? $0 : \"!*\";\n- });\n-\n- // replace \\\\ with \\\n- value = value.replace(/\\\\\\\\/g, \"\\\\\");\n-\n- // convert .* to * (the sequence #.* is not allowed)\n- value = value.replace(/\\.\\*/g, \"*\");\n+ calculateInRange: function() {\n+ var inRange = false;\n \n- return value;\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: clone\n- * Clones this filter.\n+ /** \n+ * APIMethod: setIsBaseLayer\n * \n- * Returns:\n- * {} Clone of this filter.\n+ * Parameters:\n+ * isBaseLayer - {Boolean}\n */\n- clone: function() {\n- return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);\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- CLASS_NAME: \"OpenLayers.Filter.Comparison\"\n-});\n-\n-\n-OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n-OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n-OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n-OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n-OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n-OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n-OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n-OpenLayers.Filter.Comparison.LIKE = \"~\";\n-OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n-/* ======================================================================\n- OpenLayers/Format/Filter.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- * @requires OpenLayers/Filter/FeatureId.js\n- * @requires OpenLayers/Filter/Logical.js\n- * @requires OpenLayers/Filter/Comparison.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.Filter\n- * Read/Write ogc:Filter. Create a new instance with the \n- * constructor.\n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.0.0\".\n- */\n- defaultVersion: \"1.0.0\",\n+ /********************************************************/\n+ /* */\n+ /* Baselayer Functions */\n+ /* */\n+ /********************************************************/\n \n- /**\n- * APIMethod: write\n- * Write an ogc:Filter given a filter object.\n- *\n- * Parameters:\n- * filter - {} An filter.\n- * options - {Object} Optional configuration object.\n- *\n- * Returns:\n- * {Elment} An ogc:Filter element node.\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- /**\n- * APIMethod: read\n- * Read and Filter doc and return an object representing the Filter.\n- *\n- * Parameters:\n- * data - {String | DOMElement} Data to read.\n- *\n- * Returns:\n- * {} A filter object.\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- CLASS_NAME: \"OpenLayers.Format.Filter\"\n-});\n-/* ======================================================================\n- OpenLayers/Filter/Function.js\n- ====================================================================== */\n+ var i, len, p;\n+ var props = {},\n+ alwaysInRange = true;\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\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-/**\n- * @requires OpenLayers/Filter.js\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- * Class: OpenLayers.Filter.Function\n- * This class represents a filter function.\n- * We are using this class for creation of complex \n- * filters that can contain filter functions as values.\n- * Nesting function as other functions parameter is supported.\n- * \n- * Inherits from:\n- * - \n- */\n-OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {\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- * APIProperty: name\n- * {String} Name of the function.\n- */\n- name: null,\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- /**\n- * APIProperty: params\n- * {Array( || String || Number)} Function parameters\n- * For now support only other Functions, String or Number\n- */\n- params: null,\n+ // ok, we new need to set properties in the instance\n \n- /** \n- * Constructor: OpenLayers.Filter.Function\n- * Creates a filter function.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * function.\n- * \n- * Returns:\n- * {}\n- */\n+ // get maxResolution from the config if it's defined there\n+ var maxResolution;\n+ if (this.options.maxResolution &&\n+ this.options.maxResolution !== \"auto\") {\n+ maxResolution = this.options.maxResolution;\n+ }\n+ if (this.options.minScale) {\n+ maxResolution = OpenLayers.Util.getResolutionFromScale(\n+ this.options.minScale, this.units);\n+ }\n \n- CLASS_NAME: \"OpenLayers.Filter.Function\"\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-/* ======================================================================\n- OpenLayers/BaseTypes/Date.js\n- ====================================================================== */\n+ if (props.resolutions) {\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ //sort resolutions array descendingly\n+ props.resolutions.sort(function(a, b) {\n+ return (b - a);\n+ });\n \n-/**\n- * @requires OpenLayers/SingleFile.js\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-/**\n- * Namespace: OpenLayers.Date\n- * Contains implementations of Date.parse and date.toISOString that match the\n- * ECMAScript 5 specification for parsing RFC 3339 dates.\n- * http://tools.ietf.org/html/rfc3339\n- */\n-OpenLayers.Date = {\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- /** \n- * APIProperty: dateRegEx\n- * The regex to be used for validating dates. You can provide your own\n- * regex for instance for adding support for years before BC. Default\n- * value is: /^(?:(\\d{4})(?:-(\\d{2})(?:-(\\d{2}))?)?)?(?:(?:T(\\d{1,2}):(\\d{2}):(\\d{2}(?:\\.\\d+)?)(Z|(?:[+-]\\d{1,2}(?::(\\d{2}))?)))|Z)?$/\n- */\n- dateRegEx: /^(?:(\\d{4})(?:-(\\d{2})(?:-(\\d{2}))?)?)?(?:(?:T(\\d{1,2}):(\\d{2}):(\\d{2}(?:\\.\\d+)?)(Z|(?:[+-]\\d{1,2}(?::(\\d{2}))?)))|Z)?$/,\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: toISOString\n- * Generates a string representing a date. The format of the string follows\n- * the profile of ISO 8601 for date and time on the Internet (see\n- * http://tools.ietf.org/html/rfc3339). If the toISOString method is\n- * available on the Date prototype, that is used. The toISOString\n- * method for Date instances is defined in ECMA-262.\n+ * Method: resolutionsFromScales\n+ * Derive resolutions from scales.\n *\n * Parameters:\n- * date - {Date} A date object.\n+ * scales - {Array(Number)} Scales\n *\n- * Returns:\n- * {String} A string representing the date (e.g.\n- * \"2010-08-07T16:58:23.123Z\"). If the date does not have a valid time\n- * (i.e. isNaN(date.getTime())) this method returns the string \"Invalid\n- * Date\". The ECMA standard says the toISOString method should throw\n- * RangeError in this case, but Firefox returns a string instead. For\n- * best results, use isNaN(date.getTime()) to determine date validity\n- * before generating date strings.\n+ * Returns\n+ * {Array(Number)} Resolutions\n */\n- toISOString: (function() {\n- if (\"toISOString\" in Date.prototype) {\n- return function(date) {\n- return date.toISOString();\n- };\n- } else {\n- return function(date) {\n- var str;\n- if (isNaN(date.getTime())) {\n- // ECMA-262 says throw RangeError, Firefox returns\n- // \"Invalid Date\"\n- str = \"Invalid Date\";\n- } else {\n- str =\n- date.getUTCFullYear() + \"-\" +\n- OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + \"-\" +\n- OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + \"T\" +\n- OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + \":\" +\n- OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + \":\" +\n- OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + \".\" +\n- OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + \"Z\";\n- }\n- return str;\n- };\n+ resolutionsFromScales: function(scales) {\n+ if (scales == null) {\n+ return;\n }\n-\n- })(),\n+ var resolutions, i, len;\n+ len = scales.length;\n+ resolutions = new Array(len);\n+ for (i = 0; i < len; i++) {\n+ resolutions[i] = OpenLayers.Util.getResolutionFromScale(\n+ scales[i], this.units);\n+ }\n+ return resolutions;\n+ },\n \n /**\n- * APIMethod: parse\n- * Generate a date object from a string. The format for the string follows\n- * the profile of ISO 8601 for date and time on the Internet (see\n- * http://tools.ietf.org/html/rfc3339). We don't call the native\n- * Date.parse because of inconsistency between implmentations. In\n- * Chrome, calling Date.parse with a string that doesn't contain any\n- * indication of the timezone (e.g. \"2011\"), the date is interpreted\n- * in local time. On Firefox, the assumption is UTC.\n+ * Method: calculateResolutions\n+ * Calculate resolutions based on the provided properties.\n *\n * Parameters:\n- * str - {String} A string representing the date (e.g.\n- * \"2010\", \"2010-08\", \"2010-08-07\", \"2010-08-07T16:58:23.123Z\",\n- * \"2010-08-07T11:58:23.123-06\").\n+ * props - {Object} Properties\n *\n * Returns:\n- * {Date} A date object. If the string could not be parsed, an invalid\n- * date is returned (i.e. isNaN(date.getTime())).\n+ * {Array({Number})} Array of resolutions.\n */\n- parse: function(str) {\n- var date;\n- var match = str.match(this.dateRegEx);\n- if (match && (match[1] || match[7])) { // must have at least year or time\n- var year = parseInt(match[1], 10) || 0;\n- var month = (parseInt(match[2], 10) - 1) || 0;\n- var day = parseInt(match[3], 10) || 1;\n- date = new Date(Date.UTC(year, month, day));\n- // optional time\n- var type = match[7];\n- if (type) {\n- var hours = parseInt(match[4], 10);\n- var minutes = parseInt(match[5], 10);\n- var secFrac = parseFloat(match[6]);\n- var seconds = secFrac | 0;\n- var milliseconds = Math.round(1000 * (secFrac - seconds));\n- date.setUTCHours(hours, minutes, seconds, milliseconds);\n- // check offset\n- if (type !== \"Z\") {\n- var hoursOffset = parseInt(type, 10);\n- var minutesOffset = parseInt(match[8], 10) || 0;\n- var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);\n- date = new Date(date.getTime() + offset);\n- }\n- }\n- } else {\n- date = new Date(\"invalid\");\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- return date;\n- }\n-};\n-/* ======================================================================\n- OpenLayers/Format/Filter/v1.js\n- ====================================================================== */\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-/**\n- * @requires OpenLayers/Format/Filter.js\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Filter/Function.js\n- * @requires OpenLayers/BaseTypes/Date.js\n- */\n+ // determine minResolution\n+ var minResolution = props.minResolution;\n+ if (props.maxScale != null) {\n+ minResolution =\n+ OpenLayers.Util.getResolutionFromScale(props.maxScale,\n+ this.units);\n+ } else if (props.minResolution == \"auto\" && this.minExtent != null) {\n+ viewSize = this.map.getSize();\n+ wRes = this.minExtent.getWidth() / viewSize.w;\n+ hRes = this.minExtent.getHeight() / viewSize.h;\n+ minResolution = Math.max(wRes, hRes);\n+ }\n \n-/**\n- * Class: OpenLayers.Format.Filter.v1\n- * Superclass for Filter version 1 parsers.\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\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- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- ogc: \"http://www.opengis.net/ogc\",\n- gml: \"http://www.opengis.net/gml\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\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- /**\n- * Property: defaultPrefix\n- */\n- defaultPrefix: \"ogc\",\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- /**\n- * Property: schemaLocation\n- * {String} Schema location for a particular minor version.\n- */\n- schemaLocation: null,\n+ // now we have numZoomLevels and at least one of maxResolution\n+ // or minResolution, we can populate the resolutions array\n \n- /**\n- * Constructor: OpenLayers.Format.Filter.v1\n- * Instances of this class are not created directly. Use the\n- * constructor instead.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ 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- * Method: read\n- *\n- * Parameters:\n- * data - {DOMElement} A Filter document element.\n- *\n+ * APIMethod: getResolution\n+ * \n * Returns:\n- * {} A filter object.\n+ * {Float} The currently selected resolution of the map, taken from the\n+ * resolutions array, indexed by current zoom level.\n */\n- read: function(data) {\n- var obj = {};\n- this.readers.ogc[\"Filter\"].apply(this, [data, obj]);\n- return obj.filter;\n+ getResolution: function() {\n+ var zoom = this.map.getZoom();\n+ return this.getResolutionForZoom(zoom);\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- \"ogc\": {\n- \"_expression\": function(node) {\n- // only the simplest of ogc:expression handled\n- // \"some text and an attribute\"}\n- var obj, value = \"\";\n- for (var child = node.firstChild; child; child = child.nextSibling) {\n- switch (child.nodeType) {\n- case 1:\n- obj = this.readNode(child);\n- if (obj.property) {\n- value += \"${\" + obj.property + \"}\";\n- } else if (obj.value !== undefined) {\n- value += obj.value;\n- }\n- break;\n- case 3: // text node\n- case 4: // cdata section\n- value += child.nodeValue;\n- }\n- }\n- return value;\n- },\n- \"Filter\": function(node, parent) {\n- // Filters correspond to subclasses of OpenLayers.Filter.\n- // Since they contain information we don't persist, we\n- // create a temporary object and then pass on the filter\n- // (ogc:Filter) to the parent obj.\n- var obj = {\n- fids: [],\n- filters: []\n- };\n- this.readChildNodes(node, obj);\n- if (obj.fids.length > 0) {\n- parent.filter = new OpenLayers.Filter.FeatureId({\n- fids: obj.fids\n- });\n- } else if (obj.filters.length > 0) {\n- parent.filter = obj.filters[0];\n- }\n- },\n- \"FeatureId\": function(node, obj) {\n- var fid = node.getAttribute(\"fid\");\n- if (fid) {\n- obj.fids.push(fid);\n- }\n- },\n- \"And\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.AND\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"Or\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.OR\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"Not\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.NOT\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"PropertyIsLessThan\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.LESS_THAN\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"PropertyIsGreaterThan\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.GREATER_THAN\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"PropertyIsLessThanOrEqualTo\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"PropertyIsGreaterThanOrEqualTo\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"PropertyIsBetween\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.BETWEEN\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"Literal\": function(node, obj) {\n- obj.value = OpenLayers.String.numericIf(\n- this.getChildValue(node), true);\n- },\n- \"PropertyName\": function(node, filter) {\n- filter.property = this.getChildValue(node);\n- },\n- \"LowerBoundary\": function(node, filter) {\n- filter.lowerBoundary = OpenLayers.String.numericIf(\n- this.readers.ogc._expression.call(this, node), true);\n- },\n- \"UpperBoundary\": function(node, filter) {\n- filter.upperBoundary = OpenLayers.String.numericIf(\n- this.readers.ogc._expression.call(this, node), true);\n- },\n- \"Intersects\": function(node, obj) {\n- this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);\n- },\n- \"Within\": function(node, obj) {\n- this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);\n- },\n- \"Contains\": function(node, obj) {\n- this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);\n- },\n- \"DWithin\": function(node, obj) {\n- this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);\n- },\n- \"Distance\": function(node, obj) {\n- obj.distance = parseInt(this.getChildValue(node));\n- obj.distanceUnits = node.getAttribute(\"units\");\n- },\n- \"Function\": function(node, obj) {\n- //TODO write decoder for it\n- return;\n- },\n- \"PropertyIsNull\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.IS_NULL\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\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 },\n \n /**\n- * Method: readSpatial\n- *\n- * Read a {} filter.\n+ * APIMethod: getZoomForExtent\n * \n * Parameters:\n- * node - {DOMElement} A DOM element that contains an ogc:expression.\n- * obj - {Object} The target object.\n- * type - {String} One of the OpenLayers.Filter.Spatial.* constants.\n+ * extent - {}\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- * {} The created filter.\n+ * {Integer} The index of the zoomLevel (entry in the resolutions array) \n+ * for the passed-in extent. We do this by calculating the ideal \n+ * resolution for the given extent (based on the map size) and then \n+ * calling getZoomForResolution(), passing along the 'closest'\n+ * parameter.\n */\n- readSpatial: function(node, obj, type) {\n- var filter = new OpenLayers.Filter.Spatial({\n- type: type\n- });\n- this.readChildNodes(node, filter);\n- filter.value = filter.components[0];\n- delete filter.components;\n- obj.filters.push(filter);\n+ getZoomForExtent: function(extent, closest) {\n+ var viewSize = this.map.getSize();\n+ var idealResolution = Math.max(extent.getWidth() / viewSize.w,\n+ extent.getHeight() / viewSize.h);\n+\n+ return this.getZoomForResolution(idealResolution, closest);\n+ },\n+\n+ /** \n+ * Method: getDataExtent\n+ * Calculates the max extent which includes all of the data for the layer.\n+ * This function is to be implemented by subclasses.\n+ * \n+ * Returns:\n+ * {}\n+ */\n+ getDataExtent: function() {\n+ //to be implemented by subclasses\n },\n \n /**\n- * APIMethod: encodeLiteral\n- * Generates the string representation of a value for use in \n- * elements. The default encoder writes Date values as ISO 8601 \n- * strings.\n- *\n+ * APIMethod: getResolutionForZoom\n+ * \n * Parameters:\n- * value - {Object} Literal value to encode\n- *\n+ * zoom - {Float}\n+ * \n * Returns:\n- * {String} String representation of the provided value.\n+ * {Float} A suitable resolution for the specified zoom.\n */\n- encodeLiteral: function(value) {\n- if (value instanceof Date) {\n- value = OpenLayers.Date.toISOString(value);\n+ getResolutionForZoom: function(zoom) {\n+ zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n+ var resolution;\n+ if (this.map.fractionalZoom) {\n+ var low = Math.floor(zoom);\n+ var high = Math.ceil(zoom);\n+ resolution = this.resolutions[low] -\n+ ((zoom - low) * (this.resolutions[low] - this.resolutions[high]));\n+ } else {\n+ resolution = this.resolutions[Math.round(zoom)];\n }\n- return value;\n+ return resolution;\n },\n \n /**\n- * Method: writeOgcExpression\n- * Limited support for writing OGC expressions. Currently it supports\n- * ( || String || Number)\n- *\n+ * APIMethod: getZoomForResolution\n+ * \n * Parameters:\n- * value - ( || String || Number)\n- * node - {DOMElement} A parent DOM element \n- *\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- * {DOMElement} Updated node element.\n+ * {Integer} The index of the zoomLevel (entry in the resolutions array) \n+ * that corresponds to the best fit resolution given the passed in \n+ * value and the 'closest' specification.\n */\n- writeOgcExpression: function(value, node) {\n- if (value instanceof OpenLayers.Filter.Function) {\n- this.writeNode(\"Function\", value, node);\n+ getZoomForResolution: function(resolution, closest) {\n+ var zoom, i, len;\n+ if (this.map.fractionalZoom) {\n+ var lowZoom = 0;\n+ var highZoom = this.resolutions.length - 1;\n+ var highRes = this.resolutions[lowZoom];\n+ var lowRes = this.resolutions[highZoom];\n+ var res;\n+ for (i = 0, len = this.resolutions.length; i < len; ++i) {\n+ res = this.resolutions[i];\n+ if (res >= resolution) {\n+ highRes = res;\n+ lowZoom = i;\n+ }\n+ if (res <= resolution) {\n+ lowRes = res;\n+ highZoom = i;\n+ break;\n+ }\n+ }\n+ var dRes = highRes - lowRes;\n+ if (dRes > 0) {\n+ zoom = lowZoom + ((highRes - resolution) / dRes);\n+ } else {\n+ zoom = lowZoom;\n+ }\n } else {\n- this.writeNode(\"Literal\", value, node);\n+ var diff;\n+ var minDiff = Number.POSITIVE_INFINITY;\n+ for (i = 0, len = this.resolutions.length; i < len; i++) {\n+ if (closest) {\n+ diff = Math.abs(this.resolutions[i] - resolution);\n+ if (diff > minDiff) {\n+ break;\n+ }\n+ minDiff = diff;\n+ } else {\n+ if (this.resolutions[i] < resolution) {\n+ break;\n+ }\n+ }\n+ }\n+ zoom = Math.max(0, i - 1);\n }\n- return node;\n+ return zoom;\n },\n \n /**\n- * Method: write\n- *\n+ * APIMethod: getLonLatFromViewPortPx\n+ * \n * Parameters:\n- * filter - {} A filter object.\n+ * viewPortPx - {|Object} An OpenLayers.Pixel or\n+ * an object with a 'x'\n+ * and 'y' properties.\n *\n * Returns:\n- * {DOMElement} An ogc:Filter element.\n+ * {} An OpenLayers.LonLat which is the passed-in \n+ * view port , translated into lon/lat by the layer.\n */\n- write: function(filter) {\n- return this.writers.ogc[\"Filter\"].apply(this, [filter]);\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ var map = this.map;\n+ if (viewPortPx != null && map.minPx) {\n+ var res = map.getResolution();\n+ var maxExtent = map.getMaxExtent({\n+ restricted: true\n+ });\n+ var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n+ var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n+ lonlat = new OpenLayers.LonLat(lon, lat);\n+\n+ if (this.wrapDateLine) {\n+ lonlat = lonlat.wrapDateLine(this.maxExtent);\n+ }\n+ }\n+ return lonlat;\n },\n \n /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n+ * APIMethod: getViewPortPxFromLonLat\n+ * Returns a pixel location given a map location. This method will return\n+ * fractional pixel values.\n+ * \n+ * Parameters:\n+ * lonlat - {|Object} An OpenLayers.LonLat or\n+ * an object with a 'lon'\n+ * and 'lat' properties.\n+ *\n+ * Returns: \n+ * {} An which is the passed-in \n+ * lonlat translated into view port pixels.\n */\n- writers: {\n- \"ogc\": {\n- \"Filter\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:Filter\");\n- this.writeNode(this.getFilterType(filter), filter, node);\n- return node;\n- },\n- \"_featureIds\": function(filter) {\n- var node = this.createDocumentFragment();\n- for (var i = 0, ii = filter.fids.length; i < ii; ++i) {\n- this.writeNode(\"ogc:FeatureId\", filter.fids[i], node);\n- }\n- return node;\n- },\n- \"FeatureId\": function(fid) {\n- return this.createElementNSPlus(\"ogc:FeatureId\", {\n- attributes: {\n- fid: fid\n- }\n- });\n- },\n- \"And\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:And\");\n- var childFilter;\n- for (var i = 0, ii = filter.filters.length; i < ii; ++i) {\n- childFilter = filter.filters[i];\n- this.writeNode(\n- this.getFilterType(childFilter), childFilter, node\n- );\n- }\n- return node;\n- },\n- \"Or\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:Or\");\n- var childFilter;\n- for (var i = 0, ii = filter.filters.length; i < ii; ++i) {\n- childFilter = filter.filters[i];\n- this.writeNode(\n- this.getFilterType(childFilter), childFilter, node\n- );\n+ getViewPortPxFromLonLat: function(lonlat, resolution) {\n+ var px = null;\n+ if (lonlat != null) {\n+ resolution = resolution || this.map.getResolution();\n+ var extent = this.map.calculateBounds(null, resolution);\n+ px = new OpenLayers.Pixel(\n+ (1 / resolution * (lonlat.lon - extent.left)),\n+ (1 / resolution * (extent.top - lonlat.lat))\n+ );\n+ }\n+ return px;\n+ },\n+\n+ /**\n+ * APIMethod: setOpacity\n+ * Sets the opacity for the entire layer (all images)\n+ * \n+ * Parameters:\n+ * opacity - {Float}\n+ */\n+ setOpacity: function(opacity) {\n+ if (opacity != this.opacity) {\n+ this.opacity = opacity;\n+ var childNodes = this.div.childNodes;\n+ for (var i = 0, len = childNodes.length; i < len; ++i) {\n+ var element = childNodes[i].firstChild || childNodes[i];\n+ var lastChild = childNodes[i].lastChild;\n+ //TODO de-uglify this\n+ if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n+ element = lastChild.parentNode;\n }\n- return node;\n- },\n- \"Not\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:Not\");\n- var childFilter = filter.filters[0];\n- this.writeNode(\n- this.getFilterType(childFilter), childFilter, node\n- );\n- return node;\n- },\n- \"PropertyIsLessThan\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:PropertyIsLessThan\");\n- // no ogc:expression handling for PropertyName for now\n- this.writeNode(\"PropertyName\", filter, node);\n- // handle Literals or Functions for now\n- this.writeOgcExpression(filter.value, node);\n- return node;\n- },\n- \"PropertyIsGreaterThan\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:PropertyIsGreaterThan\");\n- // no ogc:expression handling for PropertyName for now\n- this.writeNode(\"PropertyName\", filter, node);\n- // handle Literals or Functions for now\n- this.writeOgcExpression(filter.value, node);\n- return node;\n- },\n- \"PropertyIsLessThanOrEqualTo\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:PropertyIsLessThanOrEqualTo\");\n- // no ogc:expression handling for PropertyName for now\n- this.writeNode(\"PropertyName\", filter, node);\n- // handle Literals or Functions for now\n- this.writeOgcExpression(filter.value, node);\n- return node;\n- },\n- \"PropertyIsGreaterThanOrEqualTo\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:PropertyIsGreaterThanOrEqualTo\");\n- // no ogc:expression handling for PropertyName for now\n- this.writeNode(\"PropertyName\", filter, node);\n- // handle Literals or Functions for now\n- this.writeOgcExpression(filter.value, node);\n- return node;\n- },\n- \"PropertyIsBetween\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:PropertyIsBetween\");\n- // no ogc:expression handling for PropertyName for now\n- this.writeNode(\"PropertyName\", filter, node);\n- this.writeNode(\"LowerBoundary\", filter, node);\n- this.writeNode(\"UpperBoundary\", filter, node);\n- return node;\n- },\n- \"PropertyName\": function(filter) {\n- // no ogc:expression handling for now\n- return this.createElementNSPlus(\"ogc:PropertyName\", {\n- value: filter.property\n- });\n- },\n- \"Literal\": function(value) {\n- var encode = this.encodeLiteral ||\n- OpenLayers.Format.Filter.v1.prototype.encodeLiteral;\n- return this.createElementNSPlus(\"ogc:Literal\", {\n- value: encode(value)\n- });\n- },\n- \"LowerBoundary\": function(filter) {\n- // handle Literals or Functions for now\n- var node = this.createElementNSPlus(\"ogc:LowerBoundary\");\n- this.writeOgcExpression(filter.lowerBoundary, node);\n- return node;\n- },\n- \"UpperBoundary\": function(filter) {\n- // handle Literals or Functions for now\n- var node = this.createElementNSPlus(\"ogc:UpperBoundary\");\n- this.writeNode(\"Literal\", filter.upperBoundary, node);\n- return node;\n- },\n- \"INTERSECTS\": function(filter) {\n- return this.writeSpatial(filter, \"Intersects\");\n- },\n- \"WITHIN\": function(filter) {\n- return this.writeSpatial(filter, \"Within\");\n- },\n- \"CONTAINS\": function(filter) {\n- return this.writeSpatial(filter, \"Contains\");\n- },\n- \"DWITHIN\": function(filter) {\n- var node = this.writeSpatial(filter, \"DWithin\");\n- this.writeNode(\"Distance\", filter, node);\n- return node;\n- },\n- \"Distance\": function(filter) {\n- return this.createElementNSPlus(\"ogc:Distance\", {\n- attributes: {\n- units: filter.distanceUnits\n- },\n- value: filter.distance\n- });\n- },\n- \"Function\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:Function\", {\n- attributes: {\n- name: filter.name\n- }\n+ OpenLayers.Util.modifyDOMElement(element, null, null, null,\n+ null, null, null, opacity);\n+ }\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"opacity\"\n });\n- var params = filter.params;\n- for (var i = 0, len = params.length; i < len; i++) {\n- this.writeOgcExpression(params[i], node);\n- }\n- return node;\n- },\n- \"PropertyIsNull\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:PropertyIsNull\");\n- this.writeNode(\"PropertyName\", filter, node);\n- return node;\n }\n }\n },\n \n /**\n- * Method: getFilterType\n+ * Method: getZIndex\n+ * \n+ * Returns: \n+ * {Integer} the z-index of this layer\n */\n- getFilterType: function(filter) {\n- var filterType = this.filterMap[filter.type];\n- if (!filterType) {\n- throw \"Filter writing not supported for rule type: \" + filter.type;\n- }\n- return filterType;\n+ getZIndex: function() {\n+ return this.div.style.zIndex;\n },\n \n /**\n- * Property: filterMap\n- * {Object} Contains a member for each filter type. Values are node names\n- * for corresponding OGC Filter child elements.\n+ * Method: setZIndex\n+ * \n+ * Parameters: \n+ * zIndex - {Integer}\n */\n- filterMap: {\n- \"&&\": \"And\",\n- \"||\": \"Or\",\n- \"!\": \"Not\",\n- \"==\": \"PropertyIsEqualTo\",\n- \"!=\": \"PropertyIsNotEqualTo\",\n- \"<\": \"PropertyIsLessThan\",\n- \">\": \"PropertyIsGreaterThan\",\n- \"<=\": \"PropertyIsLessThanOrEqualTo\",\n- \">=\": \"PropertyIsGreaterThanOrEqualTo\",\n- \"..\": \"PropertyIsBetween\",\n- \"~\": \"PropertyIsLike\",\n- \"NULL\": \"PropertyIsNull\",\n- \"BBOX\": \"BBOX\",\n- \"DWITHIN\": \"DWITHIN\",\n- \"WITHIN\": \"WITHIN\",\n- \"CONTAINS\": \"CONTAINS\",\n- \"INTERSECTS\": \"INTERSECTS\",\n- \"FID\": \"_featureIds\"\n+ setZIndex: function(zIndex) {\n+ this.div.style.zIndex = zIndex;\n },\n \n- CLASS_NAME: \"OpenLayers.Format.Filter.v1\"\n+ /**\n+ * Method: adjustBounds\n+ * This function will take a bounds, and if wrapDateLine option is set\n+ * on the layer, it will return a bounds which is wrapped around the \n+ * world. We do not wrap for bounds which *cross* the \n+ * maxExtent.left/right, only bounds which are entirely to the left \n+ * or entirely to the right.\n+ * \n+ * Parameters:\n+ * bounds - {}\n+ */\n+ adjustBounds: function(bounds) {\n+\n+ if (this.gutter) {\n+ // Adjust the extent of a bounds in map units by the \n+ // layer's gutter in pixels.\n+ var mapGutter = this.gutter * this.map.getResolution();\n+ bounds = new OpenLayers.Bounds(bounds.left - mapGutter,\n+ bounds.bottom - mapGutter,\n+ bounds.right + mapGutter,\n+ bounds.top + mapGutter);\n+ }\n+\n+ if (this.wrapDateLine) {\n+ // wrap around the date line, within the limits of rounding error\n+ var wrappingOptions = {\n+ 'rightTolerance': this.getResolution(),\n+ 'leftTolerance': this.getResolution()\n+ };\n+ bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);\n+\n+ }\n+ return bounds;\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Layer\"\n });\n /* ======================================================================\n- OpenLayers/Format/GML.js\n+ OpenLayers/Layer/HTTPRequest.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/MultiPoint.js\n- * @requires OpenLayers/Geometry/LineString.js\n- * @requires OpenLayers/Geometry/MultiLineString.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- * @requires OpenLayers/Geometry/MultiPolygon.js\n+ * @requires OpenLayers/Layer.js\n */\n \n /**\n- * Class: OpenLayers.Format.GML\n- * Read/Write GML. Create a new instance with the \n- * constructor. Supports the GML simple features profile.\n+ * Class: OpenLayers.Layer.HTTPRequest\n * \n- * Inherits from:\n- * - \n+ * Inherits from: \n+ * - \n */\n-OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * APIProperty: featureNS\n- * {String} Namespace used for feature attributes. Default is\n- * \"http://mapserver.gis.umn.edu/mapserver\".\n- */\n- featureNS: \"http://mapserver.gis.umn.edu/mapserver\",\n-\n- /**\n- * APIProperty: featurePrefix\n- * {String} Namespace alias (or prefix) for feature nodes. Default is\n- * \"feature\".\n- */\n- featurePrefix: \"feature\",\n-\n- /**\n- * APIProperty: featureName\n- * {String} Element name for features. Default is \"featureMember\".\n- */\n- featureName: \"featureMember\",\n-\n- /**\n- * APIProperty: layerName\n- * {String} Name of data layer. Default is \"features\".\n- */\n- layerName: \"features\",\n-\n- /**\n- * APIProperty: geometryName\n- * {String} Name of geometry element. Defaults to \"geometry\".\n- */\n- geometryName: \"geometry\",\n+OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n \n /** \n- * APIProperty: collectionName\n- * {String} Name of featureCollection element.\n- */\n- collectionName: \"FeatureCollection\",\n-\n- /**\n- * APIProperty: gmlns\n- * {String} GML Namespace.\n+ * Constant: URL_HASH_FACTOR\n+ * {Float} Used to hash URL param strings for multi-WMS server selection.\n+ * Set to the Golden Ratio per Knuth's recommendation.\n */\n- gmlns: \"http://www.opengis.net/gml\",\n+ URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n \n- /**\n- * APIProperty: extractAttributes\n- * {Boolean} Extract attributes from GML.\n+ /** \n+ * Property: url\n+ * {Array(String) or String} This is either an array of url strings or \n+ * a single url string. \n */\n- extractAttributes: true,\n+ url: null,\n \n- /**\n- * APIProperty: xy\n- * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)\n- * Changing is not recommended, a new Format should be instantiated.\n+ /** \n+ * Property: params\n+ * {Object} Hashtable of key/value parameters\n */\n- xy: true,\n+ params: null,\n \n- /**\n- * Constructor: OpenLayers.Format.GML\n- * Create a new parser for GML.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n+ /** \n+ * APIProperty: reproject\n+ * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html\n+ * for information on the replacement for this functionality. \n+ * {Boolean} Whether layer should reproject itself based on base layer \n+ * locations. This allows reprojection onto commercial layers. \n+ * Default is false: Most layers can't reproject, but layers \n+ * which can create non-square geographic pixels can, like WMS.\n+ * \n */\n- 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- };\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- },\n+ reproject: false,\n \n /**\n- * APIMethod: read\n- * Read data from a string, and return a list of features. \n+ * Constructor: OpenLayers.Layer.HTTPRequest\n * \n * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array()} An array of features.\n+ * name - {String}\n+ * url - {Array(String) or String}\n+ * params - {Object}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- var featureNodes = this.getElementsByTagNameNS(data.documentElement,\n- this.gmlns,\n- this.featureName);\n- var features = [];\n- for (var i = 0; i < featureNodes.length; i++) {\n- var feature = this.parseFeature(featureNodes[i]);\n- if (feature) {\n- features.push(feature);\n- }\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n+ this.url = url;\n+ if (!this.params) {\n+ this.params = OpenLayers.Util.extend({}, params);\n }\n- return features;\n },\n \n /**\n- * Method: parseFeature\n- * This function is the core of the GML parsing code in OpenLayers.\n- * It creates the geometries that are then attached to the returned\n- * feature, and calls parseAttributes() to get attribute data out.\n- * \n- * Parameters:\n- * node - {DOMElement} A GML feature node. \n+ * APIMethod: destroy\n */\n- parseFeature: function(node) {\n- // only accept one geometry per feature - look for highest \"order\"\n- var order = [\"MultiPolygon\", \"Polygon\",\n- \"MultiLineString\", \"LineString\",\n- \"MultiPoint\", \"Point\", \"Envelope\"\n- ];\n- // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope,\n- // this code creates a geometry derived from the Envelope. This is not correct.\n- var type, nodeList, geometry, parser;\n- for (var i = 0; i < order.length; ++i) {\n- type = order[i];\n- nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);\n- if (nodeList.length > 0) {\n- // only deal with first geometry of this type\n- parser = this.parseGeometry[type.toLowerCase()];\n- if (parser) {\n- geometry = parser.apply(this, [nodeList[0]]);\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.externalProjection,\n- this.internalProjection);\n- }\n- } else {\n- throw new TypeError(\"Unsupported geometry type: \" + type);\n- }\n- // stop looking for different geometry types\n- break;\n- }\n- }\n-\n- var bounds;\n- var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, \"Box\");\n- for (i = 0; i < boxNodes.length; ++i) {\n- var boxNode = boxNodes[i];\n- var box = this.parseGeometry[\"box\"].apply(this, [boxNode]);\n- var parentNode = boxNode.parentNode;\n- var parentName = parentNode.localName ||\n- parentNode.nodeName.split(\":\").pop();\n- if (parentName === \"boundedBy\") {\n- bounds = box;\n- } else {\n- geometry = box.toGeometry();\n- }\n- }\n-\n- // construct feature (optionally with attributes)\n- var attributes;\n- if (this.extractAttributes) {\n- attributes = this.parseAttributes(node);\n- }\n- var feature = new OpenLayers.Feature.Vector(geometry, attributes);\n- feature.bounds = bounds;\n-\n- feature.gml = {\n- featureType: node.firstChild.nodeName.split(\":\")[1],\n- featureNS: node.firstChild.namespaceURI,\n- featureNSPrefix: node.firstChild.prefix\n- };\n-\n- // assign fid - this can come from a \"fid\" or \"id\" attribute\n- var childNode = node.firstChild;\n- var fid;\n- while (childNode) {\n- if (childNode.nodeType == 1) {\n- fid = childNode.getAttribute(\"fid\") ||\n- childNode.getAttribute(\"id\");\n- if (fid) {\n- break;\n- }\n- }\n- childNode = childNode.nextSibling;\n- }\n- feature.fid = fid;\n- return feature;\n+ destroy: function() {\n+ this.url = null;\n+ this.params = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Property: parseGeometry\n- * Properties of this object are the functions that parse geometries based\n- * on their type.\n+ * APIMethod: clone\n+ * \n+ * Parameters:\n+ * obj - {Object}\n+ * \n+ * Returns:\n+ * {} An exact clone of this \n+ * \n */\n- parseGeometry: {\n-\n- /**\n- * Method: parseGeometry.point\n- * Given a GML node representing a point geometry, create an OpenLayers\n- * point geometry.\n- *\n- * Parameters:\n- * node - {DOMElement} A GML node.\n- *\n- * Returns:\n- * {} A point geometry.\n- */\n- point: function(node) {\n- /**\n- * Three coordinate variations to consider:\n- * 1) x y z\n- * 2) x, y, z\n- * 3) xy\n- */\n- var nodeList, coordString;\n- var coords = [];\n-\n- // look for \n- var nodeList = this.getElementsByTagNameNS(node, this.gmlns, \"pos\");\n- if (nodeList.length > 0) {\n- coordString = nodeList[0].firstChild.nodeValue;\n- coordString = coordString.replace(this.regExes.trimSpace, \"\");\n- coords = coordString.split(this.regExes.splitSpace);\n- }\n-\n- // look for \n- if (coords.length == 0) {\n- nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n- \"coordinates\");\n- if (nodeList.length > 0) {\n- coordString = nodeList[0].firstChild.nodeValue;\n- coordString = coordString.replace(this.regExes.removeSpace,\n- \"\");\n- coords = coordString.split(\",\");\n- }\n- }\n-\n- // look for \n- if (coords.length == 0) {\n- nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n- \"coord\");\n- if (nodeList.length > 0) {\n- var xList = this.getElementsByTagNameNS(nodeList[0],\n- this.gmlns, \"X\");\n- var yList = this.getElementsByTagNameNS(nodeList[0],\n- this.gmlns, \"Y\");\n- if (xList.length > 0 && yList.length > 0) {\n- coords = [xList[0].firstChild.nodeValue,\n- yList[0].firstChild.nodeValue\n- ];\n- }\n- }\n- }\n-\n- // preserve third dimension\n- if (coords.length == 2) {\n- coords[2] = null;\n- }\n-\n- if (this.xy) {\n- return new OpenLayers.Geometry.Point(coords[0], coords[1],\n- coords[2]);\n- } else {\n- return new OpenLayers.Geometry.Point(coords[1], coords[0],\n- coords[2]);\n- }\n- },\n-\n- /**\n- * Method: parseGeometry.multipoint\n- * Given a GML node representing a multipoint geometry, create an\n- * OpenLayers multipoint geometry.\n- *\n- * Parameters:\n- * node - {DOMElement} A GML node.\n- *\n- * Returns:\n- * {} A multipoint geometry.\n- */\n- multipoint: function(node) {\n- var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n- \"Point\");\n- var components = [];\n- if (nodeList.length > 0) {\n- var point;\n- for (var i = 0; i < nodeList.length; ++i) {\n- point = this.parseGeometry.point.apply(this, [nodeList[i]]);\n- if (point) {\n- components.push(point);\n- }\n- }\n- }\n- return new OpenLayers.Geometry.MultiPoint(components);\n- },\n-\n- /**\n- * Method: parseGeometry.linestring\n- * Given a GML node representing a linestring geometry, create an\n- * OpenLayers linestring geometry.\n- *\n- * Parameters:\n- * node - {DOMElement} A GML node.\n- *\n- * Returns:\n- * {} A linestring geometry.\n- */\n- linestring: function(node, ring) {\n- /**\n- * Two coordinate variations to consider:\n- * 1) x0 y0 z0 x1 y1 z1\n- * 2) x0, y0, z0 x1, y1, z1\n- */\n- var nodeList, coordString;\n- var coords = [];\n- var points = [];\n-\n- // look for \n- nodeList = this.getElementsByTagNameNS(node, this.gmlns, \"posList\");\n- if (nodeList.length > 0) {\n- coordString = this.getChildValue(nodeList[0]);\n- coordString = coordString.replace(this.regExes.trimSpace, \"\");\n- coords = coordString.split(this.regExes.splitSpace);\n- var dim = parseInt(nodeList[0].getAttribute(\"dimension\"));\n- var j, x, y, z;\n- for (var i = 0; i < coords.length / dim; ++i) {\n- j = i * dim;\n- x = coords[j];\n- y = coords[j + 1];\n- z = (dim == 2) ? null : coords[j + 2];\n- if (this.xy) {\n- points.push(new OpenLayers.Geometry.Point(x, y, z));\n- } else {\n- points.push(new OpenLayers.Geometry.Point(y, x, z));\n- }\n- }\n- }\n-\n- // look for \n- if (coords.length == 0) {\n- nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n- \"coordinates\");\n- if (nodeList.length > 0) {\n- coordString = this.getChildValue(nodeList[0]);\n- coordString = coordString.replace(this.regExes.trimSpace,\n- \"\");\n- coordString = coordString.replace(this.regExes.trimComma,\n- \",\");\n- var pointList = coordString.split(this.regExes.splitSpace);\n- for (var i = 0; i < pointList.length; ++i) {\n- coords = pointList[i].split(\",\");\n- if (coords.length == 2) {\n- coords[2] = null;\n- }\n- if (this.xy) {\n- points.push(new OpenLayers.Geometry.Point(coords[0],\n- coords[1],\n- coords[2]));\n- } else {\n- points.push(new OpenLayers.Geometry.Point(coords[1],\n- coords[0],\n- coords[2]));\n- }\n- }\n- }\n- }\n-\n- var line = null;\n- if (points.length != 0) {\n- if (ring) {\n- line = new OpenLayers.Geometry.LinearRing(points);\n- } else {\n- line = new OpenLayers.Geometry.LineString(points);\n- }\n- }\n- return line;\n- },\n-\n- /**\n- * Method: parseGeometry.multilinestring\n- * Given a GML node representing a multilinestring geometry, create an\n- * OpenLayers multilinestring geometry.\n- *\n- * Parameters:\n- * node - {DOMElement} A GML node.\n- *\n- * Returns:\n- * {} A multilinestring geometry.\n- */\n- multilinestring: function(node) {\n- var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n- \"LineString\");\n- var components = [];\n- if (nodeList.length > 0) {\n- var line;\n- for (var i = 0; i < nodeList.length; ++i) {\n- line = this.parseGeometry.linestring.apply(this,\n- [nodeList[i]]);\n- if (line) {\n- components.push(line);\n- }\n- }\n- }\n- return new OpenLayers.Geometry.MultiLineString(components);\n- },\n-\n- /**\n- * Method: parseGeometry.polygon\n- * Given a GML node representing a polygon geometry, create an\n- * OpenLayers polygon geometry.\n- *\n- * Parameters:\n- * node - {DOMElement} A GML node.\n- *\n- * Returns:\n- * {} A polygon geometry.\n- */\n- polygon: function(node) {\n- var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n- \"LinearRing\");\n- var components = [];\n- if (nodeList.length > 0) {\n- // this assumes exterior ring first, inner rings after\n- var ring;\n- for (var i = 0; i < nodeList.length; ++i) {\n- ring = this.parseGeometry.linestring.apply(this,\n- [nodeList[i], true]);\n- if (ring) {\n- components.push(ring);\n- }\n- }\n- }\n- return new OpenLayers.Geometry.Polygon(components);\n- },\n-\n- /**\n- * Method: parseGeometry.multipolygon\n- * Given a GML node representing a multipolygon geometry, create an\n- * OpenLayers multipolygon geometry.\n- *\n- * Parameters:\n- * node - {DOMElement} A GML node.\n- *\n- * Returns:\n- * {} A multipolygon geometry.\n- */\n- multipolygon: function(node) {\n- var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n- \"Polygon\");\n- var components = [];\n- if (nodeList.length > 0) {\n- var polygon;\n- for (var i = 0; i < nodeList.length; ++i) {\n- polygon = this.parseGeometry.polygon.apply(this,\n- [nodeList[i]]);\n- if (polygon) {\n- components.push(polygon);\n- }\n- }\n- }\n- return new OpenLayers.Geometry.MultiPolygon(components);\n- },\n-\n- envelope: function(node) {\n- var components = [];\n- var coordString;\n- var envelope;\n-\n- var lpoint = this.getElementsByTagNameNS(node, this.gmlns, \"lowerCorner\");\n- if (lpoint.length > 0) {\n- var coords = [];\n-\n- if (lpoint.length > 0) {\n- coordString = lpoint[0].firstChild.nodeValue;\n- coordString = coordString.replace(this.regExes.trimSpace, \"\");\n- coords = coordString.split(this.regExes.splitSpace);\n- }\n-\n- if (coords.length == 2) {\n- coords[2] = null;\n- }\n- if (this.xy) {\n- var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);\n- } else {\n- var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]);\n- }\n- }\n-\n- var upoint = this.getElementsByTagNameNS(node, this.gmlns, \"upperCorner\");\n- if (upoint.length > 0) {\n- var coords = [];\n-\n- if (upoint.length > 0) {\n- coordString = upoint[0].firstChild.nodeValue;\n- coordString = coordString.replace(this.regExes.trimSpace, \"\");\n- coords = coordString.split(this.regExes.splitSpace);\n- }\n-\n- if (coords.length == 2) {\n- coords[2] = null;\n- }\n- if (this.xy) {\n- var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);\n- } else {\n- var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]);\n- }\n- }\n+ clone: function(obj) {\n \n- if (lowerPoint && upperPoint) {\n- components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));\n- components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));\n- components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));\n- components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));\n- components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.HTTPRequest(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n+ }\n \n- var ring = new OpenLayers.Geometry.LinearRing(components);\n- envelope = new OpenLayers.Geometry.Polygon([ring]);\n- }\n- return envelope;\n- },\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n \n- /**\n- * Method: parseGeometry.box\n- * Given a GML node representing a box geometry, create an\n- * OpenLayers.Bounds.\n- *\n- * Parameters:\n- * node - {DOMElement} A GML node.\n- *\n- * Returns:\n- * {} A bounds representing the box.\n- */\n- box: function(node) {\n- var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n- \"coordinates\");\n- var coordString;\n- var coords, beginPoint = null,\n- endPoint = null;\n- if (nodeList.length > 0) {\n- coordString = nodeList[0].firstChild.nodeValue;\n- coords = coordString.split(\" \");\n- if (coords.length == 2) {\n- beginPoint = coords[0].split(\",\");\n- endPoint = coords[1].split(\",\");\n- }\n- }\n- if (beginPoint !== null && endPoint !== null) {\n- return new OpenLayers.Bounds(parseFloat(beginPoint[0]),\n- parseFloat(beginPoint[1]),\n- parseFloat(endPoint[0]),\n- parseFloat(endPoint[1]));\n- }\n- }\n+ // copy/set any non-init, non-simple values here\n \n+ return obj;\n },\n \n- /**\n- * Method: parseAttributes\n- *\n+ /** \n+ * APIMethod: setUrl\n+ * \n * Parameters:\n- * node - {DOMElement}\n- *\n- * Returns:\n- * {Object} An attributes object.\n+ * newUrl - {String}\n */\n- parseAttributes: function(node) {\n- var attributes = {};\n- // assume attributes are children of the first type 1 child\n- var childNode = node.firstChild;\n- var children, i, child, grandchildren, grandchild, name, value;\n- while (childNode) {\n- if (childNode.nodeType == 1) {\n- // attributes are type 1 children with one type 3 child\n- children = childNode.childNodes;\n- for (i = 0; i < children.length; ++i) {\n- child = children[i];\n- if (child.nodeType == 1) {\n- grandchildren = child.childNodes;\n- if (grandchildren.length == 1) {\n- grandchild = grandchildren[0];\n- if (grandchild.nodeType == 3 ||\n- grandchild.nodeType == 4) {\n- name = (child.prefix) ?\n- child.nodeName.split(\":\")[1] :\n- child.nodeName;\n- value = grandchild.nodeValue.replace(\n- this.regExes.trimSpace, \"\");\n- attributes[name] = value;\n- }\n- } else {\n- // If child has no childNodes (grandchildren),\n- // set an attribute with null value.\n- // e.g. becomes\n- // {fieldname: null}\n- attributes[child.nodeName.split(\":\").pop()] = null;\n- }\n- }\n- }\n- break;\n- }\n- childNode = childNode.nextSibling;\n- }\n- return attributes;\n+ setUrl: function(newUrl) {\n+ this.url = newUrl;\n },\n \n /**\n- * APIMethod: write\n- * Generate a GML document string given a list of features. \n+ * APIMethod: mergeNewParams\n * \n * Parameters:\n- * features - {Array()} List of features to\n- * serialize into a string.\n+ * newParams - {Object}\n *\n * Returns:\n- * {String} A string representing the GML document.\n+ * redrawn: {Boolean} whether the layer was actually redrawn.\n */\n- write: function(features) {\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n- }\n- var gml = this.createElementNS(\"http://www.opengis.net/wfs\",\n- \"wfs:\" + this.collectionName);\n- for (var i = 0; i < features.length; i++) {\n- gml.appendChild(this.createFeatureXML(features[i]));\n+ mergeNewParams: function(newParams) {\n+ this.params = OpenLayers.Util.extend(this.params, newParams);\n+ var ret = this.redraw();\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"params\"\n+ });\n }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);\n+ return ret;\n },\n \n- /** \n- * Method: createFeatureXML\n- * Accept an OpenLayers.Feature.Vector, and build a GML node for it.\n+ /**\n+ * APIMethod: redraw\n+ * Redraws the layer. Returns true if the layer was redrawn, false if not.\n *\n * Parameters:\n- * feature - {} The feature to be built as GML.\n+ * force - {Boolean} Force redraw by adding random parameter.\n *\n * Returns:\n- * {DOMElement} A node reprensting the feature in GML.\n+ * {Boolean} The layer was redrawn.\n */\n- createFeatureXML: function(feature) {\n- var geometry = feature.geometry;\n- var geometryNode = this.buildGeometryNode(geometry);\n- var geomContainer = this.createElementNS(this.featureNS,\n- this.featurePrefix + \":\" +\n- this.geometryName);\n- geomContainer.appendChild(geometryNode);\n- var featureNode = this.createElementNS(this.gmlns,\n- \"gml:\" + this.featureName);\n- var featureContainer = this.createElementNS(this.featureNS,\n- this.featurePrefix + \":\" +\n- this.layerName);\n- var fid = feature.fid || feature.id;\n- featureContainer.setAttribute(\"fid\", fid);\n- featureContainer.appendChild(geomContainer);\n- for (var attr in feature.attributes) {\n- var attrText = this.createTextNode(feature.attributes[attr]);\n- var nodename = attr.substring(attr.lastIndexOf(\":\") + 1);\n- var attrContainer = this.createElementNS(this.featureNS,\n- this.featurePrefix + \":\" +\n- nodename);\n- attrContainer.appendChild(attrText);\n- featureContainer.appendChild(attrContainer);\n+ redraw: function(force) {\n+ if (force) {\n+ return this.mergeNewParams({\n+ \"_olSalt\": Math.random()\n+ });\n+ } else {\n+ return OpenLayers.Layer.prototype.redraw.apply(this, []);\n }\n- featureNode.appendChild(featureContainer);\n- return featureNode;\n },\n \n /**\n- * APIMethod: buildGeometryNode\n+ * Method: selectUrl\n+ * selectUrl() implements the standard floating-point multiplicative\n+ * hash function described by Knuth, and hashes the contents of the \n+ * given param string into a float between 0 and 1. This float is then\n+ * scaled to the size of the provided urls array, and used to select\n+ * a URL.\n+ *\n+ * Parameters:\n+ * paramString - {String}\n+ * urls - {Array(String)}\n+ * \n+ * Returns:\n+ * {String} An entry from the urls array, deterministically selected based\n+ * on the paramString.\n */\n- buildGeometryNode: function(geometry) {\n- if (this.externalProjection && this.internalProjection) {\n- geometry = geometry.clone();\n- geometry.transform(this.internalProjection,\n- this.externalProjection);\n+ selectUrl: function(paramString, urls) {\n+ var product = 1;\n+ for (var i = 0, len = paramString.length; i < len; i++) {\n+ product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n+ product -= Math.floor(product);\n }\n- var className = geometry.CLASS_NAME;\n- var type = className.substring(className.lastIndexOf(\".\") + 1);\n- var builder = this.buildGeometry[type.toLowerCase()];\n- return builder.apply(this, [geometry]);\n+ return urls[Math.floor(product * urls.length)];\n },\n \n- /**\n- * Property: buildGeometry\n- * Object containing methods to do the actual geometry node building\n- * based on geometry type.\n+ /** \n+ * Method: getFullRequestString\n+ * Combine url with layer's params and these newParams. \n+ * \n+ * does checking on the serverPath variable, allowing for cases when it \n+ * is supplied with trailing ? or &, as well as cases where not. \n+ *\n+ * return in formatted string like this:\n+ * \"server?key1=value1&key2=value2&key3=value3\"\n+ * \n+ * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.\n+ *\n+ * Parameters:\n+ * newParams - {Object}\n+ * altUrl - {String} Use this as the url instead of the layer's url\n+ * \n+ * Returns: \n+ * {String}\n */\n- buildGeometry: {\n- // TBD retrieve the srs from layer\n- // srsName is non-standard, so not including it until it's right.\n- // gml.setAttribute(\"srsName\",\n- // \"http://www.opengis.net/gml/srs/epsg.xml#4326\");\n-\n- /**\n- * Method: buildGeometry.point\n- * Given an OpenLayers point geometry, create a GML point.\n- *\n- * Parameters:\n- * geometry - {} A point geometry.\n- *\n- * Returns:\n- * {DOMElement} A GML point node.\n- */\n- point: function(geometry) {\n- var gml = this.createElementNS(this.gmlns, \"gml:Point\");\n- gml.appendChild(this.buildCoordinatesNode(geometry));\n- return gml;\n- },\n-\n- /**\n- * Method: buildGeometry.multipoint\n- * Given an OpenLayers multipoint geometry, create a GML multipoint.\n- *\n- * Parameters:\n- * geometry - {} A multipoint geometry.\n- *\n- * Returns:\n- * {DOMElement} A GML multipoint node.\n- */\n- multipoint: function(geometry) {\n- var gml = this.createElementNS(this.gmlns, \"gml:MultiPoint\");\n- var points = geometry.components;\n- var pointMember, pointGeom;\n- for (var i = 0; i < points.length; i++) {\n- pointMember = this.createElementNS(this.gmlns,\n- \"gml:pointMember\");\n- pointGeom = this.buildGeometry.point.apply(this,\n- [points[i]]);\n- pointMember.appendChild(pointGeom);\n- gml.appendChild(pointMember);\n- }\n- return gml;\n- },\n-\n- /**\n- * Method: buildGeometry.linestring\n- * Given an OpenLayers linestring geometry, create a GML linestring.\n- *\n- * Parameters:\n- * geometry - {} A linestring geometry.\n- *\n- * Returns:\n- * {DOMElement} A GML linestring node.\n- */\n- linestring: function(geometry) {\n- var gml = this.createElementNS(this.gmlns, \"gml:LineString\");\n- gml.appendChild(this.buildCoordinatesNode(geometry));\n- return gml;\n- },\n-\n- /**\n- * Method: buildGeometry.multilinestring\n- * Given an OpenLayers multilinestring geometry, create a GML\n- * multilinestring.\n- *\n- * Parameters:\n- * geometry - {} A multilinestring\n- * geometry.\n- *\n- * Returns:\n- * {DOMElement} A GML multilinestring node.\n- */\n- multilinestring: function(geometry) {\n- var gml = this.createElementNS(this.gmlns, \"gml:MultiLineString\");\n- var lines = geometry.components;\n- var lineMember, lineGeom;\n- for (var i = 0; i < lines.length; ++i) {\n- lineMember = this.createElementNS(this.gmlns,\n- \"gml:lineStringMember\");\n- lineGeom = this.buildGeometry.linestring.apply(this,\n- [lines[i]]);\n- lineMember.appendChild(lineGeom);\n- gml.appendChild(lineMember);\n- }\n- return gml;\n- },\n-\n- /**\n- * Method: buildGeometry.linearring\n- * Given an OpenLayers linearring geometry, create a GML linearring.\n- *\n- * Parameters:\n- * geometry - {} A linearring geometry.\n- *\n- * Returns:\n- * {DOMElement} A GML linearring node.\n- */\n- linearring: function(geometry) {\n- var gml = this.createElementNS(this.gmlns, \"gml:LinearRing\");\n- gml.appendChild(this.buildCoordinatesNode(geometry));\n- return gml;\n- },\n-\n- /**\n- * Method: buildGeometry.polygon\n- * Given an OpenLayers polygon geometry, create a GML polygon.\n- *\n- * Parameters:\n- * geometry - {} A polygon geometry.\n- *\n- * Returns:\n- * {DOMElement} A GML polygon node.\n- */\n- polygon: function(geometry) {\n- var gml = this.createElementNS(this.gmlns, \"gml:Polygon\");\n- var rings = geometry.components;\n- var ringMember, ringGeom, type;\n- for (var i = 0; i < rings.length; ++i) {\n- type = (i == 0) ? \"outerBoundaryIs\" : \"innerBoundaryIs\";\n- ringMember = this.createElementNS(this.gmlns,\n- \"gml:\" + type);\n- ringGeom = this.buildGeometry.linearring.apply(this,\n- [rings[i]]);\n- ringMember.appendChild(ringGeom);\n- gml.appendChild(ringMember);\n- }\n- return gml;\n- },\n+ getFullRequestString: function(newParams, altUrl) {\n \n- /**\n- * Method: buildGeometry.multipolygon\n- * Given an OpenLayers multipolygon geometry, create a GML multipolygon.\n- *\n- * Parameters:\n- * geometry - {} A multipolygon\n- * geometry.\n- *\n- * Returns:\n- * {DOMElement} A GML multipolygon node.\n- */\n- multipolygon: function(geometry) {\n- var gml = this.createElementNS(this.gmlns, \"gml:MultiPolygon\");\n- var polys = geometry.components;\n- var polyMember, polyGeom;\n- for (var i = 0; i < polys.length; ++i) {\n- polyMember = this.createElementNS(this.gmlns,\n- \"gml:polygonMember\");\n- polyGeom = this.buildGeometry.polygon.apply(this,\n- [polys[i]]);\n- polyMember.appendChild(polyGeom);\n- gml.appendChild(polyMember);\n- }\n- return gml;\n+ // if not altUrl passed in, use layer's url\n+ var url = altUrl || this.url;\n \n- },\n+ // create a new params hashtable with all the layer params and the \n+ // new params together. then convert to string\n+ var allParams = OpenLayers.Util.extend({}, this.params);\n+ allParams = OpenLayers.Util.extend(allParams, newParams);\n+ var paramsString = OpenLayers.Util.getParameterString(allParams);\n \n- /**\n- * Method: buildGeometry.bounds\n- * Given an OpenLayers bounds, create a GML box.\n- *\n- * Parameters:\n- * bounds - {} A bounds object.\n- *\n- * Returns:\n- * {DOMElement} A GML box node.\n- */\n- bounds: function(bounds) {\n- var gml = this.createElementNS(this.gmlns, \"gml:Box\");\n- gml.appendChild(this.buildCoordinatesNode(bounds));\n- return gml;\n+ // if url is not a string, it should be an array of strings, \n+ // in which case we will deterministically select one of them in \n+ // order to evenly distribute requests to different urls.\n+ //\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(paramsString, url);\n }\n- },\n-\n- /**\n- * Method: buildCoordinates\n- * builds the coordinates XmlNode\n- * (code)\n- * ...\n- * (end)\n- *\n- * Parameters: \n- * geometry - {} \n- *\n- * Returns:\n- * {XmlNode} created xmlNode\n- */\n- buildCoordinatesNode: function(geometry) {\n- var coordinatesNode = this.createElementNS(this.gmlns,\n- \"gml:coordinates\");\n- coordinatesNode.setAttribute(\"decimal\", \".\");\n- coordinatesNode.setAttribute(\"cs\", \",\");\n- coordinatesNode.setAttribute(\"ts\", \" \");\n-\n- var parts = [];\n \n- if (geometry instanceof OpenLayers.Bounds) {\n- parts.push(geometry.left + \",\" + geometry.bottom);\n- parts.push(geometry.right + \",\" + geometry.top);\n- } else {\n- var points = (geometry.components) ? geometry.components : [geometry];\n- for (var i = 0; i < points.length; i++) {\n- parts.push(points[i].x + \",\" + points[i].y);\n+ // ignore parameters that are already in the url search string\n+ var urlParams =\n+ OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n+ for (var key in allParams) {\n+ if (key.toUpperCase() in urlParams) {\n+ delete allParams[key];\n }\n }\n+ paramsString = OpenLayers.Util.getParameterString(allParams);\n \n- var txtNode = this.createTextNode(parts.join(\" \"));\n- coordinatesNode.appendChild(txtNode);\n-\n- return coordinatesNode;\n+ return OpenLayers.Util.urlAppend(url, paramsString);\n },\n \n- CLASS_NAME: \"OpenLayers.Format.GML\"\n+ CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n });\n /* ======================================================================\n- OpenLayers/Format/GML/Base.js\n+ OpenLayers/Tile.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/GML.js\n- */\n \n /**\n- * Though required in the full build, if the GML format is excluded, we set\n- * the namespace here.\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n */\n-if (!OpenLayers.Format.GML) {\n- OpenLayers.Format.GML = {};\n-}\n \n /**\n- * Class: OpenLayers.Format.GML.Base\n- * Superclass for GML parsers.\n- *\n- * Inherits from:\n- * - \n+ * Class: OpenLayers.Tile \n+ * This is a class designed to designate a single tile, however\n+ * it is explicitly designed to do relatively little. Tiles store \n+ * information about themselves -- such as the URL that they are related\n+ * to, and their size - but do not add themselves to the layer div \n+ * automatically, for example. Create a new tile with the \n+ * constructor, or a subclass. \n+ * \n+ * TBD 3.0 - remove reference to url in above paragraph\n+ * \n */\n-OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {\n+OpenLayers.Tile = OpenLayers.Class({\n \n /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n+ * APIProperty: events\n+ * {} An events object that handles all \n+ * events on the tile.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * tile.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types:\n+ * beforedraw - Triggered before the tile is drawn. Used to defer\n+ * drawing to an animation queue. To defer drawing, listeners need\n+ * to return false, which will abort drawing. The queue handler needs\n+ * to call (true) to actually draw the tile.\n+ * loadstart - Triggered when tile loading starts.\n+ * loadend - Triggered when tile loading ends.\n+ * loaderror - Triggered before the loadend event (i.e. when the tile is\n+ * still hidden) if the tile could not be loaded.\n+ * reload - Triggered when an already loading tile is reloaded.\n+ * unload - Triggered before a tile is unloaded.\n */\n- namespaces: {\n- gml: \"http://www.opengis.net/gml\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n- wfs: \"http://www.opengis.net/wfs\" // this is a convenience for reading wfs:FeatureCollection\n- },\n+ events: null,\n \n /**\n- * Property: defaultPrefix\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+ * This options can be set in the ``tileOptions`` option from\n+ * . For example, to be notified of the\n+ * ``loadend`` event of each tiles:\n+ * (code)\n+ * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {\n+ * tileOptions: {\n+ * eventListeners: {\n+ * 'loadend': function(evt) {\n+ * // do something on loadend\n+ * }\n+ * }\n+ * }\n+ * });\n+ * (end)\n */\n- defaultPrefix: \"gml\",\n+ eventListeners: null,\n \n /**\n- * Property: schemaLocation\n- * {String} Schema location for a particular minor version.\n+ * Property: id \n+ * {String} null\n */\n- schemaLocation: null,\n+ id: null,\n \n- /**\n- * APIProperty: featureType\n- * {Array(String) or String} The local (without prefix) feature typeName(s).\n+ /** \n+ * Property: layer \n+ * {} layer the tile is attached to \n */\n- featureType: null,\n+ layer: null,\n \n /**\n- * APIProperty: featureNS\n- * {String} The feature namespace. Must be set in the options at\n- * construction.\n+ * Property: url\n+ * {String} url of the request.\n+ *\n+ * TBD 3.0 \n+ * Deprecated. The base tile class does not need an url. This should be \n+ * handled in subclasses. Does not belong here.\n */\n- featureNS: null,\n+ url: null,\n \n- /**\n- * APIProperty: geometry\n- * {String} Name of geometry element. Defaults to \"geometry\". If null, it\n- * will be set on when the first geometry is parsed.\n+ /** \n+ * APIProperty: bounds \n+ * {} null\n */\n- geometryName: \"geometry\",\n+ bounds: null,\n \n- /**\n- * APIProperty: extractAttributes\n- * {Boolean} Extract attributes from GML. Default is true.\n+ /** \n+ * Property: size \n+ * {} null\n */\n- extractAttributes: true,\n+ size: null,\n \n- /**\n- * APIProperty: srsName\n- * {String} URI for spatial reference system. This is optional for\n- * single part geometries and mandatory for collections and multis.\n- * If set, the srsName attribute will be written for all geometries.\n- * Default is null.\n+ /** \n+ * Property: position \n+ * {} Top Left pixel of the tile\n */\n- srsName: null,\n+ position: null,\n \n /**\n- * APIProperty: xy\n- * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)\n- * Changing is not recommended, a new Format should be instantiated.\n+ * Property: isLoading\n+ * {Boolean} Is the tile loading?\n */\n- xy: true,\n+ isLoading: false,\n \n- /**\n- * Property: geometryTypes\n- * {Object} Maps OpenLayers geometry class names to GML element names.\n- * Use before accessing this property.\n+ /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.\n+ * there is no need for the base tile class to have a url.\n */\n- geometryTypes: null,\n \n- /**\n- * Property: singleFeatureType\n- * {Boolean} True if there is only 1 featureType, and not an array\n- * of featuretypes.\n+ /** \n+ * Constructor: OpenLayers.Tile\n+ * Constructor for a new instance.\n+ * \n+ * Parameters:\n+ * layer - {} layer that the tile will go in.\n+ * position - {}\n+ * bounds - {}\n+ * url - {}\n+ * size - {}\n+ * options - {Object}\n */\n- singleFeatureType: null,\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ this.layer = layer;\n+ this.position = position.clone();\n+ this.setBounds(bounds);\n+ this.url = url;\n+ if (size) {\n+ this.size = size.clone();\n+ }\n \n- /**\n- * Property: autoConfig\n- * {Boolean} Indicates if the format was configured without a ,\n- * but auto-configured and during read.\n- * Subclasses making use of auto-configuration should make\n- * the first call to the method (usually in the read method)\n- * with true as 3rd argument, so the auto-configured featureType can be\n- * reset and the format can be reused for subsequent reads with data from\n- * different featureTypes. Set to false after read if you want to keep the\n- * auto-configured values.\n- */\n+ //give the tile a unique id based on its BBOX.\n+ this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\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- featureMember: (/^(.*:)?featureMembers?$/)\n+ OpenLayers.Util.extend(this, options);\n+\n+ this.events = new OpenLayers.Events(this);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners);\n+ }\n },\n \n /**\n- * Constructor: OpenLayers.Format.GML.Base\n- * Instances of this class are not created directly. Use the\n- * or constructor\n- * instead.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- *\n- * Valid options properties:\n- * featureType - {Array(String) or String} Local (without prefix) feature \n- * typeName(s) (required for write).\n- * featureNS - {String} Feature namespace (required for write).\n- * geometryName - {String} Geometry element name (required for write).\n+ * Method: unload\n+ * Call immediately before destroying if you are listening to tile\n+ * events, so that counters are properly handled if tile is still\n+ * loading at destroy-time. Will only fire an event if the tile is\n+ * still loading.\n */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- this.setGeometryTypes();\n- if (options && options.featureNS) {\n- this.setNamespace(\"feature\", options.featureNS);\n+ unload: function() {\n+ if (this.isLoading) {\n+ this.isLoading = false;\n+ this.events.triggerEvent(\"unload\");\n }\n- this.singleFeatureType = !options || (typeof options.featureType === \"string\");\n },\n \n- /**\n- * Method: read\n- *\n- * Parameters:\n- * data - {DOMElement} A gml:featureMember element, a gml:featureMembers\n- * element, or an element containing either of the above at any level.\n- *\n- * Returns:\n- * {Array()} An array of features.\n+ /** \n+ * APIMethod: destroy\n+ * Nullify references to prevent circular references and memory leaks.\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 features = [];\n- this.readNode(data, {\n- features: features\n- }, true);\n- if (features.length == 0) {\n- // look for gml:featureMember elements\n- var elements = this.getElementsByTagNameNS(\n- data, this.namespaces.gml, \"featureMember\"\n- );\n- if (elements.length) {\n- for (var i = 0, len = elements.length; i < len; ++i) {\n- this.readNode(elements[i], {\n- features: features\n- }, true);\n- }\n- } else {\n- // look for gml:featureMembers elements (this is v3, but does no harm here)\n- var elements = this.getElementsByTagNameNS(\n- data, this.namespaces.gml, \"featureMembers\"\n- );\n- if (elements.length) {\n- // there can be only one\n- this.readNode(elements[0], {\n- features: features\n- }, true);\n- }\n- }\n+ destroy: function() {\n+ this.layer = null;\n+ this.bounds = null;\n+ this.size = null;\n+ this.position = null;\n+\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n }\n- return features;\n+ this.events.destroy();\n+ this.eventListeners = null;\n+ this.events = null;\n },\n \n /**\n- * Method: readNode\n- * Shorthand for applying one of the named readers given the node\n- * namespace and local name. Readers take two args (node, obj) and\n- * generally extend or modify the second.\n+ * Method: draw\n+ * Clear whatever is currently in the tile, then return whether or not \n+ * it should actually be re-drawn. This is an example implementation\n+ * that can be overridden by subclasses. The minimum thing to do here\n+ * is to call and return the result from .\n *\n * Parameters:\n- * node - {DOMElement} The node to be read (required).\n- * obj - {Object} The object to be modified (optional).\n- * first - {Boolean} Should be set to true for the first node read. This\n- * is usually the readNode call in the read method. Without this being\n- * set, auto-configured properties will stick on subsequent reads.\n- *\n+ * force - {Boolean} If true, the tile will not be cleared and no beforedraw\n+ * event will be fired. This is used for drawing tiles asynchronously\n+ * after drawing has been cancelled by returning false from a beforedraw\n+ * listener.\n+ * \n * Returns:\n- * {Object} The input object, modified (or a new one if none was provided).\n+ * {Boolean} Whether or not the tile should actually be drawn. Returns null\n+ * if a beforedraw listener returned false.\n */\n- readNode: function(node, obj, first) {\n- // on subsequent calls of format.read(), we want to reset auto-\n- // configured properties and auto-configure again.\n- if (first === true && this.autoConfig === true) {\n- this.featureType = null;\n- delete this.namespaceAlias[this.featureNS];\n- delete this.namespaces[\"feature\"];\n- this.featureNS = null;\n+ draw: function(force) {\n+ if (!force) {\n+ //clear tile's contents and mark as not drawn\n+ this.clear();\n }\n- // featureType auto-configuration\n- if (!this.featureNS && (!(node.prefix in this.namespaces) &&\n- node.parentNode.namespaceURI == this.namespaces[\"gml\"] &&\n- this.regExes.featureMember.test(node.parentNode.nodeName))) {\n- this.featureType = node.nodeName.split(\":\").pop();\n- this.setNamespace(\"feature\", node.namespaceURI);\n- this.featureNS = node.namespaceURI;\n- this.autoConfig = true;\n+ var draw = this.shouldDraw();\n+ if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n+ draw = null;\n }\n- return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);\n+ return draw;\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+ * Method: shouldDraw\n+ * Return whether or not the tile should actually be (re-)drawn. The only\n+ * case where we *wouldn't* want to draw the tile is if the tile is outside\n+ * its layer's maxExtent\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the tile should actually be drawn.\n */\n- readers: {\n- \"gml\": {\n- \"_inherit\": function(node, obj, container) {\n- // To be implemented by version specific parsers\n- },\n- \"featureMember\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"featureMembers\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"name\": function(node, obj) {\n- obj.name = this.getChildValue(node);\n- },\n- \"boundedBy\": function(node, obj) {\n- var container = {};\n- this.readChildNodes(node, container);\n- if (container.components && container.components.length > 0) {\n- obj.bounds = container.components[0];\n- }\n- },\n- \"Point\": function(node, container) {\n- var obj = {\n- points: []\n- };\n- this.readChildNodes(node, obj);\n- if (!container.components) {\n- container.components = [];\n- }\n- container.components.push(obj.points[0]);\n- },\n- \"coordinates\": function(node, obj) {\n- var str = this.getChildValue(node).replace(\n- this.regExes.trimSpace, \"\"\n- );\n- str = str.replace(this.regExes.trimComma, \",\");\n- var pointList = str.split(this.regExes.splitSpace);\n- var coords;\n- var numPoints = pointList.length;\n- var points = new Array(numPoints);\n- for (var i = 0; i < numPoints; ++i) {\n- coords = pointList[i].split(\",\");\n- if (this.xy) {\n- points[i] = new OpenLayers.Geometry.Point(\n- coords[0], coords[1], coords[2]\n- );\n- } else {\n- points[i] = new OpenLayers.Geometry.Point(\n- coords[1], coords[0], coords[2]\n- );\n- }\n- }\n- obj.points = points;\n- },\n- \"coord\": function(node, obj) {\n- var coord = {};\n- this.readChildNodes(node, coord);\n- if (!obj.points) {\n- obj.points = [];\n- }\n- obj.points.push(new OpenLayers.Geometry.Point(\n- coord.x, coord.y, coord.z\n- ));\n- },\n- \"X\": function(node, coord) {\n- coord.x = this.getChildValue(node);\n- },\n- \"Y\": function(node, coord) {\n- coord.y = this.getChildValue(node);\n- },\n- \"Z\": function(node, coord) {\n- coord.z = this.getChildValue(node);\n- },\n- \"MultiPoint\": function(node, container) {\n- var obj = {\n- components: []\n- };\n- this.readers.gml._inherit.apply(this, [node, obj, container]);\n- this.readChildNodes(node, obj);\n- container.components = [\n- new OpenLayers.Geometry.MultiPoint(obj.components)\n- ];\n- },\n- \"pointMember\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"LineString\": function(node, container) {\n- var obj = {};\n- this.readers.gml._inherit.apply(this, [node, obj, container]);\n- this.readChildNodes(node, obj);\n- if (!container.components) {\n- container.components = [];\n- }\n- container.components.push(\n- new OpenLayers.Geometry.LineString(obj.points)\n- );\n- },\n- \"MultiLineString\": function(node, container) {\n- var obj = {\n- components: []\n- };\n- this.readers.gml._inherit.apply(this, [node, obj, container]);\n- this.readChildNodes(node, obj);\n- container.components = [\n- new OpenLayers.Geometry.MultiLineString(obj.components)\n- ];\n- },\n- \"lineStringMember\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"Polygon\": function(node, container) {\n- var obj = {\n- outer: null,\n- inner: []\n- };\n- this.readers.gml._inherit.apply(this, [node, obj, container]);\n- this.readChildNodes(node, obj);\n- obj.inner.unshift(obj.outer);\n- if (!container.components) {\n- container.components = [];\n- }\n- container.components.push(\n- new OpenLayers.Geometry.Polygon(obj.inner)\n- );\n- },\n- \"LinearRing\": function(node, obj) {\n- var container = {};\n- this.readers.gml._inherit.apply(this, [node, container]);\n- this.readChildNodes(node, container);\n- obj.components = [new OpenLayers.Geometry.LinearRing(\n- container.points\n- )];\n- },\n- \"MultiPolygon\": function(node, container) {\n- var obj = {\n- components: []\n- };\n- this.readers.gml._inherit.apply(this, [node, obj, container]);\n- this.readChildNodes(node, obj);\n- container.components = [\n- new OpenLayers.Geometry.MultiPolygon(obj.components)\n- ];\n- },\n- \"polygonMember\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"GeometryCollection\": function(node, container) {\n- var obj = {\n- components: []\n- };\n- this.readers.gml._inherit.apply(this, [node, obj, container]);\n- this.readChildNodes(node, obj);\n- container.components = [\n- new OpenLayers.Geometry.Collection(obj.components)\n- ];\n- },\n- \"geometryMember\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- }\n- },\n- \"feature\": {\n- \"*\": function(node, obj) {\n- // The node can either be named like the featureType, or it\n- // can be a child of the feature:featureType. Children can be\n- // geometry or attributes.\n- var name;\n- var local = node.localName || node.nodeName.split(\":\").pop();\n- // Since an attribute can have the same name as the feature type\n- // we only want to read the node as a feature if the parent\n- // node can have feature nodes as children. In this case, the\n- // obj.features property is set.\n- if (obj.features) {\n- if (!this.singleFeatureType &&\n- (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {\n- name = \"_typeName\";\n- } else if (local === this.featureType) {\n- name = \"_typeName\";\n- }\n- } else {\n- // Assume attribute elements have one child node and that the child\n- // is a text node. Otherwise assume it is a geometry node.\n- if (node.childNodes.length == 0 ||\n- (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {\n- if (this.extractAttributes) {\n- name = \"_attribute\";\n- }\n- } else {\n- name = \"_geometry\";\n- }\n- }\n- if (name) {\n- this.readers.feature[name].apply(this, [node, obj]);\n- }\n- },\n- \"_typeName\": function(node, obj) {\n- var container = {\n- components: [],\n- attributes: {}\n- };\n- this.readChildNodes(node, container);\n- // look for common gml namespaced elements\n- if (container.name) {\n- container.attributes.name = container.name;\n- }\n- var feature = new OpenLayers.Feature.Vector(\n- container.components[0], container.attributes\n- );\n- if (!this.singleFeatureType) {\n- feature.type = node.nodeName.split(\":\").pop();\n- feature.namespace = node.namespaceURI;\n- }\n- var fid = node.getAttribute(\"fid\") ||\n- this.getAttributeNS(node, this.namespaces[\"gml\"], \"id\");\n- if (fid) {\n- feature.fid = fid;\n- }\n- if (this.internalProjection && this.externalProjection &&\n- feature.geometry) {\n- feature.geometry.transform(\n- this.externalProjection, this.internalProjection\n- );\n- }\n- if (container.bounds) {\n- feature.bounds = container.bounds;\n- }\n- obj.features.push(feature);\n- },\n- \"_geometry\": function(node, obj) {\n- if (!this.geometryName) {\n- this.geometryName = node.nodeName.split(\":\").pop();\n- }\n- this.readChildNodes(node, obj);\n- },\n- \"_attribute\": function(node, obj) {\n- var local = node.localName || node.nodeName.split(\":\").pop();\n- var value = this.getChildValue(node);\n- obj.attributes[local] = value;\n- }\n- },\n- \"wfs\": {\n- \"FeatureCollection\": function(node, obj) {\n- this.readChildNodes(node, obj);\n+ shouldDraw: function() {\n+ var withinMaxExtent = false,\n+ maxExtent = this.layer.maxExtent;\n+ if (maxExtent) {\n+ var map = this.layer.map;\n+ var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n+ if (this.bounds.intersectsBounds(maxExtent, {\n+ inclusive: false,\n+ worldBounds: worldBounds\n+ })) {\n+ withinMaxExtent = true;\n }\n }\n+\n+ return withinMaxExtent || this.layer.displayOutsideMaxExtent;\n },\n \n /**\n- * Method: write\n+ * Method: setBounds\n+ * Sets the bounds on this instance\n *\n * Parameters:\n- * features - {Array() | OpenLayers.Feature.Vector}\n- * An array of features or a single feature.\n- *\n- * Returns:\n- * {String} Given an array of features, a doc with a gml:featureMembers\n- * element will be returned. Given a single feature, a doc with a\n- * gml:featureMember element will be returned.\n+ * bounds {}\n */\n- write: function(features) {\n- var name;\n- if (OpenLayers.Util.isArray(features)) {\n- name = \"featureMembers\";\n- } else {\n- name = \"featureMember\";\n+ setBounds: function(bounds) {\n+ bounds = bounds.clone();\n+ if (this.layer.map.baseLayer.wrapDateLine) {\n+ var worldExtent = this.layer.map.getMaxExtent(),\n+ tolerance = this.layer.map.getResolution();\n+ bounds = bounds.wrapDateLine(worldExtent, {\n+ leftTolerance: tolerance,\n+ rightTolerance: tolerance\n+ });\n }\n- var root = this.writeNode(\"gml:\" + name, features);\n- this.setAttributeNS(\n- root, this.namespaces[\"xsi\"],\n- \"xsi:schemaLocation\", this.schemaLocation\n- );\n-\n- return OpenLayers.Format.XML.prototype.write.apply(this, [root]);\n+ this.bounds = bounds;\n },\n \n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n+ /** \n+ * Method: moveTo\n+ * Reposition the tile.\n+ *\n+ * Parameters:\n+ * bounds - {}\n+ * position - {}\n+ * redraw - {Boolean} Call draw method on tile after moving.\n+ * Default is true\n */\n- writers: {\n- \"gml\": {\n- \"featureMember\": function(feature) {\n- var node = this.createElementNSPlus(\"gml:featureMember\");\n- this.writeNode(\"feature:_typeName\", feature, node);\n- return node;\n- },\n- \"MultiPoint\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:MultiPoint\");\n- var components = geometry.components || [geometry];\n- for (var i = 0, ii = components.length; i < ii; ++i) {\n- this.writeNode(\"pointMember\", components[i], node);\n- }\n- return node;\n- },\n- \"pointMember\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:pointMember\");\n- this.writeNode(\"Point\", geometry, node);\n- return node;\n- },\n- \"MultiLineString\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:MultiLineString\");\n- var components = geometry.components || [geometry];\n- for (var i = 0, ii = components.length; i < ii; ++i) {\n- this.writeNode(\"lineStringMember\", components[i], node);\n- }\n- return node;\n- },\n- \"lineStringMember\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:lineStringMember\");\n- this.writeNode(\"LineString\", geometry, node);\n- return node;\n- },\n- \"MultiPolygon\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:MultiPolygon\");\n- var components = geometry.components || [geometry];\n- for (var i = 0, ii = components.length; i < ii; ++i) {\n- this.writeNode(\n- \"polygonMember\", components[i], node\n- );\n- }\n- return node;\n- },\n- \"polygonMember\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:polygonMember\");\n- this.writeNode(\"Polygon\", geometry, node);\n- return node;\n- },\n- \"GeometryCollection\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:GeometryCollection\");\n- for (var i = 0, len = geometry.components.length; i < len; ++i) {\n- this.writeNode(\"geometryMember\", geometry.components[i], node);\n- }\n- return node;\n- },\n- \"geometryMember\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:geometryMember\");\n- var child = this.writeNode(\"feature:_geometry\", geometry);\n- node.appendChild(child.firstChild);\n- return node;\n- }\n- },\n- \"feature\": {\n- \"_typeName\": function(feature) {\n- var node = this.createElementNSPlus(\"feature:\" + this.featureType, {\n- attributes: {\n- fid: feature.fid\n- }\n- });\n- if (feature.geometry) {\n- this.writeNode(\"feature:_geometry\", feature.geometry, node);\n- }\n- for (var name in feature.attributes) {\n- var value = feature.attributes[name];\n- if (value != null) {\n- this.writeNode(\n- \"feature:_attribute\", {\n- name: name,\n- value: value\n- }, node\n- );\n- }\n- }\n- return node;\n- },\n- \"_geometry\": function(geometry) {\n- if (this.externalProjection && this.internalProjection) {\n- geometry = geometry.clone().transform(\n- this.internalProjection, this.externalProjection\n- );\n- }\n- var node = this.createElementNSPlus(\n- \"feature:\" + this.geometryName\n- );\n- var type = this.geometryTypes[geometry.CLASS_NAME];\n- var child = this.writeNode(\"gml:\" + type, geometry, node);\n- if (this.srsName) {\n- child.setAttribute(\"srsName\", this.srsName);\n- }\n- return node;\n- },\n- \"_attribute\": function(obj) {\n- return this.createElementNSPlus(\"feature:\" + obj.name, {\n- value: obj.value\n- });\n- }\n- },\n- \"wfs\": {\n- \"FeatureCollection\": function(features) {\n- /**\n- * This is only here because GML2 only describes abstract\n- * feature collections. Typically, you would not be using\n- * the GML format to write wfs elements. This just provides\n- * some way to write out lists of features. GML3 defines the\n- * featureMembers element, so that is used by default instead.\n- */\n- var node = this.createElementNSPlus(\"wfs:FeatureCollection\");\n- for (var i = 0, len = features.length; i < len; ++i) {\n- this.writeNode(\"gml:featureMember\", features[i], node);\n- }\n- return node;\n- }\n+ moveTo: function(bounds, position, redraw) {\n+ if (redraw == null) {\n+ redraw = true;\n+ }\n+\n+ this.setBounds(bounds);\n+ this.position = position.clone();\n+ if (redraw) {\n+ this.draw();\n }\n },\n \n- /**\n- * Method: setGeometryTypes\n- * Sets the mapping.\n+ /** \n+ * Method: clear\n+ * Clear the tile of any bounds/position-related data so that it can \n+ * be reused in a new location.\n */\n- setGeometryTypes: function() {\n- this.geometryTypes = {\n- \"OpenLayers.Geometry.Point\": \"Point\",\n- \"OpenLayers.Geometry.MultiPoint\": \"MultiPoint\",\n- \"OpenLayers.Geometry.LineString\": \"LineString\",\n- \"OpenLayers.Geometry.MultiLineString\": \"MultiLineString\",\n- \"OpenLayers.Geometry.Polygon\": \"Polygon\",\n- \"OpenLayers.Geometry.MultiPolygon\": \"MultiPolygon\",\n- \"OpenLayers.Geometry.Collection\": \"GeometryCollection\"\n- };\n+ clear: function(draw) {\n+ // to be extended by subclasses\n },\n \n- CLASS_NAME: \"OpenLayers.Format.GML.Base\"\n-\n+ CLASS_NAME: \"OpenLayers.Tile\"\n });\n /* ======================================================================\n- OpenLayers/Format/GML/v3.js\n+ OpenLayers/Tile/Image.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Format/GML/Base.js\n+ * @requires OpenLayers/Tile.js\n+ * @requires OpenLayers/Animation.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Format.GML.v3\n- * Parses GML version 3.\n+ * Class: OpenLayers.Tile.Image\n+ * Instances of OpenLayers.Tile.Image are used to manage the image tiles\n+ * used by various layers. Create a new image tile with the\n+ * constructor.\n *\n * Inherits from:\n- * - \n+ * - \n */\n-OpenLayers.Format.GML.v3 = OpenLayers.Class(OpenLayers.Format.GML.Base, {\n+OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n \n /**\n- * Property: schemaLocation\n- * {String} Schema location for a particular minor version. The writers\n- * conform with the Simple Features Profile for GML.\n+ * APIProperty: events\n+ * {} An events object that handles all \n+ * events on the tile.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * tile.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to the events):\n+ * beforeload - Triggered before an image is prepared for loading, when the\n+ * url for the image is known already. Listeners may call on\n+ * the tile instance. If they do so, that image will be used and no new\n+ * one will be created.\n */\n- schemaLocation: \"http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd\",\n \n- /**\n- * Property: curve\n- * {Boolean} Write gml:Curve instead of gml:LineString elements. This also\n- * affects the elements in multi-part geometries. Default is false.\n- * To write gml:Curve elements instead of gml:LineString, set curve\n- * to true in the options to the contstructor (cannot be changed after\n- * instantiation).\n+ /** \n+ * APIProperty: url\n+ * {String} The URL of the image being requested. No default. Filled in by\n+ * layer.getURL() function. May be modified by loadstart listeners.\n */\n- curve: false,\n+ url: null,\n+\n+ /** \n+ * Property: imgDiv\n+ * {HTMLImageElement} The image for this tile.\n+ */\n+ imgDiv: null,\n \n /**\n- * Property: multiCurve\n- * {Boolean} Write gml:MultiCurve instead of gml:MultiLineString. Since\n- * the latter is deprecated in GML 3, the default is true. To write\n- * gml:MultiLineString instead of gml:MultiCurve, set multiCurve to\n- * false in the options to the constructor (cannot be changed after\n- * instantiation).\n+ * Property: frame\n+ * {DOMElement} The image element is appended to the frame. Any gutter on\n+ * the image will be hidden behind the frame. If no gutter is set,\n+ * this will be null.\n */\n- multiCurve: true,\n+ frame: null,\n+\n+ /** \n+ * Property: imageReloadAttempts\n+ * {Integer} Attempts to load the image.\n+ */\n+ imageReloadAttempts: null,\n \n /**\n- * Property: surface\n- * {Boolean} Write gml:Surface instead of gml:Polygon elements. This also\n- * affects the elements in multi-part geometries. Default is false.\n- * To write gml:Surface elements instead of gml:Polygon, set surface\n- * to true in the options to the contstructor (cannot be changed after\n- * instantiation).\n+ * Property: layerAlphaHack\n+ * {Boolean} True if the png alpha hack needs to be applied on the layer's div.\n */\n- surface: false,\n+ layerAlphaHack: null,\n \n /**\n- * Property: multiSurface\n- * {Boolean} Write gml:multiSurface instead of gml:MultiPolygon. Since\n- * the latter is deprecated in GML 3, the default is true. To write\n- * gml:MultiPolygon instead of gml:multiSurface, set multiSurface to\n- * false in the options to the constructor (cannot be changed after\n- * instantiation).\n+ * Property: asyncRequestId\n+ * {Integer} ID of an request to see if request is still valid. This is a\n+ * number which increments by 1 for each asynchronous request.\n */\n- multiSurface: true,\n+ asyncRequestId: null,\n \n /**\n- * Constructor: OpenLayers.Format.GML.v3\n- * Create a parser for GML v3.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n+ * APIProperty: maxGetUrlLength\n+ * {Number} If set, requests that would result in GET urls with more\n+ * characters than the number provided will be made using form-encoded\n+ * HTTP POST. It is good practice to avoid urls that are longer than 2048\n+ * characters.\n *\n- * Valid options properties:\n- * featureType - {String} Local (without prefix) feature typeName (required).\n- * featureNS - {String} Feature namespace (required).\n- * geometryName - {String} Geometry element name.\n+ * Caution:\n+ * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most\n+ * Opera versions do not fully support this option. On all browsers,\n+ * transition effects are not supported if POST requests are used.\n */\n- initialize: function(options) {\n- OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);\n- },\n+ maxGetUrlLength: null,\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+ * Property: canvasContext\n+ * {CanvasRenderingContext2D} A canvas context associated with\n+ * the tile image.\n */\n- readers: {\n- \"gml\": OpenLayers.Util.applyDefaults({\n- \"_inherit\": function(node, obj, container) {\n- // SRSReferenceGroup attributes\n- var dim = parseInt(node.getAttribute(\"srsDimension\"), 10) ||\n- (container && container.srsDimension);\n- if (dim) {\n- obj.srsDimension = dim;\n- }\n- },\n- \"featureMembers\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"Curve\": function(node, container) {\n- var obj = {\n- points: []\n- };\n- this.readers.gml._inherit.apply(this, [node, obj, container]);\n- this.readChildNodes(node, obj);\n- if (!container.components) {\n- container.components = [];\n- }\n- container.components.push(\n- new OpenLayers.Geometry.LineString(obj.points)\n- );\n- },\n- \"segments\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"LineStringSegment\": function(node, container) {\n- var obj = {};\n- this.readChildNodes(node, obj);\n- if (obj.points) {\n- Array.prototype.push.apply(container.points, obj.points);\n- }\n- },\n- \"pos\": function(node, obj) {\n- var str = this.getChildValue(node).replace(\n- this.regExes.trimSpace, \"\"\n- );\n- var coords = str.split(this.regExes.splitSpace);\n- var point;\n- if (this.xy) {\n- point = new OpenLayers.Geometry.Point(\n- coords[0], coords[1], coords[2]\n- );\n- } else {\n- point = new OpenLayers.Geometry.Point(\n- coords[1], coords[0], coords[2]\n- );\n- }\n- obj.points = [point];\n- },\n- \"posList\": function(node, obj) {\n- var str = this.getChildValue(node).replace(\n- this.regExes.trimSpace, \"\"\n- );\n- var coords = str.split(this.regExes.splitSpace);\n- // The \"dimension\" attribute is from the GML 3.0.1 spec.\n- var dim = obj.srsDimension ||\n- parseInt(node.getAttribute(\"srsDimension\") || node.getAttribute(\"dimension\"), 10) || 2;\n- var j, x, y, z;\n- var numPoints = coords.length / dim;\n- var points = new Array(numPoints);\n- for (var i = 0, len = coords.length; i < len; i += dim) {\n- x = coords[i];\n- y = coords[i + 1];\n- z = (dim == 2) ? undefined : coords[i + 2];\n- if (this.xy) {\n- points[i / dim] = new OpenLayers.Geometry.Point(x, y, z);\n- } else {\n- points[i / dim] = new OpenLayers.Geometry.Point(y, x, z);\n- }\n- }\n- obj.points = points;\n- },\n- \"Surface\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"patches\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"PolygonPatch\": function(node, obj) {\n- this.readers.gml.Polygon.apply(this, [node, obj]);\n- },\n- \"exterior\": function(node, container) {\n- var obj = {};\n- this.readChildNodes(node, obj);\n- container.outer = obj.components[0];\n- },\n- \"interior\": function(node, container) {\n- var obj = {};\n- this.readChildNodes(node, obj);\n- container.inner.push(obj.components[0]);\n- },\n- \"MultiCurve\": function(node, container) {\n- var obj = {\n- components: []\n- };\n- this.readers.gml._inherit.apply(this, [node, obj, container]);\n- this.readChildNodes(node, obj);\n- if (obj.components.length > 0) {\n- container.components = [\n- new OpenLayers.Geometry.MultiLineString(obj.components)\n- ];\n- }\n- },\n- \"curveMember\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"MultiSurface\": function(node, container) {\n- var obj = {\n- components: []\n- };\n- this.readers.gml._inherit.apply(this, [node, obj, container]);\n- this.readChildNodes(node, obj);\n- if (obj.components.length > 0) {\n- container.components = [\n- new OpenLayers.Geometry.MultiPolygon(obj.components)\n- ];\n- }\n- },\n- \"surfaceMember\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"surfaceMembers\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"pointMembers\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"lineStringMembers\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"polygonMembers\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"geometryMembers\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"Envelope\": function(node, container) {\n- var obj = {\n- points: new Array(2)\n- };\n- this.readChildNodes(node, obj);\n- if (!container.components) {\n- container.components = [];\n- }\n- var min = obj.points[0];\n- var max = obj.points[1];\n- container.components.push(\n- new OpenLayers.Bounds(min.x, min.y, max.x, max.y)\n- );\n- },\n- \"lowerCorner\": function(node, container) {\n- var obj = {};\n- this.readers.gml.pos.apply(this, [node, obj]);\n- container.points[0] = obj.points[0];\n- },\n- \"upperCorner\": function(node, container) {\n- var obj = {};\n- this.readers.gml.pos.apply(this, [node, obj]);\n- container.points[1] = obj.points[0];\n- }\n- }, OpenLayers.Format.GML.Base.prototype.readers[\"gml\"]),\n- \"feature\": OpenLayers.Format.GML.Base.prototype.readers[\"feature\"],\n- \"wfs\": OpenLayers.Format.GML.Base.prototype.readers[\"wfs\"]\n- },\n+ canvasContext: null,\n \n /**\n- * Method: write\n- *\n+ * APIProperty: crossOriginKeyword\n+ * The value of the crossorigin keyword to use when loading images. This is\n+ * only relevant when using for tiles from remote\n+ * origins and should be set to either 'anonymous' or 'use-credentials'\n+ * for servers that send Access-Control-Allow-Origin headers with their\n+ * tiles.\n+ */\n+ crossOriginKeyword: null,\n+\n+ /** TBD 3.0 - reorder the parameters to the init function to remove \n+ * URL. the getUrl() function on the layer gets called on \n+ * each draw(), so no need to specify it here.\n+ */\n+\n+ /** \n+ * Constructor: OpenLayers.Tile.Image\n+ * Constructor for a new instance.\n+ * \n * Parameters:\n- * features - {Array() | OpenLayers.Feature.Vector}\n- * An array of features or a single feature.\n- *\n- * Returns:\n- * {String} Given an array of features, a doc with a gml:featureMembers\n- * element will be returned. Given a single feature, a doc with a\n- * gml:featureMember element will be returned.\n+ * layer - {} layer that the tile will go in.\n+ * position - {}\n+ * bounds - {}\n+ * url - {} Deprecated. Remove me in 3.0.\n+ * size - {}\n+ * options - {Object}\n */\n- write: function(features) {\n- var name;\n- if (OpenLayers.Util.isArray(features)) {\n- name = \"featureMembers\";\n- } else {\n- name = \"featureMember\";\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ OpenLayers.Tile.prototype.initialize.apply(this, arguments);\n+\n+ this.url = url; //deprecated remove me\n+\n+ this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\n+\n+ if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n+ // only create frame if it's needed\n+ this.frame = document.createElement(\"div\");\n+ this.frame.style.position = \"absolute\";\n+ this.frame.style.overflow = \"hidden\";\n }\n- var root = this.writeNode(\"gml:\" + name, features);\n- this.setAttributeNS(\n- root, this.namespaces[\"xsi\"],\n- \"xsi:schemaLocation\", this.schemaLocation\n- );\n+ if (this.maxGetUrlLength != null) {\n+ OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);\n+ }\n+ },\n \n- return OpenLayers.Format.XML.prototype.write.apply(this, [root]);\n+ /** \n+ * APIMethod: destroy\n+ * nullify references to prevent circular references and memory leaks\n+ */\n+ destroy: function() {\n+ if (this.imgDiv) {\n+ this.clear();\n+ this.imgDiv = null;\n+ this.frame = null;\n+ }\n+ // don't handle async requests any more\n+ this.asyncRequestId = null;\n+ OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n+ * Method: draw\n+ * Check that a tile should be drawn, and draw it.\n+ * \n+ * Returns:\n+ * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned\n+ * false.\n */\n- writers: {\n- \"gml\": OpenLayers.Util.applyDefaults({\n- \"featureMembers\": function(features) {\n- var node = this.createElementNSPlus(\"gml:featureMembers\");\n- for (var i = 0, len = features.length; i < len; ++i) {\n- this.writeNode(\"feature:_typeName\", features[i], node);\n- }\n- return node;\n- },\n- \"Point\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:Point\");\n- this.writeNode(\"pos\", geometry, node);\n- return node;\n- },\n- \"pos\": function(point) {\n- // only 2d for simple features profile\n- var pos = (this.xy) ?\n- (point.x + \" \" + point.y) : (point.y + \" \" + point.x);\n- return this.createElementNSPlus(\"gml:pos\", {\n- value: pos\n- });\n- },\n- \"LineString\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:LineString\");\n- this.writeNode(\"posList\", geometry.components, node);\n- return node;\n- },\n- \"Curve\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:Curve\");\n- this.writeNode(\"segments\", geometry, node);\n- return node;\n- },\n- \"segments\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:segments\");\n- this.writeNode(\"LineStringSegment\", geometry, node);\n- return node;\n- },\n- \"LineStringSegment\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:LineStringSegment\");\n- this.writeNode(\"posList\", geometry.components, node);\n- return node;\n- },\n- \"posList\": function(points) {\n- // only 2d for simple features profile\n- var len = points.length;\n- var parts = new Array(len);\n- var point;\n- for (var i = 0; i < len; ++i) {\n- point = points[i];\n- if (this.xy) {\n- parts[i] = point.x + \" \" + point.y;\n- } else {\n- parts[i] = point.y + \" \" + point.x;\n- }\n- }\n- return this.createElementNSPlus(\"gml:posList\", {\n- value: parts.join(\" \")\n- });\n- },\n- \"Surface\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:Surface\");\n- this.writeNode(\"patches\", geometry, node);\n- return node;\n- },\n- \"patches\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:patches\");\n- this.writeNode(\"PolygonPatch\", geometry, node);\n- return node;\n- },\n- \"PolygonPatch\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:PolygonPatch\", {\n- attributes: {\n- interpolation: \"planar\"\n- }\n- });\n- this.writeNode(\"exterior\", geometry.components[0], node);\n- for (var i = 1, len = geometry.components.length; i < len; ++i) {\n- this.writeNode(\n- \"interior\", geometry.components[i], node\n- );\n- }\n- return node;\n- },\n- \"Polygon\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:Polygon\");\n- this.writeNode(\"exterior\", geometry.components[0], node);\n- for (var i = 1, len = geometry.components.length; i < len; ++i) {\n- this.writeNode(\n- \"interior\", geometry.components[i], node\n- );\n- }\n- return node;\n- },\n- \"exterior\": function(ring) {\n- var node = this.createElementNSPlus(\"gml:exterior\");\n- this.writeNode(\"LinearRing\", ring, node);\n- return node;\n- },\n- \"interior\": function(ring) {\n- var node = this.createElementNSPlus(\"gml:interior\");\n- this.writeNode(\"LinearRing\", ring, node);\n- return node;\n- },\n- \"LinearRing\": function(ring) {\n- var node = this.createElementNSPlus(\"gml:LinearRing\");\n- this.writeNode(\"posList\", ring.components, node);\n- return node;\n- },\n- \"MultiCurve\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:MultiCurve\");\n- var components = geometry.components || [geometry];\n- for (var i = 0, len = components.length; i < len; ++i) {\n- this.writeNode(\"curveMember\", components[i], node);\n- }\n- return node;\n- },\n- \"curveMember\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:curveMember\");\n- if (this.curve) {\n- this.writeNode(\"Curve\", geometry, node);\n- } else {\n- this.writeNode(\"LineString\", geometry, node);\n- }\n- return node;\n- },\n- \"MultiSurface\": function(geometry) {\n- var node = this.createElementNSPlus(\"gml:MultiSurface\");\n- var components = geometry.components || [geometry];\n- for (var i = 0, len = components.length; i < len; ++i) {\n- this.writeNode(\"surfaceMember\", components[i], node);\n- }\n- return node;\n- },\n- \"surfaceMember\": function(polygon) {\n- var node = this.createElementNSPlus(\"gml:surfaceMember\");\n- if (this.surface) {\n- this.writeNode(\"Surface\", polygon, node);\n- } else {\n- this.writeNode(\"Polygon\", polygon, node);\n- }\n- return node;\n- },\n- \"Envelope\": function(bounds) {\n- var node = this.createElementNSPlus(\"gml:Envelope\");\n- this.writeNode(\"lowerCorner\", bounds, node);\n- this.writeNode(\"upperCorner\", bounds, node);\n- // srsName attribute is required for gml:Envelope\n- if (this.srsName) {\n- node.setAttribute(\"srsName\", this.srsName);\n- }\n- return node;\n- },\n- \"lowerCorner\": function(bounds) {\n- // only 2d for simple features profile\n- var pos = (this.xy) ?\n- (bounds.left + \" \" + bounds.bottom) :\n- (bounds.bottom + \" \" + bounds.left);\n- return this.createElementNSPlus(\"gml:lowerCorner\", {\n- value: pos\n- });\n- },\n- \"upperCorner\": function(bounds) {\n- // only 2d for simple features profile\n- var pos = (this.xy) ?\n- (bounds.right + \" \" + bounds.top) :\n- (bounds.top + \" \" + bounds.right);\n- return this.createElementNSPlus(\"gml:upperCorner\", {\n- value: pos\n- });\n+ draw: function() {\n+ var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n+ if (shouldDraw) {\n+ // The layer's reproject option is deprecated.\n+ if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n+ // getBoundsFromBaseLayer is defined in deprecated.js.\n+ this.bounds = this.getBoundsFromBaseLayer(this.position);\n }\n- }, OpenLayers.Format.GML.Base.prototype.writers[\"gml\"]),\n- \"feature\": OpenLayers.Format.GML.Base.prototype.writers[\"feature\"],\n- \"wfs\": OpenLayers.Format.GML.Base.prototype.writers[\"wfs\"]\n+ if (this.isLoading) {\n+ //if we're already loading, send 'reload' instead of 'loadstart'.\n+ this._loadEvent = \"reload\";\n+ } else {\n+ this.isLoading = true;\n+ this._loadEvent = \"loadstart\";\n+ }\n+ this.renderTile();\n+ this.positionTile();\n+ } else if (shouldDraw === false) {\n+ this.unload();\n+ }\n+ return shouldDraw;\n },\n \n /**\n- * Method: setGeometryTypes\n- * Sets the mapping.\n+ * Method: renderTile\n+ * Internal function to actually initialize the image tile,\n+ * position it correctly, and set its url.\n */\n- setGeometryTypes: function() {\n- this.geometryTypes = {\n- \"OpenLayers.Geometry.Point\": \"Point\",\n- \"OpenLayers.Geometry.MultiPoint\": \"MultiPoint\",\n- \"OpenLayers.Geometry.LineString\": (this.curve === true) ? \"Curve\" : \"LineString\",\n- \"OpenLayers.Geometry.MultiLineString\": (this.multiCurve === false) ? \"MultiLineString\" : \"MultiCurve\",\n- \"OpenLayers.Geometry.Polygon\": (this.surface === true) ? \"Surface\" : \"Polygon\",\n- \"OpenLayers.Geometry.MultiPolygon\": (this.multiSurface === false) ? \"MultiPolygon\" : \"MultiSurface\",\n- \"OpenLayers.Geometry.Collection\": \"GeometryCollection\"\n- };\n+ renderTile: function() {\n+ if (this.layer.async) {\n+ // Asynchronous image requests call the asynchronous getURL method\n+ // on the layer to fetch an image that covers 'this.bounds'.\n+ var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n+ this.layer.getURLasync(this.bounds, function(url) {\n+ if (id == this.asyncRequestId) {\n+ this.url = url;\n+ this.initImage();\n+ }\n+ }, this);\n+ } else {\n+ // synchronous image requests get the url immediately.\n+ this.url = this.layer.getURL(this.bounds);\n+ this.initImage();\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Format.GML.v3\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/Filter/v1_1_0.js\n- ====================================================================== */\n+ /**\n+ * Method: positionTile\n+ * Using the properties currenty set on the layer, position the tile correctly.\n+ * This method is used both by the async and non-async versions of the Tile.Image\n+ * code.\n+ */\n+ positionTile: function() {\n+ var style = this.getTile().style,\n+ size = this.frame ? this.size :\n+ this.layer.getImageSize(this.bounds),\n+ ratio = 1;\n+ if (this.layer instanceof OpenLayers.Layer.Grid) {\n+ ratio = this.layer.getServerResolution() / this.layer.map.getResolution();\n+ }\n+ style.left = this.position.x + \"px\";\n+ style.top = this.position.y + \"px\";\n+ style.width = Math.round(ratio * size.w) + \"px\";\n+ style.height = Math.round(ratio * size.h) + \"px\";\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: clear\n+ * Remove the tile from the DOM, clear it of any image related data so that\n+ * it can be reused in a new location.\n+ */\n+ clear: function() {\n+ OpenLayers.Tile.prototype.clear.apply(this, arguments);\n+ var img = this.imgDiv;\n+ if (img) {\n+ var tile = this.getTile();\n+ if (tile.parentNode === this.layer.div) {\n+ this.layer.div.removeChild(tile);\n+ }\n+ this.setImgSrc();\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter = \"\";\n+ }\n+ OpenLayers.Element.removeClass(img, \"olImageLoadError\");\n+ }\n+ this.canvasContext = null;\n+ },\n \n-/**\n- * @requires OpenLayers/Format/Filter/v1.js\n- * @requires OpenLayers/Format/GML/v3.js\n- */\n+ /**\n+ * Method: getImage\n+ * Returns or creates and returns the tile image.\n+ */\n+ getImage: function() {\n+ if (!this.imgDiv) {\n+ this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n \n-/**\n- * Class: OpenLayers.Format.Filter.v1_1_0\n- * Write ogc:Filter version 1.1.0.\n- *\n- * Differences from the v1.0.0 parser:\n- * - uses GML v3 instead of GML v2\n- * - reads matchCase attribute on ogc:PropertyIsEqual and\n- * ogc:PropertyIsNotEqual elements.\n- * - writes matchCase attribute from comparison filters of type EQUAL_TO,\n- * NOT_EQUAL_TO and LIKE.\n- * \n- * Inherits from: \n- * - \n- * - \n- */\n-OpenLayers.Format.Filter.v1_1_0 = OpenLayers.Class(\n- OpenLayers.Format.GML.v3, OpenLayers.Format.Filter.v1, {\n+ var style = this.imgDiv.style;\n+ if (this.frame) {\n+ var left = 0,\n+ top = 0;\n+ if (this.layer.gutter) {\n+ left = this.layer.gutter / this.layer.tileSize.w * 100;\n+ top = this.layer.gutter / this.layer.tileSize.h * 100;\n+ }\n+ style.left = -left + \"%\";\n+ style.top = -top + \"%\";\n+ style.width = (2 * left + 100) + \"%\";\n+ style.height = (2 * top + 100) + \"%\";\n+ }\n+ style.visibility = \"hidden\";\n+ style.opacity = 0;\n+ if (this.layer.opacity < 1) {\n+ style.filter = 'alpha(opacity=' +\n+ (this.layer.opacity * 100) +\n+ ')';\n+ }\n+ style.position = \"absolute\";\n+ if (this.layerAlphaHack) {\n+ // move the image out of sight\n+ style.paddingTop = style.height;\n+ style.height = \"0\";\n+ style.width = \"100%\";\n+ }\n+ if (this.frame) {\n+ this.frame.appendChild(this.imgDiv);\n+ }\n+ }\n \n- /**\n- * Constant: VERSION\n- * {String} 1.1.0\n- */\n- VERSION: \"1.1.0\",\n+ return this.imgDiv;\n+ },\n \n- /**\n- * Property: schemaLocation\n- * {String} http://www.opengis.net/ogc/filter/1.1.0/filter.xsd\n- */\n- schemaLocation: \"http://www.opengis.net/ogc/filter/1.1.0/filter.xsd\",\n+ /**\n+ * APIMethod: setImage\n+ * Sets the image element for this tile. This method should only be called\n+ * from beforeload listeners.\n+ *\n+ * Parameters\n+ * img - {HTMLImageElement} The image to use for this tile.\n+ */\n+ setImage: function(img) {\n+ this.imgDiv = img;\n+ },\n \n- /**\n- * Constructor: OpenLayers.Format.Filter.v1_1_0\n- * Instances of this class are not created directly. Use the\n- * constructor instead.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Format.GML.v3.prototype.initialize.apply(\n- this, [options]\n+ /**\n+ * Method: initImage\n+ * Creates the content for the frame on the tile.\n+ */\n+ initImage: function() {\n+ if (!this.url && !this.imgDiv) {\n+ // fast path out - if there is no tile url and no previous image\n+ this.isLoading = false;\n+ return;\n+ }\n+ this.events.triggerEvent('beforeload');\n+ this.layer.div.appendChild(this.getTile());\n+ this.events.triggerEvent(this._loadEvent);\n+ var img = this.getImage();\n+ var src = img.getAttribute('src') || '';\n+ if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n+ this._loadTimeout = window.setTimeout(\n+ OpenLayers.Function.bind(this.onImageLoad, this), 0\n );\n- },\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- \"ogc\": OpenLayers.Util.applyDefaults({\n- \"PropertyIsEqualTo\": function(node, obj) {\n- var matchCase = node.getAttribute(\"matchCase\");\n- var filter = new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.EQUAL_TO,\n- matchCase: !(matchCase === \"false\" || matchCase === \"0\")\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"PropertyIsNotEqualTo\": function(node, obj) {\n- var matchCase = node.getAttribute(\"matchCase\");\n- var filter = new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,\n- matchCase: !(matchCase === \"false\" || matchCase === \"0\")\n- });\n- this.readChildNodes(node, filter);\n- obj.filters.push(filter);\n- },\n- \"PropertyIsLike\": function(node, obj) {\n- var filter = new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.LIKE\n- });\n- this.readChildNodes(node, filter);\n- var wildCard = node.getAttribute(\"wildCard\");\n- var singleChar = node.getAttribute(\"singleChar\");\n- var esc = node.getAttribute(\"escapeChar\");\n- filter.value2regex(wildCard, singleChar, esc);\n- obj.filters.push(filter);\n- }\n- }, OpenLayers.Format.Filter.v1.prototype.readers[\"ogc\"]),\n- \"gml\": OpenLayers.Format.GML.v3.prototype.readers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v3.prototype.readers[\"feature\"]\n- },\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"ogc\": OpenLayers.Util.applyDefaults({\n- \"PropertyIsEqualTo\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:PropertyIsEqualTo\", {\n- attributes: {\n- matchCase: filter.matchCase\n- }\n- });\n- // no ogc:expression handling for PropertyName for now\n- this.writeNode(\"PropertyName\", filter, node);\n- // handle Literals or Functions for now\n- this.writeOgcExpression(filter.value, node);\n- return node;\n- },\n- \"PropertyIsNotEqualTo\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:PropertyIsNotEqualTo\", {\n- attributes: {\n- matchCase: filter.matchCase\n- }\n- });\n- // no ogc:expression handling for PropertyName for now\n- this.writeNode(\"PropertyName\", filter, node);\n- // handle Literals or Functions for now\n- this.writeOgcExpression(filter.value, node);\n- return node;\n- },\n- \"PropertyIsLike\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:PropertyIsLike\", {\n- attributes: {\n- matchCase: filter.matchCase,\n- wildCard: \"*\",\n- singleChar: \".\",\n- escapeChar: \"!\"\n- }\n- });\n- // no ogc:expression handling for now\n- this.writeNode(\"PropertyName\", filter, node);\n- // convert regex string to ogc string\n- this.writeNode(\"Literal\", filter.regex2value(), node);\n- return node;\n- },\n- \"BBOX\": function(filter) {\n- var node = this.createElementNSPlus(\"ogc:BBOX\");\n- // PropertyName is optional in 1.1.0\n- filter.property && this.writeNode(\"PropertyName\", filter, node);\n- var box = this.writeNode(\"gml:Envelope\", filter.value);\n- if (filter.projection) {\n- box.setAttribute(\"srsName\", filter.projection);\n- }\n- node.appendChild(box);\n- return node;\n- },\n- \"SortBy\": function(sortProperties) {\n- var node = this.createElementNSPlus(\"ogc:SortBy\");\n- for (var i = 0, l = sortProperties.length; i < l; i++) {\n- this.writeNode(\n- \"ogc:SortProperty\",\n- sortProperties[i],\n- node\n- );\n- }\n- return node;\n- },\n- \"SortProperty\": function(sortProperty) {\n- var node = this.createElementNSPlus(\"ogc:SortProperty\");\n- this.writeNode(\n- \"ogc:PropertyName\",\n- sortProperty,\n- node\n- );\n- this.writeNode(\n- \"ogc:SortOrder\",\n- (sortProperty.order == 'DESC') ? 'DESC' : 'ASC',\n- node\n- );\n- return node;\n- },\n- \"SortOrder\": function(value) {\n- var node = this.createElementNSPlus(\"ogc:SortOrder\", {\n- value: value\n- });\n- return node;\n- }\n- }, OpenLayers.Format.Filter.v1.prototype.writers[\"ogc\"]),\n- \"gml\": OpenLayers.Format.GML.v3.prototype.writers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v3.prototype.writers[\"feature\"]\n- },\n+ } else {\n+ this.stopLoading();\n+ if (this.crossOriginKeyword) {\n+ img.removeAttribute(\"crossorigin\");\n+ }\n+ OpenLayers.Event.observe(img, \"load\",\n+ OpenLayers.Function.bind(this.onImageLoad, this)\n+ );\n+ OpenLayers.Event.observe(img, \"error\",\n+ OpenLayers.Function.bind(this.onImageError, this)\n+ );\n+ this.imageReloadAttempts = 0;\n+ this.setImgSrc(this.url);\n+ }\n+ },\n \n- /**\n- * Method: writeSpatial\n- *\n- * Read a {} filter and converts it into XML.\n- *\n- * Parameters:\n- * filter - {} The filter.\n- * name - {String} Name of the generated XML element.\n- *\n- * Returns:\n- * {DOMElement} The created XML element.\n- */\n- writeSpatial: function(filter, name) {\n- var node = this.createElementNSPlus(\"ogc:\" + name);\n- this.writeNode(\"PropertyName\", filter, node);\n- if (filter.value instanceof OpenLayers.Filter.Function) {\n- this.writeNode(\"Function\", filter.value, node);\n- } else {\n- var child;\n- if (filter.value instanceof OpenLayers.Geometry) {\n- child = this.writeNode(\"feature:_geometry\", filter.value).firstChild;\n+ /**\n+ * Method: setImgSrc\n+ * Sets the source for the tile image\n+ *\n+ * Parameters:\n+ * url - {String} or undefined to hide the image\n+ */\n+ setImgSrc: function(url) {\n+ var img = this.imgDiv;\n+ if (url) {\n+ img.style.visibility = 'hidden';\n+ img.style.opacity = 0;\n+ // don't set crossOrigin if the url is a data URL\n+ if (this.crossOriginKeyword) {\n+ if (url.substr(0, 5) !== 'data:') {\n+ img.setAttribute(\"crossorigin\", this.crossOriginKeyword);\n } else {\n- child = this.writeNode(\"gml:Envelope\", filter.value);\n- }\n- if (filter.projection) {\n- child.setAttribute(\"srsName\", filter.projection);\n+ img.removeAttribute(\"crossorigin\");\n }\n- node.appendChild(child);\n }\n- return node;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.Filter.v1_1_0\"\n+ img.src = url;\n+ } else {\n+ // Remove reference to the image, and leave it to the browser's\n+ // caching and garbage collection.\n+ this.stopLoading();\n+ this.imgDiv = null;\n+ if (img.parentNode) {\n+ img.parentNode.removeChild(img);\n+ }\n+ }\n+ },\n \n- });\n-/* ======================================================================\n- OpenLayers/Format/OWSCommon/v1_0_0.js\n- ====================================================================== */\n+ /**\n+ * Method: getTile\n+ * Get the tile's markup.\n+ *\n+ * Returns:\n+ * {DOMElement} The tile's markup\n+ */\n+ getTile: function() {\n+ return this.frame ? this.frame : this.getImage();\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: createBackBuffer\n+ * Create a backbuffer for this tile. A backbuffer isn't exactly a clone\n+ * of the tile's markup, because we want to avoid the reloading of the\n+ * image. So we clone the frame, and steal the image from the tile.\n+ *\n+ * Returns:\n+ * {DOMElement} The markup, or undefined if the tile has no image\n+ * or if it's currently loading.\n+ */\n+ createBackBuffer: function() {\n+ if (!this.imgDiv || this.isLoading) {\n+ return;\n+ }\n+ var backBuffer;\n+ if (this.frame) {\n+ backBuffer = this.frame.cloneNode(false);\n+ backBuffer.appendChild(this.imgDiv);\n+ } else {\n+ backBuffer = this.imgDiv;\n+ }\n+ this.imgDiv = null;\n+ return backBuffer;\n+ },\n \n-/**\n- * @requires OpenLayers/Format/OWSCommon/v1.js\n- */\n+ /**\n+ * Method: onImageLoad\n+ * Handler for the image onload event\n+ */\n+ onImageLoad: function() {\n+ var img = this.imgDiv;\n+ this.stopLoading();\n+ img.style.visibility = 'inherit';\n+ img.style.opacity = this.layer.opacity;\n+ this.isLoading = false;\n+ this.canvasContext = null;\n+ this.events.triggerEvent(\"loadend\");\n \n-/**\n- * Class: OpenLayers.Format.OWSCommon.v1_0_0\n- * Parser for OWS Common version 1.0.0.\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Format.OWSCommon.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter =\n+ \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" +\n+ img.src + \"', sizingMethod='scale')\";\n+ }\n+ },\n \n /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n+ * Method: onImageError\n+ * Handler for the image onerror event\n */\n- namespaces: {\n- ows: \"http://www.opengis.net/ows\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n+ onImageError: function() {\n+ var img = this.imgDiv;\n+ if (img.src != null) {\n+ this.imageReloadAttempts++;\n+ if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n+ this.setImgSrc(this.layer.getURL(this.bounds));\n+ } else {\n+ OpenLayers.Element.addClass(img, \"olImageLoadError\");\n+ this.events.triggerEvent(\"loaderror\");\n+ this.onImageLoad();\n+ }\n+ }\n },\n \n /**\n- * 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+ * Method: stopLoading\n+ * Stops a loading sequence so won't be executed.\n */\n- readers: {\n- \"ows\": OpenLayers.Util.applyDefaults({\n- \"ExceptionReport\": function(node, obj) {\n- obj.success = false;\n- obj.exceptionReport = {\n- version: node.getAttribute('version'),\n- language: node.getAttribute('language'),\n- exceptions: []\n- };\n- this.readChildNodes(node, obj.exceptionReport);\n- }\n- }, OpenLayers.Format.OWSCommon.v1.prototype.readers.ows)\n+ stopLoading: function() {\n+ OpenLayers.Event.stopObservingElement(this.imgDiv);\n+ window.clearTimeout(this._loadTimeout);\n+ delete this._loadTimeout;\n },\n \n /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n+ * APIMethod: getCanvasContext\n+ * Returns a canvas context associated with the tile image (with\n+ * the image drawn on it).\n+ * Returns undefined if the browser does not support canvas, if\n+ * the tile has no image or if it's currently loading.\n+ *\n+ * The function returns a canvas context instance but the\n+ * underlying canvas is still available in the 'canvas' property:\n+ * (code)\n+ * var context = tile.getCanvasContext();\n+ * if (context) {\n+ * var data = context.canvas.toDataURL('image/jpeg');\n+ * }\n+ * (end)\n+ *\n+ * Returns:\n+ * {Boolean}\n */\n- writers: {\n- \"ows\": OpenLayers.Format.OWSCommon.v1.prototype.writers.ows\n+ getCanvasContext: function() {\n+ if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n+ if (!this.canvasContext) {\n+ var canvas = document.createElement(\"canvas\");\n+ canvas.width = this.size.w;\n+ canvas.height = this.size.h;\n+ this.canvasContext = canvas.getContext(\"2d\");\n+ this.canvasContext.drawImage(this.imgDiv, 0, 0);\n+ }\n+ return this.canvasContext;\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Format.OWSCommon.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Tile.Image\"\n \n });\n-/* ======================================================================\n- OpenLayers/Format/WFST/v1_1_0.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n \n-/**\n- * @requires OpenLayers/Format/WFST/v1.js\n- * @requires OpenLayers/Format/Filter/v1_1_0.js\n- * @requires OpenLayers/Format/OWSCommon/v1_0_0.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WFST.v1_1_0\n- * A format for creating WFS v1.1.0 transactions. Create a new instance with the\n- * constructor.\n- *\n- * Inherits from:\n- * - \n- * - \n+/** \n+ * Constant: OpenLayers.Tile.Image.IMAGE\n+ * {HTMLImageElement} The image for a tile.\n */\n-OpenLayers.Format.WFST.v1_1_0 = OpenLayers.Class(\n- OpenLayers.Format.Filter.v1_1_0, OpenLayers.Format.WFST.v1, {\n-\n- /**\n- * Property: version\n- * {String} WFS version number.\n- */\n- version: \"1.1.0\",\n-\n- /**\n- * Property: schemaLocations\n- * {Object} Properties are namespace aliases, values are schema locations.\n- */\n- schemaLocations: {\n- \"wfs\": \"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\"\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.WFST.v1_1_0\n- * A class for parsing and generating WFS v1.1.0 transactions.\n- *\n- * To read additional information like hit count (numberOfFeatures) from\n- * the FeatureCollection, call the method\n- * with {output: \"object\"} as 2nd argument. Note that it is possible to\n- * just request the hit count from a WFS 1.1.0 server with the\n- * resultType=\"hits\" request parameter.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options properties:\n- * featureType - {String} Local (without prefix) feature typeName (required).\n- * featureNS - {String} Feature namespace (optional).\n- * featurePrefix - {String} Feature namespace alias (optional - only used\n- * if featureNS is provided). Default is 'feature'.\n- * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n- */\n- initialize: function(options) {\n- OpenLayers.Format.Filter.v1_1_0.prototype.initialize.apply(this, [options]);\n- OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);\n- },\n-\n- /**\n- * Method: readNode\n- * Shorthand for applying one of the named readers given the node\n- * namespace and local name. Readers take two args (node, obj) and\n- * generally extend or modify the second.\n- *\n- * Parameters:\n- * node - {DOMElement} The node to be read (required).\n- * obj - {Object} The object to be modified (optional).\n- * first - {Boolean} Should be set to true for the first node read. This\n- * is usually the readNode call in the read method. Without this being\n- * set, auto-configured properties will stick on subsequent reads.\n- *\n- * Returns:\n- * {Object} The input object, modified (or a new one if none was provided).\n- */\n- readNode: function(node, obj, first) {\n- // Not the superclass, only the mixin classes inherit from\n- // Format.GML.v3. We need this because we don't want to get readNode\n- // from the superclass's superclass, which is OpenLayers.Format.XML.\n- return OpenLayers.Format.GML.v3.prototype.readNode.apply(this, arguments);\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wfs\": OpenLayers.Util.applyDefaults({\n- \"FeatureCollection\": function(node, obj) {\n- obj.numberOfFeatures = parseInt(node.getAttribute(\n- \"numberOfFeatures\"));\n- OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"][\"FeatureCollection\"].apply(\n- this, arguments);\n- },\n- \"TransactionResponse\": function(node, obj) {\n- obj.insertIds = [];\n- obj.success = false;\n- this.readChildNodes(node, obj);\n- },\n- \"TransactionSummary\": function(node, obj) {\n- // this is a limited test of success\n- obj.success = true;\n- },\n- \"InsertResults\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"Feature\": function(node, container) {\n- var obj = {\n- fids: []\n- };\n- this.readChildNodes(node, obj);\n- container.insertIds.push(obj.fids[0]);\n- }\n- }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n- \"gml\": OpenLayers.Format.GML.v3.prototype.readers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v3.prototype.readers[\"feature\"],\n- \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.readers[\"ogc\"],\n- \"ows\": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"]\n- },\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"wfs\": OpenLayers.Util.applyDefaults({\n- \"GetFeature\": function(options) {\n- var node = OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"][\"GetFeature\"].apply(this, arguments);\n- options && this.setAttributes(node, {\n- resultType: options.resultType,\n- startIndex: options.startIndex,\n- count: options.count\n- });\n- return node;\n- },\n- \"Query\": function(options) {\n- options = OpenLayers.Util.extend({\n- featureNS: this.featureNS,\n- featurePrefix: this.featurePrefix,\n- featureType: this.featureType,\n- srsName: this.srsName\n- }, options);\n- var prefix = options.featurePrefix;\n- var node = this.createElementNSPlus(\"wfs:Query\", {\n- attributes: {\n- typeName: (prefix ? prefix + \":\" : \"\") +\n- options.featureType,\n- srsName: options.srsName\n- }\n- });\n- if (options.featureNS) {\n- node.setAttribute(\"xmlns:\" + prefix, options.featureNS);\n- }\n- if (options.propertyNames) {\n- for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n- this.writeNode(\n- \"wfs:PropertyName\", {\n- property: options.propertyNames[i]\n- },\n- node\n- );\n- }\n- }\n- if (options.filter) {\n- OpenLayers.Format.WFST.v1_1_0.prototype.setFilterProperty.call(this, options.filter);\n- this.writeNode(\"ogc:Filter\", options.filter, node);\n- }\n- return node;\n- },\n- \"PropertyName\": function(obj) {\n- return this.createElementNSPlus(\"wfs:PropertyName\", {\n- value: obj.property\n- });\n- }\n- }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n- \"gml\": OpenLayers.Format.GML.v3.prototype.writers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v3.prototype.writers[\"feature\"],\n- \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers[\"ogc\"]\n- },\n+OpenLayers.Tile.Image.IMAGE = (function() {\n+ var img = new Image();\n+ img.className = \"olTileImage\";\n+ // avoid image gallery menu in IE6\n+ img.galleryImg = \"no\";\n+ return img;\n+}());\n \n- CLASS_NAME: \"OpenLayers.Format.WFST.v1_1_0\"\n- });\n /* ======================================================================\n- OpenLayers/Format/WPSExecute.js\n+ OpenLayers/Layer/Grid.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n- * @requires OpenLayers/Format/WCSGetCoverage.js\n- * @requires OpenLayers/Format/WFST/v1_1_0.js\n+ * @requires OpenLayers/Layer/HTTPRequest.js\n+ * @requires OpenLayers/Tile/Image.js\n */\n \n /**\n- * Class: OpenLayers.Format.WPSExecute version 1.0.0\n+ * Class: OpenLayers.Layer.Grid\n+ * Base class for layers that use a lattice of tiles. Create a new grid\n+ * layer with the constructor.\n *\n * Inherits from:\n- * - \n+ * - \n */\n-OpenLayers.Format.WPSExecute = OpenLayers.Class(OpenLayers.Format.XML,\n- OpenLayers.Format.Filter.v1_1_0, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- gml: \"http://www.opengis.net/gml\",\n- wps: \"http://www.opengis.net/wps/1.0.0\",\n- wfs: \"http://www.opengis.net/wfs\",\n- ogc: \"http://www.opengis.net/ogc\",\n- wcs: \"http://www.opengis.net/wcs\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n-\n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n-\n- /**\n- * Constant: VERSION\n- * {String} 1.0.0\n- */\n- VERSION: \"1.0.0\",\n-\n- /**\n- * Property: schemaLocation\n- * {String} Schema location\n- */\n- schemaLocation: \"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd\",\n-\n- schemaLocationAttr: function(options) {\n- return undefined;\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.WPSExecute\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Method: write\n- *\n- * Parameters:\n- * options - {Object} Optional object.\n- *\n- * Returns:\n- * {String} An WPS Execute request XML string.\n- */\n- write: function(options) {\n- var doc;\n- if (window.ActiveXObject) {\n- doc = new ActiveXObject(\"Microsoft.XMLDOM\");\n- this.xmldom = doc;\n- } else {\n- doc = document.implementation.createDocument(\"\", \"\", null);\n- }\n- var node = this.writeNode(\"wps:Execute\", options, doc);\n- this.setAttributeNS(\n- node, this.namespaces.xsi,\n- \"xsi:schemaLocation\", this.schemaLocation\n- );\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n- },\n+OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n \n- /**\n- * APIMethod: read\n- * Parse a WPS Execute and return an object with its information.\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object}\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var info = {};\n- this.readNode(data, info);\n- return info;\n- },\n+ /**\n+ * APIProperty: tileSize\n+ * {}\n+ */\n+ tileSize: null,\n \n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"wps\": {\n- \"Execute\": function(options) {\n- var node = this.createElementNSPlus(\"wps:Execute\", {\n- attributes: {\n- version: this.VERSION,\n- service: 'WPS'\n- }\n- });\n- this.writeNode(\"ows:Identifier\", options.identifier, node);\n- this.writeNode(\"wps:DataInputs\", options.dataInputs, node);\n- this.writeNode(\"wps:ResponseForm\", options.responseForm, node);\n- return node;\n- },\n- \"ResponseForm\": function(responseForm) {\n- var node = this.createElementNSPlus(\"wps:ResponseForm\", {});\n- if (responseForm.rawDataOutput) {\n- this.writeNode(\"wps:RawDataOutput\", responseForm.rawDataOutput, node);\n- }\n- if (responseForm.responseDocument) {\n- this.writeNode(\"wps:ResponseDocument\", responseForm.responseDocument, node);\n- }\n- return node;\n- },\n- \"ResponseDocument\": function(responseDocument) {\n- var node = this.createElementNSPlus(\"wps:ResponseDocument\", {\n- attributes: {\n- storeExecuteResponse: responseDocument.storeExecuteResponse,\n- lineage: responseDocument.lineage,\n- status: responseDocument.status\n- }\n- });\n- if (responseDocument.outputs) {\n- for (var i = 0, len = responseDocument.outputs.length; i < len; i++) {\n- this.writeNode(\"wps:Output\", responseDocument.outputs[i], node);\n- }\n- }\n- return node;\n- },\n- \"Output\": function(output) {\n- var node = this.createElementNSPlus(\"wps:Output\", {\n- attributes: {\n- asReference: output.asReference,\n- mimeType: output.mimeType,\n- encoding: output.encoding,\n- schema: output.schema\n- }\n- });\n- this.writeNode(\"ows:Identifier\", output.identifier, node);\n- this.writeNode(\"ows:Title\", output.title, node);\n- this.writeNode(\"ows:Abstract\", output[\"abstract\"], node);\n- return node;\n- },\n- \"RawDataOutput\": function(rawDataOutput) {\n- var node = this.createElementNSPlus(\"wps:RawDataOutput\", {\n- attributes: {\n- mimeType: rawDataOutput.mimeType,\n- encoding: rawDataOutput.encoding,\n- schema: rawDataOutput.schema\n- }\n- });\n- this.writeNode(\"ows:Identifier\", rawDataOutput.identifier, node);\n- return node;\n- },\n- \"DataInputs\": function(dataInputs) {\n- var node = this.createElementNSPlus(\"wps:DataInputs\", {});\n- for (var i = 0, ii = dataInputs.length; i < ii; ++i) {\n- this.writeNode(\"wps:Input\", dataInputs[i], node);\n- }\n- return node;\n- },\n- \"Input\": function(input) {\n- var node = this.createElementNSPlus(\"wps:Input\", {});\n- this.writeNode(\"ows:Identifier\", input.identifier, node);\n- if (input.title) {\n- this.writeNode(\"ows:Title\", input.title, node);\n- }\n- if (input.data) {\n- this.writeNode(\"wps:Data\", input.data, node);\n- }\n- if (input.reference) {\n- this.writeNode(\"wps:Reference\", input.reference, node);\n- }\n- if (input.boundingBoxData) {\n- this.writeNode(\"wps:BoundingBoxData\", input.boundingBoxData, node);\n- }\n- return node;\n- },\n- \"Data\": function(data) {\n- var node = this.createElementNSPlus(\"wps:Data\", {});\n- if (data.literalData) {\n- this.writeNode(\"wps:LiteralData\", data.literalData, node);\n- } else if (data.complexData) {\n- this.writeNode(\"wps:ComplexData\", data.complexData, node);\n- } else if (data.boundingBoxData) {\n- this.writeNode(\"ows:BoundingBox\", data.boundingBoxData, node);\n- }\n- return node;\n- },\n- \"LiteralData\": function(literalData) {\n- var node = this.createElementNSPlus(\"wps:LiteralData\", {\n- attributes: {\n- uom: literalData.uom\n- },\n- value: literalData.value\n- });\n- return node;\n- },\n- \"ComplexData\": function(complexData) {\n- var node = this.createElementNSPlus(\"wps:ComplexData\", {\n- attributes: {\n- mimeType: complexData.mimeType,\n- encoding: complexData.encoding,\n- schema: complexData.schema\n- }\n- });\n- var data = complexData.value;\n- if (typeof data === \"string\") {\n- node.appendChild(\n- this.getXMLDoc().createCDATASection(complexData.value)\n- );\n- } else {\n- node.appendChild(data);\n- }\n- return node;\n- },\n- \"Reference\": function(reference) {\n- var node = this.createElementNSPlus(\"wps:Reference\", {\n- attributes: {\n- mimeType: reference.mimeType,\n- \"xlink:href\": reference.href,\n- method: reference.method,\n- encoding: reference.encoding,\n- schema: reference.schema\n- }\n- });\n- if (reference.body) {\n- this.writeNode(\"wps:Body\", reference.body, node);\n- }\n- return node;\n- },\n- \"BoundingBoxData\": function(node, obj) {\n- this.writers['ows']['BoundingBox'].apply(this, [node, obj, \"wps:BoundingBoxData\"]);\n- },\n- \"Body\": function(body) {\n- var node = this.createElementNSPlus(\"wps:Body\", {});\n- if (body.wcs) {\n- this.writeNode(\"wcs:GetCoverage\", body.wcs, node);\n- } else if (body.wfs) {\n- // OpenLayers.Format.WFST expects these to be on the \n- // instance and not in the options\n- this.featureType = body.wfs.featureType;\n- this.version = body.wfs.version;\n- this.writeNode(\"wfs:GetFeature\", body.wfs, node);\n- } else {\n- this.writeNode(\"wps:Execute\", body, node);\n- }\n- return node;\n- }\n- },\n- \"wcs\": OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs,\n- \"wfs\": OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs,\n- \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc,\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows\n- },\n+ /**\n+ * Property: tileOriginCorner\n+ * {String} If the property is not provided, the tile origin \n+ * will be derived from the layer's . The corner of the \n+ * used is determined by this property. Acceptable values\n+ * are \"tl\" (top left), \"tr\" (top right), \"bl\" (bottom left), and \"br\"\n+ * (bottom right). Default is \"bl\".\n+ */\n+ tileOriginCorner: \"bl\",\n \n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wps\": {\n- \"ExecuteResponse\": function(node, obj) {\n- obj.executeResponse = {\n- lang: node.getAttribute(\"lang\"),\n- statusLocation: node.getAttribute(\"statusLocation\"),\n- serviceInstance: node.getAttribute(\"serviceInstance\"),\n- service: node.getAttribute(\"service\")\n- };\n- this.readChildNodes(node, obj.executeResponse);\n- },\n- \"Process\": function(node, obj) {\n- obj.process = {};\n- this.readChildNodes(node, obj.process);\n- },\n- \"Status\": function(node, obj) {\n- obj.status = {\n- creationTime: node.getAttribute(\"creationTime\")\n- };\n- this.readChildNodes(node, obj.status);\n- },\n- \"ProcessSucceeded\": function(node, obj) {\n- obj.processSucceeded = true;\n- },\n- \"ProcessOutputs\": function(node, processDescription) {\n- processDescription.processOutputs = [];\n- this.readChildNodes(node, processDescription.processOutputs);\n- },\n- \"Output\": function(node, processOutputs) {\n- var output = {};\n- this.readChildNodes(node, output);\n- processOutputs.push(output);\n- },\n- \"Reference\": function(node, output) {\n- output.reference = {\n- href: node.getAttribute(\"href\"),\n- mimeType: node.getAttribute(\"mimeType\"),\n- encoding: node.getAttribute(\"encoding\"),\n- schema: node.getAttribute(\"schema\")\n- };\n- },\n- \"Data\": function(node, output) {\n- output.data = {};\n- this.readChildNodes(node, output);\n- },\n- \"LiteralData\": function(node, output) {\n- output.literalData = {\n- dataType: node.getAttribute(\"dataType\"),\n- uom: node.getAttribute(\"uom\"),\n- value: this.getChildValue(node)\n- };\n- },\n- \"ComplexData\": function(node, output) {\n- output.complexData = {\n- mimeType: node.getAttribute(\"mimeType\"),\n- schema: node.getAttribute(\"schema\"),\n- encoding: node.getAttribute(\"encoding\"),\n- value: \"\"\n- };\n+ /**\n+ * APIProperty: tileOrigin\n+ * {} Optional origin for aligning the grid of tiles.\n+ * If provided, requests for tiles at all resolutions will be aligned\n+ * with this location (no tiles shall overlap this location). If\n+ * not provided, the grid of tiles will be aligned with the layer's\n+ * . Default is ``null``.\n+ */\n+ tileOrigin: null,\n \n- // try to get *some* value, ignore the empty text values\n- if (this.isSimpleContent(node)) {\n- var child;\n- for (child = node.firstChild; child; child = child.nextSibling) {\n- switch (child.nodeType) {\n- case 3: // text node\n- case 4: // cdata section\n- output.complexData.value += child.nodeValue;\n- }\n- }\n- } else {\n- for (child = node.firstChild; child; child = child.nextSibling) {\n- if (child.nodeType == 1) {\n- output.complexData.value = child;\n- }\n- }\n- }\n+ /** APIProperty: tileOptions\n+ * {Object} optional configuration options for instances\n+ * created by this Layer, if supported by the tile class.\n+ */\n+ tileOptions: null,\n \n- },\n- \"BoundingBox\": function(node, output) {\n- output.boundingBoxData = {\n- dimensions: node.getAttribute(\"dimensions\"),\n- crs: node.getAttribute(\"crs\")\n- };\n- this.readChildNodes(node, output.boundingBoxData);\n- }\n- },\n+ /**\n+ * APIProperty: tileClass\n+ * {} The tile class to use for this layer.\n+ * Defaults is OpenLayers.Tile.Image.\n+ */\n+ tileClass: OpenLayers.Tile.Image,\n \n- // TODO: we should add Exception parsing here\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n+ /**\n+ * Property: grid\n+ * {Array(Array())} This is an array of rows, each row is \n+ * an array of tiles.\n+ */\n+ grid: null,\n \n- CLASS_NAME: \"OpenLayers.Format.WPSExecute\"\n+ /**\n+ * APIProperty: singleTile\n+ * {Boolean} Moves the layer into single-tile mode, meaning that one tile \n+ * will be loaded. The tile's size will be determined by the 'ratio'\n+ * property. When the tile is dragged such that it does not cover the \n+ * entire viewport, it is reloaded.\n+ */\n+ singleTile: false,\n \n- });\n-/* ======================================================================\n- OpenLayers/Events.js\n- ====================================================================== */\n+ /** APIProperty: ratio\n+ * {Float} Used only when in single-tile mode, this specifies the \n+ * ratio of the size of the single tile to the size of the map.\n+ * Default value is 1.5.\n+ */\n+ ratio: 1.5,\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: buffer\n+ * {Integer} Used only when in gridded mode, this specifies the number of \n+ * extra rows and colums of tiles on each side which will\n+ * surround the minimum grid tiles to cover the map.\n+ * For very slow loading layers, a larger value may increase\n+ * performance somewhat when dragging, but will increase bandwidth\n+ * use significantly. \n+ */\n+ buffer: 0,\n \n+ /**\n+ * APIProperty: transitionEffect\n+ * {String} The transition effect to use when the map is zoomed.\n+ * Two posible values:\n+ *\n+ * \"resize\" - Existing tiles are resized on zoom to provide a visual\n+ * effect of the zoom having taken place immediately. As the\n+ * new tiles become available, they are drawn on top of the\n+ * resized tiles (this is the default setting).\n+ * \"map-resize\" - Existing tiles are resized on zoom and placed below the\n+ * base layer. New tiles for the base layer will cover existing tiles.\n+ * This setting is recommended when having an overlay duplicated during\n+ * the transition is undesirable (e.g. street labels or big transparent\n+ * fills). \n+ * null - No transition effect.\n+ *\n+ * Using \"resize\" on non-opaque layers can cause undesired visual\n+ * effects. Set transitionEffect to null in this case.\n+ */\n+ transitionEffect: \"resize\",\n \n-/**\n- * @requires OpenLayers/Util.js\n- */\n+ /**\n+ * APIProperty: numLoadingTiles\n+ * {Integer} How many tiles are still loading?\n+ */\n+ numLoadingTiles: 0,\n \n-/**\n- * Namespace: OpenLayers.Event\n- * Utility functions for event handling.\n- */\n-OpenLayers.Event = {\n+ /**\n+ * Property: serverResolutions\n+ * {Array(Number}} This property is documented in subclasses as\n+ * an API property.\n+ */\n+ serverResolutions: null,\n \n- /** \n- * Property: observers \n- * {Object} A hashtable cache of the event observers. Keyed by\n- * element._eventCacheID \n+ /**\n+ * Property: loading\n+ * {Boolean} Indicates if tiles are being loaded.\n */\n- observers: false,\n+ loading: false,\n \n /**\n- * Constant: KEY_SPACE\n- * {int}\n+ * Property: backBuffer\n+ * {DOMElement} The back buffer.\n */\n- KEY_SPACE: 32,\n+ backBuffer: null,\n \n- /** \n- * Constant: KEY_BACKSPACE \n- * {int} \n+ /**\n+ * Property: gridResolution\n+ * {Number} The resolution of the current grid. Used for backbuffer and\n+ * client zoom. This property is updated every time the grid is\n+ * initialized.\n */\n- KEY_BACKSPACE: 8,\n+ gridResolution: null,\n \n- /** \n- * Constant: KEY_TAB \n- * {int} \n+ /**\n+ * Property: backBufferResolution\n+ * {Number} The resolution of the current back buffer. This property is\n+ * updated each time a back buffer is created.\n */\n- KEY_TAB: 9,\n+ backBufferResolution: null,\n \n- /** \n- * Constant: KEY_RETURN \n- * {int} \n+ /**\n+ * Property: backBufferLonLat\n+ * {Object} The top-left corner of the current back buffer. Includes lon\n+ * and lat properties. This object is updated each time a back buffer\n+ * is created.\n */\n- KEY_RETURN: 13,\n+ backBufferLonLat: null,\n \n- /** \n- * Constant: KEY_ESC \n- * {int} \n+ /**\n+ * Property: backBufferTimerId\n+ * {Number} The id of the back buffer timer. This timer is used to\n+ * delay the removal of the back buffer, thereby preventing\n+ * flash effects caused by tile animation.\n */\n- KEY_ESC: 27,\n+ backBufferTimerId: null,\n \n- /** \n- * Constant: KEY_LEFT \n- * {int} \n+ /**\n+ * APIProperty: removeBackBufferDelay\n+ * {Number} Delay for removing the backbuffer when all tiles have finished\n+ * loading. Can be set to 0 when no css opacity transitions for the\n+ * olTileImage class are used. Default is 0 for layers,\n+ * 2500 for tiled layers. See for more information on\n+ * tile animation.\n */\n- KEY_LEFT: 37,\n+ removeBackBufferDelay: null,\n \n- /** \n- * Constant: KEY_UP \n- * {int} \n+ /**\n+ * APIProperty: className\n+ * {String} Name of the class added to the layer div. If not set in the\n+ * options passed to the constructor then className defaults to\n+ * \"olLayerGridSingleTile\" for single tile layers (see ),\n+ * and \"olLayerGrid\" for non single tile layers.\n+ *\n+ * Note:\n+ *\n+ * The displaying of tiles is not animated by default for single tile\n+ * layers - OpenLayers' default theme (style.css) includes this:\n+ * (code)\n+ * .olLayerGrid .olTileImage {\n+ * -webkit-transition: opacity 0.2s linear;\n+ * -moz-transition: opacity 0.2s linear;\n+ * -o-transition: opacity 0.2s linear;\n+ * transition: opacity 0.2s linear;\n+ * }\n+ * (end)\n+ * To animate tile displaying for any grid layer the following\n+ * CSS rule can be used:\n+ * (code)\n+ * .olTileImage {\n+ * -webkit-transition: opacity 0.2s linear;\n+ * -moz-transition: opacity 0.2s linear;\n+ * -o-transition: opacity 0.2s linear;\n+ * transition: opacity 0.2s linear;\n+ * }\n+ * (end)\n+ * In that case, to avoid flash effects, \n+ * should not be zero.\n */\n- KEY_UP: 38,\n+ className: null,\n \n- /** \n- * Constant: KEY_RIGHT \n- * {int} \n+ /**\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * layer.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n+ *\n+ * All event objects have at least the following properties:\n+ * object - {Object} A reference to layer.events.object.\n+ * element - {DOMElement} A reference to layer.events.element.\n+ *\n+ * Supported event types:\n+ * addtile - Triggered when a tile is added to this layer. Listeners receive\n+ * an object as first argument, which has a tile property that\n+ * references the tile that has been added.\n+ * tileloadstart - Triggered when a tile starts loading. Listeners receive\n+ * an object as first argument, which has a tile property that\n+ * references the tile that starts loading.\n+ * tileloaded - Triggered when each new tile is\n+ * loaded, as a means of progress update to listeners.\n+ * listeners can access 'numLoadingTiles' if they wish to keep\n+ * track of the loading progress. Listeners are called with an object\n+ * with a 'tile' property as first argument, making the loaded tile\n+ * available to the listener, and an 'aborted' property, which will be\n+ * true when loading was aborted and no tile data is available.\n+ * tileerror - Triggered before the tileloaded event (i.e. when the tile is\n+ * still hidden) if a tile failed to load. Listeners receive an object\n+ * as first argument, which has a tile property that references the\n+ * tile that could not be loaded.\n+ * retile - Triggered when the layer recreates its tile grid.\n */\n- KEY_RIGHT: 39,\n \n- /** \n- * Constant: KEY_DOWN \n- * {int} \n+ /**\n+ * Property: gridLayout\n+ * {Object} Object containing properties tilelon, tilelat, startcol,\n+ * startrow\n */\n- KEY_DOWN: 40,\n+ gridLayout: null,\n \n- /** \n- * Constant: KEY_DELETE \n- * {int} \n+ /**\n+ * Property: rowSign\n+ * {Number} 1 for grids starting at the top, -1 for grids starting at the\n+ * bottom. This is used for several grid index and offset calculations.\n */\n- KEY_DELETE: 46,\n+ rowSign: null,\n \n+ /**\n+ * Property: transitionendEvents\n+ * {Array} Event names for transitionend\n+ */\n+ transitionendEvents: [\n+ 'transitionend', 'webkitTransitionEnd', 'otransitionend',\n+ 'oTransitionEnd'\n+ ],\n \n /**\n- * Method: element\n- * Cross browser event element detection.\n- * \n+ * Constructor: OpenLayers.Layer.Grid\n+ * Create a new grid layer\n+ *\n * Parameters:\n- * event - {Event} \n- * \n- * Returns:\n- * {DOMElement} The element that caused the event \n+ * name - {String}\n+ * url - {String}\n+ * params - {Object}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- element: function(event) {\n- return event.target || event.srcElement;\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,\n+ arguments);\n+ this.grid = [];\n+ this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n+\n+ this.initProperties();\n+\n+ this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1;\n },\n \n /**\n- * Method: isSingleTouch\n- * Determine whether event was caused by a single touch\n- *\n- * Parameters:\n- * event - {Event}\n- *\n- * Returns:\n- * {Boolean}\n+ * Method: initProperties\n+ * Set any properties that depend on the value of singleTile.\n+ * Currently sets removeBackBufferDelay and className\n */\n- isSingleTouch: function(event) {\n- return event.touches && event.touches.length == 1;\n+ initProperties: function() {\n+ if (this.options.removeBackBufferDelay === undefined) {\n+ this.removeBackBufferDelay = this.singleTile ? 0 : 2500;\n+ }\n+\n+ if (this.options.className === undefined) {\n+ this.className = this.singleTile ? 'olLayerGridSingleTile' :\n+ 'olLayerGrid';\n+ }\n },\n \n /**\n- * Method: isMultiTouch\n- * Determine whether event was caused by a multi touch\n+ * Method: setMap\n *\n * Parameters:\n- * event - {Event}\n- *\n- * Returns:\n- * {Boolean}\n+ * map - {} The map.\n */\n- isMultiTouch: function(event) {\n- return event.touches && event.touches.length > 1;\n+ setMap: function(map) {\n+ OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n+ OpenLayers.Element.addClass(this.div, this.className);\n },\n \n /**\n- * Method: isLeftClick\n- * Determine whether event was caused by a left click. \n+ * Method: removeMap\n+ * Called when the layer is removed from the map.\n *\n * Parameters:\n- * event - {Event} \n- * \n- * Returns:\n- * {Boolean}\n+ * map - {} The map.\n */\n- isLeftClick: function(event) {\n- return (((event.which) && (event.which == 1)) ||\n- ((event.button) && (event.button == 1)));\n+ removeMap: function(map) {\n+ this.removeBackBuffer();\n },\n \n /**\n- * Method: isRightClick\n- * Determine whether event was caused by a right mouse click. \n- *\n- * Parameters:\n- * event - {Event} \n- * \n- * Returns:\n- * {Boolean}\n+ * APIMethod: destroy\n+ * Deconstruct the layer and clear the grid.\n */\n- isRightClick: function(event) {\n- return (((event.which) && (event.which == 3)) ||\n- ((event.button) && (event.button == 2)));\n+ destroy: function() {\n+ this.removeBackBuffer();\n+ this.clearGrid();\n+\n+ this.grid = null;\n+ this.tileSize = null;\n+ OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: stop\n- * Stops an event from propagating. \n+ * APIMethod: mergeNewParams\n+ * Refetches tiles with new params merged, keeping a backbuffer. Each\n+ * loading new tile will have a css class of '.olTileReplacing'. If a\n+ * stylesheet applies a 'display: none' style to that class, any fade-in\n+ * transition will not apply, and backbuffers for each tile will be removed\n+ * as soon as the tile is loaded.\n+ * \n+ * Parameters:\n+ * newParams - {Object}\n *\n- * Parameters: \n- * event - {Event} \n- * allowDefault - {Boolean} If true, we stop the event chain but \n- * still allow the default browser behaviour (text selection,\n- * radio-button clicking, etc). Default is false.\n+ * Returns:\n+ * redrawn: {Boolean} whether the layer was actually redrawn.\n */\n- stop: function(event, allowDefault) {\n-\n- if (!allowDefault) {\n- OpenLayers.Event.preventDefault(event);\n- }\n \n- if (event.stopPropagation) {\n- event.stopPropagation();\n- } else {\n- event.cancelBubble = true;\n+ /**\n+ * Method: clearGrid\n+ * Go through and remove all tiles from the grid, calling\n+ * destroy() on each of them to kill circular references\n+ */\n+ clearGrid: function() {\n+ if (this.grid) {\n+ for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n+ var row = this.grid[iRow];\n+ for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n+ var tile = row[iCol];\n+ this.destroyTile(tile);\n+ }\n+ }\n+ this.grid = [];\n+ this.gridResolution = null;\n+ this.gridLayout = null;\n }\n },\n \n /**\n- * Method: preventDefault\n- * Cancels the event if it is cancelable, without stopping further\n- * propagation of the event.\n- *\n+ * APIMethod: addOptions\n+ * \n * Parameters:\n- * event - {Event}\n+ * newOptions - {Object}\n+ * reinitialize - {Boolean} If set to true, and if resolution options of the\n+ * current baseLayer were changed, the map will be recentered to make\n+ * sure that it is displayed with a valid resolution, and a\n+ * changebaselayer event will be triggered.\n */\n- preventDefault: function(event) {\n- if (event.preventDefault) {\n- event.preventDefault();\n- } else {\n- event.returnValue = false;\n+ addOptions: function(newOptions, reinitialize) {\n+ var singleTileChanged = newOptions.singleTile !== undefined &&\n+ newOptions.singleTile !== this.singleTile;\n+ OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n+ if (this.map && singleTileChanged) {\n+ this.initProperties();\n+ this.clearGrid();\n+ this.tileSize = this.options.tileSize;\n+ this.setTileSize();\n+ this.moveTo(null, true);\n }\n },\n \n- /** \n- * Method: findElement\n- * \n+ /**\n+ * APIMethod: clone\n+ * Create a clone of this layer\n+ *\n * Parameters:\n- * event - {Event} \n- * tagName - {String} \n+ * obj - {Object} Is this ever used?\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+ * {} An exact clone of this OpenLayers.Layer.Grid\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+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Grid(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n }\n- return element;\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+ if (this.tileSize != null) {\n+ obj.tileSize = this.tileSize.clone();\n+ }\n+\n+ // we do not want to copy reference to grid, so we make a new array\n+ obj.grid = [];\n+ obj.gridResolution = null;\n+ // same for backbuffer\n+ obj.backBuffer = null;\n+ obj.backBufferTimerId = null;\n+ obj.loading = false;\n+ obj.numLoadingTiles = 0;\n+\n+ return obj;\n },\n \n- /** \n- * Method: observe\n- * \n+ /**\n+ * Method: moveTo\n+ * This function is called whenever the map is moved. All the moving\n+ * of actual 'tiles' is done by the map, but moveTo's role is to accept\n+ * a bounds and make sure the data that that bounds requires is pre-loaded.\n+ *\n * Parameters:\n- * elementParam - {DOMElement || String} \n- * name - {String} \n- * observer - {function} \n- * useCapture - {Boolean} \n+ * bounds - {}\n+ * zoomChanged - {Boolean}\n+ * dragging - {Boolean}\n */\n- observe: function(elementParam, name, observer, useCapture) {\n- var element = OpenLayers.Util.getElement(elementParam);\n- useCapture = useCapture || false;\n+ moveTo: function(bounds, zoomChanged, dragging) {\n \n- if (name == 'keypress' &&\n- (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n- element.attachEvent)) {\n- name = 'keydown';\n- }\n+ OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n \n- //if observers cache has not yet been created, create it\n- if (!this.observers) {\n- this.observers = {};\n- }\n+ bounds = bounds || this.map.getExtent();\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+ if (bounds != null) {\n \n- var cacheID = element._eventCacheID;\n+ // if grid is empty or zoom has changed, we *must* re-tile\n+ var forceReTile = !this.grid.length || zoomChanged;\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+ // total bounds of the tiles\n+ var tilesBounds = this.getTilesBounds();\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+ // the new map resolution\n+ var resolution = this.map.getResolution();\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+ // the server-supported resolution for the new map resolution\n+ var serverResolution = this.getServerResolution(resolution);\n+\n+ if (this.singleTile) {\n+\n+ // We want to redraw whenever even the slightest part of the \n+ // current bounds is not contained by our tile.\n+ // (thus, we do not specify partial -- its default is false)\n+\n+ if (forceReTile ||\n+ (!dragging && !tilesBounds.containsBounds(bounds))) {\n+\n+ // In single tile mode with no transition effect, we insert\n+ // a non-scaled backbuffer when the layer is moved. But if\n+ // a zoom occurs right after a move, i.e. before the new\n+ // image is received, we need to remove the backbuffer, or\n+ // an ill-positioned image will be visible during the zoom\n+ // transition.\n+\n+ if (zoomChanged && this.transitionEffect !== 'resize') {\n+ this.removeBackBuffer();\n+ }\n+\n+ if (!zoomChanged || this.transitionEffect === 'resize') {\n+ this.applyBackBuffer(resolution);\n+ }\n+\n+ this.initSingleTile(bounds);\n+ }\n+ } else {\n+\n+ // if the bounds have changed such that they are not even \n+ // *partially* contained by our tiles (e.g. when user has \n+ // programmatically panned to the other side of the earth on\n+ // zoom level 18), then moveGriddedTiles could potentially have\n+ // to run through thousands of cycles, so we want to reTile\n+ // instead (thus, partial true). \n+ forceReTile = forceReTile ||\n+ !tilesBounds.intersectsBounds(bounds, {\n+ worldBounds: this.map.baseLayer.wrapDateLine &&\n+ this.map.getMaxExtent()\n+ });\n+\n+ if (forceReTile) {\n+ if (zoomChanged && (this.transitionEffect === 'resize' ||\n+ this.gridResolution === resolution)) {\n+ this.applyBackBuffer(resolution);\n+ }\n+ this.initGriddedTiles(bounds);\n+ } else {\n+ this.moveGriddedTiles();\n+ }\n+ }\n }\n },\n \n- /** \n- * Method: 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+ * Method: getTileData\n+ * Given a map location, retrieve a tile and the pixel offset within that\n+ * tile corresponding to the location. If there is not an existing \n+ * tile in the grid that covers the given location, null will be \n+ * returned.\n+ *\n+ * Parameters:\n+ * loc - {} map location\n+ *\n+ * Returns:\n+ * {Object} Object with the following properties: tile ({}),\n+ * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel\n+ * offset from top left).\n */\n- stopObservingElement: function(elementParam) {\n- var element = OpenLayers.Util.getElement(elementParam);\n- var cacheID = element._eventCacheID;\n+ getTileData: function(loc) {\n+ var data = null,\n+ x = loc.lon,\n+ y = loc.lat,\n+ numRows = this.grid.length;\n \n- this._removeElementObservers(OpenLayers.Event.observers[cacheID]);\n+ if (this.map && numRows) {\n+ var res = this.map.getResolution(),\n+ tileWidth = this.tileSize.w,\n+ tileHeight = this.tileSize.h,\n+ bounds = this.grid[0][0].bounds,\n+ left = bounds.left,\n+ top = bounds.top;\n+\n+ if (x < left) {\n+ // deal with multiple worlds\n+ if (this.map.baseLayer.wrapDateLine) {\n+ var worldWidth = this.map.getMaxExtent().getWidth();\n+ var worldsAway = Math.ceil((left - x) / worldWidth);\n+ x += worldWidth * worldsAway;\n+ }\n+ }\n+ // tile distance to location (fractional number of tiles);\n+ var dtx = (x - left) / (res * tileWidth);\n+ var dty = (top - y) / (res * tileHeight);\n+ // index of tile in grid\n+ var col = Math.floor(dtx);\n+ var row = Math.floor(dty);\n+ if (row >= 0 && row < numRows) {\n+ var tile = this.grid[row][col];\n+ if (tile) {\n+ data = {\n+ tile: tile,\n+ // pixel index within tile\n+ i: Math.floor((dtx - col) * tileWidth),\n+ j: Math.floor((dty - row) * tileHeight)\n+ };\n+ }\n+ }\n+ }\n+ return data;\n },\n \n /**\n- * Method: _removeElementObservers\n+ * Method: destroyTile\n *\n * Parameters:\n- * elementObservers - {Array(Object)} Array of (element, name, \n- * observer, usecapture) objects, \n- * taken directly from hashtable\n+ * tile - {}\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+ destroyTile: function(tile) {\n+ this.removeTileMonitoringHooks(tile);\n+ tile.destroy();\n },\n \n /**\n- * Method: stopObserving\n- * \n+ * Method: getServerResolution\n+ * Return the closest server-supported resolution.\n+ *\n * Parameters:\n- * elementParam - {DOMElement || String} \n- * name - {String} \n- * observer - {function} \n- * useCapture - {Boolean} \n- * \n+ * resolution - {Number} The base resolution. If undefined the\n+ * map resolution is used.\n+ *\n * Returns:\n- * {Boolean} Whether or not the event observer was removed\n+ * {Number} The closest server resolution value.\n */\n- stopObserving: function(elementParam, name, observer, useCapture) {\n- useCapture = useCapture || false;\n+ getServerResolution: function(resolution) {\n+ var distance = Number.POSITIVE_INFINITY;\n+ resolution = resolution || this.map.getResolution();\n+ if (this.serverResolutions &&\n+ OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n+ var i, newDistance, newResolution, serverResolution;\n+ for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n+ newResolution = this.serverResolutions[i];\n+ newDistance = Math.abs(newResolution - resolution);\n+ if (newDistance > distance) {\n+ break;\n+ }\n+ distance = newDistance;\n+ serverResolution = newResolution;\n+ }\n+ resolution = serverResolution;\n+ }\n+ return resolution;\n+ },\n \n- var element = OpenLayers.Util.getElement(elementParam);\n- var cacheID = element._eventCacheID;\n+ /**\n+ * Method: getServerZoom\n+ * Return the zoom value corresponding to the best matching server\n+ * resolution, taking into account and .\n+ *\n+ * Returns:\n+ * {Number} The closest server supported zoom. This is not the map zoom\n+ * level, but an index of the server's resolutions array.\n+ */\n+ getServerZoom: function() {\n+ var resolution = this.getServerResolution();\n+ return this.serverResolutions ?\n+ OpenLayers.Util.indexOf(this.serverResolutions, resolution) :\n+ this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);\n+ },\n \n- if (name == 'keypress') {\n- if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n- element.detachEvent) {\n- name = 'keydown';\n+ /**\n+ * Method: applyBackBuffer\n+ * Create, insert, scale and position a back buffer for the layer.\n+ *\n+ * Parameters:\n+ * resolution - {Number} The resolution to transition to.\n+ */\n+ applyBackBuffer: function(resolution) {\n+ if (this.backBufferTimerId !== null) {\n+ this.removeBackBuffer();\n+ }\n+ var backBuffer = this.backBuffer;\n+ if (!backBuffer) {\n+ backBuffer = this.createBackBuffer();\n+ if (!backBuffer) {\n+ return;\n+ }\n+ if (resolution === this.gridResolution) {\n+ this.div.insertBefore(backBuffer, this.div.firstChild);\n+ } else {\n+ this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);\n }\n+ this.backBuffer = backBuffer;\n+\n+ // set some information in the instance for subsequent\n+ // calls to applyBackBuffer where the same back buffer\n+ // is reused\n+ var topLeftTileBounds = this.grid[0][0].bounds;\n+ this.backBufferLonLat = {\n+ lon: topLeftTileBounds.left,\n+ lat: topLeftTileBounds.top\n+ };\n+ this.backBufferResolution = this.gridResolution;\n }\n \n- // 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+ var ratio = this.backBufferResolution / resolution;\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+ // scale the tiles inside the back buffer\n+ var tiles = backBuffer.childNodes,\n+ tile;\n+ for (var i = tiles.length - 1; i >= 0; --i) {\n+ tile = tiles[i];\n+ tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';\n+ tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';\n+ tile.style.width = Math.round(ratio * tile._w) + 'px';\n+ tile.style.height = Math.round(ratio * tile._h) + 'px';\n+ }\n \n- if ((cacheEntry.name == name) &&\n- (cacheEntry.observer == observer) &&\n- (cacheEntry.useCapture == useCapture)) {\n+ // and position it (based on the grid's top-left corner)\n+ var position = this.getViewPortPxFromLonLat(\n+ this.backBufferLonLat, resolution);\n+ var leftOffset = this.map.layerContainerOriginPx.x;\n+ var topOffset = this.map.layerContainerOriginPx.y;\n+ backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';\n+ backBuffer.style.top = Math.round(position.y - topOffset) + 'px';\n+ },\n \n- elementObservers.splice(i, 1);\n- if (elementObservers.length == 0) {\n- delete OpenLayers.Event.observers[cacheID];\n+ /**\n+ * Method: createBackBuffer\n+ * Create a back buffer.\n+ *\n+ * Returns:\n+ * {DOMElement} The DOM element for the back buffer, undefined if the\n+ * grid isn't initialized yet.\n+ */\n+ createBackBuffer: function() {\n+ var backBuffer;\n+ if (this.grid.length > 0) {\n+ backBuffer = document.createElement('div');\n+ backBuffer.id = this.div.id + '_bb';\n+ backBuffer.className = 'olBackBuffer';\n+ backBuffer.style.position = 'absolute';\n+ var map = this.map;\n+ backBuffer.style.zIndex = this.transitionEffect === 'resize' ?\n+ this.getZIndex() - 1 :\n+ // 'map-resize':\n+ map.Z_INDEX_BASE.BaseLayer -\n+ (map.getNumLayers() - map.getLayerIndex(this));\n+ for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n+ for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n+ var tile = this.grid[i][j],\n+ markup = this.grid[i][j].createBackBuffer();\n+ if (markup) {\n+ markup._i = i;\n+ markup._j = j;\n+ markup._w = tile.size.w;\n+ markup._h = tile.size.h;\n+ markup.id = tile.id + '_bb';\n+ backBuffer.appendChild(markup);\n }\n- foundEntry = true;\n- break;\n }\n- i++;\n }\n }\n+ return backBuffer;\n+ },\n \n- //actually remove the event listener from browser\n- if (foundEntry) {\n- if (element.removeEventListener) {\n- element.removeEventListener(name, observer, useCapture);\n- } else if (element && element.detachEvent) {\n- element.detachEvent('on' + name, observer);\n+ /**\n+ * Method: removeBackBuffer\n+ * Remove back buffer from DOM.\n+ */\n+ removeBackBuffer: function() {\n+ if (this._transitionElement) {\n+ for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.stopObserving(this._transitionElement,\n+ this.transitionendEvents[i], this._removeBackBuffer);\n+ }\n+ delete this._transitionElement;\n+ }\n+ if (this.backBuffer) {\n+ if (this.backBuffer.parentNode) {\n+ this.backBuffer.parentNode.removeChild(this.backBuffer);\n+ }\n+ this.backBuffer = null;\n+ this.backBufferResolution = null;\n+ if (this.backBufferTimerId !== null) {\n+ window.clearTimeout(this.backBufferTimerId);\n+ this.backBufferTimerId = null;\n }\n }\n- return foundEntry;\n },\n \n- /** \n- * Method: unloadCache\n- * Cycle through all the element entries in the events cache and call\n- * stopObservingElement on each. \n+ /**\n+ * Method: moveByPx\n+ * Move the layer based on pixel vector.\n+ *\n+ * Parameters:\n+ * dx - {Number}\n+ * dy - {Number}\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+ moveByPx: function(dx, dy) {\n+ if (!this.singleTile) {\n+ this.moveGriddedTiles();\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Event\"\n-};\n+ /**\n+ * APIMethod: setTileSize\n+ * Check if we are in singleTile mode and if so, set the size as a ratio\n+ * of the map size (as specified by the layer's 'ratio' property).\n+ * \n+ * Parameters:\n+ * size - {}\n+ */\n+ setTileSize: function(size) {\n+ if (this.singleTile) {\n+ size = this.map.getSize();\n+ size.h = parseInt(size.h * this.ratio, 10);\n+ size.w = parseInt(size.w * this.ratio, 10);\n+ }\n+ OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);\n+ },\n \n-/* prevent memory leaks in IE */\n-OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);\n+ /**\n+ * APIMethod: getTilesBounds\n+ * Return the bounds of the tile grid.\n+ *\n+ * Returns:\n+ * {} A Bounds object representing the bounds of all the\n+ * currently loaded tiles (including those partially or not at all seen \n+ * onscreen).\n+ */\n+ getTilesBounds: function() {\n+ var bounds = null;\n \n-/**\n- * Class: OpenLayers.Events\n- */\n-OpenLayers.Events = OpenLayers.Class({\n+ var length = this.grid.length;\n+ if (length) {\n+ var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n+ width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n+ height = this.grid.length * bottomLeftTileBounds.getHeight();\n \n- /** \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+ bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,\n+ bottomLeftTileBounds.bottom,\n+ bottomLeftTileBounds.left + width,\n+ bottomLeftTileBounds.bottom + height);\n+ }\n+ return bounds;\n+ },\n \n- /** \n- * Property: listeners \n- * {Object} Hashtable of Array(Function): events listener functions \n+ /**\n+ * Method: initSingleTile\n+ * \n+ * Parameters: \n+ * bounds - {}\n */\n- listeners: null,\n+ initSingleTile: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\n \n- /** \n- * Property: object \n- * {Object} the code object issuing application events \n- */\n- object: null,\n+ //determine new tile bounds\n+ var center = bounds.getCenterLonLat();\n+ var tileWidth = bounds.getWidth() * this.ratio;\n+ var tileHeight = bounds.getHeight() * this.ratio;\n \n- /** \n- * Property: element \n- * {DOMElement} the DOM element receiving browser events \n- */\n- element: null,\n+ var tileBounds =\n+ new OpenLayers.Bounds(center.lon - (tileWidth / 2),\n+ center.lat - (tileHeight / 2),\n+ center.lon + (tileWidth / 2),\n+ center.lat + (tileHeight / 2));\n \n- /** \n- * Property: eventHandler \n- * {Function} bound event handler attached to elements \n- */\n- eventHandler: null,\n+ var px = this.map.getLayerPxFromLonLat({\n+ lon: tileBounds.left,\n+ lat: tileBounds.top\n+ });\n \n- /** \n- * APIProperty: fallThrough \n- * {Boolean} \n- */\n- fallThrough: null,\n+ if (!this.grid.length) {\n+ this.grid[0] = [];\n+ }\n+\n+ var tile = this.grid[0][0];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+\n+ this.addTileMonitoringHooks(tile);\n+ tile.draw();\n+ this.grid[0][0] = tile;\n+ } else {\n+ tile.moveTo(tileBounds, px);\n+ }\n+\n+ //remove all but our single tile\n+ this.removeExcessTiles(1, 1);\n+\n+ // store the resolution of the grid\n+ this.gridResolution = this.getServerResolution();\n+ },\n \n /** \n- * 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+ * Method: calculateGridLayout\n+ * Generate parameters for the grid layout.\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+ * Parameters:\n+ * bounds - {|Object} OpenLayers.Bounds or an\n+ * object with a 'left' and 'top' properties.\n+ * origin - {|Object} OpenLayers.LonLat or an\n+ * object with a 'lon' and 'lat' properties.\n+ * resolution - {Number}\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+ * Returns:\n+ * {Object} Object containing properties tilelon, tilelat, startcol,\n+ * startrow\n */\n- includeXY: false,\n+ calculateGridLayout: function(bounds, origin, resolution) {\n+ var tilelon = resolution * this.tileSize.w;\n+ var tilelat = resolution * this.tileSize.h;\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+ var offsetlon = bounds.left - origin.lon;\n+ var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\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+ var rowSign = this.rowSign;\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+ var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n+ var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign;\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+ return {\n+ tilelon: tilelon,\n+ tilelat: tilelat,\n+ startcol: tilecol,\n+ startrow: tilerow\n+ };\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+ * Method: getTileOrigin\n+ * Determine the origin for aligning the grid of tiles. If a \n+ * property is supplied, that will be returned. Otherwise, the origin\n+ * will be derived from the layer's property. In this case,\n+ * the tile origin will be the corner of the given by the \n+ * property.\n+ *\n+ * Returns:\n+ * {} The tile origin.\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+ getTileOrigin: function() {\n+ var origin = this.tileOrigin;\n+ if (!origin) {\n+ var extent = this.getMaxExtent();\n+ var edges = ({\n+ \"tl\": [\"left\", \"top\"],\n+ \"tr\": [\"right\", \"top\"],\n+ \"bl\": [\"left\", \"bottom\"],\n+ \"br\": [\"right\", \"bottom\"]\n+ })[this.tileOriginCorner];\n+ origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);\n }\n- this.element = null;\n-\n- this.listeners = null;\n- this.object = null;\n- this.fallThrough = null;\n- this.eventHandler = null;\n+ return origin;\n },\n \n /**\n- * APIMethod: addEventType\n- * Deprecated. Any event can be triggered without adding it first.\n- * \n+ * Method: getTileBoundsForGridIndex\n+ *\n * Parameters:\n- * eventName - {String}\n+ * row - {Number} The row of the grid\n+ * col - {Number} The column of the grid\n+ *\n+ * Returns:\n+ * {} The bounds for the tile at (row, col)\n */\n- addEventType: function(eventName) {},\n+ getTileBoundsForGridIndex: function(row, col) {\n+ var origin = this.getTileOrigin();\n+ var tileLayout = this.gridLayout;\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n+ var startcol = tileLayout.startcol;\n+ var startrow = tileLayout.startrow;\n+ var rowSign = this.rowSign;\n+ return new OpenLayers.Bounds(\n+ origin.lon + (startcol + col) * tilelon,\n+ origin.lat - (startrow + row * rowSign) * tilelat * rowSign,\n+ origin.lon + (startcol + col + 1) * tilelon,\n+ origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign\n+ );\n+ },\n \n /**\n- * Method: attachToElement\n- *\n+ * Method: initGriddedTiles\n+ * \n * Parameters:\n- * element - {HTMLDOMElement} a DOM element to attach browser events to\n+ * bounds - {}\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+ initGriddedTiles: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\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+ // work out mininum number of rows and columns; this is the number of\n+ // tiles required to cover the viewport plus at least one for panning\n+\n+ var viewSize = this.map.getSize();\n+\n+ var origin = this.getTileOrigin();\n+ var resolution = this.map.getResolution(),\n+ serverResolution = this.getServerResolution(),\n+ ratio = resolution / serverResolution,\n+ tileSize = {\n+ w: this.tileSize.w / ratio,\n+ h: this.tileSize.h / ratio\n+ };\n+\n+ var minRows = Math.ceil(viewSize.h / tileSize.h) +\n+ 2 * this.buffer + 1;\n+ var minCols = Math.ceil(viewSize.w / tileSize.w) +\n+ 2 * this.buffer + 1;\n+\n+ var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n+ this.gridLayout = tileLayout;\n+\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n+\n+ var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n+ var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n+\n+ var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n+ var startPx = this.map.getViewPortPxFromLonLat(\n+ new OpenLayers.LonLat(tileBounds.left, tileBounds.top)\n+ );\n+ startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n+ startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n+\n+ var tileData = [],\n+ center = this.map.getCenter();\n+\n+ var rowidx = 0;\n+ do {\n+ var row = this.grid[rowidx];\n+ if (!row) {\n+ row = [];\n+ this.grid.push(row);\n }\n+\n+ var colidx = 0;\n+ do {\n+ tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n+ var px = startPx.clone();\n+ px.x = px.x + colidx * Math.round(tileSize.w);\n+ px.y = px.y + rowidx * Math.round(tileSize.h);\n+ var tile = row[colidx];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+ this.addTileMonitoringHooks(tile);\n+ row.push(tile);\n+ } else {\n+ tile.moveTo(tileBounds, px, false);\n+ }\n+ var tileCenter = tileBounds.getCenterLonLat();\n+ tileData.push({\n+ tile: tile,\n+ distance: Math.pow(tileCenter.lon - center.lon, 2) +\n+ Math.pow(tileCenter.lat - center.lat, 2)\n+ });\n+\n+ colidx += 1;\n+ } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) ||\n+ colidx < minCols);\n+\n+ rowidx += 1;\n+ } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) ||\n+ rowidx < minRows);\n+\n+ //shave off exceess rows and colums\n+ this.removeExcessTiles(rowidx, colidx);\n+\n+ var resolution = this.getServerResolution();\n+ // store the resolution of the grid\n+ this.gridResolution = resolution;\n+\n+ //now actually draw the tiles\n+ tileData.sort(function(a, b) {\n+ return a.distance - b.distance;\n+ });\n+ for (var i = 0, ii = tileData.length; i < ii; ++i) {\n+ tileData[i].tile.draw();\n }\n- // 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+ * Method: getMaxExtent\n+ * Get this layer's maximum extent. (Implemented as a getter for\n+ * potential specific implementations in sub-classes.)\n *\n- * Parameters:\n- * object - {Object} \n+ * Returns:\n+ * {}\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+ getMaxExtent: function() {\n+ return this.maxExtent;\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+ * APIMethod: addTile\n+ * Create a tile, initialize it, and add it to the layer div. \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+ * Parameters\n+ * bounds - {}\n+ * position - {}\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+ * Returns:\n+ * {} The added OpenLayers.Tile\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+ addTile: function(bounds, position) {\n+ var tile = new this.tileClass(\n+ this, position, bounds, null, this.tileSize, this.tileOptions\n+ );\n+ this.events.triggerEvent(\"addtile\", {\n+ tile: tile\n+ });\n+ return tile;\n+ },\n+\n+ /** \n+ * Method: addTileMonitoringHooks\n+ * This function takes a tile as input and adds the appropriate hooks to \n+ * the tile so that the layer can keep track of the loading tiles.\n+ * \n+ * Parameters: \n+ * tile - {}\n+ */\n+ addTileMonitoringHooks: function(tile) {\n+\n+ var replacingCls = 'olTileReplacing';\n+\n+ tile.onLoadStart = function() {\n+ //if that was first tile then trigger a 'loadstart' on the layer\n+ if (this.loading === false) {\n+ this.loading = true;\n+ this.events.triggerEvent(\"loadstart\");\n }\n- var listeners = this.listeners[type];\n- if (!listeners) {\n- listeners = [];\n- this.listeners[type] = listeners;\n- this.extensionCount[type] = 0;\n+ this.events.triggerEvent(\"tileloadstart\", {\n+ tile: tile\n+ });\n+ this.numLoadingTiles++;\n+ if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ OpenLayers.Element.addClass(tile.getTile(), replacingCls);\n }\n- 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+\n+ tile.onLoadEnd = function(evt) {\n+ this.numLoadingTiles--;\n+ var aborted = evt.type === 'unload';\n+ this.events.triggerEvent(\"tileloaded\", {\n+ tile: tile,\n+ aborted: aborted\n+ });\n+ if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ var tileDiv = tile.getTile();\n+ if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {\n+ var bufferTile = document.getElementById(tile.id + '_bb');\n+ if (bufferTile) {\n+ bufferTile.parentNode.removeChild(bufferTile);\n+ }\n+ }\n+ OpenLayers.Element.removeClass(tileDiv, replacingCls);\n+ }\n+ //if that was the last tile, then trigger a 'loadend' on the layer\n+ if (this.numLoadingTiles === 0) {\n+ if (this.backBuffer) {\n+ if (this.backBuffer.childNodes.length === 0) {\n+ // no tiles transitioning, remove immediately\n+ this.removeBackBuffer();\n+ } else {\n+ // wait until transition has ended or delay has passed\n+ this._transitionElement = aborted ?\n+ this.div.lastChild : tile.imgDiv;\n+ var transitionendEvents = this.transitionendEvents;\n+ for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.observe(this._transitionElement,\n+ transitionendEvents[i],\n+ this._removeBackBuffer);\n+ }\n+ // the removal of the back buffer is delayed to prevent\n+ // flash effects due to the animation of tile displaying\n+ this.backBufferTimerId = window.setTimeout(\n+ this._removeBackBuffer, this.removeBackBufferDelay\n+ );\n+ }\n }\n+ this.loading = false;\n+ this.events.triggerEvent(\"loadend\");\n+ }\n+ };\n+\n+ tile.onLoadError = function() {\n+ this.events.triggerEvent(\"tileerror\", {\n+ tile: tile\n+ });\n+ };\n+\n+ tile.events.on({\n+ \"loadstart\": tile.onLoadStart,\n+ \"loadend\": tile.onLoadEnd,\n+ \"unload\": tile.onLoadEnd,\n+ \"loaderror\": tile.onLoadError,\n+ scope: this\n+ });\n+ },\n+\n+ /** \n+ * Method: removeTileMonitoringHooks\n+ * This function takes a tile as input and removes the tile hooks \n+ * that were added in addTileMonitoringHooks()\n+ * \n+ * Parameters: \n+ * tile - {}\n+ */\n+ removeTileMonitoringHooks: function(tile) {\n+ tile.unload();\n+ tile.events.un({\n+ \"loadstart\": tile.onLoadStart,\n+ \"loadend\": tile.onLoadEnd,\n+ \"unload\": tile.onLoadEnd,\n+ \"loaderror\": tile.onLoadError,\n+ scope: this\n+ });\n+ },\n+\n+ /**\n+ * Method: moveGriddedTiles\n+ */\n+ moveGriddedTiles: function() {\n+ var buffer = this.buffer + 1;\n+ while (true) {\n+ var tlTile = this.grid[0][0];\n+ var tlViewPort = {\n+ x: tlTile.position.x +\n+ this.map.layerContainerOriginPx.x,\n+ y: tlTile.position.y +\n+ this.map.layerContainerOriginPx.y\n+ };\n+ var ratio = this.getServerResolution() / this.map.getResolution();\n+ var tileSize = {\n+ w: Math.round(this.tileSize.w * ratio),\n+ h: Math.round(this.tileSize.h * ratio)\n+ };\n+ if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n+ this.shiftColumn(true, tileSize);\n+ } else if (tlViewPort.x < -tileSize.w * buffer) {\n+ this.shiftColumn(false, tileSize);\n+ } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n+ this.shiftRow(true, tileSize);\n+ } else if (tlViewPort.y < -tileSize.h * buffer) {\n+ this.shiftRow(false, tileSize);\n } else {\n- listeners.push(listener);\n+ break;\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+ * Method: shiftRow\n+ * Shifty grid work\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+ * prepend - {Boolean} if true, prepend to beginning.\n+ * if false, then append to end\n+ * tileSize - {Object} rendered tile size; object with w and h properties\n */\n- registerPriority: function(type, obj, func) {\n- this.register(type, obj, func, true);\n+ shiftRow: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var rowIndex = prepend ? 0 : (grid.length - 1);\n+ var sign = prepend ? -1 : 1;\n+ var rowSign = this.rowSign;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startrow += sign * rowSign;\n+\n+ var modelRow = grid[rowIndex];\n+ var row = grid[prepend ? 'pop' : 'shift']();\n+ for (var i = 0, len = row.length; i < len; i++) {\n+ var tile = row[i];\n+ var position = modelRow[i].position.clone();\n+ position.y += tileSize.h * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);\n+ }\n+ grid[prepend ? 'unshift' : 'push'](row);\n },\n \n /**\n- * 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+ * Method: shiftColumn\n+ * Shift grid work in the other dimension\n *\n- * // this is equivalent to the following\n- * events.unregister(\"loadstart\", object, loadStartListener);\n- * events.unregister(\"loadend\", object, loadEndListener);\n- * (end)\n+ * Parameters:\n+ * prepend - {Boolean} if true, prepend to beginning.\n+ * if false, then append to end\n+ * tileSize - {Object} rendered tile size; object with w and h properties\n */\n- 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+ shiftColumn: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var colIndex = prepend ? 0 : (grid[0].length - 1);\n+ var sign = prepend ? -1 : 1;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startcol += sign;\n+\n+ for (var i = 0, len = grid.length; i < len; i++) {\n+ var row = grid[i];\n+ var position = row[colIndex].position.clone();\n+ var tile = row[prepend ? 'pop' : 'shift']();\n+ position.x += tileSize.w * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n+ row[prepend ? 'unshift' : 'push'](tile);\n }\n },\n \n /**\n- * APIMethod: unregister\n- *\n+ * Method: removeExcessTiles\n+ * When the size of the map or the buffer changes, we may need to\n+ * remove some excess rows and columns.\n+ * \n * Parameters:\n- * type - {String} \n- * obj - {Object} If none specified, defaults to this.object\n- * func - {Function} \n+ * rows - {Integer} Maximum number of rows we want our grid to have.\n+ * columns - {Integer} Maximum number of columns we want our grid to have.\n */\n- unregister: function(type, obj, func) {\n- if (obj == null) {\n- obj = this.object;\n+ removeExcessTiles: function(rows, columns) {\n+ var i, l;\n+\n+ // remove extra rows\n+ while (this.grid.length > rows) {\n+ var row = this.grid.pop();\n+ for (i = 0, l = row.length; i < l; i++) {\n+ var tile = row[i];\n+ this.destroyTile(tile);\n+ }\n }\n- 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+ // remove extra columns\n+ for (i = 0, l = this.grid.length; i < l; i++) {\n+ while (this.grid[i].length > columns) {\n+ var row = this.grid[i];\n+ var tile = row.pop();\n+ this.destroyTile(tile);\n }\n }\n },\n \n- /** \n- * Method: 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+ * Method: onMapResize\n+ * For singleTile layers, this will set a new tile size according to the\n+ * dimensions of the map pane.\n */\n- remove: function(type) {\n- if (this.listeners[type] != null) {\n- this.listeners[type] = [];\n+ onMapResize: function() {\n+ if (this.singleTile) {\n+ this.clearGrid();\n+ this.setTileSize();\n }\n },\n \n /**\n- * APIMethod: triggerEvent\n- * Trigger a specified registered event. \n- * \n+ * APIMethod: getTileBounds\n+ * Returns The tile bounds for a layer given a pixel location.\n+ *\n * Parameters:\n- * type - {String} \n- * evt - {Event || Object} will be passed to the listeners.\n+ * viewPortPx - {} The location in the viewport.\n *\n * Returns:\n- * {Boolean} The last listener return. If a listener returns false, the\n- * chain of listeners will stop getting called.\n+ * {} Bounds of the tile at the given pixel location.\n */\n- triggerEvent: function(type, evt) {\n- var listeners = this.listeners[type];\n+ getTileBounds: function(viewPortPx) {\n+ var maxExtent = this.maxExtent;\n+ var resolution = this.getResolution();\n+ var tileMapWidth = resolution * this.tileSize.w;\n+ var tileMapHeight = resolution * this.tileSize.h;\n+ var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n+ var tileLeft = maxExtent.left + (tileMapWidth *\n+ Math.floor((mapPoint.lon -\n+ maxExtent.left) /\n+ tileMapWidth));\n+ var tileBottom = maxExtent.bottom + (tileMapHeight *\n+ Math.floor((mapPoint.lat -\n+ maxExtent.bottom) /\n+ tileMapHeight));\n+ return new OpenLayers.Bounds(tileLeft, tileBottom,\n+ tileLeft + tileMapWidth,\n+ tileBottom + tileMapHeight);\n+ },\n \n- // fast path\n- if (!listeners || listeners.length == 0) {\n- return undefined;\n- }\n+ CLASS_NAME: \"OpenLayers.Layer.Grid\"\n+});\n+/* ======================================================================\n+ OpenLayers/TileManager.js\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+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\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+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/BaseTypes.js\n+ * @requires OpenLayers/BaseTypes/Element.js\n+ * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/Tile/Image.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.TileManager\n+ * Provides queueing of image requests and caching of image elements.\n+ *\n+ * Queueing avoids unnecessary image requests while changing zoom levels\n+ * quickly, and helps improve dragging performance on mobile devices that show\n+ * a lag in dragging when loading of new images starts. and\n+ * are the configuration options to control this behavior.\n+ *\n+ * Caching avoids setting the src on image elements for images that have already\n+ * been used. Several maps can share a TileManager instance, in which case each\n+ * map gets its own tile queue, but all maps share the same tile cache.\n+ */\n+OpenLayers.TileManager = OpenLayers.Class({\n+\n+ /**\n+ * APIProperty: cacheSize\n+ * {Number} Number of image elements to keep referenced in this instance's\n+ * cache for fast reuse. Default is 256.\n+ */\n+ cacheSize: 256,\n+\n+ /**\n+ * APIProperty: tilesPerFrame\n+ * {Number} Number of queued tiles to load per frame (see ).\n+ * Default is 2.\n+ */\n+ tilesPerFrame: 2,\n+\n+ /**\n+ * APIProperty: frameDelay\n+ * {Number} Delay between tile loading frames (see ) in\n+ * milliseconds. Default is 16.\n+ */\n+ frameDelay: 16,\n+\n+ /**\n+ * APIProperty: moveDelay\n+ * {Number} Delay in milliseconds after a map's move event before loading\n+ * tiles. Default is 100.\n+ */\n+ moveDelay: 100,\n+\n+ /**\n+ * APIProperty: zoomDelay\n+ * {Number} Delay in milliseconds after a map's zoomend event before loading\n+ * tiles. Default is 200.\n+ */\n+ zoomDelay: 200,\n+\n+ /**\n+ * Property: maps\n+ * {Array()} The maps to manage tiles on.\n+ */\n+ maps: null,\n+\n+ /**\n+ * Property: tileQueueId\n+ * {Object} The ids of the loop, keyed by map id.\n+ */\n+ tileQueueId: null,\n+\n+ /**\n+ * Property: tileQueue\n+ * {Object(Array())} Tiles queued for drawing, keyed by\n+ * map id.\n+ */\n+ tileQueue: null,\n+\n+ /**\n+ * Property: tileCache\n+ * {Object} Cached image elements, keyed by URL.\n+ */\n+ tileCache: null,\n+\n+ /**\n+ * Property: tileCacheIndex\n+ * {Array(String)} URLs of cached tiles. First entry is the least recently\n+ * used.\n+ */\n+ tileCacheIndex: null,\n+\n+ /** \n+ * Constructor: OpenLayers.TileManager\n+ * Constructor for a new instance.\n+ * \n+ * Parameters:\n+ * options - {Object} Configuration for this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.maps = [];\n+ this.tileQueueId = {};\n+ this.tileQueue = {};\n+ this.tileCache = {};\n+ this.tileCacheIndex = [];\n+ },\n+\n+ /**\n+ * Method: addMap\n+ * Binds this instance to a map\n+ *\n+ * Parameters:\n+ * map - {}\n+ */\n+ addMap: function(map) {\n+ if (this._destroyed || !OpenLayers.Layer.Grid) {\n+ return;\n }\n- // don't fall through to other DOM elements\n- if (!this.fallThrough) {\n- OpenLayers.Event.stop(evt, true);\n+ this.maps.push(map);\n+ this.tileQueue[map.id] = [];\n+ for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n+ this.addLayer({\n+ layer: map.layers[i]\n+ });\n }\n- return continueChain;\n+ map.events.on({\n+ move: this.move,\n+ zoomend: this.zoomEnd,\n+ changelayer: this.changeLayer,\n+ addlayer: this.addLayer,\n+ preremovelayer: this.removeLayer,\n+ scope: this\n+ });\n },\n \n /**\n- * Method: 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+ * Method: removeMap\n+ * Unbinds this instance from a map\n *\n * Parameters:\n- * evt - {Event} \n+ * map - {}\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+ removeMap: function(map) {\n+ if (this._destroyed || !OpenLayers.Layer.Grid) {\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+ window.clearTimeout(this.tileQueueId[map.id]);\n+ if (map.layers) {\n+ for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n+ this.removeLayer({\n+ layer: map.layers[i]\n+ });\n }\n- evt.clientX = x / num;\n- evt.clientY = y / num;\n }\n- if (this.includeXY) {\n- evt.xy = this.getMousePosition(evt);\n+ if (map.events) {\n+ map.events.un({\n+ move: this.move,\n+ zoomend: this.zoomEnd,\n+ changelayer: this.changeLayer,\n+ addlayer: this.addLayer,\n+ preremovelayer: this.removeLayer,\n+ scope: this\n+ });\n }\n- this.triggerEvent(type, evt);\n+ delete this.tileQueue[map.id];\n+ delete this.tileQueueId[map.id];\n+ OpenLayers.Util.removeItem(this.maps, map);\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+ * Method: move\n+ * Handles the map's move event\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+ * evt - {Object} Listener argument\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+ move: function(evt) {\n+ this.updateTimeout(evt.object, this.moveDelay, true);\n },\n \n /**\n- * APIMethod: clearMouseCache\n- * Clear cached data about the mouse position. This should be called any \n- * time the element that events are registered on changes position \n- * within the page.\n+ * Method: zoomEnd\n+ * Handles the map's zoomEnd event\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument\n */\n- clearMouseCache: function() {\n- this.element.scrolls = null;\n- this.element.lefttop = null;\n- this.element.offsets = null;\n+ zoomEnd: function(evt) {\n+ this.updateTimeout(evt.object, this.zoomDelay);\n },\n \n /**\n- * Method: getMousePosition\n- * \n+ * Method: changeLayer\n+ * Handles the map's changeLayer event\n+ *\n * Parameters:\n- * evt - {Event} \n- * \n- * Returns:\n- * {} The current xy coordinate of the mouse, adjusted\n- * for offsets\n+ * evt - {Object} Listener argument\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+ changeLayer: function(evt) {\n+ if (evt.property === 'visibility' || evt.property === 'params') {\n+ this.updateTimeout(evt.object, 0);\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+ * Method: addLayer\n+ * Handles the map's addlayer event\n+ *\n+ * Parameters:\n+ * evt - {Object} The listener argument\n+ */\n+ addLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (layer instanceof OpenLayers.Layer.Grid) {\n+ layer.events.on({\n+ addtile: this.addTile,\n+ retile: this.clearTileQueue,\n+ scope: this\n+ });\n+ var i, j, tile;\n+ for (i = layer.grid.length - 1; i >= 0; --i) {\n+ for (j = layer.grid[i].length - 1; j >= 0; --j) {\n+ tile = layer.grid[i][j];\n+ this.addTile({\n+ tile: tile\n+ });\n+ if (tile.url && !tile.imgDiv) {\n+ this.manageTileCache({\n+ object: tile\n+ });\n+ }\n+ }\n+ }\n }\n+ },\n \n- if (!this.element.offsets) {\n- this.element.offsets = OpenLayers.Util.pagePosition(this.element);\n+ /**\n+ * Method: removeLayer\n+ * Handles the map's preremovelayer event\n+ *\n+ * Parameters:\n+ * evt - {Object} The listener argument\n+ */\n+ removeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (layer instanceof OpenLayers.Layer.Grid) {\n+ this.clearTileQueue({\n+ object: layer\n+ });\n+ if (layer.events) {\n+ layer.events.un({\n+ addtile: this.addTile,\n+ retile: this.clearTileQueue,\n+ scope: this\n+ });\n+ }\n+ if (layer.grid) {\n+ var i, j, tile;\n+ for (i = layer.grid.length - 1; i >= 0; --i) {\n+ for (j = layer.grid[i].length - 1; j >= 0; --j) {\n+ tile = layer.grid[i][j];\n+ this.unloadTile({\n+ object: tile\n+ });\n+ }\n+ }\n+ }\n }\n-\n- 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+ * Method: updateTimeout\n+ * Applies the or to the loop,\n+ * and schedules more queue processing after if there are still\n+ * tiles in the queue.\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+ * map - {} The map to update the timeout for\n+ * delay - {Number} The delay to apply\n+ * nice - {Boolean} If true, the timeout function will only be created if\n+ * the tilequeue is not empty. This is used by the move handler to\n+ * avoid impacts on dragging performance. For other events, the tile\n+ * queue may not be populated yet, so we need to set the timer\n+ * regardless of the queue size.\n */\n- 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+ updateTimeout: function(map, delay, nice) {\n+ window.clearTimeout(this.tileQueueId[map.id]);\n+ var tileQueue = this.tileQueue[map.id];\n+ if (!nice || tileQueue.length) {\n+ this.tileQueueId[map.id] = window.setTimeout(\n+ OpenLayers.Function.bind(function() {\n+ this.drawTilesFromQueue(map);\n+ if (tileQueue.length) {\n+ this.updateTimeout(map, this.frameDelay);\n }\n- },\n- type: type\n- }, evt));\n+ }, this), delay\n+ );\n }\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+ * Method: addTile\n+ * Listener for the layer's addtile event\n+ *\n+ * Parameters:\n+ * evt - {Object} The listener argument\n+ */\n+ addTile: function(evt) {\n+ if (evt.tile instanceof OpenLayers.Tile.Image) {\n+ evt.tile.events.on({\n+ beforedraw: this.queueTileDraw,\n+ beforeload: this.manageTileCache,\n+ loadend: this.addToCache,\n+ unload: this.unloadTile,\n+ scope: this\n+ });\n+ } else {\n+ // Layer has the wrong tile type, so don't handle it any longer\n+ this.removeLayer({\n+ layer: evt.tile.layer\n+ });\n }\n },\n \n /**\n- * Method: addMsTouchListenerStart\n+ * Method: unloadTile\n+ * Listener for the tile's unload event\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+ * evt - {Object} The listener argument\n */\n- addMsTouchListenerStart: function(element, type, handler) {\n- var touches = this._msTouches;\n-\n- var cb = function(e) {\n+ unloadTile: function(evt) {\n+ var tile = evt.object;\n+ tile.events.un({\n+ beforedraw: this.queueTileDraw,\n+ beforeload: this.manageTileCache,\n+ loadend: this.addToCache,\n+ unload: this.unloadTile,\n+ scope: this\n+ });\n+ OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile);\n+ },\n \n- 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+ * Method: queueTileDraw\n+ * Adds a tile to the queue that will draw it.\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument of the tile's beforedraw event\n+ */\n+ queueTileDraw: function(evt) {\n+ var tile = evt.object;\n+ var queued = false;\n+ var layer = tile.layer;\n+ var url = layer.getURL(tile.bounds);\n+ var img = this.tileCache[url];\n+ if (img && img.className !== 'olTileImage') {\n+ // cached image no longer valid, e.g. because we're olTileReplacing\n+ delete this.tileCache[url];\n+ OpenLayers.Util.removeItem(this.tileCacheIndex, url);\n+ img = null;\n+ }\n+ // queue only if image with same url not cached already\n+ if (layer.url && (layer.async || !img)) {\n+ // add to queue only if not in queue already\n+ var tileQueue = this.tileQueue[layer.map.id];\n+ if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {\n+ tileQueue.push(tile);\n }\n+ queued = true;\n+ }\n+ return !queued;\n+ },\n \n- 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+ * Method: drawTilesFromQueue\n+ * Draws tiles from the tileQueue, and unqueues the tiles\n+ */\n+ drawTilesFromQueue: function(map) {\n+ var tileQueue = this.tileQueue[map.id];\n+ var limit = this.tilesPerFrame;\n+ var animating = map.zoomTween && map.zoomTween.playing;\n+ while (!animating && tileQueue.length && limit) {\n+ tileQueue.shift().draw(true);\n+ --limit;\n+ }\n },\n \n /**\n- * Method: addMsTouchListenerMove\n+ * Method: manageTileCache\n+ * Adds, updates, removes and fetches cache entries.\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+ * evt - {Object} Listener argument of the tile's beforeload event\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+ manageTileCache: function(evt) {\n+ var tile = evt.object;\n+ var img = this.tileCache[tile.url];\n+ if (img) {\n+ // if image is on its layer's backbuffer, remove it from backbuffer\n+ if (img.parentNode &&\n+ OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) {\n+ img.parentNode.removeChild(img);\n+ img.id = null;\n }\n- 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+ // only use image from cache if it is not on a layer already\n+ if (!img.parentNode) {\n+ img.style.visibility = 'hidden';\n+ img.style.opacity = 0;\n+ tile.setImage(img);\n+ // LRU - move tile to the end of the array to mark it as the most\n+ // recently used\n+ OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);\n+ this.tileCacheIndex.push(tile.url);\n }\n-\n- e.touches = touches.slice();\n- handler(e);\n- };\n-\n- OpenLayers.Event.observe(element, 'MSPointerMove', cb);\n+ }\n },\n \n /**\n- * Method: addMsTouchListenerEnd\n+ * Method: addToCache\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+ * evt - {Object} Listener argument for the tile's loadend event\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+ addToCache: function(evt) {\n+ var tile = evt.object;\n+ if (!this.tileCache[tile.url]) {\n+ if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) {\n+ if (this.tileCacheIndex.length >= this.cacheSize) {\n+ delete this.tileCache[this.tileCacheIndex[0]];\n+ this.tileCacheIndex.shift();\n }\n+ this.tileCache[tile.url] = tile.imgDiv;\n+ this.tileCacheIndex.push(tile.url);\n }\n+ }\n+ },\n \n- e.touches = touches.slice();\n- handler(e);\n- };\n-\n- OpenLayers.Event.observe(element, 'MSPointerUp', cb);\n+ /**\n+ * Method: clearTileQueue\n+ * Clears the tile queue from tiles of a specific layer\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument of the layer's retile event\n+ */\n+ clearTileQueue: function(evt) {\n+ var layer = evt.object;\n+ var tileQueue = this.tileQueue[layer.map.id];\n+ for (var i = tileQueue.length - 1; i >= 0; --i) {\n+ if (tileQueue[i].layer === layer) {\n+ tileQueue.splice(i, 1);\n+ }\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Events\"\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ for (var i = this.maps.length - 1; i >= 0; --i) {\n+ this.removeMap(this.maps[i]);\n+ }\n+ this.maps = null;\n+ this.tileQueue = null;\n+ this.tileQueueId = null;\n+ this.tileCache = null;\n+ this.tileCacheIndex = null;\n+ this._destroyed = true;\n+ }\n+\n });\n /* ======================================================================\n OpenLayers/Request/XMLHttpRequest.js\n ====================================================================== */\n \n // XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)\n //\n@@ -19738,9893 +17145,14761 @@\n method: \"OPTIONS\"\n });\n return OpenLayers.Request.issue(config);\n }\n \n });\n /* ======================================================================\n- OpenLayers/WPSProcess.js\n+ OpenLayers/Geometry.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/SingleFile.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * @requires OpenLayers/Geometry.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Format/WKT.js\n- * @requires OpenLayers/Format/GeoJSON.js\n- * @requires OpenLayers/Format/WPSExecute.js\n- * @requires OpenLayers/Request.js\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- * Class: OpenLayers.WPSProcess\n- * Representation of a WPS process. Usually instances of\n- * are created by calling 'getProcess' on an\n- * instance.\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- * Currently supports processes that have geometries\n- * or features as output, using WKT or GeoJSON as output format. It also\n- * supports chaining of processes by using the method to create a\n- * handle that is used as process input instead of a static value.\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.WPSProcess = OpenLayers.Class({\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/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: client\n- * {} The client that manages this process.\n+ * Property: options\n+ * {Object} A reference to options passed to the constructor.\n */\n- client: null,\n+ options: null,\n \n /**\n- * Property: server\n- * {String} Local client identifier for this process's server.\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- server: null,\n+ externalProjection: null,\n \n /**\n- * Property: identifier\n- * {String} Process identifier known to the server.\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- identifier: null,\n+ internalProjection: null,\n \n /**\n- * Property: description\n- * {Object} DescribeProcess response for this process.\n+ * APIProperty: data\n+ * {Object} When is true, this is the parsed string sent to\n+ * .\n */\n- description: null,\n+ data: null,\n \n /**\n- * APIProperty: localWPS\n- * {String} Service endpoint for locally chained WPS processes. Default is\n- * 'http://geoserver/wps'.\n+ * APIProperty: keepData\n+ * {Object} Maintain a reference () to the most recently read data.\n+ * Default is false.\n */\n- localWPS: 'http://geoserver/wps',\n+ keepData: false,\n \n /**\n- * Property: formats\n- * {Object} OpenLayers.Format instances keyed by mimetype.\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- formats: null,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n+ },\n \n /**\n- * Property: chained\n- * {Integer} Number of chained processes for pending execute requests that\n- * don't have a full configuration yet.\n+ * APIMethod: destroy\n+ * Clean up.\n */\n- chained: 0,\n+ destroy: function() {},\n \n /**\n- * Property: executeCallbacks\n- * {Array} Callbacks waiting to be executed until all chained processes\n- * are configured;\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- executeCallbacks: null,\n+ read: function(data) {\n+ throw new Error('Read not implemented.');\n+ },\n \n /**\n- * Constructor: OpenLayers.WPSProcess\n+ * Method: write\n+ * Accept an object, and return a string. \n *\n * Parameters:\n- * options - {Object} Object whose properties will be set on the instance.\n+ * object - {Object} Object to be serialized\n *\n- * Avaliable options:\n- * client - {} Mandatory. Client that manages this\n- * process.\n- * server - {String} Mandatory. Local client identifier of this process's\n- * server.\n- * identifier - {String} Mandatory. Process identifier known to the server.\n+ * Returns:\n+ * {String} A string representation of the object.\n */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.executeCallbacks = [];\n- this.formats = {\n- 'application/wkt': new OpenLayers.Format.WKT(),\n- 'application/json': new OpenLayers.Format.GeoJSON()\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- * Method: describe\n- * Makes the client issue a DescribeProcess request asynchronously.\n+ * Constructor: OpenLayers.Geometry.Point\n+ * Construct a point geometry.\n *\n * Parameters:\n- * options - {Object} Configuration for the method call\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- * Available options:\n- * callback - {Function} Callback to execute when the description is\n- * available. Will be called with the parsed description as argument.\n- * Optional.\n- * scope - {Object} The scope in which the callback will be executed.\n- * Default is the global object.\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- describe: function(options) {\n- options = options || {};\n- if (!this.description) {\n- this.client.describeProcess(this.server, this.identifier, function(description) {\n- if (!this.description) {\n- this.parseDescription(description);\n- }\n- if (options.callback) {\n- options.callback.call(options.scope, this.description);\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- }, this);\n- } else if (options.callback) {\n- var description = this.description;\n- window.setTimeout(function() {\n- options.callback.call(options.scope, description);\n- }, 0);\n+ component.parent = this;\n+ this.clearBounds();\n+ added = true;\n+ }\n }\n+ return added;\n },\n \n /**\n- * APIMethod: configure\n- * Configure the process, but do not execute it. Use this for processes\n- * that are chained as input of a different process by means of the\n- * method.\n+ * APIMethod: removeComponents\n+ * Remove components from this geometry.\n *\n * Parameters:\n- * options - {Object}\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- * {} this process.\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- * Available options:\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- * callback - {Function} Callback to call when the configuration is\n- * complete. Optional.\n- * scope - {Object} Optional scope for the callback.\n+ * Returns:\n+ * {Float} The area of the collection by summing its parts\n */\n- configure: function(options) {\n- this.describe({\n- callback: function() {\n- var description = this.description,\n- inputs = options.inputs,\n- input, i, ii;\n- for (i = 0, ii = description.dataInputs.length; i < ii; ++i) {\n- input = description.dataInputs[i];\n- this.setInputData(input, inputs[input.identifier]);\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- if (options.callback) {\n- options.callback.call(options.scope);\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- scope: this\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: execute\n- * Configures and executes the process\n+ * APIMethod: intersects\n+ * Determine if the input geometry intersects this one.\n *\n * Parameters:\n- * options - {Object}\n+ * geometry - {} Any type of geometry.\n *\n- * Available options:\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 the output to request and parse.\n- * Optional. If not provided, the first output will be requested.\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- * (or 'result' if output was not configured). For processes that\n- * generate spatial output, the value will be an array of\n- * instances.\n- * scope - {Object} Optional scope for the success callback.\n+ * Returns:\n+ * {Boolean} The input geometry intersects this one.\n */\n- execute: function(options) {\n- this.configure({\n- inputs: options.inputs,\n- callback: function() {\n- var me = this;\n- //TODO For now we only deal with a single output\n- var outputIndex = this.getOutputIndex(\n- me.description.processOutputs, options.output\n- );\n- me.setResponseForm({\n- outputIndex: outputIndex\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- (function callback() {\n- OpenLayers.Util.removeItem(me.executeCallbacks, callback);\n- if (me.chained !== 0) {\n- // need to wait until chained processes have a\n- // description and configuration - see chainProcess\n- me.executeCallbacks.push(callback);\n- return;\n- }\n- // all chained processes are added as references now, so\n- // let's proceed.\n- OpenLayers.Request.POST({\n- url: me.client.servers[me.server].url,\n- data: new OpenLayers.Format.WPSExecute().write(me.description),\n- success: function(response) {\n- var output = me.description.processOutputs[outputIndex];\n- var mimeType = me.findMimeType(\n- output.complexOutput.supported.formats\n- );\n- //TODO For now we assume a spatial output\n- var features = me.formats[mimeType].read(response.responseText);\n- if (features instanceof OpenLayers.Feature.Vector) {\n- features = [features];\n- }\n- if (options.success) {\n- var outputs = {};\n- outputs[options.output || 'result'] = features;\n- options.success.call(options.scope, outputs);\n- }\n- },\n- scope: me\n- });\n- })();\n- },\n- scope: this\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: output\n- * Chain an output of a configured process (see ) as input to\n- * another process.\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- * (code)\n- * intersect = client.getProcess('opengeo', 'JTS:intersection'); \n- * intersect.configure({\n- * // ...\n- * });\n- * buffer = client.getProcess('opengeo', 'JTS:buffer');\n- * buffer.execute({\n- * inputs: {\n- * geom: intersect.output('result'), // <-- here we're chaining\n- * distance: 1\n- * },\n- * // ...\n- * });\n- * (end)\n+ * Parameters: \n+ * point - {} The point to be removed\n *\n- * Parameters:\n- * identifier - {String} Identifier of the output that we're chaining. If\n- * not provided, the first output will be used.\n+ * Returns: \n+ * {Boolean} The component was removed.\n */\n- output: function(identifier) {\n- return new OpenLayers.WPSProcess.ChainLink({\n- process: this,\n- output: identifier\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- * Method: parseDescription\n- * Parses the DescribeProcess response\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- * description - {Object}\n+ * geometry - {}\n+ *\n+ * Returns:\n+ * {Boolean} The input geometry intersects this geometry.\n */\n- parseDescription: function(description) {\n- var server = this.client.servers[this.server];\n- this.description = new OpenLayers.Format.WPSDescribeProcess()\n- .read(server.processDescription[this.identifier])\n- .processDescriptions[this.identifier];\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: setInputData\n- * Sets the data for a single input\n+ * Method: getSortedSegments\n *\n- * Parameters:\n- * input - {Object} An entry from the dataInputs array of the process\n- * description.\n- * data - {Mixed} For spatial data inputs, this is usually an\n- * , an or an array of\n- * geometries or features.\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- setInputData: function(input, data) {\n- // clear any previous data\n- delete input.data;\n- delete input.reference;\n- if (data instanceof OpenLayers.WPSProcess.ChainLink) {\n- ++this.chained;\n- input.reference = {\n- method: 'POST',\n- href: data.process.server === this.server ?\n- this.localWPS : this.client.servers[data.process.server].url\n- };\n- data.process.describe({\n- callback: function() {\n- --this.chained;\n- this.chainProcess(input, data);\n- },\n- scope: this\n- });\n- } else {\n- input.data = {};\n- var complexData = input.complexData;\n- if (complexData) {\n- var format = this.findMimeType(complexData.supported.formats);\n- input.data.complexData = {\n- mimeType: format,\n- value: this.formats[format].write(this.toFeatures(data))\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- input.data.literalData = {\n- value: data\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: setResponseForm\n- * Sets the responseForm property of the payload.\n+ * Method: splitWithSegment\n+ * Split this geometry with the given segment.\n *\n * Parameters:\n- * options - {Object} See below.\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- * Available options:\n- * outputIndex - {Integer} The index of the output to use. Optional.\n- * supportedFormats - {Object} Object with supported mime types as key,\n- * and true as value for supported types. Optional.\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- setResponseForm: function(options) {\n- options = options || {};\n- var output = this.description.processOutputs[options.outputIndex || 0];\n- this.description.responseForm = {\n- rawDataOutput: {\n- identifier: output.identifier,\n- mimeType: this.findMimeType(output.complexOutput.supported.formats, options.supportedFormats)\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: getOutputIndex\n- * Gets the index of a processOutput by its identifier\n- *\n+ * Method: split\n+ * Use this geometry (the source) to attempt to split a target geometry.\n+ * \n * Parameters:\n- * outputs - {Array} The processOutputs array to look at\n- * identifier - {String} The identifier of the output\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- * Returns\n- * {Integer} The index of the processOutput with the provided identifier\n- * in the outputs array.\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- getOutputIndex: function(outputs, identifier) {\n- var output;\n- if (identifier) {\n- for (var i = outputs.length - 1; i >= 0; --i) {\n- if (outputs[i].identifier === identifier) {\n- output = i;\n- break;\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- output = 0;\n+ results = target.splitWith(this, options);\n }\n- return output;\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: chainProcess\n- * Sets a fully configured chained process as input for this process.\n+ * Method: splitWith\n+ * Split this geometry (the target) with the given geometry (the source).\n *\n * Parameters:\n- * input - {Object} The dataInput that the chained process provides.\n- * chainLink - {} The process to chain.\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- chainProcess: function(input, chainLink) {\n- var output = this.getOutputIndex(\n- chainLink.process.description.processOutputs, chainLink.output\n- );\n- input.reference.mimeType = this.findMimeType(\n- input.complexData.supported.formats,\n- chainLink.process.description.processOutputs[output].complexOutput.supported.formats\n- );\n- var formats = {};\n- formats[input.reference.mimeType] = true;\n- chainLink.process.setResponseForm({\n- outputIndex: output,\n- supportedFormats: formats\n- });\n- input.reference.body = chainLink.process.description;\n- while (this.executeCallbacks.length > 0) {\n- this.executeCallbacks[0]();\n- }\n+ splitWith: function(geometry, options) {\n+ return geometry.split(this, options);\n+\n },\n \n /**\n- * Method: toFeatures\n- * Converts spatial input into features so it can be processed by\n- * instances.\n+ * APIMethod: getVertices\n+ * Return a list of all points in this geometry.\n *\n * Parameters:\n- * source - {Mixed} An , an\n- * , or an array of geometries or features\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()}\n+ * {Array} A list of all vertices in the geometry.\n */\n- toFeatures: function(source) {\n- var isArray = OpenLayers.Util.isArray(source);\n- if (!isArray) {\n- source = [source];\n- }\n- var target = new Array(source.length),\n- current;\n- for (var i = 0, ii = source.length; i < ii; ++i) {\n- current = source[i];\n- target[i] = current instanceof OpenLayers.Feature.Vector ?\n- current : new OpenLayers.Feature.Vector(current);\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 isArray ? target : target[0];\n+ return vertices;\n },\n \n /**\n- * Method: findMimeType\n- * Finds a supported mime type.\n+ * APIMethod: distanceTo\n+ * Calculate the closest distance between two geometries (on the x-y plane).\n *\n * Parameters:\n- * sourceFormats - {Object} An object literal with mime types as key and\n- * true as value for supported formats.\n- * targetFormats - {Object} Like , but optional to check for\n- * supported mime types on a different target than this process.\n- * Default is to check against this process's supported formats.\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- * {String} A supported mime type.\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- findMimeType: function(sourceFormats, targetFormats) {\n- targetFormats = targetFormats || this.formats;\n- for (var f in sourceFormats) {\n- if (f in targetFormats) {\n- return f;\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- CLASS_NAME: \"OpenLayers.WPSProcess\"\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-});\n+ var compareNumbers = function(a, b) {\n+ return (a - b);\n+ };\n \n-/**\n- * Class: OpenLayers.WPSProcess.ChainLink\n- * Type for chaining processes.\n- */\n-OpenLayers.WPSProcess.ChainLink = OpenLayers.Class({\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- /**\n- * Property: process\n- * {} The process to chain\n- */\n- process: null,\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- /**\n- * Property: output\n- * {String} The output identifier of the output we are going to use as\n- * input for another process.\n- */\n- output: null,\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- * Constructor: OpenLayers.WPSProcess.ChainLink\n- *\n- * Parameters:\n- * options - {Object} Properties to set on the instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\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- CLASS_NAME: \"OpenLayers.WPSProcess.ChainLink\"\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/Rule.js\n+ OpenLayers/Geometry/MultiLineString.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+/**\n+ * @requires OpenLayers/Geometry/Collection.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ */\n \n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Style.js\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+ );\n+ }\n+ }\n+ sourceSplit = true;\n+ splits = splits[1];\n+ }\n+ if (splits.length) {\n+ // splice in new target parts\n+ splits.unshift(j, 1);\n+ Array.prototype.splice.apply(targetParts, splits);\n+ break;\n+ }\n+ }\n+ }\n+ if (!sourceSplit) {\n+ // source line was not hit\n+ if (sourceParts.length) {\n+ // add line to existing multi\n+ sourceParts[sourceParts.length - 1].addComponent(\n+ sourceLine.clone()\n+ );\n+ } else {\n+ // create a fresh multi\n+ sourceParts = [\n+ new OpenLayers.Geometry.MultiLineString(\n+ sourceLine.clone()\n+ )\n+ ];\n+ }\n+ }\n+ }\n+ if (sourceParts && sourceParts.length > 1) {\n+ sourceSplit = true;\n+ } else {\n+ sourceParts = [];\n+ }\n+ if (targetParts && targetParts.length > 1) {\n+ targetSplit = true;\n+ } else {\n+ targetParts = [];\n+ }\n+ if (sourceSplit || targetSplit) {\n+ if (mutual) {\n+ results = [sourceParts, targetParts];\n+ } else {\n+ results = targetParts;\n+ }\n+ }\n+ return results;\n+ },\n+\n+ /**\n+ * Method: splitWith\n+ * Split this geometry (the target) with the given geometry (the source).\n+ *\n+ * Parameters:\n+ * geometry - {} A geometry used to split this\n+ * geometry (the source).\n+ * options - {Object} Properties of this object will be used to determine\n+ * how the split is conducted.\n+ *\n+ * Valid options:\n+ * mutual - {Boolean} Split the source geometry in addition to the target\n+ * geometry. Default is false.\n+ * edge - {Boolean} Allow splitting when only edges intersect. Default is\n+ * true. If false, a vertex on the source must be within the tolerance\n+ * distance of the intersection to be considered a split.\n+ * tolerance - {Number} If a non-null value is provided, intersections\n+ * within the tolerance distance of an existing vertex on the source\n+ * will be assumed to occur at the vertex.\n+ * \n+ * Returns:\n+ * {Array} A list of geometries (of this same type as the target) that\n+ * result from splitting the target with the source geometry. The\n+ * source and target geometry will remain unmodified. If no split\n+ * results, null will be returned. If mutual is true and a split\n+ * results, return will be an array of two arrays - the first will be\n+ * all geometries that result from splitting the source geometry and\n+ * the second will be all geometries that result from splitting the\n+ * target geometry.\n+ */\n+ splitWith: function(geometry, options) {\n+ var results = null;\n+ var mutual = options && options.mutual;\n+ var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;\n+ if (geometry instanceof OpenLayers.Geometry.LineString) {\n+ targetParts = [];\n+ sourceParts = [geometry];\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ targetSplit = false;\n+ targetLine = this.components[i];\n+ for (var j = 0; j < sourceParts.length; ++j) {\n+ splits = sourceParts[j].split(targetLine, options);\n+ if (splits) {\n+ if (mutual) {\n+ sourceLines = splits[0];\n+ if (sourceLines.length) {\n+ // splice in new source parts\n+ sourceLines.unshift(j, 1);\n+ Array.prototype.splice.apply(sourceParts, sourceLines);\n+ j += sourceLines.length - 2;\n+ }\n+ splits = splits[1];\n+ if (splits.length === 0) {\n+ splits = [targetLine.clone()];\n+ }\n+ }\n+ for (var k = 0, klen = splits.length; k < klen; ++k) {\n+ if (k === 0 && targetParts.length) {\n+ targetParts[targetParts.length - 1].addComponent(\n+ splits[k]\n+ );\n+ } else {\n+ targetParts.push(\n+ new OpenLayers.Geometry.MultiLineString([\n+ splits[k]\n+ ])\n+ );\n+ }\n+ }\n+ targetSplit = true;\n+ }\n+ }\n+ if (!targetSplit) {\n+ // target component was not hit\n+ if (targetParts.length) {\n+ // add it to any existing multi-line\n+ targetParts[targetParts.length - 1].addComponent(\n+ targetLine.clone()\n+ );\n+ } else {\n+ // or start with a fresh multi-line\n+ targetParts = [\n+ new OpenLayers.Geometry.MultiLineString([\n+ targetLine.clone()\n+ ])\n+ ];\n+ }\n+\n+ }\n+ }\n+ } else {\n+ results = geometry.split(this);\n+ }\n+ if (sourceParts && sourceParts.length > 1) {\n+ sourceSplit = true;\n+ } else {\n+ sourceParts = [];\n+ }\n+ if (targetParts && targetParts.length > 1) {\n+ targetSplit = true;\n+ } else {\n+ targetParts = [];\n+ }\n+ if (sourceSplit || targetSplit) {\n+ if (mutual) {\n+ results = [sourceParts, targetParts];\n+ } else {\n+ results = targetParts;\n+ }\n+ }\n+ return results;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Geometry.MultiLineString\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Geometry/LinearRing.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n /**\n- * Class: OpenLayers.Rule\n- * This class represents an SLD Rule, as being used for rule-based SLD styling.\n+ * @requires OpenLayers/Geometry/LineString.js\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+ * Class: OpenLayers.Geometry.LinearRing\n+ * \n+ * A Linear Ring is a special LineString which is closed. It closes itself \n+ * automatically on every addPoint/removePoint by adding a copy of the first\n+ * point as the last point. \n+ * \n+ * Also, as it is the first in the line family to close itself, a getArea()\n+ * function is defined to calculate the enclosed area of the linearRing\n+ * \n+ * Inherits:\n+ * - \n+ */\n+OpenLayers.Geometry.LinearRing = OpenLayers.Class(\n+ OpenLayers.Geometry.LineString, {\n \n- /**\n- * APIProperty: name\n- * {String} name of this rule\n- */\n- name: null,\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- * Property: title\n- * {String} Title of this rule (set if included in SLD)\n- */\n- title: null,\n+ /**\n+ * Constructor: OpenLayers.Geometry.LinearRing\n+ * Linear rings are constructed with an array of points. This array\n+ * can represent a closed or open ring. If the ring is open (the last\n+ * point does not equal the first point), the constructor will close\n+ * the ring. If the ring is already closed (the last point does equal\n+ * the first point), it will be left closed.\n+ * \n+ * Parameters:\n+ * points - {Array()} points\n+ */\n \n- /**\n- * Property: description\n- * {String} Description of this rule (set if abstract is included in SLD)\n- */\n- description: null,\n+ /**\n+ * APIMethod: addComponent\n+ * Adds a point to geometry components. If the point is to be added to\n+ * the end of the components array and it is the same as the last point\n+ * already in that array, the duplicate point is not added. This has \n+ * the effect of closing the ring if it is not already closed, and \n+ * doing the right thing if it is already closed. This behavior can \n+ * be overridden by calling the method with a non-null index as the \n+ * second argument.\n+ *\n+ * Parameters:\n+ * point - {}\n+ * index - {Integer} Index into the array to insert the component\n+ * \n+ * Returns:\n+ * {Boolean} Was the Point successfully added?\n+ */\n+ addComponent: function(point, index) {\n+ var added = false;\n \n- /**\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+ //remove last point\n+ var lastPoint = this.components.pop();\n \n- /**\n- * Property: filter\n- * {} Optional filter for the rule.\n- */\n- filter: null,\n+ // given an index, add the point\n+ // without an index only add non-duplicate points\n+ if (index != null || !point.equals(lastPoint)) {\n+ added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n+ arguments);\n+ }\n \n- /**\n- * 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+ //append copy of first point\n+ var firstPoint = this.components[0];\n+ OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n+ [firstPoint]);\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+ return added;\n+ },\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+ * APIMethod: removeComponent\n+ * Removes a point from geometry components.\n+ *\n+ * Parameters:\n+ * point - {}\n+ *\n+ * Returns: \n+ * {Boolean} The component was removed.\n+ */\n+ removeComponent: function(point) {\n+ var removed = this.components && (this.components.length > 3);\n+ if (removed) {\n+ //remove last point\n+ this.components.pop();\n \n- /**\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+ //remove our point\n+ OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,\n+ arguments);\n+ //append copy of first point\n+ var firstPoint = this.components[0];\n+ OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n+ [firstPoint]);\n+ }\n+ return removed;\n+ },\n \n- /**\n- * 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+ * APIMethod: move\n+ * Moves a geometry by the given displacement along positive x and y axes.\n+ * This modifies the position of the geometry and clears the cached\n+ * bounds.\n+ *\n+ * Parameters:\n+ * x - {Float} Distance to move geometry in positive x direction. \n+ * y - {Float} Distance to move geometry in positive y direction.\n+ */\n+ move: function(x, y) {\n+ for (var i = 0, len = this.components.length; i < len - 1; i++) {\n+ this.components[i].move(x, y);\n+ }\n+ },\n \n- /** \n- * 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+ * 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 - 1; ++i) {\n+ this.components[i].rotate(angle, origin);\n+ }\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+ * 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, len = this.components.length; i < len - 1; ++i) {\n+ this.components[i].resize(scale, origin, ratio);\n+ }\n+ return this;\n+ },\n \n- /**\n- * APIMethod: 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+ * 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 - 1; i++) {\n+ var component = this.components[i];\n+ component.transform(source, dest);\n+ }\n+ this.bounds = null;\n+ }\n+ return this;\n+ },\n \n- if (this.minScaleDenominator || this.maxScaleDenominator) {\n- var scale = feature.layer.map.getScale();\n- }\n+ /**\n+ * APIMethod: getCentroid\n+ *\n+ * Returns:\n+ * {} The centroid of the collection\n+ */\n+ getCentroid: function() {\n+ if (this.components) {\n+ var len = this.components.length;\n+ if (len > 0 && len <= 2) {\n+ return this.components[0].clone();\n+ } else if (len > 2) {\n+ var sumX = 0.0;\n+ var sumY = 0.0;\n+ var x0 = this.components[0].x;\n+ var y0 = this.components[0].y;\n+ var area = -1 * this.getArea();\n+ if (area != 0) {\n+ for (var i = 0; i < len - 1; i++) {\n+ var b = this.components[i];\n+ var c = this.components[i + 1];\n+ sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n+ sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n+ }\n+ var x = x0 + sumX / (6 * area);\n+ var y = y0 + sumY / (6 * area);\n+ } else {\n+ for (var i = 0; i < len - 1; i++) {\n+ sumX += this.components[i].x;\n+ sumY += this.components[i].y;\n+ }\n+ var x = sumX / (len - 1);\n+ var y = sumY / (len - 1);\n+ }\n+ return new OpenLayers.Geometry.Point(x, y);\n+ } else {\n+ return null;\n+ }\n+ }\n+ },\n \n- // 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+ * APIMethod: getArea\n+ * Note - The area is positive if the ring is oriented CW, otherwise\n+ * it will be negative.\n+ * \n+ * Returns:\n+ * {Float} The signed area for a ring.\n+ */\n+ getArea: function() {\n+ var area = 0.0;\n+ if (this.components && (this.components.length > 2)) {\n+ var sum = 0.0;\n+ for (var i = 0, len = this.components.length; i < len - 1; i++) {\n+ var b = this.components[i];\n+ var c = this.components[i + 1];\n+ sum += (b.x + c.x) * (c.y - b.y);\n+ }\n+ area = -sum / 2.0;\n+ }\n+ return area;\n+ },\n \n- // 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+ * APIMethod: getGeodesicArea\n+ * Calculate the approximate area of the polygon were it projected onto\n+ * the earth. Note that this area will be positive if ring is oriented\n+ * clockwise, otherwise it will be negative.\n+ *\n+ * Parameters:\n+ * projection - {} The spatial reference system\n+ * for the geometry coordinates. If not provided, Geographic/WGS84 is\n+ * assumed.\n+ * \n+ * Reference:\n+ * Robert. G. Chamberlain and William H. Duquette, \"Some Algorithms for\n+ * Polygons on a Sphere\", JPL Publication 07-03, Jet Propulsion\n+ * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409\n+ *\n+ * Returns:\n+ * {float} The approximate signed geodesic area of the polygon in square\n+ * meters.\n+ */\n+ getGeodesicArea: function(projection) {\n+ var ring = this; // so we can work with a clone if needed\n+ if (projection) {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ if (!gg.equals(projection)) {\n+ ring = this.clone().transform(projection, gg);\n+ }\n }\n- }\n+ var area = 0.0;\n+ var len = ring.components && ring.components.length;\n+ if (len > 2) {\n+ var p1, p2;\n+ for (var i = 0; i < len - 1; i++) {\n+ p1 = ring.components[i];\n+ p2 = ring.components[i + 1];\n+ area += OpenLayers.Util.rad(p2.x - p1.x) *\n+ (2 + Math.sin(OpenLayers.Util.rad(p1.y)) +\n+ Math.sin(OpenLayers.Util.rad(p2.y)));\n+ }\n+ area = area * 6378137.0 * 6378137.0 / 2.0;\n+ }\n+ return area;\n+ },\n \n- return applies;\n- },\n+ /**\n+ * Method: containsPoint\n+ * Test if a point is inside a linear ring. For the case where a point\n+ * is coincident with a linear ring edge, returns 1. Otherwise,\n+ * returns boolean.\n+ *\n+ * Parameters:\n+ * point - {}\n+ *\n+ * Returns:\n+ * {Boolean | Number} The point is inside the linear ring. Returns 1 if\n+ * the point is coincident with an edge. Returns boolean otherwise.\n+ */\n+ containsPoint: function(point) {\n+ var approx = OpenLayers.Number.limitSigDigs;\n+ var digs = 14;\n+ var px = approx(point.x, digs);\n+ var py = approx(point.y, digs);\n \n- /**\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+ function getX(y, x1, y1, x2, y2) {\n+ return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2;\n+ }\n+ var numSeg = this.components.length - 1;\n+ var start, end, x1, y1, x2, y2, cx, cy;\n+ var crosses = 0;\n+ for (var i = 0; i < numSeg; ++i) {\n+ start = this.components[i];\n+ x1 = approx(start.x, digs);\n+ y1 = approx(start.y, digs);\n+ end = this.components[i + 1];\n+ x2 = approx(end.x, digs);\n+ y2 = approx(end.y, digs);\n \n- /**\n- * 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+ * The following conditions enforce five edge-crossing rules:\n+ * 1. points coincident with edges are considered contained;\n+ * 2. an upward edge includes its starting endpoint, and\n+ * excludes its final endpoint;\n+ * 3. a downward edge excludes its starting endpoint, and\n+ * includes its final endpoint;\n+ * 4. horizontal edges are excluded; and\n+ * 5. the edge-ray intersection point must be strictly right\n+ * of the point P.\n+ */\n+ if (y1 == y2) {\n+ // horizontal edge\n+ if (py == y1) {\n+ // point on horizontal line\n+ if (x1 <= x2 && (px >= x1 && px <= x2) || // right or vert\n+ x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert\n+ // point on edge\n+ crosses = -1;\n+ break;\n+ }\n+ }\n+ // ignore other horizontal edges\n+ continue;\n+ }\n+ cx = approx(getX(py, x1, y1, x2, y2), digs);\n+ if (cx == px) {\n+ // point on line\n+ if (y1 < y2 && (py >= y1 && py <= y2) || // upward\n+ y1 > y2 && (py <= y1 && py >= y2)) { // downward\n+ // point on edge\n+ crosses = -1;\n+ break;\n+ }\n+ }\n+ if (cx <= px) {\n+ // no crossing to the right\n+ continue;\n+ }\n+ if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {\n+ // no crossing\n+ continue;\n+ }\n+ if (y1 < y2 && (py >= y1 && py < y2) || // upward\n+ y1 > y2 && (py < y1 && py >= y2)) { // downward\n+ ++crosses;\n+ }\n }\n- } 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+ var contained = (crosses == -1) ?\n+ // on edge\n+ 1 :\n+ // even (out) or odd (in)\n+ !!(crosses & 1);\n+\n+ return contained;\n+ },\n+\n+ /**\n+ * APIMethod: intersects\n+ * Determine if the input geometry intersects this one.\n+ *\n+ * Parameters:\n+ * geometry - {} Any type of geometry.\n+ *\n+ * Returns:\n+ * {Boolean} The input geometry intersects this one.\n+ */\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ intersect = this.containsPoint(geometry);\n+ } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n+ intersect = geometry.intersects(this);\n+ } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(\n+ this, [geometry]\n+ );\n+ } else {\n+ // check for component intersections\n+ for (var i = 0, len = geometry.components.length; i < len; ++i) {\n+ intersect = geometry.components[i].intersects(this);\n+ if (intersect) {\n+ break;\n+ }\n }\n }\n- }\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 intersect;\n+ },\n \n- CLASS_NAME: \"OpenLayers.Rule\"\n-});\n+ /**\n+ * APIMethod: getVertices\n+ * Return a list of all points in this geometry.\n+ *\n+ * Parameters:\n+ * nodes - {Boolean} For lines, only return vertices that are\n+ * endpoints. If false, for lines, only vertices that are not\n+ * endpoints will be returned. If not provided, all vertices will\n+ * be returned.\n+ *\n+ * Returns:\n+ * {Array} A list of all vertices in the geometry.\n+ */\n+ getVertices: function(nodes) {\n+ return (nodes === true) ? [] : this.components.slice(0, this.components.length - 1);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Geometry.LinearRing\"\n+ });\n /* ======================================================================\n- OpenLayers/Tween.js\n+ OpenLayers/Geometry/Polygon.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Animation.js\n+ * @requires OpenLayers/Geometry/Collection.js\n+ * @requires OpenLayers/Geometry/LinearRing.js\n */\n \n /**\n- * Namespace: OpenLayers.Tween\n+ * Class: OpenLayers.Geometry.Polygon \n+ * Polygon is a collection of Geometry.LinearRings. \n+ * \n+ * Inherits from:\n+ * - \n+ * - \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+OpenLayers.Geometry.Polygon = OpenLayers.Class(\n+ OpenLayers.Geometry.Collection, {\n \n- /**\n- * APIProperty: duration\n- * {int} duration of the tween (number of steps)\n- */\n- duration: null,\n+ /**\n+ * Property: componentTypes\n+ * {Array(String)} An array of class names representing the types of\n+ * components that the collection can include. A null value means the\n+ * component types are not restricted.\n+ */\n+ componentTypes: [\"OpenLayers.Geometry.LinearRing\"],\n \n- /**\n- * 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+ * Constructor: OpenLayers.Geometry.Polygon\n+ * Constructor for a Polygon geometry. \n+ * The first ring (this.component[0])is the outer bounds of the polygon and \n+ * all subsequent rings (this.component[1-n]) are internal holes.\n+ *\n+ *\n+ * Parameters:\n+ * components - {Array()} \n+ */\n \n- /**\n- * Property: time\n- * {int} Step counter\n- */\n- time: null,\n+ /** \n+ * APIMethod: getArea\n+ * Calculated by subtracting the areas of the internal holes from the \n+ * area of the outer hole.\n+ * \n+ * Returns:\n+ * {float} The area of the geometry\n+ */\n+ getArea: function() {\n+ var area = 0.0;\n+ if (this.components && (this.components.length > 0)) {\n+ area += Math.abs(this.components[0].getArea());\n+ for (var i = 1, len = this.components.length; i < len; i++) {\n+ area -= Math.abs(this.components[i].getArea());\n+ }\n+ }\n+ return area;\n+ },\n \n- /**\n- * 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+ * 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 polygon in square meters.\n+ */\n+ getGeodesicArea: function(projection) {\n+ var area = 0.0;\n+ if (this.components && (this.components.length > 0)) {\n+ area += Math.abs(this.components[0].getGeodesicArea(projection));\n+ for (var i = 1, len = this.components.length; i < len; i++) {\n+ area -= Math.abs(this.components[i].getGeodesicArea(projection));\n+ }\n+ }\n+ return area;\n+ },\n \n- /**\n- * Property: startTime\n- * {Number} The timestamp of the first execution step. Used for skipping\n- * frames\n- */\n- startTime: null,\n+ /**\n+ * Method: containsPoint\n+ * Test if a point is inside a polygon. Points on a polygon edge are\n+ * considered inside.\n+ *\n+ * Parameters:\n+ * point - {}\n+ *\n+ * Returns:\n+ * {Boolean | Number} The point is inside the polygon. Returns 1 if the\n+ * point is on an edge. Returns boolean otherwise.\n+ */\n+ containsPoint: function(point) {\n+ var numRings = this.components.length;\n+ var contained = false;\n+ if (numRings > 0) {\n+ // check exterior ring - 1 means on edge, boolean otherwise\n+ contained = this.components[0].containsPoint(point);\n+ if (contained !== 1) {\n+ if (contained && numRings > 1) {\n+ // check interior rings\n+ var hole;\n+ for (var i = 1; i < numRings; ++i) {\n+ hole = this.components[i].containsPoint(point);\n+ if (hole) {\n+ if (hole === 1) {\n+ // on edge\n+ contained = 1;\n+ } else {\n+ // in hole\n+ contained = false;\n+ }\n+ break;\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return contained;\n+ },\n \n- /**\n- * Property: animationId\n- * {int} Loop id returned by OpenLayers.Animation.start\n- */\n- animationId: null,\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+ var i, len;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ intersect = this.containsPoint(geometry);\n+ } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" ||\n+ geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ // check if rings/linestrings intersect\n+ for (i = 0, len = this.components.length; i < len; ++i) {\n+ intersect = geometry.intersects(this.components[i]);\n+ if (intersect) {\n+ break;\n+ }\n+ }\n+ if (!intersect) {\n+ // check if this poly contains points of the ring/linestring\n+ for (i = 0, len = geometry.components.length; i < len; ++i) {\n+ intersect = this.containsPoint(geometry.components[i]);\n+ if (intersect) {\n+ break;\n+ }\n+ }\n+ }\n+ } else {\n+ for (i = 0, len = geometry.components.length; i < len; ++i) {\n+ intersect = this.intersects(geometry.components[i]);\n+ if (intersect) {\n+ break;\n+ }\n+ }\n+ }\n+ // check case where this poly is wholly contained by another\n+ if (!intersect && geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n+ // exterior ring points will be contained in the other geometry\n+ var ring = this.components[0];\n+ for (i = 0, len = ring.components.length; i < len; ++i) {\n+ intersect = geometry.containsPoint(ring.components[i]);\n+ if (intersect) {\n+ break;\n+ }\n+ }\n+ }\n+ return intersect;\n+ },\n \n- /**\n- * Property: playing\n- * {Boolean} Tells if the easing is currently playing\n- */\n- playing: false,\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 result;\n+ // this is the case where we might not be looking for distance to edge\n+ if (!edge && this.intersects(geometry)) {\n+ result = 0;\n+ } else {\n+ result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(\n+ this, [geometry, options]\n+ );\n+ }\n+ return result;\n+ },\n \n- /** \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+ CLASS_NAME: \"OpenLayers.Geometry.Polygon\"\n+ });\n \n- /**\n- * APIMethod: start\n- * Plays the Tween, and calls the callback method on each step\n- * \n- * Parameters:\n- * begin - {Object} values to start the animation with\n- * finish - {Object} values to finish the animation with\n- * duration - {int} duration of the tween (number of steps)\n- * options - {Object} hash of options (callbacks (start, eachStep, done),\n- * minFrameRate)\n- */\n- start: function(begin, finish, duration, options) {\n- this.playing = true;\n- this.begin = begin;\n- this.finish = finish;\n- this.duration = duration;\n- this.callbacks = options.callbacks;\n- this.minFrameRate = options.minFrameRate || 30;\n- this.time = 0;\n- this.startTime = new Date().getTime();\n- OpenLayers.Animation.stop(this.animationId);\n- this.animationId = null;\n- if (this.callbacks && this.callbacks.start) {\n- this.callbacks.start.call(this, this.begin);\n- }\n- this.animationId = OpenLayers.Animation.start(\n- OpenLayers.Function.bind(this.play, this)\n- );\n- },\n+/**\n+ * APIMethod: createRegularPolygon\n+ * Create a regular polygon around a radius. Useful for creating circles \n+ * and the like.\n+ *\n+ * Parameters:\n+ * origin - {} center of polygon.\n+ * radius - {Float} distance to vertex, in map units.\n+ * sides - {Integer} Number of sides. 20 approximates a circle.\n+ * rotation - {Float} original angle of rotation, in degrees.\n+ */\n+OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {\n+ var angle = Math.PI * ((1 / sides) - (1 / 2));\n+ if (rotation) {\n+ angle += (rotation / 180) * Math.PI;\n+ }\n+ var rotatedAngle, x, y;\n+ var points = [];\n+ for (var i = 0; i < sides; ++i) {\n+ rotatedAngle = angle + (i * 2 * Math.PI / sides);\n+ x = origin.x + (radius * Math.cos(rotatedAngle));\n+ y = origin.y + (radius * Math.sin(rotatedAngle));\n+ points.push(new OpenLayers.Geometry.Point(x, y));\n+ }\n+ var ring = new OpenLayers.Geometry.LinearRing(points);\n+ return new OpenLayers.Geometry.Polygon([ring]);\n+};\n+/* ======================================================================\n+ OpenLayers/Geometry/MultiPolygon.js\n+ ====================================================================== */\n \n- /**\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- }\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- if (this.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+ * @requires OpenLayers/Geometry/Collection.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n+ */\n \n- /**\n- * Method: play\n- * Calls the appropriate easing method\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+ * Class: OpenLayers.Geometry.MultiPolygon\n+ * MultiPolygon is a geometry with multiple \n+ * components. Create a new instance with the \n+ * constructor.\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(\n+ OpenLayers.Geometry.Collection, {\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+ * Property: componentTypes\n+ * {Array(String)} An array of class names representing the types of\n+ * components that the collection can include. A null value means the\n+ * component types are not restricted.\n+ */\n+ componentTypes: [\"OpenLayers.Geometry.Polygon\"],\n \n- 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+ * Constructor: OpenLayers.Geometry.MultiPolygon\n+ * Create a new MultiPolygon geometry\n+ *\n+ * Parameters:\n+ * components - {Array()} An array of polygons\n+ * used to generate the MultiPolygon\n+ *\n+ */\n \n- if (this.time > this.duration) {\n- this.stop();\n- }\n- },\n+ CLASS_NAME: \"OpenLayers.Geometry.MultiPolygon\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/WKT.js\n+ ====================================================================== */\n \n- /**\n- * Create empty functions for all easing methods.\n- */\n- CLASS_NAME: \"OpenLayers.Tween\"\n-});\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-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+ * @requires OpenLayers/Format.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/MultiPoint.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Geometry/MultiLineString.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n+ * @requires OpenLayers/Geometry/MultiPolygon.js\n */\n-OpenLayers.Easing = {\n- /**\n- * Create empty functions for all easing methods.\n- */\n- CLASS_NAME: \"OpenLayers.Easing\"\n-};\n \n /**\n- * Namespace: OpenLayers.Easing.Linear\n+ * Class: OpenLayers.Format.WKT\n+ * Class for reading and writing Well-Known Text. Create a new instance\n+ * with the constructor.\n+ * \n+ * Inherits from:\n+ * - \n */\n-OpenLayers.Easing.Linear = {\n+OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, {\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+ * Constructor: OpenLayers.Format.WKT\n+ * Create a new parser for WKT\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+ * options - {Object} An optional object whose properties will be set on\n+ * this instance\n *\n * Returns:\n- * {Float}\n+ * {} A new WKT parser.\n */\n- easeOut: function(t, b, c, d) {\n- return c * t / d + b;\n+ initialize: function(options) {\n+ this.regExes = {\n+ 'typeStr': /^\\s*(\\w+)\\s*\\(\\s*(.*)\\s*\\)\\s*$/,\n+ 'spaces': /\\s+/,\n+ 'parenComma': /\\)\\s*,\\s*\\(/,\n+ 'doubleParenComma': /\\)\\s*\\)\\s*,\\s*\\(\\s*\\(/, // can't use {2} here\n+ 'trimParens': /^\\s*\\(?(.*?)\\)?\\s*$/\n+ };\n+ OpenLayers.Format.prototype.initialize.apply(this, [options]);\n },\n \n /**\n- * Function: easeInOut\n- * \n+ * APIMethod: read\n+ * Deserialize a WKT string and return a vector feature or an\n+ * array of vector features. Supports WKT for POINT, MULTIPOINT,\n+ * LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and\n+ * GEOMETRYCOLLECTION.\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+ * wkt - {String} A WKT string\n *\n * Returns:\n- * {Float}\n+ * {|Array} A feature or array of features for\n+ * GEOMETRYCOLLECTION WKT.\n */\n- easeInOut: function(t, b, c, d) {\n- return c * t / d + b;\n+ read: function(wkt) {\n+ var features, type, str;\n+ wkt = wkt.replace(/[\\n\\r]/g, \" \");\n+ var matches = this.regExes.typeStr.exec(wkt);\n+ if (matches) {\n+ type = matches[1].toLowerCase();\n+ str = matches[2];\n+ if (this.parse[type]) {\n+ features = this.parse[type].apply(this, [str]);\n+ }\n+ if (this.internalProjection && this.externalProjection) {\n+ if (features &&\n+ features.CLASS_NAME == \"OpenLayers.Feature.Vector\") {\n+ features.geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ } else if (features &&\n+ type != \"geometrycollection\" &&\n+ typeof features == \"object\") {\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ var component = features[i];\n+ component.geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ }\n+ }\n+ }\n+ }\n+ return features;\n },\n \n- CLASS_NAME: \"OpenLayers.Easing.Linear\"\n-};\n-\n-/**\n- * Namespace: OpenLayers.Easing.Expo\n- */\n-OpenLayers.Easing.Expo = {\n-\n /**\n- * Function: easeIn\n- * \n+ * APIMethod: write\n+ * Serialize a feature or array of features into a WKT string.\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+ * features - {|Array} A feature or array of\n+ * features\n *\n * Returns:\n- * {Float}\n+ * {String} The WKT string representation of the input geometries\n */\n- easeIn: function(t, b, c, d) {\n- return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;\n+ write: function(features) {\n+ var collection, geometry, isCollection;\n+ if (features.constructor == Array) {\n+ collection = features;\n+ isCollection = true;\n+ } else {\n+ collection = [features];\n+ isCollection = false;\n+ }\n+ var pieces = [];\n+ if (isCollection) {\n+ pieces.push('GEOMETRYCOLLECTION(');\n+ }\n+ for (var i = 0, len = collection.length; i < len; ++i) {\n+ if (isCollection && i > 0) {\n+ pieces.push(',');\n+ }\n+ geometry = collection[i].geometry;\n+ pieces.push(this.extractGeometry(geometry));\n+ }\n+ if (isCollection) {\n+ pieces.push(')');\n+ }\n+ return pieces.join('');\n },\n \n /**\n- * Function: easeOut\n- * \n+ * Method: extractGeometry\n+ * Entry point to construct the WKT for a single Geometry object.\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+ * geometry - {}\n *\n * Returns:\n- * {Float}\n+ * {String} A WKT string of representing the geometry\n */\n- easeOut: function(t, b, c, d) {\n- return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;\n+ extractGeometry: function(geometry) {\n+ var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();\n+ if (!this.extract[type]) {\n+ return null;\n+ }\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry = geometry.clone();\n+ geometry.transform(this.internalProjection, this.externalProjection);\n+ }\n+ var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase();\n+ var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')';\n+ return data;\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+ * Object with properties corresponding to the geometry types.\n+ * Property values are functions that do the actual data extraction.\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+ extract: {\n+ /**\n+ * Return a space delimited string of point coordinates.\n+ * @param {OpenLayers.Geometry.Point} point\n+ * @returns {String} A string of coordinates representing the point\n+ */\n+ 'point': function(point) {\n+ return point.x + ' ' + point.y;\n+ },\n \n- CLASS_NAME: \"OpenLayers.Easing.Expo\"\n-};\n+ /**\n+ * Return a comma delimited string of point coordinates from a multipoint.\n+ * @param {OpenLayers.Geometry.MultiPoint} multipoint\n+ * @returns {String} A string of point coordinate strings representing\n+ * the multipoint\n+ */\n+ 'multipoint': function(multipoint) {\n+ var array = [];\n+ for (var i = 0, len = multipoint.components.length; i < len; ++i) {\n+ array.push('(' +\n+ this.extract.point.apply(this, [multipoint.components[i]]) +\n+ ')');\n+ }\n+ return array.join(',');\n+ },\n \n-/**\n- * Namespace: OpenLayers.Easing.Quad\n- */\n-OpenLayers.Easing.Quad = {\n+ /**\n+ * Return a comma delimited string of point coordinates from a line.\n+ * @param {OpenLayers.Geometry.LineString} linestring\n+ * @returns {String} A string of point coordinate strings representing\n+ * the linestring\n+ */\n+ 'linestring': function(linestring) {\n+ var array = [];\n+ for (var i = 0, len = linestring.components.length; i < len; ++i) {\n+ array.push(this.extract.point.apply(this, [linestring.components[i]]));\n+ }\n+ return array.join(',');\n+ },\n \n- /**\n- * Function: easeIn\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n- */\n- easeIn: function(t, b, c, d) {\n- return c * (t /= d) * t + b;\n- },\n+ /**\n+ * Return a comma delimited string of linestring strings from a multilinestring.\n+ * @param {OpenLayers.Geometry.MultiLineString} multilinestring\n+ * @returns {String} A string of of linestring strings representing\n+ * the multilinestring\n+ */\n+ 'multilinestring': function(multilinestring) {\n+ var array = [];\n+ for (var i = 0, len = multilinestring.components.length; i < len; ++i) {\n+ array.push('(' +\n+ this.extract.linestring.apply(this, [multilinestring.components[i]]) +\n+ ')');\n+ }\n+ return array.join(',');\n+ },\n+\n+ /**\n+ * Return a comma delimited string of linear ring arrays from a polygon.\n+ * @param {OpenLayers.Geometry.Polygon} polygon\n+ * @returns {String} An array of linear ring arrays representing the polygon\n+ */\n+ 'polygon': function(polygon) {\n+ var array = [];\n+ for (var i = 0, len = polygon.components.length; i < len; ++i) {\n+ array.push('(' +\n+ this.extract.linestring.apply(this, [polygon.components[i]]) +\n+ ')');\n+ }\n+ return array.join(',');\n+ },\n+\n+ /**\n+ * Return an array of polygon arrays from a multipolygon.\n+ * @param {OpenLayers.Geometry.MultiPolygon} multipolygon\n+ * @returns {String} An array of polygon arrays representing\n+ * the multipolygon\n+ */\n+ 'multipolygon': function(multipolygon) {\n+ var array = [];\n+ for (var i = 0, len = multipolygon.components.length; i < len; ++i) {\n+ array.push('(' +\n+ this.extract.polygon.apply(this, [multipolygon.components[i]]) +\n+ ')');\n+ }\n+ return array.join(',');\n+ },\n+\n+ /**\n+ * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an \n+ * @param {OpenLayers.Geometry.Collection} collection\n+ * @returns {String} internal WKT representation of the collection\n+ */\n+ 'collection': function(collection) {\n+ var array = [];\n+ for (var i = 0, len = collection.components.length; i < len; ++i) {\n+ array.push(this.extractGeometry.apply(this, [collection.components[i]]));\n+ }\n+ return array.join(',');\n+ }\n \n- /**\n- * Function: easeOut\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n- */\n- easeOut: function(t, b, c, d) {\n- return -c * (t /= d) * (t - 2) + b;\n },\n \n /**\n- * Function: easeInOut\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n+ * Object with properties corresponding to the geometry types.\n+ * Property values are functions that do the actual parsing.\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+ parse: {\n+ /**\n+ * Return point feature given a point WKT fragment.\n+ * @param {String} str A WKT fragment representing the point\n+ * @returns {OpenLayers.Feature.Vector} A point feature\n+ * @private\n+ */\n+ 'point': function(str) {\n+ var coords = OpenLayers.String.trim(str).split(this.regExes.spaces);\n+ return new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.Point(coords[0], coords[1])\n+ );\n+ },\n+\n+ /**\n+ * Return a multipoint feature given a multipoint WKT fragment.\n+ * @param {String} str A WKT fragment representing the multipoint\n+ * @returns {OpenLayers.Feature.Vector} A multipoint feature\n+ * @private\n+ */\n+ 'multipoint': function(str) {\n+ var point;\n+ var points = OpenLayers.String.trim(str).split(',');\n+ var components = [];\n+ for (var i = 0, len = points.length; i < len; ++i) {\n+ point = points[i].replace(this.regExes.trimParens, '$1');\n+ components.push(this.parse.point.apply(this, [point]).geometry);\n+ }\n+ return new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.MultiPoint(components)\n+ );\n+ },\n+\n+ /**\n+ * Return a linestring feature given a linestring WKT fragment.\n+ * @param {String} str A WKT fragment representing the linestring\n+ * @returns {OpenLayers.Feature.Vector} A linestring feature\n+ * @private\n+ */\n+ 'linestring': function(str) {\n+ var points = OpenLayers.String.trim(str).split(',');\n+ var components = [];\n+ for (var i = 0, len = points.length; i < len; ++i) {\n+ components.push(this.parse.point.apply(this, [points[i]]).geometry);\n+ }\n+ return new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.LineString(components)\n+ );\n+ },\n+\n+ /**\n+ * Return a multilinestring feature given a multilinestring WKT fragment.\n+ * @param {String} str A WKT fragment representing the multilinestring\n+ * @returns {OpenLayers.Feature.Vector} A multilinestring feature\n+ * @private\n+ */\n+ 'multilinestring': function(str) {\n+ var line;\n+ var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma);\n+ var components = [];\n+ for (var i = 0, len = lines.length; i < len; ++i) {\n+ line = lines[i].replace(this.regExes.trimParens, '$1');\n+ components.push(this.parse.linestring.apply(this, [line]).geometry);\n+ }\n+ return new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.MultiLineString(components)\n+ );\n+ },\n+\n+ /**\n+ * Return a polygon feature given a polygon WKT fragment.\n+ * @param {String} str A WKT fragment representing the polygon\n+ * @returns {OpenLayers.Feature.Vector} A polygon feature\n+ * @private\n+ */\n+ 'polygon': function(str) {\n+ var ring, linestring, linearring;\n+ var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma);\n+ var components = [];\n+ for (var i = 0, len = rings.length; i < len; ++i) {\n+ ring = rings[i].replace(this.regExes.trimParens, '$1');\n+ linestring = this.parse.linestring.apply(this, [ring]).geometry;\n+ linearring = new OpenLayers.Geometry.LinearRing(linestring.components);\n+ components.push(linearring);\n+ }\n+ return new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.Polygon(components)\n+ );\n+ },\n+\n+ /**\n+ * Return a multipolygon feature given a multipolygon WKT fragment.\n+ * @param {String} str A WKT fragment representing the multipolygon\n+ * @returns {OpenLayers.Feature.Vector} A multipolygon feature\n+ * @private\n+ */\n+ 'multipolygon': function(str) {\n+ var polygon;\n+ var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);\n+ var components = [];\n+ for (var i = 0, len = polygons.length; i < len; ++i) {\n+ polygon = polygons[i].replace(this.regExes.trimParens, '$1');\n+ components.push(this.parse.polygon.apply(this, [polygon]).geometry);\n+ }\n+ return new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.MultiPolygon(components)\n+ );\n+ },\n+\n+ /**\n+ * Return an array of features given a geometrycollection WKT fragment.\n+ * @param {String} str A WKT fragment representing the geometrycollection\n+ * @returns {Array} An array of OpenLayers.Feature.Vector\n+ * @private\n+ */\n+ 'geometrycollection': function(str) {\n+ // separate components of the collection with |\n+ str = str.replace(/,\\s*([A-Za-z])/g, '|$1');\n+ var wktArray = OpenLayers.String.trim(str).split('|');\n+ var components = [];\n+ for (var i = 0, len = wktArray.length; i < len; ++i) {\n+ components.push(OpenLayers.Format.WKT.prototype.read.apply(this, [wktArray[i]]));\n+ }\n+ return components;\n+ }\n+\n },\n \n- CLASS_NAME: \"OpenLayers.Easing.Quad\"\n-};\n+ CLASS_NAME: \"OpenLayers.Format.WKT\"\n+});\n /* ======================================================================\n- OpenLayers/Projection.js\n+ OpenLayers/Format/JSON.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n+ * Note:\n+ * This work draws heavily from the public domain JSON serializer/deserializer\n+ * at http://www.json.org/json.js. Rewritten so that it doesn't modify\n+ * basic data prototypes.\n */\n \n /**\n- * 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+ * @requires OpenLayers/Format.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.JSON\n+ * A parser to read/write JSON safely. Create a new instance with the\n+ * constructor.\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+ * Inherits from:\n+ * - \n */\n-OpenLayers.Projection = OpenLayers.Class({\n+OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {\n \n /**\n- * Property: proj\n- * {Object} Proj4js.Proj instance.\n+ * APIProperty: indent\n+ * {String} For \"pretty\" printing, the indent string will be used once for\n+ * each indentation level.\n */\n- proj: null,\n+ indent: \" \",\n \n /**\n- * Property: projCode\n- * {String}\n+ * APIProperty: space\n+ * {String} For \"pretty\" printing, the space string will be used after\n+ * the \":\" separating a name/value pair.\n */\n- projCode: null,\n+ space: \" \",\n \n /**\n- * Property: titleRegEx\n- * {RegExp} regular expression to strip the title from a proj4js definition\n+ * APIProperty: newline\n+ * {String} For \"pretty\" printing, the newline string will be used at the\n+ * end of each name/value pair or array item.\n */\n- titleRegEx: /\\+title=[^\\+]*/,\n+ newline: \"\\n\",\n \n /**\n- * Constructor: OpenLayers.Projection\n- * This class offers several methods for interacting with a wrapped \n- * pro4js projection object. \n+ * Property: level\n+ * {Integer} For \"pretty\" printing, this is incremented/decremented during\n+ * serialization.\n+ */\n+ level: 0,\n+\n+ /**\n+ * Property: pretty\n+ * {Boolean} Serialize with extra whitespace for structure. This is set\n+ * by the method.\n+ */\n+ pretty: false,\n+\n+ /**\n+ * Property: nativeJSON\n+ * {Boolean} Does the browser support native json?\n+ */\n+ nativeJSON: (function() {\n+ return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\");\n+ })(),\n+\n+ /**\n+ * Constructor: OpenLayers.Format.JSON\n+ * Create a new parser for JSON.\n *\n * Parameters:\n- * 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+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Deserialize a json string.\n *\n+ * Parameters:\n+ * json - {String} A JSON string\n+ * filter - {Function} A function which will be called for every key and\n+ * value at every level of the final result. Each value will be\n+ * replaced by the result of the filter function. This can be used to\n+ * reform generic objects into instances of classes, or to transform\n+ * date strings into Date objects.\n+ * \n * Returns:\n- * {} A projection object.\n+ * {Object} An object, array, string, or number .\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+ read: function(json, filter) {\n+ var object;\n+ if (this.nativeJSON) {\n+ object = JSON.parse(json, filter);\n+ } else try {\n+ /**\n+ * Parsing happens in three stages. In the first stage, we run the\n+ * text against a regular expression which looks for non-JSON\n+ * characters. We are especially concerned with '()' and 'new'\n+ * because they can cause invocation, and '=' because it can\n+ * cause mutation. But just to be safe, we will reject all\n+ * unexpected characters.\n+ */\n+ if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, '@').replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']').replace(/(?:^|:|,)(?:\\s*\\[)+/g, ''))) {\n+\n+ /**\n+ * In the second stage we use the eval function to compile the\n+ * text into a JavaScript structure. The '{' operator is\n+ * subject to a syntactic ambiguity in JavaScript - it can\n+ * begin a block or an object literal. We wrap the text in\n+ * parens to eliminate the ambiguity.\n+ */\n+ object = eval('(' + json + ')');\n+\n+ /**\n+ * In the optional third stage, we recursively walk the new\n+ * structure, passing each name/value pair to a filter\n+ * function for possible transformation.\n+ */\n+ if (typeof filter === 'function') {\n+ function walk(k, v) {\n+ if (v && typeof v === 'object') {\n+ for (var i in v) {\n+ if (v.hasOwnProperty(i)) {\n+ v[i] = walk(i, v[i]);\n+ }\n+ }\n+ }\n+ return filter(k, v);\n+ }\n+ object = walk('', object);\n+ }\n+ }\n+ } catch (e) {\n+ // Fall through if the regexp test fails.\n+ }\n+\n+ if (this.keepData) {\n+ this.data = object;\n }\n+\n+ return object;\n },\n \n /**\n- * APIMethod: getCode\n- * Get the string SRS code.\n+ * APIMethod: write\n+ * Serialize an object into a JSON string.\n+ *\n+ * Parameters:\n+ * value - {String} The object, array, string, number, boolean or date\n+ * to be serialized.\n+ * pretty - {Boolean} Structure the output with newlines and indentation.\n+ * Default is false.\n *\n * Returns:\n- * {String} The SRS code.\n+ * {String} The JSON string representation of the input value.\n */\n- getCode: function() {\n- return this.proj ? this.proj.srsCode : this.projCode;\n+ write: function(value, pretty) {\n+ this.pretty = !!pretty;\n+ var json = null;\n+ var type = typeof value;\n+ if (this.serialize[type]) {\n+ try {\n+ json = (!this.pretty && this.nativeJSON) ?\n+ JSON.stringify(value) :\n+ this.serialize[type].apply(this, [value]);\n+ } catch (err) {\n+ OpenLayers.Console.error(\"Trouble serializing: \" + err);\n+ }\n+ }\n+ return json;\n },\n \n /**\n- * APIMethod: getUnits\n- * Get the units string for the projection -- returns null if \n- * proj4js is not available.\n+ * Method: writeIndent\n+ * Output an indentation string depending on the indentation level.\n *\n * Returns:\n- * {String} The units abbreviation.\n+ * {String} An appropriate indentation string.\n */\n- getUnits: function() {\n- return this.proj ? this.proj.units : null;\n+ writeIndent: function() {\n+ var pieces = [];\n+ if (this.pretty) {\n+ for (var i = 0; i < this.level; ++i) {\n+ pieces.push(this.indent);\n+ }\n+ }\n+ return pieces.join('');\n },\n \n /**\n- * Method: toString\n- * Convert projection to string (getCode wrapper).\n+ * Method: writeNewline\n+ * Output a string representing a newline if in pretty printing mode.\n *\n * Returns:\n- * {String} The projection code.\n+ * {String} A string representing a new line.\n */\n- toString: function() {\n- return this.getCode();\n+ writeNewline: function() {\n+ return (this.pretty) ? this.newline : '';\n },\n \n /**\n- * Method: equals\n- * Test equality of two projection instances. Determines equality based\n- * soley on the projection code.\n+ * Method: writeSpace\n+ * Output a string representing a space if in pretty printing mode.\n *\n * Returns:\n- * {Boolean} The two projections are equivalent.\n+ * {String} A space.\n */\n- equals: function(projection) {\n- var p = projection,\n- equals = false;\n- if (p) {\n- if (!(p instanceof OpenLayers.Projection)) {\n- p = new OpenLayers.Projection(p);\n- }\n- if ((typeof Proj4js == \"object\") && this.proj.defData && p.proj.defData) {\n- equals = this.proj.defData.replace(this.titleRegEx, \"\") ==\n- p.proj.defData.replace(this.titleRegEx, \"\");\n- } else if (p.getCode) {\n- var source = this.getCode(),\n- target = p.getCode();\n- equals = source == target ||\n- !!OpenLayers.Projection.transforms[source] &&\n- OpenLayers.Projection.transforms[source][target] ===\n- OpenLayers.Projection.nullTransform;\n- }\n- }\n- return equals;\n+ writeSpace: function() {\n+ return (this.pretty) ? this.space : '';\n },\n \n- /* Method: destroy\n- * Destroy projection object.\n+ /**\n+ * Property: serialize\n+ * Object with properties corresponding to the serializable data types.\n+ * Property values are functions that do the actual serializing.\n */\n- destroy: function() {\n- delete this.proj;\n- delete this.projCode;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Projection\"\n-});\n+ serialize: {\n+ /**\n+ * Method: serialize.object\n+ * Transform an object into a JSON string.\n+ *\n+ * Parameters:\n+ * object - {Object} The object to be serialized.\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the object.\n+ */\n+ 'object': function(object) {\n+ // three special objects that we want to treat differently\n+ if (object == null) {\n+ return \"null\";\n+ }\n+ if (object.constructor == Date) {\n+ return this.serialize.date.apply(this, [object]);\n+ }\n+ if (object.constructor == Array) {\n+ return this.serialize.array.apply(this, [object]);\n+ }\n+ var pieces = ['{'];\n+ this.level += 1;\n+ var key, keyJSON, valueJSON;\n \n-/**\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+ var addComma = false;\n+ for (key in object) {\n+ if (object.hasOwnProperty(key)) {\n+ // recursive calls need to allow for sub-classing\n+ keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [key, this.pretty]);\n+ valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [object[key], this.pretty]);\n+ if (keyJSON != null && valueJSON != null) {\n+ if (addComma) {\n+ pieces.push(',');\n+ }\n+ pieces.push(this.writeNewline(), this.writeIndent(),\n+ keyJSON, ':', this.writeSpace(), valueJSON);\n+ addComma = true;\n+ }\n+ }\n+ }\n \n-/**\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+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), '}');\n+ return pieces.join('');\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+ * Method: serialize.array\n+ * Transform an array into a JSON string.\n+ *\n+ * Parameters:\n+ * array - {Array} The array to be serialized\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the array.\n+ */\n+ 'array': function(array) {\n+ var json;\n+ var pieces = ['['];\n+ this.level += 1;\n \n-/**\n- * 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+ for (var i = 0, len = array.length; i < len; ++i) {\n+ // recursive calls need to allow for sub-classing\n+ json = OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [array[i], this.pretty]);\n+ if (json != null) {\n+ if (i > 0) {\n+ pieces.push(',');\n+ }\n+ pieces.push(this.writeNewline(), this.writeIndent(), json);\n+ }\n }\n- }\n- }\n- 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+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), ']');\n+ return pieces.join('');\n+ },\n \n- var pole = 20037508.34;\n+ /**\n+ * Method: serialize.string\n+ * Transform a string into a JSON string.\n+ *\n+ * Parameters:\n+ * string - {String} The string to be serialized\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the string.\n+ */\n+ 'string': function(string) {\n+ // If the string contains no control characters, no quote characters, and no\n+ // backslash characters, then we can simply slap some quotes around it.\n+ // Otherwise we must also replace the offending characters with safe\n+ // sequences. \n+ var m = {\n+ '\\b': '\\\\b',\n+ '\\t': '\\\\t',\n+ '\\n': '\\\\n',\n+ '\\f': '\\\\f',\n+ '\\r': '\\\\r',\n+ '\"': '\\\\\"',\n+ '\\\\': '\\\\\\\\'\n+ };\n+ if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n+ return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n+ var c = m[b];\n+ if (c) {\n+ return c;\n+ }\n+ c = b.charCodeAt();\n+ return '\\\\u00' +\n+ Math.floor(c / 16).toString(16) +\n+ (c % 16).toString(16);\n+ }) + '\"';\n+ }\n+ return '\"' + string + '\"';\n+ },\n \n- 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+ * Method: serialize.number\n+ * Transform a number into a JSON string.\n+ *\n+ * Parameters:\n+ * number - {Number} The number to be serialized.\n+ *\n+ * Returns:\n+ * {String} A JSON string representing the number.\n+ */\n+ 'number': function(number) {\n+ return isFinite(number) ? String(number) : \"null\";\n+ },\n \n- 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+ * Method: serialize.boolean\n+ * Transform a boolean into a JSON string.\n+ *\n+ * Parameters:\n+ * bool - {Boolean} The boolean to be serialized.\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the boolean.\n+ */\n+ 'boolean': function(bool) {\n+ return String(bool);\n+ },\n \n- 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+ * Method: serialize.object\n+ * Transform a date into a JSON string.\n+ *\n+ * Parameters:\n+ * date - {Date} The date to be serialized.\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the date.\n+ */\n+ 'date': function(date) {\n+ function format(number) {\n+ // Format integers to have at least two digits.\n+ return (number < 10) ? '0' + number : number;\n }\n+ return '\"' + date.getFullYear() + '-' +\n+ format(date.getMonth() + 1) + '-' +\n+ format(date.getDate()) + 'T' +\n+ format(date.getHours()) + ':' +\n+ format(date.getMinutes()) + ':' +\n+ format(date.getSeconds()) + '\"';\n }\n- }\n+ },\n \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+ CLASS_NAME: \"OpenLayers.Format.JSON\"\n \n-})();\n+});\n /* ======================================================================\n- OpenLayers/Map.js\n+ OpenLayers/Format/GeoJSON.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/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+ * @requires OpenLayers/Format/JSON.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/MultiPoint.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Geometry/MultiLineString.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n+ * @requires OpenLayers/Geometry/MultiPolygon.js\n+ * @requires OpenLayers/Console.js\n */\n \n /**\n- * Class: OpenLayers.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+ * Class: OpenLayers.Format.GeoJSON\n+ * Read and write GeoJSON. Create a new parser with the\n+ * constructor.\n+ *\n+ * Inherits from:\n+ * - \n */\n-OpenLayers.Map = OpenLayers.Class({\n+OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {\n \n /**\n- * Constant: Z_INDEX_BASE\n- * {Object} Base z-indexes for different classes of thing \n+ * APIProperty: ignoreExtraDims\n+ * {Boolean} Ignore dimensions higher than 2 when reading geometry\n+ * coordinates.\n */\n- Z_INDEX_BASE: {\n- BaseLayer: 100,\n- Overlay: 325,\n- Feature: 725,\n- Popup: 750,\n- Control: 1000\n- },\n+ ignoreExtraDims: false,\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+ * Constructor: OpenLayers.Format.GeoJSON\n+ * Create a new parser for GeoJSON.\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+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\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+ * APIMethod: read\n+ * Deserialize a GeoJSON string.\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+ * Parameters:\n+ * json - {String} A GeoJSON string\n+ * type - {String} Optional string that determines the structure of\n+ * the output. Supported values are \"Geometry\", \"Feature\", and\n+ * \"FeatureCollection\". If absent or null, a default of\n+ * \"FeatureCollection\" is assumed.\n+ * filter - {Function} A function which will be called for every key and\n+ * value at every level of the final result. Each value will be\n+ * replaced by the result of the filter function. This can be used to\n+ * reform generic objects into instances of classes, or to transform\n+ * date strings into Date objects.\n *\n- * 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+ * Returns: \n+ * {Object} The return depends on the value of the type argument. If type\n+ * is \"FeatureCollection\" (the default), the return will be an array\n+ * of . If type is \"Geometry\", the input json\n+ * must represent a single geometry, and the return will be an\n+ * . If type is \"Feature\", the input json must\n+ * represent a single feature, and the return will be an\n+ * .\n */\n- events: null,\n+ read: function(json, type, filter) {\n+ type = (type) ? type : \"FeatureCollection\";\n+ var results = null;\n+ var obj = null;\n+ if (typeof json == \"string\") {\n+ obj = OpenLayers.Format.JSON.prototype.read.apply(this,\n+ [json, filter]);\n+ } else {\n+ obj = json;\n+ }\n+ if (!obj) {\n+ OpenLayers.Console.error(\"Bad JSON: \" + json);\n+ } else if (typeof(obj.type) != \"string\") {\n+ OpenLayers.Console.error(\"Bad GeoJSON - no type: \" + json);\n+ } else if (this.isValidType(obj, type)) {\n+ switch (type) {\n+ case \"Geometry\":\n+ try {\n+ results = this.parseGeometry(obj);\n+ } catch (err) {\n+ OpenLayers.Console.error(err);\n+ }\n+ break;\n+ case \"Feature\":\n+ try {\n+ results = this.parseFeature(obj);\n+ results.type = \"Feature\";\n+ } catch (err) {\n+ OpenLayers.Console.error(err);\n+ }\n+ break;\n+ case \"FeatureCollection\":\n+ // for type FeatureCollection, we allow input to be any type\n+ results = [];\n+ switch (obj.type) {\n+ case \"Feature\":\n+ try {\n+ results.push(this.parseFeature(obj));\n+ } catch (err) {\n+ results = null;\n+ OpenLayers.Console.error(err);\n+ }\n+ break;\n+ case \"FeatureCollection\":\n+ for (var i = 0, len = obj.features.length; i < len; ++i) {\n+ try {\n+ results.push(this.parseFeature(obj.features[i]));\n+ } catch (err) {\n+ results = null;\n+ OpenLayers.Console.error(err);\n+ }\n+ }\n+ break;\n+ default:\n+ try {\n+ var geom = this.parseGeometry(obj);\n+ results.push(new OpenLayers.Feature.Vector(geom));\n+ } catch (err) {\n+ results = null;\n+ OpenLayers.Console.error(err);\n+ }\n+ }\n+ break;\n+ }\n+ }\n+ return results;\n+ },\n \n /**\n- * APIProperty: 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+ * Method: isValidType\n+ * Check if a GeoJSON object is a valid representative of the given type.\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+ * Returns:\n+ * {Boolean} The object is valid GeoJSON object of the given type.\n */\n- layerContainerDiv: null,\n+ isValidType: function(obj, type) {\n+ var valid = false;\n+ switch (type) {\n+ case \"Geometry\":\n+ if (OpenLayers.Util.indexOf(\n+ [\"Point\", \"MultiPoint\", \"LineString\", \"MultiLineString\",\n+ \"Polygon\", \"MultiPolygon\", \"Box\", \"GeometryCollection\"\n+ ],\n+ obj.type) == -1) {\n+ // unsupported geometry type\n+ OpenLayers.Console.error(\"Unsupported geometry type: \" +\n+ obj.type);\n+ } else {\n+ valid = true;\n+ }\n+ break;\n+ case \"FeatureCollection\":\n+ // allow for any type to be converted to a feature collection\n+ valid = true;\n+ break;\n+ default:\n+ // for Feature types must match\n+ if (obj.type == type) {\n+ valid = true;\n+ } else {\n+ OpenLayers.Console.error(\"Cannot convert types from \" +\n+ obj.type + \" to \" + type);\n+ }\n+ }\n+ return valid;\n+ },\n \n /**\n- * APIProperty: layers\n- * {Array()} Ordered list of layers in the map\n+ * Method: parseFeature\n+ * Convert a feature object from GeoJSON into an\n+ * .\n+ *\n+ * Parameters:\n+ * obj - {Object} An object created from a GeoJSON object\n+ *\n+ * Returns:\n+ * {} A feature.\n */\n- layers: null,\n+ parseFeature: function(obj) {\n+ var feature, geometry, attributes, bbox;\n+ attributes = (obj.properties) ? obj.properties : {};\n+ bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;\n+ try {\n+ geometry = this.parseGeometry(obj.geometry);\n+ } catch (err) {\n+ // deal with bad geometries\n+ throw err;\n+ }\n+ feature = new OpenLayers.Feature.Vector(geometry, attributes);\n+ if (bbox) {\n+ feature.bounds = OpenLayers.Bounds.fromArray(bbox);\n+ }\n+ if (obj.id) {\n+ feature.fid = obj.id;\n+ }\n+ return feature;\n+ },\n \n /**\n- * APIProperty: controls\n- * {Array()} List of controls associated with the map.\n+ * Method: parseGeometry\n+ * Convert a geometry object from GeoJSON into an .\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+ * Parameters:\n+ * obj - {Object} An object created from a GeoJSON object\n+ *\n+ * Returns: \n+ * {} A geometry.\n */\n- controls: null,\n+ parseGeometry: function(obj) {\n+ if (obj == null) {\n+ return null;\n+ }\n+ var geometry, collection = false;\n+ if (obj.type == \"GeometryCollection\") {\n+ if (!(OpenLayers.Util.isArray(obj.geometries))) {\n+ throw \"GeometryCollection must have geometries array: \" + obj;\n+ }\n+ var numGeom = obj.geometries.length;\n+ var components = new Array(numGeom);\n+ for (var i = 0; i < numGeom; ++i) {\n+ components[i] = this.parseGeometry.apply(\n+ this, [obj.geometries[i]]\n+ );\n+ }\n+ geometry = new OpenLayers.Geometry.Collection(components);\n+ collection = true;\n+ } else {\n+ if (!(OpenLayers.Util.isArray(obj.coordinates))) {\n+ throw \"Geometry must have coordinates array: \" + obj;\n+ }\n+ if (!this.parseCoords[obj.type.toLowerCase()]) {\n+ throw \"Unsupported geometry type: \" + obj.type;\n+ }\n+ try {\n+ geometry = this.parseCoords[obj.type.toLowerCase()].apply(\n+ this, [obj.coordinates]\n+ );\n+ } catch (err) {\n+ // deal with bad coordinates\n+ throw err;\n+ }\n+ }\n+ // We don't reproject collections because the children are reprojected\n+ // for us when they are created.\n+ if (this.internalProjection && this.externalProjection && !collection) {\n+ geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ }\n+ return geometry;\n+ },\n \n /**\n- * Property: popups\n- * {Array()} List of popups associated with the map\n+ * Property: parseCoords\n+ * Object with properties corresponding to the GeoJSON geometry types.\n+ * Property values are functions that do the actual parsing.\n */\n- popups: null,\n+ parseCoords: {\n+ /**\n+ * Method: parseCoords.point\n+ * Convert a coordinate array from GeoJSON into an\n+ * .\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {} A geometry.\n+ */\n+ \"point\": function(array) {\n+ if (this.ignoreExtraDims == false &&\n+ array.length != 2) {\n+ throw \"Only 2D points are supported: \" + array;\n+ }\n+ return new OpenLayers.Geometry.Point(array[0], array[1]);\n+ },\n \n- /**\n- * APIProperty: baseLayer\n- * {} The currently selected base layer. This determines\n- * min/max zoom level, projection, etc.\n- */\n- baseLayer: null,\n+ /**\n+ * Method: parseCoords.multipoint\n+ * Convert a coordinate array from GeoJSON into an\n+ * .\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {} A geometry.\n+ */\n+ \"multipoint\": function(array) {\n+ var points = [];\n+ var p = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ p = this.parseCoords[\"point\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n+ }\n+ points.push(p);\n+ }\n+ return new OpenLayers.Geometry.MultiPoint(points);\n+ },\n \n- /**\n- * Property: center\n- * {} The current center of the map\n- */\n- center: null,\n+ /**\n+ * Method: parseCoords.linestring\n+ * Convert a coordinate array from GeoJSON into an\n+ * .\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {} A geometry.\n+ */\n+ \"linestring\": function(array) {\n+ var points = [];\n+ var p = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ p = this.parseCoords[\"point\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n+ }\n+ points.push(p);\n+ }\n+ return new OpenLayers.Geometry.LineString(points);\n+ },\n \n- /**\n- * Property: resolution\n- * {Float} The resolution of the map.\n- */\n- resolution: null,\n+ /**\n+ * Method: parseCoords.multilinestring\n+ * Convert a coordinate array from GeoJSON into an\n+ * .\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {} A geometry.\n+ */\n+ \"multilinestring\": function(array) {\n+ var lines = [];\n+ var l = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ l = this.parseCoords[\"linestring\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n+ }\n+ lines.push(l);\n+ }\n+ return new OpenLayers.Geometry.MultiLineString(lines);\n+ },\n \n- /**\n- * Property: zoom\n- * {Integer} The current zoom level of the map\n- */\n- zoom: 0,\n+ /**\n+ * Method: parseCoords.polygon\n+ * Convert a coordinate array from GeoJSON into an\n+ * .\n+ *\n+ * Returns:\n+ * {} A geometry.\n+ */\n+ \"polygon\": function(array) {\n+ var rings = [];\n+ var r, l;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ l = this.parseCoords[\"linestring\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n+ }\n+ r = new OpenLayers.Geometry.LinearRing(l.components);\n+ rings.push(r);\n+ }\n+ return new OpenLayers.Geometry.Polygon(rings);\n+ },\n \n- /**\n- * Property: panRatio\n- * {Float} The ratio of the current extent within\n- * which panning will tween.\n- */\n- panRatio: 1.5,\n+ /**\n+ * Method: parseCoords.multipolygon\n+ * Convert a coordinate array from GeoJSON into an\n+ * .\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {} A geometry.\n+ */\n+ \"multipolygon\": function(array) {\n+ var polys = [];\n+ var p = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ p = this.parseCoords[\"polygon\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n+ }\n+ polys.push(p);\n+ }\n+ return new OpenLayers.Geometry.MultiPolygon(polys);\n+ },\n \n- /**\n- * APIProperty: options\n- * {Object} The options object passed to the class constructor. Read-only.\n- */\n- options: null,\n+ /**\n+ * Method: parseCoords.box\n+ * Convert a coordinate array from GeoJSON into an\n+ * .\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {} A geometry.\n+ */\n+ \"box\": function(array) {\n+ if (array.length != 2) {\n+ throw \"GeoJSON box coordinates must have 2 elements\";\n+ }\n+ return new OpenLayers.Geometry.Polygon([\n+ new OpenLayers.Geometry.LinearRing([\n+ new OpenLayers.Geometry.Point(array[0][0], array[0][1]),\n+ new OpenLayers.Geometry.Point(array[1][0], array[0][1]),\n+ new OpenLayers.Geometry.Point(array[1][0], array[1][1]),\n+ new OpenLayers.Geometry.Point(array[0][0], array[1][1]),\n+ new OpenLayers.Geometry.Point(array[0][0], array[0][1])\n+ ])\n+ ]);\n+ }\n \n- // Options\n+ },\n \n /**\n- * APIProperty: tileSize\n- * {} Set in the map options to override the default tile\n- * size for this map.\n+ * APIMethod: write\n+ * Serialize a feature, geometry, array of features into a GeoJSON string.\n+ *\n+ * Parameters:\n+ * obj - {Object} An , ,\n+ * or an array of features.\n+ * pretty - {Boolean} Structure the output with newlines and indentation.\n+ * Default is false.\n+ *\n+ * Returns:\n+ * {String} The GeoJSON string representation of the input geometry,\n+ * features, or array of features.\n */\n- tileSize: null,\n+ write: function(obj, pretty) {\n+ var geojson = {\n+ \"type\": null\n+ };\n+ if (OpenLayers.Util.isArray(obj)) {\n+ geojson.type = \"FeatureCollection\";\n+ var numFeatures = obj.length;\n+ geojson.features = new Array(numFeatures);\n+ for (var i = 0; i < numFeatures; ++i) {\n+ var element = obj[i];\n+ if (!element instanceof OpenLayers.Feature.Vector) {\n+ var msg = \"FeatureCollection only supports collections \" +\n+ \"of features: \" + element;\n+ throw msg;\n+ }\n+ geojson.features[i] = this.extract.feature.apply(\n+ this, [element]\n+ );\n+ }\n+ } else if (obj.CLASS_NAME.indexOf(\"OpenLayers.Geometry\") == 0) {\n+ geojson = this.extract.geometry.apply(this, [obj]);\n+ } else if (obj instanceof OpenLayers.Feature.Vector) {\n+ geojson = this.extract.feature.apply(this, [obj]);\n+ if (obj.layer && obj.layer.projection) {\n+ geojson.crs = this.createCRSObject(obj);\n+ }\n+ }\n+ return OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [geojson, pretty]);\n+ },\n \n /**\n- * 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+ * Method: createCRSObject\n+ * Create the CRS object for an object.\n+ *\n+ * Parameters:\n+ * object - {} \n+ *\n+ * Returns:\n+ * {Object} An object which can be assigned to the crs property\n+ * of a GeoJSON object.\n */\n- projection: \"EPSG:4326\",\n+ createCRSObject: function(object) {\n+ var proj = object.layer.projection.toString();\n+ var crs = {};\n+ if (proj.match(/epsg:/i)) {\n+ var code = parseInt(proj.substring(proj.indexOf(\":\") + 1));\n+ if (code == 4326) {\n+ crs = {\n+ \"type\": \"name\",\n+ \"properties\": {\n+ \"name\": \"urn:ogc:def:crs:OGC:1.3:CRS84\"\n+ }\n+ };\n+ } else {\n+ crs = {\n+ \"type\": \"name\",\n+ \"properties\": {\n+ \"name\": \"EPSG:\" + code\n+ }\n+ };\n+ }\n+ }\n+ return crs;\n+ },\n \n /**\n- * 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+ * Property: extract\n+ * Object with properties corresponding to the GeoJSON types.\n+ * Property values are functions that do the actual value extraction.\n */\n- units: null,\n+ extract: {\n+ /**\n+ * Method: extract.feature\n+ * Return a partial GeoJSON object representing a single feature.\n+ *\n+ * Parameters:\n+ * feature - {}\n+ *\n+ * Returns:\n+ * {Object} An object representing the point.\n+ */\n+ 'feature': function(feature) {\n+ var geom = this.extract.geometry.apply(this, [feature.geometry]);\n+ var json = {\n+ \"type\": \"Feature\",\n+ \"properties\": feature.attributes,\n+ \"geometry\": geom\n+ };\n+ if (feature.fid != null) {\n+ json.id = feature.fid;\n+ }\n+ return json;\n+ },\n \n- /**\n- * 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+ * Method: extract.geometry\n+ * Return a GeoJSON object representing a single geometry.\n+ *\n+ * Parameters:\n+ * geometry - {}\n+ *\n+ * Returns:\n+ * {Object} An object representing the geometry.\n+ */\n+ 'geometry': function(geometry) {\n+ if (geometry == null) {\n+ return null;\n+ }\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry = geometry.clone();\n+ geometry.transform(this.internalProjection,\n+ this.externalProjection);\n+ }\n+ var geometryType = geometry.CLASS_NAME.split('.')[2];\n+ var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);\n+ var json;\n+ if (geometryType == \"Collection\") {\n+ json = {\n+ \"type\": \"GeometryCollection\",\n+ \"geometries\": data\n+ };\n+ } else {\n+ json = {\n+ \"type\": geometryType,\n+ \"coordinates\": data\n+ };\n+ }\n \n- /**\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+ return json;\n+ },\n \n- /**\n- * APIProperty: minResolution\n- * {Float}\n- */\n- minResolution: null,\n+ /**\n+ * Method: extract.point\n+ * Return an array of coordinates from a point.\n+ *\n+ * Parameters:\n+ * point - {}\n+ *\n+ * Returns: \n+ * {Array} An array of coordinates representing the point.\n+ */\n+ 'point': function(point) {\n+ return [point.x, point.y];\n+ },\n \n- /**\n- * APIProperty: maxScale\n- * {Float}\n- */\n- maxScale: null,\n+ /**\n+ * Method: extract.multipoint\n+ * Return an array of point coordinates from a multipoint.\n+ *\n+ * Parameters:\n+ * multipoint - {}\n+ *\n+ * Returns:\n+ * {Array} An array of point coordinate arrays representing\n+ * the multipoint.\n+ */\n+ 'multipoint': function(multipoint) {\n+ var array = [];\n+ for (var i = 0, len = multipoint.components.length; i < len; ++i) {\n+ array.push(this.extract.point.apply(this, [multipoint.components[i]]));\n+ }\n+ return array;\n+ },\n \n- /**\n- * APIProperty: minScale\n- * {Float}\n- */\n- minScale: null,\n+ /**\n+ * Method: extract.linestring\n+ * Return an array of coordinate arrays from a linestring.\n+ *\n+ * Parameters:\n+ * linestring - {}\n+ *\n+ * Returns:\n+ * {Array} An array of coordinate arrays representing\n+ * the linestring.\n+ */\n+ 'linestring': function(linestring) {\n+ var array = [];\n+ for (var i = 0, len = linestring.components.length; i < len; ++i) {\n+ array.push(this.extract.point.apply(this, [linestring.components[i]]));\n+ }\n+ return array;\n+ },\n \n- /**\n- * 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+ * Method: extract.multilinestring\n+ * Return an array of linestring arrays from a linestring.\n+ * \n+ * Parameters:\n+ * multilinestring - {}\n+ * \n+ * Returns:\n+ * {Array} An array of linestring arrays representing\n+ * the multilinestring.\n+ */\n+ 'multilinestring': function(multilinestring) {\n+ var array = [];\n+ for (var i = 0, len = multilinestring.components.length; i < len; ++i) {\n+ array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));\n+ }\n+ return array;\n+ },\n \n- /**\n- * 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+ * Method: extract.polygon\n+ * Return an array of linear ring arrays from a polygon.\n+ *\n+ * Parameters:\n+ * polygon - {}\n+ * \n+ * Returns:\n+ * {Array} An array of linear ring arrays representing the polygon.\n+ */\n+ 'polygon': function(polygon) {\n+ var array = [];\n+ for (var i = 0, len = polygon.components.length; i < len; ++i) {\n+ array.push(this.extract.linestring.apply(this, [polygon.components[i]]));\n+ }\n+ return array;\n+ },\n \n- /**\n- * 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+ * Method: extract.multipolygon\n+ * Return an array of polygon arrays from a multipolygon.\n+ * \n+ * Parameters:\n+ * multipolygon - {}\n+ * \n+ * Returns:\n+ * {Array} An array of polygon arrays representing\n+ * the multipolygon\n+ */\n+ 'multipolygon': function(multipolygon) {\n+ var array = [];\n+ for (var i = 0, len = multipolygon.components.length; i < len; ++i) {\n+ array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));\n+ }\n+ return array;\n+ },\n \n- /**\n- * 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+ * Method: extract.collection\n+ * Return an array of geometries from a geometry collection.\n+ * \n+ * Parameters:\n+ * collection - {}\n+ * \n+ * Returns:\n+ * {Array} An array of geometry objects representing the geometry\n+ * collection.\n+ */\n+ 'collection': function(collection) {\n+ var len = collection.components.length;\n+ var array = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ array[i] = this.extract.geometry.apply(\n+ this, [collection.components[i]]\n+ );\n+ }\n+ return array;\n+ }\n \n- /**\n- * 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- */\n- displayProjection: null,\n+ },\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+ CLASS_NAME: \"OpenLayers.Format.GeoJSON\"\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+ OpenLayers/Format/XML.js\n+ ====================================================================== */\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+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * APIProperty: 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+ * @requires OpenLayers/Format.js\n+ */\n \n- /**\n- * Property: panTween\n- * {} Animated panning tween object, see panTo()\n- */\n- panTween: null,\n+/**\n+ * Class: OpenLayers.Format.XML\n+ * Read and write XML. For cross-browser XML generation, use methods on an\n+ * instance of the XML format class instead of on document.\n+ * The DOM creation and traversing methods exposed here all mimic the\n+ * W3C XML DOM methods. Create a new parser with the\n+ * constructor.\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {\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+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs. Properties\n+ * of this object should not be set individually. Read-only. All\n+ * XML subclasses should have their own namespaces object. Use\n+ * to add or set a namespace alias after construction.\n */\n- panMethod: OpenLayers.Easing.Expo.easeOut,\n+ namespaces: null,\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+ * Property: namespaceAlias\n+ * {Object} Mapping of namespace URI to namespace alias. This object\n+ * is read-only. Use to add or set a namespace alias.\n */\n- panDuration: 50,\n+ namespaceAlias: null,\n \n /**\n- * Property: zoomTween\n- * {} Animated zooming tween object, see zoomTo()\n+ * Property: defaultPrefix\n+ * {String} The default namespace alias for creating element nodes.\n */\n- zoomTween: null,\n+ defaultPrefix: 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+ * 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- zoomMethod: OpenLayers.Easing.Quad.easeOut,\n+ readers: {},\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+ * Property: writers\n+ * As a compliment to the property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n */\n- zoomDuration: 20,\n+ writers: {},\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+ * Property: xmldom\n+ * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM\n+ * object. It is not intended to be a browser sniffing property.\n+ * Instead, the xmldom property is used instead of document\n+ * where namespaced node creation methods are not supported. In all\n+ * other browsers, this remains null.\n */\n- paddingForPopups: null,\n+ xmldom: null,\n \n /**\n- * Property: layerContainerOriginPx\n- * {Object} Cached object representing the layer container origin (in pixels).\n+ * Constructor: OpenLayers.Format.XML\n+ * Construct an XML parser. The parser is used to read and write XML.\n+ * Reading XML from a string returns a DOM element. Writing XML from\n+ * a DOM element returns a string.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on\n+ * the object.\n */\n- layerContainerOriginPx: null,\n+ initialize: function(options) {\n+ if (window.ActiveXObject) {\n+ this.xmldom = new ActiveXObject(\"Microsoft.XMLDOM\");\n+ }\n+ OpenLayers.Format.prototype.initialize.apply(this, [options]);\n+ // clone the namespace object and set all namespace aliases\n+ this.namespaces = OpenLayers.Util.extend({}, this.namespaces);\n+ this.namespaceAlias = {};\n+ for (var alias in this.namespaces) {\n+ this.namespaceAlias[this.namespaces[alias]] = alias;\n+ }\n+ },\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+ * APIMethod: destroy\n+ * Clean up.\n */\n- minPx: null,\n+ destroy: function() {\n+ this.xmldom = null;\n+ OpenLayers.Format.prototype.destroy.apply(this, arguments);\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: setNamespace\n+ * Set a namespace alias and URI for the format.\n+ *\n+ * Parameters:\n+ * alias - {String} The namespace alias (prefix).\n+ * uri - {String} The namespace URI.\n */\n- maxPx: null,\n+ setNamespace: function(alias, uri) {\n+ this.namespaces[alias] = uri;\n+ this.namespaceAlias[uri] = alias;\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+ * APIMethod: read\n+ * Deserialize a XML string and return a DOM node.\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- *\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+ * text - {String} A XML string\n+ \n+ * Returns:\n+ * {DOMElement} A DOM node\n */\n- initialize: function(div, options) {\n-\n- // If only one argument is provided, check if it is an object.\n- if (arguments.length === 1 && typeof div === \"object\") {\n- options = div;\n- div = options && options.div;\n- }\n-\n- // Simple-type defaults are set in class definition. \n- // Now set complex-type defaults \n- this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,\n- OpenLayers.Map.TILE_HEIGHT);\n-\n- this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);\n-\n- this.theme = OpenLayers._getScriptLocation() +\n- 'theme/default/style.css';\n-\n- // backup original options\n- this.options = OpenLayers.Util.extend({}, options);\n-\n- // now override default options \n- OpenLayers.Util.extend(this, options);\n-\n- var projCode = this.projection instanceof OpenLayers.Projection ?\n- this.projection.projCode : this.projection;\n- OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);\n-\n- // allow extents and center to be arrays\n- if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {\n- this.maxExtent = new OpenLayers.Bounds(this.maxExtent);\n- }\n- if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {\n- this.minExtent = new OpenLayers.Bounds(this.minExtent);\n- }\n- if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {\n- this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);\n- }\n- if (this.center && !(this.center instanceof OpenLayers.LonLat)) {\n- this.center = new OpenLayers.LonLat(this.center);\n- }\n-\n- // initialize layers array\n- this.layers = [];\n-\n- this.id = OpenLayers.Util.createUniqueID(\"OpenLayers.Map_\");\n-\n- this.div = OpenLayers.Util.getElement(div);\n- if (!this.div) {\n- this.div = document.createElement(\"div\");\n- this.div.style.height = \"1px\";\n- this.div.style.width = \"1px\";\n+ read: function(text) {\n+ var index = text.indexOf('<');\n+ if (index > 0) {\n+ text = text.substring(index);\n }\n+ var node = OpenLayers.Util.Try(\n+ OpenLayers.Function.bind((\n+ function() {\n+ var xmldom;\n+ /**\n+ * Since we want to be able to call this method on the prototype\n+ * itself, this.xmldom may not exist even if in IE.\n+ */\n+ if (window.ActiveXObject && !this.xmldom) {\n+ xmldom = new ActiveXObject(\"Microsoft.XMLDOM\");\n+ } else {\n+ xmldom = this.xmldom;\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+ xmldom.loadXML(text);\n+ return xmldom;\n+ }\n+ ), this),\n+ function() {\n+ return new DOMParser().parseFromString(text, 'text/xml');\n+ },\n+ function() {\n+ var req = new XMLHttpRequest();\n+ req.open(\"GET\", \"data:\" + \"text/xml\" +\n+ \";charset=utf-8,\" + encodeURIComponent(text), false);\n+ if (req.overrideMimeType) {\n+ req.overrideMimeType(\"text/xml\");\n+ }\n+ req.send(null);\n+ return req.responseXML;\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+ if (this.keepData) {\n+ this.data = node;\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+ return node;\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+ * APIMethod: write\n+ * Serialize a DOM node into a XML string.\n+ * \n+ * Parameters:\n+ * node - {DOMElement} A DOM node.\n+ *\n+ * Returns:\n+ * {String} The XML string representation of the input node.\n+ */\n+ write: function(node) {\n+ var data;\n+ if (this.xmldom) {\n+ data = node.xml;\n+ } else {\n+ var serializer = new XMLSerializer();\n+ if (node.nodeType == 1) {\n+ // Add nodes to a document before serializing. Everything else\n+ // is serialized as is. This may need more work. See #1218 .\n+ var doc = document.implementation.createDocument(\"\", \"\", null);\n+ if (doc.importNode) {\n+ node = doc.importNode(node, true);\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+ doc.appendChild(node);\n+ data = serializer.serializeToString(doc);\n+ } else {\n+ data = serializer.serializeToString(node);\n }\n }\n+ return data;\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+ * APIMethod: createElementNS\n+ * Create a new element with namespace. This node can be appended to\n+ * another node with the standard node.appendChild method. For\n+ * cross-browser support, this method must be used instead of\n+ * document.createElementNS.\n+ *\n+ * Parameters:\n+ * uri - {String} Namespace URI for the element.\n+ * name - {String} The qualified name of the element (prefix:localname).\n+ * \n+ * Returns:\n+ * {Element} A DOM element with namespace.\n+ */\n+ createElementNS: function(uri, name) {\n+ var element;\n+ if (this.xmldom) {\n+ if (typeof uri == \"string\") {\n+ element = this.xmldom.createNode(1, name, uri);\n+ } else {\n+ element = this.xmldom.createNode(1, name, \"\");\n }\n+ } else {\n+ element = document.createElementNS(uri, name);\n }\n+ return element;\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+ * APIMethod: createDocumentFragment\n+ * Create a document fragment node that can be appended to another node\n+ * created by createElementNS. This will call \n+ * document.createDocumentFragment outside of IE. In IE, the ActiveX\n+ * object's createDocumentFragment method is used.\n+ *\n+ * Returns:\n+ * {Element} A document fragment.\n+ */\n+ createDocumentFragment: function() {\n+ var element;\n+ if (this.xmldom) {\n+ element = this.xmldom.createDocumentFragment();\n+ } else {\n+ element = document.createDocumentFragment();\n }\n+ return element;\n+ },\n \n- if (this.panMethod) {\n- this.panTween = new OpenLayers.Tween(this.panMethod);\n+ /**\n+ * APIMethod: createTextNode\n+ * Create a text node. This node can be appended to another node with\n+ * the standard node.appendChild method. For cross-browser support,\n+ * this method must be used instead of document.createTextNode.\n+ * \n+ * Parameters:\n+ * text - {String} The text of the node.\n+ * \n+ * Returns: \n+ * {DOMElement} A DOM text node.\n+ */\n+ createTextNode: function(text) {\n+ var node;\n+ if (typeof text !== \"string\") {\n+ text = String(text);\n }\n- if (this.zoomMethod && this.applyTransform.transform) {\n- this.zoomTween = new OpenLayers.Tween(this.zoomMethod);\n+ if (this.xmldom) {\n+ node = this.xmldom.createTextNode(text);\n+ } else {\n+ node = document.createTextNode(text);\n }\n+ return node;\n },\n \n- /** \n- * APIMethod: getViewport\n- * Get the DOMElement representing the view port.\n- *\n+ /**\n+ * APIMethod: getElementsByTagNameNS\n+ * Get a list of elements on a node given the namespace URI and local name.\n+ * To return all nodes in a given namespace, use '*' for the name\n+ * argument. To return all nodes of a given (local) name, regardless\n+ * of namespace, use '*' for the uri argument.\n+ * \n+ * Parameters:\n+ * node - {Element} Node on which to search for other nodes.\n+ * uri - {String} Namespace URI.\n+ * name - {String} Local name of the tag (without the prefix).\n+ * \n * Returns:\n- * {DOMElement}\n+ * {NodeList} A node list or array of elements.\n */\n- getViewport: function() {\n- return this.viewPortDiv;\n+ getElementsByTagNameNS: function(node, uri, name) {\n+ var elements = [];\n+ if (node.getElementsByTagNameNS) {\n+ elements = node.getElementsByTagNameNS(uri, name);\n+ } else {\n+ // brute force method\n+ var allNodes = node.getElementsByTagName(\"*\");\n+ var potentialNode, fullName;\n+ for (var i = 0, len = allNodes.length; i < len; ++i) {\n+ potentialNode = allNodes[i];\n+ fullName = (potentialNode.prefix) ?\n+ (potentialNode.prefix + \":\" + name) : name;\n+ if ((name == \"*\") || (fullName == potentialNode.nodeName)) {\n+ if ((uri == \"*\") || (uri == potentialNode.namespaceURI)) {\n+ elements.push(potentialNode);\n+ }\n+ }\n+ }\n+ }\n+ return elements;\n },\n \n /**\n- * APIMethod: render\n- * Render the map to a specified container.\n+ * APIMethod: getAttributeNodeNS\n+ * Get an attribute node given the namespace URI and local name.\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+ * node - {Element} Node on which to search for attribute nodes.\n+ * uri - {String} Namespace URI.\n+ * name - {String} Local name of the attribute (without the prefix).\n+ * \n+ * Returns:\n+ * {DOMElement} An attribute node or null if none found.\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+ getAttributeNodeNS: function(node, uri, name) {\n+ var attributeNode = null;\n+ if (node.getAttributeNodeNS) {\n+ attributeNode = node.getAttributeNodeNS(uri, name);\n+ } else {\n+ var attributes = node.attributes;\n+ var potentialNode, fullName;\n+ for (var i = 0, len = attributes.length; i < len; ++i) {\n+ potentialNode = attributes[i];\n+ if (potentialNode.namespaceURI == uri) {\n+ fullName = (potentialNode.prefix) ?\n+ (potentialNode.prefix + \":\" + name) : name;\n+ if (fullName == potentialNode.nodeName) {\n+ attributeNode = potentialNode;\n+ break;\n+ }\n+ }\n+ }\n+ }\n+ return attributeNode;\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+ * APIMethod: getAttributeNS\n+ * Get an attribute value given the namespace URI and local name.\n+ * \n+ * Parameters:\n+ * node - {Element} Node on which to search for an attribute.\n+ * uri - {String} Namespace URI.\n+ * name - {String} Local name of the attribute (without the prefix).\n+ * \n+ * Returns:\n+ * {String} An attribute value or and empty string if none found.\n */\n- unloadDestroy: null,\n+ getAttributeNS: function(node, uri, name) {\n+ var attributeValue = \"\";\n+ if (node.getAttributeNS) {\n+ attributeValue = node.getAttributeNS(uri, name) || \"\";\n+ } else {\n+ var attributeNode = this.getAttributeNodeNS(node, uri, name);\n+ if (attributeNode) {\n+ attributeValue = attributeNode.nodeValue;\n+ }\n+ }\n+ return attributeValue;\n+ },\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+ * APIMethod: getChildValue\n+ * Get the textual value of the node if it exists, or return an\n+ * optional default string. Returns an empty string if no first child\n+ * exists and no default value is supplied.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} The element used to look for a first child value.\n+ * def - {String} Optional string to return in the event that no\n+ * first child value exists.\n+ *\n+ * Returns:\n+ * {String} The value of the first child of the given node.\n */\n- updateSizeDestroy: null,\n+ getChildValue: function(node, def) {\n+ var value = def || \"\";\n+ if (node) {\n+ for (var child = node.firstChild; child; child = child.nextSibling) {\n+ switch (child.nodeType) {\n+ case 3: // text node\n+ case 4: // cdata section\n+ value += child.nodeValue;\n+ }\n+ }\n+ }\n+ return value;\n+ },\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+ * APIMethod: isSimpleContent\n+ * Test if the given node has only simple content (i.e. no child element\n+ * nodes).\n+ *\n+ * Parameters:\n+ * node - {DOMElement} An element node.\n+ *\n+ * Returns:\n+ * {Boolean} The node has no child element nodes (nodes of type 1). \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+ isSimpleContent: function(node) {\n+ var simple = true;\n+ for (var child = node.firstChild; child; child = child.nextSibling) {\n+ if (child.nodeType === 1) {\n+ simple = false;\n+ break;\n+ }\n }\n+ return simple;\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+ * APIMethod: contentType\n+ * Determine the content type for a given node.\n+ *\n+ * Parameters:\n+ * node - {DOMElement}\n+ *\n+ * Returns:\n+ * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}\n+ * if the node has no, simple, complex, or mixed content.\n+ */\n+ contentType: function(node) {\n+ var simple = false,\n+ complex = false;\n \n- this.paddingForPopups = null;\n+ var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;\n \n- if (this.controls != null) {\n- for (var i = this.controls.length - 1; i >= 0; --i) {\n- this.controls[i].destroy();\n+ for (var child = node.firstChild; child; child = child.nextSibling) {\n+ switch (child.nodeType) {\n+ case 1: // element\n+ complex = true;\n+ break;\n+ case 8: // comment\n+ break;\n+ default:\n+ simple = true;\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 (complex && simple) {\n+ break;\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+ if (complex && simple) {\n+ type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;\n+ } else if (complex) {\n+ return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;\n+ } else if (simple) {\n+ return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;\n }\n+ return type;\n+ },\n \n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- this.eventListeners = null;\n+ /**\n+ * APIMethod: hasAttributeNS\n+ * Determine whether a node has a particular attribute matching the given\n+ * name and namespace.\n+ * \n+ * Parameters:\n+ * node - {Element} Node on which to search for an attribute.\n+ * uri - {String} Namespace URI.\n+ * name - {String} Local name of the attribute (without the prefix).\n+ * \n+ * Returns:\n+ * {Boolean} The node has an attribute matching the name and namespace.\n+ */\n+ hasAttributeNS: function(node, uri, name) {\n+ var found = false;\n+ if (node.hasAttributeNS) {\n+ found = node.hasAttributeNS(uri, name);\n+ } else {\n+ found = !!this.getAttributeNodeNS(node, uri, name);\n }\n- this.events.destroy();\n- this.events = null;\n-\n- this.options = null;\n+ return found;\n },\n \n /**\n- * APIMethod: setOptions\n- * Change the map options\n+ * APIMethod: setAttributeNS\n+ * Adds a new attribute or changes the value of an attribute with the given\n+ * namespace and name.\n *\n * Parameters:\n- * options - {Object} Hashtable of options to tag to the map\n+ * node - {Element} Element node on which to set the attribute.\n+ * uri - {String} Namespace URI for the attribute.\n+ * name - {String} Qualified name (prefix:localname) for the attribute.\n+ * value - {String} Attribute value.\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+ setAttributeNS: function(node, uri, name, value) {\n+ if (node.setAttributeNS) {\n+ node.setAttributeNS(uri, name, value);\n+ } else {\n+ if (this.xmldom) {\n+ if (uri) {\n+ var attribute = node.ownerDocument.createNode(\n+ 2, name, uri\n+ );\n+ attribute.nodeValue = value;\n+ node.setAttributeNode(attribute);\n+ } else {\n+ node.setAttribute(name, value);\n+ }\n+ } else {\n+ throw \"setAttributeNS not implemented\";\n+ }\n+ }\n },\n \n /**\n- * APIMethod: getTileSize\n- * Get the tile size for the map\n+ * Method: createElementNSPlus\n+ * Shorthand for creating namespaced elements with optional attributes and\n+ * child text nodes.\n+ *\n+ * Parameters:\n+ * name - {String} The qualified node name.\n+ * options - {Object} Optional object for node configuration.\n+ *\n+ * Valid options:\n+ * uri - {String} Optional namespace uri for the element - supply a prefix\n+ * instead if the namespace uri is a property of the format's namespace\n+ * object.\n+ * attributes - {Object} Optional attributes to be set using the\n+ * method.\n+ * value - {String} Optional text to be appended as a text node.\n *\n * Returns:\n- * {}\n+ * {Element} An element node.\n */\n- getTileSize: function() {\n- return this.tileSize;\n+ createElementNSPlus: function(name, options) {\n+ options = options || {};\n+ // order of prefix preference\n+ // 1. in the uri option\n+ // 2. in the prefix option\n+ // 3. in the qualified name\n+ // 4. from the defaultPrefix\n+ var uri = options.uri || this.namespaces[options.prefix];\n+ if (!uri) {\n+ var loc = name.indexOf(\":\");\n+ uri = this.namespaces[name.substring(0, loc)];\n+ }\n+ if (!uri) {\n+ uri = this.namespaces[this.defaultPrefix];\n+ }\n+ var node = this.createElementNS(uri, name);\n+ if (options.attributes) {\n+ this.setAttributes(node, options.attributes);\n+ }\n+ var value = options.value;\n+ if (value != null) {\n+ node.appendChild(this.createTextNode(value));\n+ }\n+ return node;\n },\n \n-\n /**\n- * APIMethod: getBy\n- * Get a list of objects given a property and a match item.\n+ * Method: setAttributes\n+ * Set multiple attributes given key value pairs from an object.\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+ * node - {Element} An element node.\n+ * obj - {Object || Array} An object whose properties represent attribute\n+ * names and values represent attribute values. If an attribute name\n+ * is a qualified name (\"prefix:local\"), the prefix will be looked up\n+ * in the parsers {namespaces} object. If the prefix is found,\n+ * setAttributeNS will be used instead of setAttribute.\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+ setAttributes: function(node, obj) {\n+ var value, uri;\n+ for (var name in obj) {\n+ if (obj[name] != null && obj[name].toString) {\n+ value = obj[name].toString();\n+ // check for qualified attribute name (\"prefix:local\")\n+ uri = this.namespaces[name.substring(0, name.indexOf(\":\"))] || null;\n+ this.setAttributeNS(node, uri, name, value);\n+ }\n+ }\n },\n \n /**\n- * APIMethod: getLayersBy\n- * Get a list of layers with properties matching the given criteria.\n+ * Method: readNode\n+ * Shorthand for applying one of the named readers given the node\n+ * namespace and local name. Readers take two args (node, obj) and\n+ * generally extend or modify the second.\n *\n * Parameters:\n- * 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+ * node - {DOMElement} The node to be read (required).\n+ * obj - {Object} The object to be modified (optional).\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+ * {Object} The input object, modified (or a new one if none was provided).\n */\n- getLayersBy: function(property, match) {\n- return this.getBy(\"layers\", property, match);\n+ readNode: function(node, obj) {\n+ if (!obj) {\n+ obj = {};\n+ }\n+ var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI] : this.defaultPrefix];\n+ if (group) {\n+ var local = node.localName || node.nodeName.split(\":\").pop();\n+ var reader = group[local] || group[\"*\"];\n+ if (reader) {\n+ reader.apply(this, [node, obj]);\n+ }\n+ }\n+ return obj;\n },\n \n /**\n- * APIMethod: getLayersByName\n- * Get a list of layers with names matching the given name.\n+ * Method: readChildNodes\n+ * Shorthand for applying the named readers to all children of a node.\n+ * For each child of type 1 (element), is called.\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+ * node - {DOMElement} The node to be read (required).\n+ * obj - {Object} The object to be modified (optional).\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+ * {Object} The input object, modified.\n */\n- getLayersByName: function(match) {\n- return this.getLayersBy(\"name\", match);\n+ readChildNodes: function(node, obj) {\n+ if (!obj) {\n+ obj = {};\n+ }\n+ var children = node.childNodes;\n+ var child;\n+ for (var i = 0, len = children.length; i < len; ++i) {\n+ child = children[i];\n+ if (child.nodeType == 1) {\n+ this.readNode(child, obj);\n+ }\n+ }\n+ return obj;\n },\n \n /**\n- * APIMethod: getLayersByClass\n- * Get a list of layers of a given class (CLASS_NAME).\n+ * Method: writeNode\n+ * Shorthand for applying one of the named writers and appending the\n+ * results to a node. If a qualified name is not provided for the\n+ * second argument (and a local name is used instead), the namespace\n+ * of the parent node will be assumed.\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+ * name - {String} The name of a node to generate. If a qualified name\n+ * (e.g. \"pre:Name\") is used, the namespace prefix is assumed to be\n+ * in the group. If a local name is used (e.g. \"Name\") then\n+ * the namespace of the parent is assumed. If a local name is used\n+ * and no parent is supplied, then the default namespace is assumed.\n+ * obj - {Object} Structure containing data for the writer.\n+ * parent - {DOMElement} Result will be appended to this node. If no parent\n+ * is supplied, the node will not be appended to anything.\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+ * {DOMElement} The child node.\n */\n- getLayersByClass: function(match) {\n- return this.getLayersBy(\"CLASS_NAME\", match);\n+ writeNode: function(name, obj, parent) {\n+ var prefix, local;\n+ var split = name.indexOf(\":\");\n+ if (split > 0) {\n+ prefix = name.substring(0, split);\n+ local = name.substring(split + 1);\n+ } else {\n+ if (parent) {\n+ prefix = this.namespaceAlias[parent.namespaceURI];\n+ } else {\n+ prefix = this.defaultPrefix;\n+ }\n+ local = name;\n+ }\n+ var child = this.writers[prefix][local].apply(this, [obj]);\n+ if (parent) {\n+ parent.appendChild(child);\n+ }\n+ return child;\n },\n \n /**\n- * APIMethod: getControlsBy\n- * Get a list of controls with properties matching the given criteria.\n+ * APIMethod: getChildEl\n+ * Get the first child element. Optionally only return the first child\n+ * if it matches the given name and namespace URI.\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+ * node - {DOMElement} The parent node.\n+ * name - {String} Optional node name (local) to search for.\n+ * uri - {String} Optional namespace URI to search for.\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+ * {DOMElement} The first child. Returns null if no element is found, if\n+ * something significant besides an element is found, or if the element\n+ * found does not match the optional name and uri.\n */\n- getControlsBy: function(property, match) {\n- return this.getBy(\"controls\", property, match);\n+ getChildEl: function(node, name, uri) {\n+ return node && this.getThisOrNextEl(node.firstChild, name, uri);\n },\n \n /**\n- * APIMethod: getControlsByClass\n- * Get a list of controls of a given class (CLASS_NAME).\n+ * APIMethod: getNextEl\n+ * Get the next sibling element. Optionally get the first sibling only\n+ * if it matches the given local name and namespace URI.\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+ * node - {DOMElement} The node.\n+ * name - {String} Optional local name of the sibling to search for.\n+ * uri - {String} Optional namespace URI of the sibling to search for.\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+ * {DOMElement} The next sibling element. Returns null if no element is\n+ * found, something significant besides an element is found, or the\n+ * found element does not match the optional name and uri.\n */\n- getControlsByClass: function(match) {\n- return this.getControlsBy(\"CLASS_NAME\", match);\n+ getNextEl: function(node, name, uri) {\n+ return node && this.getThisOrNextEl(node.nextSibling, name, uri);\n },\n \n- /********************************************************/\n- /* */\n- /* Layer Functions */\n- /* */\n- /* The following functions deal with adding and */\n- /* removing Layers to and from the Map */\n- /* */\n- /********************************************************/\n-\n /**\n- * APIMethod: getLayer\n- * Get a layer based on its id\n+ * Method: getThisOrNextEl\n+ * Return this node or the next element node. Optionally get the first\n+ * sibling with the given local name or namespace URI.\n *\n * Parameters:\n- * id - {String} A layer id\n+ * node - {DOMElement} The node.\n+ * name - {String} Optional local name of the sibling to search for.\n+ * uri - {String} Optional namespace URI of the sibling to search for.\n *\n * Returns:\n- * {} The Layer with the corresponding id from the map's \n- * layer collection, or null if not found.\n+ * {DOMElement} The next sibling element. Returns null if no element is\n+ * found, something significant besides an element is found, or the\n+ * found element does not match the query.\n */\n- getLayer: function(id) {\n- var foundLayer = null;\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- if (layer.id == id) {\n- foundLayer = layer;\n- break;\n- }\n+ getThisOrNextEl: function(node, name, uri) {\n+ outer: for (var sibling = node; sibling; sibling = sibling.nextSibling) {\n+ switch (sibling.nodeType) {\n+ case 1: // Element\n+ if ((!name || name === (sibling.localName || sibling.nodeName.split(\":\").pop())) &&\n+ (!uri || uri === sibling.namespaceURI)) {\n+ // matches\n+ break outer;\n+ }\n+ sibling = null;\n+ break outer;\n+ case 3: // Text\n+ if (/^\\s*$/.test(sibling.nodeValue)) {\n+ break;\n+ }\n+ case 4: // CDATA\n+ case 6: // ENTITY_NODE\n+ case 12: // NOTATION_NODE\n+ case 10: // DOCUMENT_TYPE_NODE\n+ case 11: // DOCUMENT_FRAGMENT_NODE\n+ sibling = null;\n+ break outer;\n+ } // ignore comments and processing instructions\n }\n- return foundLayer;\n+ return sibling || null;\n },\n \n /**\n- * Method: setLayerZIndex\n- * \n+ * APIMethod: lookupNamespaceURI\n+ * Takes a prefix and returns the namespace URI associated with it on the given\n+ * node if found (and null if not). Supplying null for the prefix will\n+ * return the default namespace.\n+ *\n+ * For browsers that support it, this calls the native lookupNamesapceURI\n+ * function. In other browsers, this is an implementation of\n+ * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.\n+ *\n+ * For browsers that don't support the attribute.ownerElement property, this\n+ * method cannot be called on attribute nodes.\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-\n- /**\n- * Method: resetLayersZIndex\n- * Reset each layer's z-index based on layer's array index\n+ * node - {DOMElement} The node from which to start looking.\n+ * prefix - {String} The prefix to lookup or null to lookup the default namespace.\n+ * \n+ * Returns:\n+ * {String} The namespace URI for the given prefix. Returns null if the prefix\n+ * cannot be found or the node is the wrong type.\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+ lookupNamespaceURI: function(node, prefix) {\n+ var uri = null;\n+ if (node) {\n+ if (node.lookupNamespaceURI) {\n+ uri = node.lookupNamespaceURI(prefix);\n+ } else {\n+ outer: switch (node.nodeType) {\n+ case 1: // ELEMENT_NODE\n+ if (node.namespaceURI !== null && node.prefix === prefix) {\n+ uri = node.namespaceURI;\n+ break outer;\n+ }\n+ var len = node.attributes.length;\n+ if (len) {\n+ var attr;\n+ for (var i = 0; i < len; ++i) {\n+ attr = node.attributes[i];\n+ if (attr.prefix === \"xmlns\" && attr.name === \"xmlns:\" + prefix) {\n+ uri = attr.value || null;\n+ break outer;\n+ } else if (attr.name === \"xmlns\" && prefix === null) {\n+ uri = attr.value || null;\n+ break outer;\n+ }\n+ }\n+ }\n+ uri = this.lookupNamespaceURI(node.parentNode, prefix);\n+ break outer;\n+ case 2: // ATTRIBUTE_NODE\n+ uri = this.lookupNamespaceURI(node.ownerElement, prefix);\n+ break outer;\n+ case 9: // DOCUMENT_NODE\n+ uri = this.lookupNamespaceURI(node.documentElement, prefix);\n+ break outer;\n+ case 6: // ENTITY_NODE\n+ case 12: // NOTATION_NODE\n+ case 10: // DOCUMENT_TYPE_NODE\n+ case 11: // DOCUMENT_FRAGMENT_NODE\n+ break outer;\n+ default:\n+ // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),\n+ // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)\n+ uri = this.lookupNamespaceURI(node.parentNode, prefix);\n+ break outer;\n+ }\n+ }\n }\n+ return uri;\n },\n \n /**\n- * APIMethod: addLayer\n- *\n- * Parameters:\n- * layer - {} \n+ * Method: getXMLDoc\n+ * Get an XML document for nodes that are not supported in HTML (e.g.\n+ * createCDATASection). On IE, this will either return an existing or\n+ * create a new on the instance. On other browsers, this will\n+ * either return an existing or create a new shared document (see\n+ * ).\n *\n * Returns:\n- * {Boolean} True if the layer has been added to the map.\n+ * {XMLDocument}\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+ getXMLDoc: function() {\n+ if (!OpenLayers.Format.XML.document && !this.xmldom) {\n+ if (document.implementation && document.implementation.createDocument) {\n+ OpenLayers.Format.XML.document =\n+ document.implementation.createDocument(\"\", \"\", null);\n+ } else if (!this.xmldom && window.ActiveXObject) {\n+ this.xmldom = new ActiveXObject(\"Microsoft.XMLDOM\");\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 OpenLayers.Format.XML.document || this.xmldom;\n+ },\n \n- layer.div.className = \"olLayerDiv\";\n- layer.div.style.overflow = \"\";\n- this.setLayerZIndex(layer, this.layers.length);\n+ CLASS_NAME: \"OpenLayers.Format.XML\"\n \n- if (layer.isFixed) {\n- this.viewPortDiv.appendChild(layer.div);\n- } else {\n- this.layerContainerDiv.appendChild(layer.div);\n- }\n- this.layers.push(layer);\n- layer.setMap(this);\n+});\n \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+OpenLayers.Format.XML.CONTENT_TYPE = {\n+ EMPTY: 0,\n+ SIMPLE: 1,\n+ COMPLEX: 2,\n+ MIXED: 3\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+ * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI\n+ * Takes a prefix and returns the namespace URI associated with it on the given\n+ * node if found (and null if not). Supplying null for the prefix will\n+ * return the default namespace.\n+ *\n+ * For browsers that support it, this calls the native lookupNamesapceURI\n+ * function. In other browsers, this is an implementation of\n+ * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.\n+ *\n+ * For browsers that don't support the attribute.ownerElement property, this\n+ * method cannot be called on attribute nodes.\n+ * \n+ * Parameters:\n+ * node - {DOMElement} The node from which to start looking.\n+ * prefix - {String} The prefix to lookup or null to lookup the default namespace.\n+ * \n+ * Returns:\n+ * {String} The namespace URI for the given prefix. Returns null if the prefix\n+ * cannot be found or the node is the wrong type.\n+ */\n+OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(\n+ OpenLayers.Format.XML.prototype.lookupNamespaceURI,\n+ OpenLayers.Format.XML.prototype\n+);\n \n- return true;\n- },\n+/**\n+ * Property: OpenLayers.Format.XML.document\n+ * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes,\n+ * like document.createCDATASection.\n+ */\n+OpenLayers.Format.XML.document = null;\n+/* ======================================================================\n+ OpenLayers/Format/OGCExceptionReport.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.OGCExceptionReport\n+ * Class to read exception reports for various OGC services and versions.\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, {\n \n /**\n- * APIMethod: addLayers \n- *\n- * Parameters:\n- * layers - {Array()} \n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n */\n- addLayers: function(layers) {\n- for (var i = 0, len = layers.length; i < len; i++) {\n- this.addLayer(layers[i]);\n- }\n+ namespaces: {\n+ ogc: \"http://www.opengis.net/ogc\"\n },\n \n- /** \n- * APIMethod: removeLayer\n- * Removes a layer from the map by removing its visual element (the \n- * layer.div property), then removing it from the map's internal list \n- * of layers, setting the layer's map property to null. \n- * \n- * a \"removelayer\" event is triggered.\n- * \n- * very worthy of mention is that simply removing a layer from a map\n- * will not cause the removal of any popups which may have been created\n- * by the layer. this is due to the fact that it was decided at some\n- * point that popups would not belong to layers. thus there is no way \n- * for us to know here to which layer the popup belongs.\n- * \n- * A simple solution to this is simply to call destroy() on the layer.\n- * the default OpenLayers.Layer class's destroy() function\n- * automatically takes care to remove itself from whatever map it has\n- * been attached to. \n- * \n- * The correct solution is for the layer itself to register an \n- * event-handler on \"removelayer\" and when it is called, if it \n- * recognizes itself as the layer being removed, then it cycles through\n- * its own personal list of popups, removing them from the map.\n- * \n- * Parameters:\n- * layer - {} \n- * setNewBaseLayer - {Boolean} Default is true\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n */\n- removeLayer: function(layer, setNewBaseLayer) {\n- if (this.events.triggerEvent(\"preremovelayer\", {\n- layer: layer\n- }) === false) {\n- return;\n- }\n- if (setNewBaseLayer == null) {\n- setNewBaseLayer = true;\n- }\n-\n- if (layer.isFixed) {\n- this.viewPortDiv.removeChild(layer.div);\n- } else {\n- this.layerContainerDiv.removeChild(layer.div);\n- }\n- OpenLayers.Util.removeItem(this.layers, layer);\n- layer.removeMap(this);\n- layer.map = null;\n-\n- // if we removed the base layer, need to set a new one\n- if (this.baseLayer == layer) {\n- this.baseLayer = null;\n- if (setNewBaseLayer) {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var iLayer = this.layers[i];\n- if (iLayer.isBaseLayer || this.allOverlays) {\n- this.setBaseLayer(iLayer);\n- break;\n- }\n- }\n- }\n- }\n-\n- this.resetLayersZIndex();\n-\n- this.events.triggerEvent(\"removelayer\", {\n- layer: layer\n- });\n- layer.events.triggerEvent(\"removed\", {\n- map: this,\n- layer: layer\n- });\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n },\n \n /**\n- * APIMethod: getNumLayers\n- * \n- * Returns:\n- * {Int} The number of layers attached to the map.\n+ * Property: defaultPrefix\n */\n- getNumLayers: function() {\n- return this.layers.length;\n- },\n+ defaultPrefix: \"ogc\",\n \n- /** \n- * APIMethod: getLayerIndex\n+ /**\n+ * Constructor: OpenLayers.Format.OGCExceptionReport\n+ * Create a new parser for OGC exception reports.\n *\n * Parameters:\n- * layer - {}\n- *\n- * Returns:\n- * {Integer} The current (zero-based) index of the given layer in the map's\n- * layer stack. Returns -1 if the layer isn't on the map.\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\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+ * APIMethod: read\n+ * Read OGC exception report data from a string, and return an object with\n+ * information about the exceptions.\n *\n * Parameters:\n- * layer - {} \n- * idx - {int} \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Information about the exceptions that occurred.\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+ read: function(data) {\n+ var result;\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\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+ var root = data.documentElement;\n+ var exceptionInfo = {\n+ exceptionReport: null\n+ };\n+ if (root) {\n+ this.readChildNodes(data, exceptionInfo);\n+ if (exceptionInfo.exceptionReport === null) {\n+ // fall-back to OWSCommon since this is a common output format for exceptions\n+ // we cannot easily use the ows readers directly since they differ for 1.0 and 1.1\n+ exceptionInfo = new OpenLayers.Format.OWSCommon().read(data);\n }\n }\n+ return exceptionInfo;\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+ * 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- raiseLayer: function(layer, delta) {\n- var idx = this.getLayerIndex(layer) + delta;\n- this.setLayerIndex(layer, idx);\n+ readers: {\n+ \"ogc\": {\n+ \"ServiceExceptionReport\": function(node, obj) {\n+ obj.exceptionReport = {\n+ exceptions: []\n+ };\n+ this.readChildNodes(node, obj.exceptionReport);\n+ },\n+ \"ServiceException\": function(node, exceptionReport) {\n+ var exception = {\n+ code: node.getAttribute(\"code\"),\n+ locator: node.getAttribute(\"locator\"),\n+ text: this.getChildValue(node)\n+ };\n+ exceptionReport.exceptions.push(exception);\n+ }\n+ }\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+ CLASS_NAME: \"OpenLayers.Format.OGCExceptionReport\"\n \n- if (newBaseLayer != this.baseLayer) {\n+});\n+/* ======================================================================\n+ OpenLayers/Format/XML/VersionedOGC.js\n+ ====================================================================== */\n \n- // ensure newBaseLayer is already loaded\n- if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\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+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/OGCExceptionReport.js\n+ */\n \n- // make the old base layer invisible \n- if (this.baseLayer != null && !this.allOverlays) {\n- this.baseLayer.setVisibility(false);\n- }\n+/**\n+ * Class: OpenLayers.Format.XML.VersionedOGC\n+ * Base class for versioned formats, i.e. a format which supports multiple\n+ * versions.\n+ *\n+ * To enable checking if parsing succeeded, you will need to define a property\n+ * called errorProperty on the parser you want to check. The parser will then\n+ * check the returned object to see if that property is present. If it is, it\n+ * assumes the parsing was successful. If it is not present (or is null), it will\n+ * pass the document through an OGCExceptionReport parser.\n+ * \n+ * If errorProperty is undefined for the parser, this error checking mechanism\n+ * will be disabled.\n+ *\n+ *\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, {\n \n- // set new baselayer\n- this.baseLayer = newBaseLayer;\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found.\n+ */\n+ defaultVersion: null,\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+ * APIProperty: version\n+ * {String} Specify a version string if one is known.\n+ */\n+ version: null,\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+ * APIProperty: profile\n+ * {String} If provided, use a custom profile.\n+ */\n+ profile: null,\n \n- this.events.triggerEvent(\"changebaselayer\", {\n- layer: this.baseLayer\n- });\n- }\n- }\n- },\n+ /**\n+ * APIProperty: allowFallback\n+ * {Boolean} If a profiled parser cannot be found for the returned version,\n+ * use a non-profiled parser as the fallback. Application code using this\n+ * should take into account that the return object structure might be\n+ * missing the specifics of the profile. Defaults to false.\n+ */\n+ allowFallback: false,\n \n+ /**\n+ * Property: name\n+ * {String} The name of this parser, this is the part of the CLASS_NAME\n+ * except for \"OpenLayers.Format.\"\n+ */\n+ name: null,\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+ * APIProperty: stringifyOutput\n+ * {Boolean} If true, write will return a string otherwise a DOMElement.\n+ * Default is false.\n+ */\n+ stringifyOutput: false,\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+ * Property: parser\n+ * {Object} Instance of the versioned parser. Cached for multiple read and\n+ * write calls of the same version.\n */\n- addControl: function(control, px) {\n- this.controls.push(control);\n- this.addControlToMap(control, px);\n- },\n+ parser: null,\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+ * Constructor: OpenLayers.Format.XML.VersionedOGC.\n+ * Constructor.\n+ *\n * Parameters:\n- * controls - {Array()}\n- * pixels - {Array()}\n+ * options - {Object} Optional object whose properties will be set on\n+ * the object.\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+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ var className = this.CLASS_NAME;\n+ this.name = className.substring(className.lastIndexOf(\".\") + 1);\n },\n \n /**\n- * Method: addControlToMap\n- * \n+ * Method: getVersion\n+ * Returns the version to use. Subclasses can override this function\n+ * if a different version detection is needed.\n+ *\n * Parameters:\n- * \n- * control - {}\n- * px - {}\n+ * root - {DOMElement}\n+ * options - {Object} Optional configuration object.\n+ *\n+ * Returns:\n+ * {String} The version to use.\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+ getVersion: function(root, options) {\n+ var version;\n+ // read\n+ if (root) {\n+ version = this.version;\n+ if (!version) {\n+ version = root.getAttribute(\"version\");\n+ if (!version) {\n+ version = this.defaultVersion;\n+ }\n }\n+ } else { // write\n+ version = (options && options.version) ||\n+ this.version || this.defaultVersion;\n }\n- if (control.autoActivate) {\n- control.activate();\n- }\n+ return version;\n },\n \n /**\n- * APIMethod: getControl\n- * \n+ * Method: getParser\n+ * Get an instance of the cached parser if available, otherwise create one.\n+ *\n * Parameters:\n- * id - {String} ID of the control to return.\n- * \n+ * version - {String}\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 */\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+ getParser: function(version) {\n+ version = version || this.defaultVersion;\n+ var profile = this.profile ? \"_\" + this.profile : \"\";\n+ if (!this.parser || this.parser.VERSION != version) {\n+ var format = OpenLayers.Format[this.name][\n+ \"v\" + version.replace(/\\./g, \"_\") + profile\n+ ];\n+ if (!format) {\n+ if (profile !== \"\" && this.allowFallback) {\n+ // fallback to the non-profiled version of the parser\n+ profile = \"\";\n+ format = OpenLayers.Format[this.name][\n+ \"v\" + version.replace(/\\./g, \"_\")\n+ ];\n+ }\n+ if (!format) {\n+ throw \"Can't find a \" + this.name + \" parser for version \" +\n+ version + profile;\n+ }\n }\n+ this.parser = new format(this.options);\n }\n- return returnControl;\n+ return this.parser;\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+ /**\n+ * APIMethod: write\n+ * Write a document.\n+ *\n * Parameters:\n- * control - {} The control to remove.\n+ * obj - {Object} An object representing the document.\n+ * options - {Object} Optional configuration object.\n+ *\n+ * Returns:\n+ * {String} The document as a string\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+ write: function(obj, options) {\n+ var version = this.getVersion(null, options);\n+ this.parser = this.getParser(version);\n+ var root = this.parser.write(obj, options);\n+ if (this.stringifyOutput === false) {\n+ return root;\n+ } else {\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [root]);\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+ /**\n+ * APIMethod: read\n+ * Read a doc and return an object representing the document.\n+ *\n * Parameters:\n- * popup - {}\n- * exclusive - {Boolean} If true, closes all other popups first\n+ * data - {String | DOMElement} Data to read.\n+ * options - {Object} Options for the reader.\n+ *\n+ * Returns:\n+ * {Object} An object representing the document.\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+ read: function(data, options) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n }\n+ var root = data.documentElement;\n+ var version = this.getVersion(root);\n+ this.parser = this.getParser(version); // Select the parser\n+ var obj = this.parser.read(data, options); // Parse the data\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+ var errorProperty = this.parser.errorProperty || null;\n+ if (errorProperty !== null && obj[errorProperty] === undefined) {\n+ // an error must have happened, so parse it and report back\n+ var format = new OpenLayers.Format.OGCExceptionReport();\n+ obj.error = format.read(data);\n }\n+ obj.version = version;\n+ return obj;\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+ CLASS_NAME: \"OpenLayers.Format.XML.VersionedOGC\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/OWSCommon.js\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+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD 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.OWSCommon\n+ * Read OWSCommon. Create a new instance with the \n+ * constructor.\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.OWSCommon = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\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+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.0.0\".\n */\n- getSize: function() {\n- var size = null;\n- if (this.size != null) {\n- size = this.size.clone();\n- }\n- return size;\n- },\n+ defaultVersion: \"1.0.0\",\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+ * Constructor: OpenLayers.Format.OWSCommon\n+ * Create a new parser for OWSCommon.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\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+ * Method: getVersion\n+ * Returns the version to use. Subclasses can override this function\n+ * if a different version detection is needed.\n+ *\n+ * Parameters:\n+ * root - {DOMElement}\n+ * options - {Object} Optional configuration object.\n+ *\n * Returns:\n- * {} A new object with the dimensions \n- * of the map div\n+ * {String} The version to use.\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+ getVersion: function(root, options) {\n+ var version = this.version;\n+ if (!version) {\n+ // remember version does not correspond to the OWS version\n+ // it corresponds to the WMS/WFS/WCS etc. request version\n+ var uri = root.getAttribute(\"xmlns:ows\");\n+ // the above will fail if the namespace prefix is different than\n+ // ows and if the namespace is declared on a different element\n+ if (uri && uri.substring(uri.lastIndexOf(\"/\") + 1) === \"1.1\") {\n+ version = \"1.1.0\";\n+ }\n+ if (!version) {\n+ version = this.defaultVersion;\n+ }\n }\n- return size;\n+ return version;\n },\n \n- /** \n- * Method: calculateBounds\n- * \n+ /**\n+ * APIMethod: read\n+ * Read an OWSCommon document and return an object.\n+ *\n * Parameters:\n- * center - {} Default is this.getCenter()\n- * resolution - {float} Default is this.getResolution() \n- * \n+ * data - {String | DOMElement} Data to read.\n+ * options - {Object} Options for the reader.\n+ *\n * Returns:\n- * {} A bounds based on resolution, center, and \n- * current mapsize.\n+ * {Object} An object representing the structure of the document.\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+ CLASS_NAME: \"OpenLayers.Format.OWSCommon\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/OWSCommon/v1.js\n+ ====================================================================== */\n \n- extent = new OpenLayers.Bounds(center.lon - halfWDeg,\n- center.lat - halfHDeg,\n- center.lon + halfWDeg,\n- center.lat + halfHDeg);\n- }\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- return extent;\n- },\n+/**\n+ * @requires OpenLayers/Format/OWSCommon.js\n+ */\n \n+/**\n+ * Class: OpenLayers.Format.OWSCommon.v1\n+ * Common readers and writers for OWSCommon v1.X formats\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.OWSCommon.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\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+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\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+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n },\n \n /**\n- * Method: getCachedCenter\n+ * Method: read\n+ *\n+ * Parameters:\n+ * data - {DOMElement} An OWSCommon document element.\n+ * options - {Object} Options for the reader.\n *\n * Returns:\n- * {}\n+ * {Object} An object representing the OWSCommon document.\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+ read: function(data, options) {\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var ows = {};\n+ this.readChildNodes(data, ows);\n+ return ows;\n },\n \n /**\n- * APIMethod: getZoom\n- * \n- * Returns:\n- * {Integer}\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- getZoom: function() {\n- return this.zoom;\n+ readers: {\n+ \"ows\": {\n+ \"Exception\": function(node, exceptionReport) {\n+ var exception = {\n+ code: node.getAttribute('exceptionCode'),\n+ locator: node.getAttribute('locator'),\n+ texts: []\n+ };\n+ exceptionReport.exceptions.push(exception);\n+ this.readChildNodes(node, exception);\n+ },\n+ \"ExceptionText\": function(node, exception) {\n+ var text = this.getChildValue(node);\n+ exception.texts.push(text);\n+ },\n+ \"ServiceIdentification\": function(node, obj) {\n+ obj.serviceIdentification = {};\n+ this.readChildNodes(node, obj.serviceIdentification);\n+ },\n+ \"Title\": function(node, obj) {\n+ obj.title = this.getChildValue(node);\n+ },\n+ \"Abstract\": function(node, serviceIdentification) {\n+ serviceIdentification[\"abstract\"] = this.getChildValue(node);\n+ },\n+ \"Keywords\": function(node, serviceIdentification) {\n+ serviceIdentification.keywords = {};\n+ this.readChildNodes(node, serviceIdentification.keywords);\n+ },\n+ \"Keyword\": function(node, keywords) {\n+ keywords[this.getChildValue(node)] = true;\n+ },\n+ \"ServiceType\": function(node, serviceIdentification) {\n+ serviceIdentification.serviceType = {\n+ codeSpace: node.getAttribute('codeSpace'),\n+ value: this.getChildValue(node)\n+ };\n+ },\n+ \"ServiceTypeVersion\": function(node, serviceIdentification) {\n+ serviceIdentification.serviceTypeVersion = this.getChildValue(node);\n+ },\n+ \"Fees\": function(node, serviceIdentification) {\n+ serviceIdentification.fees = this.getChildValue(node);\n+ },\n+ \"AccessConstraints\": function(node, serviceIdentification) {\n+ serviceIdentification.accessConstraints =\n+ this.getChildValue(node);\n+ },\n+ \"ServiceProvider\": function(node, obj) {\n+ obj.serviceProvider = {};\n+ this.readChildNodes(node, obj.serviceProvider);\n+ },\n+ \"ProviderName\": function(node, serviceProvider) {\n+ serviceProvider.providerName = this.getChildValue(node);\n+ },\n+ \"ProviderSite\": function(node, serviceProvider) {\n+ serviceProvider.providerSite = this.getAttributeNS(node,\n+ this.namespaces.xlink, \"href\");\n+ },\n+ \"ServiceContact\": function(node, serviceProvider) {\n+ serviceProvider.serviceContact = {};\n+ this.readChildNodes(node, serviceProvider.serviceContact);\n+ },\n+ \"IndividualName\": function(node, serviceContact) {\n+ serviceContact.individualName = this.getChildValue(node);\n+ },\n+ \"PositionName\": function(node, serviceContact) {\n+ serviceContact.positionName = this.getChildValue(node);\n+ },\n+ \"ContactInfo\": function(node, serviceContact) {\n+ serviceContact.contactInfo = {};\n+ this.readChildNodes(node, serviceContact.contactInfo);\n+ },\n+ \"Phone\": function(node, contactInfo) {\n+ contactInfo.phone = {};\n+ this.readChildNodes(node, contactInfo.phone);\n+ },\n+ \"Voice\": function(node, phone) {\n+ phone.voice = this.getChildValue(node);\n+ },\n+ \"Address\": function(node, contactInfo) {\n+ contactInfo.address = {};\n+ this.readChildNodes(node, contactInfo.address);\n+ },\n+ \"DeliveryPoint\": function(node, address) {\n+ address.deliveryPoint = this.getChildValue(node);\n+ },\n+ \"City\": function(node, address) {\n+ address.city = this.getChildValue(node);\n+ },\n+ \"AdministrativeArea\": function(node, address) {\n+ address.administrativeArea = this.getChildValue(node);\n+ },\n+ \"PostalCode\": function(node, address) {\n+ address.postalCode = this.getChildValue(node);\n+ },\n+ \"Country\": function(node, address) {\n+ address.country = this.getChildValue(node);\n+ },\n+ \"ElectronicMailAddress\": function(node, address) {\n+ address.electronicMailAddress = this.getChildValue(node);\n+ },\n+ \"Role\": function(node, serviceContact) {\n+ serviceContact.role = this.getChildValue(node);\n+ },\n+ \"OperationsMetadata\": function(node, obj) {\n+ obj.operationsMetadata = {};\n+ this.readChildNodes(node, obj.operationsMetadata);\n+ },\n+ \"Operation\": function(node, operationsMetadata) {\n+ var name = node.getAttribute(\"name\");\n+ operationsMetadata[name] = {};\n+ this.readChildNodes(node, operationsMetadata[name]);\n+ },\n+ \"DCP\": function(node, operation) {\n+ operation.dcp = {};\n+ this.readChildNodes(node, operation.dcp);\n+ },\n+ \"HTTP\": function(node, dcp) {\n+ dcp.http = {};\n+ this.readChildNodes(node, dcp.http);\n+ },\n+ \"Get\": function(node, http) {\n+ if (!http.get) {\n+ http.get = [];\n+ }\n+ var obj = {\n+ url: this.getAttributeNS(node, this.namespaces.xlink, \"href\")\n+ };\n+ this.readChildNodes(node, obj);\n+ http.get.push(obj);\n+ },\n+ \"Post\": function(node, http) {\n+ if (!http.post) {\n+ http.post = [];\n+ }\n+ var obj = {\n+ url: this.getAttributeNS(node, this.namespaces.xlink, \"href\")\n+ };\n+ this.readChildNodes(node, obj);\n+ http.post.push(obj);\n+ },\n+ \"Parameter\": function(node, operation) {\n+ if (!operation.parameters) {\n+ operation.parameters = {};\n+ }\n+ var name = node.getAttribute(\"name\");\n+ operation.parameters[name] = {};\n+ this.readChildNodes(node, operation.parameters[name]);\n+ },\n+ \"Constraint\": function(node, obj) {\n+ if (!obj.constraints) {\n+ obj.constraints = {};\n+ }\n+ var name = node.getAttribute(\"name\");\n+ obj.constraints[name] = {};\n+ this.readChildNodes(node, obj.constraints[name]);\n+ },\n+ \"Value\": function(node, allowedValues) {\n+ allowedValues[this.getChildValue(node)] = true;\n+ },\n+ \"OutputFormat\": function(node, obj) {\n+ obj.formats.push({\n+ value: this.getChildValue(node)\n+ });\n+ this.readChildNodes(node, obj);\n+ },\n+ \"WGS84BoundingBox\": function(node, obj) {\n+ var boundingBox = {};\n+ boundingBox.crs = node.getAttribute(\"crs\");\n+ if (obj.BoundingBox) {\n+ obj.BoundingBox.push(boundingBox);\n+ } else {\n+ obj.projection = boundingBox.crs;\n+ boundingBox = obj;\n+ }\n+ this.readChildNodes(node, boundingBox);\n+ },\n+ \"BoundingBox\": function(node, obj) {\n+ // FIXME: We consider that BoundingBox is the same as WGS84BoundingBox\n+ // LowerCorner = \"min_x min_y\"\n+ // UpperCorner = \"max_x max_y\"\n+ // It should normally depend on the projection\n+ this.readers['ows']['WGS84BoundingBox'].apply(this, [node, obj]);\n+ },\n+ \"LowerCorner\": function(node, obj) {\n+ var str = this.getChildValue(node).replace(\n+ this.regExes.trimSpace, \"\");\n+ str = str.replace(this.regExes.trimComma, \",\");\n+ var pointList = str.split(this.regExes.splitSpace);\n+ obj.left = pointList[0];\n+ obj.bottom = pointList[1];\n+ },\n+ \"UpperCorner\": function(node, obj) {\n+ var str = this.getChildValue(node).replace(\n+ this.regExes.trimSpace, \"\");\n+ str = str.replace(this.regExes.trimComma, \",\");\n+ var pointList = str.split(this.regExes.splitSpace);\n+ obj.right = pointList[0];\n+ obj.top = pointList[1];\n+ obj.bounds = new OpenLayers.Bounds(obj.left, obj.bottom,\n+ obj.right, obj.top);\n+ delete obj.left;\n+ delete obj.bottom;\n+ delete obj.right;\n+ delete obj.top;\n+ },\n+ \"Language\": function(node, obj) {\n+ obj.language = this.getChildValue(node);\n+ }\n+ }\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+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n */\n- 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+ writers: {\n+ \"ows\": {\n+ \"BoundingBox\": function(options, nodeName) {\n+ var node = this.createElementNSPlus(nodeName || \"ows:BoundingBox\", {\n+ attributes: {\n+ crs: options.projection\n }\n- }\n+ });\n+ this.writeNode(\"ows:LowerCorner\", options, node);\n+ this.writeNode(\"ows:UpperCorner\", options, node);\n+ return node;\n+ },\n+ \"LowerCorner\": function(options) {\n+ var node = this.createElementNSPlus(\"ows:LowerCorner\", {\n+ value: options.bounds.left + \" \" + options.bounds.bottom\n+ });\n+ return node;\n+ },\n+ \"UpperCorner\": function(options) {\n+ var node = this.createElementNSPlus(\"ows:UpperCorner\", {\n+ value: options.bounds.right + \" \" + options.bounds.top\n+ });\n+ return node;\n+ },\n+ \"Identifier\": function(identifier) {\n+ var node = this.createElementNSPlus(\"ows:Identifier\", {\n+ value: identifier\n+ });\n+ return node;\n+ },\n+ \"Title\": function(title) {\n+ var node = this.createElementNSPlus(\"ows:Title\", {\n+ value: title\n+ });\n+ return node;\n+ },\n+ \"Abstract\": function(abstractValue) {\n+ var node = this.createElementNSPlus(\"ows:Abstract\", {\n+ value: abstractValue\n+ });\n+ return node;\n+ },\n+ \"OutputFormat\": function(format) {\n+ var node = this.createElementNSPlus(\"ows:OutputFormat\", {\n+ value: format\n+ });\n+ return node;\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+ CLASS_NAME: \"OpenLayers.Format.OWSCommon.v1\"\n \n- // center will not change, don't do nothing\n- if (lonlat.equals(center)) {\n- return;\n- }\n+});\n+/* ======================================================================\n+ OpenLayers/Format/OWSCommon/v1_1_0.js\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+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\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+ * @requires OpenLayers/Format/OWSCommon/v1.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.OWSCommon.v1_1_0\n+ * Parser for OWS Common version 1.1.0.\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {\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+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\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+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n },\n \n- /** \n- * Method: moveByPx\n- * Drag the map by pixels.\n- *\n- * Parameters:\n- * dx - {Number}\n- * dy - {Number}\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- 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+ readers: {\n+ \"ows\": OpenLayers.Util.applyDefaults({\n+ \"ExceptionReport\": function(node, obj) {\n+ obj.exceptionReport = {\n+ version: node.getAttribute('version'),\n+ language: node.getAttribute('xml:lang'),\n+ exceptions: []\n+ };\n+ this.readChildNodes(node, obj.exceptionReport);\n+ },\n+ \"AllowedValues\": function(node, parameter) {\n+ parameter.allowedValues = {};\n+ this.readChildNodes(node, parameter.allowedValues);\n+ },\n+ \"AnyValue\": function(node, parameter) {\n+ parameter.anyValue = true;\n+ },\n+ \"DataType\": function(node, parameter) {\n+ parameter.dataType = this.getChildValue(node);\n+ },\n+ \"Range\": function(node, allowedValues) {\n+ allowedValues.range = {};\n+ this.readChildNodes(node, allowedValues.range);\n+ },\n+ \"MinimumValue\": function(node, range) {\n+ range.minValue = this.getChildValue(node);\n+ },\n+ \"MaximumValue\": function(node, range) {\n+ range.maxValue = this.getChildValue(node);\n+ },\n+ \"Identifier\": function(node, obj) {\n+ obj.identifier = this.getChildValue(node);\n+ },\n+ \"SupportedCRS\": function(node, obj) {\n+ obj.supportedCRS = this.getChildValue(node);\n }\n- this.events.triggerEvent(\"move\");\n- }\n+ }, OpenLayers.Format.OWSCommon.v1.prototype.readers[\"ows\"])\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+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n */\n- 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+ writers: {\n+ \"ows\": OpenLayers.Util.applyDefaults({\n+ \"Range\": function(range) {\n+ var node = this.createElementNSPlus(\"ows:Range\", {\n+ attributes: {\n+ 'ows:rangeClosure': range.closure\n }\n- }\n+ });\n+ this.writeNode(\"ows:MinimumValue\", range.minValue, node);\n+ this.writeNode(\"ows:MaximumValue\", range.maxValue, node);\n+ return node;\n+ },\n+ \"MinimumValue\": function(minValue) {\n+ var node = this.createElementNSPlus(\"ows:MinimumValue\", {\n+ value: minValue\n+ });\n+ return node;\n+ },\n+ \"MaximumValue\": function(maxValue) {\n+ var node = this.createElementNSPlus(\"ows:MaximumValue\", {\n+ value: maxValue\n+ });\n+ return node;\n+ },\n+ \"Value\": function(value) {\n+ var node = this.createElementNSPlus(\"ows:Value\", {\n+ value: value\n+ });\n+ return node;\n }\n- }\n- return zoom;\n+ }, OpenLayers.Format.OWSCommon.v1.prototype.writers[\"ows\"])\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.OWSCommon.v1_1_0\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WCSGetCoverage.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD 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.WCSGetCoverage version 1.1.0\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.WCSGetCoverage = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ wcs: \"http://www.opengis.net/wcs/1.1\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n },\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+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n */\n- getMinZoom: function() {\n- return this.adjustZoom(0);\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n },\n \n /**\n- * Method: moveTo\n+ * Constant: VERSION\n+ * {String} 1.1.2\n+ */\n+ VERSION: \"1.1.2\",\n+\n+ /**\n+ * Property: schemaLocation\n+ * {String} Schema location\n+ */\n+ schemaLocation: \"http://www.opengis.net/wcs/1.1 http://schemas.opengis.net/wcs/1.1/wcsGetCoverage.xsd\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WCSGetCoverage\n *\n * Parameters:\n- * lonlat - {}\n- * zoom - {Integer}\n- * options - {Object}\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\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+ * Method: write\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object.\n+ *\n+ * Returns:\n+ * {String} A WCS GetCoverage request XML string.\n+ */\n+ write: function(options) {\n+ var node = this.writeNode(\"wcs:GetCoverage\", options);\n+ this.setAttributeNS(\n+ node, this.namespaces.xsi,\n+ \"xsi:schemaLocation\", this.schemaLocation\n+ );\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n+ },\n \n- 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+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"wcs\": {\n+ \"GetCoverage\": function(options) {\n+ var node = this.createElementNSPlus(\"wcs:GetCoverage\", {\n+ attributes: {\n+ version: options.version || this.VERSION,\n+ service: 'WCS'\n+ }\n+ });\n+ this.writeNode(\"ows:Identifier\", options.identifier, node);\n+ this.writeNode(\"wcs:DomainSubset\", options.domainSubset, node);\n+ this.writeNode(\"wcs:Output\", options.output, node);\n+ return node;\n+ },\n+ \"DomainSubset\": function(domainSubset) {\n+ var node = this.createElementNSPlus(\"wcs:DomainSubset\", {});\n+ this.writeNode(\"ows:BoundingBox\", domainSubset.boundingBox, node);\n+ if (domainSubset.temporalSubset) {\n+ this.writeNode(\"wcs:TemporalSubset\", domainSubset.temporalSubset, node);\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+ return node;\n+ },\n+ \"TemporalSubset\": function(temporalSubset) {\n+ var node = this.createElementNSPlus(\"wcs:TemporalSubset\", {});\n+ for (var i = 0, len = temporalSubset.timePeriods.length; i < len; ++i) {\n+ this.writeNode(\"wcs:TimePeriod\", temporalSubset.timePeriods[i], node);\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+ return node;\n+ },\n+ \"TimePeriod\": function(timePeriod) {\n+ var node = this.createElementNSPlus(\"wcs:TimePeriod\", {});\n+ this.writeNode(\"wcs:BeginPosition\", timePeriod.begin, node);\n+ this.writeNode(\"wcs:EndPosition\", timePeriod.end, node);\n+ if (timePeriod.resolution) {\n+ this.writeNode(\"wcs:TimeResolution\", timePeriod.resolution, node);\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+ return node;\n+ },\n+ \"BeginPosition\": function(begin) {\n+ var node = this.createElementNSPlus(\"wcs:BeginPosition\", {\n+ value: begin\n+ });\n+ return node;\n+ },\n+ \"EndPosition\": function(end) {\n+ var node = this.createElementNSPlus(\"wcs:EndPosition\", {\n+ value: end\n+ });\n+ return node;\n+ },\n+ \"TimeResolution\": function(resolution) {\n+ var node = this.createElementNSPlus(\"wcs:TimeResolution\", {\n+ value: resolution\n+ });\n+ return node;\n+ },\n+ \"Output\": function(output) {\n+ var node = this.createElementNSPlus(\"wcs:Output\", {\n+ attributes: {\n+ format: output.format,\n+ store: output.store\n+ }\n+ });\n+ if (output.gridCRS) {\n+ this.writeNode(\"wcs:GridCRS\", output.gridCRS, node);\n+ }\n+ return node;\n+ },\n+ \"GridCRS\": function(gridCRS) {\n+ var node = this.createElementNSPlus(\"wcs:GridCRS\", {});\n+ this.writeNode(\"wcs:GridBaseCRS\", gridCRS.baseCRS, node);\n+ if (gridCRS.type) {\n+ this.writeNode(\"wcs:GridType\", gridCRS.type, node);\n+ }\n+ if (gridCRS.origin) {\n+ this.writeNode(\"wcs:GridOrigin\", gridCRS.origin, node);\n+ }\n+ this.writeNode(\"wcs:GridOffsets\", gridCRS.offsets, node);\n+ if (gridCRS.CS) {\n+ this.writeNode(\"wcs:GridCS\", gridCRS.CS, node);\n+ }\n+ return node;\n+ },\n+ \"GridBaseCRS\": function(baseCRS) {\n+ return this.createElementNSPlus(\"wcs:GridBaseCRS\", {\n+ value: baseCRS\n+ });\n+ },\n+ \"GridOrigin\": function(origin) {\n+ return this.createElementNSPlus(\"wcs:GridOrigin\", {\n+ value: origin\n+ });\n+ },\n+ \"GridType\": function(type) {\n+ return this.createElementNSPlus(\"wcs:GridType\", {\n+ value: type\n+ });\n+ },\n+ \"GridOffsets\": function(offsets) {\n+ return this.createElementNSPlus(\"wcs:GridOffsets\", {\n+ value: offsets\n+ });\n+ },\n+ \"GridCS\": function(CS) {\n+ return this.createElementNSPlus(\"wcs:GridCS\", {\n+ value: CS\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+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows\n+ },\n \n- if (zoomChanged) {\n- this.zoom = zoom;\n- this.resolution = res;\n- }\n+ CLASS_NAME: \"OpenLayers.Format.WCSGetCoverage\"\n \n- var bounds = this.getExtent();\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WFST.js\n+ ====================================================================== */\n \n- //send the move call to the baselayer and all the overlays \n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- if (this.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+ * @requires OpenLayers/Format.js\n+ */\n \n- bounds = this.baseLayer.getExtent();\n+/**\n+ * Function: OpenLayers.Format.WFST\n+ * Used to create a versioned WFS protocol. Default version is 1.0.0.\n+ *\n+ * Returns:\n+ * {} A WFST format of the given version.\n+ */\n+OpenLayers.Format.WFST = function(options) {\n+ options = OpenLayers.Util.applyDefaults(\n+ options, OpenLayers.Format.WFST.DEFAULTS\n+ );\n+ var cls = OpenLayers.Format.WFST[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported WFST version: \" + options.version;\n+ }\n+ return new cls(options);\n+};\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+ * Constant: OpenLayers.Format.WFST.DEFAULTS\n+ * {Object} Default properties for the WFST format.\n+ */\n+OpenLayers.Format.WFST.DEFAULTS = {\n+ \"version\": \"1.0.0\"\n+};\n+/* ======================================================================\n+ OpenLayers/Filter.js\n+ ====================================================================== */\n \n- this.events.triggerEvent(\"move\");\n- dragging || this.events.triggerEvent(\"moveend\");\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- if (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+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Style.js\n+ */\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+ * Class: OpenLayers.Filter\n+ * This class represents an OGC Filter.\n+ */\n+OpenLayers.Filter = OpenLayers.Class({\n \n- /**\n- * Method: isValidZoomLevel\n- * \n+ /** \n+ * Constructor: OpenLayers.Filter\n+ * This class represents a generic filter.\n+ *\n * Parameters:\n- * zoomLevel - {Integer}\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\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 */\n- isValidZoomLevel: function(zoomLevel) {\n- return ((zoomLevel != null) &&\n- (zoomLevel >= 0) &&\n- (zoomLevel < this.getNumZoomLevels()));\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n },\n \n+ /** \n+ * APIMethod: destroy\n+ * Remove reference to anything added.\n+ */\n+ destroy: function() {},\n+\n /**\n- * Method: isValidLonLat\n+ * APIMethod: evaluate\n+ * Evaluates this filter in a specific context. Instances or subclasses\n+ * are supposed to override this method.\n * \n * Parameters:\n- * lonlat - {}\n+ * context - {Object} Context to use in evaluating the filter. If a vector\n+ * feature is provided, the feature.attributes will be used as context.\n * \n * Returns:\n- * {Boolean} Whether or not the lonlat passed in is non-null and within\n- * the maxExtent bounds\n+ * {Boolean} The filter applies.\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+ evaluate: function(context) {\n+ return true;\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+ * APIMethod: clone\n+ * Clones this filter. Should be implemented by subclasses.\n * \n * Returns:\n- * {String} The Projection string from the base layer or null. \n+ * {} Clone of this filter.\n */\n- getProjection: function() {\n- var projection = this.getProjectionObject();\n- return projection ? projection.getCode() : null;\n+ clone: function() {\n+ return null;\n },\n \n /**\n- * APIMethod: getProjectionObject\n- * Returns the projection obect from the baselayer.\n+ * APIMethod: toString\n *\n * Returns:\n- * {} The Projection of the base layer.\n+ * {String} Include in your build to get a CQL\n+ * representation of the filter returned. Otherwise \"[Object object]\"\n+ * will be returned.\n */\n- getProjectionObject: function() {\n- var projection = null;\n- if (this.baseLayer != null) {\n- projection = this.baseLayer.projection;\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.CQL) {\n+ string = OpenLayers.Format.CQL.prototype.write(this);\n+ } else {\n+ string = Object.prototype.toString.call(this);\n }\n- return projection;\n+ return string;\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+ CLASS_NAME: \"OpenLayers.Filter\"\n+});\n+/* ======================================================================\n+ OpenLayers/Filter/Spatial.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Filter.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Filter.Spatial\n+ * This class represents a spatial filter.\n+ * Currently implemented: BBOX, DWithin and Intersects\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {\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+ * APIProperty: type\n+ * {String} Type of spatial filter.\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+ * The type should be one of:\n+ * - OpenLayers.Filter.Spatial.BBOX\n+ * - OpenLayers.Filter.Spatial.INTERSECTS\n+ * - OpenLayers.Filter.Spatial.DWITHIN\n+ * - OpenLayers.Filter.Spatial.WITHIN\n+ * - OpenLayers.Filter.Spatial.CONTAINS\n */\n- 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+ type: null,\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+ * APIProperty: property\n+ * {String} Name of the context property to compare.\n */\n- getNumZoomLevels: function() {\n- var numZoomLevels = null;\n- if (this.baseLayer != null) {\n- numZoomLevels = this.baseLayer.numZoomLevels;\n- }\n- return numZoomLevels;\n- },\n+ property: null,\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+ * APIProperty: value\n+ * { || } The bounds or geometry\n+ * to be used by the filter. Use bounds for BBOX filters and geometry\n+ * for INTERSECTS or DWITHIN filters.\n+ */\n+ value: null,\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+ * APIProperty: distance\n+ * {Number} The distance to use in a DWithin spatial filter.\n */\n- getExtent: function() {\n- var extent = null;\n- if (this.baseLayer != null) {\n- extent = this.baseLayer.getExtent();\n- }\n- return extent;\n- },\n+ distance: null,\n \n /**\n- * APIMethod: getResolution\n+ * APIProperty: distanceUnits\n+ * {String} The units to use for the distance, e.g. 'm'.\n+ */\n+ distanceUnits: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Filter.Spatial\n+ * Creates a spatial filter.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object with properties to set on the\n+ * filter.\n * \n * Returns:\n- * {Float} The current resolution of the map. \n- * If no baselayer is set, returns null.\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- return resolution;\n- },\n \n /**\n- * APIMethod: getUnits\n+ * Method: evaluate\n+ * Evaluates this filter for a specific feature.\n+ * \n+ * Parameters:\n+ * feature - {} feature to apply the filter to.\n * \n * Returns:\n- * {Float} The current units of the map. \n- * If no baselayer is set, returns null.\n+ * {Boolean} The feature meets filter criteria.\n */\n- getUnits: function() {\n- var units = null;\n- if (this.baseLayer != null) {\n- units = this.baseLayer.units;\n+ evaluate: function(feature) {\n+ var intersect = false;\n+ switch (this.type) {\n+ case OpenLayers.Filter.Spatial.BBOX:\n+ case OpenLayers.Filter.Spatial.INTERSECTS:\n+ if (feature.geometry) {\n+ var geom = this.value;\n+ if (this.value.CLASS_NAME == \"OpenLayers.Bounds\") {\n+ geom = this.value.toGeometry();\n+ }\n+ if (feature.geometry.intersects(geom)) {\n+ intersect = true;\n+ }\n+ }\n+ break;\n+ default:\n+ throw new Error('evaluate is not implemented for this filter type.');\n }\n- return units;\n+ return intersect;\n },\n \n /**\n- * APIMethod: getScale\n+ * APIMethod: clone\n+ * Clones this filter.\n * \n * Returns:\n- * {Float} The current scale denominator of the map. \n- * If no baselayer is set, returns null.\n+ * {} Clone of this filter.\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+ clone: function() {\n+ var options = OpenLayers.Util.applyDefaults({\n+ value: this.value && this.value.clone && this.value.clone()\n+ }, this);\n+ return new OpenLayers.Filter.Spatial(options);\n },\n+ CLASS_NAME: \"OpenLayers.Filter.Spatial\"\n+});\n+\n+OpenLayers.Filter.Spatial.BBOX = \"BBOX\";\n+OpenLayers.Filter.Spatial.INTERSECTS = \"INTERSECTS\";\n+OpenLayers.Filter.Spatial.DWITHIN = \"DWITHIN\";\n+OpenLayers.Filter.Spatial.WITHIN = \"WITHIN\";\n+OpenLayers.Filter.Spatial.CONTAINS = \"CONTAINS\";\n+/* ======================================================================\n+ OpenLayers/Filter/FeatureId.js\n+ ====================================================================== */\n \n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * APIMethod: 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+/**\n+ * @requires OpenLayers/Filter.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Filter.FeatureId\n+ * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD\n+ * styling\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {\n+\n+ /** \n+ * APIProperty: fids\n+ * {Array(String)} Feature Ids to evaluate this rule against. \n+ * To be passed inside the params object.\n+ */\n+ fids: null,\n+\n+ /** \n+ * Property: type\n+ * {String} Type to identify this filter.\n+ */\n+ type: \"FID\",\n+\n+ /** \n+ * Constructor: OpenLayers.Filter.FeatureId\n+ * Creates an ogc:FeatureId rule.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object with properties to set on the\n+ * rule\n * \n * Returns:\n- * {Integer} A suitable zoom level for the specified bounds.\n- * If no baselayer is set, returns null.\n+ * {}\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+ initialize: function(options) {\n+ this.fids = [];\n+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n },\n \n /**\n- * APIMethod: getResolutionForZoom\n+ * APIMethod: evaluate\n+ * evaluates this rule for a specific feature\n * \n * Parameters:\n- * zoom - {Float}\n+ * feature - {} feature to apply the rule to.\n+ * For vector features, the check is run against the fid,\n+ * for plain features against the id.\n * \n * Returns:\n- * {Float} A suitable resolution for the specified zoom. If no baselayer\n- * is set, returns null.\n+ * {Boolean} true if the rule applies, false if it does not\n */\n- getResolutionForZoom: function(zoom) {\n- var resolution = null;\n- if (this.baseLayer) {\n- resolution = this.baseLayer.getResolutionForZoom(zoom);\n+ evaluate: function(feature) {\n+ for (var i = 0, len = this.fids.length; i < len; i++) {\n+ var fid = feature.fid || feature.id;\n+ if (fid == this.fids[i]) {\n+ return true;\n+ }\n }\n- return resolution;\n+ return false;\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+ * APIMethod: clone\n+ * Clones this filter.\n * \n * Returns:\n- * {Integer} A suitable zoom level for the specified resolution.\n- * If no baselayer is set, returns null.\n+ * {} Clone of this filter.\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+ clone: function() {\n+ var filter = new OpenLayers.Filter.FeatureId();\n+ OpenLayers.Util.extend(filter, this);\n+ filter.fids = this.fids.slice();\n+ return filter;\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+ CLASS_NAME: \"OpenLayers.Filter.FeatureId\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WFST/v1.js\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+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- var 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+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/WFST.js\n+ * @requires OpenLayers/Filter/Spatial.js\n+ * @requires OpenLayers/Filter/FeatureId.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WFST.v1\n+ * Superclass for WFST parsers.\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\n \n /**\n- * APIMethod: zoomIn\n- * \n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n */\n- zoomIn: function() {\n- this.zoomTo(this.getZoom() + 1);\n+ namespaces: {\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n+ wfs: \"http://www.opengis.net/wfs\",\n+ gml: \"http://www.opengis.net/gml\",\n+ ogc: \"http://www.opengis.net/ogc\",\n+ ows: \"http://www.opengis.net/ows\"\n },\n \n /**\n- * APIMethod: zoomOut\n- * \n+ * Property: defaultPrefix\n */\n- zoomOut: function() {\n- this.zoomTo(this.getZoom() - 1);\n- },\n+ defaultPrefix: \"wfs\",\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+ * Property: version\n+ * {String} WFS version number.\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+ version: null,\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+ * Property: schemaLocation\n+ * {String} Schema location for a particular minor version.\n+ */\n+ schemaLocations: null,\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+ * APIProperty: srsName\n+ * {String} URI for spatial reference system.\n */\n- zoomToMaxExtent: function(options) {\n- //restricted is true by default\n- var restricted = (options) ? options.restricted : true;\n+ srsName: null,\n \n- var maxExtent = this.getMaxExtent({\n- 'restricted': restricted\n- });\n- this.zoomToExtent(maxExtent);\n- },\n+ /**\n+ * APIProperty: extractAttributes\n+ * {Boolean} Extract attributes from GML. Default is true.\n+ */\n+ extractAttributes: true,\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+ * APIProperty: xy\n+ * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)\n+ * Changing is not recommended, a new Format should be instantiated.\n */\n- zoomToScale: function(scale, closest) {\n- var res = OpenLayers.Util.getResolutionFromScale(scale,\n- this.baseLayer.units);\n+ xy: true,\n \n- var halfWDeg = (this.size.w * res) / 2;\n- var halfHDeg = (this.size.h * res) / 2;\n- var center = this.getCachedCenter();\n+ /**\n+ * Property: stateName\n+ * {Object} Maps feature states to node names.\n+ */\n+ stateName: null,\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+ * Constructor: OpenLayers.Format.WFST.v1\n+ * Instances of this class are not created directly. Use the\n+ * or \n+ * constructor instead.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ // set state name mapping\n+ this.stateName = {};\n+ this.stateName[OpenLayers.State.INSERT] = \"wfs:Insert\";\n+ this.stateName[OpenLayers.State.UPDATE] = \"wfs:Update\";\n+ this.stateName[OpenLayers.State.DELETE] = \"wfs:Delete\";\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\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+ * Method: getSrsName\n+ */\n+ getSrsName: function(feature, options) {\n+ var srsName = options && options.srsName;\n+ if (!srsName) {\n+ if (feature && feature.layer) {\n+ srsName = feature.layer.projection.getCode();\n+ } else {\n+ srsName = this.srsName;\n+ }\n+ }\n+ return srsName;\n+ },\n \n /**\n- * Method: getLonLatFromViewPortPx\n- * \n+ * APIMethod: read\n+ * Parse the response from a transaction. Because WFS is split into\n+ * Transaction requests (create, update, and delete) and GetFeature\n+ * requests (read), this method handles parsing of both types of\n+ * responses.\n+ *\n * Parameters:\n- * viewPortPx - {|Object} An OpenLayers.Pixel or\n- * an object with a 'x'\n- * and 'y' properties.\n- * \n+ * data - {String | Document} The WFST document to read\n+ * options - {Object} Options for the reader\n+ *\n+ * Valid options properties:\n+ * output - {String} either \"features\" or \"object\". The default is\n+ * \"features\", which means that the method will return an array of\n+ * features. If set to \"object\", an object with a \"features\" property\n+ * and other properties read by the parser will be returned.\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+ * {Array | Object} Output depending on the output option.\n */\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- if (this.baseLayer != null) {\n- lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);\n+ read: function(data, options) {\n+ options = options || {};\n+ OpenLayers.Util.applyDefaults(options, {\n+ output: \"features\"\n+ });\n+\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n }\n- return lonlat;\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var obj = {};\n+ if (data) {\n+ this.readNode(data, obj, true);\n+ }\n+ if (obj.features && options.output === \"features\") {\n+ obj = obj.features;\n+ }\n+ return obj;\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+ * 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- getViewPortPxFromLonLat: function(lonlat) {\n- var px = null;\n- if (this.baseLayer != null) {\n- px = this.baseLayer.getViewPortPxFromLonLat(lonlat);\n+ readers: {\n+ \"wfs\": {\n+ \"FeatureCollection\": function(node, obj) {\n+ obj.features = [];\n+ this.readChildNodes(node, obj);\n+ }\n }\n- return px;\n },\n \n /**\n- * Method: getZoomTargetCenter\n+ * Method: write\n+ * Given an array of features, write a WFS transaction. This assumes\n+ * the features have a state property that determines the operation\n+ * type - insert, update, or delete.\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+ * features - {Array()} A list of features. See\n+ * below for a more detailed description of the influence of the\n+ * feature's *modified* property.\n+ * options - {Object}\n+ *\n+ * feature.modified rules:\n+ * If a feature has a modified property set, the following checks will be\n+ * made before a feature's geometry or attribute is included in an Update\n+ * transaction:\n+ * - *modified* is not set at all: The geometry and all attributes will be\n+ * included.\n+ * - *modified.geometry* is set (null or a geometry): The geometry will be\n+ * included. If *modified.attributes* is not set, all attributes will\n+ * be included.\n+ * - *modified.attributes* is set: Only the attributes set (i.e. to null or\n+ * a value) in *modified.attributes* will be included. \n+ * If *modified.geometry* is not set, the geometry will not be included.\n+ *\n+ * Valid options include:\n+ * - *multi* {Boolean} If set to true, geometries will be casted to\n+ * Multi geometries before writing.\n *\n * Returns:\n- * {} The location of the map center after the\n- * transformation described by the origin xy and the target resolution.\n+ * {String} A serialized WFS transaction.\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+ write: function(features, options) {\n+ var node = this.writeNode(\"wfs:Transaction\", {\n+ features: features,\n+ options: options\n+ });\n+ var value = this.schemaLocationAttr();\n+ if (value) {\n+ this.setAttributeNS(\n+ node, this.namespaces[\"xsi\"], \"xsi:schemaLocation\", value\n );\n }\n- return lonlat;\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"wfs\": {\n+ \"GetFeature\": function(options) {\n+ var node = this.createElementNSPlus(\"wfs:GetFeature\", {\n+ attributes: {\n+ service: \"WFS\",\n+ version: this.version,\n+ handle: options && options.handle,\n+ outputFormat: options && options.outputFormat,\n+ maxFeatures: options && options.maxFeatures,\n+ \"xsi:schemaLocation\": this.schemaLocationAttr(options)\n+ }\n+ });\n+ if (typeof this.featureType == \"string\") {\n+ this.writeNode(\"Query\", options, node);\n+ } else {\n+ for (var i = 0, len = this.featureType.length; i < len; i++) {\n+ options.featureType = this.featureType[i];\n+ this.writeNode(\"Query\", options, node);\n+ }\n+ }\n+ return node;\n+ },\n+ \"Transaction\": function(obj) {\n+ obj = obj || {};\n+ var options = obj.options || {};\n+ var node = this.createElementNSPlus(\"wfs:Transaction\", {\n+ attributes: {\n+ service: \"WFS\",\n+ version: this.version,\n+ handle: options.handle\n+ }\n+ });\n+ var i, len;\n+ var features = obj.features;\n+ if (features) {\n+ // temporarily re-assigning geometry types\n+ if (options.multi === true) {\n+ OpenLayers.Util.extend(this.geometryTypes, {\n+ \"OpenLayers.Geometry.Point\": \"MultiPoint\",\n+ \"OpenLayers.Geometry.LineString\": (this.multiCurve === true) ? \"MultiCurve\" : \"MultiLineString\",\n+ \"OpenLayers.Geometry.Polygon\": (this.multiSurface === true) ? \"MultiSurface\" : \"MultiPolygon\"\n+ });\n+ }\n+ var name, feature;\n+ for (i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ name = this.stateName[feature.state];\n+ if (name) {\n+ this.writeNode(name, {\n+ feature: feature,\n+ options: options\n+ }, node);\n+ }\n+ }\n+ // switch back to original geometry types assignment\n+ if (options.multi === true) {\n+ this.setGeometryTypes();\n+ }\n+ }\n+ if (options.nativeElements) {\n+ for (i = 0, len = options.nativeElements.length; i < len; ++i) {\n+ this.writeNode(\"wfs:Native\",\n+ options.nativeElements[i], node);\n+ }\n+ }\n+ return node;\n+ },\n+ \"Native\": function(nativeElement) {\n+ var node = this.createElementNSPlus(\"wfs:Native\", {\n+ attributes: {\n+ vendorId: nativeElement.vendorId,\n+ safeToIgnore: nativeElement.safeToIgnore\n+ },\n+ value: nativeElement.value\n+ });\n+ return node;\n+ },\n+ \"Insert\": function(obj) {\n+ var feature = obj.feature;\n+ var options = obj.options;\n+ var node = this.createElementNSPlus(\"wfs:Insert\", {\n+ attributes: {\n+ handle: options && options.handle\n+ }\n+ });\n+ this.srsName = this.getSrsName(feature);\n+ this.writeNode(\"feature:_typeName\", feature, node);\n+ return node;\n+ },\n+ \"Update\": function(obj) {\n+ var feature = obj.feature;\n+ var options = obj.options;\n+ var node = this.createElementNSPlus(\"wfs:Update\", {\n+ attributes: {\n+ handle: options && options.handle,\n+ typeName: (this.featureNS ? this.featurePrefix + \":\" : \"\") +\n+ this.featureType\n+ }\n+ });\n+ if (this.featureNS) {\n+ node.setAttribute(\"xmlns:\" + this.featurePrefix, this.featureNS);\n+ }\n+\n+ // add in geometry\n+ var modified = feature.modified;\n+ if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) {\n+ this.srsName = this.getSrsName(feature);\n+ this.writeNode(\n+ \"Property\", {\n+ name: this.geometryName,\n+ value: feature.geometry\n+ }, node\n+ );\n+ }\n+\n+ // add in attributes\n+ for (var key in feature.attributes) {\n+ if (feature.attributes[key] !== undefined &&\n+ (!modified || !modified.attributes ||\n+ (modified.attributes && modified.attributes[key] !== undefined))) {\n+ this.writeNode(\n+ \"Property\", {\n+ name: key,\n+ value: feature.attributes[key]\n+ }, node\n+ );\n+ }\n+ }\n+\n+ // add feature id filter\n+ this.writeNode(\"ogc:Filter\", new OpenLayers.Filter.FeatureId({\n+ fids: [feature.fid]\n+ }), node);\n+\n+ return node;\n+ },\n+ \"Property\": function(obj) {\n+ var node = this.createElementNSPlus(\"wfs:Property\");\n+ this.writeNode(\"Name\", obj.name, node);\n+ if (obj.value !== null) {\n+ this.writeNode(\"Value\", obj.value, node);\n+ }\n+ return node;\n+ },\n+ \"Name\": function(name) {\n+ return this.createElementNSPlus(\"wfs:Name\", {\n+ value: name\n+ });\n+ },\n+ \"Value\": function(obj) {\n+ var node;\n+ if (obj instanceof OpenLayers.Geometry) {\n+ node = this.createElementNSPlus(\"wfs:Value\");\n+ var geom = this.writeNode(\"feature:_geometry\", obj).firstChild;\n+ node.appendChild(geom);\n+ } else {\n+ node = this.createElementNSPlus(\"wfs:Value\", {\n+ value: obj\n+ });\n+ }\n+ return node;\n+ },\n+ \"Delete\": function(obj) {\n+ var feature = obj.feature;\n+ var options = obj.options;\n+ var node = this.createElementNSPlus(\"wfs:Delete\", {\n+ attributes: {\n+ handle: options && options.handle,\n+ typeName: (this.featureNS ? this.featurePrefix + \":\" : \"\") +\n+ this.featureType\n+ }\n+ });\n+ if (this.featureNS) {\n+ node.setAttribute(\"xmlns:\" + this.featurePrefix, this.featureNS);\n+ }\n+ this.writeNode(\"ogc:Filter\", new OpenLayers.Filter.FeatureId({\n+ fids: [feature.fid]\n+ }), node);\n+ return node;\n+ }\n+ }\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+ * Method: schemaLocationAttr\n+ * Generate the xsi:schemaLocation attribute value.\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+ * {String} The xsi:schemaLocation attribute or undefined if none.\n */\n- getLonLatFromPixel: function(px) {\n- return this.getLonLatFromViewPortPx(px);\n+ schemaLocationAttr: function(options) {\n+ options = OpenLayers.Util.extend({\n+ featurePrefix: this.featurePrefix,\n+ schema: this.schema\n+ }, options);\n+ var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);\n+ if (options.schema) {\n+ schemaLocations[options.featurePrefix] = options.schema;\n+ }\n+ var parts = [];\n+ var uri;\n+ for (var key in schemaLocations) {\n+ uri = this.namespaces[key];\n+ if (uri) {\n+ parts.push(uri + \" \" + schemaLocations[key]);\n+ }\n+ }\n+ var value = parts.join(\" \") || undefined;\n+ return value;\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+ * Method: setFilterProperty\n+ * Set the property of each spatial filter.\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+ * filter - {}\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+ setFilterProperty: function(filter) {\n+ if (filter.filters) {\n+ for (var i = 0, len = filter.filters.length; i < len; ++i) {\n+ OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]);\n+ }\n+ } else {\n+ if (filter instanceof OpenLayers.Filter.Spatial && !filter.property) {\n+ // got a spatial filter without property, so set it\n+ filter.property = this.geometryName;\n+ }\n+ }\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+ CLASS_NAME: \"OpenLayers.Format.WFST.v1\"\n \n- return new OpenLayers.Size(\n- OpenLayers.Util.distVincenty(left, right),\n- OpenLayers.Util.distVincenty(bottom, top)\n- );\n- },\n+});\n+/* ======================================================================\n+ OpenLayers/Filter/Logical.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n \n+/**\n+ * @requires OpenLayers/Filter.js\n+ */\n \n- //\n- // TRANSLATION: ViewPortPx <-> LayerPx\n- //\n+/**\n+ * Class: OpenLayers.Filter.Logical\n+ * This class represents ogc:And, ogc:Or and ogc:Not rules.\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {\n \n /**\n- * APIMethod: getViewPortPxFromLayerPx\n- * \n- * Parameters:\n- * layerPx - {}\n- * \n- * Returns:\n- * {} Layer Pixel translated into ViewPort Pixel \n- * coordinates\n+ * APIProperty: filters\n+ * {Array()} Child filters for this filter.\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+ filters: null,\n \n /**\n- * APIMethod: getLayerPxFromViewPortPx\n- * \n+ * APIProperty: type\n+ * {String} type of logical operator. Available types are:\n+ * - OpenLayers.Filter.Logical.AND = \"&&\";\n+ * - OpenLayers.Filter.Logical.OR = \"||\";\n+ * - OpenLayers.Filter.Logical.NOT = \"!\";\n+ */\n+ type: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Filter.Logical\n+ * Creates a logical filter (And, Or, Not).\n+ *\n * Parameters:\n- * viewPortPx - {}\n+ * options - {Object} An optional object with properties to set on the\n+ * filter.\n * \n * Returns:\n- * {} ViewPort Pixel translated into Layer Pixel \n- * coordinates\n+ * {}\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+ initialize: function(options) {\n+ this.filters = [];\n+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n },\n \n- //\n- // TRANSLATION: LonLat <-> LayerPx\n- //\n-\n- /**\n- * Method: getLonLatFromLayerPx\n- * \n- * Parameters:\n- * px - {}\n- *\n- * Returns:\n- * {}\n+ /** \n+ * APIMethod: destroy\n+ * Remove reference to child filters.\n */\n- getLonLatFromLayerPx: function(px) {\n- //adjust for displacement of layerContainerDiv\n- px = this.getViewPortPxFromLayerPx(px);\n- return this.getLonLatFromViewPortPx(px);\n+ destroy: function() {\n+ this.filters = null;\n+ OpenLayers.Filter.prototype.destroy.apply(this);\n },\n \n /**\n- * APIMethod: getLayerPxFromLonLat\n+ * APIMethod: evaluate\n+ * Evaluates this filter in a specific context.\n * \n * Parameters:\n- * lonlat - {} lonlat\n- *\n+ * context - {Object} Context to use in evaluating the filter. A vector\n+ * feature may also be provided to evaluate feature attributes in \n+ * comparison filters or geometries in spatial filters.\n+ * \n * Returns:\n- * {} 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+ * {Boolean} The filter applies.\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+ evaluate: function(context) {\n+ var i, len;\n+ switch (this.type) {\n+ case OpenLayers.Filter.Logical.AND:\n+ for (i = 0, len = this.filters.length; i < len; i++) {\n+ if (this.filters[i].evaluate(context) == false) {\n+ return false;\n+ }\n }\n- // 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+ return true;\n+\n+ case OpenLayers.Filter.Logical.OR:\n+ for (i = 0, len = this.filters.length; i < len; i++) {\n+ if (this.filters[i].evaluate(context) == true) {\n+ return true;\n+ }\n }\n- this.applyTransform.template = template;\n- }\n+ return false;\n+\n+ case OpenLayers.Filter.Logical.NOT:\n+ return (!this.filters[0].evaluate(context));\n }\n+ return undefined;\n+ },\n \n- // 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+ * APIMethod: clone\n+ * Clones this filter.\n+ * \n+ * Returns:\n+ * {} Clone of this filter.\n+ */\n+ clone: function() {\n+ var filters = [];\n+ for (var i = 0, len = this.filters.length; i < len; ++i) {\n+ filters.push(this.filters[i].clone());\n }\n+ return new OpenLayers.Filter.Logical({\n+ type: this.type,\n+ filters: filters\n+ });\n },\n \n- CLASS_NAME: \"OpenLayers.Map\"\n+ CLASS_NAME: \"OpenLayers.Filter.Logical\"\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.Filter.Logical.AND = \"&&\";\n+OpenLayers.Filter.Logical.OR = \"||\";\n+OpenLayers.Filter.Logical.NOT = \"!\";\n /* ======================================================================\n- OpenLayers/Layer.js\n+ OpenLayers/Filter/Comparison.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Map.js\n- * @requires OpenLayers/Projection.js\n+ * @requires OpenLayers/Filter.js\n */\n \n /**\n- * Class: OpenLayers.Layer\n+ * Class: OpenLayers.Filter.Comparison\n+ * This class represents a comparison filter.\n+ * \n+ * Inherits from:\n+ * - \n */\n-OpenLayers.Layer = OpenLayers.Class({\n+OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {\n \n /**\n- * APIProperty: id\n- * {String}\n+ * APIProperty: type\n+ * {String} type: type of the comparison. This is one of\n+ * - OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n+ * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n+ * - OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n+ * - OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n+ * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n+ * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n+ * - OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n+ * - OpenLayers.Filter.Comparison.LIKE = \"~\";\n+ * - OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n */\n- id: null,\n+ type: null,\n \n- /** \n- * APIProperty: name\n+ /**\n+ * APIProperty: property\n * {String}\n+ * name of the context property to compare\n */\n- name: null,\n+ property: null,\n \n- /** \n- * APIProperty: div\n- * {DOMElement}\n+ /**\n+ * APIProperty: value\n+ * {Number} or {String}\n+ * comparison value for binary comparisons. In the case of a String, this\n+ * can be a combination of text and propertyNames in the form\n+ * \"literal ${propertyName}\"\n */\n- div: null,\n+ value: 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+ * Property: matchCase\n+ * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO\n+ * comparisons. The Filter Encoding 1.1 specification added a matchCase\n+ * attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo\n+ * elements. This property will be serialized with those elements only\n+ * if using the v1.1.0 filter format. However, when evaluating filters\n+ * here, the matchCase property will always be respected (for EQUAL_TO\n+ * and NOT_EQUAL_TO). Default is true. \n */\n- opacity: 1,\n+ matchCase: true,\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+ * APIProperty: lowerBoundary\n+ * {Number} or {String}\n+ * lower boundary for between comparisons. In the case of a String, this\n+ * can be a combination of text and propertyNames in the form\n+ * \"literal ${propertyName}\"\n */\n- alwaysInRange: null,\n+ lowerBoundary: null,\n \n /**\n- * Constant: RESOLUTION_PROPERTIES\n- * {Array} The properties that are used for calculating resolutions\n- * information.\n+ * APIProperty: upperBoundary\n+ * {Number} or {String}\n+ * upper boundary for between comparisons. In the case of a String, this\n+ * can be a combination of text and propertyNames in the form\n+ * \"literal ${propertyName}\"\n */\n- RESOLUTION_PROPERTIES: [\n- 'scales', 'resolutions',\n- 'maxScale', 'minScale',\n- 'maxResolution', 'minResolution',\n- 'numZoomLevels', 'maxZoomLevel'\n- ],\n+ upperBoundary: null,\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+ * Constructor: OpenLayers.Filter.Comparison\n+ * Creates a comparison rule.\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+ * Parameters:\n+ * options - {Object} An optional object with properties to set on the\n+ * rule\n+ * \n+ * Returns:\n+ * {}\n */\n- events: null,\n+ initialize: function(options) {\n+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n+ // since matchCase on PropertyIsLike is not schema compliant, we only\n+ // want to use this if explicitly asked for\n+ if (this.type === OpenLayers.Filter.Comparison.LIKE &&\n+ options.matchCase === undefined) {\n+ this.matchCase = null;\n+ }\n+ },\n \n /**\n- * APIProperty: map\n- * {} This variable is set when the layer is added to \n- * the map, via the accessor function setMap().\n+ * APIMethod: evaluate\n+ * Evaluates this filter in a specific context.\n+ * \n+ * Parameters:\n+ * context - {Object} Context to use in evaluating the filter. If a vector\n+ * feature is provided, the feature.attributes will be used as context.\n+ * \n+ * Returns:\n+ * {Boolean} The filter applies.\n */\n- map: null,\n+ evaluate: function(context) {\n+ if (context instanceof OpenLayers.Feature.Vector) {\n+ context = context.attributes;\n+ }\n+ var result = false;\n+ var got = context[this.property];\n+ var exp;\n+ switch (this.type) {\n+ case OpenLayers.Filter.Comparison.EQUAL_TO:\n+ exp = this.value;\n+ if (!this.matchCase &&\n+ typeof got == \"string\" && typeof exp == \"string\") {\n+ result = (got.toUpperCase() == exp.toUpperCase());\n+ } else {\n+ result = (got == exp);\n+ }\n+ break;\n+ case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:\n+ exp = this.value;\n+ if (!this.matchCase &&\n+ typeof got == \"string\" && typeof exp == \"string\") {\n+ result = (got.toUpperCase() != exp.toUpperCase());\n+ } else {\n+ result = (got != exp);\n+ }\n+ break;\n+ case OpenLayers.Filter.Comparison.LESS_THAN:\n+ result = got < this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.GREATER_THAN:\n+ result = got > this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:\n+ result = got <= this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:\n+ result = got >= this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.BETWEEN:\n+ result = (got >= this.lowerBoundary) &&\n+ (got <= this.upperBoundary);\n+ break;\n+ case OpenLayers.Filter.Comparison.LIKE:\n+ var regexp = new RegExp(this.value, \"gi\");\n+ result = regexp.test(got);\n+ break;\n+ case OpenLayers.Filter.Comparison.IS_NULL:\n+ result = (got === null);\n+ break;\n+ }\n+ return result;\n+ },\n \n /**\n- * 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+ * APIMethod: value2regex\n+ * Converts the value of this rule into a regular expression string,\n+ * according to the wildcard characters specified. This method has to\n+ * be called after instantiation of this class, if the value is not a\n+ * regular expression already.\n+ * \n+ * Parameters:\n+ * wildCard - {Char} wildcard character in the above value, default\n+ * is \"*\"\n+ * singleChar - {Char} single-character wildcard in the above value\n+ * default is \".\"\n+ * escapeChar - {Char} escape character in the above value, default is\n+ * \"!\"\n+ * \n+ * Returns:\n+ * {String} regular expression string\n */\n- isBaseLayer: false,\n+ value2regex: function(wildCard, singleChar, escapeChar) {\n+ if (wildCard == \".\") {\n+ throw new Error(\"'.' is an unsupported wildCard character for \" +\n+ \"OpenLayers.Filter.Comparison\");\n+ }\n \n- /**\n- * 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+ // set UMN MapServer defaults for unspecified parameters\n+ wildCard = wildCard ? wildCard : \"*\";\n+ singleChar = singleChar ? singleChar : \".\";\n+ escapeChar = escapeChar ? escapeChar : \"!\";\n \n- /**\n- * APIProperty: visibility\n- * {Boolean} The layer should be displayed in the map. Default is true.\n- */\n- visibility: true,\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\" + escapeChar + \"(.|$)\", \"g\"), \"\\\\$1\");\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\" + singleChar, \"g\"), \".\");\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\" + wildCard, \"g\"), \".*\");\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\\\\.\\\\*\", \"g\"), \"\\\\\" + wildCard);\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\\\\\\\\.\", \"g\"), \"\\\\\" + singleChar);\n+\n+ return this.value;\n+ },\n \n /**\n- * APIProperty: attribution\n- * {String} Attribution string, displayed when an \n- * has been added to the map.\n+ * Method: regex2value\n+ * Convert the value of this rule from a regular expression string into an\n+ * ogc literal string using a wildCard of *, a singleChar of ., and an\n+ * escape of !. Leaves the property unmodified.\n+ * \n+ * Returns:\n+ * {String} A string value.\n */\n- attribution: null,\n+ regex2value: function() {\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+ var value = this.value;\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+ // replace ! with !!\n+ value = value.replace(/!/g, \"!!\");\n \n- // OPTIONS\n+ // replace \\. with !. (watching out for \\\\.)\n+ value = value.replace(/(\\\\)?\\\\\\./g, function($0, $1) {\n+ return $1 ? $0 : \"!.\";\n+ });\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+ // replace \\* with #* (watching out for \\\\*)\n+ value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n+ return $1 ? $0 : \"!*\";\n+ });\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+ // replace \\\\ with \\\n+ value = value.replace(/\\\\\\\\/g, \"\\\\\");\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+ // convert .* to * (the sequence #.* is not allowed)\n+ value = value.replace(/\\.\\*/g, \"*\");\n+\n+ return value;\n+ },\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+ * APIMethod: clone\n+ * Clones this filter.\n * \n+ * Returns:\n+ * {} Clone of this filter.\n */\n- projection: null,\n+ clone: function() {\n+ return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);\n+ },\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+ CLASS_NAME: \"OpenLayers.Filter.Comparison\"\n+});\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+OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n+OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n+OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n+OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n+OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n+OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n+OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n+OpenLayers.Filter.Comparison.LIKE = \"~\";\n+OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n+/* ======================================================================\n+ OpenLayers/Format/Filter.js\n+ ====================================================================== */\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+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * APIProperty: 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+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ * @requires OpenLayers/Filter/FeatureId.js\n+ * @requires OpenLayers/Filter/Logical.js\n+ * @requires OpenLayers/Filter/Comparison.js\n+ */\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+ * Class: OpenLayers.Format.Filter\n+ * Read/Write ogc:Filter. Create a new instance with the \n+ * constructor.\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n \n /**\n- * APIProperty: minResolution\n- * {Float}\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.0.0\".\n */\n- minResolution: null,\n+ defaultVersion: \"1.0.0\",\n \n /**\n- * APIProperty: numZoomLevels\n- * {Integer}\n+ * APIMethod: write\n+ * Write an ogc:Filter given a filter object.\n+ *\n+ * Parameters:\n+ * filter - {} An filter.\n+ * options - {Object} Optional configuration object.\n+ *\n+ * Returns:\n+ * {Elment} An ogc:Filter element node.\n */\n- numZoomLevels: null,\n \n /**\n- * APIProperty: minScale\n- * {Float}\n+ * APIMethod: read\n+ * Read and Filter doc and return an object representing the Filter.\n+ *\n+ * Parameters:\n+ * data - {String | DOMElement} Data to read.\n+ *\n+ * Returns:\n+ * {} A filter object.\n */\n- minScale: null,\n \n- /**\n- * APIProperty: maxScale\n- * {Float}\n- */\n- maxScale: null,\n+ CLASS_NAME: \"OpenLayers.Format.Filter\"\n+});\n+/* ======================================================================\n+ OpenLayers/Filter/Function.js\n+ ====================================================================== */\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+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Filter.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Filter.Function\n+ * This class represents a filter function.\n+ * We are using this class for creation of complex \n+ * filters that can contain filter functions as values.\n+ * Nesting function as other functions parameter is supported.\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {\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+ * APIProperty: name\n+ * {String} Name of the function.\n */\n- wrapDateLine: false,\n+ name: null,\n \n /**\n- * Property: metadata\n- * {Object} This object can be used to store additional information on a\n- * layer object.\n+ * APIProperty: params\n+ * {Array( || String || Number)} Function parameters\n+ * For now support only other Functions, String or Number\n */\n- metadata: null,\n+ params: null,\n \n- /**\n- * Constructor: OpenLayers.Layer\n+ /** \n+ * Constructor: OpenLayers.Filter.Function\n+ * Creates a filter function.\n *\n * Parameters:\n- * name - {String} The layer name\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * options - {Object} An optional object with properties to set on the\n+ * function.\n+ * \n+ * Returns:\n+ * {}\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+ CLASS_NAME: \"OpenLayers.Filter.Function\"\n+});\n \n- if (this.id == null) {\n+/* ======================================================================\n+ OpenLayers/BaseTypes/Date.js\n+ ====================================================================== */\n \n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\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+ * @requires OpenLayers/SingleFile.js\n+ */\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+ * Namespace: OpenLayers.Date\n+ * Contains implementations of Date.parse and date.toISOString that match the\n+ * ECMAScript 5 specification for parsing RFC 3339 dates.\n+ * http://tools.ietf.org/html/rfc3339\n+ */\n+OpenLayers.Date = {\n \n- }\n- },\n+ /** \n+ * APIProperty: dateRegEx\n+ * The regex to be used for validating dates. You can provide your own\n+ * regex for instance for adding support for years before BC. Default\n+ * value is: /^(?:(\\d{4})(?:-(\\d{2})(?:-(\\d{2}))?)?)?(?:(?:T(\\d{1,2}):(\\d{2}):(\\d{2}(?:\\.\\d+)?)(Z|(?:[+-]\\d{1,2}(?::(\\d{2}))?)))|Z)?$/\n+ */\n+ dateRegEx: /^(?:(\\d{4})(?:-(\\d{2})(?:-(\\d{2}))?)?)?(?:(?:T(\\d{1,2}):(\\d{2}):(\\d{2}(?:\\.\\d+)?)(Z|(?:[+-]\\d{1,2}(?::(\\d{2}))?)))|Z)?$/,\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+ * APIMethod: toISOString\n+ * Generates a string representing a date. The format of the string follows\n+ * the profile of ISO 8601 for date and time on the Internet (see\n+ * http://tools.ietf.org/html/rfc3339). If the toISOString method is\n+ * available on the Date prototype, that is used. The toISOString\n+ * method for Date instances is defined in ECMA-262.\n *\n * Parameters:\n- * setNewBaseLayer - {Boolean} Set a new base layer when this layer has\n- * been destroyed. Default is true.\n+ * date - {Date} A date object.\n+ *\n+ * Returns:\n+ * {String} A string representing the date (e.g.\n+ * \"2010-08-07T16:58:23.123Z\"). If the date does not have a valid time\n+ * (i.e. isNaN(date.getTime())) this method returns the string \"Invalid\n+ * Date\". The ECMA standard says the toISOString method should throw\n+ * RangeError in this case, but Firefox returns a string instead. For\n+ * best results, use isNaN(date.getTime()) to determine date validity\n+ * before generating date strings.\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+ toISOString: (function() {\n+ if (\"toISOString\" in Date.prototype) {\n+ return function(date) {\n+ return date.toISOString();\n+ };\n+ } else {\n+ return function(date) {\n+ var str;\n+ if (isNaN(date.getTime())) {\n+ // ECMA-262 says throw RangeError, Firefox returns\n+ // \"Invalid Date\"\n+ str = \"Invalid Date\";\n+ } else {\n+ str =\n+ date.getUTCFullYear() + \"-\" +\n+ OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + \"-\" +\n+ OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + \"T\" +\n+ OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + \":\" +\n+ OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + \":\" +\n+ OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + \".\" +\n+ OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + \"Z\";\n+ }\n+ return str;\n+ };\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 /**\n- * Method: clone\n+ * APIMethod: parse\n+ * Generate a date object from a string. The format for the string follows\n+ * the profile of ISO 8601 for date and time on the Internet (see\n+ * http://tools.ietf.org/html/rfc3339). We don't call the native\n+ * Date.parse because of inconsistency between implmentations. In\n+ * Chrome, calling Date.parse with a string that doesn't contain any\n+ * indication of the timezone (e.g. \"2011\"), the date is interpreted\n+ * in local time. On Firefox, the assumption is UTC.\n *\n * Parameters:\n- * obj - {} The layer to be cloned\n+ * str - {String} A string representing the date (e.g.\n+ * \"2010\", \"2010-08\", \"2010-08-07\", \"2010-08-07T16:58:23.123Z\",\n+ * \"2010-08-07T11:58:23.123-06\").\n *\n * Returns:\n- * {} An exact clone of this \n+ * {Date} A date object. If the string could not be parsed, an invalid\n+ * date is returned (i.e. isNaN(date.getTime())).\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer(this.name, this.getOptions());\n+ parse: function(str) {\n+ var date;\n+ var match = str.match(this.dateRegEx);\n+ if (match && (match[1] || match[7])) { // must have at least year or time\n+ var year = parseInt(match[1], 10) || 0;\n+ var month = (parseInt(match[2], 10) - 1) || 0;\n+ var day = parseInt(match[3], 10) || 1;\n+ date = new Date(Date.UTC(year, month, day));\n+ // optional time\n+ var type = match[7];\n+ if (type) {\n+ var hours = parseInt(match[4], 10);\n+ var minutes = parseInt(match[5], 10);\n+ var secFrac = parseFloat(match[6]);\n+ var seconds = secFrac | 0;\n+ var milliseconds = Math.round(1000 * (secFrac - seconds));\n+ date.setUTCHours(hours, minutes, seconds, milliseconds);\n+ // check offset\n+ if (type !== \"Z\") {\n+ var hoursOffset = parseInt(type, 10);\n+ var minutesOffset = parseInt(match[8], 10) || 0;\n+ var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);\n+ date = new Date(date.getTime() + offset);\n+ }\n+ }\n+ } else {\n+ date = new Date(\"invalid\");\n }\n+ return date;\n+ }\n+};\n+/* ======================================================================\n+ OpenLayers/Format/Filter/v1.js\n+ ====================================================================== */\n \n- // catch any randomly tagged-on properties\n- OpenLayers.Util.applyDefaults(obj, this);\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+/**\n+ * @requires OpenLayers/Format/Filter.js\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Filter/Function.js\n+ * @requires OpenLayers/BaseTypes/Date.js\n+ */\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+ * Class: OpenLayers.Format.Filter.v1\n+ * Superclass for Filter version 1 parsers.\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\n \n- return obj;\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ogc: \"http://www.opengis.net/ogc\",\n+ gml: \"http://www.opengis.net/gml\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\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+ * Property: defaultPrefix\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+ defaultPrefix: \"ogc\",\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+ * Property: schemaLocation\n+ * {String} Schema location for a particular minor version.\n+ */\n+ schemaLocation: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.Filter.v1\n+ * Instances of this class are not created directly. Use the\n+ * constructor instead.\n *\n * Parameters:\n- * newName - {String} The new name.\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\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+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n },\n \n /**\n- * APIMethod: addOptions\n- * \n+ * Method: read\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+ * data - {DOMElement} A Filter document element.\n+ *\n+ * Returns:\n+ * {} A filter object.\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+ read: function(data) {\n+ var obj = {};\n+ this.readers.ogc[\"Filter\"].apply(this, [data, obj]);\n+ return obj.filter;\n+ },\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+ * 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+ \"ogc\": {\n+ \"_expression\": function(node) {\n+ // only the simplest of ogc:expression handled\n+ // \"some text and an attribute\"}\n+ var obj, value = \"\";\n+ for (var child = node.firstChild; child; child = child.nextSibling) {\n+ switch (child.nodeType) {\n+ case 1:\n+ obj = this.readNode(child);\n+ if (obj.property) {\n+ value += \"${\" + obj.property + \"}\";\n+ } else if (obj.value !== undefined) {\n+ value += obj.value;\n+ }\n+ break;\n+ case 3: // text node\n+ case 4: // cdata section\n+ value += child.nodeValue;\n }\n- break;\n }\n+ return value;\n+ },\n+ \"Filter\": function(node, parent) {\n+ // Filters correspond to subclasses of OpenLayers.Filter.\n+ // Since they contain information we don't persist, we\n+ // create a temporary object and then pass on the filter\n+ // (ogc:Filter) to the parent obj.\n+ var obj = {\n+ fids: [],\n+ filters: []\n+ };\n+ this.readChildNodes(node, obj);\n+ if (obj.fids.length > 0) {\n+ parent.filter = new OpenLayers.Filter.FeatureId({\n+ fids: obj.fids\n+ });\n+ } else if (obj.filters.length > 0) {\n+ parent.filter = obj.filters[0];\n+ }\n+ },\n+ \"FeatureId\": function(node, obj) {\n+ var fid = node.getAttribute(\"fid\");\n+ if (fid) {\n+ obj.fids.push(fid);\n+ }\n+ },\n+ \"And\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.AND\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"Or\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.OR\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"Not\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.NOT\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"PropertyIsLessThan\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.LESS_THAN\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"PropertyIsGreaterThan\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.GREATER_THAN\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"PropertyIsLessThanOrEqualTo\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"PropertyIsGreaterThanOrEqualTo\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"PropertyIsBetween\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.BETWEEN\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"Literal\": function(node, obj) {\n+ obj.value = OpenLayers.String.numericIf(\n+ this.getChildValue(node), true);\n+ },\n+ \"PropertyName\": function(node, filter) {\n+ filter.property = this.getChildValue(node);\n+ },\n+ \"LowerBoundary\": function(node, filter) {\n+ filter.lowerBoundary = OpenLayers.String.numericIf(\n+ this.readers.ogc._expression.call(this, node), true);\n+ },\n+ \"UpperBoundary\": function(node, filter) {\n+ filter.upperBoundary = OpenLayers.String.numericIf(\n+ this.readers.ogc._expression.call(this, node), true);\n+ },\n+ \"Intersects\": function(node, obj) {\n+ this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);\n+ },\n+ \"Within\": function(node, obj) {\n+ this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);\n+ },\n+ \"Contains\": function(node, obj) {\n+ this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);\n+ },\n+ \"DWithin\": function(node, obj) {\n+ this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);\n+ },\n+ \"Distance\": function(node, obj) {\n+ obj.distance = parseInt(this.getChildValue(node));\n+ obj.distanceUnits = node.getAttribute(\"units\");\n+ },\n+ \"Function\": function(node, obj) {\n+ //TODO write decoder for it\n+ return;\n+ },\n+ \"PropertyIsNull\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.IS_NULL\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n }\n }\n },\n \n /**\n- * APIMethod: onMapResize\n- * This function can be implemented by subclasses\n+ * Method: readSpatial\n+ *\n+ * Read a {} filter.\n+ * \n+ * Parameters:\n+ * node - {DOMElement} A DOM element that contains an ogc:expression.\n+ * obj - {Object} The target object.\n+ * type - {String} One of the OpenLayers.Filter.Spatial.* constants.\n+ *\n+ * Returns:\n+ * {} The created filter.\n */\n- onMapResize: function() {\n- //this function can be implemented by subclasses \n+ readSpatial: function(node, obj, type) {\n+ var filter = new OpenLayers.Filter.Spatial({\n+ type: type\n+ });\n+ this.readChildNodes(node, filter);\n+ filter.value = filter.components[0];\n+ delete filter.components;\n+ obj.filters.push(filter);\n },\n \n /**\n- * APIMethod: redraw\n- * Redraws the layer. Returns true if the layer was redrawn, false if not.\n+ * APIMethod: encodeLiteral\n+ * Generates the string representation of a value for use in \n+ * elements. The default encoder writes Date values as ISO 8601 \n+ * strings.\n+ *\n+ * Parameters:\n+ * value - {Object} Literal value to encode\n *\n * Returns:\n- * {Boolean} The layer was redrawn.\n+ * {String} String representation of the provided value.\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+ encodeLiteral: function(value) {\n+ if (value instanceof Date) {\n+ value = OpenLayers.Date.toISOString(value);\n }\n- return redrawn;\n+ return value;\n },\n \n /**\n- * Method: moveTo\n- * \n+ * Method: writeOgcExpression\n+ * Limited support for writing OGC expressions. Currently it supports\n+ * ( || String || Number)\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+ * value - ( || String || Number)\n+ * node - {DOMElement} A parent DOM element \n+ *\n+ * Returns:\n+ * {DOMElement} Updated node element.\n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- var display = this.visibility;\n- if (!this.isBaseLayer) {\n- display = display && this.inRange;\n+ writeOgcExpression: function(value, node) {\n+ if (value instanceof OpenLayers.Filter.Function) {\n+ this.writeNode(\"Function\", value, node);\n+ } else {\n+ this.writeNode(\"Literal\", value, node);\n }\n- this.display(display);\n+ return node;\n },\n \n /**\n- * Method: moveByPx\n- * Move the layer based on pixel vector. To be implemented by subclasses.\n+ * Method: write\n *\n * Parameters:\n- * dx - {Number} The x coord of the displacement vector.\n- * dy - {Number} The y coord of the displacement vector.\n+ * filter - {} A filter object.\n+ *\n+ * Returns:\n+ * {DOMElement} An ogc:Filter element.\n */\n- moveByPx: function(dx, dy) {},\n+ write: function(filter) {\n+ return this.writers.ogc[\"Filter\"].apply(this, [filter]);\n+ },\n \n /**\n- * Method: setMap\n- * Set the map property for the layer. This is done through an accessor\n- * so that subclasses can override this and take special action once \n- * they have their map variable set. \n- * \n- * Here we take care to bring over any of the necessary default \n- * properties from the map. \n- * \n- * Parameters:\n- * map - {}\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n */\n- setMap: function(map) {\n- if (this.map == null) {\n+ writers: {\n+ \"ogc\": {\n+ \"Filter\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:Filter\");\n+ this.writeNode(this.getFilterType(filter), filter, node);\n+ return node;\n+ },\n+ \"_featureIds\": function(filter) {\n+ var node = this.createDocumentFragment();\n+ for (var i = 0, ii = filter.fids.length; i < ii; ++i) {\n+ this.writeNode(\"ogc:FeatureId\", filter.fids[i], node);\n+ }\n+ return node;\n+ },\n+ \"FeatureId\": function(fid) {\n+ return this.createElementNSPlus(\"ogc:FeatureId\", {\n+ attributes: {\n+ fid: fid\n+ }\n+ });\n+ },\n+ \"And\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:And\");\n+ var childFilter;\n+ for (var i = 0, ii = filter.filters.length; i < ii; ++i) {\n+ childFilter = filter.filters[i];\n+ this.writeNode(\n+ this.getFilterType(childFilter), childFilter, node\n+ );\n+ }\n+ return node;\n+ },\n+ \"Or\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:Or\");\n+ var childFilter;\n+ for (var i = 0, ii = filter.filters.length; i < ii; ++i) {\n+ childFilter = filter.filters[i];\n+ this.writeNode(\n+ this.getFilterType(childFilter), childFilter, node\n+ );\n+ }\n+ return node;\n+ },\n+ \"Not\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:Not\");\n+ var childFilter = filter.filters[0];\n+ this.writeNode(\n+ this.getFilterType(childFilter), childFilter, node\n+ );\n+ return node;\n+ },\n+ \"PropertyIsLessThan\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:PropertyIsLessThan\");\n+ // no ogc:expression handling for PropertyName for now\n+ this.writeNode(\"PropertyName\", filter, node);\n+ // handle Literals or Functions for now\n+ this.writeOgcExpression(filter.value, node);\n+ return node;\n+ },\n+ \"PropertyIsGreaterThan\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:PropertyIsGreaterThan\");\n+ // no ogc:expression handling for PropertyName for now\n+ this.writeNode(\"PropertyName\", filter, node);\n+ // handle Literals or Functions for now\n+ this.writeOgcExpression(filter.value, node);\n+ return node;\n+ },\n+ \"PropertyIsLessThanOrEqualTo\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:PropertyIsLessThanOrEqualTo\");\n+ // no ogc:expression handling for PropertyName for now\n+ this.writeNode(\"PropertyName\", filter, node);\n+ // handle Literals or Functions for now\n+ this.writeOgcExpression(filter.value, node);\n+ return node;\n+ },\n+ \"PropertyIsGreaterThanOrEqualTo\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:PropertyIsGreaterThanOrEqualTo\");\n+ // no ogc:expression handling for PropertyName for now\n+ this.writeNode(\"PropertyName\", filter, node);\n+ // handle Literals or Functions for now\n+ this.writeOgcExpression(filter.value, node);\n+ return node;\n+ },\n+ \"PropertyIsBetween\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:PropertyIsBetween\");\n+ // no ogc:expression handling for PropertyName for now\n+ this.writeNode(\"PropertyName\", filter, node);\n+ this.writeNode(\"LowerBoundary\", filter, node);\n+ this.writeNode(\"UpperBoundary\", filter, node);\n+ return node;\n+ },\n+ \"PropertyName\": function(filter) {\n+ // no ogc:expression handling for now\n+ return this.createElementNSPlus(\"ogc:PropertyName\", {\n+ value: filter.property\n+ });\n+ },\n+ \"Literal\": function(value) {\n+ var encode = this.encodeLiteral ||\n+ OpenLayers.Format.Filter.v1.prototype.encodeLiteral;\n+ return this.createElementNSPlus(\"ogc:Literal\", {\n+ value: encode(value)\n+ });\n+ },\n+ \"LowerBoundary\": function(filter) {\n+ // handle Literals or Functions for now\n+ var node = this.createElementNSPlus(\"ogc:LowerBoundary\");\n+ this.writeOgcExpression(filter.lowerBoundary, node);\n+ return node;\n+ },\n+ \"UpperBoundary\": function(filter) {\n+ // handle Literals or Functions for now\n+ var node = this.createElementNSPlus(\"ogc:UpperBoundary\");\n+ this.writeNode(\"Literal\", filter.upperBoundary, node);\n+ return node;\n+ },\n+ \"INTERSECTS\": function(filter) {\n+ return this.writeSpatial(filter, \"Intersects\");\n+ },\n+ \"WITHIN\": function(filter) {\n+ return this.writeSpatial(filter, \"Within\");\n+ },\n+ \"CONTAINS\": function(filter) {\n+ return this.writeSpatial(filter, \"Contains\");\n+ },\n+ \"DWITHIN\": function(filter) {\n+ var node = this.writeSpatial(filter, \"DWithin\");\n+ this.writeNode(\"Distance\", filter, node);\n+ return node;\n+ },\n+ \"Distance\": function(filter) {\n+ return this.createElementNSPlus(\"ogc:Distance\", {\n+ attributes: {\n+ units: filter.distanceUnits\n+ },\n+ value: filter.distance\n+ });\n+ },\n+ \"Function\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:Function\", {\n+ attributes: {\n+ name: filter.name\n+ }\n+ });\n+ var params = filter.params;\n+ for (var i = 0, len = params.length; i < len; i++) {\n+ this.writeOgcExpression(params[i], node);\n+ }\n+ return node;\n+ },\n+ \"PropertyIsNull\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:PropertyIsNull\");\n+ this.writeNode(\"PropertyName\", filter, node);\n+ return node;\n+ }\n+ }\n+ },\n \n- this.map = map;\n+ /**\n+ * Method: getFilterType\n+ */\n+ getFilterType: function(filter) {\n+ var filterType = this.filterMap[filter.type];\n+ if (!filterType) {\n+ throw \"Filter writing not supported for rule type: \" + filter.type;\n+ }\n+ return filterType;\n+ },\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+ * Property: filterMap\n+ * {Object} Contains a member for each filter type. Values are node names\n+ * for corresponding OGC Filter child elements.\n+ */\n+ filterMap: {\n+ \"&&\": \"And\",\n+ \"||\": \"Or\",\n+ \"!\": \"Not\",\n+ \"==\": \"PropertyIsEqualTo\",\n+ \"!=\": \"PropertyIsNotEqualTo\",\n+ \"<\": \"PropertyIsLessThan\",\n+ \">\": \"PropertyIsGreaterThan\",\n+ \"<=\": \"PropertyIsLessThanOrEqualTo\",\n+ \">=\": \"PropertyIsGreaterThanOrEqualTo\",\n+ \"..\": \"PropertyIsBetween\",\n+ \"~\": \"PropertyIsLike\",\n+ \"NULL\": \"PropertyIsNull\",\n+ \"BBOX\": \"BBOX\",\n+ \"DWITHIN\": \"DWITHIN\",\n+ \"WITHIN\": \"WITHIN\",\n+ \"CONTAINS\": \"CONTAINS\",\n+ \"INTERSECTS\": \"INTERSECTS\",\n+ \"FID\": \"_featureIds\"\n+ },\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+ CLASS_NAME: \"OpenLayers.Format.Filter.v1\"\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+/* ======================================================================\n+ OpenLayers/Format/GML.js\n+ ====================================================================== */\n \n- this.initResolutions();\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- if (!this.isBaseLayer) {\n- this.inRange = this.calculateInRange();\n- var show = ((this.visibility) && (this.inRange));\n- this.div.style.display = show ? \"\" : \"none\";\n- }\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/MultiPoint.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Geometry/MultiLineString.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n+ * @requires OpenLayers/Geometry/MultiPolygon.js\n+ */\n \n- // deal with gutters\n- this.setTileSize();\n- }\n- },\n+/**\n+ * Class: OpenLayers.Format.GML\n+ * Read/Write GML. Create a new instance with the \n+ * constructor. Supports the GML simple features profile.\n+ * \n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {\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+ * APIProperty: featureNS\n+ * {String} Namespace used for feature attributes. Default is\n+ * \"http://mapserver.gis.umn.edu/mapserver\".\n */\n- afterAdd: function() {},\n+ featureNS: \"http://mapserver.gis.umn.edu/mapserver\",\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+ * APIProperty: featurePrefix\n+ * {String} Namespace alias (or prefix) for feature nodes. Default is\n+ * \"feature\".\n */\n- removeMap: function(map) {\n- //to be overridden by subclasses\n- },\n+ featurePrefix: \"feature\",\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+ * APIProperty: featureName\n+ * {String} Element name for features. Default is \"featureMember\".\n */\n- getImageSize: function(bounds) {\n- return (this.imageSize || this.tileSize);\n- },\n+ featureName: \"featureMember\",\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+ * APIProperty: layerName\n+ * {String} Name of data layer. Default is \"features\".\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+ layerName: \"features\",\n \n /**\n- * APIMethod: getVisibility\n- * \n- * Returns:\n- * {Boolean} The layer should be displayed (if in range).\n+ * APIProperty: geometryName\n+ * {String} Name of geometry element. Defaults to \"geometry\".\n */\n- getVisibility: function() {\n- return this.visibility;\n- },\n+ geometryName: \"geometry\",\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+ * APIProperty: collectionName\n+ * {String} Name of featureCollection element.\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+ collectionName: \"FeatureCollection\",\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+ /**\n+ * APIProperty: gmlns\n+ * {String} GML Namespace.\n+ */\n+ gmlns: \"http://www.opengis.net/gml\",\n+\n+ /**\n+ * APIProperty: extractAttributes\n+ * {Boolean} Extract attributes from GML.\n+ */\n+ extractAttributes: true,\n+\n+ /**\n+ * APIProperty: xy\n+ * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)\n+ * Changing is not recommended, a new Format should be instantiated.\n+ */\n+ xy: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.GML\n+ * Create a new parser for GML.\n+ *\n * Parameters:\n- * display - {Boolean}\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\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+ 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+ };\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n },\n \n /**\n- * APIMethod: calculateInRange\n+ * APIMethod: read\n+ * Read data from a string, and return a list of features. \n * \n+ * Parameters:\n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n * Returns:\n- * {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+ * {Array()} An array of features.\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+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ var featureNodes = this.getElementsByTagNameNS(data.documentElement,\n+ this.gmlns,\n+ this.featureName);\n+ var features = [];\n+ for (var i = 0; i < featureNodes.length; i++) {\n+ var feature = this.parseFeature(featureNodes[i]);\n+ if (feature) {\n+ features.push(feature);\n }\n }\n- return inRange;\n+ return features;\n },\n \n- /** \n- * APIMethod: setIsBaseLayer\n- * \n+ /**\n+ * Method: parseFeature\n+ * This function is the core of the GML parsing code in OpenLayers.\n+ * It creates the geometries that are then attached to the returned\n+ * feature, and calls parseAttributes() to get attribute data out.\n+ * \n * Parameters:\n- * isBaseLayer - {Boolean}\n+ * node - {DOMElement} A GML feature node. \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+ parseFeature: function(node) {\n+ // only accept one geometry per feature - look for highest \"order\"\n+ var order = [\"MultiPolygon\", \"Polygon\",\n+ \"MultiLineString\", \"LineString\",\n+ \"MultiPoint\", \"Point\", \"Envelope\"\n+ ];\n+ // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope,\n+ // this code creates a geometry derived from the Envelope. This is not correct.\n+ var type, nodeList, geometry, parser;\n+ for (var i = 0; i < order.length; ++i) {\n+ type = order[i];\n+ nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);\n+ if (nodeList.length > 0) {\n+ // only deal with first geometry of this type\n+ parser = this.parseGeometry[type.toLowerCase()];\n+ if (parser) {\n+ geometry = parser.apply(this, [nodeList[0]]);\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ }\n+ } else {\n+ throw new TypeError(\"Unsupported geometry type: \" + type);\n+ }\n+ // stop looking for different geometry types\n+ break;\n }\n }\n- },\n-\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+ var bounds;\n+ var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, \"Box\");\n+ for (i = 0; i < boxNodes.length; ++i) {\n+ var boxNode = boxNodes[i];\n+ var box = this.parseGeometry[\"box\"].apply(this, [boxNode]);\n+ var parentNode = boxNode.parentNode;\n+ var parentName = parentNode.localName ||\n+ parentNode.nodeName.split(\":\").pop();\n+ if (parentName === \"boundedBy\") {\n+ bounds = box;\n+ } else {\n+ geometry = box.toGeometry();\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+ // construct feature (optionally with attributes)\n+ var attributes;\n+ if (this.extractAttributes) {\n+ attributes = this.parseAttributes(node);\n }\n+ var feature = new OpenLayers.Feature.Vector(geometry, attributes);\n+ feature.bounds = bounds;\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+ feature.gml = {\n+ featureType: node.firstChild.nodeName.split(\":\")[1],\n+ featureNS: node.firstChild.namespaceURI,\n+ featureNSPrefix: node.firstChild.prefix\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+ // assign fid - this can come from a \"fid\" or \"id\" attribute\n+ var childNode = node.firstChild;\n+ var fid;\n+ while (childNode) {\n+ if (childNode.nodeType == 1) {\n+ fid = childNode.getAttribute(\"fid\") ||\n+ childNode.getAttribute(\"id\");\n+ if (fid) {\n+ break;\n+ }\n }\n+ childNode = childNode.nextSibling;\n }\n+ feature.fid = fid;\n+ return feature;\n+ },\n \n- // ok, we new need to set properties in the instance\n+ /**\n+ * Property: parseGeometry\n+ * Properties of this object are the functions that parse geometries based\n+ * on their type.\n+ */\n+ parseGeometry: {\n \n- // 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+ * Method: parseGeometry.point\n+ * Given a GML node representing a point geometry, create an OpenLayers\n+ * point geometry.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A GML node.\n+ *\n+ * Returns:\n+ * {} A point geometry.\n+ */\n+ point: function(node) {\n+ /**\n+ * Three coordinate variations to consider:\n+ * 1) x y z\n+ * 2) x, y, z\n+ * 3) xy\n+ */\n+ var nodeList, coordString;\n+ var coords = [];\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+ // look for \n+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns, \"pos\");\n+ if (nodeList.length > 0) {\n+ coordString = nodeList[0].firstChild.nodeValue;\n+ coordString = coordString.replace(this.regExes.trimSpace, \"\");\n+ coords = coordString.split(this.regExes.splitSpace);\n+ }\n \n- if (props.resolutions) {\n+ // look for \n+ if (coords.length == 0) {\n+ nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n+ \"coordinates\");\n+ if (nodeList.length > 0) {\n+ coordString = nodeList[0].firstChild.nodeValue;\n+ coordString = coordString.replace(this.regExes.removeSpace,\n+ \"\");\n+ coords = coordString.split(\",\");\n+ }\n+ }\n \n- //sort resolutions array descendingly\n- props.resolutions.sort(function(a, b) {\n- return (b - a);\n- });\n+ // look for \n+ if (coords.length == 0) {\n+ nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n+ \"coord\");\n+ if (nodeList.length > 0) {\n+ var xList = this.getElementsByTagNameNS(nodeList[0],\n+ this.gmlns, \"X\");\n+ var yList = this.getElementsByTagNameNS(nodeList[0],\n+ this.gmlns, \"Y\");\n+ if (xList.length > 0 && yList.length > 0) {\n+ coords = [xList[0].firstChild.nodeValue,\n+ yList[0].firstChild.nodeValue\n+ ];\n+ }\n+ }\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+ // preserve third dimension\n+ if (coords.length == 2) {\n+ coords[2] = null;\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+ if (this.xy) {\n+ return new OpenLayers.Geometry.Point(coords[0], coords[1],\n+ coords[2]);\n+ } else {\n+ return new OpenLayers.Geometry.Point(coords[1], coords[0],\n+ coords[2]);\n }\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+ * Method: parseGeometry.multipoint\n+ * Given a GML node representing a multipoint geometry, create an\n+ * OpenLayers multipoint geometry.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A GML node.\n+ *\n+ * Returns:\n+ * {} A multipoint geometry.\n+ */\n+ multipoint: function(node) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n+ \"Point\");\n+ var components = [];\n+ if (nodeList.length > 0) {\n+ var point;\n+ for (var i = 0; i < nodeList.length; ++i) {\n+ point = this.parseGeometry.point.apply(this, [nodeList[i]]);\n+ if (point) {\n+ components.push(point);\n+ }\n+ }\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+ return new OpenLayers.Geometry.MultiPoint(components);\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+ * Method: parseGeometry.linestring\n+ * Given a GML node representing a linestring geometry, create an\n+ * OpenLayers linestring geometry.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A GML node.\n+ *\n+ * Returns:\n+ * {} A linestring geometry.\n+ */\n+ linestring: function(node, ring) {\n+ /**\n+ * Two coordinate variations to consider:\n+ * 1) x0 y0 z0 x1 y1 z1\n+ * 2) x0, y0, z0 x1, y1, z1\n+ */\n+ var nodeList, coordString;\n+ var coords = [];\n+ var points = [];\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+ // look for \n+ nodeList = this.getElementsByTagNameNS(node, this.gmlns, \"posList\");\n+ if (nodeList.length > 0) {\n+ coordString = this.getChildValue(nodeList[0]);\n+ coordString = coordString.replace(this.regExes.trimSpace, \"\");\n+ coords = coordString.split(this.regExes.splitSpace);\n+ var dim = parseInt(nodeList[0].getAttribute(\"dimension\"));\n+ var j, x, y, z;\n+ for (var i = 0; i < coords.length / dim; ++i) {\n+ j = i * dim;\n+ x = coords[j];\n+ y = coords[j + 1];\n+ z = (dim == 2) ? null : coords[j + 2];\n+ if (this.xy) {\n+ points.push(new OpenLayers.Geometry.Point(x, y, z));\n+ } else {\n+ points.push(new OpenLayers.Geometry.Point(y, x, z));\n+ }\n+ }\n+ }\n \n- var viewSize, wRes, hRes;\n+ // look for \n+ if (coords.length == 0) {\n+ nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n+ \"coordinates\");\n+ if (nodeList.length > 0) {\n+ coordString = this.getChildValue(nodeList[0]);\n+ coordString = coordString.replace(this.regExes.trimSpace,\n+ \"\");\n+ coordString = coordString.replace(this.regExes.trimComma,\n+ \",\");\n+ var pointList = coordString.split(this.regExes.splitSpace);\n+ for (var i = 0; i < pointList.length; ++i) {\n+ coords = pointList[i].split(\",\");\n+ if (coords.length == 2) {\n+ coords[2] = null;\n+ }\n+ if (this.xy) {\n+ points.push(new OpenLayers.Geometry.Point(coords[0],\n+ coords[1],\n+ coords[2]));\n+ } else {\n+ points.push(new OpenLayers.Geometry.Point(coords[1],\n+ coords[0],\n+ coords[2]));\n+ }\n+ }\n+ }\n+ }\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+ var line = null;\n+ if (points.length != 0) {\n+ if (ring) {\n+ line = new OpenLayers.Geometry.LinearRing(points);\n+ } else {\n+ line = new OpenLayers.Geometry.LineString(points);\n+ }\n+ }\n+ return line;\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+ * Method: parseGeometry.multilinestring\n+ * Given a GML node representing a multilinestring geometry, create an\n+ * OpenLayers multilinestring geometry.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A GML node.\n+ *\n+ * Returns:\n+ * {} A multilinestring geometry.\n+ */\n+ multilinestring: function(node) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n+ \"LineString\");\n+ var components = [];\n+ if (nodeList.length > 0) {\n+ var line;\n+ for (var i = 0; i < nodeList.length; ++i) {\n+ line = this.parseGeometry.linestring.apply(this,\n+ [nodeList[i]]);\n+ if (line) {\n+ components.push(line);\n+ }\n+ }\n+ }\n+ return new OpenLayers.Geometry.MultiLineString(components);\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+ * Method: parseGeometry.polygon\n+ * Given a GML node representing a polygon geometry, create an\n+ * OpenLayers polygon geometry.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A GML node.\n+ *\n+ * Returns:\n+ * {} A polygon geometry.\n+ */\n+ polygon: function(node) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n+ \"LinearRing\");\n+ var components = [];\n+ if (nodeList.length > 0) {\n+ // this assumes exterior ring first, inner rings after\n+ var ring;\n+ for (var i = 0; i < nodeList.length; ++i) {\n+ ring = this.parseGeometry.linestring.apply(this,\n+ [nodeList[i], true]);\n+ if (ring) {\n+ components.push(ring);\n+ }\n+ }\n+ }\n+ return new OpenLayers.Geometry.Polygon(components);\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+ * Method: parseGeometry.multipolygon\n+ * Given a GML node representing a multipolygon geometry, create an\n+ * OpenLayers multipolygon geometry.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A GML node.\n+ *\n+ * Returns:\n+ * {} A multipolygon geometry.\n+ */\n+ multipolygon: function(node) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n+ \"Polygon\");\n+ var components = [];\n+ if (nodeList.length > 0) {\n+ var polygon;\n+ for (var i = 0; i < nodeList.length; ++i) {\n+ polygon = this.parseGeometry.polygon.apply(this,\n+ [nodeList[i]]);\n+ if (polygon) {\n+ components.push(polygon);\n+ }\n+ }\n+ }\n+ return new OpenLayers.Geometry.MultiPolygon(components);\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+ envelope: function(node) {\n+ var components = [];\n+ var coordString;\n+ var envelope;\n \n- // now we have numZoomLevels and at least one of maxResolution\n- // or minResolution, we can populate the resolutions array\n+ var lpoint = this.getElementsByTagNameNS(node, this.gmlns, \"lowerCorner\");\n+ if (lpoint.length > 0) {\n+ var coords = [];\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+ if (lpoint.length > 0) {\n+ coordString = lpoint[0].firstChild.nodeValue;\n+ coordString = coordString.replace(this.regExes.trimSpace, \"\");\n+ coords = coordString.split(this.regExes.splitSpace);\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+ if (coords.length == 2) {\n+ coords[2] = null;\n+ }\n+ if (this.xy) {\n+ var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);\n+ } else {\n+ var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]);\n+ }\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+ var upoint = this.getElementsByTagNameNS(node, this.gmlns, \"upperCorner\");\n+ if (upoint.length > 0) {\n+ var coords = [];\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- },\n+ if (upoint.length > 0) {\n+ coordString = upoint[0].firstChild.nodeValue;\n+ coordString = coordString.replace(this.regExes.trimSpace, \"\");\n+ coords = coordString.split(this.regExes.splitSpace);\n+ }\n \n- /**\n- * APIMethod: getZoomForExtent\n- * \n- * Parameters:\n- * extent - {}\n- * closest - {Boolean} Find the zoom level that most closely fits the \n- * specified bounds. Note that this may result in a zoom that does \n- * not exactly contain the entire extent.\n- * Default is false.\n- *\n- * Returns:\n- * {Integer} The index of the zoomLevel (entry in the resolutions array) \n- * for the passed-in extent. We do this by calculating the ideal \n- * resolution for the given extent (based on the map size) and then \n- * calling getZoomForResolution(), passing along the 'closest'\n- * parameter.\n- */\n- getZoomForExtent: function(extent, closest) {\n- var viewSize = this.map.getSize();\n- var idealResolution = Math.max(extent.getWidth() / viewSize.w,\n- extent.getHeight() / viewSize.h);\n+ if (coords.length == 2) {\n+ coords[2] = null;\n+ }\n+ if (this.xy) {\n+ var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);\n+ } else {\n+ var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]);\n+ }\n+ }\n \n- return this.getZoomForResolution(idealResolution, closest);\n- },\n+ if (lowerPoint && upperPoint) {\n+ components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));\n+ components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));\n+ components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));\n+ components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));\n+ components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));\n \n- /** \n- * Method: getDataExtent\n- * Calculates the max extent which includes all of the data for the layer.\n- * This function is to be implemented by subclasses.\n- * \n- * Returns:\n- * {}\n- */\n- getDataExtent: function() {\n- //to be implemented by subclasses\n- },\n+ var ring = new OpenLayers.Geometry.LinearRing(components);\n+ envelope = new OpenLayers.Geometry.Polygon([ring]);\n+ }\n+ return envelope;\n+ },\n \n- /**\n- * APIMethod: getResolutionForZoom\n- * \n- * Parameters:\n- * zoom - {Float}\n- * \n- * Returns:\n- * {Float} A suitable resolution for the specified zoom.\n- */\n- getResolutionForZoom: function(zoom) {\n- zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n- var resolution;\n- if (this.map.fractionalZoom) {\n- var low = Math.floor(zoom);\n- var high = Math.ceil(zoom);\n- resolution = this.resolutions[low] -\n- ((zoom - low) * (this.resolutions[low] - this.resolutions[high]));\n- } else {\n- resolution = this.resolutions[Math.round(zoom)];\n+ /**\n+ * Method: parseGeometry.box\n+ * Given a GML node representing a box geometry, create an\n+ * OpenLayers.Bounds.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A GML node.\n+ *\n+ * Returns:\n+ * {} A bounds representing the box.\n+ */\n+ box: function(node) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,\n+ \"coordinates\");\n+ var coordString;\n+ var coords, beginPoint = null,\n+ endPoint = null;\n+ if (nodeList.length > 0) {\n+ coordString = nodeList[0].firstChild.nodeValue;\n+ coords = coordString.split(\" \");\n+ if (coords.length == 2) {\n+ beginPoint = coords[0].split(\",\");\n+ endPoint = coords[1].split(\",\");\n+ }\n+ }\n+ if (beginPoint !== null && endPoint !== null) {\n+ return new OpenLayers.Bounds(parseFloat(beginPoint[0]),\n+ parseFloat(beginPoint[1]),\n+ parseFloat(endPoint[0]),\n+ parseFloat(endPoint[1]));\n+ }\n }\n- return resolution;\n+\n },\n \n /**\n- * APIMethod: getZoomForResolution\n- * \n+ * Method: parseAttributes\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+ * node - {DOMElement}\n+ *\n * Returns:\n- * {Integer} The index of the zoomLevel (entry in the resolutions array) \n- * that corresponds to the best fit resolution given the passed in \n- * value and the 'closest' specification.\n+ * {Object} An attributes object.\n */\n- getZoomForResolution: function(resolution, closest) {\n- var zoom, i, len;\n- if (this.map.fractionalZoom) {\n- var lowZoom = 0;\n- var highZoom = this.resolutions.length - 1;\n- var highRes = this.resolutions[lowZoom];\n- var lowRes = this.resolutions[highZoom];\n- var res;\n- for (i = 0, len = this.resolutions.length; i < len; ++i) {\n- res = this.resolutions[i];\n- if (res >= resolution) {\n- highRes = res;\n- lowZoom = i;\n- }\n- if (res <= resolution) {\n- lowRes = res;\n- highZoom = i;\n- break;\n- }\n- }\n- var dRes = highRes - lowRes;\n- if (dRes > 0) {\n- zoom = lowZoom + ((highRes - resolution) / dRes);\n- } else {\n- zoom = lowZoom;\n- }\n- } else {\n- var diff;\n- var minDiff = Number.POSITIVE_INFINITY;\n- for (i = 0, len = this.resolutions.length; i < len; i++) {\n- if (closest) {\n- diff = Math.abs(this.resolutions[i] - resolution);\n- if (diff > minDiff) {\n- break;\n- }\n- minDiff = diff;\n- } else {\n- if (this.resolutions[i] < resolution) {\n- break;\n+ parseAttributes: function(node) {\n+ var attributes = {};\n+ // assume attributes are children of the first type 1 child\n+ var childNode = node.firstChild;\n+ var children, i, child, grandchildren, grandchild, name, value;\n+ while (childNode) {\n+ if (childNode.nodeType == 1) {\n+ // attributes are type 1 children with one type 3 child\n+ children = childNode.childNodes;\n+ for (i = 0; i < children.length; ++i) {\n+ child = children[i];\n+ if (child.nodeType == 1) {\n+ grandchildren = child.childNodes;\n+ if (grandchildren.length == 1) {\n+ grandchild = grandchildren[0];\n+ if (grandchild.nodeType == 3 ||\n+ grandchild.nodeType == 4) {\n+ name = (child.prefix) ?\n+ child.nodeName.split(\":\")[1] :\n+ child.nodeName;\n+ value = grandchild.nodeValue.replace(\n+ this.regExes.trimSpace, \"\");\n+ attributes[name] = value;\n+ }\n+ } else {\n+ // If child has no childNodes (grandchildren),\n+ // set an attribute with null value.\n+ // e.g. becomes\n+ // {fieldname: null}\n+ attributes[child.nodeName.split(\":\").pop()] = null;\n+ }\n }\n }\n+ break;\n }\n- zoom = Math.max(0, i - 1);\n+ childNode = childNode.nextSibling;\n }\n- return zoom;\n+ return attributes;\n },\n \n /**\n- * APIMethod: getLonLatFromViewPortPx\n+ * APIMethod: write\n+ * Generate a GML document string given a list of features. \n * \n * Parameters:\n- * viewPortPx - {|Object} An OpenLayers.Pixel or\n- * an object with a 'x'\n- * and 'y' properties.\n+ * features - {Array()} List of features to\n+ * serialize into a string.\n *\n * Returns:\n- * {} An OpenLayers.LonLat which is the passed-in \n- * view port , translated into lon/lat by the layer.\n+ * {String} A string representing the GML document.\n */\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- var map = this.map;\n- if (viewPortPx != null && map.minPx) {\n- var res = map.getResolution();\n- var maxExtent = map.getMaxExtent({\n- restricted: true\n- });\n- var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n- var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n- lonlat = new OpenLayers.LonLat(lon, lat);\n-\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent);\n- }\n+ write: function(features) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n }\n- return lonlat;\n+ var gml = this.createElementNS(\"http://www.opengis.net/wfs\",\n+ \"wfs:\" + this.collectionName);\n+ for (var i = 0; i < features.length; i++) {\n+ gml.appendChild(this.createFeatureXML(features[i]));\n+ }\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);\n },\n \n- /**\n- * APIMethod: getViewPortPxFromLonLat\n- * Returns a pixel location given a map location. This method will return\n- * fractional pixel values.\n- * \n+ /** \n+ * Method: createFeatureXML\n+ * Accept an OpenLayers.Feature.Vector, and build a GML node for it.\n+ *\n * Parameters:\n- * lonlat - {|Object} An OpenLayers.LonLat or\n- * an object with a 'lon'\n- * and 'lat' properties.\n+ * feature - {} The feature to be built as GML.\n *\n- * Returns: \n- * {} An which is the passed-in \n- * lonlat translated into view port pixels.\n+ * Returns:\n+ * {DOMElement} A node reprensting the feature in GML.\n */\n- getViewPortPxFromLonLat: function(lonlat, resolution) {\n- var px = null;\n- if (lonlat != null) {\n- resolution = resolution || this.map.getResolution();\n- var extent = this.map.calculateBounds(null, resolution);\n- px = new OpenLayers.Pixel(\n- (1 / resolution * (lonlat.lon - extent.left)),\n- (1 / resolution * (extent.top - lonlat.lat))\n- );\n+ createFeatureXML: function(feature) {\n+ var geometry = feature.geometry;\n+ var geometryNode = this.buildGeometryNode(geometry);\n+ var geomContainer = this.createElementNS(this.featureNS,\n+ this.featurePrefix + \":\" +\n+ this.geometryName);\n+ geomContainer.appendChild(geometryNode);\n+ var featureNode = this.createElementNS(this.gmlns,\n+ \"gml:\" + this.featureName);\n+ var featureContainer = this.createElementNS(this.featureNS,\n+ this.featurePrefix + \":\" +\n+ this.layerName);\n+ var fid = feature.fid || feature.id;\n+ featureContainer.setAttribute(\"fid\", fid);\n+ featureContainer.appendChild(geomContainer);\n+ for (var attr in feature.attributes) {\n+ var attrText = this.createTextNode(feature.attributes[attr]);\n+ var nodename = attr.substring(attr.lastIndexOf(\":\") + 1);\n+ var attrContainer = this.createElementNS(this.featureNS,\n+ this.featurePrefix + \":\" +\n+ nodename);\n+ attrContainer.appendChild(attrText);\n+ featureContainer.appendChild(attrContainer);\n }\n- return px;\n+ featureNode.appendChild(featureContainer);\n+ return featureNode;\n },\n \n /**\n- * APIMethod: setOpacity\n- * Sets the opacity for the entire layer (all images)\n- * \n- * Parameters:\n- * opacity - {Float}\n+ * APIMethod: buildGeometryNode\n */\n- setOpacity: function(opacity) {\n- if (opacity != this.opacity) {\n- this.opacity = opacity;\n- var childNodes = this.div.childNodes;\n- for (var i = 0, len = childNodes.length; i < len; ++i) {\n- var element = childNodes[i].firstChild || childNodes[i];\n- var lastChild = childNodes[i].lastChild;\n- //TODO de-uglify this\n- if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n- element = lastChild.parentNode;\n- }\n- OpenLayers.Util.modifyDOMElement(element, null, null, null,\n- null, null, null, opacity);\n- }\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n- });\n- }\n+ buildGeometryNode: function(geometry) {\n+ if (this.externalProjection && this.internalProjection) {\n+ geometry = geometry.clone();\n+ geometry.transform(this.internalProjection,\n+ this.externalProjection);\n }\n+ var className = geometry.CLASS_NAME;\n+ var type = className.substring(className.lastIndexOf(\".\") + 1);\n+ var builder = this.buildGeometry[type.toLowerCase()];\n+ return builder.apply(this, [geometry]);\n },\n \n /**\n- * Method: getZIndex\n- * \n- * Returns: \n- * {Integer} the z-index of this layer\n- */\n- getZIndex: function() {\n- return this.div.style.zIndex;\n- },\n-\n- /**\n- * Method: setZIndex\n- * \n- * Parameters: \n- * zIndex - {Integer}\n- */\n- setZIndex: function(zIndex) {\n- this.div.style.zIndex = zIndex;\n- },\n-\n- /**\n- * Method: adjustBounds\n- * This function will take a bounds, and if wrapDateLine option is set\n- * on the layer, it will return a bounds which is wrapped around the \n- * world. We do not wrap for bounds which *cross* the \n- * maxExtent.left/right, only bounds which are entirely to the left \n- * or entirely to the right.\n- * \n- * Parameters:\n- * bounds - {}\n+ * Property: buildGeometry\n+ * Object containing methods to do the actual geometry node building\n+ * based on geometry type.\n */\n- adjustBounds: function(bounds) {\n+ buildGeometry: {\n+ // TBD retrieve the srs from layer\n+ // srsName is non-standard, so not including it until it's right.\n+ // gml.setAttribute(\"srsName\",\n+ // \"http://www.opengis.net/gml/srs/epsg.xml#4326\");\n \n- if (this.gutter) {\n- // Adjust the extent of a bounds in map units by the \n- // layer's gutter in pixels.\n- var mapGutter = this.gutter * this.map.getResolution();\n- bounds = new OpenLayers.Bounds(bounds.left - mapGutter,\n- bounds.bottom - mapGutter,\n- bounds.right + mapGutter,\n- bounds.top + mapGutter);\n- }\n+ /**\n+ * Method: buildGeometry.point\n+ * Given an OpenLayers point geometry, create a GML point.\n+ *\n+ * Parameters:\n+ * geometry - {} A point geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A GML point node.\n+ */\n+ point: function(geometry) {\n+ var gml = this.createElementNS(this.gmlns, \"gml:Point\");\n+ gml.appendChild(this.buildCoordinatesNode(geometry));\n+ return gml;\n+ },\n \n- if (this.wrapDateLine) {\n- // wrap around the date line, within the limits of rounding error\n- var wrappingOptions = {\n- 'rightTolerance': this.getResolution(),\n- 'leftTolerance': this.getResolution()\n- };\n- bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);\n+ /**\n+ * Method: buildGeometry.multipoint\n+ * Given an OpenLayers multipoint geometry, create a GML multipoint.\n+ *\n+ * Parameters:\n+ * geometry - {} A multipoint geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A GML multipoint node.\n+ */\n+ multipoint: function(geometry) {\n+ var gml = this.createElementNS(this.gmlns, \"gml:MultiPoint\");\n+ var points = geometry.components;\n+ var pointMember, pointGeom;\n+ for (var i = 0; i < points.length; i++) {\n+ pointMember = this.createElementNS(this.gmlns,\n+ \"gml:pointMember\");\n+ pointGeom = this.buildGeometry.point.apply(this,\n+ [points[i]]);\n+ pointMember.appendChild(pointGeom);\n+ gml.appendChild(pointMember);\n+ }\n+ return gml;\n+ },\n \n- }\n- return bounds;\n- },\n+ /**\n+ * Method: buildGeometry.linestring\n+ * Given an OpenLayers linestring geometry, create a GML linestring.\n+ *\n+ * Parameters:\n+ * geometry - {} A linestring geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A GML linestring node.\n+ */\n+ linestring: function(geometry) {\n+ var gml = this.createElementNS(this.gmlns, \"gml:LineString\");\n+ gml.appendChild(this.buildCoordinatesNode(geometry));\n+ return gml;\n+ },\n \n- CLASS_NAME: \"OpenLayers.Layer\"\n-});\n-/* ======================================================================\n- OpenLayers/Symbolizer.js\n- ====================================================================== */\n+ /**\n+ * Method: buildGeometry.multilinestring\n+ * Given an OpenLayers multilinestring geometry, create a GML\n+ * multilinestring.\n+ *\n+ * Parameters:\n+ * geometry - {} A multilinestring\n+ * geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A GML multilinestring node.\n+ */\n+ multilinestring: function(geometry) {\n+ var gml = this.createElementNS(this.gmlns, \"gml:MultiLineString\");\n+ var lines = geometry.components;\n+ var lineMember, lineGeom;\n+ for (var i = 0; i < lines.length; ++i) {\n+ lineMember = this.createElementNS(this.gmlns,\n+ \"gml:lineStringMember\");\n+ lineGeom = this.buildGeometry.linestring.apply(this,\n+ [lines[i]]);\n+ lineMember.appendChild(lineGeom);\n+ gml.appendChild(lineMember);\n+ }\n+ return gml;\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: buildGeometry.linearring\n+ * Given an OpenLayers linearring geometry, create a GML linearring.\n+ *\n+ * Parameters:\n+ * geometry - {} A linearring geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A GML linearring node.\n+ */\n+ linearring: function(geometry) {\n+ var gml = this.createElementNS(this.gmlns, \"gml:LinearRing\");\n+ gml.appendChild(this.buildCoordinatesNode(geometry));\n+ return gml;\n+ },\n \n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n+ /**\n+ * Method: buildGeometry.polygon\n+ * Given an OpenLayers polygon geometry, create a GML polygon.\n+ *\n+ * Parameters:\n+ * geometry - {} A polygon geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A GML polygon node.\n+ */\n+ polygon: function(geometry) {\n+ var gml = this.createElementNS(this.gmlns, \"gml:Polygon\");\n+ var rings = geometry.components;\n+ var ringMember, ringGeom, type;\n+ for (var i = 0; i < rings.length; ++i) {\n+ type = (i == 0) ? \"outerBoundaryIs\" : \"innerBoundaryIs\";\n+ ringMember = this.createElementNS(this.gmlns,\n+ \"gml:\" + type);\n+ ringGeom = this.buildGeometry.linearring.apply(this,\n+ [rings[i]]);\n+ ringMember.appendChild(ringGeom);\n+ gml.appendChild(ringMember);\n+ }\n+ return gml;\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+ * Method: buildGeometry.multipolygon\n+ * Given an OpenLayers multipolygon geometry, create a GML multipolygon.\n+ *\n+ * Parameters:\n+ * geometry - {} A multipolygon\n+ * geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A GML multipolygon node.\n+ */\n+ multipolygon: function(geometry) {\n+ var gml = this.createElementNS(this.gmlns, \"gml:MultiPolygon\");\n+ var polys = geometry.components;\n+ var polyMember, polyGeom;\n+ for (var i = 0; i < polys.length; ++i) {\n+ polyMember = this.createElementNS(this.gmlns,\n+ \"gml:polygonMember\");\n+ polyGeom = this.buildGeometry.polygon.apply(this,\n+ [polys[i]]);\n+ polyMember.appendChild(polyGeom);\n+ gml.appendChild(polyMember);\n+ }\n+ return gml;\n \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+ * Method: buildGeometry.bounds\n+ * Given an OpenLayers bounds, create a GML box.\n+ *\n+ * Parameters:\n+ * bounds - {} A bounds object.\n+ *\n+ * Returns:\n+ * {DOMElement} A GML box node.\n+ */\n+ bounds: function(bounds) {\n+ var gml = this.createElementNS(this.gmlns, \"gml:Box\");\n+ gml.appendChild(this.buildCoordinatesNode(bounds));\n+ return gml;\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Symbolizer\n- * Instances of this class are not useful. See one of the subclasses.\n+ * Method: buildCoordinates\n+ * builds the coordinates XmlNode\n+ * (code)\n+ * ...\n+ * (end)\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+ * Parameters: \n+ * geometry - {} \n *\n * Returns:\n- * A new symbolizer.\n+ * {XmlNode} created xmlNode\n */\n- initialize: function(config) {\n- OpenLayers.Util.extend(this, config);\n- },\n+ buildCoordinatesNode: function(geometry) {\n+ var coordinatesNode = this.createElementNS(this.gmlns,\n+ \"gml:coordinates\");\n+ coordinatesNode.setAttribute(\"decimal\", \".\");\n+ coordinatesNode.setAttribute(\"cs\", \",\");\n+ coordinatesNode.setAttribute(\"ts\", \" \");\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+ var parts = [];\n \n- CLASS_NAME: \"OpenLayers.Symbolizer\"\n+ if (geometry instanceof OpenLayers.Bounds) {\n+ parts.push(geometry.left + \",\" + geometry.bottom);\n+ parts.push(geometry.right + \",\" + geometry.top);\n+ } else {\n+ var points = (geometry.components) ? geometry.components : [geometry];\n+ for (var i = 0; i < points.length; i++) {\n+ parts.push(points[i].x + \",\" + points[i].y);\n+ }\n+ }\n \n-});\n+ var txtNode = this.createTextNode(parts.join(\" \"));\n+ coordinatesNode.appendChild(txtNode);\n+\n+ return coordinatesNode;\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Format.GML\"\n+});\n /* ======================================================================\n- OpenLayers/Icon.js\n+ OpenLayers/Format/GML/Base.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD 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/GML.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+ * Though required in the full build, if the GML format is excluded, we set\n+ * the namespace here.\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+if (!OpenLayers.Format.GML) {\n+ OpenLayers.Format.GML = {};\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+ * Class: OpenLayers.Format.GML.Base\n+ * Superclass for GML parsers.\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {\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+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n */\n- setSize: function(size) {\n- if (size != null) {\n- this.size = size;\n- }\n- this.draw();\n+ namespaces: {\n+ gml: \"http://www.opengis.net/gml\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n+ wfs: \"http://www.opengis.net/wfs\" // this is a convenience for reading wfs:FeatureCollection\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+ * Property: defaultPrefix\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+ defaultPrefix: \"gml\",\n \n- /** \n- * Method: erase\n- * Erase the underlying image element.\n+ /**\n+ * Property: schemaLocation\n+ * {String} Schema location for a particular minor version.\n */\n- erase: function() {\n- if (this.imageDiv != null && this.imageDiv.parentNode != null) {\n- OpenLayers.Element.remove(this.imageDiv);\n- }\n- },\n+ schemaLocation: null,\n \n- /** \n- * Method: setOpacity\n- * Change the icon's opacity\n- *\n- * Parameters:\n- * opacity - {float} \n+ /**\n+ * APIProperty: featureType\n+ * {Array(String) or String} The local (without prefix) feature typeName(s).\n */\n- setOpacity: function(opacity) {\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,\n- null, null, null, null, opacity);\n-\n- },\n+ featureType: null,\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+ * APIProperty: featureNS\n+ * {String} The feature namespace. Must be set in the options at\n+ * construction.\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+ featureNS: null,\n \n- /** \n- * Method: display\n- * Hide or show the icon\n- *\n- * Parameters:\n- * display - {Boolean} \n+ /**\n+ * APIProperty: geometry\n+ * {String} Name of geometry element. Defaults to \"geometry\". If null, it\n+ * will be set on when the first geometry is parsed.\n */\n- display: function(display) {\n- this.imageDiv.style.display = (display) ? \"\" : \"none\";\n- },\n-\n+ geometryName: \"geometry\",\n \n /**\n- * APIMethod: isDrawn\n- * \n- * Returns:\n- * {Boolean} Whether or not the icon is drawn.\n+ * APIProperty: extractAttributes\n+ * {Boolean} Extract attributes from GML. Default is true.\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/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+ extractAttributes: true,\n \n /**\n- * Property: threshold\n- * In most cases changing the threshold isn't needed.\n- * In px/ms, default to 0.\n+ * APIProperty: srsName\n+ * {String} URI for spatial reference system. This is optional for\n+ * single part geometries and mandatory for collections and multis.\n+ * If set, the srsName attribute will be written for all geometries.\n+ * Default is null.\n */\n- threshold: 0,\n+ srsName: null,\n \n /**\n- * Property: deceleration\n- * {Float} the deseleration in px/ms\u00b2, default to 0.0035.\n+ * APIProperty: xy\n+ * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)\n+ * Changing is not recommended, a new Format should be instantiated.\n */\n- deceleration: 0.0035,\n+ xy: true,\n \n /**\n- * Property: nbPoints\n- * {Integer} the number of points we use to calculate the kinetic\n- * initial values.\n+ * Property: geometryTypes\n+ * {Object} Maps OpenLayers geometry class names to GML element names.\n+ * Use before accessing this property.\n */\n- nbPoints: 100,\n+ geometryTypes: null,\n \n /**\n- * Property: delay\n- * {Float} time to consider to calculate the kinetic initial values.\n- * In ms, default to 200.\n+ * Property: singleFeatureType\n+ * {Boolean} True if there is only 1 featureType, and not an array\n+ * of featuretypes.\n */\n- delay: 200,\n+ singleFeatureType: null,\n \n /**\n- * Property: points\n- * List of points use to calculate the kinetic initial values.\n+ * Property: autoConfig\n+ * {Boolean} Indicates if the format was configured without a ,\n+ * but auto-configured and during read.\n+ * Subclasses making use of auto-configuration should make\n+ * the first call to the method (usually in the read method)\n+ * with true as 3rd argument, so the auto-configured featureType can be\n+ * reset and the format can be reused for subsequent reads with data from\n+ * different featureTypes. Set to false after read if you want to keep the\n+ * auto-configured values.\n */\n- points: undefined,\n \n /**\n- * Property: timerId\n- * ID of the timer.\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n */\n- timerId: undefined,\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g),\n+ featureMember: (/^(.*:)?featureMembers?$/)\n+ },\n \n /**\n- * Constructor: OpenLayers.Kinetic\n+ * Constructor: OpenLayers.Format.GML.Base\n+ * Instances of this class are not created directly. Use the\n+ * or constructor\n+ * instead.\n *\n * Parameters:\n- * options - {Object}\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ *\n+ * Valid options properties:\n+ * featureType - {Array(String) or String} Local (without prefix) feature \n+ * typeName(s) (required for write).\n+ * featureNS - {String} Feature namespace (required for write).\n+ * geometryName - {String} Geometry element name (required for write).\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+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ this.setGeometryTypes();\n+ if (options && options.featureNS) {\n+ this.setNamespace(\"feature\", options.featureNS);\n+ }\n+ this.singleFeatureType = !options || (typeof options.featureType === \"string\");\n },\n \n /**\n- * Method: update\n- * Updates during the dragging.\n+ * Method: read\n *\n * Parameters:\n- * xy - {} The new position.\n+ * data - {DOMElement} A gml:featureMember element, a gml:featureMembers\n+ * element, or an element containing either of the above at any level.\n+ *\n+ * Returns:\n+ * {Array()} An array of features.\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+ 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 features = [];\n+ this.readNode(data, {\n+ features: features\n+ }, true);\n+ if (features.length == 0) {\n+ // look for gml:featureMember elements\n+ var elements = this.getElementsByTagNameNS(\n+ data, this.namespaces.gml, \"featureMember\"\n+ );\n+ if (elements.length) {\n+ for (var i = 0, len = elements.length; i < len; ++i) {\n+ this.readNode(elements[i], {\n+ features: features\n+ }, true);\n+ }\n+ } else {\n+ // look for gml:featureMembers elements (this is v3, but does no harm here)\n+ var elements = this.getElementsByTagNameNS(\n+ data, this.namespaces.gml, \"featureMembers\"\n+ );\n+ if (elements.length) {\n+ // there can be only one\n+ this.readNode(elements[0], {\n+ features: features\n+ }, true);\n+ }\n+ }\n }\n+ return features;\n },\n \n /**\n- * Method: end\n- * Ends the dragging, start the kinetic.\n+ * Method: readNode\n+ * Shorthand for applying one of the named readers given the node\n+ * namespace and local name. Readers take two args (node, obj) and\n+ * generally extend or modify the second.\n *\n * Parameters:\n- * xy - {} The last position.\n+ * node - {DOMElement} The node to be read (required).\n+ * obj - {Object} The object to be modified (optional).\n+ * first - {Boolean} Should be set to true for the first node read. This\n+ * is usually the readNode call in the read method. Without this being\n+ * set, auto-configured properties will stick on subsequent reads.\n *\n * Returns:\n- * {Object} 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+ * {Object} The input object, modified (or a new one if none was provided).\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+ readNode: function(node, obj, first) {\n+ // on subsequent calls of format.read(), we want to reset auto-\n+ // configured properties and auto-configure again.\n+ if (first === true && this.autoConfig === true) {\n+ this.featureType = null;\n+ delete this.namespaceAlias[this.featureNS];\n+ delete this.namespaces[\"feature\"];\n+ this.featureNS = null;\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+ // featureType auto-configuration\n+ if (!this.featureNS && (!(node.prefix in this.namespaces) &&\n+ node.parentNode.namespaceURI == this.namespaces[\"gml\"] &&\n+ this.regExes.featureMember.test(node.parentNode.nodeName))) {\n+ this.featureType = node.nodeName.split(\":\").pop();\n+ this.setNamespace(\"feature\", node.namespaceURI);\n+ this.featureNS = node.namespaceURI;\n+ this.autoConfig = true;\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+ return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"gml\": {\n+ \"_inherit\": function(node, obj, container) {\n+ // To be implemented by version specific parsers\n+ },\n+ \"featureMember\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"featureMembers\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"name\": function(node, obj) {\n+ obj.name = this.getChildValue(node);\n+ },\n+ \"boundedBy\": function(node, obj) {\n+ var container = {};\n+ this.readChildNodes(node, container);\n+ if (container.components && container.components.length > 0) {\n+ obj.bounds = container.components[0];\n+ }\n+ },\n+ \"Point\": function(node, container) {\n+ var obj = {\n+ points: []\n+ };\n+ this.readChildNodes(node, obj);\n+ if (!container.components) {\n+ container.components = [];\n+ }\n+ container.components.push(obj.points[0]);\n+ },\n+ \"coordinates\": function(node, obj) {\n+ var str = this.getChildValue(node).replace(\n+ this.regExes.trimSpace, \"\"\n+ );\n+ str = str.replace(this.regExes.trimComma, \",\");\n+ var pointList = str.split(this.regExes.splitSpace);\n+ var coords;\n+ var numPoints = pointList.length;\n+ var points = new Array(numPoints);\n+ for (var i = 0; i < numPoints; ++i) {\n+ coords = pointList[i].split(\",\");\n+ if (this.xy) {\n+ points[i] = new OpenLayers.Geometry.Point(\n+ coords[0], coords[1], coords[2]\n+ );\n+ } else {\n+ points[i] = new OpenLayers.Geometry.Point(\n+ coords[1], coords[0], coords[2]\n+ );\n+ }\n+ }\n+ obj.points = points;\n+ },\n+ \"coord\": function(node, obj) {\n+ var coord = {};\n+ this.readChildNodes(node, coord);\n+ if (!obj.points) {\n+ obj.points = [];\n+ }\n+ obj.points.push(new OpenLayers.Geometry.Point(\n+ coord.x, coord.y, coord.z\n+ ));\n+ },\n+ \"X\": function(node, coord) {\n+ coord.x = this.getChildValue(node);\n+ },\n+ \"Y\": function(node, coord) {\n+ coord.y = this.getChildValue(node);\n+ },\n+ \"Z\": function(node, coord) {\n+ coord.z = this.getChildValue(node);\n+ },\n+ \"MultiPoint\": function(node, container) {\n+ var obj = {\n+ components: []\n+ };\n+ this.readers.gml._inherit.apply(this, [node, obj, container]);\n+ this.readChildNodes(node, obj);\n+ container.components = [\n+ new OpenLayers.Geometry.MultiPoint(obj.components)\n+ ];\n+ },\n+ \"pointMember\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"LineString\": function(node, container) {\n+ var obj = {};\n+ this.readers.gml._inherit.apply(this, [node, obj, container]);\n+ this.readChildNodes(node, obj);\n+ if (!container.components) {\n+ container.components = [];\n+ }\n+ container.components.push(\n+ new OpenLayers.Geometry.LineString(obj.points)\n+ );\n+ },\n+ \"MultiLineString\": function(node, container) {\n+ var obj = {\n+ components: []\n+ };\n+ this.readers.gml._inherit.apply(this, [node, obj, container]);\n+ this.readChildNodes(node, obj);\n+ container.components = [\n+ new OpenLayers.Geometry.MultiLineString(obj.components)\n+ ];\n+ },\n+ \"lineStringMember\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Polygon\": function(node, container) {\n+ var obj = {\n+ outer: null,\n+ inner: []\n+ };\n+ this.readers.gml._inherit.apply(this, [node, obj, container]);\n+ this.readChildNodes(node, obj);\n+ obj.inner.unshift(obj.outer);\n+ if (!container.components) {\n+ container.components = [];\n+ }\n+ container.components.push(\n+ new OpenLayers.Geometry.Polygon(obj.inner)\n+ );\n+ },\n+ \"LinearRing\": function(node, obj) {\n+ var container = {};\n+ this.readers.gml._inherit.apply(this, [node, container]);\n+ this.readChildNodes(node, container);\n+ obj.components = [new OpenLayers.Geometry.LinearRing(\n+ container.points\n+ )];\n+ },\n+ \"MultiPolygon\": function(node, container) {\n+ var obj = {\n+ components: []\n+ };\n+ this.readers.gml._inherit.apply(this, [node, obj, container]);\n+ this.readChildNodes(node, obj);\n+ container.components = [\n+ new OpenLayers.Geometry.MultiPolygon(obj.components)\n+ ];\n+ },\n+ \"polygonMember\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"GeometryCollection\": function(node, container) {\n+ var obj = {\n+ components: []\n+ };\n+ this.readers.gml._inherit.apply(this, [node, obj, container]);\n+ this.readChildNodes(node, obj);\n+ container.components = [\n+ new OpenLayers.Geometry.Collection(obj.components)\n+ ];\n+ },\n+ \"geometryMember\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ }\n+ },\n+ \"feature\": {\n+ \"*\": function(node, obj) {\n+ // The node can either be named like the featureType, or it\n+ // can be a child of the feature:featureType. Children can be\n+ // geometry or attributes.\n+ var name;\n+ var local = node.localName || node.nodeName.split(\":\").pop();\n+ // Since an attribute can have the same name as the feature type\n+ // we only want to read the node as a feature if the parent\n+ // node can have feature nodes as children. In this case, the\n+ // obj.features property is set.\n+ if (obj.features) {\n+ if (!this.singleFeatureType &&\n+ (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {\n+ name = \"_typeName\";\n+ } else if (local === this.featureType) {\n+ name = \"_typeName\";\n+ }\n+ } else {\n+ // Assume attribute elements have one child node and that the child\n+ // is a text node. Otherwise assume it is a geometry node.\n+ if (node.childNodes.length == 0 ||\n+ (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {\n+ if (this.extractAttributes) {\n+ name = \"_attribute\";\n+ }\n+ } else {\n+ name = \"_geometry\";\n+ }\n+ }\n+ if (name) {\n+ this.readers.feature[name].apply(this, [node, obj]);\n+ }\n+ },\n+ \"_typeName\": function(node, obj) {\n+ var container = {\n+ components: [],\n+ attributes: {}\n+ };\n+ this.readChildNodes(node, container);\n+ // look for common gml namespaced elements\n+ if (container.name) {\n+ container.attributes.name = container.name;\n+ }\n+ var feature = new OpenLayers.Feature.Vector(\n+ container.components[0], container.attributes\n+ );\n+ if (!this.singleFeatureType) {\n+ feature.type = node.nodeName.split(\":\").pop();\n+ feature.namespace = node.namespaceURI;\n+ }\n+ var fid = node.getAttribute(\"fid\") ||\n+ this.getAttributeNS(node, this.namespaces[\"gml\"], \"id\");\n+ if (fid) {\n+ feature.fid = fid;\n+ }\n+ if (this.internalProjection && this.externalProjection &&\n+ feature.geometry) {\n+ feature.geometry.transform(\n+ this.externalProjection, this.internalProjection\n+ );\n+ }\n+ if (container.bounds) {\n+ feature.bounds = container.bounds;\n+ }\n+ obj.features.push(feature);\n+ },\n+ \"_geometry\": function(node, obj) {\n+ if (!this.geometryName) {\n+ this.geometryName = node.nodeName.split(\":\").pop();\n+ }\n+ this.readChildNodes(node, obj);\n+ },\n+ \"_attribute\": function(node, obj) {\n+ var local = node.localName || node.nodeName.split(\":\").pop();\n+ var value = this.getChildValue(node);\n+ obj.attributes[local] = value;\n+ }\n+ },\n+ \"wfs\": {\n+ \"FeatureCollection\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ }\n }\n- return {\n- speed: speed,\n- theta: theta\n- };\n },\n \n /**\n- * Method: move\n- * Launch the kinetic move pan.\n+ * Method: write\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() | OpenLayers.Feature.Vector}\n+ * An array of features or a single feature.\n+ *\n+ * Returns:\n+ * {String} Given an array of features, a doc with a gml:featureMembers\n+ * element will be returned. Given a single feature, a doc with a\n+ * gml:featureMember element will be returned.\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+ write: function(features) {\n+ var name;\n+ if (OpenLayers.Util.isArray(features)) {\n+ name = \"featureMembers\";\n+ } else {\n+ name = \"featureMember\";\n+ }\n+ var root = this.writeNode(\"gml:\" + name, features);\n+ this.setAttributeNS(\n+ root, this.namespaces[\"xsi\"],\n+ \"xsi:schemaLocation\", this.schemaLocation\n+ );\n \n- var lastX = 0;\n- var lastY = 0;\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [root]);\n+ },\n \n- var timerCallback = function() {\n- if (this.timerId == null) {\n- return;\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"gml\": {\n+ \"featureMember\": function(feature) {\n+ var node = this.createElementNSPlus(\"gml:featureMember\");\n+ this.writeNode(\"feature:_typeName\", feature, node);\n+ return node;\n+ },\n+ \"MultiPoint\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:MultiPoint\");\n+ var components = geometry.components || [geometry];\n+ for (var i = 0, ii = components.length; i < ii; ++i) {\n+ this.writeNode(\"pointMember\", components[i], node);\n+ }\n+ return node;\n+ },\n+ \"pointMember\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:pointMember\");\n+ this.writeNode(\"Point\", geometry, node);\n+ return node;\n+ },\n+ \"MultiLineString\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:MultiLineString\");\n+ var components = geometry.components || [geometry];\n+ for (var i = 0, ii = components.length; i < ii; ++i) {\n+ this.writeNode(\"lineStringMember\", components[i], node);\n+ }\n+ return node;\n+ },\n+ \"lineStringMember\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:lineStringMember\");\n+ this.writeNode(\"LineString\", geometry, node);\n+ return node;\n+ },\n+ \"MultiPolygon\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:MultiPolygon\");\n+ var components = geometry.components || [geometry];\n+ for (var i = 0, ii = components.length; i < ii; ++i) {\n+ this.writeNode(\n+ \"polygonMember\", components[i], node\n+ );\n+ }\n+ return node;\n+ },\n+ \"polygonMember\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:polygonMember\");\n+ this.writeNode(\"Polygon\", geometry, node);\n+ return node;\n+ },\n+ \"GeometryCollection\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:GeometryCollection\");\n+ for (var i = 0, len = geometry.components.length; i < len; ++i) {\n+ this.writeNode(\"geometryMember\", geometry.components[i], node);\n+ }\n+ return node;\n+ },\n+ \"geometryMember\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:geometryMember\");\n+ var child = this.writeNode(\"feature:_geometry\", geometry);\n+ node.appendChild(child.firstChild);\n+ return node;\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+ \"feature\": {\n+ \"_typeName\": function(feature) {\n+ var node = this.createElementNSPlus(\"feature:\" + this.featureType, {\n+ attributes: {\n+ fid: feature.fid\n+ }\n+ });\n+ if (feature.geometry) {\n+ this.writeNode(\"feature:_geometry\", feature.geometry, node);\n+ }\n+ for (var name in feature.attributes) {\n+ var value = feature.attributes[name];\n+ if (value != null) {\n+ this.writeNode(\n+ \"feature:_attribute\", {\n+ name: name,\n+ value: value\n+ }, node\n+ );\n+ }\n+ }\n+ return node;\n+ },\n+ \"_geometry\": function(geometry) {\n+ if (this.externalProjection && this.internalProjection) {\n+ geometry = geometry.clone().transform(\n+ this.internalProjection, this.externalProjection\n+ );\n+ }\n+ var node = this.createElementNSPlus(\n+ \"feature:\" + this.geometryName\n+ );\n+ var type = this.geometryTypes[geometry.CLASS_NAME];\n+ var child = this.writeNode(\"gml:\" + type, geometry, node);\n+ if (this.srsName) {\n+ child.setAttribute(\"srsName\", this.srsName);\n+ }\n+ return node;\n+ },\n+ \"_attribute\": function(obj) {\n+ return this.createElementNSPlus(\"feature:\" + obj.name, {\n+ value: obj.value\n+ });\n+ }\n+ },\n+ \"wfs\": {\n+ \"FeatureCollection\": function(features) {\n+ /**\n+ * This is only here because GML2 only describes abstract\n+ * feature collections. Typically, you would not be using\n+ * the GML format to write wfs elements. This just provides\n+ * some way to write out lists of features. GML3 defines the\n+ * featureMembers element, so that is used by default instead.\n+ */\n+ var node = this.createElementNSPlus(\"wfs:FeatureCollection\");\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ this.writeNode(\"gml:featureMember\", features[i], node);\n+ }\n+ return node;\n }\n+ }\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+ * Method: setGeometryTypes\n+ * Sets the mapping.\n+ */\n+ setGeometryTypes: function() {\n+ this.geometryTypes = {\n+ \"OpenLayers.Geometry.Point\": \"Point\",\n+ \"OpenLayers.Geometry.MultiPoint\": \"MultiPoint\",\n+ \"OpenLayers.Geometry.LineString\": \"LineString\",\n+ \"OpenLayers.Geometry.MultiLineString\": \"MultiLineString\",\n+ \"OpenLayers.Geometry.Polygon\": \"Polygon\",\n+ \"OpenLayers.Geometry.MultiPolygon\": \"MultiPolygon\",\n+ \"OpenLayers.Geometry.Collection\": \"GeometryCollection\"\n };\n-\n- this.timerId = OpenLayers.Animation.start(\n- OpenLayers.Function.bind(timerCallback, this)\n- );\n },\n \n- CLASS_NAME: \"OpenLayers.Kinetic\"\n+ CLASS_NAME: \"OpenLayers.Format.GML.Base\"\n+\n });\n /* ======================================================================\n- OpenLayers/Layer/HTTPRequest.js\n+ OpenLayers/Format/GML/v3.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Format/GML/Base.js\n */\n \n /**\n- * Class: OpenLayers.Layer.HTTPRequest\n- * \n- * Inherits from: \n- * - \n+ * Class: OpenLayers.Format.GML.v3\n+ * Parses GML version 3.\n+ *\n+ * Inherits from:\n+ * - \n */\n-OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n-\n- /** \n- * Constant: URL_HASH_FACTOR\n- * {Float} Used to hash URL param strings for multi-WMS server selection.\n- * Set to the Golden Ratio per Knuth's recommendation.\n- */\n- URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n-\n- /** \n- * Property: url\n- * {Array(String) or String} This is either an array of url strings or \n- * a single url string. \n- */\n- url: null,\n+OpenLayers.Format.GML.v3 = OpenLayers.Class(OpenLayers.Format.GML.Base, {\n \n- /** \n- * Property: params\n- * {Object} Hashtable of key/value parameters\n+ /**\n+ * Property: schemaLocation\n+ * {String} Schema location for a particular minor version. The writers\n+ * conform with the Simple Features Profile for GML.\n */\n- params: null,\n+ schemaLocation: \"http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd\",\n \n- /** \n- * APIProperty: reproject\n- * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html\n- * for information on the replacement for this functionality. \n- * {Boolean} Whether layer should reproject itself based on base layer \n- * locations. This allows reprojection onto commercial layers. \n- * Default is false: Most layers can't reproject, but layers \n- * which can create non-square geographic pixels can, like WMS.\n- * \n+ /**\n+ * Property: curve\n+ * {Boolean} Write gml:Curve instead of gml:LineString elements. This also\n+ * affects the elements in multi-part geometries. Default is false.\n+ * To write gml:Curve elements instead of gml:LineString, set curve\n+ * to true in the options to the contstructor (cannot be changed after\n+ * instantiation).\n */\n- reproject: false,\n+ curve: false,\n \n /**\n- * Constructor: OpenLayers.Layer.HTTPRequest\n- * \n- * Parameters:\n- * name - {String}\n- * url - {Array(String) or String}\n- * params - {Object}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * Property: multiCurve\n+ * {Boolean} Write gml:MultiCurve instead of gml:MultiLineString. Since\n+ * the latter is deprecated in GML 3, the default is true. To write\n+ * gml:MultiLineString instead of gml:MultiCurve, set multiCurve to\n+ * false in the options to the constructor (cannot be changed after\n+ * instantiation).\n */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n- this.url = url;\n- if (!this.params) {\n- this.params = OpenLayers.Util.extend({}, params);\n- }\n- },\n+ multiCurve: true,\n \n /**\n- * APIMethod: destroy\n+ * Property: surface\n+ * {Boolean} Write gml:Surface instead of gml:Polygon elements. This also\n+ * affects the elements in multi-part geometries. Default is false.\n+ * To write gml:Surface elements instead of gml:Polygon, set surface\n+ * to true in the options to the contstructor (cannot be changed after\n+ * instantiation).\n */\n- destroy: function() {\n- this.url = null;\n- this.params = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n- },\n+ surface: false,\n \n /**\n- * APIMethod: clone\n- * \n- * Parameters:\n- * obj - {Object}\n- * \n- * Returns:\n- * {} An exact clone of this \n- * \n+ * Property: multiSurface\n+ * {Boolean} Write gml:multiSurface instead of gml:MultiPolygon. Since\n+ * the latter is deprecated in GML 3, the default is true. To write\n+ * gml:MultiPolygon instead of gml:multiSurface, set multiSurface to\n+ * false in the options to the constructor (cannot be changed after\n+ * instantiation).\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.HTTPRequest(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n- },\n+ multiSurface: true,\n \n- /** \n- * APIMethod: setUrl\n- * \n+ /**\n+ * Constructor: OpenLayers.Format.GML.v3\n+ * Create a parser for GML v3.\n+ *\n * Parameters:\n- * newUrl - {String}\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ *\n+ * Valid options properties:\n+ * featureType - {String} Local (without prefix) feature typeName (required).\n+ * featureNS - {String} Feature namespace (required).\n+ * geometryName - {String} Geometry element name.\n */\n- setUrl: function(newUrl) {\n- this.url = newUrl;\n+ initialize: function(options) {\n+ OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);\n },\n \n /**\n- * APIMethod: mergeNewParams\n- * \n- * Parameters:\n- * newParams - {Object}\n- *\n- * Returns:\n- * redrawn: {Boolean} whether the layer was actually redrawn.\n+ * 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- mergeNewParams: function(newParams) {\n- this.params = OpenLayers.Util.extend(this.params, newParams);\n- var ret = this.redraw();\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"params\"\n- });\n- }\n- return ret;\n+ readers: {\n+ \"gml\": OpenLayers.Util.applyDefaults({\n+ \"_inherit\": function(node, obj, container) {\n+ // SRSReferenceGroup attributes\n+ var dim = parseInt(node.getAttribute(\"srsDimension\"), 10) ||\n+ (container && container.srsDimension);\n+ if (dim) {\n+ obj.srsDimension = dim;\n+ }\n+ },\n+ \"featureMembers\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Curve\": function(node, container) {\n+ var obj = {\n+ points: []\n+ };\n+ this.readers.gml._inherit.apply(this, [node, obj, container]);\n+ this.readChildNodes(node, obj);\n+ if (!container.components) {\n+ container.components = [];\n+ }\n+ container.components.push(\n+ new OpenLayers.Geometry.LineString(obj.points)\n+ );\n+ },\n+ \"segments\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"LineStringSegment\": function(node, container) {\n+ var obj = {};\n+ this.readChildNodes(node, obj);\n+ if (obj.points) {\n+ Array.prototype.push.apply(container.points, obj.points);\n+ }\n+ },\n+ \"pos\": function(node, obj) {\n+ var str = this.getChildValue(node).replace(\n+ this.regExes.trimSpace, \"\"\n+ );\n+ var coords = str.split(this.regExes.splitSpace);\n+ var point;\n+ if (this.xy) {\n+ point = new OpenLayers.Geometry.Point(\n+ coords[0], coords[1], coords[2]\n+ );\n+ } else {\n+ point = new OpenLayers.Geometry.Point(\n+ coords[1], coords[0], coords[2]\n+ );\n+ }\n+ obj.points = [point];\n+ },\n+ \"posList\": function(node, obj) {\n+ var str = this.getChildValue(node).replace(\n+ this.regExes.trimSpace, \"\"\n+ );\n+ var coords = str.split(this.regExes.splitSpace);\n+ // The \"dimension\" attribute is from the GML 3.0.1 spec.\n+ var dim = obj.srsDimension ||\n+ parseInt(node.getAttribute(\"srsDimension\") || node.getAttribute(\"dimension\"), 10) || 2;\n+ var j, x, y, z;\n+ var numPoints = coords.length / dim;\n+ var points = new Array(numPoints);\n+ for (var i = 0, len = coords.length; i < len; i += dim) {\n+ x = coords[i];\n+ y = coords[i + 1];\n+ z = (dim == 2) ? undefined : coords[i + 2];\n+ if (this.xy) {\n+ points[i / dim] = new OpenLayers.Geometry.Point(x, y, z);\n+ } else {\n+ points[i / dim] = new OpenLayers.Geometry.Point(y, x, z);\n+ }\n+ }\n+ obj.points = points;\n+ },\n+ \"Surface\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"patches\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"PolygonPatch\": function(node, obj) {\n+ this.readers.gml.Polygon.apply(this, [node, obj]);\n+ },\n+ \"exterior\": function(node, container) {\n+ var obj = {};\n+ this.readChildNodes(node, obj);\n+ container.outer = obj.components[0];\n+ },\n+ \"interior\": function(node, container) {\n+ var obj = {};\n+ this.readChildNodes(node, obj);\n+ container.inner.push(obj.components[0]);\n+ },\n+ \"MultiCurve\": function(node, container) {\n+ var obj = {\n+ components: []\n+ };\n+ this.readers.gml._inherit.apply(this, [node, obj, container]);\n+ this.readChildNodes(node, obj);\n+ if (obj.components.length > 0) {\n+ container.components = [\n+ new OpenLayers.Geometry.MultiLineString(obj.components)\n+ ];\n+ }\n+ },\n+ \"curveMember\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"MultiSurface\": function(node, container) {\n+ var obj = {\n+ components: []\n+ };\n+ this.readers.gml._inherit.apply(this, [node, obj, container]);\n+ this.readChildNodes(node, obj);\n+ if (obj.components.length > 0) {\n+ container.components = [\n+ new OpenLayers.Geometry.MultiPolygon(obj.components)\n+ ];\n+ }\n+ },\n+ \"surfaceMember\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"surfaceMembers\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"pointMembers\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"lineStringMembers\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"polygonMembers\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"geometryMembers\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Envelope\": function(node, container) {\n+ var obj = {\n+ points: new Array(2)\n+ };\n+ this.readChildNodes(node, obj);\n+ if (!container.components) {\n+ container.components = [];\n+ }\n+ var min = obj.points[0];\n+ var max = obj.points[1];\n+ container.components.push(\n+ new OpenLayers.Bounds(min.x, min.y, max.x, max.y)\n+ );\n+ },\n+ \"lowerCorner\": function(node, container) {\n+ var obj = {};\n+ this.readers.gml.pos.apply(this, [node, obj]);\n+ container.points[0] = obj.points[0];\n+ },\n+ \"upperCorner\": function(node, container) {\n+ var obj = {};\n+ this.readers.gml.pos.apply(this, [node, obj]);\n+ container.points[1] = obj.points[0];\n+ }\n+ }, OpenLayers.Format.GML.Base.prototype.readers[\"gml\"]),\n+ \"feature\": OpenLayers.Format.GML.Base.prototype.readers[\"feature\"],\n+ \"wfs\": OpenLayers.Format.GML.Base.prototype.readers[\"wfs\"]\n },\n \n /**\n- * APIMethod: redraw\n- * Redraws the layer. Returns true if the layer was redrawn, false if not.\n+ * Method: write\n *\n * Parameters:\n- * force - {Boolean} Force redraw by adding random parameter.\n+ * features - {Array() | OpenLayers.Feature.Vector}\n+ * An array of features or a single feature.\n *\n * Returns:\n- * {Boolean} The layer was redrawn.\n+ * {String} Given an array of features, a doc with a gml:featureMembers\n+ * element will be returned. Given a single feature, a doc with a\n+ * gml:featureMember element will be returned.\n */\n- redraw: function(force) {\n- if (force) {\n- return this.mergeNewParams({\n- \"_olSalt\": Math.random()\n- });\n+ write: function(features) {\n+ var name;\n+ if (OpenLayers.Util.isArray(features)) {\n+ name = \"featureMembers\";\n } else {\n- return OpenLayers.Layer.prototype.redraw.apply(this, []);\n+ name = \"featureMember\";\n }\n+ var root = this.writeNode(\"gml:\" + name, features);\n+ this.setAttributeNS(\n+ root, this.namespaces[\"xsi\"],\n+ \"xsi:schemaLocation\", this.schemaLocation\n+ );\n+\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [root]);\n },\n \n /**\n- * Method: selectUrl\n- * selectUrl() implements the standard floating-point multiplicative\n- * hash function described by Knuth, and hashes the contents of the \n- * given param string into a float between 0 and 1. This float is then\n- * scaled to the size of the provided urls array, and used to select\n- * a URL.\n- *\n- * Parameters:\n- * paramString - {String}\n- * urls - {Array(String)}\n- * \n- * Returns:\n- * {String} An entry from the urls array, deterministically selected based\n- * on the paramString.\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n */\n- selectUrl: function(paramString, urls) {\n- var product = 1;\n- for (var i = 0, len = paramString.length; i < len; i++) {\n- product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n- product -= Math.floor(product);\n- }\n- return urls[Math.floor(product * urls.length)];\n+ writers: {\n+ \"gml\": OpenLayers.Util.applyDefaults({\n+ \"featureMembers\": function(features) {\n+ var node = this.createElementNSPlus(\"gml:featureMembers\");\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ this.writeNode(\"feature:_typeName\", features[i], node);\n+ }\n+ return node;\n+ },\n+ \"Point\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:Point\");\n+ this.writeNode(\"pos\", geometry, node);\n+ return node;\n+ },\n+ \"pos\": function(point) {\n+ // only 2d for simple features profile\n+ var pos = (this.xy) ?\n+ (point.x + \" \" + point.y) : (point.y + \" \" + point.x);\n+ return this.createElementNSPlus(\"gml:pos\", {\n+ value: pos\n+ });\n+ },\n+ \"LineString\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:LineString\");\n+ this.writeNode(\"posList\", geometry.components, node);\n+ return node;\n+ },\n+ \"Curve\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:Curve\");\n+ this.writeNode(\"segments\", geometry, node);\n+ return node;\n+ },\n+ \"segments\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:segments\");\n+ this.writeNode(\"LineStringSegment\", geometry, node);\n+ return node;\n+ },\n+ \"LineStringSegment\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:LineStringSegment\");\n+ this.writeNode(\"posList\", geometry.components, node);\n+ return node;\n+ },\n+ \"posList\": function(points) {\n+ // only 2d for simple features profile\n+ var len = points.length;\n+ var parts = new Array(len);\n+ var point;\n+ for (var i = 0; i < len; ++i) {\n+ point = points[i];\n+ if (this.xy) {\n+ parts[i] = point.x + \" \" + point.y;\n+ } else {\n+ parts[i] = point.y + \" \" + point.x;\n+ }\n+ }\n+ return this.createElementNSPlus(\"gml:posList\", {\n+ value: parts.join(\" \")\n+ });\n+ },\n+ \"Surface\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:Surface\");\n+ this.writeNode(\"patches\", geometry, node);\n+ return node;\n+ },\n+ \"patches\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:patches\");\n+ this.writeNode(\"PolygonPatch\", geometry, node);\n+ return node;\n+ },\n+ \"PolygonPatch\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:PolygonPatch\", {\n+ attributes: {\n+ interpolation: \"planar\"\n+ }\n+ });\n+ this.writeNode(\"exterior\", geometry.components[0], node);\n+ for (var i = 1, len = geometry.components.length; i < len; ++i) {\n+ this.writeNode(\n+ \"interior\", geometry.components[i], node\n+ );\n+ }\n+ return node;\n+ },\n+ \"Polygon\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:Polygon\");\n+ this.writeNode(\"exterior\", geometry.components[0], node);\n+ for (var i = 1, len = geometry.components.length; i < len; ++i) {\n+ this.writeNode(\n+ \"interior\", geometry.components[i], node\n+ );\n+ }\n+ return node;\n+ },\n+ \"exterior\": function(ring) {\n+ var node = this.createElementNSPlus(\"gml:exterior\");\n+ this.writeNode(\"LinearRing\", ring, node);\n+ return node;\n+ },\n+ \"interior\": function(ring) {\n+ var node = this.createElementNSPlus(\"gml:interior\");\n+ this.writeNode(\"LinearRing\", ring, node);\n+ return node;\n+ },\n+ \"LinearRing\": function(ring) {\n+ var node = this.createElementNSPlus(\"gml:LinearRing\");\n+ this.writeNode(\"posList\", ring.components, node);\n+ return node;\n+ },\n+ \"MultiCurve\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:MultiCurve\");\n+ var components = geometry.components || [geometry];\n+ for (var i = 0, len = components.length; i < len; ++i) {\n+ this.writeNode(\"curveMember\", components[i], node);\n+ }\n+ return node;\n+ },\n+ \"curveMember\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:curveMember\");\n+ if (this.curve) {\n+ this.writeNode(\"Curve\", geometry, node);\n+ } else {\n+ this.writeNode(\"LineString\", geometry, node);\n+ }\n+ return node;\n+ },\n+ \"MultiSurface\": function(geometry) {\n+ var node = this.createElementNSPlus(\"gml:MultiSurface\");\n+ var components = geometry.components || [geometry];\n+ for (var i = 0, len = components.length; i < len; ++i) {\n+ this.writeNode(\"surfaceMember\", components[i], node);\n+ }\n+ return node;\n+ },\n+ \"surfaceMember\": function(polygon) {\n+ var node = this.createElementNSPlus(\"gml:surfaceMember\");\n+ if (this.surface) {\n+ this.writeNode(\"Surface\", polygon, node);\n+ } else {\n+ this.writeNode(\"Polygon\", polygon, node);\n+ }\n+ return node;\n+ },\n+ \"Envelope\": function(bounds) {\n+ var node = this.createElementNSPlus(\"gml:Envelope\");\n+ this.writeNode(\"lowerCorner\", bounds, node);\n+ this.writeNode(\"upperCorner\", bounds, node);\n+ // srsName attribute is required for gml:Envelope\n+ if (this.srsName) {\n+ node.setAttribute(\"srsName\", this.srsName);\n+ }\n+ return node;\n+ },\n+ \"lowerCorner\": function(bounds) {\n+ // only 2d for simple features profile\n+ var pos = (this.xy) ?\n+ (bounds.left + \" \" + bounds.bottom) :\n+ (bounds.bottom + \" \" + bounds.left);\n+ return this.createElementNSPlus(\"gml:lowerCorner\", {\n+ value: pos\n+ });\n+ },\n+ \"upperCorner\": function(bounds) {\n+ // only 2d for simple features profile\n+ var pos = (this.xy) ?\n+ (bounds.right + \" \" + bounds.top) :\n+ (bounds.top + \" \" + bounds.right);\n+ return this.createElementNSPlus(\"gml:upperCorner\", {\n+ value: pos\n+ });\n+ }\n+ }, OpenLayers.Format.GML.Base.prototype.writers[\"gml\"]),\n+ \"feature\": OpenLayers.Format.GML.Base.prototype.writers[\"feature\"],\n+ \"wfs\": OpenLayers.Format.GML.Base.prototype.writers[\"wfs\"]\n },\n \n- /** \n- * Method: getFullRequestString\n- * Combine url with layer's params and these newParams. \n- * \n- * does checking on the serverPath variable, allowing for cases when it \n- * is supplied with trailing ? or &, as well as cases where not. \n- *\n- * return in formatted string like this:\n- * \"server?key1=value1&key2=value2&key3=value3\"\n- * \n- * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.\n- *\n- * Parameters:\n- * newParams - {Object}\n- * altUrl - {String} Use this as the url instead of the layer's url\n- * \n- * Returns: \n- * {String}\n+ /**\n+ * Method: setGeometryTypes\n+ * Sets the mapping.\n */\n- getFullRequestString: function(newParams, altUrl) {\n-\n- // if not altUrl passed in, use layer's url\n- var url = altUrl || this.url;\n-\n- // create a new params hashtable with all the layer params and the \n- // new params together. then convert to string\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n-\n- // if url is not a string, it should be an array of strings, \n- // in which case we will deterministically select one of them in \n- // order to evenly distribute requests to different urls.\n- //\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(paramsString, url);\n- }\n-\n- // ignore parameters that are already in the url search string\n- var urlParams =\n- OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key];\n- }\n- }\n- paramsString = OpenLayers.Util.getParameterString(allParams);\n-\n- return OpenLayers.Util.urlAppend(url, paramsString);\n+ setGeometryTypes: function() {\n+ this.geometryTypes = {\n+ \"OpenLayers.Geometry.Point\": \"Point\",\n+ \"OpenLayers.Geometry.MultiPoint\": \"MultiPoint\",\n+ \"OpenLayers.Geometry.LineString\": (this.curve === true) ? \"Curve\" : \"LineString\",\n+ \"OpenLayers.Geometry.MultiLineString\": (this.multiCurve === false) ? \"MultiLineString\" : \"MultiCurve\",\n+ \"OpenLayers.Geometry.Polygon\": (this.surface === true) ? \"Surface\" : \"Polygon\",\n+ \"OpenLayers.Geometry.MultiPolygon\": (this.multiSurface === false) ? \"MultiPolygon\" : \"MultiSurface\",\n+ \"OpenLayers.Geometry.Collection\": \"GeometryCollection\"\n+ };\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n+ CLASS_NAME: \"OpenLayers.Format.GML.v3\"\n+\n });\n /* ======================================================================\n- OpenLayers/Tile.js\n+ OpenLayers/Format/Filter/v1_1_0.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Format/Filter/v1.js\n+ * @requires OpenLayers/Format/GML/v3.js\n */\n \n /**\n- * Class: OpenLayers.Tile \n- * This is a class designed to designate a single tile, however\n- * it is explicitly designed to do relatively little. Tiles store \n- * information about themselves -- such as the URL that they are related\n- * to, and their size - but do not add themselves to the layer div \n- * automatically, for example. Create a new tile with the \n- * constructor, or a subclass. \n- * \n- * TBD 3.0 - remove reference to url in above paragraph\n+ * Class: OpenLayers.Format.Filter.v1_1_0\n+ * Write ogc:Filter version 1.1.0.\n+ *\n+ * Differences from the v1.0.0 parser:\n+ * - uses GML v3 instead of GML v2\n+ * - reads matchCase attribute on ogc:PropertyIsEqual and\n+ * ogc:PropertyIsNotEqual elements.\n+ * - writes matchCase attribute from comparison filters of type EQUAL_TO,\n+ * NOT_EQUAL_TO and LIKE.\n * \n+ * Inherits from: \n+ * - \n+ * - \n */\n-OpenLayers.Tile = OpenLayers.Class({\n-\n- /**\n- * APIProperty: events\n- * {} An events object that handles all \n- * events on the tile.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * tile.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types:\n- * beforedraw - Triggered before the tile is drawn. Used to defer\n- * drawing to an animation queue. To defer drawing, listeners need\n- * to return false, which will abort drawing. The queue handler needs\n- * to call (true) to actually draw the tile.\n- * loadstart - Triggered when tile loading starts.\n- * loadend - Triggered when tile loading ends.\n- * loaderror - Triggered before the loadend event (i.e. when the tile is\n- * still hidden) if the tile could not be loaded.\n- * reload - Triggered when an already loading tile is reloaded.\n- * unload - Triggered before a tile is unloaded.\n- */\n- events: null,\n-\n- /**\n- * APIProperty: eventListeners\n- * {Object} If set as an option at construction, the eventListeners\n- * object will be registered with . Object\n- * structure must be a listeners object as shown in the example for\n- * the events.on method.\n- *\n- * This options can be set in the ``tileOptions`` option from\n- * . For example, to be notified of the\n- * ``loadend`` event of each tiles:\n- * (code)\n- * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {\n- * tileOptions: {\n- * eventListeners: {\n- * 'loadend': function(evt) {\n- * // do something on loadend\n- * }\n- * }\n- * }\n- * });\n- * (end)\n- */\n- eventListeners: null,\n-\n- /**\n- * Property: id \n- * {String} null\n- */\n- id: null,\n-\n- /** \n- * Property: layer \n- * {} layer the tile is attached to \n- */\n- layer: null,\n-\n- /**\n- * Property: url\n- * {String} url of the request.\n- *\n- * TBD 3.0 \n- * Deprecated. The base tile class does not need an url. This should be \n- * handled in subclasses. Does not belong here.\n- */\n- url: null,\n-\n- /** \n- * APIProperty: bounds \n- * {} null\n- */\n- bounds: null,\n+OpenLayers.Format.Filter.v1_1_0 = OpenLayers.Class(\n+ OpenLayers.Format.GML.v3, OpenLayers.Format.Filter.v1, {\n \n- /** \n- * Property: size \n- * {} null\n- */\n- size: null,\n+ /**\n+ * Constant: VERSION\n+ * {String} 1.1.0\n+ */\n+ VERSION: \"1.1.0\",\n \n- /** \n- * Property: position \n- * {} Top Left pixel of the tile\n- */\n- position: null,\n+ /**\n+ * Property: schemaLocation\n+ * {String} http://www.opengis.net/ogc/filter/1.1.0/filter.xsd\n+ */\n+ schemaLocation: \"http://www.opengis.net/ogc/filter/1.1.0/filter.xsd\",\n \n- /**\n- * Property: isLoading\n- * {Boolean} Is the tile loading?\n- */\n- isLoading: false,\n+ /**\n+ * Constructor: OpenLayers.Format.Filter.v1_1_0\n+ * Instances of this class are not created directly. Use the\n+ * constructor instead.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.GML.v3.prototype.initialize.apply(\n+ this, [options]\n+ );\n+ },\n \n- /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.\n- * there is no need for the base tile class to have a url.\n- */\n+ /**\n+ * Property: 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+ \"ogc\": OpenLayers.Util.applyDefaults({\n+ \"PropertyIsEqualTo\": function(node, obj) {\n+ var matchCase = node.getAttribute(\"matchCase\");\n+ var filter = new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.EQUAL_TO,\n+ matchCase: !(matchCase === \"false\" || matchCase === \"0\")\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"PropertyIsNotEqualTo\": function(node, obj) {\n+ var matchCase = node.getAttribute(\"matchCase\");\n+ var filter = new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,\n+ matchCase: !(matchCase === \"false\" || matchCase === \"0\")\n+ });\n+ this.readChildNodes(node, filter);\n+ obj.filters.push(filter);\n+ },\n+ \"PropertyIsLike\": function(node, obj) {\n+ var filter = new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.LIKE\n+ });\n+ this.readChildNodes(node, filter);\n+ var wildCard = node.getAttribute(\"wildCard\");\n+ var singleChar = node.getAttribute(\"singleChar\");\n+ var esc = node.getAttribute(\"escapeChar\");\n+ filter.value2regex(wildCard, singleChar, esc);\n+ obj.filters.push(filter);\n+ }\n+ }, OpenLayers.Format.Filter.v1.prototype.readers[\"ogc\"]),\n+ \"gml\": OpenLayers.Format.GML.v3.prototype.readers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v3.prototype.readers[\"feature\"]\n+ },\n \n- /** \n- * Constructor: OpenLayers.Tile\n- * Constructor for a new instance.\n- * \n- * Parameters:\n- * layer - {} layer that the tile will go in.\n- * position - {}\n- * bounds - {}\n- * url - {}\n- * size - {}\n- * options - {Object}\n- */\n- initialize: function(layer, position, bounds, url, size, options) {\n- this.layer = layer;\n- this.position = position.clone();\n- this.setBounds(bounds);\n- this.url = url;\n- if (size) {\n- this.size = size.clone();\n- }\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"ogc\": OpenLayers.Util.applyDefaults({\n+ \"PropertyIsEqualTo\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:PropertyIsEqualTo\", {\n+ attributes: {\n+ matchCase: filter.matchCase\n+ }\n+ });\n+ // no ogc:expression handling for PropertyName for now\n+ this.writeNode(\"PropertyName\", filter, node);\n+ // handle Literals or Functions for now\n+ this.writeOgcExpression(filter.value, node);\n+ return node;\n+ },\n+ \"PropertyIsNotEqualTo\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:PropertyIsNotEqualTo\", {\n+ attributes: {\n+ matchCase: filter.matchCase\n+ }\n+ });\n+ // no ogc:expression handling for PropertyName for now\n+ this.writeNode(\"PropertyName\", filter, node);\n+ // handle Literals or Functions for now\n+ this.writeOgcExpression(filter.value, node);\n+ return node;\n+ },\n+ \"PropertyIsLike\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:PropertyIsLike\", {\n+ attributes: {\n+ matchCase: filter.matchCase,\n+ wildCard: \"*\",\n+ singleChar: \".\",\n+ escapeChar: \"!\"\n+ }\n+ });\n+ // no ogc:expression handling for now\n+ this.writeNode(\"PropertyName\", filter, node);\n+ // convert regex string to ogc string\n+ this.writeNode(\"Literal\", filter.regex2value(), node);\n+ return node;\n+ },\n+ \"BBOX\": function(filter) {\n+ var node = this.createElementNSPlus(\"ogc:BBOX\");\n+ // PropertyName is optional in 1.1.0\n+ filter.property && this.writeNode(\"PropertyName\", filter, node);\n+ var box = this.writeNode(\"gml:Envelope\", filter.value);\n+ if (filter.projection) {\n+ box.setAttribute(\"srsName\", filter.projection);\n+ }\n+ node.appendChild(box);\n+ return node;\n+ },\n+ \"SortBy\": function(sortProperties) {\n+ var node = this.createElementNSPlus(\"ogc:SortBy\");\n+ for (var i = 0, l = sortProperties.length; i < l; i++) {\n+ this.writeNode(\n+ \"ogc:SortProperty\",\n+ sortProperties[i],\n+ node\n+ );\n+ }\n+ return node;\n+ },\n+ \"SortProperty\": function(sortProperty) {\n+ var node = this.createElementNSPlus(\"ogc:SortProperty\");\n+ this.writeNode(\n+ \"ogc:PropertyName\",\n+ sortProperty,\n+ node\n+ );\n+ this.writeNode(\n+ \"ogc:SortOrder\",\n+ (sortProperty.order == 'DESC') ? 'DESC' : 'ASC',\n+ node\n+ );\n+ return node;\n+ },\n+ \"SortOrder\": function(value) {\n+ var node = this.createElementNSPlus(\"ogc:SortOrder\", {\n+ value: value\n+ });\n+ return node;\n+ }\n+ }, OpenLayers.Format.Filter.v1.prototype.writers[\"ogc\"]),\n+ \"gml\": OpenLayers.Format.GML.v3.prototype.writers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v3.prototype.writers[\"feature\"]\n+ },\n \n- //give the tile a unique id based on its BBOX.\n- this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\n+ /**\n+ * Method: writeSpatial\n+ *\n+ * Read a {} filter and converts it into XML.\n+ *\n+ * Parameters:\n+ * filter - {} The filter.\n+ * name - {String} Name of the generated XML element.\n+ *\n+ * Returns:\n+ * {DOMElement} The created XML element.\n+ */\n+ writeSpatial: function(filter, name) {\n+ var node = this.createElementNSPlus(\"ogc:\" + name);\n+ this.writeNode(\"PropertyName\", filter, node);\n+ if (filter.value instanceof OpenLayers.Filter.Function) {\n+ this.writeNode(\"Function\", filter.value, node);\n+ } else {\n+ var child;\n+ if (filter.value instanceof OpenLayers.Geometry) {\n+ child = this.writeNode(\"feature:_geometry\", filter.value).firstChild;\n+ } else {\n+ child = this.writeNode(\"gml:Envelope\", filter.value);\n+ }\n+ if (filter.projection) {\n+ child.setAttribute(\"srsName\", filter.projection);\n+ }\n+ node.appendChild(child);\n+ }\n+ return node;\n+ },\n \n- OpenLayers.Util.extend(this, options);\n+ CLASS_NAME: \"OpenLayers.Format.Filter.v1_1_0\"\n \n- this.events = new OpenLayers.Events(this);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners);\n- }\n- },\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/OWSCommon/v1_0_0.js\n+ ====================================================================== */\n \n- /**\n- * Method: unload\n- * Call immediately before destroying if you are listening to tile\n- * events, so that counters are properly handled if tile is still\n- * loading at destroy-time. Will only fire an event if the tile is\n- * still loading.\n- */\n- unload: function() {\n- if (this.isLoading) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"unload\");\n- }\n- },\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /** \n- * APIMethod: destroy\n- * Nullify references to prevent circular references and memory leaks.\n- */\n- destroy: function() {\n- this.layer = null;\n- this.bounds = null;\n- this.size = null;\n- this.position = null;\n+/**\n+ * @requires OpenLayers/Format/OWSCommon/v1.js\n+ */\n \n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- }\n- this.events.destroy();\n- this.eventListeners = null;\n- this.events = null;\n- },\n+/**\n+ * Class: OpenLayers.Format.OWSCommon.v1_0_0\n+ * Parser for OWS Common version 1.0.0.\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.OWSCommon.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {\n \n /**\n- * Method: draw\n- * Clear whatever is currently in the tile, then return whether or not \n- * it should actually be re-drawn. This is an example implementation\n- * that can be overridden by subclasses. The minimum thing to do here\n- * is to call and return the result from .\n- *\n- * Parameters:\n- * force - {Boolean} If true, the tile will not be cleared and no beforedraw\n- * event will be fired. This is used for drawing tiles asynchronously\n- * after drawing has been cancelled by returning false from a beforedraw\n- * listener.\n- * \n- * Returns:\n- * {Boolean} Whether or not the tile should actually be drawn. Returns null\n- * if a beforedraw listener returned false.\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n */\n- draw: function(force) {\n- if (!force) {\n- //clear tile's contents and mark as not drawn\n- this.clear();\n- }\n- var draw = this.shouldDraw();\n- if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n- draw = null;\n- }\n- return draw;\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n },\n \n /**\n- * Method: shouldDraw\n- * Return whether or not the tile should actually be (re-)drawn. The only\n- * case where we *wouldn't* want to draw the tile is if the tile is outside\n- * its layer's maxExtent\n- * \n- * Returns:\n- * {Boolean} Whether or not the tile should actually be drawn.\n+ * 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- shouldDraw: function() {\n- var withinMaxExtent = false,\n- maxExtent = this.layer.maxExtent;\n- if (maxExtent) {\n- var map = this.layer.map;\n- var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n- if (this.bounds.intersectsBounds(maxExtent, {\n- inclusive: false,\n- worldBounds: worldBounds\n- })) {\n- withinMaxExtent = true;\n+ readers: {\n+ \"ows\": OpenLayers.Util.applyDefaults({\n+ \"ExceptionReport\": function(node, obj) {\n+ obj.success = false;\n+ obj.exceptionReport = {\n+ version: node.getAttribute('version'),\n+ language: node.getAttribute('language'),\n+ exceptions: []\n+ };\n+ this.readChildNodes(node, obj.exceptionReport);\n }\n- }\n-\n- return withinMaxExtent || this.layer.displayOutsideMaxExtent;\n+ }, OpenLayers.Format.OWSCommon.v1.prototype.readers.ows)\n },\n \n /**\n- * Method: setBounds\n- * Sets the bounds on this instance\n- *\n- * Parameters:\n- * bounds {}\n- */\n- setBounds: function(bounds) {\n- bounds = bounds.clone();\n- if (this.layer.map.baseLayer.wrapDateLine) {\n- var worldExtent = this.layer.map.getMaxExtent(),\n- tolerance = this.layer.map.getResolution();\n- bounds = bounds.wrapDateLine(worldExtent, {\n- leftTolerance: tolerance,\n- rightTolerance: tolerance\n- });\n- }\n- this.bounds = bounds;\n- },\n-\n- /** \n- * Method: moveTo\n- * Reposition the tile.\n- *\n- * Parameters:\n- * bounds - {}\n- * position - {}\n- * redraw - {Boolean} Call draw method on tile after moving.\n- * Default is true\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n */\n- moveTo: function(bounds, position, redraw) {\n- if (redraw == null) {\n- redraw = true;\n- }\n-\n- this.setBounds(bounds);\n- this.position = position.clone();\n- if (redraw) {\n- this.draw();\n- }\n+ writers: {\n+ \"ows\": OpenLayers.Format.OWSCommon.v1.prototype.writers.ows\n },\n \n- /** \n- * Method: clear\n- * Clear the tile of any bounds/position-related data so that it can \n- * be reused in a new location.\n- */\n- clear: function(draw) {\n- // to be extended by subclasses\n- },\n+ CLASS_NAME: \"OpenLayers.Format.OWSCommon.v1_0_0\"\n \n- CLASS_NAME: \"OpenLayers.Tile\"\n });\n /* ======================================================================\n- OpenLayers/Tile/Image.js\n+ OpenLayers/Format/WFST/v1_1_0.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Tile.js\n- * @requires OpenLayers/Animation.js\n- * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Format/WFST/v1.js\n+ * @requires OpenLayers/Format/Filter/v1_1_0.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_0_0.js\n */\n \n /**\n- * Class: OpenLayers.Tile.Image\n- * Instances of OpenLayers.Tile.Image are used to manage the image tiles\n- * used by various layers. Create a new image tile with the\n- * constructor.\n+ * Class: OpenLayers.Format.WFST.v1_1_0\n+ * A format for creating WFS v1.1.0 transactions. Create a new instance with the\n+ * constructor.\n *\n * Inherits from:\n- * - \n+ * - \n+ * - \n */\n-OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n+OpenLayers.Format.WFST.v1_1_0 = OpenLayers.Class(\n+ OpenLayers.Format.Filter.v1_1_0, OpenLayers.Format.WFST.v1, {\n \n- /**\n- * APIProperty: events\n- * {} An events object that handles all \n- * events on the tile.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * tile.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to the events):\n- * beforeload - Triggered before an image is prepared for loading, when the\n- * url for the image is known already. Listeners may call on\n- * the tile instance. If they do so, that image will be used and no new\n- * one will be created.\n- */\n+ /**\n+ * Property: version\n+ * {String} WFS version number.\n+ */\n+ version: \"1.1.0\",\n \n- /** \n- * APIProperty: url\n- * {String} The URL of the image being requested. No default. Filled in by\n- * layer.getURL() function. May be modified by loadstart listeners.\n- */\n- url: null,\n+ /**\n+ * Property: schemaLocations\n+ * {Object} Properties are namespace aliases, values are schema locations.\n+ */\n+ schemaLocations: {\n+ \"wfs\": \"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\"\n+ },\n \n- /** \n- * Property: imgDiv\n- * {HTMLImageElement} The image for this tile.\n- */\n- imgDiv: null,\n+ /**\n+ * Constructor: OpenLayers.Format.WFST.v1_1_0\n+ * A class for parsing and generating WFS v1.1.0 transactions.\n+ *\n+ * To read additional information like hit count (numberOfFeatures) from\n+ * the FeatureCollection, call the method\n+ * with {output: \"object\"} as 2nd argument. Note that it is possible to\n+ * just request the hit count from a WFS 1.1.0 server with the\n+ * resultType=\"hits\" request parameter.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties:\n+ * featureType - {String} Local (without prefix) feature typeName (required).\n+ * featureNS - {String} Feature namespace (optional).\n+ * featurePrefix - {String} Feature namespace alias (optional - only used\n+ * if featureNS is provided). Default is 'feature'.\n+ * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.Filter.v1_1_0.prototype.initialize.apply(this, [options]);\n+ OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);\n+ },\n \n- /**\n- * Property: frame\n- * {DOMElement} The image element is appended to the frame. Any gutter on\n- * the image will be hidden behind the frame. If no gutter is set,\n- * this will be null.\n- */\n- frame: null,\n+ /**\n+ * Method: readNode\n+ * Shorthand for applying one of the named readers given the node\n+ * namespace and local name. Readers take two args (node, obj) and\n+ * generally extend or modify the second.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} The node to be read (required).\n+ * obj - {Object} The object to be modified (optional).\n+ * first - {Boolean} Should be set to true for the first node read. This\n+ * is usually the readNode call in the read method. Without this being\n+ * set, auto-configured properties will stick on subsequent reads.\n+ *\n+ * Returns:\n+ * {Object} The input object, modified (or a new one if none was provided).\n+ */\n+ readNode: function(node, obj, first) {\n+ // Not the superclass, only the mixin classes inherit from\n+ // Format.GML.v3. We need this because we don't want to get readNode\n+ // from the superclass's superclass, which is OpenLayers.Format.XML.\n+ return OpenLayers.Format.GML.v3.prototype.readNode.apply(this, arguments);\n+ },\n \n- /** \n- * Property: imageReloadAttempts\n- * {Integer} Attempts to load the image.\n- */\n- imageReloadAttempts: null,\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"FeatureCollection\": function(node, obj) {\n+ obj.numberOfFeatures = parseInt(node.getAttribute(\n+ \"numberOfFeatures\"));\n+ OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"][\"FeatureCollection\"].apply(\n+ this, arguments);\n+ },\n+ \"TransactionResponse\": function(node, obj) {\n+ obj.insertIds = [];\n+ obj.success = false;\n+ this.readChildNodes(node, obj);\n+ },\n+ \"TransactionSummary\": function(node, obj) {\n+ // this is a limited test of success\n+ obj.success = true;\n+ },\n+ \"InsertResults\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Feature\": function(node, container) {\n+ var obj = {\n+ fids: []\n+ };\n+ this.readChildNodes(node, obj);\n+ container.insertIds.push(obj.fids[0]);\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n+ \"gml\": OpenLayers.Format.GML.v3.prototype.readers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v3.prototype.readers[\"feature\"],\n+ \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.readers[\"ogc\"],\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"]\n+ },\n \n- /**\n- * Property: layerAlphaHack\n- * {Boolean} True if the png alpha hack needs to be applied on the layer's div.\n- */\n- layerAlphaHack: null,\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"GetFeature\": function(options) {\n+ var node = OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"][\"GetFeature\"].apply(this, arguments);\n+ options && this.setAttributes(node, {\n+ resultType: options.resultType,\n+ startIndex: options.startIndex,\n+ count: options.count\n+ });\n+ return node;\n+ },\n+ \"Query\": function(options) {\n+ options = OpenLayers.Util.extend({\n+ featureNS: this.featureNS,\n+ featurePrefix: this.featurePrefix,\n+ featureType: this.featureType,\n+ srsName: this.srsName\n+ }, options);\n+ var prefix = options.featurePrefix;\n+ var node = this.createElementNSPlus(\"wfs:Query\", {\n+ attributes: {\n+ typeName: (prefix ? prefix + \":\" : \"\") +\n+ options.featureType,\n+ srsName: options.srsName\n+ }\n+ });\n+ if (options.featureNS) {\n+ node.setAttribute(\"xmlns:\" + prefix, options.featureNS);\n+ }\n+ if (options.propertyNames) {\n+ for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n+ this.writeNode(\n+ \"wfs:PropertyName\", {\n+ property: options.propertyNames[i]\n+ },\n+ node\n+ );\n+ }\n+ }\n+ if (options.filter) {\n+ OpenLayers.Format.WFST.v1_1_0.prototype.setFilterProperty.call(this, options.filter);\n+ this.writeNode(\"ogc:Filter\", options.filter, node);\n+ }\n+ return node;\n+ },\n+ \"PropertyName\": function(obj) {\n+ return this.createElementNSPlus(\"wfs:PropertyName\", {\n+ value: obj.property\n+ });\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n+ \"gml\": OpenLayers.Format.GML.v3.prototype.writers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v3.prototype.writers[\"feature\"],\n+ \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers[\"ogc\"]\n+ },\n \n- /**\n- * Property: asyncRequestId\n- * {Integer} ID of an request to see if request is still valid. This is a\n- * number which increments by 1 for each asynchronous request.\n- */\n- asyncRequestId: null,\n+ CLASS_NAME: \"OpenLayers.Format.WFST.v1_1_0\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/WPSExecute.js\n+ ====================================================================== */\n \n- /**\n- * APIProperty: maxGetUrlLength\n- * {Number} If set, requests that would result in GET urls with more\n- * characters than the number provided will be made using form-encoded\n- * HTTP POST. It is good practice to avoid urls that are longer than 2048\n- * characters.\n- *\n- * Caution:\n- * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most\n- * Opera versions do not fully support this option. On all browsers,\n- * transition effects are not supported if POST requests are used.\n- */\n- maxGetUrlLength: null,\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * Property: canvasContext\n- * {CanvasRenderingContext2D} A canvas context associated with\n- * the tile image.\n- */\n- canvasContext: null,\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ * @requires OpenLayers/Format/WCSGetCoverage.js\n+ * @requires OpenLayers/Format/WFST/v1_1_0.js\n+ */\n \n- /**\n- * APIProperty: crossOriginKeyword\n- * The value of the crossorigin keyword to use when loading images. This is\n- * only relevant when using for tiles from remote\n- * origins and should be set to either 'anonymous' or 'use-credentials'\n- * for servers that send Access-Control-Allow-Origin headers with their\n- * tiles.\n- */\n- crossOriginKeyword: null,\n+/**\n+ * Class: OpenLayers.Format.WPSExecute version 1.0.0\n+ *\n+ * Inherits from:\n+ * - \n+ */\n+OpenLayers.Format.WPSExecute = OpenLayers.Class(OpenLayers.Format.XML,\n+ OpenLayers.Format.Filter.v1_1_0, {\n \n- /** TBD 3.0 - reorder the parameters to the init function to remove \n- * URL. the getUrl() function on the layer gets called on \n- * each draw(), so no need to specify it here.\n- */\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ gml: \"http://www.opengis.net/gml\",\n+ wps: \"http://www.opengis.net/wps/1.0.0\",\n+ wfs: \"http://www.opengis.net/wfs\",\n+ ogc: \"http://www.opengis.net/ogc\",\n+ wcs: \"http://www.opengis.net/wcs\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n \n- /** \n- * Constructor: OpenLayers.Tile.Image\n- * Constructor for a new instance.\n- * \n- * Parameters:\n- * layer - {} layer that the tile will go in.\n- * position - {}\n- * bounds - {}\n- * url - {} Deprecated. Remove me in 3.0.\n- * size - {}\n- * options - {Object}\n- */\n- initialize: function(layer, position, bounds, url, size, options) {\n- OpenLayers.Tile.prototype.initialize.apply(this, arguments);\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- this.url = url; //deprecated remove me\n+ /**\n+ * Constant: VERSION\n+ * {String} 1.0.0\n+ */\n+ VERSION: \"1.0.0\",\n \n- this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\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- if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n- // only create frame if it's needed\n- this.frame = document.createElement(\"div\");\n- this.frame.style.position = \"absolute\";\n- this.frame.style.overflow = \"hidden\";\n- }\n- if (this.maxGetUrlLength != null) {\n- OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);\n- }\n- },\n+ schemaLocationAttr: function(options) {\n+ return undefined;\n+ },\n \n- /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n- if (this.imgDiv) {\n- this.clear();\n- this.imgDiv = null;\n- this.frame = null;\n- }\n- // don't handle async requests any more\n- this.asyncRequestId = null;\n- OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n- },\n+ /**\n+ * Constructor: OpenLayers.Format.WPSExecute\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n \n- /**\n- * Method: draw\n- * Check that a tile should be drawn, and draw it.\n- * \n- * Returns:\n- * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned\n- * false.\n- */\n- draw: function() {\n- var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n- if (shouldDraw) {\n- // The layer's reproject option is deprecated.\n- if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n- // getBoundsFromBaseLayer is defined in deprecated.js.\n- this.bounds = this.getBoundsFromBaseLayer(this.position);\n- }\n- if (this.isLoading) {\n- //if we're already loading, send 'reload' instead of 'loadstart'.\n- this._loadEvent = \"reload\";\n+ /**\n+ * Method: write\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object.\n+ *\n+ * Returns:\n+ * {String} An WPS Execute request XML string.\n+ */\n+ write: function(options) {\n+ var doc;\n+ if (window.ActiveXObject) {\n+ doc = new ActiveXObject(\"Microsoft.XMLDOM\");\n+ this.xmldom = doc;\n } else {\n- this.isLoading = true;\n- this._loadEvent = \"loadstart\";\n+ doc = document.implementation.createDocument(\"\", \"\", null);\n }\n- this.renderTile();\n- this.positionTile();\n- } else if (shouldDraw === false) {\n- this.unload();\n- }\n- return shouldDraw;\n- },\n-\n- /**\n- * Method: renderTile\n- * Internal function to actually initialize the image tile,\n- * position it correctly, and set its url.\n- */\n- renderTile: function() {\n- if (this.layer.async) {\n- // Asynchronous image requests call the asynchronous getURL method\n- // on the layer to fetch an image that covers 'this.bounds'.\n- var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n- this.layer.getURLasync(this.bounds, function(url) {\n- if (id == this.asyncRequestId) {\n- this.url = url;\n- this.initImage();\n- }\n- }, this);\n- } else {\n- // synchronous image requests get the url immediately.\n- this.url = this.layer.getURL(this.bounds);\n- this.initImage();\n- }\n- },\n-\n- /**\n- * Method: positionTile\n- * Using the properties currenty set on the layer, position the tile correctly.\n- * This method is used both by the async and non-async versions of the Tile.Image\n- * code.\n- */\n- positionTile: function() {\n- var style = this.getTile().style,\n- size = this.frame ? this.size :\n- this.layer.getImageSize(this.bounds),\n- ratio = 1;\n- if (this.layer instanceof OpenLayers.Layer.Grid) {\n- ratio = this.layer.getServerResolution() / this.layer.map.getResolution();\n- }\n- style.left = this.position.x + \"px\";\n- style.top = this.position.y + \"px\";\n- style.width = Math.round(ratio * size.w) + \"px\";\n- style.height = Math.round(ratio * size.h) + \"px\";\n- },\n+ var node = this.writeNode(\"wps:Execute\", options, doc);\n+ this.setAttributeNS(\n+ node, this.namespaces.xsi,\n+ \"xsi:schemaLocation\", this.schemaLocation\n+ );\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n+ },\n \n- /** \n- * Method: clear\n- * Remove the tile from the DOM, clear it of any image related data so that\n- * it can be reused in a new location.\n- */\n- clear: function() {\n- OpenLayers.Tile.prototype.clear.apply(this, arguments);\n- var img = this.imgDiv;\n- if (img) {\n- var tile = this.getTile();\n- if (tile.parentNode === this.layer.div) {\n- this.layer.div.removeChild(tile);\n+ /**\n+ * APIMethod: read\n+ * Parse a WPS Execute and return an object with its information.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object}\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n }\n- this.setImgSrc();\n- if (this.layerAlphaHack === true) {\n- img.style.filter = \"\";\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n }\n- OpenLayers.Element.removeClass(img, \"olImageLoadError\");\n- }\n- this.canvasContext = null;\n- },\n-\n- /**\n- * Method: getImage\n- * Returns or creates and returns the tile image.\n- */\n- getImage: function() {\n- if (!this.imgDiv) {\n- this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n+ var info = {};\n+ this.readNode(data, info);\n+ return info;\n+ },\n \n- var style = this.imgDiv.style;\n- if (this.frame) {\n- var left = 0,\n- top = 0;\n- if (this.layer.gutter) {\n- left = this.layer.gutter / this.layer.tileSize.w * 100;\n- top = this.layer.gutter / this.layer.tileSize.h * 100;\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"wps\": {\n+ \"Execute\": function(options) {\n+ var node = this.createElementNSPlus(\"wps:Execute\", {\n+ attributes: {\n+ version: this.VERSION,\n+ service: 'WPS'\n+ }\n+ });\n+ this.writeNode(\"ows:Identifier\", options.identifier, node);\n+ this.writeNode(\"wps:DataInputs\", options.dataInputs, node);\n+ this.writeNode(\"wps:ResponseForm\", options.responseForm, node);\n+ return node;\n+ },\n+ \"ResponseForm\": function(responseForm) {\n+ var node = this.createElementNSPlus(\"wps:ResponseForm\", {});\n+ if (responseForm.rawDataOutput) {\n+ this.writeNode(\"wps:RawDataOutput\", responseForm.rawDataOutput, node);\n+ }\n+ if (responseForm.responseDocument) {\n+ this.writeNode(\"wps:ResponseDocument\", responseForm.responseDocument, node);\n+ }\n+ return node;\n+ },\n+ \"ResponseDocument\": function(responseDocument) {\n+ var node = this.createElementNSPlus(\"wps:ResponseDocument\", {\n+ attributes: {\n+ storeExecuteResponse: responseDocument.storeExecuteResponse,\n+ lineage: responseDocument.lineage,\n+ status: responseDocument.status\n+ }\n+ });\n+ if (responseDocument.outputs) {\n+ for (var i = 0, len = responseDocument.outputs.length; i < len; i++) {\n+ this.writeNode(\"wps:Output\", responseDocument.outputs[i], node);\n+ }\n+ }\n+ return node;\n+ },\n+ \"Output\": function(output) {\n+ var node = this.createElementNSPlus(\"wps:Output\", {\n+ attributes: {\n+ asReference: output.asReference,\n+ mimeType: output.mimeType,\n+ encoding: output.encoding,\n+ schema: output.schema\n+ }\n+ });\n+ this.writeNode(\"ows:Identifier\", output.identifier, node);\n+ this.writeNode(\"ows:Title\", output.title, node);\n+ this.writeNode(\"ows:Abstract\", output[\"abstract\"], node);\n+ return node;\n+ },\n+ \"RawDataOutput\": function(rawDataOutput) {\n+ var node = this.createElementNSPlus(\"wps:RawDataOutput\", {\n+ attributes: {\n+ mimeType: rawDataOutput.mimeType,\n+ encoding: rawDataOutput.encoding,\n+ schema: rawDataOutput.schema\n+ }\n+ });\n+ this.writeNode(\"ows:Identifier\", rawDataOutput.identifier, node);\n+ return node;\n+ },\n+ \"DataInputs\": function(dataInputs) {\n+ var node = this.createElementNSPlus(\"wps:DataInputs\", {});\n+ for (var i = 0, ii = dataInputs.length; i < ii; ++i) {\n+ this.writeNode(\"wps:Input\", dataInputs[i], node);\n+ }\n+ return node;\n+ },\n+ \"Input\": function(input) {\n+ var node = this.createElementNSPlus(\"wps:Input\", {});\n+ this.writeNode(\"ows:Identifier\", input.identifier, node);\n+ if (input.title) {\n+ this.writeNode(\"ows:Title\", input.title, node);\n+ }\n+ if (input.data) {\n+ this.writeNode(\"wps:Data\", input.data, node);\n+ }\n+ if (input.reference) {\n+ this.writeNode(\"wps:Reference\", input.reference, node);\n+ }\n+ if (input.boundingBoxData) {\n+ this.writeNode(\"wps:BoundingBoxData\", input.boundingBoxData, node);\n+ }\n+ return node;\n+ },\n+ \"Data\": function(data) {\n+ var node = this.createElementNSPlus(\"wps:Data\", {});\n+ if (data.literalData) {\n+ this.writeNode(\"wps:LiteralData\", data.literalData, node);\n+ } else if (data.complexData) {\n+ this.writeNode(\"wps:ComplexData\", data.complexData, node);\n+ } else if (data.boundingBoxData) {\n+ this.writeNode(\"ows:BoundingBox\", data.boundingBoxData, node);\n+ }\n+ return node;\n+ },\n+ \"LiteralData\": function(literalData) {\n+ var node = this.createElementNSPlus(\"wps:LiteralData\", {\n+ attributes: {\n+ uom: literalData.uom\n+ },\n+ value: literalData.value\n+ });\n+ return node;\n+ },\n+ \"ComplexData\": function(complexData) {\n+ var node = this.createElementNSPlus(\"wps:ComplexData\", {\n+ attributes: {\n+ mimeType: complexData.mimeType,\n+ encoding: complexData.encoding,\n+ schema: complexData.schema\n+ }\n+ });\n+ var data = complexData.value;\n+ if (typeof data === \"string\") {\n+ node.appendChild(\n+ this.getXMLDoc().createCDATASection(complexData.value)\n+ );\n+ } else {\n+ node.appendChild(data);\n+ }\n+ return node;\n+ },\n+ \"Reference\": function(reference) {\n+ var node = this.createElementNSPlus(\"wps:Reference\", {\n+ attributes: {\n+ mimeType: reference.mimeType,\n+ \"xlink:href\": reference.href,\n+ method: reference.method,\n+ encoding: reference.encoding,\n+ schema: reference.schema\n+ }\n+ });\n+ if (reference.body) {\n+ this.writeNode(\"wps:Body\", reference.body, node);\n+ }\n+ return node;\n+ },\n+ \"BoundingBoxData\": function(node, obj) {\n+ this.writers['ows']['BoundingBox'].apply(this, [node, obj, \"wps:BoundingBoxData\"]);\n+ },\n+ \"Body\": function(body) {\n+ var node = this.createElementNSPlus(\"wps:Body\", {});\n+ if (body.wcs) {\n+ this.writeNode(\"wcs:GetCoverage\", body.wcs, node);\n+ } else if (body.wfs) {\n+ // OpenLayers.Format.WFST expects these to be on the \n+ // instance and not in the options\n+ this.featureType = body.wfs.featureType;\n+ this.version = body.wfs.version;\n+ this.writeNode(\"wfs:GetFeature\", body.wfs, node);\n+ } else {\n+ this.writeNode(\"wps:Execute\", body, node);\n+ }\n+ return node;\n }\n- style.left = -left + \"%\";\n- style.top = -top + \"%\";\n- style.width = (2 * left + 100) + \"%\";\n- style.height = (2 * top + 100) + \"%\";\n- }\n- style.visibility = \"hidden\";\n- style.opacity = 0;\n- if (this.layer.opacity < 1) {\n- style.filter = 'alpha(opacity=' +\n- (this.layer.opacity * 100) +\n- ')';\n- }\n- style.position = \"absolute\";\n- if (this.layerAlphaHack) {\n- // move the image out of sight\n- style.paddingTop = style.height;\n- style.height = \"0\";\n- style.width = \"100%\";\n- }\n- if (this.frame) {\n- this.frame.appendChild(this.imgDiv);\n- }\n- }\n-\n- return this.imgDiv;\n- },\n+ },\n+ \"wcs\": OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs,\n+ \"wfs\": OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs,\n+ \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc,\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows\n+ },\n \n- /**\n- * APIMethod: setImage\n- * Sets the image element for this tile. This method should only be called\n- * from beforeload listeners.\n- *\n- * Parameters\n- * img - {HTMLImageElement} The image to use for this tile.\n- */\n- setImage: function(img) {\n- this.imgDiv = img;\n- },\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wps\": {\n+ \"ExecuteResponse\": function(node, obj) {\n+ obj.executeResponse = {\n+ lang: node.getAttribute(\"lang\"),\n+ statusLocation: node.getAttribute(\"statusLocation\"),\n+ serviceInstance: node.getAttribute(\"serviceInstance\"),\n+ service: node.getAttribute(\"service\")\n+ };\n+ this.readChildNodes(node, obj.executeResponse);\n+ },\n+ \"Process\": function(node, obj) {\n+ obj.process = {};\n+ this.readChildNodes(node, obj.process);\n+ },\n+ \"Status\": function(node, obj) {\n+ obj.status = {\n+ creationTime: node.getAttribute(\"creationTime\")\n+ };\n+ this.readChildNodes(node, obj.status);\n+ },\n+ \"ProcessSucceeded\": function(node, obj) {\n+ obj.processSucceeded = true;\n+ },\n+ \"ProcessOutputs\": function(node, processDescription) {\n+ processDescription.processOutputs = [];\n+ this.readChildNodes(node, processDescription.processOutputs);\n+ },\n+ \"Output\": function(node, processOutputs) {\n+ var output = {};\n+ this.readChildNodes(node, output);\n+ processOutputs.push(output);\n+ },\n+ \"Reference\": function(node, output) {\n+ output.reference = {\n+ href: node.getAttribute(\"href\"),\n+ mimeType: node.getAttribute(\"mimeType\"),\n+ encoding: node.getAttribute(\"encoding\"),\n+ schema: node.getAttribute(\"schema\")\n+ };\n+ },\n+ \"Data\": function(node, output) {\n+ output.data = {};\n+ this.readChildNodes(node, output);\n+ },\n+ \"LiteralData\": function(node, output) {\n+ output.literalData = {\n+ dataType: node.getAttribute(\"dataType\"),\n+ uom: node.getAttribute(\"uom\"),\n+ value: this.getChildValue(node)\n+ };\n+ },\n+ \"ComplexData\": function(node, output) {\n+ output.complexData = {\n+ mimeType: node.getAttribute(\"mimeType\"),\n+ schema: node.getAttribute(\"schema\"),\n+ encoding: node.getAttribute(\"encoding\"),\n+ value: \"\"\n+ };\n \n- /**\n- * Method: initImage\n- * Creates the content for the frame on the tile.\n- */\n- initImage: function() {\n- if (!this.url && !this.imgDiv) {\n- // fast path out - if there is no tile url and no previous image\n- this.isLoading = false;\n- return;\n- }\n- this.events.triggerEvent('beforeload');\n- this.layer.div.appendChild(this.getTile());\n- this.events.triggerEvent(this._loadEvent);\n- var img = this.getImage();\n- var src = img.getAttribute('src') || '';\n- if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n- this._loadTimeout = window.setTimeout(\n- OpenLayers.Function.bind(this.onImageLoad, this), 0\n- );\n- } else {\n- this.stopLoading();\n- if (this.crossOriginKeyword) {\n- img.removeAttribute(\"crossorigin\");\n- }\n- OpenLayers.Event.observe(img, \"load\",\n- OpenLayers.Function.bind(this.onImageLoad, this)\n- );\n- OpenLayers.Event.observe(img, \"error\",\n- OpenLayers.Function.bind(this.onImageError, this)\n- );\n- this.imageReloadAttempts = 0;\n- this.setImgSrc(this.url);\n- }\n- },\n+ // try to get *some* value, ignore the empty text values\n+ if (this.isSimpleContent(node)) {\n+ var child;\n+ for (child = node.firstChild; child; child = child.nextSibling) {\n+ switch (child.nodeType) {\n+ case 3: // text node\n+ case 4: // cdata section\n+ output.complexData.value += child.nodeValue;\n+ }\n+ }\n+ } else {\n+ for (child = node.firstChild; child; child = child.nextSibling) {\n+ if (child.nodeType == 1) {\n+ output.complexData.value = child;\n+ }\n+ }\n+ }\n \n- /**\n- * Method: setImgSrc\n- * Sets the source for the tile image\n- *\n- * Parameters:\n- * url - {String} or undefined to hide the image\n- */\n- setImgSrc: function(url) {\n- var img = this.imgDiv;\n- if (url) {\n- img.style.visibility = 'hidden';\n- img.style.opacity = 0;\n- // don't set crossOrigin if the url is a data URL\n- if (this.crossOriginKeyword) {\n- if (url.substr(0, 5) !== 'data:') {\n- img.setAttribute(\"crossorigin\", this.crossOriginKeyword);\n- } else {\n- img.removeAttribute(\"crossorigin\");\n+ },\n+ \"BoundingBox\": function(node, output) {\n+ output.boundingBoxData = {\n+ dimensions: node.getAttribute(\"dimensions\"),\n+ crs: node.getAttribute(\"crs\")\n+ };\n+ this.readChildNodes(node, output.boundingBoxData);\n }\n- }\n- img.src = url;\n- } else {\n- // Remove reference to the image, and leave it to the browser's\n- // caching and garbage collection.\n- this.stopLoading();\n- this.imgDiv = null;\n- if (img.parentNode) {\n- img.parentNode.removeChild(img);\n- }\n- }\n- },\n-\n- /**\n- * Method: getTile\n- * Get the tile's markup.\n- *\n- * Returns:\n- * {DOMElement} The tile's markup\n- */\n- getTile: function() {\n- return this.frame ? this.frame : this.getImage();\n- },\n-\n- /**\n- * Method: createBackBuffer\n- * Create a backbuffer for this tile. A backbuffer isn't exactly a clone\n- * of the tile's markup, because we want to avoid the reloading of the\n- * image. So we clone the frame, and steal the image from the tile.\n- *\n- * Returns:\n- * {DOMElement} The markup, or undefined if the tile has no image\n- * or if it's currently loading.\n- */\n- createBackBuffer: function() {\n- if (!this.imgDiv || this.isLoading) {\n- return;\n- }\n- var backBuffer;\n- if (this.frame) {\n- backBuffer = this.frame.cloneNode(false);\n- backBuffer.appendChild(this.imgDiv);\n- } else {\n- backBuffer = this.imgDiv;\n- }\n- this.imgDiv = null;\n- return backBuffer;\n- },\n-\n- /**\n- * Method: onImageLoad\n- * Handler for the image onload event\n- */\n- onImageLoad: function() {\n- var img = this.imgDiv;\n- this.stopLoading();\n- img.style.visibility = 'inherit';\n- img.style.opacity = this.layer.opacity;\n- this.isLoading = false;\n- this.canvasContext = null;\n- this.events.triggerEvent(\"loadend\");\n-\n- if (this.layerAlphaHack === true) {\n- img.style.filter =\n- \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" +\n- img.src + \"', sizingMethod='scale')\";\n- }\n- },\n-\n- /**\n- * Method: onImageError\n- * Handler for the image onerror event\n- */\n- onImageError: function() {\n- var img = this.imgDiv;\n- if (img.src != null) {\n- this.imageReloadAttempts++;\n- if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n- this.setImgSrc(this.layer.getURL(this.bounds));\n- } else {\n- OpenLayers.Element.addClass(img, \"olImageLoadError\");\n- this.events.triggerEvent(\"loaderror\");\n- this.onImageLoad();\n- }\n- }\n- },\n-\n- /**\n- * Method: stopLoading\n- * Stops a loading sequence so won't be executed.\n- */\n- stopLoading: function() {\n- OpenLayers.Event.stopObservingElement(this.imgDiv);\n- window.clearTimeout(this._loadTimeout);\n- delete this._loadTimeout;\n- },\n-\n- /**\n- * APIMethod: getCanvasContext\n- * Returns a canvas context associated with the tile image (with\n- * the image drawn on it).\n- * Returns undefined if the browser does not support canvas, if\n- * the tile has no image or if it's currently loading.\n- *\n- * The function returns a canvas context instance but the\n- * underlying canvas is still available in the 'canvas' property:\n- * (code)\n- * var context = tile.getCanvasContext();\n- * if (context) {\n- * var data = context.canvas.toDataURL('image/jpeg');\n- * }\n- * (end)\n- *\n- * Returns:\n- * {Boolean}\n- */\n- getCanvasContext: function() {\n- if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n- if (!this.canvasContext) {\n- var canvas = document.createElement(\"canvas\");\n- canvas.width = this.size.w;\n- canvas.height = this.size.h;\n- this.canvasContext = canvas.getContext(\"2d\");\n- this.canvasContext.drawImage(this.imgDiv, 0, 0);\n- }\n- return this.canvasContext;\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Tile.Image\"\n+ },\n \n-});\n+ // TODO: we should add Exception parsing here\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ },\n \n-/** \n- * Constant: OpenLayers.Tile.Image.IMAGE\n- * {HTMLImageElement} The image for a tile.\n- */\n-OpenLayers.Tile.Image.IMAGE = (function() {\n- var img = new Image();\n- img.className = \"olTileImage\";\n- // avoid image gallery menu in IE6\n- img.galleryImg = \"no\";\n- return img;\n-}());\n+ CLASS_NAME: \"OpenLayers.Format.WPSExecute\"\n \n+ });\n /* ======================================================================\n- OpenLayers/Layer/Grid.js\n+ OpenLayers/WPSProcess.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+/**\n+ * @requires OpenLayers/SingleFile.js\n+ */\n \n /**\n- * @requires OpenLayers/Layer/HTTPRequest.js\n- * @requires OpenLayers/Tile/Image.js\n+ * @requires OpenLayers/Geometry.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Format/WKT.js\n+ * @requires OpenLayers/Format/GeoJSON.js\n+ * @requires OpenLayers/Format/WPSExecute.js\n+ * @requires OpenLayers/Request.js\n */\n \n /**\n- * Class: OpenLayers.Layer.Grid\n- * Base class for layers that use a lattice of tiles. Create a new grid\n- * layer with the constructor.\n+ * Class: OpenLayers.WPSProcess\n+ * Representation of a WPS process. Usually instances of\n+ * are created by calling 'getProcess' on an\n+ * instance.\n *\n- * Inherits from:\n- * - \n+ * Currently supports processes that have geometries\n+ * or features as output, using WKT or GeoJSON as output format. It also\n+ * supports chaining of processes by using the method to create a\n+ * handle that is used as process input instead of a static value.\n */\n-OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n-\n- /**\n- * APIProperty: tileSize\n- * {}\n- */\n- tileSize: null,\n-\n- /**\n- * Property: tileOriginCorner\n- * {String} If the property is not provided, the tile origin \n- * will be derived from the layer's . The corner of the \n- * used is determined by this property. Acceptable values\n- * are \"tl\" (top left), \"tr\" (top right), \"bl\" (bottom left), and \"br\"\n- * (bottom right). Default is \"bl\".\n- */\n- tileOriginCorner: \"bl\",\n-\n- /**\n- * APIProperty: tileOrigin\n- * {} Optional origin for aligning the grid of tiles.\n- * If provided, requests for tiles at all resolutions will be aligned\n- * with this location (no tiles shall overlap this location). If\n- * not provided, the grid of tiles will be aligned with the layer's\n- * . Default is ``null``.\n- */\n- tileOrigin: null,\n-\n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for instances\n- * created by this Layer, if supported by the tile class.\n- */\n- tileOptions: null,\n-\n- /**\n- * APIProperty: tileClass\n- * {} The tile class to use for this layer.\n- * Defaults is OpenLayers.Tile.Image.\n- */\n- tileClass: OpenLayers.Tile.Image,\n-\n- /**\n- * Property: grid\n- * {Array(Array())} This is an array of rows, each row is \n- * an array of tiles.\n- */\n- grid: null,\n-\n- /**\n- * APIProperty: singleTile\n- * {Boolean} Moves the layer into single-tile mode, meaning that one tile \n- * will be loaded. The tile's size will be determined by the 'ratio'\n- * property. When the tile is dragged such that it does not cover the \n- * entire viewport, it is reloaded.\n- */\n- singleTile: false,\n-\n- /** APIProperty: ratio\n- * {Float} Used only when in single-tile mode, this specifies the \n- * ratio of the size of the single tile to the size of the map.\n- * Default value is 1.5.\n- */\n- ratio: 1.5,\n-\n- /**\n- * APIProperty: buffer\n- * {Integer} Used only when in gridded mode, this specifies the number of \n- * extra rows and colums of tiles on each side which will\n- * surround the minimum grid tiles to cover the map.\n- * For very slow loading layers, a larger value may increase\n- * performance somewhat when dragging, but will increase bandwidth\n- * use significantly. \n- */\n- buffer: 0,\n-\n- /**\n- * APIProperty: transitionEffect\n- * {String} The transition effect to use when the map is zoomed.\n- * Two posible values:\n- *\n- * \"resize\" - Existing tiles are resized on zoom to provide a visual\n- * effect of the zoom having taken place immediately. As the\n- * new tiles become available, they are drawn on top of the\n- * resized tiles (this is the default setting).\n- * \"map-resize\" - Existing tiles are resized on zoom and placed below the\n- * base layer. New tiles for the base layer will cover existing tiles.\n- * This setting is recommended when having an overlay duplicated during\n- * the transition is undesirable (e.g. street labels or big transparent\n- * fills). \n- * null - No transition effect.\n- *\n- * Using \"resize\" on non-opaque layers can cause undesired visual\n- * effects. Set transitionEffect to null in this case.\n- */\n- transitionEffect: \"resize\",\n-\n- /**\n- * APIProperty: numLoadingTiles\n- * {Integer} How many tiles are still loading?\n- */\n- numLoadingTiles: 0,\n-\n- /**\n- * Property: serverResolutions\n- * {Array(Number}} This property is documented in subclasses as\n- * an API property.\n- */\n- serverResolutions: null,\n+OpenLayers.WPSProcess = OpenLayers.Class({\n \n /**\n- * Property: loading\n- * {Boolean} Indicates if tiles are being loaded.\n+ * Property: client\n+ * {} The client that manages this process.\n */\n- loading: false,\n+ client: null,\n \n /**\n- * Property: backBuffer\n- * {DOMElement} The back buffer.\n+ * Property: server\n+ * {String} Local client identifier for this process's server.\n */\n- backBuffer: null,\n+ server: null,\n \n /**\n- * Property: gridResolution\n- * {Number} The resolution of the current grid. Used for backbuffer and\n- * client zoom. This property is updated every time the grid is\n- * initialized.\n+ * Property: identifier\n+ * {String} Process identifier known to the server.\n */\n- gridResolution: null,\n+ identifier: null,\n \n /**\n- * Property: backBufferResolution\n- * {Number} The resolution of the current back buffer. This property is\n- * updated each time a back buffer is created.\n+ * Property: description\n+ * {Object} DescribeProcess response for this process.\n */\n- backBufferResolution: null,\n+ description: null,\n \n /**\n- * Property: backBufferLonLat\n- * {Object} The top-left corner of the current back buffer. Includes lon\n- * and lat properties. This object is updated each time a back buffer\n- * is created.\n+ * APIProperty: localWPS\n+ * {String} Service endpoint for locally chained WPS processes. Default is\n+ * 'http://geoserver/wps'.\n */\n- backBufferLonLat: null,\n+ localWPS: 'http://geoserver/wps',\n \n /**\n- * Property: backBufferTimerId\n- * {Number} The id of the back buffer timer. This timer is used to\n- * delay the removal of the back buffer, thereby preventing\n- * flash effects caused by tile animation.\n+ * Property: formats\n+ * {Object} OpenLayers.Format instances keyed by mimetype.\n */\n- backBufferTimerId: null,\n+ formats: null,\n \n /**\n- * APIProperty: removeBackBufferDelay\n- * {Number} Delay for removing the backbuffer when all tiles have finished\n- * loading. Can be set to 0 when no css opacity transitions for the\n- * olTileImage class are used. Default is 0 for layers,\n- * 2500 for tiled layers. See for more information on\n- * tile animation.\n+ * Property: chained\n+ * {Integer} Number of chained processes for pending execute requests that\n+ * don't have a full configuration yet.\n */\n- removeBackBufferDelay: null,\n+ chained: 0,\n \n /**\n- * APIProperty: className\n- * {String} Name of the class added to the layer div. If not set in the\n- * options passed to the constructor then className defaults to\n- * \"olLayerGridSingleTile\" for single tile layers (see ),\n- * and \"olLayerGrid\" for non single tile layers.\n- *\n- * Note:\n- *\n- * The displaying of tiles is not animated by default for single tile\n- * layers - OpenLayers' default theme (style.css) includes this:\n- * (code)\n- * .olLayerGrid .olTileImage {\n- * -webkit-transition: opacity 0.2s linear;\n- * -moz-transition: opacity 0.2s linear;\n- * -o-transition: opacity 0.2s linear;\n- * transition: opacity 0.2s linear;\n- * }\n- * (end)\n- * To animate tile displaying for any grid layer the following\n- * CSS rule can be used:\n- * (code)\n- * .olTileImage {\n- * -webkit-transition: opacity 0.2s linear;\n- * -moz-transition: opacity 0.2s linear;\n- * -o-transition: opacity 0.2s linear;\n- * transition: opacity 0.2s linear;\n- * }\n- * (end)\n- * In that case, to avoid flash effects, \n- * should not be zero.\n+ * Property: executeCallbacks\n+ * {Array} Callbacks waiting to be executed until all chained processes\n+ * are configured;\n */\n- className: null,\n+ executeCallbacks: null,\n \n /**\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * layer.events.register(type, obj, listener);\n- * (end)\n- *\n- * Listeners will be called with a reference to an event object. The\n- * properties of this event depends on exactly what happened.\n+ * Constructor: OpenLayers.WPSProcess\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+ * Parameters:\n+ * options - {Object} Object whose properties will be set on the instance.\n *\n- * Supported event types:\n- * addtile - Triggered when a tile is added to this layer. Listeners receive\n- * an object as first argument, which has a tile property that\n- * references the tile that has been added.\n- * tileloadstart - Triggered when a tile starts loading. Listeners receive\n- * an object as first argument, which has a tile property that\n- * references the tile that starts loading.\n- * tileloaded - Triggered when each new tile is\n- * loaded, as a means of progress update to listeners.\n- * listeners can access 'numLoadingTiles' if they wish to keep\n- * track of the loading progress. Listeners are called with an object\n- * with a 'tile' property as first argument, making the loaded tile\n- * available to the listener, and an 'aborted' property, which will be\n- * true when loading was aborted and no tile data is available.\n- * tileerror - Triggered before the tileloaded event (i.e. when the tile is\n- * still hidden) if a tile failed to load. Listeners receive an object\n- * as first argument, which has a tile property that references the\n- * tile that could not be loaded.\n- * retile - Triggered when the layer recreates its tile grid.\n- */\n-\n- /**\n- * Property: gridLayout\n- * {Object} Object containing properties tilelon, tilelat, startcol,\n- * startrow\n- */\n- gridLayout: null,\n-\n- /**\n- * Property: rowSign\n- * {Number} 1 for grids starting at the top, -1 for grids starting at the\n- * bottom. This is used for several grid index and offset calculations.\n- */\n- rowSign: null,\n-\n- /**\n- * Property: transitionendEvents\n- * {Array} Event names for transitionend\n+ * Avaliable options:\n+ * client - {} Mandatory. Client that manages this\n+ * process.\n+ * server - {String} Mandatory. Local client identifier of this process's\n+ * server.\n+ * identifier - {String} Mandatory. Process identifier known to the server.\n */\n- transitionendEvents: [\n- 'transitionend', 'webkitTransitionEnd', 'otransitionend',\n- 'oTransitionEnd'\n- ],\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.executeCallbacks = [];\n+ this.formats = {\n+ 'application/wkt': new OpenLayers.Format.WKT(),\n+ 'application/json': new OpenLayers.Format.GeoJSON()\n+ };\n+ },\n \n /**\n- * Constructor: OpenLayers.Layer.Grid\n- * Create a new grid layer\n+ * Method: describe\n+ * Makes the client issue a DescribeProcess request asynchronously.\n *\n * Parameters:\n- * name - {String}\n- * url - {String}\n- * params - {Object}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n- */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,\n- arguments);\n- this.grid = [];\n- this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n-\n- this.initProperties();\n-\n- this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1;\n- },\n-\n- /**\n- * Method: initProperties\n- * Set any properties that depend on the value of singleTile.\n- * Currently sets removeBackBufferDelay and className\n+ * options - {Object} Configuration for the method call\n+ *\n+ * Available options:\n+ * callback - {Function} Callback to execute when the description is\n+ * available. Will be called with the parsed description as argument.\n+ * Optional.\n+ * scope - {Object} The scope in which the callback will be executed.\n+ * Default is the global object.\n */\n- initProperties: function() {\n- if (this.options.removeBackBufferDelay === undefined) {\n- this.removeBackBufferDelay = this.singleTile ? 0 : 2500;\n- }\n-\n- if (this.options.className === undefined) {\n- this.className = this.singleTile ? 'olLayerGridSingleTile' :\n- 'olLayerGrid';\n+ describe: function(options) {\n+ options = options || {};\n+ if (!this.description) {\n+ this.client.describeProcess(this.server, this.identifier, function(description) {\n+ if (!this.description) {\n+ this.parseDescription(description);\n+ }\n+ if (options.callback) {\n+ options.callback.call(options.scope, this.description);\n+ }\n+ }, this);\n+ } else if (options.callback) {\n+ var description = this.description;\n+ window.setTimeout(function() {\n+ options.callback.call(options.scope, description);\n+ }, 0);\n }\n },\n \n /**\n- * Method: setMap\n+ * APIMethod: configure\n+ * Configure the process, but do not execute it. Use this for processes\n+ * that are chained as input of a different process by means of the\n+ * method.\n *\n * Parameters:\n- * map - {} The map.\n- */\n- setMap: function(map) {\n- OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n- OpenLayers.Element.addClass(this.div, this.className);\n- },\n-\n- /**\n- * Method: removeMap\n- * Called when the layer is removed from the map.\n+ * options - {Object}\n *\n- * Parameters:\n- * map - {} The map.\n- */\n- removeMap: function(map) {\n- this.removeBackBuffer();\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Deconstruct the layer and clear the grid.\n+ * Returns:\n+ * {} this process.\n+ *\n+ * Available options:\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+ * callback - {Function} Callback to call when the configuration is\n+ * complete. Optional.\n+ * scope - {Object} Optional scope for the callback.\n */\n- destroy: function() {\n- this.removeBackBuffer();\n- this.clearGrid();\n-\n- this.grid = null;\n- this.tileSize = null;\n- OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);\n+ configure: function(options) {\n+ this.describe({\n+ callback: function() {\n+ var description = this.description,\n+ inputs = options.inputs,\n+ input, i, ii;\n+ for (i = 0, ii = description.dataInputs.length; i < ii; ++i) {\n+ input = description.dataInputs[i];\n+ this.setInputData(input, inputs[input.identifier]);\n+ }\n+ if (options.callback) {\n+ options.callback.call(options.scope);\n+ }\n+ },\n+ scope: this\n+ });\n+ return this;\n },\n \n /**\n- * APIMethod: mergeNewParams\n- * Refetches tiles with new params merged, keeping a backbuffer. Each\n- * loading new tile will have a css class of '.olTileReplacing'. If a\n- * stylesheet applies a 'display: none' style to that class, any fade-in\n- * transition will not apply, and backbuffers for each tile will be removed\n- * as soon as the tile is loaded.\n- * \n+ * APIMethod: execute\n+ * Configures and executes the process\n+ *\n * Parameters:\n- * newParams - {Object}\n+ * options - {Object}\n *\n- * Returns:\n- * redrawn: {Boolean} whether the layer was actually redrawn.\n- */\n-\n- /**\n- * Method: clearGrid\n- * Go through and remove all tiles from the grid, calling\n- * destroy() on each of them to kill circular references\n+ * Available options:\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 the output to request and parse.\n+ * Optional. If not provided, the first output will be requested.\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+ * (or 'result' if output was not configured). For processes that\n+ * generate spatial output, the value will be an array of\n+ * instances.\n+ * scope - {Object} Optional scope for the success callback.\n */\n- clearGrid: function() {\n- if (this.grid) {\n- for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n- var row = this.grid[iRow];\n- for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n- var tile = row[iCol];\n- this.destroyTile(tile);\n- }\n- }\n- this.grid = [];\n- this.gridResolution = null;\n- this.gridLayout = null;\n- }\n+ execute: function(options) {\n+ this.configure({\n+ inputs: options.inputs,\n+ callback: function() {\n+ var me = this;\n+ //TODO For now we only deal with a single output\n+ var outputIndex = this.getOutputIndex(\n+ me.description.processOutputs, options.output\n+ );\n+ me.setResponseForm({\n+ outputIndex: outputIndex\n+ });\n+ (function callback() {\n+ OpenLayers.Util.removeItem(me.executeCallbacks, callback);\n+ if (me.chained !== 0) {\n+ // need to wait until chained processes have a\n+ // description and configuration - see chainProcess\n+ me.executeCallbacks.push(callback);\n+ return;\n+ }\n+ // all chained processes are added as references now, so\n+ // let's proceed.\n+ OpenLayers.Request.POST({\n+ url: me.client.servers[me.server].url,\n+ data: new OpenLayers.Format.WPSExecute().write(me.description),\n+ success: function(response) {\n+ var output = me.description.processOutputs[outputIndex];\n+ var mimeType = me.findMimeType(\n+ output.complexOutput.supported.formats\n+ );\n+ //TODO For now we assume a spatial output\n+ var features = me.formats[mimeType].read(response.responseText);\n+ if (features instanceof OpenLayers.Feature.Vector) {\n+ features = [features];\n+ }\n+ if (options.success) {\n+ var outputs = {};\n+ outputs[options.output || 'result'] = features;\n+ options.success.call(options.scope, outputs);\n+ }\n+ },\n+ scope: me\n+ });\n+ })();\n+ },\n+ scope: this\n+ });\n },\n \n /**\n- * APIMethod: addOptions\n- * \n+ * APIMethod: output\n+ * Chain an output of a configured process (see ) as input to\n+ * another process.\n+ *\n+ * (code)\n+ * intersect = client.getProcess('opengeo', 'JTS:intersection'); \n+ * intersect.configure({\n+ * // ...\n+ * });\n+ * buffer = client.getProcess('opengeo', 'JTS:buffer');\n+ * buffer.execute({\n+ * inputs: {\n+ * geom: intersect.output('result'), // <-- here we're chaining\n+ * distance: 1\n+ * },\n+ * // ...\n+ * });\n+ * (end)\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+ * identifier - {String} Identifier of the output that we're chaining. If\n+ * not provided, the first output will be used.\n */\n- addOptions: function(newOptions, reinitialize) {\n- var singleTileChanged = newOptions.singleTile !== undefined &&\n- newOptions.singleTile !== this.singleTile;\n- OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n- if (this.map && singleTileChanged) {\n- this.initProperties();\n- this.clearGrid();\n- this.tileSize = this.options.tileSize;\n- this.setTileSize();\n- this.moveTo(null, true);\n- }\n+ output: function(identifier) {\n+ return new OpenLayers.WPSProcess.ChainLink({\n+ process: this,\n+ output: identifier\n+ });\n },\n \n /**\n- * APIMethod: clone\n- * Create a clone of this layer\n+ * Method: parseDescription\n+ * Parses the DescribeProcess response\n *\n * Parameters:\n- * obj - {Object} Is this ever used?\n- * \n- * Returns:\n- * {} An exact clone of this OpenLayers.Layer.Grid\n+ * description - {Object}\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Grid(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n- if (this.tileSize != null) {\n- obj.tileSize = this.tileSize.clone();\n- }\n-\n- // we do not want to copy reference to grid, so we make a new array\n- obj.grid = [];\n- obj.gridResolution = null;\n- // same for backbuffer\n- obj.backBuffer = null;\n- obj.backBufferTimerId = null;\n- obj.loading = false;\n- obj.numLoadingTiles = 0;\n-\n- return obj;\n+ parseDescription: function(description) {\n+ var server = this.client.servers[this.server];\n+ this.description = new OpenLayers.Format.WPSDescribeProcess()\n+ .read(server.processDescription[this.identifier])\n+ .processDescriptions[this.identifier];\n },\n \n /**\n- * Method: moveTo\n- * This function is called whenever the map is moved. All the moving\n- * of actual 'tiles' is done by the map, but moveTo's role is to accept\n- * a bounds and make sure the data that that bounds requires is pre-loaded.\n+ * Method: setInputData\n+ * Sets the data for a single input\n *\n * Parameters:\n- * bounds - {}\n- * zoomChanged - {Boolean}\n- * dragging - {Boolean}\n+ * input - {Object} An entry from the dataInputs array of the process\n+ * description.\n+ * data - {Mixed} For spatial data inputs, this is usually an\n+ * , an or an array of\n+ * geometries or features.\n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n-\n- OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n-\n- bounds = bounds || this.map.getExtent();\n-\n- if (bounds != null) {\n-\n- // if grid is empty or zoom has changed, we *must* re-tile\n- var forceReTile = !this.grid.length || zoomChanged;\n-\n- // total bounds of the tiles\n- var tilesBounds = this.getTilesBounds();\n-\n- // the new map resolution\n- var resolution = this.map.getResolution();\n-\n- // the server-supported resolution for the new map resolution\n- var serverResolution = this.getServerResolution(resolution);\n-\n- if (this.singleTile) {\n-\n- // We want to redraw whenever even the slightest part of the \n- // current bounds is not contained by our tile.\n- // (thus, we do not specify partial -- its default is false)\n-\n- if (forceReTile ||\n- (!dragging && !tilesBounds.containsBounds(bounds))) {\n-\n- // In single tile mode with no transition effect, we insert\n- // a non-scaled backbuffer when the layer is moved. But if\n- // a zoom occurs right after a move, i.e. before the new\n- // image is received, we need to remove the backbuffer, or\n- // an ill-positioned image will be visible during the zoom\n- // transition.\n-\n- if (zoomChanged && this.transitionEffect !== 'resize') {\n- this.removeBackBuffer();\n- }\n-\n- if (!zoomChanged || this.transitionEffect === 'resize') {\n- this.applyBackBuffer(resolution);\n- }\n-\n- this.initSingleTile(bounds);\n- }\n+ setInputData: function(input, data) {\n+ // clear any previous data\n+ delete input.data;\n+ delete input.reference;\n+ if (data instanceof OpenLayers.WPSProcess.ChainLink) {\n+ ++this.chained;\n+ input.reference = {\n+ method: 'POST',\n+ href: data.process.server === this.server ?\n+ this.localWPS : this.client.servers[data.process.server].url\n+ };\n+ data.process.describe({\n+ callback: function() {\n+ --this.chained;\n+ this.chainProcess(input, data);\n+ },\n+ scope: this\n+ });\n+ } else {\n+ input.data = {};\n+ var complexData = input.complexData;\n+ if (complexData) {\n+ var format = this.findMimeType(complexData.supported.formats);\n+ input.data.complexData = {\n+ mimeType: format,\n+ value: this.formats[format].write(this.toFeatures(data))\n+ };\n } else {\n-\n- // if the bounds have changed such that they are not even \n- // *partially* contained by our tiles (e.g. when user has \n- // programmatically panned to the other side of the earth on\n- // zoom level 18), then moveGriddedTiles could potentially have\n- // to run through thousands of cycles, so we want to reTile\n- // instead (thus, partial true). \n- forceReTile = forceReTile ||\n- !tilesBounds.intersectsBounds(bounds, {\n- worldBounds: this.map.baseLayer.wrapDateLine &&\n- this.map.getMaxExtent()\n- });\n-\n- if (forceReTile) {\n- if (zoomChanged && (this.transitionEffect === 'resize' ||\n- this.gridResolution === resolution)) {\n- this.applyBackBuffer(resolution);\n- }\n- this.initGriddedTiles(bounds);\n- } else {\n- this.moveGriddedTiles();\n- }\n+ input.data.literalData = {\n+ value: data\n+ };\n }\n }\n },\n \n /**\n- * Method: getTileData\n- * Given a map location, retrieve a tile and the pixel offset within that\n- * tile corresponding to the location. If there is not an existing \n- * tile in the grid that covers the given location, null will be \n- * returned.\n+ * Method: setResponseForm\n+ * Sets the responseForm property of the payload.\n *\n * Parameters:\n- * loc - {} map location\n+ * options - {Object} See below.\n *\n- * Returns:\n- * {Object} Object with the following properties: tile ({}),\n- * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel\n- * offset from top left).\n+ * Available options:\n+ * outputIndex - {Integer} The index of the output to use. Optional.\n+ * supportedFormats - {Object} Object with supported mime types as key,\n+ * and true as value for supported types. Optional.\n */\n- getTileData: function(loc) {\n- var data = null,\n- x = loc.lon,\n- y = loc.lat,\n- numRows = this.grid.length;\n-\n- if (this.map && numRows) {\n- var res = this.map.getResolution(),\n- tileWidth = this.tileSize.w,\n- tileHeight = this.tileSize.h,\n- bounds = this.grid[0][0].bounds,\n- left = bounds.left,\n- top = bounds.top;\n-\n- if (x < left) {\n- // deal with multiple worlds\n- if (this.map.baseLayer.wrapDateLine) {\n- var worldWidth = this.map.getMaxExtent().getWidth();\n- var worldsAway = Math.ceil((left - x) / worldWidth);\n- x += worldWidth * worldsAway;\n- }\n- }\n- // tile distance to location (fractional number of tiles);\n- var dtx = (x - left) / (res * tileWidth);\n- var dty = (top - y) / (res * tileHeight);\n- // index of tile in grid\n- var col = Math.floor(dtx);\n- var row = Math.floor(dty);\n- if (row >= 0 && row < numRows) {\n- var tile = this.grid[row][col];\n- if (tile) {\n- data = {\n- tile: tile,\n- // pixel index within tile\n- i: Math.floor((dtx - col) * tileWidth),\n- j: Math.floor((dty - row) * tileHeight)\n- };\n- }\n+ setResponseForm: function(options) {\n+ options = options || {};\n+ var output = this.description.processOutputs[options.outputIndex || 0];\n+ this.description.responseForm = {\n+ rawDataOutput: {\n+ identifier: output.identifier,\n+ mimeType: this.findMimeType(output.complexOutput.supported.formats, options.supportedFormats)\n }\n- }\n- return data;\n- },\n-\n- /**\n- * Method: destroyTile\n- *\n- * Parameters:\n- * tile - {}\n- */\n- destroyTile: function(tile) {\n- this.removeTileMonitoringHooks(tile);\n- tile.destroy();\n+ };\n },\n \n /**\n- * Method: getServerResolution\n- * Return the closest server-supported resolution.\n+ * Method: getOutputIndex\n+ * Gets the index of a processOutput by its identifier\n *\n * Parameters:\n- * resolution - {Number} The base resolution. If undefined the\n- * map resolution is used.\n+ * outputs - {Array} The processOutputs array to look at\n+ * identifier - {String} The identifier of the output\n *\n- * Returns:\n- * {Number} The closest server resolution value.\n+ * Returns\n+ * {Integer} The index of the processOutput with the provided identifier\n+ * in the outputs array.\n */\n- getServerResolution: function(resolution) {\n- var distance = Number.POSITIVE_INFINITY;\n- resolution = resolution || this.map.getResolution();\n- if (this.serverResolutions &&\n- OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n- var i, newDistance, newResolution, serverResolution;\n- for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n- newResolution = this.serverResolutions[i];\n- newDistance = Math.abs(newResolution - resolution);\n- if (newDistance > distance) {\n+ getOutputIndex: function(outputs, identifier) {\n+ var output;\n+ if (identifier) {\n+ for (var i = outputs.length - 1; i >= 0; --i) {\n+ if (outputs[i].identifier === identifier) {\n+ output = i;\n break;\n }\n- distance = newDistance;\n- serverResolution = newResolution;\n }\n- resolution = serverResolution;\n+ } else {\n+ output = 0;\n }\n- return resolution;\n- },\n-\n- /**\n- * Method: getServerZoom\n- * Return the zoom value corresponding to the best matching server\n- * resolution, taking into account and .\n- *\n- * Returns:\n- * {Number} The closest server supported zoom. This is not the map zoom\n- * level, but an index of the server's resolutions array.\n- */\n- getServerZoom: function() {\n- var resolution = this.getServerResolution();\n- return this.serverResolutions ?\n- OpenLayers.Util.indexOf(this.serverResolutions, resolution) :\n- this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);\n+ return output;\n },\n \n /**\n- * Method: applyBackBuffer\n- * Create, insert, scale and position a back buffer for the layer.\n+ * Method: chainProcess\n+ * Sets a fully configured chained process as input for this process.\n *\n * Parameters:\n- * resolution - {Number} The resolution to transition to.\n+ * input - {Object} The dataInput that the chained process provides.\n+ * chainLink - {} The process to chain.\n */\n- applyBackBuffer: function(resolution) {\n- if (this.backBufferTimerId !== null) {\n- this.removeBackBuffer();\n- }\n- var backBuffer = this.backBuffer;\n- if (!backBuffer) {\n- backBuffer = this.createBackBuffer();\n- if (!backBuffer) {\n- return;\n- }\n- if (resolution === this.gridResolution) {\n- this.div.insertBefore(backBuffer, this.div.firstChild);\n- } else {\n- this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);\n- }\n- this.backBuffer = backBuffer;\n-\n- // set some information in the instance for subsequent\n- // calls to applyBackBuffer where the same back buffer\n- // is reused\n- var topLeftTileBounds = this.grid[0][0].bounds;\n- this.backBufferLonLat = {\n- lon: topLeftTileBounds.left,\n- lat: topLeftTileBounds.top\n- };\n- this.backBufferResolution = this.gridResolution;\n- }\n-\n- var ratio = this.backBufferResolution / resolution;\n-\n- // scale the tiles inside the back buffer\n- var tiles = backBuffer.childNodes,\n- tile;\n- for (var i = tiles.length - 1; i >= 0; --i) {\n- tile = tiles[i];\n- tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';\n- tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';\n- tile.style.width = Math.round(ratio * tile._w) + 'px';\n- tile.style.height = Math.round(ratio * tile._h) + 'px';\n+ chainProcess: function(input, chainLink) {\n+ var output = this.getOutputIndex(\n+ chainLink.process.description.processOutputs, chainLink.output\n+ );\n+ input.reference.mimeType = this.findMimeType(\n+ input.complexData.supported.formats,\n+ chainLink.process.description.processOutputs[output].complexOutput.supported.formats\n+ );\n+ var formats = {};\n+ formats[input.reference.mimeType] = true;\n+ chainLink.process.setResponseForm({\n+ outputIndex: output,\n+ supportedFormats: formats\n+ });\n+ input.reference.body = chainLink.process.description;\n+ while (this.executeCallbacks.length > 0) {\n+ this.executeCallbacks[0]();\n }\n-\n- // and position it (based on the grid's top-left corner)\n- var position = this.getViewPortPxFromLonLat(\n- this.backBufferLonLat, resolution);\n- var leftOffset = this.map.layerContainerOriginPx.x;\n- var topOffset = this.map.layerContainerOriginPx.y;\n- backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';\n- backBuffer.style.top = Math.round(position.y - topOffset) + 'px';\n },\n \n /**\n- * Method: createBackBuffer\n- * Create a back buffer.\n+ * Method: toFeatures\n+ * Converts spatial input into features so it can be processed by\n+ * instances.\n+ *\n+ * Parameters:\n+ * source - {Mixed} An , an\n+ * , or an array of geometries or features\n *\n * Returns:\n- * {DOMElement} The DOM element for the back buffer, undefined if the\n- * grid isn't initialized yet.\n- */\n- createBackBuffer: function() {\n- var backBuffer;\n- if (this.grid.length > 0) {\n- backBuffer = document.createElement('div');\n- backBuffer.id = this.div.id + '_bb';\n- backBuffer.className = 'olBackBuffer';\n- backBuffer.style.position = 'absolute';\n- var map = this.map;\n- backBuffer.style.zIndex = this.transitionEffect === 'resize' ?\n- this.getZIndex() - 1 :\n- // 'map-resize':\n- map.Z_INDEX_BASE.BaseLayer -\n- (map.getNumLayers() - map.getLayerIndex(this));\n- for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n- for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n- var tile = this.grid[i][j],\n- markup = this.grid[i][j].createBackBuffer();\n- if (markup) {\n- markup._i = i;\n- markup._j = j;\n- markup._w = tile.size.w;\n- markup._h = tile.size.h;\n- markup.id = tile.id + '_bb';\n- backBuffer.appendChild(markup);\n- }\n- }\n- }\n- }\n- return backBuffer;\n- },\n-\n- /**\n- * Method: removeBackBuffer\n- * Remove back buffer from DOM.\n+ * {Array()}\n */\n- removeBackBuffer: function() {\n- if (this._transitionElement) {\n- for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.stopObserving(this._transitionElement,\n- this.transitionendEvents[i], this._removeBackBuffer);\n- }\n- delete this._transitionElement;\n+ toFeatures: function(source) {\n+ var isArray = OpenLayers.Util.isArray(source);\n+ if (!isArray) {\n+ source = [source];\n }\n- if (this.backBuffer) {\n- if (this.backBuffer.parentNode) {\n- this.backBuffer.parentNode.removeChild(this.backBuffer);\n- }\n- this.backBuffer = null;\n- this.backBufferResolution = null;\n- if (this.backBufferTimerId !== null) {\n- window.clearTimeout(this.backBufferTimerId);\n- this.backBufferTimerId = null;\n- }\n+ var target = new Array(source.length),\n+ current;\n+ for (var i = 0, ii = source.length; i < ii; ++i) {\n+ current = source[i];\n+ target[i] = current instanceof OpenLayers.Feature.Vector ?\n+ current : new OpenLayers.Feature.Vector(current);\n }\n+ return isArray ? target : target[0];\n },\n \n /**\n- * Method: moveByPx\n- * Move the layer based on pixel vector.\n+ * Method: findMimeType\n+ * Finds a supported mime type.\n *\n * Parameters:\n- * dx - {Number}\n- * dy - {Number}\n- */\n- moveByPx: function(dx, dy) {\n- if (!this.singleTile) {\n- this.moveGriddedTiles();\n- }\n- },\n-\n- /**\n- * APIMethod: setTileSize\n- * Check if we are in singleTile mode and if so, set the size as a ratio\n- * of the map size (as specified by the layer's 'ratio' property).\n- * \n- * Parameters:\n- * size - {}\n- */\n- setTileSize: function(size) {\n- if (this.singleTile) {\n- size = this.map.getSize();\n- size.h = parseInt(size.h * this.ratio, 10);\n- size.w = parseInt(size.w * this.ratio, 10);\n- }\n- OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);\n- },\n-\n- /**\n- * APIMethod: getTilesBounds\n- * Return the bounds of the tile grid.\n+ * sourceFormats - {Object} An object literal with mime types as key and\n+ * true as value for supported formats.\n+ * targetFormats - {Object} Like , but optional to check for\n+ * supported mime types on a different target than this process.\n+ * Default is to check against this process's supported formats.\n *\n * Returns:\n- * {} A Bounds object representing the bounds of all the\n- * currently loaded tiles (including those partially or not at all seen \n- * onscreen).\n+ * {String} A supported mime type.\n */\n- getTilesBounds: function() {\n- var bounds = null;\n-\n- var length = this.grid.length;\n- if (length) {\n- var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n- width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n- height = this.grid.length * bottomLeftTileBounds.getHeight();\n-\n- bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,\n- bottomLeftTileBounds.bottom,\n- bottomLeftTileBounds.left + width,\n- bottomLeftTileBounds.bottom + height);\n+ findMimeType: function(sourceFormats, targetFormats) {\n+ targetFormats = targetFormats || this.formats;\n+ for (var f in sourceFormats) {\n+ if (f in targetFormats) {\n+ return f;\n+ }\n }\n- return bounds;\n },\n \n- /**\n- * Method: initSingleTile\n- * \n- * Parameters: \n- * bounds - {}\n- */\n- initSingleTile: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n-\n- //determine new tile bounds\n- var center = bounds.getCenterLonLat();\n- var tileWidth = bounds.getWidth() * this.ratio;\n- var tileHeight = bounds.getHeight() * this.ratio;\n-\n- var tileBounds =\n- new OpenLayers.Bounds(center.lon - (tileWidth / 2),\n- center.lat - (tileHeight / 2),\n- center.lon + (tileWidth / 2),\n- center.lat + (tileHeight / 2));\n-\n- var px = this.map.getLayerPxFromLonLat({\n- lon: tileBounds.left,\n- lat: tileBounds.top\n- });\n-\n- if (!this.grid.length) {\n- this.grid[0] = [];\n- }\n-\n- var tile = this.grid[0][0];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n-\n- this.addTileMonitoringHooks(tile);\n- tile.draw();\n- this.grid[0][0] = tile;\n- } else {\n- tile.moveTo(tileBounds, px);\n- }\n+ CLASS_NAME: \"OpenLayers.WPSProcess\"\n \n- //remove all but our single tile\n- this.removeExcessTiles(1, 1);\n+});\n \n- // store the resolution of the grid\n- this.gridResolution = this.getServerResolution();\n- },\n+/**\n+ * Class: OpenLayers.WPSProcess.ChainLink\n+ * Type for chaining processes.\n+ */\n+OpenLayers.WPSProcess.ChainLink = OpenLayers.Class({\n \n- /** \n- * Method: calculateGridLayout\n- * Generate parameters for the grid layout.\n- *\n- * Parameters:\n- * bounds - {|Object} OpenLayers.Bounds or an\n- * object with a 'left' and 'top' properties.\n- * origin - {|Object} OpenLayers.LonLat or an\n- * object with a 'lon' and 'lat' properties.\n- * resolution - {Number}\n- *\n- * Returns:\n- * {Object} Object containing properties tilelon, tilelat, startcol,\n- * startrow\n+ /**\n+ * Property: process\n+ * {} The process to chain\n */\n- calculateGridLayout: function(bounds, origin, resolution) {\n- var tilelon = resolution * this.tileSize.w;\n- var tilelat = resolution * this.tileSize.h;\n-\n- var offsetlon = bounds.left - origin.lon;\n- var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n-\n- var rowSign = this.rowSign;\n-\n- var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n- var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign;\n-\n- return {\n- tilelon: tilelon,\n- tilelat: tilelat,\n- startcol: tilecol,\n- startrow: tilerow\n- };\n-\n- },\n+ process: null,\n \n /**\n- * Method: getTileOrigin\n- * Determine the origin for aligning the grid of tiles. If a \n- * property is supplied, that will be returned. Otherwise, the origin\n- * will be derived from the layer's property. In this case,\n- * the tile origin will be the corner of the given by the \n- * property.\n- *\n- * Returns:\n- * {} The tile origin.\n+ * Property: output\n+ * {String} The output identifier of the output we are going to use as\n+ * input for another process.\n */\n- getTileOrigin: function() {\n- var origin = this.tileOrigin;\n- if (!origin) {\n- var extent = this.getMaxExtent();\n- var edges = ({\n- \"tl\": [\"left\", \"top\"],\n- \"tr\": [\"right\", \"top\"],\n- \"bl\": [\"left\", \"bottom\"],\n- \"br\": [\"right\", \"bottom\"]\n- })[this.tileOriginCorner];\n- origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);\n- }\n- return origin;\n- },\n+ output: null,\n \n /**\n- * Method: getTileBoundsForGridIndex\n+ * Constructor: OpenLayers.WPSProcess.ChainLink\n *\n * Parameters:\n- * row - {Number} The row of the grid\n- * col - {Number} The column of the grid\n- *\n- * Returns:\n- * {} The bounds for the tile at (row, col)\n+ * options - {Object} Properties to set on the instance.\n */\n- getTileBoundsForGridIndex: function(row, col) {\n- var origin = this.getTileOrigin();\n- var tileLayout = this.gridLayout;\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n- var startcol = tileLayout.startcol;\n- var startrow = tileLayout.startrow;\n- var rowSign = this.rowSign;\n- return new OpenLayers.Bounds(\n- origin.lon + (startcol + col) * tilelon,\n- origin.lat - (startrow + row * rowSign) * tilelat * rowSign,\n- origin.lon + (startcol + col + 1) * tilelon,\n- origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign\n- );\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n },\n \n- /**\n- * Method: initGriddedTiles\n- * \n- * Parameters:\n- * bounds - {}\n- */\n- initGriddedTiles: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n-\n- // work out mininum number of rows and columns; this is the number of\n- // tiles required to cover the viewport plus at least one for panning\n-\n- var viewSize = this.map.getSize();\n-\n- var origin = this.getTileOrigin();\n- var resolution = this.map.getResolution(),\n- serverResolution = this.getServerResolution(),\n- ratio = resolution / serverResolution,\n- tileSize = {\n- w: this.tileSize.w / ratio,\n- h: this.tileSize.h / ratio\n- };\n-\n- var minRows = Math.ceil(viewSize.h / tileSize.h) +\n- 2 * this.buffer + 1;\n- var minCols = Math.ceil(viewSize.w / tileSize.w) +\n- 2 * this.buffer + 1;\n-\n- var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n- this.gridLayout = tileLayout;\n-\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n-\n- var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n- var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n-\n- var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n- var startPx = this.map.getViewPortPxFromLonLat(\n- new OpenLayers.LonLat(tileBounds.left, tileBounds.top)\n- );\n- startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n- startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n-\n- var tileData = [],\n- center = this.map.getCenter();\n-\n- var rowidx = 0;\n- do {\n- var row = this.grid[rowidx];\n- if (!row) {\n- row = [];\n- this.grid.push(row);\n- }\n-\n- var colidx = 0;\n- do {\n- tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n- var px = startPx.clone();\n- px.x = px.x + colidx * Math.round(tileSize.w);\n- px.y = px.y + rowidx * Math.round(tileSize.h);\n- var tile = row[colidx];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n- this.addTileMonitoringHooks(tile);\n- row.push(tile);\n- } else {\n- tile.moveTo(tileBounds, px, false);\n- }\n- var tileCenter = tileBounds.getCenterLonLat();\n- tileData.push({\n- tile: tile,\n- distance: Math.pow(tileCenter.lon - center.lon, 2) +\n- Math.pow(tileCenter.lat - center.lat, 2)\n- });\n-\n- colidx += 1;\n- } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) ||\n- colidx < minCols);\n+ CLASS_NAME: \"OpenLayers.WPSProcess.ChainLink\"\n \n- rowidx += 1;\n- } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) ||\n- rowidx < minRows);\n+});\n+/* ======================================================================\n+ OpenLayers/StyleMap.js\n+ ====================================================================== */\n \n- //shave off exceess rows and colums\n- this.removeExcessTiles(rowidx, colidx);\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- var resolution = this.getServerResolution();\n- // store the resolution of the grid\n- this.gridResolution = resolution;\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Style.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ */\n \n- //now actually draw the tiles\n- tileData.sort(function(a, b) {\n- return a.distance - b.distance;\n- });\n- for (var i = 0, ii = tileData.length; i < ii; ++i) {\n- tileData[i].tile.draw();\n- }\n- },\n+/**\n+ * Class: OpenLayers.StyleMap\n+ */\n+OpenLayers.StyleMap = OpenLayers.Class({\n \n /**\n- * Method: getMaxExtent\n- * Get this layer's maximum extent. (Implemented as a getter for\n- * potential specific implementations in sub-classes.)\n- *\n- * Returns:\n- * {}\n+ * Property: styles\n+ * {Object} Hash of {}, keyed by names of well known\n+ * rendering intents (e.g. \"default\", \"temporary\", \"select\", \"delete\").\n */\n- getMaxExtent: function() {\n- return this.maxExtent;\n- },\n+ styles: null,\n \n /**\n- * APIMethod: addTile\n- * Create a tile, initialize it, and add it to the layer div. \n- *\n- * Parameters\n- * bounds - {}\n- * position - {}\n- *\n- * Returns:\n- * {} The added OpenLayers.Tile\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- addTile: function(bounds, position) {\n- var tile = new this.tileClass(\n- this, position, bounds, null, this.tileSize, this.tileOptions\n- );\n- this.events.triggerEvent(\"addtile\", {\n- tile: tile\n- });\n- return tile;\n- },\n+ extendDefault: true,\n \n- /** \n- * Method: addTileMonitoringHooks\n- * This function takes a tile as input and adds the appropriate hooks to \n- * the tile so that the layer can keep track of the loading tiles.\n+ /**\n+ * Constructor: OpenLayers.StyleMap\n * \n- * Parameters: \n- * tile - {}\n+ * Parameters:\n+ * style - {Object} Optional. Either a style hash, or a style object, or\n+ * a hash of style objects (style hashes) keyed by rendering\n+ * intent. If just one style hash or style object is passed,\n+ * this will be used for all known render intents (default,\n+ * select, temporary)\n+ * options - {Object} optional hash of additional options for this\n+ * instance\n */\n- addTileMonitoringHooks: function(tile) {\n-\n- var replacingCls = 'olTileReplacing';\n-\n- tile.onLoadStart = function() {\n- //if that was first tile then trigger a 'loadstart' on the layer\n- if (this.loading === false) {\n- this.loading = true;\n- this.events.triggerEvent(\"loadstart\");\n- }\n- this.events.triggerEvent(\"tileloadstart\", {\n- tile: tile\n- });\n- this.numLoadingTiles++;\n- if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- OpenLayers.Element.addClass(tile.getTile(), replacingCls);\n- }\n+ initialize: function(style, options) {\n+ this.styles = {\n+ \"default\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"default\"]),\n+ \"select\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"select\"]),\n+ \"temporary\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"temporary\"]),\n+ \"delete\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"delete\"])\n };\n \n- tile.onLoadEnd = function(evt) {\n- this.numLoadingTiles--;\n- var aborted = evt.type === 'unload';\n- this.events.triggerEvent(\"tileloaded\", {\n- tile: tile,\n- aborted: aborted\n- });\n- if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- var tileDiv = tile.getTile();\n- if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {\n- var bufferTile = document.getElementById(tile.id + '_bb');\n- if (bufferTile) {\n- bufferTile.parentNode.removeChild(bufferTile);\n- }\n- }\n- OpenLayers.Element.removeClass(tileDiv, replacingCls);\n- }\n- //if that was the last tile, then trigger a 'loadend' on the layer\n- if (this.numLoadingTiles === 0) {\n- if (this.backBuffer) {\n- if (this.backBuffer.childNodes.length === 0) {\n- // no tiles transitioning, remove immediately\n- this.removeBackBuffer();\n- } else {\n- // wait until transition has ended or delay has passed\n- this._transitionElement = aborted ?\n- this.div.lastChild : tile.imgDiv;\n- var transitionendEvents = this.transitionendEvents;\n- for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.observe(this._transitionElement,\n- transitionendEvents[i],\n- this._removeBackBuffer);\n- }\n- // the removal of the back buffer is delayed to prevent\n- // flash effects due to the animation of tile displaying\n- this.backBufferTimerId = window.setTimeout(\n- this._removeBackBuffer, this.removeBackBufferDelay\n- );\n- }\n+ // 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- this.loading = false;\n- this.events.triggerEvent(\"loadend\");\n- }\n- };\n-\n- tile.onLoadError = function() {\n- this.events.triggerEvent(\"tileerror\", {\n- tile: tile\n- });\n- };\n-\n- tile.events.on({\n- \"loadstart\": tile.onLoadStart,\n- \"loadend\": tile.onLoadEnd,\n- \"unload\": tile.onLoadEnd,\n- \"loaderror\": tile.onLoadError,\n- scope: this\n- });\n- },\n-\n- /** \n- * Method: removeTileMonitoringHooks\n- * This function takes a tile as input and removes the tile hooks \n- * that were added in addTileMonitoringHooks()\n- * \n- * Parameters: \n- * tile - {}\n- */\n- removeTileMonitoringHooks: function(tile) {\n- tile.unload();\n- tile.events.un({\n- \"loadstart\": tile.onLoadStart,\n- \"loadend\": tile.onLoadEnd,\n- \"unload\": tile.onLoadEnd,\n- \"loaderror\": tile.onLoadError,\n- scope: this\n- });\n- },\n-\n- /**\n- * Method: moveGriddedTiles\n- */\n- moveGriddedTiles: function() {\n- var buffer = this.buffer + 1;\n- while (true) {\n- var tlTile = this.grid[0][0];\n- var tlViewPort = {\n- x: tlTile.position.x +\n- this.map.layerContainerOriginPx.x,\n- y: tlTile.position.y +\n- this.map.layerContainerOriginPx.y\n- };\n- var ratio = this.getServerResolution() / this.map.getResolution();\n- var tileSize = {\n- w: Math.round(this.tileSize.w * ratio),\n- h: Math.round(this.tileSize.h * ratio)\n- };\n- if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n- this.shiftColumn(true, tileSize);\n- } else if (tlViewPort.x < -tileSize.w * buffer) {\n- this.shiftColumn(false, tileSize);\n- } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n- this.shiftRow(true, tileSize);\n- } else if (tlViewPort.y < -tileSize.h * buffer) {\n- this.shiftRow(false, tileSize);\n- } else {\n- break;\n }\n }\n+ OpenLayers.Util.extend(this, options);\n },\n \n /**\n- * Method: shiftRow\n- * Shifty grid work\n- *\n- * Parameters:\n- * prepend - {Boolean} if true, prepend to beginning.\n- * if false, then append to end\n- * tileSize - {Object} rendered tile size; object with w and h properties\n- */\n- shiftRow: function(prepend, tileSize) {\n- var grid = this.grid;\n- var rowIndex = prepend ? 0 : (grid.length - 1);\n- var sign = prepend ? -1 : 1;\n- var rowSign = this.rowSign;\n- var tileLayout = this.gridLayout;\n- tileLayout.startrow += sign * rowSign;\n-\n- var modelRow = grid[rowIndex];\n- var row = grid[prepend ? 'pop' : 'shift']();\n- for (var i = 0, len = row.length; i < len; i++) {\n- var tile = row[i];\n- var position = modelRow[i].position.clone();\n- position.y += tileSize.h * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);\n- }\n- grid[prepend ? 'unshift' : 'push'](row);\n- },\n-\n- /**\n- * Method: shiftColumn\n- * Shift grid work in the other dimension\n- *\n- * Parameters:\n- * prepend - {Boolean} if true, prepend to beginning.\n- * if false, then append to end\n- * tileSize - {Object} rendered tile size; object with w and h properties\n+ * Method: destroy\n */\n- shiftColumn: function(prepend, tileSize) {\n- var grid = this.grid;\n- var colIndex = prepend ? 0 : (grid[0].length - 1);\n- var sign = prepend ? -1 : 1;\n- var tileLayout = this.gridLayout;\n- tileLayout.startcol += sign;\n-\n- for (var i = 0, len = grid.length; i < len; i++) {\n- var row = grid[i];\n- var position = row[colIndex].position.clone();\n- var tile = row[prepend ? 'pop' : 'shift']();\n- position.x += tileSize.w * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n- row[prepend ? 'unshift' : 'push'](tile);\n+ destroy: function() {\n+ for (var key in this.styles) {\n+ this.styles[key].destroy();\n }\n+ this.styles = null;\n },\n \n /**\n- * Method: removeExcessTiles\n- * When the size of the map or the buffer changes, we may need to\n- * remove some excess rows and columns.\n+ * Method: createSymbolizer\n+ * Creates the symbolizer for a feature for a render intent.\n * \n * Parameters:\n- * rows - {Integer} Maximum number of rows we want our grid to have.\n- * columns - {Integer} Maximum number of columns we want our grid to have.\n+ * 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- removeExcessTiles: function(rows, columns) {\n- var i, l;\n-\n- // remove extra rows\n- while (this.grid.length > rows) {\n- var row = this.grid.pop();\n- for (i = 0, l = row.length; i < l; i++) {\n- var tile = row[i];\n- this.destroyTile(tile);\n- }\n+ createSymbolizer: function(feature, intent) {\n+ if (!feature) {\n+ feature = new OpenLayers.Feature.Vector();\n }\n-\n- // remove extra columns\n- for (i = 0, l = this.grid.length; i < l; i++) {\n- while (this.grid[i].length > columns) {\n- var row = this.grid[i];\n- var tile = row.pop();\n- this.destroyTile(tile);\n- }\n+ if (!this.styles[intent]) {\n+ intent = \"default\";\n }\n- },\n-\n- /**\n- * Method: onMapResize\n- * For singleTile layers, this will set a new tile size according to the\n- * dimensions of the map pane.\n- */\n- onMapResize: function() {\n- if (this.singleTile) {\n- this.clearGrid();\n- this.setTileSize();\n+ feature.renderIntent = intent;\n+ var defaultSymbolizer = {};\n+ if (this.extendDefault && intent != \"default\") {\n+ defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature);\n }\n+ return OpenLayers.Util.extend(defaultSymbolizer,\n+ this.styles[intent].createSymbolizer(feature));\n },\n \n /**\n- * APIMethod: getTileBounds\n- * Returns The tile bounds for a layer given a pixel location.\n- *\n+ * Method: addUniqueValueRules\n+ * Convenience method to create comparison rules for unique values of a\n+ * property. The rules will be added to the style object for a specified\n+ * rendering intent. This method is a shortcut for creating something like\n+ * the \"unique value legends\" familiar from well known desktop GIS systems\n+ * \n * Parameters:\n- * viewPortPx - {} The location in the viewport.\n- *\n- * Returns:\n- * {} Bounds of the tile at the given pixel location.\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- getTileBounds: function(viewPortPx) {\n- var maxExtent = this.maxExtent;\n- var resolution = this.getResolution();\n- var tileMapWidth = resolution * this.tileSize.w;\n- var tileMapHeight = resolution * this.tileSize.h;\n- var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n- var tileLeft = maxExtent.left + (tileMapWidth *\n- Math.floor((mapPoint.lon -\n- maxExtent.left) /\n- tileMapWidth));\n- var tileBottom = maxExtent.bottom + (tileMapHeight *\n- Math.floor((mapPoint.lat -\n- maxExtent.bottom) /\n- tileMapHeight));\n- return new OpenLayers.Bounds(tileLeft, tileBottom,\n- tileLeft + tileMapWidth,\n- tileBottom + tileMapHeight);\n+ addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n+ var rules = [];\n+ for (var value in symbolizers) {\n+ rules.push(new OpenLayers.Rule({\n+ symbolizer: symbolizers[value],\n+ context: context,\n+ filter: new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.EQUAL_TO,\n+ property: property,\n+ value: value\n+ })\n+ }));\n+ }\n+ this.styles[renderIntent].addRules(rules);\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Grid\"\n+ CLASS_NAME: \"OpenLayers.StyleMap\"\n });\n /* ======================================================================\n- OpenLayers/TileManager.js\n+ OpenLayers/Strategy.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/BaseTypes.js\n- * @requires OpenLayers/BaseTypes/Element.js\n- * @requires OpenLayers/Layer/Grid.js\n- * @requires OpenLayers/Tile/Image.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.TileManager\n- * Provides queueing of image requests and caching of image elements.\n- *\n- * Queueing avoids unnecessary image requests while changing zoom levels\n- * quickly, and helps improve dragging performance on mobile devices that show\n- * a lag in dragging when loading of new images starts. and\n- * are the configuration options to control this behavior.\n- *\n- * Caching avoids setting the src on image elements for images that have already\n- * been used. Several maps can share a TileManager instance, in which case each\n- * map gets its own tile queue, but all maps share the same tile cache.\n+ * 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.TileManager = OpenLayers.Class({\n-\n- /**\n- * APIProperty: cacheSize\n- * {Number} Number of image elements to keep referenced in this instance's\n- * cache for fast reuse. Default is 256.\n- */\n- cacheSize: 256,\n-\n- /**\n- * APIProperty: tilesPerFrame\n- * {Number} Number of queued tiles to load per frame (see ).\n- * Default is 2.\n- */\n- tilesPerFrame: 2,\n-\n- /**\n- * APIProperty: frameDelay\n- * {Number} Delay between tile loading frames (see ) in\n- * milliseconds. Default is 16.\n- */\n- frameDelay: 16,\n-\n- /**\n- * APIProperty: moveDelay\n- * {Number} Delay in milliseconds after a map's move event before loading\n- * tiles. Default is 100.\n- */\n- moveDelay: 100,\n+OpenLayers.Strategy = OpenLayers.Class({\n \n /**\n- * APIProperty: zoomDelay\n- * {Number} Delay in milliseconds after a map's zoomend event before loading\n- * tiles. Default is 200.\n+ * Property: layer\n+ * {} The layer this strategy belongs to.\n */\n- zoomDelay: 200,\n+ layer: null,\n \n /**\n- * Property: maps\n- * {Array()} The maps to manage tiles on.\n+ * Property: options\n+ * {Object} Any options sent to the constructor.\n */\n- maps: null,\n+ options: null,\n \n- /**\n- * Property: tileQueueId\n- * {Object} The ids of the loop, keyed by map id.\n+ /** \n+ * Property: active \n+ * {Boolean} The control is active.\n */\n- tileQueueId: null,\n+ active: null,\n \n /**\n- * Property: tileQueue\n- * {Object(Array())} Tiles queued for drawing, keyed by\n- * map id.\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- tileQueue: null,\n+ autoActivate: true,\n \n /**\n- * Property: tileCache\n- * {Object} Cached image elements, keyed by URL.\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- tileCache: null,\n+ autoDestroy: true,\n \n /**\n- * Property: tileCacheIndex\n- * {Array(String)} URLs of cached tiles. First entry is the least recently\n- * used.\n- */\n- tileCacheIndex: null,\n-\n- /** \n- * Constructor: OpenLayers.TileManager\n- * Constructor for a new instance.\n- * \n+ * Constructor: OpenLayers.Strategy\n+ * Abstract class for vector strategies. Create instances of a subclass.\n+ *\n * Parameters:\n- * options - {Object} Configuration for this instance.\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.maps = [];\n- this.tileQueueId = {};\n- this.tileQueue = {};\n- this.tileCache = {};\n- this.tileCacheIndex = [];\n+ this.options = options;\n+ // set the active property here, so that user cannot override it\n+ this.active = false;\n },\n \n /**\n- * Method: addMap\n- * Binds this instance to a map\n- *\n- * Parameters:\n- * map - {}\n+ * APIMethod: destroy\n+ * Clean up the strategy.\n */\n- addMap: function(map) {\n- if (this._destroyed || !OpenLayers.Layer.Grid) {\n- return;\n- }\n- this.maps.push(map);\n- this.tileQueue[map.id] = [];\n- for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n- this.addLayer({\n- layer: map.layers[i]\n- });\n- }\n- map.events.on({\n- move: this.move,\n- zoomend: this.zoomEnd,\n- changelayer: this.changeLayer,\n- addlayer: this.addLayer,\n- preremovelayer: this.removeLayer,\n- scope: this\n- });\n+ destroy: function() {\n+ this.deactivate();\n+ this.layer = null;\n+ this.options = null;\n },\n \n /**\n- * Method: removeMap\n- * Unbinds this instance from a map\n+ * Method: setLayer\n+ * Called to set the property.\n *\n * Parameters:\n- * map - {}\n+ * layer - {}\n */\n- removeMap: function(map) {\n- if (this._destroyed || !OpenLayers.Layer.Grid) {\n- return;\n- }\n- window.clearTimeout(this.tileQueueId[map.id]);\n- if (map.layers) {\n- for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n- this.removeLayer({\n- layer: map.layers[i]\n- });\n- }\n- }\n- if (map.events) {\n- map.events.un({\n- move: this.move,\n- zoomend: this.zoomEnd,\n- changelayer: this.changeLayer,\n- addlayer: this.addLayer,\n- preremovelayer: this.removeLayer,\n- scope: this\n- });\n- }\n- delete this.tileQueue[map.id];\n- delete this.tileQueueId[map.id];\n- OpenLayers.Util.removeItem(this.maps, map);\n+ setLayer: function(layer) {\n+ this.layer = layer;\n },\n \n /**\n- * Method: move\n- * Handles the map's move event\n+ * Method: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n *\n- * Parameters:\n- * evt - {Object} Listener argument\n+ * Returns:\n+ * {Boolean} True if the strategy was successfully activated or false if\n+ * the strategy was already active.\n */\n- move: function(evt) {\n- this.updateTimeout(evt.object, this.moveDelay, true);\n+ activate: function() {\n+ if (!this.active) {\n+ this.active = true;\n+ return true;\n+ }\n+ return false;\n },\n \n /**\n- * Method: zoomEnd\n- * Handles the map's zoomEnd event\n+ * Method: deactivate\n+ * Deactivate the strategy. Unregister any listeners, do appropriate\n+ * tear-down.\n *\n- * Parameters:\n- * evt - {Object} Listener argument\n+ * Returns:\n+ * {Boolean} True if the strategy was successfully deactivated or false if\n+ * the strategy was already inactive.\n */\n- zoomEnd: function(evt) {\n- this.updateTimeout(evt.object, this.zoomDelay);\n+ deactivate: function() {\n+ if (this.active) {\n+ this.active = false;\n+ return true;\n+ }\n+ return false;\n },\n \n- /**\n- * Method: changeLayer\n- * Handles the map's changeLayer event\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- * evt - {Object} Listener argument\n+ * lonlat - {} the position of this marker\n+ * icon - {} the icon for this marker\n */\n- changeLayer: function(evt) {\n- if (evt.property === 'visibility' || evt.property === 'params') {\n- this.updateTimeout(evt.object, 0);\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- * Method: addLayer\n- * Handles the map's addlayer event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\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- addLayer: function(evt) {\n- var layer = evt.layer;\n- if (layer instanceof OpenLayers.Layer.Grid) {\n- layer.events.on({\n- addtile: this.addTile,\n- retile: this.clearTileQueue,\n- scope: this\n- });\n- var i, j, tile;\n- for (i = layer.grid.length - 1; i >= 0; --i) {\n- for (j = layer.grid[i].length - 1; j >= 0; --j) {\n- tile = layer.grid[i][j];\n- this.addTile({\n- tile: tile\n- });\n- if (tile.url && !tile.imgDiv) {\n- this.manageTileCache({\n- object: tile\n- });\n- }\n- }\n- }\n+ 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: removeLayer\n- * Handles the map's preremovelayer event\n- *\n+ /** \n+ * Method: draw\n+ * Calls draw on the icon, and returns that output.\n+ * \n * Parameters:\n- * evt - {Object} The listener argument\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- removeLayer: function(evt) {\n- var layer = evt.layer;\n- if (layer instanceof OpenLayers.Layer.Grid) {\n- this.clearTileQueue({\n- object: layer\n- });\n- if (layer.events) {\n- layer.events.un({\n- addtile: this.addTile,\n- retile: this.clearTileQueue,\n- scope: this\n- });\n- }\n- if (layer.grid) {\n- var i, j, tile;\n- for (i = layer.grid.length - 1; i >= 0; --i) {\n- for (j = layer.grid[i].length - 1; j >= 0; --j) {\n- tile = layer.grid[i][j];\n- this.unloadTile({\n- object: tile\n- });\n- }\n- }\n- }\n- }\n+ draw: function(px) {\n+ return this.icon.draw(px);\n },\n \n- /**\n- * Method: updateTimeout\n- * Applies the or to the loop,\n- * and schedules more queue processing after if there are still\n- * tiles in the queue.\n- *\n- * Parameters:\n- * map - {} The map to update the timeout for\n- * delay - {Number} The delay to apply\n- * nice - {Boolean} If true, the timeout function will only be created if\n- * the tilequeue is not empty. This is used by the move handler to\n- * avoid impacts on dragging performance. For other events, the tile\n- * queue may not be populated yet, so we need to set the timer\n- * regardless of the queue size.\n+ /** \n+ * Method: erase\n+ * Erases any drawn elements for this marker.\n */\n- updateTimeout: function(map, delay, nice) {\n- window.clearTimeout(this.tileQueueId[map.id]);\n- var tileQueue = this.tileQueue[map.id];\n- if (!nice || tileQueue.length) {\n- this.tileQueueId[map.id] = window.setTimeout(\n- OpenLayers.Function.bind(function() {\n- this.drawTilesFromQueue(map);\n- if (tileQueue.length) {\n- this.updateTimeout(map, this.frameDelay);\n- }\n- }, this), delay\n- );\n+ erase: function() {\n+ if (this.icon != null) {\n+ this.icon.erase();\n }\n },\n \n /**\n- * Method: addTile\n- * Listener for the layer's addtile event\n+ * Method: moveTo\n+ * Move the marker to the new location.\n *\n * Parameters:\n- * evt - {Object} The listener argument\n+ * px - {|Object} the pixel position to move to.\n+ * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n */\n- addTile: function(evt) {\n- if (evt.tile instanceof OpenLayers.Tile.Image) {\n- evt.tile.events.on({\n- beforedraw: this.queueTileDraw,\n- beforeload: this.manageTileCache,\n- loadend: this.addToCache,\n- unload: this.unloadTile,\n- scope: this\n- });\n- } else {\n- // Layer has the wrong tile type, so don't handle it any longer\n- this.removeLayer({\n- layer: evt.tile.layer\n- });\n+ moveTo: function(px) {\n+ if ((px != null) && (this.icon != null)) {\n+ this.icon.moveTo(px);\n }\n+ this.lonlat = this.map.getLonLatFromLayerPx(px);\n },\n \n /**\n- * Method: unloadTile\n- * Listener for the tile's unload event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\n+ * APIMethod: isDrawn\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the marker is drawn.\n */\n- unloadTile: function(evt) {\n- var tile = evt.object;\n- tile.events.un({\n- beforedraw: this.queueTileDraw,\n- beforeload: this.manageTileCache,\n- loadend: this.addToCache,\n- unload: this.unloadTile,\n- scope: this\n- });\n- OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile);\n+ isDrawn: function() {\n+ var isDrawn = (this.icon && this.icon.isDrawn());\n+ return isDrawn;\n },\n \n /**\n- * Method: queueTileDraw\n- * Adds a tile to the queue that will draw it.\n+ * Method: onScreen\n *\n- * Parameters:\n- * evt - {Object} Listener argument of the tile's beforedraw event\n+ * Returns:\n+ * {Boolean} Whether or not the marker is currently visible on screen.\n */\n- queueTileDraw: function(evt) {\n- var tile = evt.object;\n- var queued = false;\n- var layer = tile.layer;\n- var url = layer.getURL(tile.bounds);\n- var img = this.tileCache[url];\n- if (img && img.className !== 'olTileImage') {\n- // cached image no longer valid, e.g. because we're olTileReplacing\n- delete this.tileCache[url];\n- OpenLayers.Util.removeItem(this.tileCacheIndex, url);\n- img = null;\n- }\n- // queue only if image with same url not cached already\n- if (layer.url && (layer.async || !img)) {\n- // add to queue only if not in queue already\n- var tileQueue = this.tileQueue[layer.map.id];\n- if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {\n- tileQueue.push(tile);\n- }\n- queued = true;\n- }\n- return !queued;\n- },\n+ onScreen: function() {\n \n- /**\n- * Method: drawTilesFromQueue\n- * Draws tiles from the tileQueue, and unqueues the tiles\n- */\n- drawTilesFromQueue: function(map) {\n- var tileQueue = this.tileQueue[map.id];\n- var limit = this.tilesPerFrame;\n- var animating = map.zoomTween && map.zoomTween.playing;\n- while (!animating && tileQueue.length && limit) {\n- tileQueue.shift().draw(true);\n- --limit;\n+ 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: manageTileCache\n- * Adds, updates, removes and fetches cache entries.\n+ * Method: inflate\n+ * Englarges the markers icon by the specified ratio.\n *\n * Parameters:\n- * evt - {Object} Listener argument of the tile's beforeload event\n+ * inflate - {float} the ratio to enlarge the marker by (passing 2\n+ * will double the size).\n */\n- manageTileCache: function(evt) {\n- var tile = evt.object;\n- var img = this.tileCache[tile.url];\n- if (img) {\n- // if image is on its layer's backbuffer, remove it from backbuffer\n- if (img.parentNode &&\n- OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) {\n- img.parentNode.removeChild(img);\n- img.id = null;\n- }\n- // only use image from cache if it is not on a layer already\n- if (!img.parentNode) {\n- img.style.visibility = 'hidden';\n- img.style.opacity = 0;\n- tile.setImage(img);\n- // LRU - move tile to the end of the array to mark it as the most\n- // recently used\n- OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);\n- this.tileCacheIndex.push(tile.url);\n- }\n+ 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: addToCache\n- *\n+ /** \n+ * Method: setOpacity\n+ * Change the opacity of the marker by changin the opacity of \n+ * its icon\n+ * \n * Parameters:\n- * evt - {Object} Listener argument for the tile's loadend event\n+ * opacity - {float} Specified as fraction (0.4, etc)\n */\n- addToCache: function(evt) {\n- var tile = evt.object;\n- if (!this.tileCache[tile.url]) {\n- if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) {\n- if (this.tileCacheIndex.length >= this.cacheSize) {\n- delete this.tileCache[this.tileCacheIndex[0]];\n- this.tileCacheIndex.shift();\n- }\n- this.tileCache[tile.url] = tile.imgDiv;\n- this.tileCacheIndex.push(tile.url);\n- }\n- }\n+ setOpacity: function(opacity) {\n+ this.icon.setOpacity(opacity);\n },\n \n /**\n- * Method: clearTileQueue\n- * Clears the tile queue from tiles of a specific layer\n- *\n- * Parameters:\n- * evt - {Object} Listener argument of the layer's retile event\n+ * Method: setUrl\n+ * Change URL of the Icon Image.\n+ * \n+ * url - {String} \n */\n- clearTileQueue: function(evt) {\n- var layer = evt.object;\n- var tileQueue = this.tileQueue[layer.map.id];\n- for (var i = tileQueue.length - 1; i >= 0; --i) {\n- if (tileQueue[i].layer === layer) {\n- tileQueue.splice(i, 1);\n- }\n- }\n+ setUrl: function(url) {\n+ this.icon.setUrl(url);\n },\n \n- /**\n- * Method: destroy\n+ /** \n+ * Method: display\n+ * Hide or show the icon\n+ * \n+ * display - {Boolean} \n */\n- destroy: function() {\n- for (var i = this.maps.length - 1; i >= 0; --i) {\n- this.removeMap(this.maps[i]);\n- }\n- this.maps = null;\n- this.tileQueue = null;\n- this.tileQueueId = null;\n- this.tileCache = null;\n- this.tileCacheIndex = null;\n- this._destroyed = true;\n- }\n+ 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/Symbolizer/Point.js\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/Symbolizer.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Symbolizer.Point\n- * A symbolizer used to render point features.\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.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {\n+OpenLayers.Renderer = OpenLayers.Class({\n+\n+ /** \n+ * Property: container\n+ * {DOMElement} \n+ */\n+ container: null,\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+ * Property: root\n+ * {DOMElement}\n+ */\n+ root: null,\n+\n+ /** \n+ * Property: extent\n+ * {}\n */\n+ extent: null,\n \n /**\n- * APIProperty: strokeOpacity\n- * {Number} Stroke opacity (0-1).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: strokeWidth\n- * {Number} Pixel stroke width.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Property: resolution\n+ * {Float} cache of current map resolution\n */\n+ resolution: null,\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: map \n+ * {} Reference to the map -- this is set in Vector's setMap()\n */\n+ map: null,\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+ * 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- * APIProperty: fillColor\n- * {String} RGB hex fill color (e.g. \"#ff0000\" for red).\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- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Returns:\n+ * {Boolean} Whether or not the browser supports the renderer class\n */\n+ supported: function() {\n+ return false;\n+ },\n \n /**\n- * APIProperty: fillOpacity\n- * {Number} Fill opacity (0-1).\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- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: pointRadius\n- * {Number} Pixel point radius.\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- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: externalGraphic\n- * {String} Url to an external graphic that will be used for rendering \n- * points.\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- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: graphicWidth\n- * {Number} Pixel width for sizing an external graphic.\n+ * Method: drawText\n+ * Function for drawing text labels.\n+ * This method is only called by the renderer itself.\n * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Parameters: \n+ * featureId - {String}\n+ * style -\n+ * location - {}\n */\n+ drawText: function(featureId, style, location) {},\n \n /**\n- * APIProperty: graphicHeight\n- * {Number} Pixel height for sizing an external graphic.\n+ * Method: removeText\n+ * Function for removing text labels.\n+ * This method is only called by the renderer itself.\n * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Parameters: \n+ * featureId - {String}\n */\n+ removeText: function(featureId) {},\n \n /**\n- * APIProperty: graphicOpacity\n- * {Number} Opacity (0-1) for an external graphic.\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- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Parameters:\n+ * evt - {} \n+ *\n+ * Returns:\n+ * {String} A feature id or undefined.\n */\n+ getFeatureIdFromEvent: function(evt) {},\n \n /**\n- * APIProperty: graphicXOffset\n- * {Number} Pixel offset along the positive x axis for displacing an \n- * external graphic.\n+ * Method: eraseFeatures \n+ * This is called by the layer to erase features\n * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * APIProperty: graphicYOffset\n- * {Number} Pixel offset along the positive y axis for displacing an \n- * external graphic.\n+ * Method: eraseGeometry\n+ * Remove a geometry from the renderer (by id).\n+ * virtual function.\n * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Parameters:\n+ * geometry - {} \n+ * featureId - {String}\n */\n+ eraseGeometry: function(geometry, featureId) {},\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+ * 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- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Parameters:\n+ * renderer - {} target renderer for the moved root\n */\n+ moveRoot: function(renderer) {},\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+ * 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- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Returns:\n+ * {String} the id of the output layer.\n */\n+ getRenderLayerId: function() {\n+ return this.container.id;\n+ },\n \n /**\n- * Constructor: OpenLayers.Symbolizer.Point\n- * Create a symbolizer for rendering points.\n- *\n+ * Method: applyDefaultSymbolizer\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+ * symbolizer - {Object}\n+ * \n * Returns:\n- * A new point symbolizer.\n+ * {Object}\n */\n- initialize: function(config) {\n- OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\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.Symbolizer.Point\"\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/Symbolizer/Line.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/Symbolizer.js\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n */\n \n /**\n- * Class: OpenLayers.Symbolizer.Line\n- * A symbolizer used to render line features.\n+ * Class: OpenLayers.Format.WPSDescribeProcess\n+ * Read WPS DescribeProcess responses. \n+ *\n+ * Inherits from:\n+ * - \n */\n-OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, {\n+OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(\n+ OpenLayers.Format.XML, {\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+ * Constant: VERSION\n+ * {String} 1.0.0\n+ */\n+ VERSION: \"1.0.0\",\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+ * 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: strokeWidth\n- * {Number} Pixel stroke width.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * 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+ * Property: defaultPrefix\n+ */\n+ defaultPrefix: \"wps\",\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+ * 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.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+ * 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- CLASS_NAME: \"OpenLayers.Symbolizer.Line\"\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+ /**\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/Symbolizer/Polygon.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/Symbolizer.js\n+ * @requires OpenLayers/SingleFile.js\n */\n \n /**\n- * Class: OpenLayers.Symbolizer.Polygon\n- * A symbolizer used to render line features.\n+ * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/WPSProcess.js\n+ * @requires OpenLayers/Format/WPSDescribeProcess.js\n+ * @requires OpenLayers/Request.js\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+ * 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- * APIProperty: strokeOpacity\n- * {Number} Stroke opacity (0-1).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Property: servers\n+ * {Object} Service metadata, keyed by a local identifier.\n+ *\n+ * Properties:\n+ * url - {String} the url of the server\n+ * version - {String} WPS version of the server\n+ * processDescription - {Object} Cache of raw DescribeProcess\n+ * responses, keyed by process identifier.\n */\n+ servers: null,\n \n /**\n- * APIProperty: strokeWidth\n- * {Number} Pixel stroke width.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Property: version\n+ * {String} The default WPS version to use if none is configured. Default\n+ * is '1.0.0'.\n */\n+ version: '1.0.0',\n \n /**\n- * APIProperty: strokeLinecap\n- * {String} Stroke cap type (\"butt\", \"round\", or \"square\").\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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: 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+ * 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- * 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+ * 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- * APIProperty: fillOpacity\n- * {Number} Fill opacity (0-1).\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\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- * Constructor: OpenLayers.Symbolizer.Polygon\n- * Create a symbolizer for rendering polygons.\n+ * APIMethod: getProcess\n+ * Creates an .\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+ * 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- * A new polygon symbolizer.\n+ * {}\n */\n- initialize: function(config) {\n- OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\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- CLASS_NAME: \"OpenLayers.Symbolizer.Polygon\"\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+ /**\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/Symbolizer/Text.js\n+ OpenLayers/Popup.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Symbolizer.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n+\n /**\n- * Class: OpenLayers.Symbolizer.Text\n- * A symbolizer used to render text labels for features.\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.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, {\n+OpenLayers.Popup = OpenLayers.Class({\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+ * 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- * APIProperty: fontFamily\n- * {String} The font family for the label.\n- * \n- * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.\n+ * Property: lonlat \n+ * {} the position of this popup on the map\n */\n+ lonlat: null,\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+ * Property: div \n+ * {DOMElement} the div that contains this popup.\n */\n+ div: null,\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+ * Property: contentSize \n+ * {} the width and height of the content.\n */\n+ contentSize: null,\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+ * Property: size \n+ * {} the width and height of the popup.\n */\n+ size: null,\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+ * Property: contentHTML \n+ * {String} An HTML string for this popup to display.\n */\n- initialize: function(config) {\n- OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n- },\n+ contentHTML: null,\n \n- CLASS_NAME: \"OpenLayers.Symbolizer.Text\"\n+ /** \n+ * Property: backgroundColor \n+ * {String} the background color used by the popup.\n+ */\n+ backgroundColor: \"\",\n \n-});\n+ /** \n+ * Property: opacity \n+ * {float} the opacity of this popup (between 0.0 and 1.0)\n+ */\n+ opacity: \"\",\n \n-/* ======================================================================\n- OpenLayers/Symbolizer/Raster.js\n- ====================================================================== */\n+ /** \n+ * Property: border \n+ * {String} the border size of the popup. (eg 2px)\n+ */\n+ border: \"\",\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: contentDiv \n+ * {DOMElement} a reference to the element that holds the content of\n+ * the div.\n+ */\n+ contentDiv: null,\n \n-/**\n- * @requires OpenLayers/Symbolizer.js\n- */\n+ /** \n+ * Property: groupDiv \n+ * {DOMElement} First and only child of 'div'. The group Div contains the\n+ * 'contentDiv' and the 'closeDiv'.\n+ */\n+ groupDiv: null,\n \n-/**\n- * Class: OpenLayers.Symbolizer.Raster\n- * A symbolizer used to render raster images.\n- */\n-OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, {\n+ /** \n+ * Property: closeDiv\n+ * {DOMElement} the optional closer image\n+ */\n+ closeDiv: null,\n+\n+ /** \n+ * APIProperty: autoSize\n+ * {Boolean} Resize the popup to auto-fit the contents.\n+ * Default is false.\n+ */\n+ autoSize: false,\n \n /**\n- * 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+ * APIProperty: minSize\n+ * {} Minimum size allowed for the popup's contents.\n */\n- initialize: function(config) {\n- OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);\n- },\n+ minSize: null,\n \n- CLASS_NAME: \"OpenLayers.Symbolizer.Raster\"\n+ /**\n+ * APIProperty: maxSize\n+ * {} Maximum size allowed for the popup's contents.\n+ */\n+ maxSize: null,\n \n-});\n-/* ======================================================================\n- OpenLayers/Style2.js\n- ====================================================================== */\n+ /** \n+ * Property: displayClass\n+ * {String} The CSS class of the popup.\n+ */\n+ displayClass: \"olPopup\",\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: contentDisplayClass\n+ * {String} The CSS class of the popup content div.\n+ */\n+ contentDisplayClass: \"olPopupContent\",\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+ * Property: padding \n+ * {int or } An extra opportunity to specify internal \n+ * padding of the content div inside the popup. This was originally\n+ * confused with the css padding as specified in style.css's \n+ * 'olPopupContent' class. We would like to get rid of this altogether,\n+ * except that it does come in handy for the framed and anchoredbubble\n+ * popups, who need to maintain yet another barrier between their \n+ * content and the outer border of the popup itself. \n+ * \n+ * Note that in order to not break API, we must continue to support \n+ * this property being set as an integer. Really, though, we'd like to \n+ * have this specified as a Bounds object so that user can specify\n+ * distinct left, top, right, bottom paddings. With the 3.0 release\n+ * we can make this only a bounds.\n+ */\n+ padding: 0,\n \n-/**\n- * Class: OpenLayers.Style2\n- * This class represents a collection of rules for rendering features.\n- */\n-OpenLayers.Style2 = OpenLayers.Class({\n+ /** \n+ * Property: disableFirefoxOverflowHack\n+ * {Boolean} The hack for overflow in Firefox causes all elements \n+ * to be re-drawn, which causes Flash elements to be \n+ * re-initialized, which is troublesome.\n+ * With this property the hack can be disabled.\n+ */\n+ disableFirefoxOverflowHack: false,\n \n /**\n- * Property: id\n- * {String} A unique id for this session.\n+ * Method: fixPadding\n+ * To be removed in 3.0, this function merely helps us to deal with the \n+ * case where the user may have set an integer value for padding, \n+ * instead of an object.\n */\n- id: null,\n+ fixPadding: function() {\n+ if (typeof this.padding == \"number\") {\n+ this.padding = new OpenLayers.Bounds(\n+ this.padding, this.padding, this.padding, this.padding\n+ );\n+ }\n+ },\n \n /**\n- * APIProperty: name\n- * {String} Style identifier.\n+ * APIProperty: panMapIfOutOfView\n+ * {Boolean} When drawn, pan map such that the entire popup is visible in\n+ * the current viewport (if necessary).\n+ * Default is false.\n */\n- name: null,\n+ panMapIfOutOfView: false,\n \n /**\n- * APIProperty: title\n- * {String} Title of this style.\n+ * APIProperty: keepInMap \n+ * {Boolean} If panMapIfOutOfView is false, and this property is true, \n+ * contrain the popup such that it always fits in the available map\n+ * space. By default, this is not set on the base class. If you are\n+ * creating popups that are near map edges and not allowing pannning,\n+ * and especially if you have a popup which has a\n+ * fixedRelativePosition, setting this to false may be a smart thing to\n+ * do. Subclasses may want to override this setting.\n+ * \n+ * Default is false.\n */\n- title: null,\n+ keepInMap: false,\n \n /**\n- * APIProperty: description\n- * {String} Description of this style.\n+ * APIProperty: closeOnMove\n+ * {Boolean} When map pans, close the popup.\n+ * Default is false.\n */\n- description: null,\n+ closeOnMove: false,\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+ * Property: map \n+ * {} this gets set in Map.js when the popup is added to the map\n */\n- layerName: null,\n+ map: null,\n \n- /**\n- * APIProperty: isDefault\n- * {Boolean}\n+ /** \n+ * Constructor: OpenLayers.Popup\n+ * Create a popup.\n+ * \n+ * Parameters: \n+ * id - {String} a unqiue identifier for this popup. If null is passed\n+ * an identifier will be automatically generated. \n+ * lonlat - {} The position on the map the popup will\n+ * be shown.\n+ * contentSize - {} The size of the content.\n+ * contentHTML - {String} An HTML string to display inside the \n+ * popup.\n+ * closeBox - {Boolean} Whether to display a close box inside\n+ * the popup.\n+ * closeBoxCallback - {Function} Function to be called on closeBox click.\n */\n- isDefault: false,\n+ initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {\n+ if (id == null) {\n+ id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ }\n+\n+ this.id = id;\n+ this.lonlat = lonlat;\n+\n+ this.contentSize = (contentSize != null) ? contentSize :\n+ new OpenLayers.Size(\n+ OpenLayers.Popup.WIDTH,\n+ OpenLayers.Popup.HEIGHT);\n+ if (contentHTML != null) {\n+ this.contentHTML = contentHTML;\n+ }\n+ this.backgroundColor = OpenLayers.Popup.COLOR;\n+ this.opacity = OpenLayers.Popup.OPACITY;\n+ this.border = OpenLayers.Popup.BORDER;\n+\n+ this.div = OpenLayers.Util.createDiv(this.id, null, null,\n+ null, null, null, \"hidden\");\n+ this.div.className = this.displayClass;\n+\n+ var groupDivId = this.id + \"_GroupDiv\";\n+ this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,\n+ null, \"relative\", null,\n+ \"hidden\");\n+\n+ var id = this.div.id + \"_contentDiv\";\n+ this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),\n+ null, \"relative\");\n+ this.contentDiv.className = this.contentDisplayClass;\n+ this.groupDiv.appendChild(this.contentDiv);\n+ this.div.appendChild(this.groupDiv);\n+\n+ if (closeBox) {\n+ this.addCloseBox(closeBoxCallback);\n+ }\n+\n+ this.registerEvents();\n+ },\n \n /** \n- * APIProperty: rules \n- * {Array()} Collection of rendering rules.\n+ * Method: destroy\n+ * nullify references to prevent circular references and memory leaks\n */\n- rules: null,\n+ destroy: function() {\n+\n+ this.id = null;\n+ this.lonlat = null;\n+ this.size = null;\n+ this.contentHTML = null;\n+\n+ this.backgroundColor = null;\n+ this.opacity = null;\n+ this.border = null;\n+\n+ if (this.closeOnMove && this.map) {\n+ this.map.events.unregister(\"movestart\", this, this.hide);\n+ }\n+\n+ this.events.destroy();\n+ this.events = null;\n+\n+ if (this.closeDiv) {\n+ OpenLayers.Event.stopObservingElement(this.closeDiv);\n+ this.groupDiv.removeChild(this.closeDiv);\n+ }\n+ this.closeDiv = null;\n+\n+ this.div.removeChild(this.groupDiv);\n+ this.groupDiv = null;\n+\n+ if (this.map != null) {\n+ this.map.removePopup(this);\n+ }\n+ this.map = null;\n+ this.div = null;\n+\n+ this.autoSize = null;\n+ this.minSize = null;\n+ this.maxSize = null;\n+ this.padding = null;\n+ this.panMapIfOutOfView = null;\n+ },\n \n /** \n- * Constructor: OpenLayers.Style2\n- * Creates a style representing a collection of rendering rules.\n+ * Method: draw\n+ * Constructs the elements that make up the popup.\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+ * px - {} the position the popup in pixels.\n+ * \n * Returns:\n- * {} A new style object.\n+ * {DOMElement} Reference to a div that contains the drawn popup\n */\n- initialize: function(config) {\n- OpenLayers.Util.extend(this, config);\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ draw: function(px) {\n+ if (px == null) {\n+ if ((this.lonlat != null) && (this.map != null)) {\n+ px = this.map.getLayerPxFromLonLat(this.lonlat);\n+ }\n+ }\n+\n+ // this assumes that this.map already exists, which is okay because \n+ // this.draw is only called once the popup has been added to the map.\n+ if (this.closeOnMove) {\n+ this.map.events.register(\"movestart\", this, this.hide);\n+ }\n+\n+ //listen to movestart, moveend to disable overflow (FF bug)\n+ if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {\n+ this.map.events.register(\"movestart\", this, function() {\n+ var style = document.defaultView.getComputedStyle(\n+ this.contentDiv, null\n+ );\n+ var currentOverflow = style.getPropertyValue(\"overflow\");\n+ if (currentOverflow != \"hidden\") {\n+ this.contentDiv._oldOverflow = currentOverflow;\n+ this.contentDiv.style.overflow = \"hidden\";\n+ }\n+ });\n+ this.map.events.register(\"moveend\", this, function() {\n+ var oldOverflow = this.contentDiv._oldOverflow;\n+ if (oldOverflow) {\n+ this.contentDiv.style.overflow = oldOverflow;\n+ this.contentDiv._oldOverflow = null;\n+ }\n+ });\n+ }\n+\n+ this.moveTo(px);\n+ if (!this.autoSize && !this.size) {\n+ this.setSize(this.contentSize);\n+ }\n+ this.setBackgroundColor();\n+ this.setOpacity();\n+ this.setBorder();\n+ this.setContentHTML();\n+\n+ if (this.panMapIfOutOfView) {\n+ this.panIntoView();\n+ }\n+\n+ return this.div;\n },\n \n /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n+ * Method: updatePosition\n+ * if the popup has a lonlat and its map members set, \n+ * then have it move itself to its proper position\n */\n- destroy: function() {\n- for (var i = 0, len = this.rules.length; i < len; i++) {\n- this.rules[i].destroy();\n+ updatePosition: function() {\n+ if ((this.lonlat) && (this.map)) {\n+ var px = this.map.getLayerPxFromLonLat(this.lonlat);\n+ if (px) {\n+ this.moveTo(px);\n+ }\n }\n- delete this.rules;\n },\n \n /**\n- * APIMethod: clone\n- * Clones this style.\n+ * Method: moveTo\n * \n- * Returns:\n- * {} Clone of this style.\n+ * Parameters:\n+ * px - {} the top and left position of the popup div. \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+ moveTo: function(px) {\n+ if ((px != null) && (this.div != null)) {\n+ this.div.style.left = px.x + \"px\";\n+ this.div.style.top = px.y + \"px\";\n }\n- return new OpenLayers.Style2(config);\n },\n \n- CLASS_NAME: \"OpenLayers.Style2\"\n-});\n-/* ======================================================================\n- OpenLayers/Spherical.js\n- ====================================================================== */\n+ /**\n+ * Method: visible\n+ *\n+ * Returns: \n+ * {Boolean} Boolean indicating whether or not the popup is visible\n+ */\n+ visible: function() {\n+ return OpenLayers.Element.visible(this.div);\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 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: toggle\n+ * Toggles visibility of the popup.\n+ */\n+ toggle: function() {\n+ if (this.visible()) {\n+ this.hide();\n+ } else {\n+ this.show();\n+ }\n+ },\n \n-/**\n- * @requires OpenLayers/SingleFile.js\n- */\n+ /**\n+ * Method: show\n+ * Makes the popup visible.\n+ */\n+ show: function() {\n+ this.div.style.display = '';\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+ if (this.panMapIfOutOfView) {\n+ this.panIntoView();\n+ }\n+ },\n \n-OpenLayers.Spherical = OpenLayers.Spherical || {};\n+ /**\n+ * Method: hide\n+ * Makes the popup invisible.\n+ */\n+ hide: function() {\n+ this.div.style.display = 'none';\n+ },\n \n-OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;\n+ /**\n+ * Method: setSize\n+ * Used to adjust the size of the popup. \n+ *\n+ * Parameters:\n+ * contentSize - {} the new size for the popup's \n+ * contents div (in pixels).\n+ */\n+ setSize: function(contentSize) {\n+ this.size = contentSize.clone();\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+ // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n+ // must add that to the desired \"size\". \n+ var contentDivPadding = this.getContentDivPadding();\n+ var wPadding = contentDivPadding.left + contentDivPadding.right;\n+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n \n+ // take into account the popup's 'padding' property\n+ this.fixPadding();\n+ wPadding += this.padding.left + this.padding.right;\n+ hPadding += this.padding.top + this.padding.bottom;\n \n-/**\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/Strategy.js\n- ====================================================================== */\n+ // make extra space for the close div\n+ if (this.closeDiv) {\n+ var closeDivWidth = parseInt(this.closeDiv.style.width);\n+ wPadding += closeDivWidth + contentDivPadding.right;\n+ }\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ //increase size of the main popup div to take into account the \n+ // users's desired padding and close div. \n+ this.size.w += wPadding;\n+ this.size.h += hPadding;\n \n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n+ //now if our browser is IE, we need to actually make the contents \n+ // div itself bigger to take its own padding into effect. this makes \n+ // me want to shoot someone, but so it goes.\n+ if (OpenLayers.BROWSER_NAME == \"msie\") {\n+ this.contentSize.w +=\n+ contentDivPadding.left + contentDivPadding.right;\n+ this.contentSize.h +=\n+ contentDivPadding.bottom + contentDivPadding.top;\n+ }\n \n-/**\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+ if (this.div != null) {\n+ this.div.style.width = this.size.w + \"px\";\n+ this.div.style.height = this.size.h + \"px\";\n+ }\n+ if (this.contentDiv != null) {\n+ this.contentDiv.style.width = contentSize.w + \"px\";\n+ this.contentDiv.style.height = contentSize.h + \"px\";\n+ }\n+ },\n \n /**\n- * Property: layer\n- * {} The layer this strategy belongs to.\n+ * APIMethod: updateSize\n+ * Auto size the popup so that it precisely fits its contents (as \n+ * determined by this.contentDiv.innerHTML). Popup size will, of\n+ * course, be limited by the available space on the current map\n */\n- layer: null,\n+ updateSize: function() {\n+\n+ // determine actual render dimensions of the contents by putting its\n+ // contents into a fake contentDiv (for the CSS) and then measuring it\n+ var preparedHTML = \"
\" +\n+ this.contentDiv.innerHTML +\n+ \"
\";\n+\n+ var containerElement = (this.map) ? this.map.div : document.body;\n+ var realSize = OpenLayers.Util.getRenderedDimensions(\n+ preparedHTML, null, {\n+ displayClass: this.displayClass,\n+ containerElement: containerElement\n+ }\n+ );\n+\n+ // is the \"real\" size of the div is safe to display in our map?\n+ var safeSize = this.getSafeContentSize(realSize);\n+\n+ var newSize = null;\n+ if (safeSize.equals(realSize)) {\n+ //real size of content is small enough to fit on the map, \n+ // so we use real size.\n+ newSize = realSize;\n+\n+ } else {\n+\n+ // make a new 'size' object with the clipped dimensions \n+ // set or null if not clipped.\n+ var fixedSize = {\n+ w: (safeSize.w < realSize.w) ? safeSize.w : null,\n+ h: (safeSize.h < realSize.h) ? safeSize.h : null\n+ };\n+\n+ if (fixedSize.w && fixedSize.h) {\n+ //content is too big in both directions, so we will use \n+ // max popup size (safeSize), knowing well that it will \n+ // overflow both ways. \n+ newSize = safeSize;\n+ } else {\n+ //content is clipped in only one direction, so we need to \n+ // run getRenderedDimensions() again with a fixed dimension\n+ var clippedSize = OpenLayers.Util.getRenderedDimensions(\n+ preparedHTML, fixedSize, {\n+ displayClass: this.contentDisplayClass,\n+ containerElement: containerElement\n+ }\n+ );\n+\n+ //if the clipped size is still the same as the safeSize, \n+ // that means that our content must be fixed in the \n+ // offending direction. If overflow is 'auto', this means \n+ // we are going to have a scrollbar for sure, so we must \n+ // adjust for that.\n+ //\n+ var currentOverflow = OpenLayers.Element.getStyle(\n+ this.contentDiv, \"overflow\"\n+ );\n+ if ((currentOverflow != \"hidden\") &&\n+ (clippedSize.equals(safeSize))) {\n+ var scrollBar = OpenLayers.Util.getScrollbarWidth();\n+ if (fixedSize.w) {\n+ clippedSize.h += scrollBar;\n+ } else {\n+ clippedSize.w += scrollBar;\n+ }\n+ }\n+\n+ newSize = this.getSafeContentSize(clippedSize);\n+ }\n+ }\n+ this.setSize(newSize);\n+ },\n \n /**\n- * Property: options\n- * {Object} Any options sent to the constructor.\n+ * Method: setBackgroundColor\n+ * Sets the background color of the popup.\n+ *\n+ * Parameters:\n+ * color - {String} the background color. eg \"#FFBBBB\"\n */\n- options: null,\n+ setBackgroundColor: function(color) {\n+ if (color != undefined) {\n+ this.backgroundColor = color;\n+ }\n \n- /** \n- * Property: active \n- * {Boolean} The control is active.\n- */\n- active: null,\n+ if (this.div != null) {\n+ this.div.style.backgroundColor = this.backgroundColor;\n+ }\n+ },\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+ * Method: setOpacity\n+ * Sets the opacity of the popup.\n+ * \n+ * Parameters:\n+ * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). \n */\n- autoActivate: true,\n+ setOpacity: function(opacity) {\n+ if (opacity != undefined) {\n+ this.opacity = opacity;\n+ }\n+\n+ if (this.div != null) {\n+ // for Mozilla and Safari\n+ this.div.style.opacity = this.opacity;\n+\n+ // for IE\n+ this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')';\n+ }\n+ },\n \n /**\n- * 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+ * Method: setBorder\n+ * Sets the border style of the popup.\n+ *\n+ * Parameters:\n+ * border - {String} The border style value. eg 2px \n */\n- autoDestroy: true,\n+ setBorder: function(border) {\n+ if (border != undefined) {\n+ this.border = border;\n+ }\n+\n+ if (this.div != null) {\n+ this.div.style.border = this.border;\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Strategy\n- * Abstract class for vector strategies. Create instances of a subclass.\n+ * Method: setContentHTML\n+ * Allows the user to set the HTML content of the popup.\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * contentHTML - {String} HTML for the div.\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+ setContentHTML: function(contentHTML) {\n+\n+ if (contentHTML != null) {\n+ this.contentHTML = contentHTML;\n+ }\n+\n+ if ((this.contentDiv != null) &&\n+ (this.contentHTML != null) &&\n+ (this.contentHTML != this.contentDiv.innerHTML)) {\n+\n+ this.contentDiv.innerHTML = this.contentHTML;\n+\n+ if (this.autoSize) {\n+\n+ //if popup has images, listen for when they finish\n+ // loading and resize accordingly\n+ this.registerImageListeners();\n+\n+ //auto size the popup to its current contents\n+ this.updateSize();\n+ }\n+ }\n+\n },\n \n /**\n- * APIMethod: destroy\n- * Clean up the strategy.\n+ * Method: registerImageListeners\n+ * Called when an image contained by the popup loaded. this function\n+ * updates the popup size, then unregisters the image load listener.\n */\n- destroy: function() {\n- this.deactivate();\n- this.layer = null;\n- this.options = null;\n+ registerImageListeners: function() {\n+\n+ // As the images load, this function will call updateSize() to \n+ // resize the popup to fit the content div (which presumably is now\n+ // bigger than when the image was not loaded).\n+ // \n+ // If the 'panMapIfOutOfView' property is set, we will pan the newly\n+ // resized popup back into view.\n+ // \n+ // Note that this function, when called, will have 'popup' and \n+ // 'img' properties in the context.\n+ //\n+ var onImgLoad = function() {\n+ if (this.popup.id === null) { // this.popup has been destroyed!\n+ return;\n+ }\n+ this.popup.updateSize();\n+\n+ if (this.popup.visible() && this.popup.panMapIfOutOfView) {\n+ this.popup.panIntoView();\n+ }\n+\n+ OpenLayers.Event.stopObserving(\n+ this.img, \"load\", this.img._onImgLoad\n+ );\n+\n+ };\n+\n+ //cycle through the images and if their size is 0x0, that means that \n+ // they haven't been loaded yet, so we attach the listener, which \n+ // will fire when the images finish loading and will resize the \n+ // popup accordingly to its new size.\n+ var images = this.contentDiv.getElementsByTagName(\"img\");\n+ for (var i = 0, len = images.length; i < len; i++) {\n+ var img = images[i];\n+ if (img.width == 0 || img.height == 0) {\n+\n+ var context = {\n+ 'popup': this,\n+ 'img': img\n+ };\n+\n+ //expando this function to the image itself before registering\n+ // it. This way we can easily and properly unregister it.\n+ img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);\n+\n+ OpenLayers.Event.observe(img, 'load', img._onImgLoad);\n+ }\n+ }\n },\n \n /**\n- * Method: setLayer\n- * Called to set the property.\n- *\n+ * APIMethod: getSafeContentSize\n+ * \n * Parameters:\n- * layer - {}\n+ * size - {} Desired size to make the popup.\n+ * \n+ * Returns:\n+ * {} A size to make the popup which is neither smaller\n+ * than the specified minimum size, nor bigger than the maximum \n+ * size (which is calculated relative to the size of the viewport).\n */\n- setLayer: function(layer) {\n- this.layer = layer;\n+ getSafeContentSize: function(size) {\n+\n+ var safeContentSize = size.clone();\n+\n+ // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n+ // must add that to the desired \"size\". \n+ var contentDivPadding = this.getContentDivPadding();\n+ var wPadding = contentDivPadding.left + contentDivPadding.right;\n+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n+\n+ // take into account the popup's 'padding' property\n+ this.fixPadding();\n+ wPadding += this.padding.left + this.padding.right;\n+ hPadding += this.padding.top + this.padding.bottom;\n+\n+ if (this.closeDiv) {\n+ var closeDivWidth = parseInt(this.closeDiv.style.width);\n+ wPadding += closeDivWidth + contentDivPadding.right;\n+ }\n+\n+ // prevent the popup from being smaller than a specified minimal size\n+ if (this.minSize) {\n+ safeContentSize.w = Math.max(safeContentSize.w,\n+ (this.minSize.w - wPadding));\n+ safeContentSize.h = Math.max(safeContentSize.h,\n+ (this.minSize.h - hPadding));\n+ }\n+\n+ // prevent the popup from being bigger than a specified maximum size\n+ if (this.maxSize) {\n+ safeContentSize.w = Math.min(safeContentSize.w,\n+ (this.maxSize.w - wPadding));\n+ safeContentSize.h = Math.min(safeContentSize.h,\n+ (this.maxSize.h - hPadding));\n+ }\n+\n+ //make sure the desired size to set doesn't result in a popup that \n+ // is bigger than the map's viewport.\n+ //\n+ if (this.map && this.map.size) {\n+\n+ var extraX = 0,\n+ extraY = 0;\n+ if (this.keepInMap && !this.panMapIfOutOfView) {\n+ var px = this.map.getPixelFromLonLat(this.lonlat);\n+ switch (this.relativePosition) {\n+ case \"tr\":\n+ extraX = px.x;\n+ extraY = this.map.size.h - px.y;\n+ break;\n+ case \"tl\":\n+ extraX = this.map.size.w - px.x;\n+ extraY = this.map.size.h - px.y;\n+ break;\n+ case \"bl\":\n+ extraX = this.map.size.w - px.x;\n+ extraY = px.y;\n+ break;\n+ case \"br\":\n+ extraX = px.x;\n+ extraY = px.y;\n+ break;\n+ default:\n+ extraX = px.x;\n+ extraY = this.map.size.h - px.y;\n+ break;\n+ }\n+ }\n+\n+ var maxY = this.map.size.h -\n+ this.map.paddingForPopups.top -\n+ this.map.paddingForPopups.bottom -\n+ hPadding - extraY;\n+\n+ var maxX = this.map.size.w -\n+ this.map.paddingForPopups.left -\n+ this.map.paddingForPopups.right -\n+ wPadding - extraX;\n+\n+ safeContentSize.w = Math.min(safeContentSize.w, maxX);\n+ safeContentSize.h = Math.min(safeContentSize.h, maxY);\n+ }\n+\n+ return safeContentSize;\n },\n \n /**\n- * Method: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n+ * Method: getContentDivPadding\n+ * Glorious, oh glorious hack in order to determine the css 'padding' of \n+ * the contentDiv. IE/Opera return null here unless we actually add the \n+ * popup's main 'div' element (which contains contentDiv) to the DOM. \n+ * So we make it invisible and then add it to the document temporarily. \n+ *\n+ * Once we've taken the padding readings we need, we then remove it \n+ * from the DOM (it will actually get added to the DOM in \n+ * Map.js's addPopup)\n *\n * Returns:\n- * {Boolean} True if the strategy was successfully activated or false if\n- * the strategy was already active.\n+ * {}\n */\n- activate: function() {\n- if (!this.active) {\n- this.active = true;\n- return true;\n+ getContentDivPadding: function() {\n+\n+ //use cached value if we have it\n+ var contentDivPadding = this._contentDivPadding;\n+ if (!contentDivPadding) {\n+\n+ if (this.div.parentNode == null) {\n+ //make the div invisible and add it to the page \n+ this.div.style.display = \"none\";\n+ document.body.appendChild(this.div);\n+ }\n+\n+ //read the padding settings from css, put them in an OL.Bounds \n+ contentDivPadding = new OpenLayers.Bounds(\n+ OpenLayers.Element.getStyle(this.contentDiv, \"padding-left\"),\n+ OpenLayers.Element.getStyle(this.contentDiv, \"padding-bottom\"),\n+ OpenLayers.Element.getStyle(this.contentDiv, \"padding-right\"),\n+ OpenLayers.Element.getStyle(this.contentDiv, \"padding-top\")\n+ );\n+\n+ //cache the value\n+ this._contentDivPadding = contentDivPadding;\n+\n+ if (this.div.parentNode == document.body) {\n+ //remove the div from the page and make it visible again\n+ document.body.removeChild(this.div);\n+ this.div.style.display = \"\";\n+ }\n }\n- return false;\n+ return contentDivPadding;\n },\n \n /**\n- * Method: deactivate\n- * Deactivate the strategy. Unregister any listeners, do appropriate\n- * tear-down.\n+ * Method: addCloseBox\n+ * \n+ * Parameters:\n+ * callback - {Function} The callback to be called when the close button\n+ * is clicked.\n+ */\n+ addCloseBox: function(callback) {\n+\n+ this.closeDiv = OpenLayers.Util.createDiv(\n+ this.id + \"_close\", null, {\n+ w: 17,\n+ h: 17\n+ }\n+ );\n+ this.closeDiv.className = \"olPopupCloseBox\";\n+\n+ // use the content div's css padding to determine if we should\n+ // padd the close div\n+ var contentDivPadding = this.getContentDivPadding();\n+\n+ this.closeDiv.style.right = contentDivPadding.right + \"px\";\n+ this.closeDiv.style.top = contentDivPadding.top + \"px\";\n+ this.groupDiv.appendChild(this.closeDiv);\n+\n+ var closePopup = callback || function(e) {\n+ this.hide();\n+ OpenLayers.Event.stop(e);\n+ };\n+ OpenLayers.Event.observe(this.closeDiv, \"touchend\",\n+ OpenLayers.Function.bindAsEventListener(closePopup, this));\n+ OpenLayers.Event.observe(this.closeDiv, \"click\",\n+ OpenLayers.Function.bindAsEventListener(closePopup, this));\n+ },\n+\n+ /**\n+ * Method: panIntoView\n+ * Pans the map such that the popup is totaly viewable (if necessary)\n+ */\n+ panIntoView: function() {\n+\n+ var mapSize = this.map.getSize();\n+\n+ //start with the top left corner of the popup, in px, \n+ // relative to the viewport\n+ var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(\n+ parseInt(this.div.style.left),\n+ parseInt(this.div.style.top)\n+ ));\n+ var newTL = origTL.clone();\n+\n+ //new left (compare to margins, using this.size to calculate right)\n+ if (origTL.x < this.map.paddingForPopups.left) {\n+ newTL.x = this.map.paddingForPopups.left;\n+ } else\n+ if ((origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {\n+ newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;\n+ }\n+\n+ //new top (compare to margins, using this.size to calculate bottom)\n+ if (origTL.y < this.map.paddingForPopups.top) {\n+ newTL.y = this.map.paddingForPopups.top;\n+ } else\n+ if ((origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {\n+ newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;\n+ }\n+\n+ var dx = origTL.x - newTL.x;\n+ var dy = origTL.y - newTL.y;\n+\n+ this.map.pan(dx, dy);\n+ },\n+\n+ /** \n+ * Method: registerEvents\n+ * Registers events on the popup.\n *\n- * Returns:\n- * {Boolean} True if the strategy was successfully deactivated or false if\n- * the strategy was already inactive.\n+ * Do this in a separate function so that subclasses can \n+ * choose to override it if they wish to deal differently\n+ * with mouse events\n+ * \n+ * Note in the following handler functions that some special\n+ * care is needed to deal correctly with mousing and popups. \n+ * \n+ * Because the user might select the zoom-rectangle option and\n+ * then drag it over a popup, we need a safe way to allow the\n+ * mousemove and mouseup events to pass through the popup when\n+ * they are initiated from outside. The same procedure is needed for\n+ * touchmove and touchend events.\n+ * \n+ * Otherwise, we want to essentially kill the event propagation\n+ * for all other events, though we have to do so carefully, \n+ * without disabling basic html functionality, like clicking on \n+ * hyperlinks or drag-selecting text.\n */\n- deactivate: function() {\n- if (this.active) {\n- this.active = false;\n- return true;\n+ registerEvents: function() {\n+ this.events = new OpenLayers.Events(this, this.div, null, true);\n+\n+ function onTouchstart(evt) {\n+ OpenLayers.Event.stop(evt, true);\n }\n- return false;\n+ this.events.on({\n+ \"mousedown\": this.onmousedown,\n+ \"mousemove\": this.onmousemove,\n+ \"mouseup\": this.onmouseup,\n+ \"click\": this.onclick,\n+ \"mouseout\": this.onmouseout,\n+ \"dblclick\": this.ondblclick,\n+ \"touchstart\": onTouchstart,\n+ scope: this\n+ });\n+\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy\"\n+ /** \n+ * Method: onmousedown \n+ * When mouse goes down within the popup, make a note of\n+ * it locally, and then do not propagate the mousedown \n+ * (but do so safely so that user can select text inside)\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ onmousedown: function(evt) {\n+ this.mousedown = true;\n+ OpenLayers.Event.stop(evt, true);\n+ },\n+\n+ /** \n+ * Method: onmousemove\n+ * If the drag was started within the popup, then \n+ * do not propagate the mousemove (but do so safely\n+ * so that user can select text inside)\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ onmousemove: function(evt) {\n+ if (this.mousedown) {\n+ OpenLayers.Event.stop(evt, true);\n+ }\n+ },\n+\n+ /** \n+ * Method: onmouseup\n+ * When mouse comes up within the popup, after going down \n+ * in it, reset the flag, and then (once again) do not \n+ * propagate the event, but do so safely so that user can \n+ * select text inside\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ onmouseup: function(evt) {\n+ if (this.mousedown) {\n+ this.mousedown = false;\n+ OpenLayers.Event.stop(evt, true);\n+ }\n+ },\n+\n+ /**\n+ * Method: onclick\n+ * Ignore clicks, but allowing default browser handling\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ onclick: function(evt) {\n+ OpenLayers.Event.stop(evt, true);\n+ },\n+\n+ /** \n+ * Method: onmouseout\n+ * When mouse goes out of the popup set the flag to false so that\n+ * if they let go and then drag back in, we won't be confused.\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ onmouseout: function(evt) {\n+ this.mousedown = false;\n+ },\n+\n+ /** \n+ * Method: ondblclick\n+ * Ignore double-clicks, but allowing default browser handling\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ ondblclick: function(evt) {\n+ OpenLayers.Event.stop(evt, true);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Popup\"\n });\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/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@@ -29908,455 +32183,342 @@\n \n CLASS_NAME: \"OpenLayers.Protocol.Response\"\n });\n \n OpenLayers.Protocol.Response.SUCCESS = 1;\n OpenLayers.Protocol.Response.FAILURE = 0;\n /* ======================================================================\n- OpenLayers/Renderer.js\n+ OpenLayers/Handler.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Events.js\n */\n \n /**\n- * Class: OpenLayers.Renderer \n- * This is the base class for all renderers.\n- *\n- * This is based on a merger code written by Paul Spencer and Bertil Chapuis.\n- * It is largely composed of virtual functions that are to be implemented\n- * in technology-specific subclasses, but there is some generic code too.\n- * \n- * The functions that *are* implemented here merely deal with the maintenance\n- * of the size and extent variables, as well as the cached 'resolution' \n- * value. \n- * \n- * A note to the user that all subclasses should use getResolution() instead\n- * of directly accessing this.resolution in order to correctly use the \n- * cacheing system.\n+ * Class: OpenLayers.Handler\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.Renderer = OpenLayers.Class({\n-\n- /** \n- * Property: container\n- * {DOMElement} \n- */\n- container: null,\n+OpenLayers.Handler = OpenLayers.Class({\n \n /**\n- * Property: root\n- * {DOMElement}\n+ * Property: id\n+ * {String}\n */\n- root: null,\n+ id: null,\n \n- /** \n- * Property: extent\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- extent: null,\n+ control: 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+ * Property: map\n+ * {}\n */\n- locked: false,\n+ map: null,\n \n- /** \n- * Property: size\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- size: null,\n+ keyMask: null,\n \n /**\n- * Property: resolution\n- * {Float} cache of current map resolution\n+ * Property: active\n+ * {Boolean}\n */\n- resolution: null,\n+ active: false,\n \n /**\n- * Property: map \n- * {} Reference to the map -- this is set in Vector's setMap()\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- map: null,\n+ evt: 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: 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- featureDx: 0,\n+ touch: false,\n \n /**\n- * Constructor: OpenLayers.Renderer \n+ * Constructor: OpenLayers.Handler\n+ * Construct a handler.\n *\n * Parameters:\n- * containerID - {} \n- * options - {Object} options for this renderer. See sublcasses for\n- * supported options.\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(containerID, options) {\n- this.container = OpenLayers.Util.getElement(containerID);\n+ initialize: function(control, callbacks, options) {\n OpenLayers.Util.extend(this, options);\n- },\n+ this.control = control;\n+ this.callbacks = callbacks;\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+ 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- * APIMethod: supported\n- * This should be overridden by specific subclasses\n- * \n- * Returns:\n- * {Boolean} Whether or not the browser supports the renderer class\n+ * Method: setMap\n */\n- supported: function() {\n- return false;\n+ setMap: function(map) {\n+ this.map = map;\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+ * 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} 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} The keyMask matches the keys down on an 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+ checkModifiers: function(evt) {\n+ if (this.keyMask == null) {\n+ return true;\n }\n- return true;\n- },\n+ /* calculate the keyboard modifier mask for this event */\n+ var keyModifiers =\n+ (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |\n+ (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |\n+ (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |\n+ (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n \n- /**\n- * 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+ /* 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- * Method: getResolution\n- * Uses cached copy of resolution if available to minimize computing\n+ /**\n+ * APIMethod: activate\n+ * Turn on the handler. Returns false if the handler was already active.\n * \n- * Returns:\n- * {Float} The current map's resolution\n+ * Returns: \n+ * {Boolean} The handler was activated.\n */\n- getResolution: function() {\n- this.resolution = this.resolution || this.map.getResolution();\n- return this.resolution;\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- * 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+ * APIMethod: deactivate\n+ * Turn off the handler. Returns false if the handler was already inactive.\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} The handler was deactivated.\n */\n- drawFeature: function(feature, style) {\n- if (style == null) {\n- style = feature.style;\n+ deactivate: function() {\n+ if (!this.active) {\n+ return false;\n }\n- if (feature.geometry) {\n- var bounds = feature.geometry.getBounds();\n- if (bounds) {\n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent();\n- }\n- if (!bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n- })) {\n- style = {\n- display: \"none\"\n- };\n- } else {\n- this.calculateFeatureDx(bounds, worldBounds);\n- }\n- var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n- if (style.display != \"none\" && style.label && rendered !== false) {\n-\n- 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+ // 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: 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: 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- 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+ 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: 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+ * Method: callback\n+ * Trigger the control's named callback with the given arguments\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+ * 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- 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+ callback: function(name, args) {\n+ if (name && this.callbacks[name]) {\n+ this.callbacks[name].apply(this.control, args);\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+ * Method: register\n+ * register an event on the map\n */\n- eraseGeometry: function(geometry, featureId) {},\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: 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: unregister\n+ * unregister an event from the map\n */\n- moveRoot: function(renderer) {},\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: getRenderLayerId\n- * Gets the layer that this renderer's output appears on. If moveRoot was\n- * used, this will be different from the id of the layer containing the\n- * features rendered by this renderer.\n- * \n- * Returns:\n- * {String} the id of the output layer.\n+ * Method: 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- getRenderLayerId: function() {\n- return this.container.id;\n+ setEvent: function(evt) {\n+ this.evt = evt;\n+ return true;\n },\n \n /**\n- * Method: applyDefaultSymbolizer\n- * \n- * Parameters:\n- * symbolizer - {Object}\n- * \n- * Returns:\n- * {Object}\n+ * Method: destroy\n+ * Deconstruct the handler.\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+ 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.Renderer\"\n+ CLASS_NAME: \"OpenLayers.Handler\"\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+ * Constant: OpenLayers.Handler.MOD_NONE\n+ * If set as the , returns false if any key is down.\n */\n-OpenLayers.Renderer.defaultSymbolizer = {\n- fillColor: \"#000000\",\n- strokeColor: \"#000000\",\n- strokeWidth: 2,\n- fillOpacity: 1,\n- strokeOpacity: 1,\n- pointRadius: 0,\n- labelAlign: 'cm'\n-};\n+OpenLayers.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.Renderer.symbol\n- * Coordinate arrays for well known (named) symbols.\n+ * Constant: OpenLayers.Handler.MOD_ALT\n+ * If set as the , returns false if Alt is down.\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+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@@ -30725,3857 +32887,4213 @@\n OpenLayers.Control.TYPE_TOGGLE = 2;\n \n /**\n * Constant: OpenLayers.Control.TYPE_TOOL\n */\n OpenLayers.Control.TYPE_TOOL = 3;\n /* ======================================================================\n- OpenLayers/Format/WPSDescribeProcess.js\n+ OpenLayers/Spherical.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ * @requires OpenLayers/SingleFile.js\n */\n \n /**\n- * Class: OpenLayers.Format.WPSDescribeProcess\n- * Read WPS DescribeProcess responses. \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- * Inherits from:\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-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+OpenLayers.Spherical = OpenLayers.Spherical || {};\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+OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;\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+ * 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- CLASS_NAME: \"OpenLayers.Format.WPSDescribeProcess\"\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/WPSClient.js\n+ OpenLayers/Renderer/Elements.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/SingleFile.js\n+ * @requires OpenLayers/Renderer.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+ * Class: OpenLayers.ElementsIndexer\n+ * This class takes care of figuring out which order elements should be\n+ * placed in the DOM based on given indexing methods. \n */\n+OpenLayers.ElementsIndexer = OpenLayers.Class({\n \n-/**\n- * 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+ * Property: maxZIndex\n+ * {Integer} This is the largest-most z-index value for a node\n+ * contained within the indexer.\n+ */\n+ maxZIndex: null,\n \n /**\n- * Property: servers\n- * {Object} Service metadata, keyed by a local identifier.\n- *\n- * Properties:\n- * url - {String} the url of the server\n- * version - {String} WPS version of the server\n- * processDescription - {Object} Cache of raw DescribeProcess\n- * responses, keyed by process identifier.\n+ * Property: order\n+ * {Array} This is an array of node id's stored in the\n+ * order that they should show up on screen. Id's higher up in the\n+ * array (higher array index) represent nodes with higher z-indeces.\n */\n- servers: null,\n+ order: 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+ * Property: indices\n+ * {Object} This is a hash that maps node ids to their z-index value\n+ * stored in the indexer. This is done to make finding a nodes z-index \n+ * value O(1).\n */\n- version: '1.0.0',\n+ indices: null,\n \n /**\n- * Property: lazy\n- * {Boolean} Should the DescribeProcess be deferred until a process is\n- * fully configured? Default is false.\n+ * Property: compare\n+ * {Function} This is the function used to determine placement of\n+ * of a new node within the indexer. If null, this defaults to to\n+ * the Z_ORDER_DRAWING_ORDER comparison method.\n */\n- lazy: false,\n+ compare: null,\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+ * APIMethod: initialize\n+ * Create a new indexer with \n+ * \n+ * Parameters:\n+ * yOrdering - {Boolean} Whether to use y-ordering.\n */\n- events: null,\n+ initialize: function(yOrdering) {\n+\n+ this.compare = yOrdering ?\n+ OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :\n+ OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;\n+\n+ this.clear();\n+ },\n \n /**\n- * Constructor: OpenLayers.WPSClient\n- *\n+ * APIMethod: insert\n+ * Insert a new node into the indexer. In order to find the correct \n+ * positioning for the node to be inserted, this method uses a binary \n+ * search. This makes inserting O(log(n)). \n+ * \n * Parameters:\n- * 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+ * newNode - {DOMElement} The new node to be inserted.\n+ * \n+ * Returns\n+ * {DOMElement} the node before which we should insert our newNode, or\n+ * null if newNode can just be appended.\n+ */\n+ insert: function(newNode) {\n+ // If the node is known to the indexer, remove it so we can\n+ // recalculate where it should go.\n+ if (this.exists(newNode)) {\n+ this.remove(newNode);\n+ }\n+\n+ var nodeId = newNode.id;\n+\n+ this.determineZIndex(newNode);\n+\n+ var leftIndex = -1;\n+ var rightIndex = this.order.length;\n+ var middle;\n+\n+ while (rightIndex - leftIndex > 1) {\n+ middle = parseInt((leftIndex + rightIndex) / 2);\n+\n+ var placement = this.compare(this, newNode,\n+ OpenLayers.Util.getElement(this.order[middle]));\n+\n+ if (placement > 0) {\n+ leftIndex = middle;\n+ } else {\n+ rightIndex = middle;\n+ }\n+ }\n+\n+ this.order.splice(rightIndex, 0, nodeId);\n+ this.indices[nodeId] = this.getZIndex(newNode);\n+\n+ // If the new node should be before another in the index\n+ // order, return the node before which we have to insert the new one;\n+ // else, return null to indicate that the new node can be appended.\n+ return this.getNextElement(rightIndex);\n+ },\n+\n+ /**\n+ * APIMethod: remove\n+ * \n+ * Parameters:\n+ * node - {DOMElement} The node to be removed.\n+ */\n+ remove: function(node) {\n+ var nodeId = node.id;\n+ var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);\n+ if (arrayIndex >= 0) {\n+ // Remove it from the order array, as well as deleting the node\n+ // from the indeces hash.\n+ this.order.splice(arrayIndex, 1);\n+ delete this.indices[nodeId];\n+\n+ // Reset the maxium z-index based on the last item in the \n+ // order array.\n+ if (this.order.length > 0) {\n+ var lastId = this.order[this.order.length - 1];\n+ this.maxZIndex = this.indices[lastId];\n+ } else {\n+ this.maxZIndex = 0;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: clear\n+ */\n+ clear: function() {\n+ this.order = [];\n+ this.indices = {};\n+ this.maxZIndex = 0;\n+ },\n+\n+ /**\n+ * APIMethod: exists\n *\n- * (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+ * Parameters:\n+ * node - {DOMElement} The node to test for existence.\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+ * Returns:\n+ * {Boolean} Whether or not the node exists in the indexer?\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+ exists: function(node) {\n+ return (this.indices[node.id] != null);\n+ },\n+\n+ /**\n+ * APIMethod: getZIndex\n+ * Get the z-index value for the current node from the node data itself.\n+ * \n+ * Parameters:\n+ * node - {DOMElement} The node whose z-index to get.\n+ * \n+ * Returns:\n+ * {Integer} The z-index value for the specified node (from the node \n+ * data itself).\n+ */\n+ getZIndex: function(node) {\n+ return node._style.graphicZIndex;\n+ },\n+\n+ /**\n+ * Method: determineZIndex\n+ * Determine the z-index for the current node if there isn't one, \n+ * and set the maximum value if we've found a new maximum.\n+ * \n+ * Parameters:\n+ * node - {DOMElement} \n+ */\n+ determineZIndex: function(node) {\n+ var zIndex = node._style.graphicZIndex;\n+\n+ // Everything must have a zIndex. If none is specified,\n+ // this means the user *must* (hint: assumption) want this\n+ // node to succomb to drawing order. To enforce drawing order\n+ // over all indexing methods, we'll create a new z-index that's\n+ // greater than any currently in the indexer.\n+ if (zIndex == null) {\n+ zIndex = this.maxZIndex;\n+ node._style.graphicZIndex = zIndex;\n+ } else if (zIndex > this.maxZIndex) {\n+ this.maxZIndex = zIndex;\n }\n },\n \n /**\n- * APIMethod: 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+ * APIMethod: getNextElement\n+ * Get the next element in the order stack.\n+ * \n * Parameters:\n- * options - {Object} Options for the execute operation.\n+ * index - {Integer} The index of the current node in this.order.\n+ * \n+ * Returns:\n+ * {DOMElement} the node following the index passed in, or\n+ * null.\n+ */\n+ getNextElement: function(index) {\n+ var nextIndex = index + 1;\n+ if (nextIndex < this.order.length) {\n+ var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);\n+ if (nextElement == undefined) {\n+ nextElement = this.getNextElement(nextIndex);\n+ }\n+ return nextElement;\n+ } else {\n+ return null;\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.ElementsIndexer\"\n+});\n+\n+/**\n+ * Namespace: OpenLayers.ElementsIndexer.IndexingMethods\n+ * These are the compare methods for figuring out where a new node should be \n+ * placed within the indexer. These methods are very similar to general \n+ * sorting methods in that they return -1, 0, and 1 to specify the \n+ * direction in which new nodes fall in the ordering.\n+ */\n+OpenLayers.ElementsIndexer.IndexingMethods = {\n+\n+ /**\n+ * Method: Z_ORDER\n+ * This compare method is used by other comparison methods.\n+ * It can be used individually for ordering, but is not recommended,\n+ * because it doesn't subscribe to drawing order.\n+ * \n+ * Parameters:\n+ * indexer - {}\n+ * newNode - {DOMElement}\n+ * nextNode - {DOMElement}\n+ * \n+ * Returns:\n+ * {Integer}\n+ */\n+ Z_ORDER: function(indexer, newNode, nextNode) {\n+ var newZIndex = indexer.getZIndex(newNode);\n+\n+ var returnVal = 0;\n+ if (nextNode) {\n+ var nextZIndex = indexer.getZIndex(nextNode);\n+ returnVal = newZIndex - nextZIndex;\n+ }\n+\n+ return returnVal;\n+ },\n+\n+ /**\n+ * APIMethod: Z_ORDER_DRAWING_ORDER\n+ * This method orders nodes by their z-index, but does so in a way\n+ * that, if there are other nodes with the same z-index, the newest \n+ * drawn will be the front most within that z-index. This is the \n+ * default indexing method.\n+ * \n+ * Parameters:\n+ * indexer - {}\n+ * newNode - {DOMElement}\n+ * nextNode - {DOMElement}\n+ * \n+ * Returns:\n+ * {Integer}\n+ */\n+ Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {\n+ var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(\n+ indexer,\n+ newNode,\n+ nextNode\n+ );\n+\n+ // Make Z_ORDER subscribe to drawing order by pushing it above\n+ // all of the other nodes with the same z-index.\n+ if (nextNode && returnVal == 0) {\n+ returnVal = 1;\n+ }\n+\n+ return returnVal;\n+ },\n+\n+ /**\n+ * APIMethod: Z_ORDER_Y_ORDER\n+ * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it\n+ * best describes which ordering methods have precedence (though, the \n+ * name would be too long). This method orders nodes by their z-index, \n+ * but does so in a way that, if there are other nodes with the same \n+ * z-index, the nodes with the lower y position will be \"closer\" than \n+ * those with a higher y position. If two nodes have the exact same y \n+ * position, however, then this method will revert to using drawing \n+ * order to decide placement.\n+ * \n+ * Parameters:\n+ * indexer - {}\n+ * newNode - {DOMElement}\n+ * nextNode - {DOMElement}\n+ * \n+ * Returns:\n+ * {Integer}\n+ */\n+ Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {\n+ var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(\n+ indexer,\n+ newNode,\n+ nextNode\n+ );\n+\n+ if (nextNode && returnVal === 0) {\n+ var result = nextNode._boundsBottom - newNode._boundsBottom;\n+ returnVal = (result === 0) ? 1 : result;\n+ }\n+\n+ return returnVal;\n+ }\n+};\n+\n+/**\n+ * Class: OpenLayers.Renderer.Elements\n+ * This is another virtual class in that it should never be instantiated by \n+ * itself as a Renderer. It exists because there is *tons* of shared \n+ * functionality between different vector libraries which use nodes/elements\n+ * as a base for rendering vectors. \n+ * \n+ * The highlevel bits of code that are implemented here are the adding and \n+ * removing of geometries, which is essentially the same for any \n+ * element-based renderer. The details of creating each node and drawing the\n+ * paths are of course different, but the machinery is the same. \n+ * \n+ * Inherits:\n+ * - \n+ */\n+OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {\n+\n+ /**\n+ * Property: rendererRoot\n+ * {DOMElement}\n+ */\n+ rendererRoot: null,\n+\n+ /**\n+ * Property: root\n+ * {DOMElement}\n+ */\n+ root: null,\n+\n+ /**\n+ * Property: vectorRoot\n+ * {DOMElement}\n+ */\n+ vectorRoot: null,\n+\n+ /**\n+ * Property: textRoot\n+ * {DOMElement}\n+ */\n+ textRoot: null,\n+\n+ /**\n+ * Property: xmlns\n+ * {String}\n+ */\n+ xmlns: null,\n+\n+ /**\n+ * Property: xOffset\n+ * {Number} Offset to apply to the renderer viewport translation in x\n+ * direction. If the renderer extent's center is on the right of the\n+ * dateline (i.e. exceeds the world bounds), we shift the viewport to the\n+ * left by one world width. This avoids that features disappear from the\n+ * map viewport. Because our dateline handling logic in other places\n+ * ensures that extents crossing the dateline always have a center\n+ * exceeding the world bounds on the left, we need this offset to make sure\n+ * that the same is true for the renderer extent in pixel space as well.\n+ */\n+ xOffset: 0,\n+\n+ /**\n+ * Property: rightOfDateLine\n+ * {Boolean} Keeps track of the location of the map extent relative to the\n+ * date line. The method compares this value (which is the one\n+ * from the previous call) with the current position of the map\n+ * extent relative to the date line and updates the xOffset when the extent\n+ * has moved from one side of the date line to the other.\n+ */\n+\n+ /**\n+ * Property: Indexer\n+ * {} An instance of OpenLayers.ElementsIndexer \n+ * created upon initialization if the zIndexing or yOrdering options\n+ * passed to this renderer's constructor are set to true.\n+ */\n+ indexer: null,\n+\n+ /**\n+ * Constant: BACKGROUND_ID_SUFFIX\n+ * {String}\n+ */\n+ BACKGROUND_ID_SUFFIX: \"_background\",\n+\n+ /**\n+ * Constant: LABEL_ID_SUFFIX\n+ * {String}\n+ */\n+ LABEL_ID_SUFFIX: \"_label\",\n+\n+ /**\n+ * Constant: LABEL_OUTLINE_SUFFIX\n+ * {String}\n+ */\n+ LABEL_OUTLINE_SUFFIX: \"_outline\",\n+\n+ /**\n+ * Constructor: OpenLayers.Renderer.Elements\n+ * \n+ * Parameters:\n+ * containerID - {String}\n+ * options - {Object} options for this renderer. \n *\n- * 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+ * Supported options are:\n+ * yOrdering - {Boolean} Whether to use y-ordering\n+ * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored\n+ * if yOrdering is set to true.\n */\n- 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+ initialize: function(containerID, options) {\n+ OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n+\n+ this.rendererRoot = this.createRenderRoot();\n+ this.root = this.createRoot(\"_root\");\n+ this.vectorRoot = this.createRoot(\"_vroot\");\n+ this.textRoot = this.createRoot(\"_troot\");\n+\n+ this.root.appendChild(this.vectorRoot);\n+ this.root.appendChild(this.textRoot);\n+\n+ this.rendererRoot.appendChild(this.root);\n+ this.container.appendChild(this.rendererRoot);\n+\n+ if (options && (options.zIndexing || options.yOrdering)) {\n+ this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);\n+ }\n },\n \n /**\n- * APIMethod: getProcess\n- * Creates an .\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+\n+ this.clear();\n+\n+ this.rendererRoot = null;\n+ this.root = null;\n+ this.xmlns = null;\n+\n+ OpenLayers.Renderer.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: clear\n+ * Remove all the elements from the root\n+ */\n+ clear: function() {\n+ var child;\n+ var root = this.vectorRoot;\n+ if (root) {\n+ while (child = root.firstChild) {\n+ root.removeChild(child);\n+ }\n+ }\n+ root = this.textRoot;\n+ if (root) {\n+ while (child = root.firstChild) {\n+ root.removeChild(child);\n+ }\n+ }\n+ if (this.indexer) {\n+ this.indexer.clear();\n+ }\n+ },\n+\n+ /**\n+ * Method: setExtent\n+ * Set the visible part of the layer.\n *\n * Parameters:\n- * serverID - {String} Local identifier from the servers that this instance\n- * was constructed with.\n- * processID - {String} Process identifier known to the server.\n+ * extent - {}\n+ * resolutionChanged - {Boolean}\n *\n * Returns:\n- * {}\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- 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+ setExtent: function(extent, resolutionChanged) {\n+ var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n+ var resolution = this.getResolution();\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ var rightOfDateLine,\n+ ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n+ extent = extent.scale(1 / ratio),\n+ world = this.map.getMaxExtent();\n+ if (world.right > extent.left && world.right < extent.right) {\n+ rightOfDateLine = true;\n+ } else if (world.left > extent.left && world.left < extent.right) {\n+ rightOfDateLine = false;\n+ }\n+ if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {\n+ coordSysUnchanged = false;\n+ this.xOffset = rightOfDateLine === true ?\n+ world.getWidth() / resolution : 0;\n+ }\n+ this.rightOfDateLine = rightOfDateLine;\n }\n- return process;\n+ return coordSysUnchanged;\n },\n \n- /**\n- * Method: describeProcess\n+ /** \n+ * Method: getNodeType\n+ * This function is in charge of asking the specific renderer which type\n+ * of node to create for the given geometry and style. All geometries\n+ * in an Elements-based renderer consist of one node and some\n+ * attributes. We have the nodeFactory() function which creates a node\n+ * for us, but it takes a 'type' as input, and that is precisely what\n+ * this function tells us. \n+ * \n+ * Parameters:\n+ * geometry - {}\n+ * style - {Object}\n+ * \n+ * Returns:\n+ * {String} The corresponding node type for the specified geometry\n+ */\n+ getNodeType: function(geometry, style) {},\n+\n+ /** \n+ * Method: drawGeometry \n+ * Draw the geometry, creating new nodes, setting paths, setting style,\n+ * setting featureId on the node. This method should only be called\n+ * by the renderer itself.\n *\n * Parameters:\n- * 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+ * geometry - {}\n+ * style - {Object}\n+ * featureId - {String}\n+ * \n+ * Returns:\n+ * {Boolean} true if the geometry has been drawn completely; null if\n+ * incomplete; false otherwise\n */\n- 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+ drawGeometry: function(geometry, style, featureId) {\n+ var className = geometry.CLASS_NAME;\n+ var rendered = true;\n+ if ((className == \"OpenLayers.Geometry.Collection\") ||\n+ (className == \"OpenLayers.Geometry.MultiPoint\") ||\n+ (className == \"OpenLayers.Geometry.MultiLineString\") ||\n+ (className == \"OpenLayers.Geometry.MultiPolygon\")) {\n+ for (var i = 0, len = geometry.components.length; i < len; i++) {\n+ rendered = this.drawGeometry(\n+ geometry.components[i], style, featureId) && rendered;\n+ }\n+ return rendered;\n+ }\n+\n+ rendered = false;\n+ var removeBackground = false;\n+ if (style.display != \"none\") {\n+ if (style.backgroundGraphic) {\n+ this.redrawBackgroundNode(geometry.id, geometry, style,\n+ featureId);\n } else {\n- // 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+ removeBackground = true;\n+ }\n+ rendered = this.redrawNode(geometry.id, geometry, style,\n+ featureId);\n+ }\n+ if (rendered == false) {\n+ var node = document.getElementById(geometry.id);\n+ if (node) {\n+ if (node._style.backgroundGraphic) {\n+ removeBackground = true;\n+ }\n+ node.parentNode.removeChild(node);\n+ }\n+ }\n+ if (removeBackground) {\n+ var node = document.getElementById(\n+ geometry.id + this.BACKGROUND_ID_SUFFIX);\n+ if (node) {\n+ node.parentNode.removeChild(node);\n }\n- } else {\n- window.setTimeout(function() {\n- callback.call(scope, server.processDescription[processID]);\n- }, 0);\n }\n+ return rendered;\n },\n \n /**\n- * Method: destroy\n+ * Method: redrawNode\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * geometry - {}\n+ * style - {Object}\n+ * featureId - {String}\n+ * \n+ * Returns:\n+ * {Boolean} true if the complete geometry could be drawn, null if parts of\n+ * the geometry could not be drawn, false otherwise\n */\n- destroy: function() {\n- this.events.destroy();\n- this.events = null;\n- this.servers = null;\n+ redrawNode: function(id, geometry, style, featureId) {\n+ style = this.applyDefaultSymbolizer(style);\n+ // Get the node if it's already on the map.\n+ var node = this.nodeFactory(id, this.getNodeType(geometry, style));\n+\n+ // Set the data for the node, then draw it.\n+ node._featureId = featureId;\n+ node._boundsBottom = geometry.getBounds().bottom;\n+ node._geometryClass = geometry.CLASS_NAME;\n+ node._style = style;\n+\n+ var drawResult = this.drawGeometryNode(node, geometry, style);\n+ if (drawResult === false) {\n+ return false;\n+ }\n+\n+ node = drawResult.node;\n+\n+ // Insert the node into the indexer so it can show us where to\n+ // place it. Note that this operation is O(log(n)). If there's a\n+ // performance problem (when dragging, for instance) this is\n+ // likely where it would be.\n+ if (this.indexer) {\n+ var insert = this.indexer.insert(node);\n+ if (insert) {\n+ this.vectorRoot.insertBefore(node, insert);\n+ } else {\n+ this.vectorRoot.appendChild(node);\n+ }\n+ } else {\n+ // if there's no indexer, simply append the node to root,\n+ // but only if the node is a new one\n+ if (node.parentNode !== this.vectorRoot) {\n+ this.vectorRoot.appendChild(node);\n+ }\n+ }\n+\n+ this.postDraw(node);\n+\n+ return drawResult.complete;\n },\n \n- CLASS_NAME: 'OpenLayers.WPSClient'\n+ /**\n+ * Method: redrawBackgroundNode\n+ * Redraws the node using special 'background' style properties. Basically\n+ * just calls redrawNode(), but instead of directly using the \n+ * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and \n+ * 'graphicZIndex' properties directly from the specified 'style' \n+ * parameter, we create a new style object and set those properties \n+ * from the corresponding 'background'-prefixed properties from \n+ * specified 'style' parameter.\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * geometry - {}\n+ * style - {Object}\n+ * featureId - {String}\n+ * \n+ * Returns:\n+ * {Boolean} true if the complete geometry could be drawn, null if parts of\n+ * the geometry could not be drawn, false otherwise\n+ */\n+ redrawBackgroundNode: function(id, geometry, style, featureId) {\n+ var backgroundStyle = OpenLayers.Util.extend({}, style);\n \n-});\n-/* ======================================================================\n- OpenLayers/Handler.js\n- ====================================================================== */\n+ // Set regular style attributes to apply to the background styles.\n+ backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;\n+ backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;\n+ backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;\n+ backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;\n+ backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;\n+ backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;\n+\n+ // Erase background styles.\n+ backgroundStyle.backgroundGraphic = null;\n+ backgroundStyle.backgroundXOffset = null;\n+ backgroundStyle.backgroundYOffset = null;\n+ backgroundStyle.backgroundGraphicZIndex = null;\n+\n+ return this.redrawNode(\n+ id + this.BACKGROUND_ID_SUFFIX,\n+ geometry,\n+ backgroundStyle,\n+ null\n+ );\n+ },\n+\n+ /**\n+ * Method: drawGeometryNode\n+ * Given a node, draw a geometry on the specified layer.\n+ * node and geometry are required arguments, style is optional.\n+ * This method is only called by the render itself.\n+ *\n+ * Parameters:\n+ * node - {DOMElement}\n+ * geometry - {}\n+ * style - {Object}\n+ * \n+ * Returns:\n+ * {Object} a hash with properties \"node\" (the drawn node) and \"complete\"\n+ * (null if parts of the geometry could not be drawn, false if nothing\n+ * could be drawn)\n+ */\n+ drawGeometryNode: function(node, geometry, style) {\n+ style = style || node._style;\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under 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 options = {\n+ 'isFilled': style.fill === undefined ?\n+ true : style.fill,\n+ 'isStroked': style.stroke === undefined ?\n+ !!style.strokeWidth : style.stroke\n+ };\n+ var drawn;\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ if (style.graphic === false) {\n+ options.isFilled = false;\n+ options.isStroked = false;\n+ }\n+ drawn = this.drawPoint(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ options.isFilled = false;\n+ drawn = this.drawLineString(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ drawn = this.drawLinearRing(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ drawn = this.drawPolygon(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.Rectangle\":\n+ drawn = this.drawRectangle(node, geometry);\n+ break;\n+ default:\n+ break;\n+ }\n \n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Events.js\n- */\n+ node._options = options;\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+ //set style\n+ //TBD simplify this\n+ if (drawn != false) {\n+ return {\n+ node: this.setStyle(node, style, options, geometry),\n+ complete: drawn\n+ };\n+ } else {\n+ return false;\n+ }\n+ },\n \n /**\n- * Property: id\n- * {String}\n+ * Method: postDraw\n+ * Things that have do be done after the geometry node is appended\n+ * to its parent node. To be overridden by subclasses.\n+ * \n+ * Parameters:\n+ * node - {DOMElement}\n */\n- id: null,\n+ postDraw: function(node) {},\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+ * Method: drawPoint\n+ * Virtual function for drawing Point Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the point\n */\n- control: null,\n+ drawPoint: function(node, geometry) {},\n \n /**\n- * Property: map\n- * {}\n+ * Method: drawLineString\n+ * Virtual function for drawing LineString Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement} or null if the renderer could not draw all components of\n+ * the linestring, or false if nothing could be drawn\n */\n- map: null,\n+ drawLineString: function(node, geometry) {},\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+ * Method: drawLinearRing\n+ * Virtual function for drawing LinearRing Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement} or null if the renderer could not draw all components\n+ * of the linear ring, or false if nothing could be drawn\n */\n- keyMask: null,\n+ drawLinearRing: function(node, geometry) {},\n \n /**\n- * Property: active\n- * {Boolean}\n+ * Method: drawPolygon\n+ * Virtual function for drawing Polygon Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement} or null if the renderer could not draw all components\n+ * of the polygon, or false if nothing could be drawn\n */\n- active: false,\n+ drawPolygon: function(node, geometry) {},\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+ * Method: drawRectangle\n+ * Virtual function for drawing Rectangle Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the rectangle\n */\n- evt: null,\n+ drawRectangle: function(node, geometry) {},\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+ * Method: drawCircle\n+ * Virtual function for drawing Circle Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the circle\n */\n- touch: false,\n+ drawCircle: function(node, geometry) {},\n \n /**\n- * Constructor: OpenLayers.Handler\n- * Construct a handler.\n- *\n+ * Method: removeText\n+ * Removes a label\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+ * featureId - {String}\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+ removeText: function(featureId) {\n+ var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);\n+ if (label) {\n+ this.textRoot.removeChild(label);\n+ }\n+ var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);\n+ if (outline) {\n+ this.textRoot.removeChild(outline);\n }\n-\n- 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+ * Method: getFeatureIdFromEvent\n+ * \n+ * Parameters:\n+ * evt - {Object} An object\n *\n * Returns:\n- * {Boolean} The keyMask matches the keys down on an event.\n+ * {String} A feature id or undefined.\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+ getFeatureIdFromEvent: function(evt) {\n+ var target = evt.target;\n+ var useElement = target && target.correspondingUseElement;\n+ var node = useElement ? useElement : (target || evt.srcElement);\n+ return node._featureId;\n },\n \n- /**\n- * APIMethod: activate\n- * Turn on the handler. Returns false if the handler was already active.\n+ /** \n+ * Method: eraseGeometry\n+ * Erase a geometry from the renderer. In the case of a multi-geometry, \n+ * we cycle through and recurse on ourselves. Otherwise, we look for a \n+ * node with the geometry.id, destroy its geometry, and remove it from\n+ * the DOM.\n * \n- * Returns: \n- * {Boolean} The handler was activated.\n+ * Parameters:\n+ * geometry - {}\n+ * featureId - {String}\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+ eraseGeometry: function(geometry, featureId) {\n+ if ((geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPoint\") ||\n+ (geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiLineString\") ||\n+ (geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPolygon\") ||\n+ (geometry.CLASS_NAME == \"OpenLayers.Geometry.Collection\")) {\n+ for (var i = 0, len = geometry.components.length; i < len; i++) {\n+ this.eraseGeometry(geometry.components[i], featureId);\n+ }\n+ } else {\n+ var element = OpenLayers.Util.getElement(geometry.id);\n+ if (element && element.parentNode) {\n+ if (element.geometry) {\n+ element.geometry.destroy();\n+ element.geometry = null;\n+ }\n+ element.parentNode.removeChild(element);\n+\n+ if (this.indexer) {\n+ this.indexer.remove(element);\n+ }\n+\n+ if (element._style.backgroundGraphic) {\n+ var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;\n+ var bElem = OpenLayers.Util.getElement(backgroundId);\n+ if (bElem && bElem.parentNode) {\n+ // No need to destroy the geometry since the element and the background\n+ // node share the same geometry.\n+ bElem.parentNode.removeChild(bElem);\n+ }\n+ }\n }\n }\n- 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+ * Method: nodeFactory\n+ * Create new node of the specified type, with the (optional) specified id.\n+ * \n+ * If node already exists with same ID and a different type, we remove it\n+ * and then call ourselves again to recreate it.\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * type - {String} type Kind of node to draw.\n * \n * Returns:\n- * {Boolean} The handler was deactivated.\n+ * {DOMElement} A new node of the given type and id.\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+ nodeFactory: function(id, type) {\n+ var node = OpenLayers.Util.getElement(id);\n+ if (node) {\n+ if (!this.nodeTypeCompare(node, type)) {\n+ node.parentNode.removeChild(node);\n+ node = this.nodeFactory(id, type);\n }\n+ } else {\n+ node = this.createNode(type, id);\n }\n- this.touch = false;\n- this.active = false;\n- return true;\n+ return node;\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+ * Method: nodeTypeCompare\n+ * \n+ * Parameters:\n+ * node - {DOMElement}\n+ * type - {String} Kind of node\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the specified node is of the specified type\n+ * This function must be overridden by subclasses.\n */\n- 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+ nodeTypeCompare: function(node, type) {},\n \n- /**\n- * Method: callback\n- * Trigger the control's named callback with the given arguments\n- *\n+ /** \n+ * Method: createNode\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+ * type - {String} Kind of node to draw.\n+ * id - {String} Id for node.\n+ * \n+ * Returns:\n+ * {DOMElement} A new node of the given type and id.\n+ * This function must be overridden by subclasses.\n */\n- callback: function(name, args) {\n- if (name && this.callbacks[name]) {\n- this.callbacks[name].apply(this.control, args);\n- }\n- },\n+ createNode: function(type, id) {},\n \n /**\n- * Method: register\n- * register an event on the map\n+ * Method: moveRoot\n+ * moves this renderer's root to a different renderer.\n+ * \n+ * Parameters:\n+ * renderer - {} target renderer for the moved root\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+ moveRoot: function(renderer) {\n+ var root = this.root;\n+ if (renderer.root.parentNode == this.rendererRoot) {\n+ root = renderer.root;\n+ }\n+ root.parentNode.removeChild(root);\n+ renderer.rendererRoot.appendChild(root);\n },\n \n /**\n- * Method: unregister\n- * unregister an event from the map\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- unregister: function(name, method) {\n- this.map.events.unregister(name, this, method);\n- this.map.events.unregister(name, this, this.setEvent);\n+ getRenderLayerId: function() {\n+ return this.root.parentNode.parentNode.id;\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+ * Method: isComplexSymbol\n+ * Determines if a symbol cannot be rendered using drawCircle\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+ * graphicName - {String}\n+ * \n+ * Returns\n+ * {Boolean} true if the symbol is complex, false if not\n */\n- destroy: function() {\n- // unregister event listeners\n- this.deactivate();\n- // eliminate circular references\n- this.control = this.map = null;\n+ isComplexSymbol: function(graphicName) {\n+ return (graphicName != \"circle\") && !!graphicName;\n },\n \n- CLASS_NAME: \"OpenLayers.Handler\"\n+ CLASS_NAME: \"OpenLayers.Renderer.Elements\"\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/StyleMap.js\n+ OpenLayers/Renderer/VML.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Style.js\n- * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Renderer/Elements.js\n */\n \n /**\n- * Class: OpenLayers.StyleMap\n+ * Class: OpenLayers.Renderer.VML\n+ * Render vector features in browsers with VML capability. Construct a new\n+ * VML renderer with the constructor.\n+ * \n+ * Note that for all calculations in this class, we use (num | 0) to truncate a \n+ * float value to an integer. This is done because it seems that VML doesn't \n+ * support float values.\n+ *\n+ * Inherits from:\n+ * - \n */\n-OpenLayers.StyleMap = OpenLayers.Class({\n+OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {\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+ * Property: xmlns\n+ * {String} XML Namespace URN\n */\n- styles: null,\n+ xmlns: \"urn:schemas-microsoft-com:vml\",\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+ * Property: symbolCache\n+ * {DOMElement} node holding symbols. This hash is keyed by symbol name,\n+ * and each value is a hash with a \"path\" and an \"extent\" property.\n */\n- extendDefault: true,\n+ symbolCache: {},\n \n /**\n- * Constructor: OpenLayers.StyleMap\n- * \n+ * Property: offset\n+ * {Object} Hash with \"x\" and \"y\" properties\n+ */\n+ offset: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Renderer.VML\n+ * Create a new VML renderer.\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+ * containerID - {String} The id for the element that contains the renderer\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(containerID) {\n+ if (!this.supported()) {\n+ return;\n+ }\n+ if (!document.namespaces.olv) {\n+ document.namespaces.add(\"olv\", this.xmlns);\n+ var style = document.createStyleSheet();\n+ var shapes = ['shape', 'rect', 'oval', 'fill', 'stroke', 'imagedata', 'group', 'textbox'];\n+ for (var i = 0, len = shapes.length; i < len; i++) {\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+ style.addRule('olv\\\\:' + shapes[i], \"behavior: url(#default#VML); \" +\n+ \"position: absolute; display: inline-block;\");\n }\n }\n- OpenLayers.Util.extend(this, options);\n+\n+ OpenLayers.Renderer.Elements.prototype.initialize.apply(this,\n+ arguments);\n },\n \n /**\n- * Method: destroy\n+ * APIMethod: supported\n+ * Determine whether a browser supports this renderer.\n+ *\n+ * Returns:\n+ * {Boolean} The browser supports the VML renderer\n */\n- destroy: function() {\n- for (var key in this.styles) {\n- this.styles[key].destroy();\n- }\n- this.styles = null;\n+ supported: function() {\n+ return !!(document.namespaces);\n },\n \n /**\n- * Method: createSymbolizer\n- * Creates the symbolizer for a feature for a render intent.\n- * \n+ * Method: setExtent\n+ * Set the renderer's extent\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+ * extent - {}\n+ * resolutionChanged - {Boolean}\n * \n * Returns:\n- * {Object} symbolizer hash\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 */\n- createSymbolizer: function(feature, intent) {\n- if (!feature) {\n- feature = new OpenLayers.Feature.Vector();\n- }\n- if (!this.styles[intent]) {\n- intent = \"default\";\n- }\n- feature.renderIntent = intent;\n- var defaultSymbolizer = {};\n- if (this.extendDefault && intent != \"default\") {\n- defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature);\n- }\n- return OpenLayers.Util.extend(defaultSymbolizer,\n- this.styles[intent].createSymbolizer(feature));\n- },\n+ setExtent: function(extent, resolutionChanged) {\n+ var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);\n+ var resolution = this.getResolution();\n \n- /**\n- * Method: addUniqueValueRules\n- * Convenience method to create comparison rules for unique values of a\n- * property. The rules will be added to the style object for a specified\n- * rendering intent. This method is a shortcut for creating something like\n- * the \"unique value legends\" familiar from well known desktop GIS systems\n- * \n- * Parameters:\n- * renderIntent - {String} rendering intent to add the rules to\n- * property - {String} values of feature attributes to create the\n- * rules for\n- * symbolizers - {Object} Hash of symbolizers, keyed by the desired\n- * property values \n- * context - {Object} An optional object with properties that\n- * symbolizers' property values should be evaluated\n- * against. If no context is specified, feature.attributes\n- * will be used\n- */\n- addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n- var rules = [];\n- for (var value in symbolizers) {\n- rules.push(new OpenLayers.Rule({\n- symbolizer: symbolizers[value],\n- context: context,\n- filter: new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.EQUAL_TO,\n- property: property,\n- value: value\n- })\n- }));\n+ var left = (extent.left / resolution) | 0;\n+ var top = (extent.top / resolution - this.size.h) | 0;\n+ if (resolutionChanged || !this.offset) {\n+ this.offset = {\n+ x: left,\n+ y: top\n+ };\n+ left = 0;\n+ top = 0;\n+ } else {\n+ left = left - this.offset.x;\n+ top = top - this.offset.y;\n }\n- this.styles[renderIntent].addRules(rules);\n- },\n-\n- CLASS_NAME: \"OpenLayers.StyleMap\"\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-\n- /** \n- * Property: contentHTML \n- * {String} An HTML string for this popup to display.\n- */\n- contentHTML: null,\n-\n- /** \n- * Property: backgroundColor \n- * {String} the background color used by the popup.\n- */\n- backgroundColor: \"\",\n-\n- /** \n- * Property: opacity \n- * {float} the opacity of this popup (between 0.0 and 1.0)\n- */\n- opacity: \"\",\n \n- /** \n- * Property: border \n- * {String} the border size of the popup. (eg 2px)\n- */\n- border: \"\",\n \n- /** \n- * Property: contentDiv \n- * {DOMElement} a reference to the element that holds the content of\n- * the div.\n- */\n- contentDiv: null,\n+ var org = (left - this.xOffset) + \" \" + top;\n+ this.root.coordorigin = org;\n+ var roots = [this.root, this.vectorRoot, this.textRoot];\n+ var root;\n+ for (var i = 0, len = roots.length; i < len; ++i) {\n+ root = roots[i];\n \n- /** \n- * Property: groupDiv \n- * {DOMElement} First and only child of 'div'. The group Div contains the\n- * 'contentDiv' and the 'closeDiv'.\n- */\n- groupDiv: null,\n+ var size = this.size.w + \" \" + this.size.h;\n+ root.coordsize = size;\n \n- /** \n- * Property: closeDiv\n- * {DOMElement} the optional closer image\n- */\n- closeDiv: null,\n+ }\n+ // flip the VML display Y axis upside down so it \n+ // matches the display Y axis of the map\n+ this.root.style.flip = \"y\";\n \n- /** \n- * APIProperty: autoSize\n- * {Boolean} Resize the popup to auto-fit the contents.\n- * Default is false.\n- */\n- autoSize: false,\n+ return coordSysUnchanged;\n+ },\n \n- /**\n- * APIProperty: minSize\n- * {} Minimum size allowed for the popup's contents.\n- */\n- minSize: null,\n \n /**\n- * APIProperty: maxSize\n- * {} Maximum size allowed for the popup's contents.\n- */\n- maxSize: null,\n-\n- /** \n- * Property: displayClass\n- * {String} The CSS class of the popup.\n- */\n- displayClass: \"olPopup\",\n-\n- /** \n- * Property: contentDisplayClass\n- * {String} The CSS class of the popup content div.\n- */\n- contentDisplayClass: \"olPopupContent\",\n-\n- /** \n- * Property: padding \n- * {int or } An extra opportunity to specify internal \n- * padding of the content div inside the popup. This was originally\n- * confused with the css padding as specified in style.css's \n- * 'olPopupContent' class. We would like to get rid of this altogether,\n- * except that it does come in handy for the framed and anchoredbubble\n- * popups, who need to maintain yet another barrier between their \n- * content and the outer border of the popup itself. \n- * \n- * Note that in order to not break API, we must continue to support \n- * this property being set as an integer. Really, though, we'd like to \n- * have this specified as a Bounds object so that user can specify\n- * distinct left, top, right, bottom paddings. With the 3.0 release\n- * we can make this only a bounds.\n- */\n- padding: 0,\n-\n- /** \n- * Property: disableFirefoxOverflowHack\n- * {Boolean} The hack for overflow in Firefox causes all elements \n- * to be re-drawn, which causes Flash elements to be \n- * re-initialized, which is troublesome.\n- * With this property the hack can be disabled.\n+ * Method: setSize\n+ * Set the size of the drawing surface\n+ *\n+ * Parameters:\n+ * size - {} the size of the drawing surface\n */\n- disableFirefoxOverflowHack: false,\n+ setSize: function(size) {\n+ OpenLayers.Renderer.prototype.setSize.apply(this, arguments);\n \n- /**\n- * Method: fixPadding\n- * To be removed in 3.0, this function merely helps us to deal with the \n- * case where the user may have set an integer value for padding, \n- * instead of an object.\n- */\n- fixPadding: function() {\n- if (typeof this.padding == \"number\") {\n- this.padding = new OpenLayers.Bounds(\n- this.padding, this.padding, this.padding, this.padding\n- );\n+ // setting width and height on all roots to avoid flicker which we\n+ // would get with 100% width and height on child roots\n+ var roots = [\n+ this.rendererRoot,\n+ this.root,\n+ this.vectorRoot,\n+ this.textRoot\n+ ];\n+ var w = this.size.w + \"px\";\n+ var h = this.size.h + \"px\";\n+ var root;\n+ for (var i = 0, len = roots.length; i < len; ++i) {\n+ root = roots[i];\n+ root.style.width = w;\n+ root.style.height = h;\n }\n },\n \n /**\n- * APIProperty: panMapIfOutOfView\n- * {Boolean} When drawn, pan map such that the entire popup is visible in\n- * the current viewport (if necessary).\n- * Default is false.\n- */\n- panMapIfOutOfView: false,\n-\n- /**\n- * APIProperty: keepInMap \n- * {Boolean} If panMapIfOutOfView is false, and this property is true, \n- * contrain the popup such that it always fits in the available map\n- * space. By default, this is not set on the base class. If you are\n- * creating popups that are near map edges and not allowing pannning,\n- * and especially if you have a popup which has a\n- * fixedRelativePosition, setting this to false may be a smart thing to\n- * do. Subclasses may want to override this setting.\n- * \n- * Default is false.\n+ * Method: getNodeType\n+ * Get the node type for a geometry and style\n+ *\n+ * Parameters:\n+ * geometry - {}\n+ * style - {Object}\n+ *\n+ * Returns:\n+ * {String} The corresponding node type for the specified geometry\n */\n- keepInMap: false,\n+ getNodeType: function(geometry, style) {\n+ var nodeType = null;\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ if (style.externalGraphic) {\n+ nodeType = \"olv:rect\";\n+ } else if (this.isComplexSymbol(style.graphicName)) {\n+ nodeType = \"olv:shape\";\n+ } else {\n+ nodeType = \"olv:oval\";\n+ }\n+ break;\n+ case \"OpenLayers.Geometry.Rectangle\":\n+ nodeType = \"olv:rect\";\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ case \"OpenLayers.Geometry.Polygon\":\n+ case \"OpenLayers.Geometry.Curve\":\n+ nodeType = \"olv:shape\";\n+ break;\n+ default:\n+ break;\n+ }\n+ return nodeType;\n+ },\n \n /**\n- * APIProperty: closeOnMove\n- * {Boolean} When map pans, close the popup.\n- * Default is false.\n- */\n- closeOnMove: false,\n-\n- /** \n- * Property: map \n- * {} this gets set in Map.js when the popup is added to the map\n- */\n- map: null,\n-\n- /** \n- * Constructor: OpenLayers.Popup\n- * Create a popup.\n- * \n- * Parameters: \n- * id - {String} a unqiue identifier for this popup. If null is passed\n- * an identifier will be automatically generated. \n- * lonlat - {} The position on the map the popup will\n- * be shown.\n- * contentSize - {} The size of the content.\n- * contentHTML - {String} An HTML string to display inside the \n- * popup.\n- * closeBox - {Boolean} Whether to display a close box inside\n- * the popup.\n- * closeBoxCallback - {Function} Function to be called on closeBox click.\n+ * Method: setStyle\n+ * Use to set all the style attributes to a VML node.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} An VML element to decorate\n+ * style - {Object}\n+ * options - {Object} Currently supported options include \n+ * 'isFilled' {Boolean} and\n+ * 'isStroked' {Boolean}\n+ * geometry - {}\n */\n- initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {\n- if (id == null) {\n- id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- }\n-\n- this.id = id;\n- this.lonlat = lonlat;\n+ setStyle: function(node, style, options, geometry) {\n+ style = style || node._style;\n+ options = options || node._options;\n+ var fillColor = style.fillColor;\n \n- this.contentSize = (contentSize != null) ? contentSize :\n- new OpenLayers.Size(\n- OpenLayers.Popup.WIDTH,\n- OpenLayers.Popup.HEIGHT);\n- if (contentHTML != null) {\n- this.contentHTML = contentHTML;\n+ var title = style.title || style.graphicTitle;\n+ if (title) {\n+ node.title = title;\n }\n- this.backgroundColor = OpenLayers.Popup.COLOR;\n- this.opacity = OpenLayers.Popup.OPACITY;\n- this.border = OpenLayers.Popup.BORDER;\n \n- this.div = OpenLayers.Util.createDiv(this.id, null, null,\n- null, null, null, \"hidden\");\n- this.div.className = this.displayClass;\n+ if (node._geometryClass === \"OpenLayers.Geometry.Point\") {\n+ if (style.externalGraphic) {\n+ options.isFilled = true;\n+ var width = style.graphicWidth || style.graphicHeight;\n+ var height = style.graphicHeight || style.graphicWidth;\n+ width = width ? width : style.pointRadius * 2;\n+ height = height ? height : style.pointRadius * 2;\n \n- var groupDivId = this.id + \"_GroupDiv\";\n- this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,\n- null, \"relative\", null,\n- \"hidden\");\n+ var resolution = this.getResolution();\n+ var xOffset = (style.graphicXOffset != undefined) ?\n+ style.graphicXOffset : -(0.5 * width);\n+ var yOffset = (style.graphicYOffset != undefined) ?\n+ style.graphicYOffset : -(0.5 * height);\n \n- var id = this.div.id + \"_contentDiv\";\n- this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),\n- null, \"relative\");\n- this.contentDiv.className = this.contentDisplayClass;\n- this.groupDiv.appendChild(this.contentDiv);\n- this.div.appendChild(this.groupDiv);\n+ node.style.left = ((((geometry.x - this.featureDx) / resolution - this.offset.x) + xOffset) | 0) + \"px\";\n+ node.style.top = (((geometry.y / resolution - this.offset.y) - (yOffset + height)) | 0) + \"px\";\n+ node.style.width = width + \"px\";\n+ node.style.height = height + \"px\";\n+ node.style.flip = \"y\";\n \n- if (closeBox) {\n- this.addCloseBox(closeBoxCallback);\n+ // modify fillColor and options for stroke styling below\n+ fillColor = \"none\";\n+ options.isStroked = false;\n+ } else if (this.isComplexSymbol(style.graphicName)) {\n+ var cache = this.importSymbol(style.graphicName);\n+ node.path = cache.path;\n+ node.coordorigin = cache.left + \",\" + cache.bottom;\n+ var size = cache.size;\n+ node.coordsize = size + \",\" + size;\n+ this.drawCircle(node, geometry, style.pointRadius);\n+ node.style.flip = \"y\";\n+ } else {\n+ this.drawCircle(node, geometry, style.pointRadius);\n+ }\n }\n \n- this.registerEvents();\n- },\n+ // fill \n+ if (options.isFilled) {\n+ node.fillcolor = fillColor;\n+ } else {\n+ node.filled = \"false\";\n+ }\n+ var fills = node.getElementsByTagName(\"fill\");\n+ var fill = (fills.length == 0) ? null : fills[0];\n+ if (!options.isFilled) {\n+ if (fill) {\n+ node.removeChild(fill);\n+ }\n+ } else {\n+ if (!fill) {\n+ fill = this.createNode('olv:fill', node.id + \"_fill\");\n+ }\n+ fill.opacity = style.fillOpacity;\n \n- /** \n- * Method: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n+ if (node._geometryClass === \"OpenLayers.Geometry.Point\" &&\n+ style.externalGraphic) {\n \n- this.id = null;\n- this.lonlat = null;\n- this.size = null;\n- this.contentHTML = null;\n+ // override fillOpacity\n+ if (style.graphicOpacity) {\n+ fill.opacity = style.graphicOpacity;\n+ }\n \n- this.backgroundColor = null;\n- this.opacity = null;\n- this.border = null;\n+ fill.src = style.externalGraphic;\n+ fill.type = \"frame\";\n \n- if (this.closeOnMove && this.map) {\n- this.map.events.unregister(\"movestart\", this, this.hide);\n+ if (!(style.graphicWidth && style.graphicHeight)) {\n+ fill.aspect = \"atmost\";\n+ }\n+ }\n+ if (fill.parentNode != node) {\n+ node.appendChild(fill);\n+ }\n }\n \n- this.events.destroy();\n- this.events = null;\n-\n- if (this.closeDiv) {\n- OpenLayers.Event.stopObservingElement(this.closeDiv);\n- this.groupDiv.removeChild(this.closeDiv);\n+ // additional rendering for rotated graphics or symbols\n+ var rotation = style.rotation;\n+ if ((rotation !== undefined || node._rotation !== undefined)) {\n+ node._rotation = rotation;\n+ if (style.externalGraphic) {\n+ this.graphicRotate(node, xOffset, yOffset, style);\n+ // make the fill fully transparent, because we now have\n+ // the graphic as imagedata element. We cannot just remove\n+ // the fill, because this is part of the hack described\n+ // in graphicRotate\n+ fill.opacity = 0;\n+ } else if (node._geometryClass === \"OpenLayers.Geometry.Point\") {\n+ node.style.rotation = rotation || 0;\n+ }\n }\n- this.closeDiv = null;\n \n- this.div.removeChild(this.groupDiv);\n- this.groupDiv = null;\n-\n- if (this.map != null) {\n- this.map.removePopup(this);\n+ // stroke \n+ var strokes = node.getElementsByTagName(\"stroke\");\n+ var stroke = (strokes.length == 0) ? null : strokes[0];\n+ if (!options.isStroked) {\n+ node.stroked = false;\n+ if (stroke) {\n+ stroke.on = false;\n+ }\n+ } else {\n+ if (!stroke) {\n+ stroke = this.createNode('olv:stroke', node.id + \"_stroke\");\n+ node.appendChild(stroke);\n+ }\n+ stroke.on = true;\n+ stroke.color = style.strokeColor;\n+ stroke.weight = style.strokeWidth + \"px\";\n+ stroke.opacity = style.strokeOpacity;\n+ stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :\n+ (style.strokeLinecap || 'round');\n+ if (style.strokeDashstyle) {\n+ stroke.dashstyle = this.dashStyle(style);\n+ }\n }\n- this.map = null;\n- this.div = null;\n \n- this.autoSize = null;\n- this.minSize = null;\n- this.maxSize = null;\n- this.padding = null;\n- this.panMapIfOutOfView = null;\n+ if (style.cursor != \"inherit\" && style.cursor != null) {\n+ node.style.cursor = style.cursor;\n+ }\n+ return node;\n },\n \n- /** \n- * Method: draw\n- * Constructs the elements that make up the popup.\n- *\n- * Parameters:\n- * px - {} the position the popup in pixels.\n+ /**\n+ * Method: graphicRotate\n+ * If a point is to be styled with externalGraphic and rotation, VML fills\n+ * cannot be used to display the graphic, because rotation of graphic\n+ * fills is not supported by the VML implementation of Internet Explorer.\n+ * This method creates a olv:imagedata element inside the VML node,\n+ * DXImageTransform.Matrix and BasicImage filters for rotation and\n+ * opacity, and a 3-step hack to remove rendering artefacts from the\n+ * graphic and preserve the ability of graphics to trigger events.\n+ * Finally, OpenLayers methods are used to determine the correct\n+ * insertion point of the rotated image, because DXImageTransform.Matrix\n+ * does the rotation without the ability to specify a rotation center\n+ * point.\n * \n- * Returns:\n- * {DOMElement} Reference to a div that contains the drawn popup\n+ * Parameters:\n+ * node - {DOMElement}\n+ * xOffset - {Number} rotation center relative to image, x coordinate\n+ * yOffset - {Number} rotation center relative to image, y coordinate\n+ * style - {Object}\n */\n- draw: function(px) {\n- if (px == null) {\n- if ((this.lonlat != null) && (this.map != null)) {\n- px = this.map.getLayerPxFromLonLat(this.lonlat);\n- }\n- }\n-\n- // this assumes that this.map already exists, which is okay because \n- // this.draw is only called once the popup has been added to the map.\n- if (this.closeOnMove) {\n- this.map.events.register(\"movestart\", this, this.hide);\n- }\n+ graphicRotate: function(node, xOffset, yOffset, style) {\n+ var style = style || node._style;\n+ var rotation = style.rotation || 0;\n \n- //listen to movestart, moveend to disable overflow (FF bug)\n- if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {\n- this.map.events.register(\"movestart\", this, function() {\n- var style = document.defaultView.getComputedStyle(\n- this.contentDiv, null\n- );\n- var currentOverflow = style.getPropertyValue(\"overflow\");\n- if (currentOverflow != \"hidden\") {\n- this.contentDiv._oldOverflow = currentOverflow;\n- this.contentDiv.style.overflow = \"hidden\";\n- }\n- });\n- this.map.events.register(\"moveend\", this, function() {\n- var oldOverflow = this.contentDiv._oldOverflow;\n- if (oldOverflow) {\n- this.contentDiv.style.overflow = oldOverflow;\n- this.contentDiv._oldOverflow = null;\n+ var aspectRatio, size;\n+ if (!(style.graphicWidth && style.graphicHeight)) {\n+ // load the image to determine its size\n+ var img = new Image();\n+ img.onreadystatechange = OpenLayers.Function.bind(function() {\n+ if (img.readyState == \"complete\" ||\n+ img.readyState == \"interactive\") {\n+ aspectRatio = img.width / img.height;\n+ size = Math.max(style.pointRadius * 2,\n+ style.graphicWidth || 0,\n+ style.graphicHeight || 0);\n+ xOffset = xOffset * aspectRatio;\n+ style.graphicWidth = size * aspectRatio;\n+ style.graphicHeight = size;\n+ this.graphicRotate(node, xOffset, yOffset, style);\n }\n- });\n- }\n+ }, this);\n+ img.src = style.externalGraphic;\n \n- this.moveTo(px);\n- if (!this.autoSize && !this.size) {\n- this.setSize(this.contentSize);\n+ // will be called again by the onreadystate handler\n+ return;\n+ } else {\n+ size = Math.max(style.graphicWidth, style.graphicHeight);\n+ aspectRatio = style.graphicWidth / style.graphicHeight;\n }\n- this.setBackgroundColor();\n- this.setOpacity();\n- this.setBorder();\n- this.setContentHTML();\n \n- if (this.panMapIfOutOfView) {\n- this.panIntoView();\n+ var width = Math.round(style.graphicWidth || size * aspectRatio);\n+ var height = Math.round(style.graphicHeight || size);\n+ node.style.width = width + \"px\";\n+ node.style.height = height + \"px\";\n+\n+ // Three steps are required to remove artefacts for images with\n+ // transparent backgrounds (resulting from using DXImageTransform\n+ // filters on svg objects), while preserving awareness for browser\n+ // events on images:\n+ // - Use the fill as usual (like for unrotated images) to handle\n+ // events\n+ // - specify an imagedata element with the same src as the fill\n+ // - style the imagedata element with an AlphaImageLoader filter\n+ // with empty src\n+ var image = document.getElementById(node.id + \"_image\");\n+ if (!image) {\n+ image = this.createNode(\"olv:imagedata\", node.id + \"_image\");\n+ node.appendChild(image);\n }\n+ image.style.width = width + \"px\";\n+ image.style.height = height + \"px\";\n+ image.src = style.externalGraphic;\n+ image.style.filter =\n+ \"progid:DXImageTransform.Microsoft.AlphaImageLoader(\" +\n+ \"src='', sizingMethod='scale')\";\n \n- return this.div;\n- },\n+ var rot = rotation * Math.PI / 180;\n+ var sintheta = Math.sin(rot);\n+ var costheta = Math.cos(rot);\n \n- /** \n- * Method: updatePosition\n- * if the popup has a lonlat and its map members set, \n- * then have it move itself to its proper position\n- */\n- updatePosition: function() {\n- if ((this.lonlat) && (this.map)) {\n- var px = this.map.getLayerPxFromLonLat(this.lonlat);\n- if (px) {\n- this.moveTo(px);\n- }\n+ // do the rotation on the image\n+ var filter =\n+ \"progid:DXImageTransform.Microsoft.Matrix(M11=\" + costheta +\n+ \",M12=\" + (-sintheta) + \",M21=\" + sintheta + \",M22=\" + costheta +\n+ \",SizingMethod='auto expand')\\n\";\n+\n+ // set the opacity (needed for the imagedata)\n+ var opacity = style.graphicOpacity || style.fillOpacity;\n+ if (opacity && opacity != 1) {\n+ filter +=\n+ \"progid:DXImageTransform.Microsoft.BasicImage(opacity=\" +\n+ opacity + \")\\n\";\n }\n+ node.style.filter = filter;\n+\n+ // do the rotation again on a box, so we know the insertion point\n+ var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);\n+ var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();\n+ imgBox.rotate(style.rotation, centerPoint);\n+ var imgBounds = imgBox.getBounds();\n+\n+ node.style.left = Math.round(\n+ parseInt(node.style.left) + imgBounds.left) + \"px\";\n+ node.style.top = Math.round(\n+ parseInt(node.style.top) - imgBounds.bottom) + \"px\";\n },\n \n /**\n- * Method: moveTo\n+ * Method: postDraw\n+ * Does some node postprocessing to work around browser issues:\n+ * - Some versions of Internet Explorer seem to be unable to set fillcolor\n+ * and strokecolor to \"none\" correctly before the fill node is appended\n+ * to a visible vml node. This method takes care of that and sets\n+ * fillcolor and strokecolor again if needed.\n+ * - In some cases, a node won't become visible after being drawn. Setting\n+ * style.visibility to \"visible\" works around that.\n * \n * Parameters:\n- * px - {} the top and left position of the popup div. \n+ * node - {DOMElement}\n */\n- moveTo: function(px) {\n- if ((px != null) && (this.div != null)) {\n- this.div.style.left = px.x + \"px\";\n- this.div.style.top = px.y + \"px\";\n+ postDraw: function(node) {\n+ node.style.visibility = \"visible\";\n+ var fillColor = node._style.fillColor;\n+ var strokeColor = node._style.strokeColor;\n+ if (fillColor == \"none\" &&\n+ node.fillcolor != fillColor) {\n+ node.fillcolor = fillColor;\n+ }\n+ if (strokeColor == \"none\" &&\n+ node.strokecolor != strokeColor) {\n+ node.strokecolor = strokeColor;\n }\n },\n \n- /**\n- * Method: visible\n- *\n- * Returns: \n- * {Boolean} Boolean indicating whether or not the popup is visible\n- */\n- visible: function() {\n- return OpenLayers.Element.visible(this.div);\n- },\n \n /**\n- * Method: toggle\n- * Toggles visibility of the popup.\n+ * Method: setNodeDimension\n+ * Get the geometry's bounds, convert it to our vml coordinate system, \n+ * then set the node's position, size, and local coordinate system.\n+ * \n+ * Parameters:\n+ * node - {DOMElement}\n+ * geometry - {}\n */\n- toggle: function() {\n- if (this.visible()) {\n- this.hide();\n- } else {\n- this.show();\n- }\n- },\n+ setNodeDimension: function(node, geometry) {\n \n- /**\n- * Method: show\n- * Makes the popup visible.\n- */\n- show: function() {\n- this.div.style.display = '';\n+ var bbox = geometry.getBounds();\n+ if (bbox) {\n+ var resolution = this.getResolution();\n \n- if (this.panMapIfOutOfView) {\n- this.panIntoView();\n+ var scaledBox =\n+ new OpenLayers.Bounds(((bbox.left - this.featureDx) / resolution - this.offset.x) | 0,\n+ (bbox.bottom / resolution - this.offset.y) | 0,\n+ ((bbox.right - this.featureDx) / resolution - this.offset.x) | 0,\n+ (bbox.top / resolution - this.offset.y) | 0);\n+\n+ // Set the internal coordinate system to draw the path\n+ node.style.left = scaledBox.left + \"px\";\n+ node.style.top = scaledBox.top + \"px\";\n+ node.style.width = scaledBox.getWidth() + \"px\";\n+ node.style.height = scaledBox.getHeight() + \"px\";\n+\n+ node.coordorigin = scaledBox.left + \" \" + scaledBox.top;\n+ node.coordsize = scaledBox.getWidth() + \" \" + scaledBox.getHeight();\n }\n },\n \n- /**\n- * Method: hide\n- * Makes the popup invisible.\n+ /** \n+ * Method: dashStyle\n+ * \n+ * Parameters:\n+ * style - {Object}\n+ * \n+ * Returns:\n+ * {String} A VML compliant 'stroke-dasharray' value\n */\n- hide: function() {\n- this.div.style.display = 'none';\n+ dashStyle: function(style) {\n+ var dash = style.strokeDashstyle;\n+ switch (dash) {\n+ case 'solid':\n+ case 'dot':\n+ case 'dash':\n+ case 'dashdot':\n+ case 'longdash':\n+ case 'longdashdot':\n+ return dash;\n+ default:\n+ // very basic guessing of dash style patterns\n+ var parts = dash.split(/[ ,]/);\n+ if (parts.length == 2) {\n+ if (1 * parts[0] >= 2 * parts[1]) {\n+ return \"longdash\";\n+ }\n+ return (parts[0] == 1 || parts[1] == 1) ? \"dot\" : \"dash\";\n+ } else if (parts.length == 4) {\n+ return (1 * parts[0] >= 2 * parts[1]) ? \"longdashdot\" :\n+ \"dashdot\";\n+ }\n+ return \"solid\";\n+ }\n },\n \n /**\n- * Method: setSize\n- * Used to adjust the size of the popup. \n+ * Method: createNode\n+ * Create a new node\n *\n * Parameters:\n- * contentSize - {} the new size for the popup's \n- * contents div (in pixels).\n+ * type - {String} Kind of node to draw\n+ * id - {String} Id for node\n+ *\n+ * Returns:\n+ * {DOMElement} A new node of the given type and id\n */\n- setSize: function(contentSize) {\n- this.size = contentSize.clone();\n-\n- // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n- // must add that to the desired \"size\". \n- var contentDivPadding = this.getContentDivPadding();\n- var wPadding = contentDivPadding.left + contentDivPadding.right;\n- var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n-\n- // take into account the popup's 'padding' property\n- this.fixPadding();\n- wPadding += this.padding.left + this.padding.right;\n- hPadding += this.padding.top + this.padding.bottom;\n-\n- // make extra space for the close div\n- if (this.closeDiv) {\n- var closeDivWidth = parseInt(this.closeDiv.style.width);\n- wPadding += closeDivWidth + contentDivPadding.right;\n+ createNode: function(type, id) {\n+ var node = document.createElement(type);\n+ if (id) {\n+ node.id = id;\n }\n \n- //increase size of the main popup div to take into account the \n- // users's desired padding and close div. \n- this.size.w += wPadding;\n- this.size.h += hPadding;\n-\n- //now if our browser is IE, we need to actually make the contents \n- // div itself bigger to take its own padding into effect. this makes \n- // me want to shoot someone, but so it goes.\n- if (OpenLayers.BROWSER_NAME == \"msie\") {\n- this.contentSize.w +=\n- contentDivPadding.left + contentDivPadding.right;\n- this.contentSize.h +=\n- contentDivPadding.bottom + contentDivPadding.top;\n- }\n+ // IE hack to make elements unselectable, to prevent 'blue flash'\n+ // while dragging vectors; #1410\n+ node.unselectable = 'on';\n+ node.onselectstart = OpenLayers.Function.False;\n \n- if (this.div != null) {\n- this.div.style.width = this.size.w + \"px\";\n- this.div.style.height = this.size.h + \"px\";\n- }\n- if (this.contentDiv != null) {\n- this.contentDiv.style.width = contentSize.w + \"px\";\n- this.contentDiv.style.height = contentSize.h + \"px\";\n- }\n+ return node;\n },\n \n /**\n- * APIMethod: updateSize\n- * Auto size the popup so that it precisely fits its contents (as \n- * determined by this.contentDiv.innerHTML). Popup size will, of\n- * course, be limited by the available space on the current map\n+ * Method: nodeTypeCompare\n+ * Determine whether a node is of a given type\n+ *\n+ * Parameters:\n+ * node - {DOMElement} An VML element\n+ * type - {String} Kind of node\n+ *\n+ * Returns:\n+ * {Boolean} Whether or not the specified node is of the specified type\n */\n- updateSize: function() {\n-\n- // determine actual render dimensions of the contents by putting its\n- // contents into a fake contentDiv (for the CSS) and then measuring it\n- var preparedHTML = \"
\" +\n- this.contentDiv.innerHTML +\n- \"
\";\n-\n- var containerElement = (this.map) ? this.map.div : document.body;\n- var realSize = OpenLayers.Util.getRenderedDimensions(\n- preparedHTML, null, {\n- displayClass: this.displayClass,\n- containerElement: containerElement\n- }\n- );\n-\n- // is the \"real\" size of the div is safe to display in our map?\n- var safeSize = this.getSafeContentSize(realSize);\n-\n- var newSize = null;\n- if (safeSize.equals(realSize)) {\n- //real size of content is small enough to fit on the map, \n- // so we use real size.\n- newSize = realSize;\n-\n- } else {\n-\n- // make a new 'size' object with the clipped dimensions \n- // set or null if not clipped.\n- var fixedSize = {\n- w: (safeSize.w < realSize.w) ? safeSize.w : null,\n- h: (safeSize.h < realSize.h) ? safeSize.h : null\n- };\n-\n- if (fixedSize.w && fixedSize.h) {\n- //content is too big in both directions, so we will use \n- // max popup size (safeSize), knowing well that it will \n- // overflow both ways. \n- newSize = safeSize;\n- } else {\n- //content is clipped in only one direction, so we need to \n- // run getRenderedDimensions() again with a fixed dimension\n- var clippedSize = OpenLayers.Util.getRenderedDimensions(\n- preparedHTML, fixedSize, {\n- displayClass: this.contentDisplayClass,\n- containerElement: containerElement\n- }\n- );\n+ nodeTypeCompare: function(node, type) {\n \n- //if the clipped size is still the same as the safeSize, \n- // that means that our content must be fixed in the \n- // offending direction. If overflow is 'auto', this means \n- // we are going to have a scrollbar for sure, so we must \n- // adjust for that.\n- //\n- var currentOverflow = OpenLayers.Element.getStyle(\n- this.contentDiv, \"overflow\"\n- );\n- if ((currentOverflow != \"hidden\") &&\n- (clippedSize.equals(safeSize))) {\n- var scrollBar = OpenLayers.Util.getScrollbarWidth();\n- if (fixedSize.w) {\n- clippedSize.h += scrollBar;\n- } else {\n- clippedSize.w += scrollBar;\n- }\n- }\n+ //split type\n+ var subType = type;\n+ var splitIndex = subType.indexOf(\":\");\n+ if (splitIndex != -1) {\n+ subType = subType.substr(splitIndex + 1);\n+ }\n \n- newSize = this.getSafeContentSize(clippedSize);\n- }\n+ //split nodeName\n+ var nodeName = node.nodeName;\n+ splitIndex = nodeName.indexOf(\":\");\n+ if (splitIndex != -1) {\n+ nodeName = nodeName.substr(splitIndex + 1);\n }\n- this.setSize(newSize);\n+\n+ return (subType == nodeName);\n },\n \n /**\n- * Method: setBackgroundColor\n- * Sets the background color of the popup.\n+ * Method: createRenderRoot\n+ * Create the renderer root\n *\n- * Parameters:\n- * color - {String} the background color. eg \"#FFBBBB\"\n+ * Returns:\n+ * {DOMElement} The specific render engine's root element\n */\n- setBackgroundColor: function(color) {\n- if (color != undefined) {\n- this.backgroundColor = color;\n- }\n-\n- if (this.div != null) {\n- this.div.style.backgroundColor = this.backgroundColor;\n- }\n+ createRenderRoot: function() {\n+ return this.nodeFactory(this.container.id + \"_vmlRoot\", \"div\");\n },\n \n /**\n- * Method: setOpacity\n- * Sets the opacity of the popup.\n+ * Method: createRoot\n+ * Create the main root element\n * \n * Parameters:\n- * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). \n+ * suffix - {String} suffix to append to the id\n+ *\n+ * Returns:\n+ * {DOMElement}\n */\n- setOpacity: function(opacity) {\n- if (opacity != undefined) {\n- this.opacity = opacity;\n- }\n-\n- if (this.div != null) {\n- // for Mozilla and Safari\n- this.div.style.opacity = this.opacity;\n-\n- // for IE\n- this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')';\n- }\n+ createRoot: function(suffix) {\n+ return this.nodeFactory(this.container.id + suffix, \"olv:group\");\n },\n \n+ /**************************************\n+ * *\n+ * GEOMETRY DRAWING FUNCTIONS *\n+ * *\n+ **************************************/\n+\n /**\n- * Method: setBorder\n- * Sets the border style of the popup.\n- *\n+ * Method: drawPoint\n+ * Render a point\n+ * \n * Parameters:\n- * border - {String} The border style value. eg 2px \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the point could not be drawn\n */\n- setBorder: function(border) {\n- if (border != undefined) {\n- this.border = border;\n- }\n-\n- if (this.div != null) {\n- this.div.style.border = this.border;\n- }\n+ drawPoint: function(node, geometry) {\n+ return this.drawCircle(node, geometry, 1);\n },\n \n /**\n- * Method: setContentHTML\n- * Allows the user to set the HTML content of the popup.\n- *\n+ * Method: drawCircle\n+ * Render a circle.\n+ * Size and Center a circle given geometry (x,y center) and radius\n+ * \n * Parameters:\n- * contentHTML - {String} HTML for the div.\n+ * node - {DOMElement}\n+ * geometry - {}\n+ * radius - {float}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the circle could not ne drawn\n */\n- setContentHTML: function(contentHTML) {\n-\n- if (contentHTML != null) {\n- this.contentHTML = contentHTML;\n- }\n-\n- if ((this.contentDiv != null) &&\n- (this.contentHTML != null) &&\n- (this.contentHTML != this.contentDiv.innerHTML)) {\n-\n- this.contentDiv.innerHTML = this.contentHTML;\n+ drawCircle: function(node, geometry, radius) {\n+ if (!isNaN(geometry.x) && !isNaN(geometry.y)) {\n+ var resolution = this.getResolution();\n \n- if (this.autoSize) {\n+ node.style.left = ((((geometry.x - this.featureDx) / resolution - this.offset.x) | 0) - radius) + \"px\";\n+ node.style.top = (((geometry.y / resolution - this.offset.y) | 0) - radius) + \"px\";\n \n- //if popup has images, listen for when they finish\n- // loading and resize accordingly\n- this.registerImageListeners();\n+ var diameter = radius * 2;\n \n- //auto size the popup to its current contents\n- this.updateSize();\n- }\n+ node.style.width = diameter + \"px\";\n+ node.style.height = diameter + \"px\";\n+ return node;\n }\n-\n+ return false;\n },\n \n+\n /**\n- * Method: registerImageListeners\n- * Called when an image contained by the popup loaded. this function\n- * updates the popup size, then unregisters the image load listener.\n+ * Method: drawLineString\n+ * Render a linestring.\n+ * \n+ * Parameters:\n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement}\n */\n- registerImageListeners: function() {\n-\n- // As the images load, this function will call updateSize() to \n- // resize the popup to fit the content div (which presumably is now\n- // bigger than when the image was not loaded).\n- // \n- // If the 'panMapIfOutOfView' property is set, we will pan the newly\n- // resized popup back into view.\n- // \n- // Note that this function, when called, will have 'popup' and \n- // 'img' properties in the context.\n- //\n- var onImgLoad = function() {\n- if (this.popup.id === null) { // this.popup has been destroyed!\n- return;\n- }\n- this.popup.updateSize();\n-\n- if (this.popup.visible() && this.popup.panMapIfOutOfView) {\n- this.popup.panIntoView();\n- }\n-\n- OpenLayers.Event.stopObserving(\n- this.img, \"load\", this.img._onImgLoad\n- );\n-\n- };\n-\n- //cycle through the images and if their size is 0x0, that means that \n- // they haven't been loaded yet, so we attach the listener, which \n- // will fire when the images finish loading and will resize the \n- // popup accordingly to its new size.\n- var images = this.contentDiv.getElementsByTagName(\"img\");\n- for (var i = 0, len = images.length; i < len; i++) {\n- var img = images[i];\n- if (img.width == 0 || img.height == 0) {\n-\n- var context = {\n- 'popup': this,\n- 'img': img\n- };\n-\n- //expando this function to the image itself before registering\n- // it. This way we can easily and properly unregister it.\n- img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);\n-\n- OpenLayers.Event.observe(img, 'load', img._onImgLoad);\n- }\n- }\n+ drawLineString: function(node, geometry) {\n+ return this.drawLine(node, geometry, false);\n },\n \n /**\n- * APIMethod: getSafeContentSize\n+ * Method: drawLinearRing\n+ * Render a linearring\n * \n * Parameters:\n- * size - {} Desired size to make the popup.\n+ * node - {DOMElement}\n+ * geometry - {}\n * \n * Returns:\n- * {} A size to make the popup which is neither smaller\n- * than the specified minimum size, nor bigger than the maximum \n- * size (which is calculated relative to the size of the viewport).\n+ * {DOMElement}\n */\n- getSafeContentSize: function(size) {\n-\n- var safeContentSize = size.clone();\n-\n- // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n- // must add that to the desired \"size\". \n- var contentDivPadding = this.getContentDivPadding();\n- var wPadding = contentDivPadding.left + contentDivPadding.right;\n- var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n-\n- // take into account the popup's 'padding' property\n- this.fixPadding();\n- wPadding += this.padding.left + this.padding.right;\n- hPadding += this.padding.top + this.padding.bottom;\n-\n- if (this.closeDiv) {\n- var closeDivWidth = parseInt(this.closeDiv.style.width);\n- wPadding += closeDivWidth + contentDivPadding.right;\n- }\n-\n- // prevent the popup from being smaller than a specified minimal size\n- if (this.minSize) {\n- safeContentSize.w = Math.max(safeContentSize.w,\n- (this.minSize.w - wPadding));\n- safeContentSize.h = Math.max(safeContentSize.h,\n- (this.minSize.h - hPadding));\n- }\n-\n- // prevent the popup from being bigger than a specified maximum size\n- if (this.maxSize) {\n- safeContentSize.w = Math.min(safeContentSize.w,\n- (this.maxSize.w - wPadding));\n- safeContentSize.h = Math.min(safeContentSize.h,\n- (this.maxSize.h - hPadding));\n- }\n-\n- //make sure the desired size to set doesn't result in a popup that \n- // is bigger than the map's viewport.\n- //\n- if (this.map && this.map.size) {\n+ drawLinearRing: function(node, geometry) {\n+ return this.drawLine(node, geometry, true);\n+ },\n \n- var extraX = 0,\n- extraY = 0;\n- if (this.keepInMap && !this.panMapIfOutOfView) {\n- var px = this.map.getPixelFromLonLat(this.lonlat);\n- switch (this.relativePosition) {\n- case \"tr\":\n- extraX = px.x;\n- extraY = this.map.size.h - px.y;\n- break;\n- case \"tl\":\n- extraX = this.map.size.w - px.x;\n- extraY = this.map.size.h - px.y;\n- break;\n- case \"bl\":\n- extraX = this.map.size.w - px.x;\n- extraY = px.y;\n- break;\n- case \"br\":\n- extraX = px.x;\n- extraY = px.y;\n- break;\n- default:\n- extraX = px.x;\n- extraY = this.map.size.h - px.y;\n- break;\n- }\n- }\n+ /**\n+ * Method: DrawLine\n+ * Render a line.\n+ * \n+ * Parameters:\n+ * node - {DOMElement}\n+ * geometry - {}\n+ * closeLine - {Boolean} Close the line? (make it a ring?)\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ drawLine: function(node, geometry, closeLine) {\n \n- var maxY = this.map.size.h -\n- this.map.paddingForPopups.top -\n- this.map.paddingForPopups.bottom -\n- hPadding - extraY;\n+ this.setNodeDimension(node, geometry);\n \n- var maxX = this.map.size.w -\n- this.map.paddingForPopups.left -\n- this.map.paddingForPopups.right -\n- wPadding - extraX;\n+ var resolution = this.getResolution();\n+ var numComponents = geometry.components.length;\n+ var parts = new Array(numComponents);\n \n- safeContentSize.w = Math.min(safeContentSize.w, maxX);\n- safeContentSize.h = Math.min(safeContentSize.h, maxY);\n+ var comp, x, y;\n+ for (var i = 0; i < numComponents; i++) {\n+ comp = geometry.components[i];\n+ x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0;\n+ y = (comp.y / resolution - this.offset.y) | 0;\n+ parts[i] = \" \" + x + \",\" + y + \" l \";\n }\n-\n- return safeContentSize;\n+ var end = (closeLine) ? \" x e\" : \" e\";\n+ node.path = \"m\" + parts.join(\"\") + end;\n+ return node;\n },\n \n /**\n- * Method: getContentDivPadding\n- * Glorious, oh glorious hack in order to determine the css 'padding' of \n- * the contentDiv. IE/Opera return null here unless we actually add the \n- * popup's main 'div' element (which contains contentDiv) to the DOM. \n- * So we make it invisible and then add it to the document temporarily. \n- *\n- * Once we've taken the padding readings we need, we then remove it \n- * from the DOM (it will actually get added to the DOM in \n- * Map.js's addPopup)\n- *\n+ * Method: drawPolygon\n+ * Render a polygon\n+ * \n+ * Parameters:\n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n * Returns:\n- * {}\n+ * {DOMElement}\n */\n- getContentDivPadding: function() {\n-\n- //use cached value if we have it\n- var contentDivPadding = this._contentDivPadding;\n- if (!contentDivPadding) {\n-\n- if (this.div.parentNode == null) {\n- //make the div invisible and add it to the page \n- this.div.style.display = \"none\";\n- document.body.appendChild(this.div);\n- }\n-\n- //read the padding settings from css, put them in an OL.Bounds \n- contentDivPadding = new OpenLayers.Bounds(\n- OpenLayers.Element.getStyle(this.contentDiv, \"padding-left\"),\n- OpenLayers.Element.getStyle(this.contentDiv, \"padding-bottom\"),\n- OpenLayers.Element.getStyle(this.contentDiv, \"padding-right\"),\n- OpenLayers.Element.getStyle(this.contentDiv, \"padding-top\")\n- );\n+ drawPolygon: function(node, geometry) {\n+ this.setNodeDimension(node, geometry);\n \n- //cache the value\n- this._contentDivPadding = contentDivPadding;\n+ var resolution = this.getResolution();\n \n- if (this.div.parentNode == document.body) {\n- //remove the div from the page and make it visible again\n- document.body.removeChild(this.div);\n- this.div.style.display = \"\";\n+ var path = [];\n+ var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;\n+ for (j = 0, jj = geometry.components.length; j < jj; j++) {\n+ path.push(\"m\");\n+ points = geometry.components[j].components;\n+ // we only close paths of interior rings with area\n+ area = (j === 0);\n+ first = null;\n+ second = null;\n+ for (i = 0, ii = points.length; i < ii; i++) {\n+ comp = points[i];\n+ x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0;\n+ y = (comp.y / resolution - this.offset.y) | 0;\n+ pathComp = \" \" + x + \",\" + y;\n+ path.push(pathComp);\n+ if (i == 0) {\n+ path.push(\" l\");\n+ }\n+ if (!area) {\n+ // IE improperly renders sub-paths that have no area.\n+ // Instead of checking the area of every ring, we confirm\n+ // the ring has at least three distinct points. This does\n+ // not catch all non-zero area cases, but it greatly improves\n+ // interior ring digitizing and is a minor performance hit\n+ // when rendering rings with many points.\n+ if (!first) {\n+ first = pathComp;\n+ } else if (first != pathComp) {\n+ if (!second) {\n+ second = pathComp;\n+ } else if (second != pathComp) {\n+ // stop looking\n+ area = true;\n+ }\n+ }\n+ }\n }\n+ path.push(area ? \" x \" : \" \");\n }\n- return contentDivPadding;\n+ path.push(\"e\");\n+ node.path = path.join(\"\");\n+ return node;\n },\n \n /**\n- * Method: addCloseBox\n+ * Method: drawRectangle\n+ * Render a rectangle\n * \n * Parameters:\n- * callback - {Function} The callback to be called when the close button\n- * is clicked.\n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement}\n */\n- addCloseBox: function(callback) {\n-\n- this.closeDiv = OpenLayers.Util.createDiv(\n- this.id + \"_close\", null, {\n- w: 17,\n- h: 17\n- }\n- );\n- this.closeDiv.className = \"olPopupCloseBox\";\n-\n- // use the content div's css padding to determine if we should\n- // padd the close div\n- var contentDivPadding = this.getContentDivPadding();\n+ drawRectangle: function(node, geometry) {\n+ var resolution = this.getResolution();\n \n- this.closeDiv.style.right = contentDivPadding.right + \"px\";\n- this.closeDiv.style.top = contentDivPadding.top + \"px\";\n- this.groupDiv.appendChild(this.closeDiv);\n+ node.style.left = (((geometry.x - this.featureDx) / resolution - this.offset.x) | 0) + \"px\";\n+ node.style.top = ((geometry.y / resolution - this.offset.y) | 0) + \"px\";\n+ node.style.width = ((geometry.width / resolution) | 0) + \"px\";\n+ node.style.height = ((geometry.height / resolution) | 0) + \"px\";\n \n- var closePopup = callback || function(e) {\n- this.hide();\n- OpenLayers.Event.stop(e);\n- };\n- OpenLayers.Event.observe(this.closeDiv, \"touchend\",\n- OpenLayers.Function.bindAsEventListener(closePopup, this));\n- OpenLayers.Event.observe(this.closeDiv, \"click\",\n- OpenLayers.Function.bindAsEventListener(closePopup, this));\n+ return node;\n },\n \n /**\n- * Method: panIntoView\n- * Pans the map such that the popup is totaly viewable (if necessary)\n+ * Method: drawText\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * featureId - {String}\n+ * style -\n+ * location - {}\n */\n- panIntoView: function() {\n+ drawText: function(featureId, style, location) {\n+ var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, \"olv:rect\");\n+ var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + \"_textbox\", \"olv:textbox\");\n \n- var mapSize = this.map.getSize();\n+ var resolution = this.getResolution();\n+ label.style.left = (((location.x - this.featureDx) / resolution - this.offset.x) | 0) + \"px\";\n+ label.style.top = ((location.y / resolution - this.offset.y) | 0) + \"px\";\n+ label.style.flip = \"y\";\n \n- //start with the top left corner of the popup, in px, \n- // relative to the viewport\n- var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(\n- parseInt(this.div.style.left),\n- parseInt(this.div.style.top)\n- ));\n- var newTL = origTL.clone();\n+ textbox.innerText = style.label;\n \n- //new left (compare to margins, using this.size to calculate right)\n- if (origTL.x < this.map.paddingForPopups.left) {\n- newTL.x = this.map.paddingForPopups.left;\n- } else\n- if ((origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {\n- newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;\n+ if (style.cursor != \"inherit\" && style.cursor != null) {\n+ textbox.style.cursor = style.cursor;\n+ }\n+ if (style.fontColor) {\n+ textbox.style.color = style.fontColor;\n+ }\n+ if (style.fontOpacity) {\n+ textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';\n+ }\n+ if (style.fontFamily) {\n+ textbox.style.fontFamily = style.fontFamily;\n+ }\n+ if (style.fontSize) {\n+ textbox.style.fontSize = style.fontSize;\n+ }\n+ if (style.fontWeight) {\n+ textbox.style.fontWeight = style.fontWeight;\n+ }\n+ if (style.fontStyle) {\n+ textbox.style.fontStyle = style.fontStyle;\n+ }\n+ if (style.labelSelect === true) {\n+ label._featureId = featureId;\n+ textbox._featureId = featureId;\n+ textbox._geometry = location;\n+ textbox._geometryClass = location.CLASS_NAME;\n }\n+ textbox.style.whiteSpace = \"nowrap\";\n+ // fun with IE: IE7 in standards compliant mode does not display any\n+ // text with a left inset of 0. So we set this to 1px and subtract one\n+ // pixel later when we set label.style.left\n+ textbox.inset = \"1px,0px,0px,0px\";\n \n- //new top (compare to margins, using this.size to calculate bottom)\n- if (origTL.y < this.map.paddingForPopups.top) {\n- newTL.y = this.map.paddingForPopups.top;\n- } else\n- if ((origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {\n- newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;\n+ if (!label.parentNode) {\n+ label.appendChild(textbox);\n+ this.textRoot.appendChild(label);\n }\n \n- var dx = origTL.x - newTL.x;\n- var dy = origTL.y - newTL.y;\n+ var align = style.labelAlign || \"cm\";\n+ if (align.length == 1) {\n+ align += \"m\";\n+ }\n+ var xshift = textbox.clientWidth *\n+ (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0, 1)]);\n+ var yshift = textbox.clientHeight *\n+ (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1, 1)]);\n+ label.style.left = parseInt(label.style.left) - xshift - 1 + \"px\";\n+ label.style.top = parseInt(label.style.top) + yshift + \"px\";\n \n- this.map.pan(dx, dy);\n },\n \n- /** \n- * Method: registerEvents\n- * Registers events on the popup.\n- *\n- * Do this in a separate function so that subclasses can \n- * choose to override it if they wish to deal differently\n- * with mouse events\n+ /**\n+ * Method: moveRoot\n+ * moves this renderer's root to a different renderer.\n * \n- * Note in the following handler functions that some special\n- * care is needed to deal correctly with mousing and popups. \n- * \n- * Because the user might select the zoom-rectangle option and\n- * then drag it over a popup, we need a safe way to allow the\n- * mousemove and mouseup events to pass through the popup when\n- * they are initiated from outside. The same procedure is needed for\n- * touchmove and touchend events.\n+ * Parameters:\n+ * renderer - {} target renderer for the moved root\n+ * root - {DOMElement} optional root node. To be used when this renderer\n+ * holds roots from multiple layers to tell this method which one to\n+ * detach\n * \n- * Otherwise, we want to essentially kill the event propagation\n- * for all other events, though we have to do so carefully, \n- * without disabling basic html functionality, like clicking on \n- * hyperlinks or drag-selecting text.\n+ * Returns:\n+ * {Boolean} true if successful, false otherwise\n */\n- registerEvents: function() {\n- this.events = new OpenLayers.Events(this, this.div, null, true);\n-\n- function onTouchstart(evt) {\n- OpenLayers.Event.stop(evt, true);\n+ moveRoot: function(renderer) {\n+ var layer = this.map.getLayer(renderer.container.id);\n+ if (layer instanceof OpenLayers.Layer.Vector.RootContainer) {\n+ layer = this.map.getLayer(this.container.id);\n }\n- this.events.on({\n- \"mousedown\": this.onmousedown,\n- \"mousemove\": this.onmousemove,\n- \"mouseup\": this.onmouseup,\n- \"click\": this.onclick,\n- \"mouseout\": this.onmouseout,\n- \"dblclick\": this.ondblclick,\n- \"touchstart\": onTouchstart,\n- scope: this\n- });\n-\n+ layer && layer.renderer.clear();\n+ OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);\n+ layer && layer.redraw();\n },\n \n- /** \n- * Method: onmousedown \n- * When mouse goes down within the popup, make a note of\n- * it locally, and then do not propagate the mousedown \n- * (but do so safely so that user can select text inside)\n+ /**\n+ * Method: importSymbol\n+ * add a new symbol definition from the rendererer's symbol hash\n * \n * Parameters:\n- * evt - {Event} \n- */\n- onmousedown: function(evt) {\n- this.mousedown = true;\n- OpenLayers.Event.stop(evt, true);\n- },\n-\n- /** \n- * Method: onmousemove\n- * If the drag was started within the popup, then \n- * do not propagate the mousemove (but do so safely\n- * so that user can select text inside)\n+ * graphicName - {String} name of the symbol to import\n * \n- * Parameters:\n- * evt - {Event} \n+ * Returns:\n+ * {Object} - hash of {DOMElement} \"symbol\" and {Number} \"size\"\n */\n- onmousemove: function(evt) {\n- if (this.mousedown) {\n- OpenLayers.Event.stop(evt, true);\n+ importSymbol: function(graphicName) {\n+ var id = this.container.id + \"-\" + graphicName;\n+\n+ // check if symbol already exists in the cache\n+ var cache = this.symbolCache[id];\n+ if (cache) {\n+ return cache;\n }\n- },\n \n- /** \n- * Method: onmouseup\n- * When mouse comes up within the popup, after going down \n- * in it, reset the flag, and then (once again) do not \n- * propagate the event, but do so safely so that user can \n- * select text inside\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- onmouseup: function(evt) {\n- if (this.mousedown) {\n- this.mousedown = false;\n- OpenLayers.Event.stop(evt, true);\n+ var symbol = OpenLayers.Renderer.symbol[graphicName];\n+ if (!symbol) {\n+ throw new Error(graphicName + ' is not a valid symbol name');\n }\n- },\n \n- /**\n- * Method: onclick\n- * Ignore clicks, but allowing default browser handling\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- onclick: function(evt) {\n- OpenLayers.Event.stop(evt, true);\n- },\n+ var symbolExtent = new OpenLayers.Bounds(\n+ Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);\n \n- /** \n- * Method: onmouseout\n- * When mouse goes out of the popup set the flag to false so that\n- * if they let go and then drag back in, we won't be confused.\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- onmouseout: function(evt) {\n- this.mousedown = false;\n- },\n+ var pathitems = [\"m\"];\n+ for (var i = 0; i < symbol.length; i = i + 2) {\n+ var x = symbol[i];\n+ var y = symbol[i + 1];\n+ symbolExtent.left = Math.min(symbolExtent.left, x);\n+ symbolExtent.bottom = Math.min(symbolExtent.bottom, y);\n+ symbolExtent.right = Math.max(symbolExtent.right, x);\n+ symbolExtent.top = Math.max(symbolExtent.top, y);\n \n- /** \n- * Method: ondblclick\n- * Ignore double-clicks, but allowing default browser handling\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- ondblclick: function(evt) {\n- OpenLayers.Event.stop(evt, true);\n+ pathitems.push(x);\n+ pathitems.push(y);\n+ if (i == 0) {\n+ pathitems.push(\"l\");\n+ }\n+ }\n+ pathitems.push(\"x e\");\n+ var path = pathitems.join(\" \");\n+\n+ var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;\n+ if (diff > 0) {\n+ symbolExtent.bottom = symbolExtent.bottom - diff;\n+ symbolExtent.top = symbolExtent.top + diff;\n+ } else {\n+ symbolExtent.left = symbolExtent.left + diff;\n+ symbolExtent.right = symbolExtent.right - diff;\n+ }\n+\n+ cache = {\n+ path: path,\n+ size: symbolExtent.getWidth(), // equals getHeight() now\n+ left: symbolExtent.left,\n+ bottom: symbolExtent.bottom\n+ };\n+ this.symbolCache[id] = cache;\n+\n+ return cache;\n },\n \n- CLASS_NAME: \"OpenLayers.Popup\"\n+ CLASS_NAME: \"OpenLayers.Renderer.VML\"\n });\n \n-OpenLayers.Popup.WIDTH = 200;\n-OpenLayers.Popup.HEIGHT = 200;\n-OpenLayers.Popup.COLOR = \"white\";\n-OpenLayers.Popup.OPACITY = 1;\n-OpenLayers.Popup.BORDER = \"0px\";\n+/**\n+ * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT\n+ * {Object}\n+ */\n+OpenLayers.Renderer.VML.LABEL_SHIFT = {\n+ \"l\": 0,\n+ \"c\": .5,\n+ \"r\": 1,\n+ \"t\": 0,\n+ \"m\": .5,\n+ \"b\": 1\n+};\n /* ======================================================================\n- OpenLayers/Marker.js\n+ OpenLayers/Renderer/Canvas.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Events.js\n- * @requires OpenLayers/Icon.js\n+ * @requires OpenLayers/Renderer.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+ * Class: OpenLayers.Renderer.Canvas \n+ * A renderer based on the 2D 'canvas' drawing element.\n+ * \n+ * Inherits:\n+ * - \n */\n-OpenLayers.Marker = OpenLayers.Class({\n+OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {\n \n- /** \n- * Property: icon \n- * {} The icon used by this marker.\n+ /**\n+ * APIProperty: hitDetection\n+ * {Boolean} Allow for hit detection of features. Default is true.\n */\n- icon: null,\n+ hitDetection: true,\n \n- /** \n- * Property: lonlat \n- * {} location of object\n+ /**\n+ * Property: hitOverflow\n+ * {Number} The method for converting feature identifiers to color values\n+ * supports 16777215 sequential values. Two features cannot be \n+ * predictably detected if their identifiers differ by more than this\n+ * value. The hitOverflow allows for bigger numbers (but the \n+ * difference in values is still limited).\n */\n- lonlat: null,\n+ hitOverflow: 0,\n \n- /** \n- * Property: events \n- * {} the event handler.\n+ /**\n+ * Property: canvas\n+ * {Canvas} The canvas context object.\n */\n- events: null,\n+ canvas: null,\n \n- /** \n- * Property: map \n- * {} the map this marker is attached to\n+ /**\n+ * Property: features\n+ * {Object} Internal object of feature/style pairs for use in redrawing the layer.\n */\n- map: null,\n+ features: 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+ * Property: pendingRedraw\n+ * {Boolean} The renderer needs a redraw call to render features added while\n+ * the renderer was locked.\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+ pendingRedraw: false,\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+ * Property: cachedSymbolBounds\n+ * {Object} Internal cache of calculated symbol extents.\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+ cachedSymbolBounds: {},\n \n- if (this.icon != null) {\n- this.icon.destroy();\n- this.icon = null;\n+ /**\n+ * Constructor: OpenLayers.Renderer.Canvas\n+ *\n+ * Parameters:\n+ * containerID - {}\n+ * options - {Object} Optional properties to be set on the renderer.\n+ */\n+ initialize: function(containerID, options) {\n+ OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n+ this.root = document.createElement(\"canvas\");\n+ this.container.appendChild(this.root);\n+ this.canvas = this.root.getContext(\"2d\");\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitCanvas = document.createElement(\"canvas\");\n+ this.hitContext = this.hitCanvas.getContext(\"2d\");\n }\n },\n \n- /** \n- * Method: draw\n- * Calls draw on the icon, and returns that output.\n- * \n+ /**\n+ * Method: setExtent\n+ * Set the visible part of the layer.\n+ *\n * Parameters:\n- * px - {}\n- * \n+ * extent - {}\n+ * resolutionChanged - {Boolean}\n+ *\n * Returns:\n- * {DOMElement} A new DOM Image with this marker's icon set at the \n- * location passed-in\n+ * {Boolean} true to notify the layer that the new extent does not exceed\n+ * the coordinate range, and the features will not need to be redrawn.\n+ * False otherwise.\n */\n- draw: function(px) {\n- return this.icon.draw(px);\n+ setExtent: function() {\n+ OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n+ // always redraw features\n+ return false;\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+ * Method: eraseGeometry\n+ * Erase a geometry from the renderer. Because the Canvas renderer has\n+ * 'memory' of the features that it has drawn, we have to remove the\n+ * feature so it doesn't redraw. \n+ * \n * Parameters:\n- * px - {|Object} the pixel position to move to.\n- * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n+ * geometry - {}\n+ * featureId - {String}\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+ eraseGeometry: function(geometry, featureId) {\n+ this.eraseFeatures(this.features[featureId][0]);\n },\n \n /**\n- * APIMethod: isDrawn\n+ * APIMethod: supported\n * \n * Returns:\n- * {Boolean} Whether or not the marker is drawn.\n+ * {Boolean} Whether or not the browser supports the renderer class\n */\n- isDrawn: function() {\n- var isDrawn = (this.icon && this.icon.isDrawn());\n- return isDrawn;\n+ supported: function() {\n+ return OpenLayers.CANVAS_SUPPORTED;\n },\n \n /**\n- * Method: onScreen\n+ * Method: setSize\n+ * Sets the size of the drawing surface.\n *\n- * Returns:\n- * {Boolean} Whether or not the marker is currently visible on screen.\n+ * Once the size is updated, redraw the canvas.\n+ *\n+ * Parameters:\n+ * size - {} \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+ setSize: function(size) {\n+ this.size = size.clone();\n+ var root = this.root;\n+ root.style.width = size.w + \"px\";\n+ root.style.height = size.h + \"px\";\n+ root.width = size.w;\n+ root.height = size.h;\n+ this.resolution = null;\n+ if (this.hitDetection) {\n+ var hitCanvas = this.hitCanvas;\n+ hitCanvas.style.width = size.w + \"px\";\n+ hitCanvas.style.height = size.h + \"px\";\n+ hitCanvas.width = size.w;\n+ hitCanvas.height = size.h;\n }\n- return onScreen;\n },\n \n /**\n- * Method: inflate\n- * Englarges the markers icon by the specified ratio.\n+ * Method: drawFeature\n+ * Draw the feature. Stores the feature in the features list,\n+ * then redraws the layer. \n *\n * Parameters:\n- * inflate - {float} the ratio to enlarge the marker by (passing 2\n- * will double the size).\n+ * feature - {} \n+ * style - {} \n+ *\n+ * Returns:\n+ * {Boolean} The feature has been drawn completely. If the feature has no\n+ * geometry, undefined will be returned. If the feature is not rendered\n+ * for other reasons, false will be returned.\n */\n- 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+ drawFeature: function(feature, style) {\n+ var rendered;\n+ if (feature.geometry) {\n+ style = this.applyDefaultSymbolizer(style || feature.style);\n+ // don't render if display none or feature outside extent\n+ var bounds = feature.geometry.getBounds();\n+\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent();\n+ }\n+\n+ var intersects = bounds && bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n });\n+\n+ rendered = (style.display !== \"none\") && !!bounds && intersects;\n+ if (rendered) {\n+ // keep track of what we have rendered for redraw\n+ this.features[feature.id] = [feature, style];\n+ } else {\n+ // remove from features tracked for redraw\n+ delete(this.features[feature.id]);\n+ }\n+ this.pendingRedraw = true;\n }\n+ if (this.pendingRedraw && !this.locked) {\n+ this.redraw();\n+ this.pendingRedraw = false;\n+ }\n+ return rendered;\n },\n \n /** \n- * Method: setOpacity\n- * Change the opacity of the marker by changin the opacity of \n- * its icon\n- * \n+ * Method: drawGeometry\n+ * Used when looping (in redraw) over the features; draws\n+ * the canvas. \n+ *\n * Parameters:\n- * opacity - {float} Specified as fraction (0.4, etc)\n+ * geometry - {} \n+ * style - {Object} \n */\n- setOpacity: function(opacity) {\n- this.icon.setOpacity(opacity);\n+ drawGeometry: function(geometry, style, featureId) {\n+ var className = geometry.CLASS_NAME;\n+ if ((className == \"OpenLayers.Geometry.Collection\") ||\n+ (className == \"OpenLayers.Geometry.MultiPoint\") ||\n+ (className == \"OpenLayers.Geometry.MultiLineString\") ||\n+ (className == \"OpenLayers.Geometry.MultiPolygon\")) {\n+ for (var i = 0; i < geometry.components.length; i++) {\n+ this.drawGeometry(geometry.components[i], style, featureId);\n+ }\n+ return;\n+ }\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ this.drawPoint(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ this.drawLineString(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ this.drawLinearRing(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ this.drawPolygon(geometry, style, featureId);\n+ break;\n+ default:\n+ break;\n+ }\n },\n \n /**\n- * Method: setUrl\n- * Change URL of the Icon Image.\n+ * Method: drawExternalGraphic\n+ * Called to draw External graphics. \n * \n- * url - {String} \n+ * Parameters: \n+ * geometry - {}\n+ * style - {Object}\n+ * featureId - {String}\n */\n- setUrl: function(url) {\n- this.icon.setUrl(url);\n+ drawExternalGraphic: function(geometry, style, featureId) {\n+ var img = new Image();\n+\n+ var title = style.title || style.graphicTitle;\n+ if (title) {\n+ img.title = title;\n+ }\n+\n+ var width = style.graphicWidth || style.graphicHeight;\n+ var height = style.graphicHeight || style.graphicWidth;\n+ width = width ? width : style.pointRadius * 2;\n+ height = height ? height : style.pointRadius * 2;\n+ var xOffset = (style.graphicXOffset != undefined) ?\n+ style.graphicXOffset : -(0.5 * width);\n+ var yOffset = (style.graphicYOffset != undefined) ?\n+ style.graphicYOffset : -(0.5 * height);\n+\n+ var opacity = style.graphicOpacity || style.fillOpacity;\n+\n+ var onLoad = function() {\n+ if (!this.features[featureId]) {\n+ return;\n+ }\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var x = (p0 + xOffset) | 0;\n+ var y = (p1 + yOffset) | 0;\n+ var canvas = this.canvas;\n+ canvas.globalAlpha = opacity;\n+ var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||\n+ (OpenLayers.Renderer.Canvas.drawImageScaleFactor =\n+ /android 2.1/.test(navigator.userAgent.toLowerCase()) ?\n+ // 320 is the screen width of the G1 phone, for\n+ // which drawImage works out of the box.\n+ 320 / window.screen.width : 1\n+ );\n+ canvas.drawImage(\n+ img, x * factor, y * factor, width * factor, height * factor\n+ );\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId);\n+ this.hitContext.fillRect(x, y, width, height);\n+ }\n+ }\n+ };\n+\n+ img.onload = OpenLayers.Function.bind(onLoad, this);\n+ img.src = style.externalGraphic;\n },\n \n- /** \n- * Method: display\n- * Hide or show the icon\n+ /**\n+ * Method: drawNamedSymbol\n+ * Called to draw Well Known Graphic Symbol Name. \n+ * This method is only called by the renderer itself.\n * \n- * display - {Boolean} \n+ * Parameters: \n+ * geometry - {}\n+ * style - {Object}\n+ * featureId - {String}\n */\n- display: function(display) {\n- this.icon.display(display);\n- },\n+ drawNamedSymbol: function(geometry, style, featureId) {\n+ var x, y, cx, cy, i, symbolBounds, scaling, angle;\n+ var unscaledStrokeWidth;\n+ var deg2rad = Math.PI / 180.0;\n \n- CLASS_NAME: \"OpenLayers.Marker\"\n-});\n+ var symbol = OpenLayers.Renderer.symbol[style.graphicName];\n \n+ if (!symbol) {\n+ throw new Error(style.graphicName + ' is not a valid symbol name');\n+ }\n \n-/**\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+ if (!symbol.length || symbol.length < 2) return;\n \n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n \n-/* ======================================================================\n- OpenLayers/Events/buttonclick.js\n- ====================================================================== */\n+ if (isNaN(p0) || isNaN(p1)) return;\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ // Use rounded line caps\n+ this.canvas.lineCap = \"round\";\n+ this.canvas.lineJoin = \"round\";\n \n-/**\n- * @requires OpenLayers/Events.js\n- */\n+ if (this.hitDetection) {\n+ this.hitContext.lineCap = \"round\";\n+ this.hitContext.lineJoin = \"round\";\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- */\n-OpenLayers.Events.buttonclick = OpenLayers.Class({\n+ // Scale and rotate symbols, using precalculated bounds whenever possible.\n+ if (style.graphicName in this.cachedSymbolBounds) {\n+ symbolBounds = this.cachedSymbolBounds[style.graphicName];\n+ } else {\n+ symbolBounds = new OpenLayers.Bounds();\n+ for (i = 0; i < symbol.length; i += 2) {\n+ symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1]));\n+ }\n+ this.cachedSymbolBounds[style.graphicName] = symbolBounds;\n+ }\n+\n+ // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.\n+ // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)\n+ this.canvas.save();\n+ if (this.hitDetection) {\n+ this.hitContext.save();\n+ }\n+\n+ // Step 3: place symbol at the desired location\n+ this.canvas.translate(p0, p1);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(p0, p1);\n+ }\n+\n+ // Step 2a. rotate the symbol if necessary\n+ angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.\n+ if (!isNaN(angle)) {\n+ this.canvas.rotate(angle);\n+ if (this.hitDetection) {\n+ this.hitContext.rotate(angle);\n+ }\n+ }\n+\n+ // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.\n+ scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());\n+ this.canvas.scale(scaling, scaling);\n+ if (this.hitDetection) {\n+ this.hitContext.scale(scaling, scaling);\n+ }\n+\n+ // Step 1: center the symbol at the origin \n+ cx = symbolBounds.getCenterLonLat().lon;\n+ cy = symbolBounds.getCenterLonLat().lat;\n+ this.canvas.translate(-cx, -cy);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(-cx, -cy);\n+ }\n+\n+ // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)\n+ // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.\n+ unscaledStrokeWidth = style.strokeWidth;\n+ style.strokeWidth = unscaledStrokeWidth / scaling;\n+\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y);\n+ }\n+ this.canvas.closePath();\n+ this.canvas.fill();\n+\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.hitContext.lineTo(x, y);\n+ }\n+ this.hitContext.closePath();\n+ this.hitContext.fill();\n+ }\n+ }\n+\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y);\n+ }\n+ this.canvas.closePath();\n+ this.canvas.stroke();\n+\n+\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style, scaling);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.hitContext.moveTo(x, y);\n+ this.hitContext.lineTo(x, y);\n+ }\n+ this.hitContext.closePath();\n+ this.hitContext.stroke();\n+ }\n+\n+ }\n+\n+ style.strokeWidth = unscaledStrokeWidth;\n+ this.canvas.restore();\n+ if (this.hitDetection) {\n+ this.hitContext.restore();\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ },\n \n /**\n- * Property: target\n- * {} The events instance that the buttonclick event will\n- * be triggered on.\n+ * Method: setCanvasStyle\n+ * Prepare the canvas for drawing by setting various global settings.\n+ *\n+ * Parameters:\n+ * type - {String} one of 'stroke', 'fill', or 'reset'\n+ * style - {Object} Symbolizer hash\n */\n- target: null,\n+ setCanvasStyle: function(type, style) {\n+ if (type === \"fill\") {\n+ this.canvas.globalAlpha = style['fillOpacity'];\n+ this.canvas.fillStyle = style['fillColor'];\n+ } else if (type === \"stroke\") {\n+ this.canvas.globalAlpha = style['strokeOpacity'];\n+ this.canvas.strokeStyle = style['strokeColor'];\n+ this.canvas.lineWidth = style['strokeWidth'];\n+ } else {\n+ this.canvas.globalAlpha = 0;\n+ this.canvas.lineWidth = 1;\n+ }\n+ },\n \n /**\n- * 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+ * Method: featureIdToHex\n+ * Convert a feature ID string into an RGB hex string.\n+ *\n+ * Parameters:\n+ * featureId - {String} Feature id\n+ *\n+ * Returns:\n+ * {String} RGB hex string.\n */\n- events: [\n- 'mousedown', 'mouseup', 'click', 'dblclick',\n- 'touchstart', 'touchmove', 'touchend', 'keydown'\n- ],\n+ featureIdToHex: function(featureId) {\n+ var id = Number(featureId.split(\"_\").pop()) + 1; // zero for no feature\n+ if (id >= 16777216) {\n+ this.hitOverflow = id - 16777215;\n+ id = id % 16777216 + 1;\n+ }\n+ var hex = \"000000\" + id.toString(16);\n+ var len = hex.length;\n+ hex = \"#\" + hex.substring(len - 6, len);\n+ return hex;\n+ },\n \n /**\n- * Property: startRegEx\n- * {RegExp} Regular expression to test Event.type for events that start\n- * a buttonclick sequence.\n+ * Method: setHitContextStyle\n+ * Prepare the hit canvas for drawing by setting various global settings.\n+ *\n+ * Parameters:\n+ * type - {String} one of 'stroke', 'fill', or 'reset'\n+ * featureId - {String} The feature id.\n+ * symbolizer - {} The symbolizer.\n */\n- startRegEx: /^mousedown|touchstart$/,\n+ setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {\n+ var hex = this.featureIdToHex(featureId);\n+ if (type == \"fill\") {\n+ this.hitContext.globalAlpha = 1.0;\n+ this.hitContext.fillStyle = hex;\n+ } else if (type == \"stroke\") {\n+ this.hitContext.globalAlpha = 1.0;\n+ this.hitContext.strokeStyle = hex;\n+ // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol \n+ // on a transformed canvas, so the antialias width bump has to scale as well.\n+ if (typeof strokeScaling === \"undefined\") {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2;\n+ } else {\n+ if (!isNaN(strokeScaling)) {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling;\n+ }\n+ }\n+ } else {\n+ this.hitContext.globalAlpha = 0;\n+ this.hitContext.lineWidth = 1;\n+ }\n+ },\n \n /**\n- * Property: cancelRegEx\n- * {RegExp} Regular expression to test Event.type for events that cancel\n- * a buttonclick sequence.\n+ * Method: drawPoint\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {}\n+ * style - {Object}\n+ * featureId - {String}\n */\n- cancelRegEx: /^touchmove$/,\n+ drawPoint: function(geometry, style, featureId) {\n+ if (style.graphic !== false) {\n+ if (style.externalGraphic) {\n+ this.drawExternalGraphic(geometry, style, featureId);\n+ } else if (style.graphicName && (style.graphicName != \"circle\")) {\n+ this.drawNamedSymbol(geometry, style, featureId);\n+ } else {\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var twoPi = Math.PI * 2;\n+ var radius = style.pointRadius;\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.fill();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.fill();\n+ }\n+ }\n+\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.stroke();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.stroke();\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ }\n+ }\n+ }\n+ }\n+ },\n \n /**\n- * Property: completeRegEx\n- * {RegExp} Regular expression to test Event.type for events that complete\n- * a buttonclick sequence.\n+ * Method: drawLineString\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {}\n+ * style - {Object}\n+ * featureId - {String}\n */\n- completeRegEx: /^mouseup|touchend$/,\n+ drawLineString: function(geometry, style, featureId) {\n+ style = OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style);\n+ this.drawLinearRing(geometry, style, featureId);\n+ },\n \n /**\n- * Property: startEvt\n- * {Event} The event that started the click sequence\n+ * Method: drawLinearRing\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {}\n+ * style - {Object}\n+ * featureId - {String}\n */\n+ drawLinearRing: function(geometry, style, featureId) {\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"fill\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"fill\");\n+ }\n+ }\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"stroke\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"stroke\");\n+ }\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ },\n \n /**\n- * 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- * target - {} The events instance that the buttonclick\n- * event will be triggered on.\n+ * Method: renderPath\n+ * Render a path with stroke and optional fill.\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+ renderPath: function(context, geometry, style, featureId, type) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ context.beginPath();\n+ var start = this.getLocalXY(components[0]);\n+ var x = start[0];\n+ var y = start[1];\n+ if (!isNaN(x) && !isNaN(y)) {\n+ context.moveTo(start[0], start[1]);\n+ for (var i = 1; i < len; ++i) {\n+ var pt = this.getLocalXY(components[i]);\n+ context.lineTo(pt[0], pt[1]);\n+ }\n+ if (type === \"fill\") {\n+ context.fill();\n+ } else {\n+ context.stroke();\n+ }\n }\n },\n \n /**\n- * Method: destroy\n+ * Method: drawPolygon\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {}\n+ * style - {Object}\n+ * featureId - {String}\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+ drawPolygon: function(geometry, style, featureId) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ this.drawLinearRing(components[0], style, featureId);\n+ // erase inner rings\n+ for (var i = 1; i < len; ++i) {\n+ /** \n+ * Note that this is overly agressive. Here we punch holes through \n+ * all previously rendered features on the same canvas. A better \n+ * solution for polygons with interior rings would be to draw the \n+ * polygon on a sketch canvas first. We could erase all holes \n+ * there and then copy the drawing to the layer canvas. \n+ * TODO: http://trac.osgeo.org/openlayers/ticket/3130 \n+ */\n+ this.canvas.globalCompositeOperation = \"destination-out\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"destination-out\";\n+ }\n+ this.drawLinearRing(\n+ components[i],\n+ OpenLayers.Util.applyDefaults({\n+ stroke: false,\n+ fillOpacity: 1.0\n+ }, style),\n+ featureId\n+ );\n+ this.canvas.globalCompositeOperation = \"source-over\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"source-over\";\n+ }\n+ this.drawLinearRing(\n+ components[i],\n+ OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style),\n+ featureId\n+ );\n }\n- 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: drawText\n+ * This method is only called by the renderer itself.\n *\n- * Returns:\n- * {DOMElement} The button element, or undefined.\n+ * Parameters:\n+ * location - {}\n+ * style - {Object}\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+ drawText: function(location, style) {\n+ var pt = this.getLocalXY(location);\n+\n+ this.setCanvasStyle(\"reset\");\n+ this.canvas.fillStyle = style.fontColor;\n+ this.canvas.globalAlpha = style.fontOpacity || 1.0;\n+ var fontStyle = [style.fontStyle ? style.fontStyle : \"normal\",\n+ \"normal\", // \"font-variant\" not supported\n+ style.fontWeight ? style.fontWeight : \"normal\",\n+ style.fontSize ? style.fontSize : \"1em\",\n+ style.fontFamily ? style.fontFamily : \"sans-serif\"\n+ ].join(\" \");\n+ var labelRows = style.label.split('\\n');\n+ var numRows = labelRows.length;\n+ if (this.canvas.fillText) {\n+ // HTML5\n+ this.canvas.font = fontStyle;\n+ this.canvas.textAlign =\n+ OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||\n+ \"center\";\n+ this.canvas.textBaseline =\n+ OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||\n+ \"middle\";\n+ var vfactor =\n+ OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5;\n }\n- element = element.parentNode;\n- } while (--depth > 0 && element);\n- return button;\n+ var lineHeight =\n+ this.canvas.measureText('Mg').height ||\n+ this.canvas.measureText('xx').width;\n+ pt[1] += lineHeight * vfactor * (numRows - 1);\n+ for (var i = 0; i < numRows; i++) {\n+ if (style.labelOutlineWidth) {\n+ this.canvas.save();\n+ this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;\n+ this.canvas.strokeStyle = style.labelOutlineColor;\n+ this.canvas.lineWidth = style.labelOutlineWidth;\n+ this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight * i) + 1);\n+ this.canvas.restore();\n+ }\n+ this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight * i));\n+ }\n+ } else if (this.canvas.mozDrawText) {\n+ // Mozilla pre-Gecko1.9.1 (}\n+ */\n+ getLocalXY: function(point) {\n+ var resolution = this.getResolution();\n+ var extent = this.extent;\n+ var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));\n+ var y = ((extent.top / resolution) - point.y / resolution);\n+ return [x, y];\n+ },\n+\n+ /**\n+ * Method: clear\n+ * Clear all vectors from the renderer.\n+ */\n+ clear: function() {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height);\n+ }\n+ },\n+\n+ /**\n+ * Method: getFeatureIdFromEvent\n+ * Returns a feature id from an event on the renderer. \n+ * \n * Parameters:\n- * element - {DOMElement} The event target.\n+ * evt - {} \n+ *\n+ * Returns:\n+ * { 0 && element);\n- return ignore;\n+ }\n+ return feature;\n },\n \n /**\n- * Method: buttonClick\n- * Check if a button was clicked, and fire the buttonclick event\n- *\n+ * Method: eraseFeatures \n+ * This is called by the layer to erase features; removes the feature from\n+ * the list, then redraws the layer.\n+ * \n * Parameters:\n- * evt - {Event}\n+ * features - {Array()} \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+ eraseFeatures: function(features) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+ for (var i = 0; i < features.length; ++i) {\n+ delete this.features[features[i].id];\n+ }\n+ this.redraw();\n+ },\n \n- 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+ * Method: redraw\n+ * The real 'meat' of the function: any time things have changed,\n+ * redraw() can be called to loop over all the data and (you guessed\n+ * it) redraw it. Unlike Elements-based Renderers, we can't interact\n+ * with things once they're drawn, to remove them, for example, so\n+ * instead we have to just clear everything and draw from scratch.\n+ */\n+ redraw: function() {\n+ if (!this.locked) {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height);\n+ }\n+ var labelMap = [];\n+ var feature, geometry, style;\n+ var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();\n+ for (var id in this.features) {\n+ if (!this.features.hasOwnProperty(id)) {\n+ continue;\n }\n- if (this.startRegEx.test(evt.type)) {\n- this.startEvt = evt;\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n+ feature = this.features[id][0];\n+ geometry = feature.geometry;\n+ this.calculateFeatureDx(geometry.getBounds(), worldBounds);\n+ style = this.features[id][1];\n+ this.drawGeometry(geometry, style, feature.id);\n+ if (style.label) {\n+ labelMap.push([feature, style]);\n }\n- } else {\n- propagate = !this.ignore(OpenLayers.Event.element(evt));\n- delete this.startEvt;\n+ }\n+ var item;\n+ for (var i = 0, len = labelMap.length; i < len; ++i) {\n+ item = labelMap[i];\n+ this.drawText(item[0].geometry.getCentroid(), item[1]);\n }\n }\n- return propagate;\n- }\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Renderer.Canvas\"\n });\n+\n+/**\n+ * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN\n+ * {Object}\n+ */\n+OpenLayers.Renderer.Canvas.LABEL_ALIGN = {\n+ \"l\": \"left\",\n+ \"r\": \"right\",\n+ \"t\": \"top\",\n+ \"b\": \"bottom\"\n+};\n+\n+/**\n+ * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR\n+ * {Object}\n+ */\n+OpenLayers.Renderer.Canvas.LABEL_FACTOR = {\n+ \"l\": 0,\n+ \"r\": -1,\n+ \"t\": 0,\n+ \"b\": -1\n+};\n+\n+/**\n+ * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor\n+ * {Number} Scale factor to apply to the canvas drawImage arguments. This\n+ * is always 1 except for Android 2.1 devices, to work around\n+ * http://code.google.com/p/android/issues/detail?id=5141.\n+ */\n+OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;\n /* ======================================================================\n- OpenLayers/Events/featureclick.js\n+ OpenLayers/Renderer/SVG.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Renderer/Elements.js\n */\n \n /**\n- * Class: OpenLayers.Events.featureclick\n- *\n- * Extension event type for handling feature click events, including overlapping\n- * features. \n+ * Class: OpenLayers.Renderer.SVG\n * \n- * Event types provided by this extension:\n- * - featureclick \n+ * Inherits:\n+ * - \n */\n-OpenLayers.Events.featureclick = OpenLayers.Class({\n+OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {\n+\n+ /** \n+ * Property: xmlns\n+ * {String}\n+ */\n+ xmlns: \"http://www.w3.org/2000/svg\",\n \n /**\n- * Property: cache\n- * {Object} A cache of features under the mouse.\n+ * Property: xlinkns\n+ * {String}\n */\n- cache: null,\n+ xlinkns: \"http://www.w3.org/1999/xlink\",\n \n /**\n- * Property: map\n- * {} The map to register browser events on.\n+ * Constant: MAX_PIXEL\n+ * {Integer} Firefox has a limitation where values larger or smaller than \n+ * about 15000 in an SVG document lock the browser up. This \n+ * works around it.\n */\n- map: null,\n+ MAX_PIXEL: 15000,\n \n /**\n- * Property: provides\n- * {Array(String)} The event types provided by this extension.\n+ * Property: translationParameters\n+ * {Object} Hash with \"x\" and \"y\" properties\n */\n- provides: [\"featureclick\", \"nofeatureclick\", \"featureover\", \"featureout\"],\n+ translationParameters: null,\n \n /**\n- * Constructor: OpenLayers.Events.featureclick\n- * Create a new featureclick event type.\n- *\n- * Parameters:\n- * target - {} The events instance to create the events\n- * for.\n+ * Property: symbolMetrics\n+ * {Object} Cache for symbol metrics according to their svg coordinate\n+ * space. This is an object keyed by the symbol's id, and values are\n+ * an array of [width, centerX, centerY].\n */\n- initialize: function(target) {\n- this.target = target;\n- if (target.object instanceof OpenLayers.Map) {\n- this.setMap(target.object);\n- } else if (target.object instanceof OpenLayers.Layer.Vector) {\n- if (target.object.map) {\n- this.setMap(target.object.map);\n- } else {\n- target.object.events.register(\"added\", this, function(evt) {\n- this.setMap(target.object.map);\n- });\n- }\n- } else {\n- throw (\"Listeners for '\" + this.provides.join(\"', '\") +\n- \"' events can only be registered for OpenLayers.Layer.Vector \" +\n- \"or OpenLayers.Map instances\");\n- }\n- for (var i = this.provides.length - 1; i >= 0; --i) {\n- target.extensions[this.provides[i]] = true;\n- }\n- },\n+ symbolMetrics: null,\n \n /**\n- * Method: setMap\n- *\n+ * Constructor: OpenLayers.Renderer.SVG\n+ * \n * Parameters:\n- * map - {} The map to register browser events on.\n+ * containerID - {String}\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+ initialize: function(containerID) {\n+ if (!this.supported()) {\n+ return;\n+ }\n+ OpenLayers.Renderer.Elements.prototype.initialize.apply(this,\n+ arguments);\n+ this.translationParameters = {\n+ x: 0,\n+ y: 0\n+ };\n+\n+ this.symbolMetrics = {};\n },\n \n /**\n- * Method: start\n- * Sets startEvt = evt.\n- *\n- * Parameters:\n- * evt - {}\n+ * APIMethod: supported\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the browser supports the SVG renderer\n */\n- start: function(evt) {\n- this.startEvt = evt;\n+ supported: function() {\n+ var svgFeature = \"http://www.w3.org/TR/SVG11/feature#\";\n+ return (document.implementation &&\n+ (document.implementation.hasFeature(\"org.w3c.svg\", \"1.0\") ||\n+ document.implementation.hasFeature(svgFeature + \"SVG\", \"1.1\") ||\n+ document.implementation.hasFeature(svgFeature + \"BasicStructure\", \"1.1\")));\n },\n \n /**\n- * Method: cancel\n- * Deletes the start event.\n+ * Method: inValidRange\n+ * See #669 for more information\n *\n * Parameters:\n- * evt - {}\n+ * x - {Integer}\n+ * y - {Integer}\n+ * xyOnly - {Boolean} whether or not to just check for x and y, which means\n+ * to not take the current translation parameters into account if true.\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the 'x' and 'y' coordinates are in the \n+ * valid range.\n */\n- cancel: function(evt) {\n- delete this.startEvt;\n+ inValidRange: function(x, y, xyOnly) {\n+ var left = x + (xyOnly ? 0 : this.translationParameters.x);\n+ var top = y + (xyOnly ? 0 : this.translationParameters.y);\n+ return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&\n+ top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);\n },\n \n /**\n- * Method: onClick\n- * Listener for the click event.\n- *\n+ * Method: setExtent\n+ * \n * Parameters:\n- * evt - {}\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- 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- }\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+ setExtent: function(extent, resolutionChanged) {\n+ var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);\n+\n+ var resolution = this.getResolution(),\n+ left = -extent.left / resolution,\n+ top = extent.top / resolution;\n+\n+ // If the resolution has changed, start over changing the corner, because\n+ // the features will redraw.\n+ if (resolutionChanged) {\n+ this.left = left;\n+ this.top = top;\n+ // Set the viewbox\n+ var extentString = \"0 0 \" + this.size.w + \" \" + this.size.h;\n+\n+ this.rendererRoot.setAttributeNS(null, \"viewBox\", extentString);\n+ this.translate(this.xOffset, 0);\n+ return true;\n+ } else {\n+ var inRange = this.translate(left - this.left + this.xOffset, top - this.top);\n+ if (!inRange) {\n+ // recenter the coordinate system\n+ this.setExtent(extent, true);\n }\n+ return coordSysUnchanged && inRange;\n }\n },\n \n /**\n- * Method: onMousemove\n- * Listener for the mousemove event.\n- *\n+ * Method: translate\n+ * Transforms the SVG coordinate system\n+ * \n * Parameters:\n- * evt - {}\n+ * x - {Float}\n+ * y - {Float}\n+ * \n+ * Returns:\n+ * {Boolean} true if the translation parameters are in the valid coordinates\n+ * range, false otherwise.\n */\n- onMousemove: function(evt) {\n- delete this.startEvt;\n- var features = this.getFeatures(evt);\n- var over = {},\n- newly = [],\n- feature;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- over[feature.id] = feature;\n- if (!this.cache[feature.id]) {\n- newly.push(feature);\n- }\n- }\n- // check if already over features\n- var out = [];\n- for (var id in this.cache) {\n- feature = this.cache[id];\n- if (feature.layer && feature.layer.map) {\n- if (!over[feature.id]) {\n- out.push(feature);\n- }\n- } else {\n- // removed\n- delete this.cache[id];\n- }\n- }\n- // fire featureover events\n- var more;\n- for (i = 0, len = newly.length; i < len; ++i) {\n- feature = newly[i];\n- this.cache[feature.id] = feature;\n- more = this.triggerEvent(\"featureover\", {\n- feature: feature\n- });\n- if (more === false) {\n- break;\n- }\n- }\n- // fire featureout events\n- for (i = 0, len = out.length; i < len; ++i) {\n- feature = out[i];\n- delete this.cache[feature.id];\n- more = this.triggerEvent(\"featureout\", {\n- feature: feature\n- });\n- if (more === false) {\n- break;\n+ translate: function(x, y) {\n+ if (!this.inValidRange(x, y, true)) {\n+ return false;\n+ } else {\n+ var transformString = \"\";\n+ if (x || y) {\n+ transformString = \"translate(\" + x + \",\" + y + \")\";\n }\n+ this.root.setAttributeNS(null, \"transform\", transformString);\n+ this.translationParameters = {\n+ x: x,\n+ y: y\n+ };\n+ return true;\n }\n },\n \n /**\n- * Method: triggerEvent\n- * Determines where to trigger the event and triggers it.\n- *\n+ * Method: setSize\n+ * Sets the size of the drawing surface.\n+ * \n * Parameters:\n- * type - {String} The event type to trigger\n- * evt - {Object} The listener argument\n- *\n- * Returns:\n- * {Boolean} The last listener return.\n+ * size - {} The size of the drawing surface\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+ setSize: function(size) {\n+ OpenLayers.Renderer.prototype.setSize.apply(this, arguments);\n+\n+ this.rendererRoot.setAttributeNS(null, \"width\", this.size.w);\n+ this.rendererRoot.setAttributeNS(null, \"height\", this.size.h);\n },\n \n- /**\n- * Method: getFeatures\n- * Get all features at the given screen location.\n- *\n+ /** \n+ * Method: getNodeType \n+ * \n * Parameters:\n- * evt - {Object} Event object.\n- *\n+ * geometry - {}\n+ * style - {Object}\n+ * \n * Returns:\n- * {Array()} List of features at the given point.\n+ * {String} The corresponding node type for the specified geometry\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+ getNodeType: function(geometry, style) {\n+ var nodeType = null;\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ if (style.externalGraphic) {\n+ nodeType = \"image\";\n+ } else if (this.isComplexSymbol(style.graphicName)) {\n+ nodeType = \"svg\";\n+ } else {\n+ nodeType = \"circle\";\n }\n- }\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+ break;\n+ case \"OpenLayers.Geometry.Rectangle\":\n+ nodeType = \"rect\";\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ nodeType = \"polyline\";\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ nodeType = \"polygon\";\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ case \"OpenLayers.Geometry.Curve\":\n+ nodeType = \"path\";\n+ break;\n+ default:\n+ break;\n }\n- return features;\n+ return nodeType;\n },\n \n- /**\n- * APIMethod: destroy\n- * Clean up.\n+ /** \n+ * Method: setStyle\n+ * Use to set all the style attributes to a SVG node.\n+ * \n+ * Takes care to adjust stroke width and point radius to be\n+ * resolution-relative\n+ *\n+ * Parameters:\n+ * node - {SVGDomElement} An SVG element to decorate\n+ * style - {Object}\n+ * options - {Object} Currently supported options include \n+ * 'isFilled' {Boolean} and\n+ * 'isStroked' {Boolean}\n */\n- destroy: function() {\n- for (var i = this.provides.length - 1; i >= 0; --i) {\n- delete this.target.extensions[this.provides[i]];\n+ setStyle: function(node, style, options) {\n+ style = style || node._style;\n+ options = options || node._options;\n+\n+ var title = style.title || style.graphicTitle;\n+ if (title) {\n+ node.setAttributeNS(null, \"title\", title);\n+ //Standards-conformant SVG\n+ // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92 \n+ var titleNode = node.getElementsByTagName(\"title\");\n+ if (titleNode.length > 0) {\n+ titleNode[0].firstChild.textContent = title;\n+ } else {\n+ var label = this.nodeFactory(null, \"title\");\n+ label.textContent = title;\n+ node.appendChild(label);\n+ }\n }\n- this.map.events.un({\n- mousemove: this.onMousemove,\n- mousedown: this.start,\n- mouseup: this.onClick,\n- touchstart: this.start,\n- touchmove: this.cancel,\n- touchend: this.onClick,\n- scope: this\n- });\n- delete this.cache;\n- delete this.map;\n- delete this.target;\n- }\n \n-});\n+ var r = parseFloat(node.getAttributeNS(null, \"r\"));\n+ var widthFactor = 1;\n+ var pos;\n+ if (node._geometryClass == \"OpenLayers.Geometry.Point\" && r) {\n+ node.style.visibility = \"\";\n+ if (style.graphic === false) {\n+ node.style.visibility = \"hidden\";\n+ } else if (style.externalGraphic) {\n+ pos = this.getPosition(node);\n+ if (style.graphicWidth && style.graphicHeight) {\n+ node.setAttributeNS(null, \"preserveAspectRatio\", \"none\");\n+ }\n+ var width = style.graphicWidth || style.graphicHeight;\n+ var height = style.graphicHeight || style.graphicWidth;\n+ width = width ? width : style.pointRadius * 2;\n+ height = height ? height : style.pointRadius * 2;\n+ var xOffset = (style.graphicXOffset != undefined) ?\n+ style.graphicXOffset : -(0.5 * width);\n+ var yOffset = (style.graphicYOffset != undefined) ?\n+ style.graphicYOffset : -(0.5 * height);\n \n-/**\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+ var opacity = style.graphicOpacity || style.fillOpacity;\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+ node.setAttributeNS(null, \"x\", (pos.x + xOffset).toFixed());\n+ node.setAttributeNS(null, \"y\", (pos.y + yOffset).toFixed());\n+ node.setAttributeNS(null, \"width\", width);\n+ node.setAttributeNS(null, \"height\", height);\n+ node.setAttributeNS(this.xlinkns, \"xlink:href\", style.externalGraphic);\n+ node.setAttributeNS(null, \"style\", \"opacity: \" + opacity);\n+ node.onclick = OpenLayers.Event.preventDefault;\n+ } else if (this.isComplexSymbol(style.graphicName)) {\n+ // the symbol viewBox is three times as large as the symbol\n+ var offset = style.pointRadius * 3;\n+ var size = offset * 2;\n+ var src = this.importSymbol(style.graphicName);\n+ pos = this.getPosition(node);\n+ widthFactor = this.symbolMetrics[src.id][0] * 3 / size;\n \n-/**\n- * Class: OpenLayers.Events.featureout\n- *\n- * Extension event type for handling leaving a feature. \n- * \n- * Event types provided by this extension:\n- * - featureout \n- */\n-OpenLayers.Events.featureout = OpenLayers.Events.featureclick;\n-/* ======================================================================\n- OpenLayers/Marker/Box.js\n- ====================================================================== */\n+ // remove the node from the dom before we modify it. This\n+ // prevents various rendering issues in Safari and FF\n+ var parent = node.parentNode;\n+ var nextSibling = node.nextSibling;\n+ if (parent) {\n+ parent.removeChild(node);\n+ }\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ // The more appropriate way to implement this would be use/defs,\n+ // but due to various issues in several browsers, it is safer to\n+ // copy the symbols instead of referencing them. \n+ // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985 \n+ // and this email thread\n+ // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html\n+ node.firstChild && node.removeChild(node.firstChild);\n+ node.appendChild(src.firstChild.cloneNode(true));\n+ node.setAttributeNS(null, \"viewBox\", src.getAttributeNS(null, \"viewBox\"));\n \n+ node.setAttributeNS(null, \"width\", size);\n+ node.setAttributeNS(null, \"height\", size);\n+ node.setAttributeNS(null, \"x\", pos.x - offset);\n+ node.setAttributeNS(null, \"y\", pos.y - offset);\n \n-/**\n- * @requires OpenLayers/Marker.js\n- */\n+ // now that the node has all its new properties, insert it\n+ // back into the dom where it was\n+ if (nextSibling) {\n+ parent.insertBefore(node, nextSibling);\n+ } else if (parent) {\n+ parent.appendChild(node);\n+ }\n+ } else {\n+ node.setAttributeNS(null, \"r\", style.pointRadius);\n+ }\n \n-/**\n- * Class: OpenLayers.Marker.Box\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {\n+ var rotation = style.rotation;\n \n- /** \n- * Property: bounds \n- * {} \n- */\n- bounds: null,\n+ if ((rotation !== undefined || node._rotation !== undefined) && pos) {\n+ node._rotation = rotation;\n+ rotation |= 0;\n+ if (node.nodeName !== \"svg\") {\n+ node.setAttributeNS(null, \"transform\",\n+ \"rotate(\" + rotation + \" \" + pos.x + \" \" +\n+ pos.y + \")\");\n+ } else {\n+ var metrics = this.symbolMetrics[src.id];\n+ node.firstChild.setAttributeNS(null, \"transform\", \"rotate(\" +\n+ rotation + \" \" +\n+ metrics[1] + \" \" +\n+ metrics[2] + \")\");\n+ }\n+ }\n+ }\n \n- /** \n- * Property: div \n- * {DOMElement} \n- */\n- div: null,\n+ if (options.isFilled) {\n+ node.setAttributeNS(null, \"fill\", style.fillColor);\n+ node.setAttributeNS(null, \"fill-opacity\", style.fillOpacity);\n+ } else {\n+ node.setAttributeNS(null, \"fill\", \"none\");\n+ }\n \n- /** \n- * Constructor: OpenLayers.Marker.Box\n- *\n- * Parameters:\n- * bounds - {} \n- * borderColor - {String} \n- * borderWidth - {int} \n- */\n- initialize: function(bounds, borderColor, borderWidth) {\n- this.bounds = bounds;\n- this.div = OpenLayers.Util.createDiv();\n- this.div.style.overflow = 'hidden';\n- this.events = new OpenLayers.Events(this, this.div);\n- this.setBorder(borderColor, borderWidth);\n- },\n+ if (options.isStroked) {\n+ node.setAttributeNS(null, \"stroke\", style.strokeColor);\n+ node.setAttributeNS(null, \"stroke-opacity\", style.strokeOpacity);\n+ node.setAttributeNS(null, \"stroke-width\", style.strokeWidth * widthFactor);\n+ node.setAttributeNS(null, \"stroke-linecap\", style.strokeLinecap || \"round\");\n+ // Hard-coded linejoin for now, to make it look the same as in VML.\n+ // There is no strokeLinejoin property yet for symbolizers.\n+ node.setAttributeNS(null, \"stroke-linejoin\", \"round\");\n+ style.strokeDashstyle && node.setAttributeNS(null,\n+ \"stroke-dasharray\", this.dashStyle(style, widthFactor));\n+ } else {\n+ node.setAttributeNS(null, \"stroke\", \"none\");\n+ }\n \n- /**\n- * Method: destroy \n- */\n- destroy: function() {\n+ if (style.pointerEvents) {\n+ node.setAttributeNS(null, \"pointer-events\", style.pointerEvents);\n+ }\n \n- this.bounds = null;\n- this.div = null;\n+ if (style.cursor != null) {\n+ node.setAttributeNS(null, \"cursor\", style.cursor);\n+ }\n \n- OpenLayers.Marker.prototype.destroy.apply(this, arguments);\n+ return node;\n+ },\n+\n+ /** \n+ * Method: dashStyle\n+ * \n+ * Parameters:\n+ * style - {Object}\n+ * widthFactor - {Number}\n+ * \n+ * Returns:\n+ * {String} A SVG compliant 'stroke-dasharray' value\n+ */\n+ dashStyle: function(style, widthFactor) {\n+ var w = style.strokeWidth * widthFactor;\n+ var str = style.strokeDashstyle;\n+ switch (str) {\n+ case 'solid':\n+ return 'none';\n+ case 'dot':\n+ return [1, 4 * w].join();\n+ case 'dash':\n+ return [4 * w, 4 * w].join();\n+ case 'dashdot':\n+ return [4 * w, 4 * w, 1, 4 * w].join();\n+ case 'longdash':\n+ return [8 * w, 4 * w].join();\n+ case 'longdashdot':\n+ return [8 * w, 4 * w, 1, 4 * w].join();\n+ default:\n+ return OpenLayers.String.trim(str).replace(/\\s+/g, \",\");\n+ }\n },\n \n /** \n- * Method: setBorder\n- * Allow the user to change the box's color and border width\n+ * Method: createNode\n * \n * Parameters:\n- * color - {String} Default is \"red\"\n- * width - {int} Default is 2\n+ * type - {String} Kind of node to draw\n+ * id - {String} Id for node\n+ * \n+ * Returns:\n+ * {DOMElement} A new node of the given type and id\n */\n- setBorder: function(color, width) {\n- if (!color) {\n- color = \"red\";\n- }\n- if (!width) {\n- width = 2;\n+ createNode: function(type, id) {\n+ var node = document.createElementNS(this.xmlns, type);\n+ if (id) {\n+ node.setAttributeNS(null, \"id\", id);\n }\n- this.div.style.border = width + \"px solid \" + color;\n+ return node;\n },\n \n /** \n- * Method: draw\n+ * Method: nodeTypeCompare\n * \n * Parameters:\n- * px - {} \n- * sz - {} \n+ * node - {SVGDomElement} An SVG element\n+ * type - {String} Kind of node\n * \n- * Returns: \n- * {DOMElement} A new DOM Image with this marker's icon set at the \n- * location passed-in\n+ * Returns:\n+ * {Boolean} Whether or not the specified node is of the specified type\n */\n- draw: function(px, sz) {\n- OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);\n- return this.div;\n+ nodeTypeCompare: function(node, type) {\n+ return (type == node.nodeName);\n },\n \n /**\n- * Method: onScreen\n+ * Method: createRenderRoot\n * \n- * Rreturn:\n- * {Boolean} Whether or not the marker is currently visible on screen.\n+ * Returns:\n+ * {DOMElement} The specific render engine's root element\n */\n- onScreen: function() {\n- var onScreen = false;\n- if (this.map) {\n- var screenBounds = this.map.getExtent();\n- onScreen = screenBounds.containsBounds(this.bounds, true, true);\n- }\n- return onScreen;\n+ createRenderRoot: function() {\n+ var svg = this.nodeFactory(this.container.id + \"_svgRoot\", \"svg\");\n+ svg.style.display = \"block\";\n+ return svg;\n },\n \n /**\n- * Method: display\n- * Hide or show the icon\n+ * Method: createRoot\n * \n * Parameters:\n- * display - {Boolean} \n+ * suffix - {String} suffix to append to the id\n+ * \n+ * Returns:\n+ * {DOMElement}\n */\n- display: function(display) {\n- this.div.style.display = (display) ? \"\" : \"none\";\n+ createRoot: function(suffix) {\n+ return this.nodeFactory(this.container.id + suffix, \"g\");\n },\n \n- CLASS_NAME: \"OpenLayers.Marker.Box\"\n-});\n-\n-/* ======================================================================\n- OpenLayers/Tile/UTFGrid.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Tile.js\n- * @requires OpenLayers/Format/JSON.js\n- * @requires OpenLayers/Request.js\n- */\n-\n-/**\n- * Class: OpenLayers.Tile.UTFGrid\n- * Instances of OpenLayers.Tile.UTFGrid are used to manage \n- * UTFGrids. This is an unusual tile type in that it doesn't have a\n- * rendered image; only a 'hit grid' that can be used to \n- * look up feature attributes.\n- *\n- * See the constructor for details on constructing a\n- * new instance.\n- *\n- * Inherits from:\n- * - \n- */\n-OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, {\n-\n- /** \n- * Property: url\n- * {String}\n- * The URL of the UTFGrid file being requested. Provided by the \n- * method. \n- */\n- url: null,\n-\n /**\n- * Property: utfgridResolution\n- * {Number}\n- * Ratio of the pixel width to the width of a UTFGrid data point. If an \n- * entry in the grid represents a 4x4 block of pixels, the \n- * utfgridResolution would be 4. Default is 2.\n- */\n- utfgridResolution: 2,\n-\n- /** \n- * Property: json\n- * {Object}\n- * Stores the parsed JSON tile data structure. \n+ * Method: createDefs\n+ *\n+ * Returns:\n+ * {DOMElement} The element to which we'll add the symbol definitions\n */\n- json: null,\n+ createDefs: function() {\n+ var defs = this.nodeFactory(this.container.id + \"_defs\", \"defs\");\n+ this.rendererRoot.appendChild(defs);\n+ return defs;\n+ },\n \n- /** \n- * Property: format\n- * {OpenLayers.Format.JSON}\n- * Parser instance used to parse JSON for cross browser support. The native\n- * JSON.parse method will be used where available (all except IE<8).\n- */\n- format: null,\n+ /**************************************\n+ * *\n+ * GEOMETRY DRAWING FUNCTIONS *\n+ * *\n+ **************************************/\n \n- /** \n- * Constructor: OpenLayers.Tile.UTFGrid\n- * Constructor for a new instance.\n+ /**\n+ * Method: drawPoint\n+ * This method is only called by the renderer itself.\n * \n- * Parameters:\n- * layer - {} layer that the tile will go in.\n- * position - {}\n- * bounds - {}\n- * url - {} Deprecated. Remove me in 3.0.\n- * size - {}\n- * options - {Object}\n- */\n-\n- /** \n- * APIMethod: destroy\n- * Clean up.\n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the point\n */\n- destroy: function() {\n- this.clear();\n- OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n+ drawPoint: function(node, geometry) {\n+ return this.drawCircle(node, geometry, 1);\n },\n \n /**\n- * Method: draw\n- * Check that a tile should be drawn, and draw it.\n- * In the case of UTFGrids, \"drawing\" it means fetching and\n- * parsing the json. \n+ * Method: drawCircle\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * radius - {Float}\n * \n * Returns:\n- * {Boolean} Was a tile drawn?\n+ * {DOMElement} or false if the renderer could not draw the circle\n */\n- draw: function() {\n- var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n- if (drawn) {\n- if (this.isLoading) {\n- this.abortLoading();\n- //if we're already loading, send 'reload' instead of 'loadstart'.\n- this.events.triggerEvent(\"reload\");\n- } else {\n- this.isLoading = true;\n- this.events.triggerEvent(\"loadstart\");\n- }\n- this.url = this.layer.getURL(this.bounds);\n+ drawCircle: function(node, geometry, radius) {\n+ var resolution = this.getResolution();\n+ var x = ((geometry.x - this.featureDx) / resolution + this.left);\n+ var y = (this.top - geometry.y / resolution);\n \n- if (this.layer.useJSONP) {\n- // Use JSONP method to avoid xbrowser policy\n- var ols = new OpenLayers.Protocol.Script({\n- url: this.url,\n- callback: function(response) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"loadend\");\n- this.json = response.data;\n- },\n- scope: this\n- });\n- ols.read();\n- this.request = ols;\n- } else {\n- // Use standard XHR\n- this.request = OpenLayers.Request.GET({\n- url: this.url,\n- callback: function(response) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"loadend\");\n- if (response.status === 200) {\n- this.parseData(response.responseText);\n- }\n- },\n- scope: this\n- });\n- }\n+ if (this.inValidRange(x, y)) {\n+ node.setAttributeNS(null, \"cx\", x);\n+ node.setAttributeNS(null, \"cy\", y);\n+ node.setAttributeNS(null, \"r\", radius);\n+ return node;\n } else {\n- this.unload();\n+ return false;\n }\n- return drawn;\n- },\n \n- /**\n- * Method: abortLoading\n- * Cancel a pending request.\n- */\n- abortLoading: function() {\n- if (this.request) {\n- this.request.abort();\n- delete this.request;\n- }\n- this.isLoading = false;\n },\n \n /**\n- * Method: getFeatureInfo\n- * Get feature information associated with a pixel offset. If the pixel\n- * offset corresponds to a feature, the returned object will have id\n- * and data properties. Otherwise, null will be returned.\n- * \n- *\n- * Parameters:\n- * i - {Number} X-axis pixel offset (from top left of tile)\n- * j - {Number} Y-axis pixel offset (from top left of tile)\n- *\n+ * Method: drawLineString\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n * Returns:\n- * {Object} Object with feature id and data properties corresponding to the \n- * given pixel offset.\n+ * {DOMElement} or null if the renderer could not draw all components of\n+ * the linestring, or false if nothing could be drawn\n */\n- getFeatureInfo: function(i, j) {\n- var info = null;\n- if (this.json) {\n- var id = this.getFeatureId(i, j);\n- if (id !== null) {\n- info = {\n- id: id,\n- data: this.json.data[id]\n- };\n- }\n+ drawLineString: function(node, geometry) {\n+ var componentsResult = this.getComponentsString(geometry.components);\n+ if (componentsResult.path) {\n+ node.setAttributeNS(null, \"points\", componentsResult.path);\n+ return (componentsResult.complete ? node : null);\n+ } else {\n+ return false;\n }\n- return info;\n },\n \n /**\n- * Method: getFeatureId\n- * Get the identifier for the feature associated with a pixel offset.\n- *\n- * Parameters:\n- * i - {Number} X-axis pixel offset (from top left of tile)\n- * j - {Number} Y-axis pixel offset (from top left of tile)\n- *\n+ * Method: drawLinearRing\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n * Returns:\n- * {Object} The feature identifier corresponding to the given pixel offset.\n- * Returns null if pixel doesn't correspond to a feature.\n+ * {DOMElement} or null if the renderer could not draw all components\n+ * of the linear ring, or false if nothing could be drawn\n */\n- getFeatureId: function(i, j) {\n- var id = null;\n- if (this.json) {\n- var resolution = this.utfgridResolution;\n- var row = Math.floor(j / resolution);\n- var col = Math.floor(i / resolution);\n- var charCode = this.json.grid[row].charCodeAt(col);\n- var index = this.indexFromCharCode(charCode);\n- var keys = this.json.keys;\n- if (!isNaN(index) && (index in keys)) {\n- id = keys[index];\n- }\n+ drawLinearRing: function(node, geometry) {\n+ var componentsResult = this.getComponentsString(geometry.components);\n+ if (componentsResult.path) {\n+ node.setAttributeNS(null, \"points\", componentsResult.path);\n+ return (componentsResult.complete ? node : null);\n+ } else {\n+ return false;\n }\n- return id;\n },\n \n /**\n- * Method: indexFromCharCode\n- * Given a character code for one of the UTFGrid \"grid\" characters, \n- * resolve the integer index for the feature id in the UTFGrid \"keys\"\n- * array.\n- *\n- * Parameters:\n- * charCode - {Integer}\n- *\n+ * Method: drawPolygon\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n+ * \n * Returns:\n- * {Integer} Index for the feature id from the keys array.\n+ * {DOMElement} or null if the renderer could not draw all components\n+ * of the polygon, or false if nothing could be drawn\n */\n- indexFromCharCode: function(charCode) {\n- if (charCode >= 93) {\n- charCode--;\n+ drawPolygon: function(node, geometry) {\n+ var d = \"\";\n+ var draw = true;\n+ var complete = true;\n+ var linearRingResult, path;\n+ for (var j = 0, len = geometry.components.length; j < len; j++) {\n+ d += \" M\";\n+ linearRingResult = this.getComponentsString(\n+ geometry.components[j].components, \" \");\n+ path = linearRingResult.path;\n+ if (path) {\n+ d += \" \" + path;\n+ complete = linearRingResult.complete && complete;\n+ } else {\n+ draw = false;\n+ }\n }\n- if (charCode >= 35) {\n- charCode--;\n+ d += \" z\";\n+ if (draw) {\n+ node.setAttributeNS(null, \"d\", d);\n+ node.setAttributeNS(null, \"fill-rule\", \"evenodd\");\n+ return complete ? node : null;\n+ } else {\n+ return false;\n }\n- return charCode - 32;\n },\n \n /**\n- * Method: parseData\n- * Parse the JSON from a request\n- *\n- * Parameters:\n- * str - {String} UTFGrid as a JSON string. \n+ * Method: drawRectangle\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {}\n * \n * Returns:\n- * {Object} parsed javascript data\n+ * {DOMElement} or false if the renderer could not draw the rectangle\n */\n- parseData: function(str) {\n- if (!this.format) {\n- this.format = new OpenLayers.Format.JSON();\n- }\n- this.json = this.format.read(str);\n- },\n+ drawRectangle: function(node, geometry) {\n+ var resolution = this.getResolution();\n+ var x = ((geometry.x - this.featureDx) / resolution + this.left);\n+ var y = (this.top - geometry.y / resolution);\n \n- /** \n- * Method: clear\n- * Delete data stored with this tile.\n- */\n- clear: function() {\n- this.json = null;\n+ if (this.inValidRange(x, y)) {\n+ node.setAttributeNS(null, \"x\", x);\n+ node.setAttributeNS(null, \"y\", y);\n+ node.setAttributeNS(null, \"width\", geometry.width / resolution);\n+ node.setAttributeNS(null, \"height\", geometry.height / resolution);\n+ return node;\n+ } else {\n+ return false;\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Tile.UTFGrid\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Tile/Image/IFrame.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Tile/Image.js\n- */\n-\n-/**\n- * Constant: OpenLayers.Tile.Image.IFrame\n- * Mixin for tiles that use form-encoded POST requests to get images from\n- * remote services. Images will be loaded using HTTP-POST into an IFrame.\n- *\n- * This mixin will be applied to instances\n- * configured with set.\n- */\n-OpenLayers.Tile.Image.IFrame = {\n-\n- /**\n- * Property: useIFrame\n- * {Boolean} true if we are currently using an IFrame to render POST\n- * responses, false if we are using an img element to render GET responses.\n- */\n- useIFrame: null,\n-\n- /**\n- * Property: blankImageUrl\n- * {String} Using a data scheme url is not supported by all browsers, but\n- * we don't care because we either set it as css backgroundImage, or the\n- * image's display style is set to \"none\" when we use it.\n- */\n- blankImageUrl: \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7\",\n-\n /**\n- * Method: draw\n- * Set useIFrame in the instance, and operate the image/iframe switch.\n- * Then call Tile.Image.draw.\n+ * Method: drawText\n+ * This method is only called by the renderer itself.\n *\n- * Returns:\n- * {Boolean}\n+ * Parameters:\n+ * featureId - {String}\n+ * style -\n+ * location - {}\n */\n- draw: function() {\n- var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this);\n- if (draw) {\n-\n- // this.url isn't set to the currect value yet, so we call getURL\n- // on the layer and store the result in a local variable\n- var url = this.layer.getURL(this.bounds);\n-\n- var usedIFrame = this.useIFrame;\n- this.useIFrame = this.maxGetUrlLength !== null &&\n- !this.layer.async &&\n- url.length > this.maxGetUrlLength;\n-\n- var fromIFrame = usedIFrame && !this.useIFrame;\n- var toIFrame = !usedIFrame && this.useIFrame;\n+ drawText: function(featureId, style, location) {\n+ var drawOutline = (!!style.labelOutlineWidth);\n+ // First draw text in halo color and size and overlay the\n+ // normal text afterwards\n+ if (drawOutline) {\n+ var outlineStyle = OpenLayers.Util.extend({}, style);\n+ outlineStyle.fontColor = outlineStyle.labelOutlineColor;\n+ outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;\n+ outlineStyle.fontStrokeWidth = style.labelOutlineWidth;\n+ if (style.labelOutlineOpacity) {\n+ outlineStyle.fontOpacity = style.labelOutlineOpacity;\n+ }\n+ delete outlineStyle.labelOutlineWidth;\n+ this.drawText(featureId, outlineStyle, location);\n+ }\n \n- if (fromIFrame || toIFrame) {\n+ var resolution = this.getResolution();\n \n- // Switching between GET (image) and POST (iframe).\n+ var x = ((location.x - this.featureDx) / resolution + this.left);\n+ var y = (location.y / resolution - this.top);\n \n- // We remove the imgDiv (really either an image or an iframe)\n- // from the frame and set it to null to make sure initImage\n- // will call getImage.\n+ var suffix = (drawOutline) ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX;\n+ var label = this.nodeFactory(featureId + suffix, \"text\");\n \n- if (this.imgDiv && this.imgDiv.parentNode === this.frame) {\n- this.frame.removeChild(this.imgDiv);\n- }\n- this.imgDiv = null;\n+ label.setAttributeNS(null, \"x\", x);\n+ label.setAttributeNS(null, \"y\", -y);\n \n- // And if we had an iframe we also remove the event pane.\n+ if (style.fontColor) {\n+ label.setAttributeNS(null, \"fill\", style.fontColor);\n+ }\n+ if (style.fontStrokeColor) {\n+ label.setAttributeNS(null, \"stroke\", style.fontStrokeColor);\n+ }\n+ if (style.fontStrokeWidth) {\n+ label.setAttributeNS(null, \"stroke-width\", style.fontStrokeWidth);\n+ }\n+ if (style.fontOpacity) {\n+ label.setAttributeNS(null, \"opacity\", style.fontOpacity);\n+ }\n+ if (style.fontFamily) {\n+ label.setAttributeNS(null, \"font-family\", style.fontFamily);\n+ }\n+ if (style.fontSize) {\n+ label.setAttributeNS(null, \"font-size\", style.fontSize);\n+ }\n+ if (style.fontWeight) {\n+ label.setAttributeNS(null, \"font-weight\", style.fontWeight);\n+ }\n+ if (style.fontStyle) {\n+ label.setAttributeNS(null, \"font-style\", style.fontStyle);\n+ }\n+ if (style.labelSelect === true) {\n+ label.setAttributeNS(null, \"pointer-events\", \"visible\");\n+ label._featureId = featureId;\n+ } else {\n+ label.setAttributeNS(null, \"pointer-events\", \"none\");\n+ }\n+ var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;\n+ label.setAttributeNS(null, \"text-anchor\",\n+ OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || \"middle\");\n \n- if (fromIFrame) {\n- this.frame.removeChild(this.frame.firstChild);\n- }\n- }\n+ if (OpenLayers.IS_GECKO === true) {\n+ label.setAttributeNS(null, \"dominant-baseline\",\n+ OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || \"central\");\n }\n- return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments);\n- },\n \n- /**\n- * Method: getImage\n- * Creates the content for the frame on the tile.\n- */\n- getImage: function() {\n- if (this.useIFrame === true) {\n- if (!this.frame.childNodes.length) {\n- var eventPane = document.createElement(\"div\"),\n- style = eventPane.style;\n- style.position = \"absolute\";\n- style.width = \"100%\";\n- style.height = \"100%\";\n- style.zIndex = 1;\n- style.backgroundImage = \"url(\" + this.blankImageUrl + \")\";\n- this.frame.appendChild(eventPane);\n+ var labelRows = style.label.split('\\n');\n+ var numRows = labelRows.length;\n+ while (label.childNodes.length > numRows) {\n+ label.removeChild(label.lastChild);\n+ }\n+ for (var i = 0; i < numRows; i++) {\n+ var tspan = this.nodeFactory(featureId + suffix + \"_tspan_\" + i, \"tspan\");\n+ if (style.labelSelect === true) {\n+ tspan._featureId = featureId;\n+ tspan._geometry = location;\n+ tspan._geometryClass = location.CLASS_NAME;\n }\n-\n- var id = this.id + '_iFrame',\n- iframe;\n- if (parseFloat(navigator.appVersion.split(\"MSIE\")[1]) < 9) {\n- // Older IE versions do not set the name attribute of an iFrame \n- // properly via DOM manipulation, so we need to do it on our own with\n- // this hack.\n- iframe = document.createElement('