commit 6810d70bddeccb486bbe88b92ee634eb16240e98 Author: Schuyler Erle Date: Fri May 12 19:35:22 2006 +0000 Importing OpenLayers "newAPI" branch. git-svn-id: http://svn.openlayers.org/trunk/openlayers@2 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf diff --git a/example.html b/example.html new file mode 100644 index 0000000000..f46ed82048 --- /dev/null +++ b/example.html @@ -0,0 +1,41 @@ + + + + + + + + + +

OpenLayers Example

+
+ + diff --git a/img/east-mini.png b/img/east-mini.png new file mode 100644 index 0000000000..0707567a7d Binary files /dev/null and b/img/east-mini.png differ diff --git a/img/north-mini.png b/img/north-mini.png new file mode 100644 index 0000000000..a8a0b4033e Binary files /dev/null and b/img/north-mini.png differ diff --git a/img/south-mini.png b/img/south-mini.png new file mode 100644 index 0000000000..6c4ac8a0f1 Binary files /dev/null and b/img/south-mini.png differ diff --git a/img/west-mini.png b/img/west-mini.png new file mode 100644 index 0000000000..db5f420ca4 Binary files /dev/null and b/img/west-mini.png differ diff --git a/img/zoom-minus-mini.png b/img/zoom-minus-mini.png new file mode 100644 index 0000000000..f9b63aba01 Binary files /dev/null and b/img/zoom-minus-mini.png differ diff --git a/img/zoom-plus-mini.png b/img/zoom-plus-mini.png new file mode 100644 index 0000000000..eecf2eb4bd Binary files /dev/null and b/img/zoom-plus-mini.png differ diff --git a/img/zoom-world-mini.png b/img/zoom-world-mini.png new file mode 100644 index 0000000000..2159dde7ba Binary files /dev/null and b/img/zoom-world-mini.png differ diff --git a/js/OpenLayers.js b/js/OpenLayers.js new file mode 100644 index 0000000000..3f90e1ca24 --- /dev/null +++ b/js/OpenLayers.js @@ -0,0 +1,65 @@ +// @requires core/application.js + +//// +/// This blob sucks in all the files in uncompressed form for ease of use +/// + +/** HACK HACK HACK +* this function is basically duplicated in api.js +* +* @return {str} +*/ + +OpenLayers = new Object(); +OpenLayers._scriptName = "js/OpenLayers.js"; +OpenLayers._getScriptLocation = function () { + var scriptLocation = ""; + var SCRIPT_NAME = OpenLayers._scriptName; + + var scripts = document.getElementsByTagName('script'); + for (var i = 0; i < scripts.length; i++) { + var src = scripts[i].getAttribute('src'); + if (src) { + var index = src.lastIndexOf(SCRIPT_NAME); + // is it found, at the end of the URL? + if ((index > -1) && (index + SCRIPT_NAME.length == src.length)) { + scriptLocation = src.slice(0, -SCRIPT_NAME.length); + break; + } + } + } + return scriptLocation; +} + +try{new OpenLayers.Map();} +catch(e){ + var jsfiles=new Array( + "Prototype.js", + "OpenLayers/Util.js", + "OpenLayers/Events.js", + "OpenLayers/Map.js", + "OpenLayers/Layer.js", + "OpenLayers/Tile.js", + "OpenLayers/Tile/Image.js", + "OpenLayers/Layer/Grid.js", + "OpenLayers/Layer/WMS.js", + "OpenLayers/Control.js", + "OpenLayers/Control/PanZoom.js" + ); // etc. + + var allScriptTags = ""; + var host = OpenLayers._getScriptLocation() + "js/"; + + // check to see if prototype.js was already loaded + // if so, skip the first dynamic include + // + var start=1; + try { x = Prototype; } + catch (e) { start=0; } + + for (var i = start; i < jsfiles.length; i++) { + var currentScriptTag = ""; + allScriptTags += currentScriptTag; + } + document.write(allScriptTags); +}; diff --git a/js/OpenLayers/Control.js b/js/OpenLayers/Control.js new file mode 100644 index 0000000000..d81886f424 --- /dev/null +++ b/js/OpenLayers/Control.js @@ -0,0 +1,22 @@ +OpenLayers.Control = Class.create(); +OpenLayers.Control.prototype = { + // OpenLayers.Map + map: null, + + // HTMLDivElement + div: null, + + initialize: function () {}, + + draw: function () { + if (this.div == null) { + this.div = OpenLayers.Util.createDiv(); + } + return this.div; + }, + + destroy: function () { + // eliminate circular references + this.map = null; + } +}; diff --git a/js/OpenLayers/Control/PanZoom.js b/js/OpenLayers/Control/PanZoom.js new file mode 100644 index 0000000000..51478da2de --- /dev/null +++ b/js/OpenLayers/Control/PanZoom.js @@ -0,0 +1,107 @@ +// @require: core/util.js +// @require: core/alphaImage.js + +// +// default zoom/pan controls +// +OpenLayers.Control.PanZoom = Class.create(); +OpenLayers.Control.PanZoom.prototype = + Object.extend( new OpenLayers.Control(), { + // Array(...) + buttons: null, + + initialize: function() { + OpenLayers.Control.prototype.initialize.apply(this, arguments); + }, + + draw: function() { + // initialize our internal div + OpenLayers.Control.prototype.draw.apply(this); + + // place the controls + this.buttons = new Array(); + + var sz = new OpenLayers.Size(18,18); + var xy = new OpenLayers.Point(4,4); + var centered = new OpenLayers.Point(xy.x+sz.w/2, xy.y); + + this._addButton("panup", "north-mini.png", centered, sz); + xy.y = centered.y+sz.h; + this._addButton("panleft", "west-mini.png", xy, sz); + this._addButton("panright", "east-mini.png", xy.addX(sz.w), sz); + this._addButton("pandown", "south-mini.png", centered.addY(sz.h*2), sz); + this._addButton("zoomin", "zoom-plus-mini.png", centered.addY(sz.h*3), sz); + this._addButton("zoomextents", "zoom-world-mini.png", centered.addY(sz.h*4), sz); + this._addButton("zoomout", "zoom-minus-mini.png", centered.addY(sz.h*5), sz); + return this.div; + }, + + _addButton:function(id, img, xy, sz) { + var imgLocation = OpenLayers.Util.getImagesLocation() + img; + // var btn = new ol.AlphaImage("_"+id, imgLocation, xy, sz); + var btn = OpenLayers.Util.createImage( + imgLocation, sz, xy, "absolute", + "OpenLayers_Control_PanZoom_" + id ); + + //we want to add the outer div + this.div.appendChild(btn); + + btn.onmousedown = this.buttonDown.bindAsEventListener(btn); + btn.ondblclick = this.doubleClick.bindAsEventListener(btn); + btn.action = id; + btn.map = this.map; + + //we want to remember/reference the outer div + this.buttons.push(btn); + return btn; + }, + + doubleClick: function (evt) { + Event.stop(evt); + }, + + buttonDown: function (evt) { + switch (this.action) { + case "panup": + var resolution = this.map.getResolution(); + var center = this.map.getCenter(); + this.map.setCenter( + new OpenLayers.LatLon(center.lat + (reslution * 50), + center.lon + ) + ); + break; + case "pandown": + var resolution = this.map.getResolution(); + var center = this.map.getCenter(); + this.map.setCenter( + new OpenLayers.LatLon(center.lat - (reslution * 50), + center.lon + ) + ); + break; + case "panleft": + var resolution = this.map.getResolution(); + var center = this.map.getCenter(); + this.map.setCenter( + new OpenLayers.LatLon(center.lat, + center.lon - (resolution * 50) + ) + ); + break; + case "panright": + var resolution = this.map.getResolution(); + var center = this.map.getCenter(); + this.map.setCenter( + new OpenLayers.LatLon(center.lat, + center.lon + (resolution * 50) + ) + ); + break; + case "zoomin": this.map.zoomIn(); break; + case "zoomout": this.map.zoomOut(); break; + case "zoomextents": this.map.zoomExtent(); break; + } + Event.stop(evt); + } +}); diff --git a/js/OpenLayers/Events.js b/js/OpenLayers/Events.js new file mode 100644 index 0000000000..84793932d6 --- /dev/null +++ b/js/OpenLayers/Events.js @@ -0,0 +1,65 @@ +OpenLayers.Events = Class.create(); + +OpenLayers.Events.prototype = { + // hash of Array(Function) + listeners: null, + + // OpenLayers.Map + map: null, + + // DOMElement + div: null, + + // supported events + BROWSER_EVENTS: [ + "mouseover", "mouseout", + "mousedown", "mouseup", "mousemove", + "click", "dblclick" + ], + MAP_EVENTS: [ + "mouseover", "mouseout", + "mousedown", "mouseup", "mousemove", + "click", "dblclick" + ], + + initialize: function (map, div) { + this.listeners = {}; + this.map = map; + this.div = div; + for (var i = 0; i < this.MAP_EVENTS.length; i++) { + this.listeners[ this.MAP_EVENTS[i] ] = []; + } + for (var i = 0; i < this.BROWSER_EVENTS.length; i++) { + var eventName = this.BROWSER_EVENTS[i]; + Event.observe(div, eventName, + this.handleBrowserEvent.bindAsEventListener(this)); + } + }, + + register: function (eventName, obj, func) { + var listeners = this.listeners[eventName]; + listeners.push( func.bindAsEventListener(obj) ); + }, + + handleBrowserEvent: function (evt) { + evt.xy = this.getMousePosition(evt); + this.triggerMapEvent(evt.type, evt) + }, + + getMousePosition: function (evt) { + var offsets = Position.page(this.div); + return new OpenLayers.Point( + evt.clientX - offsets[0], + evt.clientY - offsets[1]); + }, + + triggerMapEvent: function (type, evt) { + evt.map = this.map; + + var listeners = this.listeners[type]; + for (var i = 0; i < listeners.length; i++) { + var callback = listeners[i]; + callback(evt); + } + } +}; diff --git a/js/OpenLayers/Layer.js b/js/OpenLayers/Layer.js new file mode 100644 index 0000000000..56119d863d --- /dev/null +++ b/js/OpenLayers/Layer.js @@ -0,0 +1,36 @@ +OpenLayers.Layer = Class.create(); + +OpenLayers.Layer.prototype = { + // str: name + name: null, + + // DOMElement: div + div: null, + + // OpenLayers.Map + map: null, + + /** + * @param {str} name + */ + initialize: function(name) { + this.name = name; + }, + + /** + * Destroy is a destructor: this is to alleviate cyclic references which + * the Javascript garbage cleaner can not take care of on its own. + */ + destroy: function() { + this.map = null; + }, + + /** + * @params {OpenLayers.Bounds} bound + * @params {bool} zoomChanged tells when zoom has changed, as layers have to do some init work in that case. + */ + moveTo: function (bound,zoomChanged) { + // not implemented here + return; + } +}; diff --git a/js/OpenLayers/Layer/Grid.js b/js/OpenLayers/Layer/Grid.js new file mode 100644 index 0000000000..581958e1d1 --- /dev/null +++ b/js/OpenLayers/Layer/Grid.js @@ -0,0 +1,216 @@ +OpenLayers.Layer.Grid = Class.create(); +OpenLayers.Layer.Grid.prototype = Object.extend( new OpenLayers.Layer(), { + + // str: url + url: null, + + // hash: params + params: null, + + // tileSize: OpenLayers.Size + tileSize: null, + + // grid: Array(Array()) + // this is an array of rows, each row is an array of tiles + grid: null, + + DEFAULT_TILE_SIZE: new OpenLayers.Size(256,256), + + /** + * @param {str} name + * @param {str} url + * @param {hash} params + */ + initialize: function(name, url, params) { + OpenLayers.Layer.prototype.initialize.apply(this, [name]); + this.url = url; + this.params = params; + this.tileSize = this.DEFAULT_TILE_SIZE; + }, + /** + * moveTo + * moveTo is a function called whenever the map is moved. All the moving + * of actual 'tiles' is done by the map, but moveTo's role is to accept + * a bounds and make sure the data that that bounds requires is pre-loaded. + * @param {OpenLayers.Bounds} + */ + moveTo:function(bounds,zoomChanged) { + if (!this.grid || zoomChanged) { + this._initTiles(); + } else { + var i = 0; + while (this.getGridBounds().minlat > bounds.minlat) { + this.insertRow(false); + } + while (this.getGridBounds().minlon > bounds.minlon) { + this.insertColumn(true); + } + while (this.getGridBounds().maxlat < bounds.maxlat) { + this.insertRow(true); + } + while (this.getGridBounds().maxlon < bounds.maxlon) { + this.insertColumn(false); + } + } + }, + getGridBounds:function() { + var topLeftTile = this.grid[0][0]; + var bottomRightTile = this.grid[this.grid.length-1][this.grid[0].length-1]; + return new OpenLayers.Bounds(bottomRightTile.bounds.minlat, topLeftTile.bounds.minlon, + topLeftTile.bounds.maxlat, bottomRightTile.bounds.maxlon); + }, + _initTiles:function() { + var viewSize = this.map.getSize(); + var bounds = this.map.getExtent(); + var extent = this.map.getFullExtent(); + var resolution = this.map.getResolution(); + var tilelon = resolution*this.tileSize.w; + var tilelat = resolution*this.tileSize.h; + + var offsetlon = bounds.minlon - extent.minlon; + var tilecol = Math.floor(offsetlon/tilelon); + var tilecolremain = offsetlon/tilelon - tilecol; + var tileoffsetx = -tilecolremain * this.tileSize.w; + var tileoffsetlon = extent.minlon + tilecol * tilelon; + + var offsetlat = bounds.maxlat - (extent.minlat + tilelat); + var tilerow = Math.ceil(offsetlat/tilelat); + var tilerowremain = tilerow - offsetlat/tilelat; + var tileoffsety = -tilerowremain * this.tileSize.h; + var tileoffsetlat = extent.minlat + tilerow * tilelat; + + tileoffsetx = Math.round(tileoffsetx); // heaven help us + tileoffsety = Math.round(tileoffsety); + + this.origin = new OpenLayers.Point(tileoffsetx,tileoffsety); + this.grid = new Array(); + + var startX = tileoffsetx; + var startLon = tileoffsetlon; + + do { + var row = new Array(); + this.grid.append(row); + tileoffsetlon = startLon; + tileoffsetx = startX; + do { + var tileBounds = new OpenLayers.Bounds( + tileoffsetlat, + tileoffsetlon, + tileoffsetlat+tilelat, + tileoffsetlon+tilelon + ); + var tile = this.addTile(tileBounds, + new OpenLayers.Point(tileoffsetx, + tileoffsety + ) + ); + row.append(tile); + + tileoffsetlon += tilelon; + tileoffsetx += this.tileSize.w; + } while (tileoffsetlon < bounds.maxlon) + + tileoffsetlat -= tilelat; + tileoffsety += this.tileSize.h; + } while(tileoffsetlat > bounds.minlat - tilelat) + + }, + + /** + * @param {bool} prepend - if true, prepend to beginning. + * if false, then append to end + */ + insertRow:function(prepend) { + var modelRowIndex = (prepend) ? 0 : (this.grid.length - 1); + var modelRow = this.grid[modelRowIndex]; + + var newRow = new Array(); + + var resolution = this.map.getResolution(); + var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h; + var deltaLat = resolution * -deltaY; + + for (var i=0; i < modelRow.length; i++) { + var modelTile = modelRow[i]; + var bounds = modelTile.bounds.copyOf(); + var position = modelTile.position.copyOf(); + bounds.minlat = bounds.minlat + deltaLat; + bounds.maxlat = bounds.maxlat + deltaLat; + position.y = position.y + deltaY; + var newTile = this.addTile(bounds, position); + newRow.append(newTile); + } + + if (newRow.length>0){ + if (prepend) { + this.grid.prepend(newRow); + } else { + this.grid.append(newRow); + } + } + }, + + /** + * @param {bool} prepend - if true, prepend to beginning. + * if false, then append to end + */ + insertColumn:function(prepend) { + var modelCellIndex; + var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w; + var resolution = this.map.getResolution(); + var deltaLon = resolution * deltaX; + + for (var i=0; i 0) { + this.zoom -= 1; + this.moveToNewExtent(true); + } + }, + + zoomExtent: function() { + var fullExtent = this.getFullExtent(); + + this.zoom = this.getZoomForExtent( fullExtent ); + this.setCenter( + new OpenLayers.LatLon( + (fullExtent.minlat+fullExtent.maxlat)/2, + (fullExtent.minlon+fullExtent.maxlon)/2 + ) + ); + this.moveToNewExtent(true); + + }, + + /** + * @param {OpenLayers.LatLon} latlon + */ + moveLayerContainer: function (latlon) { + var container = this.layerContainerDiv; + var resolution = this.getResolution(); + + var deltaX = Math.round((this.center.lon - latlon.lon) / resolution); + var deltaY = Math.round((this.center.lat - latlon.lat) / resolution); + + var offsetLeft = parseInt(container.style.left); + var offsetTop = parseInt(container.style.top); + + container.style.left = (offsetLeft + deltaX) + "px"; + container.style.top = (offsetTop - deltaY) + "px"; + }, + + /** + * @param {Event} evt + */ + defaultDblClick: function (evt) { + var newCenter = this.getLatLonFromPoint( evt.xy ); + this.setCenter(newCenter); + }, + + /** + * @param {Event} evt + */ + defaultMouseDown: function (evt) { + this.mouseDragStart = evt.xy.copyOf(); + this.div.style.cursor = "move"; + Event.stop(evt); + }, + + /** + * @param {Event} evt + */ + defaultMouseMove: function (evt) { + if (this.mouseDragStart != null) { + var deltaX = this.mouseDragStart.x - evt.xy.x; + var deltaY = this.mouseDragStart.y - evt.xy.y + var size = this.getSize(); + var newXY = new OpenLayers.Point(size.w / 2 + deltaX, + size.h / 2 + deltaY); + var newCenter = this.getLatLonFromPoint( newXY ); + this.setCenter(newCenter); + this.mouseDragStart = evt.xy.copyOf(); + } + }, + + /** + * @param {Event} evt + */ + defaultMouseUp: function (evt) { + this.mouseDragStart = null; + this.div.style.cursor = "default"; + }, +}; diff --git a/js/OpenLayers/Tile.js b/js/OpenLayers/Tile.js new file mode 100644 index 0000000000..15092b119e --- /dev/null +++ b/js/OpenLayers/Tile.js @@ -0,0 +1,77 @@ +/* + * OpenLayers.Tile is a class designed to designate a single tile, however + * it is explicitly designed to do relatively little. Tiles store information + * about themselves -- such as the URL that they are related to, and their + * size - but do not add themselves to the layer div automatically, for + * example. + */ +OpenLayers.Tile = Class.create(); +OpenLayers.Tile.prototype = { + + // str - url of the request + url:null, + + // OpenLayers.Bounds + bounds:null, + + // OpenLayers.Size + size:null, + + // OpenLayers.Point Bottom Left pixel of the tile + position:null, + + /** + * @param {OpenLayers.Layer} layer + * @param {OpenLayers.LatLon} coord + */ + initialize: function(bounds,url,size) { + if (arguments.length > 0) { + this.url = url; + this.bounds = bounds; + this.size = size; + } + }, + + /** get the full request string from the ds and the tile params + * and call the AJAX loadURL(). + * + * input are function pointers for what to do on success and failure. + * + * @param {function} success + * @param {function} failure + */ + loadFeaturesForRegion:function(success, failure) { + + if (!this.loaded) { + + var server = this.ds.serverPath; + + if (server != "") { + + var queryString = this.getFullRequestString(); + // TODO: Hmmm, this stops multiple loads of the data when a + // result isn't immediately retrieved, but it's hacky. + // Do it better. + this.loaded = true; + OpenLayers.Application.loadURL(queryString, null, this, + success, failure); + } + } + }, + + + /** + */ + draw:function() { + }, + + /** remove this tile from the ds + */ + remove:function() { + }, + + + //////////////////// + who:function(){return "tiled.js";} // last! +}; + diff --git a/js/OpenLayers/Tile/Image.js b/js/OpenLayers/Tile/Image.js new file mode 100644 index 0000000000..892392f4ca --- /dev/null +++ b/js/OpenLayers/Tile/Image.js @@ -0,0 +1,30 @@ +OpenLayers.Tile.Image = Class.create(); +OpenLayers.Tile.Image.prototype = + Object.extend( new OpenLayers.Tile(), { + + // {DOMElement} img + img:null, + + initialize: function(bounds,url,size) { + OpenLayers.Tile.prototype.initialize.apply(this,arguments); + }, + + draw:function() { + OpenLayers.Tile.prototype.draw.apply(this, arguments); + this.img = OpenLayers.Util.createImage(this.url, + this.size); + }, + /* + * @param OpenLayers.Point + */ + setPosition:function(point) { + this.img.style.top = point.y+"px"; + this.img.style.left = point.x+"px"; + this.img.style.position = "absolute"; + this.position = point; + }, + getPosition: function() { + return this.position; + } + } +); diff --git a/js/OpenLayers/Util.js b/js/OpenLayers/Util.js new file mode 100644 index 0000000000..7377a88488 --- /dev/null +++ b/js/OpenLayers/Util.js @@ -0,0 +1,388 @@ +/* +* Convert degrees to radians +*/ +OpenLayers.Util=new Object(); + +OpenLayers.Point = Class.create(); +OpenLayers.Point.prototype = { + initialize: function(x,y) { + this.x=x; + this.y=y; + }, + toString:function(){ + return ("x="+this.x+",y="+this.y); + }, + copyOf:function() { + return new OpenLayers.Point(this.x, this.y); + }, + diff:function(pt){ // subtract pt from this + return new OpenLayers.Size(this.x - pt.x, this.y - pt.y); + }, + absDiff:function(pt){ // subtract pt from this + return new OpenLayers.Size(Math.abs(this.x - pt.x), Math.abs(this.y - pt.y)); + }, + diffPt:function(pt){ + sz=this.diff(pt); + return new OpenLayers.Point(sz.w,sz.h); + }, + equal:function(pt){ + d = this.diff(pt); + return (d.w==0&&d.h==0); + }, + addSize:function(sz){ + return new OpenLayers.Point(this.x+sz.w, this.y+sz.h); + }, + addX:function(w){ + return new OpenLayers.Point(this.x+w, this.y); + }, + addY:function(h){ + return new OpenLayers.Point(this.x, this.y+h); + }, + samePT:function(pt){ + return (this.x==pt.x && this.y==pt.y); + } +}; +OpenLayers.Size = Class.create(); +OpenLayers.Size.prototype = { + initialize: function(w,h) { + this.w=w; + this.h=h; + }, + toString:function(){ + return ("w="+this.w+",h="+this.h); + }, + copyOf:function(){ + return new OpenLayers.Size(this.w, this.h); + }, + sameSize:function(pt){ + return (this.w==pt.w && this.h==pt.h); + } +}; + +OpenLayers.LatLon = Class.create(); +OpenLayers.LatLon.prototype = { + initialize: function(lat,lon) { + this.lat=lat; + this.lon=lon; + }, + toString:function(){ + return ("lat="+this.lat+",lon="+this.lon); + }, + copyOf:function(){ + return new OpenLayers.LatLon(this.lat, this.lon); + }, + toShortString:function(){ + return (this.lat+", "+this.lon); + }, + diff:function(pt){ // subtract pt from this + return new OpenLayers.LatLon(this.lat - pt.lat,this.lon - pt.lon); + }, + samePT:function(pt){ + return (this.lat==pt.lat && this.lon==pt.lon); + } +}; +OpenLayers.LatLon.fromString=function(str){ + var pairs=str.split(","); + return new OpenLayers.LatLon(pairs[1],pairs[0]); +}; +OpenLayers.Bounds = Class.create(); +OpenLayers.Bounds.prototype = { + initialize: function(minlat,minlon,maxlat,maxlon){ + this.minlat=minlat; + this.minlon=minlon; + this.maxlat=maxlat; + this.maxlon=maxlon; + this.width = Math.abs(this.maxlon - this.minlon); + this.height = Math.abs(this.maxlat - this.minlat); + }, + toString:function(){ + return ("Min lat/lon=" + this.minlat +"/"+ this.minlon + + " Max lat/lon=" + this.maxlat +"/"+ this.maxlon + + " width:" + this.width + " height:" + this.height); + }, + copyOf:function(){ + return new OpenLayers.Bounds(this.minlat, this.minlon, + this.maxlat, this.maxlon); + }, + toBBOX:function(){ + return (this.minlon+","+this.minlat+","+this.maxlon+","+this.maxlat); + }, + contains:function(pt){ + return (pt.lon >=this.minlon && pt.lon <=this.maxlon + && pt.lat >=this.minlat && pt.lat <= this.maxlat) + } + +}; +/** Create bounds from coordinates in a string:-180.000000,-90.000000 180.000000,90.000000 +*/ +OpenLayers.Bounds.fromString=function(str){ + var pairs=str.split(" "); + var min=pairs[0]; + var max=pairs[1]; + + var latlon = min.split(","); + var latlon2 = max.split(","); + return new OpenLayers.Bounds(latlon[1],latlon[0],latlon2[1],latlon2[0]); +}; + +OpenLayers.Box = Class.create(); +OpenLayers.Box.prototype = { + initialize: function(x,y,w,h){ + this.xy=new OpenLayers.Point(x,y); + this.sz=new OpenLayers.Size(w,h); + this.c = new OpenLayers.Point(x+(w/2), y+(h/2)); + this.br = new OpenLayers.Point(x+w-1,y+h-1); + }, + + /* offset box by df + * @df(OpenLayers.Size) + */ + offset:function(df){ + this.xy=new OpenLayers.Point(this.xy.x+df.w,this.xy.y+df.h); + this.c = new OpenLayers.Point(this.xy.x+(this.sz.w/2), this.xy.y+(this.sz.h/2)); + var x=this.xy.x; + var y=this.xy.y; + var w=this.sz.w; + var h=this.sz.h; + this.br = new OpenLayers.Point(x+w-1,y+h-1); + }, + + getOrigin:function(){return this.xy;}, + getSize:function(){return this.sz;}, + getCenter:function(){return this.c;}, + getBotRight:function(){return this.br;}, + getWidth:function(){return this.sz.w;}, + getHeight:function(){return this.sz.h;}, + getSize:function(){return this.sz;}, + + toString:function(){ + return (this.xy.toString() + " " + this.sz.toString()); + }, + copyOf:function(){ + return new OpenLayers.Box(this.xy.x, this.xy.y, this.sz.w, this.sz.h); + }, + toCoordsString:function(){ + var x1 = this.xy.x; + var x2 = this.xy.x+this.sz.w-1; + var y1 = this.xy.y; + var y2 = this.xy.y+this.sz.h-1; + return (x1+","+y1+","+x2+","+y2); + }, + contains:function(pt){ + var returnVal = false; + + var lx=this.xy.x; + var ly=this.xy.y; + var rx=lx+this.sz.w-1; + var ry=ly+this.sz.h-1; + + if (pt != null) { + //upper left in + returnVal = (pt.x >=lx && pt.x <=rx && pt.y >=ly && pt.y <= ry); + } + return returnVal; + }, + + /** + * @param {ol.Box} box + * @param {bool} partial - if true, returns whether any part of the passed + * in box is within the calling box. otherwise box + * must contain entire box to return true. + * + * @return {bool} + */ + containsBox:function(box, partial) { + var contains = false; + + var mainTop = this.xy.y; + var mainLeft = this.xy.x; + var mainBottom = this.xy.y + this.sz.h; + var mainRight = this.xy.x + this.sz.w; + + var top = box.xy.y; + var left = box.xy.x; + var bottom = box.xy.y + box.sz.h; + var right = box.xy.x + box.sz.w; + + var inTop = (top >= mainTop) && (top <= mainBottom); + var inLeft = (left >= mainLeft) && (left <= mainRight); + var inBottom = (bottom >= mainTop) && (bottom <= mainBottom); + var inRight= (right >= mainLeft) && (right <= mainRight); + + if (partial) { + contains = (inTop || inBottom) && (inLeft || inRight ); + } else { + contains = (inTop && inLeft && inBottom && inRight); + } + + return contains; + }, + + // is this point the center of the box, +-2 pixels (@todo fix that number!) + isCenter:function(pt){ + var size = this.c.absDiff(pt); + return (size.w <= 2 && size.h <= 2); + } +}; + + +// Some other helpful things +String.prototype.startsWith = function(sStart){ + return (this.substr(0,sStart.length)==sStart); +}; +String.prototype.trim = function() { + var b=0,e=this.length -1; + while(this.substr(b,1) == " ") {b++;} + while(this.substr(e,1) == " ") {e--;} + return this.substring(b,e+1); +}; +Array.prototype.remove = function(rem) { + for(var i=0; i"; +}; + +/** +* @param {str} img - src URL +* @param {OpenLayers.Size} sz +* @param {OpenLayers.Point} xy +* @param {str} position +* @param {str} id +* @param {int} border +*/ +OpenLayers.Util.createImage = function(img, sz, xy, position, id, border) { + image = document.createElement("img"); + if (id) { + image.id = id; + image.style.alt = id; + } + if (xy) { + image.style.left = xy.x; + image.style.top = xy.y; + } + if (sz) { + image.style.width = sz.w; + image.style.height = sz.h; + } + if (position) { + image.style.position = position; + } else { + image.style.position = "relative"; + } + if (border) { + image.style.border = border + "px solid"; + } else { + image.style.border = 0; + } + image.style.cursor = "inherit"; + image.src = img; + return image; +}; + + +/** returns a concatenation of the properties of an object in +* http parameter notation. ex: +* +* "key1=value1&key2=value2&key3=value3" +* +* @param {Object} params +* +* @return {str} +*/ +OpenLayers.getParameterString = function(params) { + paramsArray = new Array(); + + for (var key in params) { + var value = params[key]; + //skip functions + if (typeof value == 'function') continue; + + paramsArray.push(key + "=" + value); + } + + return paramsArray.join("&"); +}; + +OpenLayers.Util.getImagesLocation = function () { + return "img/"; +}; diff --git a/js/Prototype.js b/js/Prototype.js new file mode 100644 index 0000000000..0e85338bab --- /dev/null +++ b/js/Prototype.js @@ -0,0 +1,1781 @@ +/* Prototype JavaScript framework, version 1.4.0 + * (c) 2005 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://prototype.conio.net/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.4.0', + ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)', + + emptyFunction: function() {}, + K: function(x) {return x} +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (property in source) { + destination[property] = source[property]; + } + return destination; +} + +Object.inspect = function(object) { + try { + if (object == undefined) return 'undefined'; + if (object == null) return 'null'; + return object.inspect ? object.inspect() : object.toString(); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } +} + +Function.prototype.bind = function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this; + return function(event) { + return __method.call(object, event || window.event); + } +} + +Object.extend(Number.prototype, { + toColorPart: function() { + var digits = this.toString(16); + if (this < 16) return '0' + digits; + return digits; + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + } +}); + +var Try = { + these: function() { + var returnValue; + + for (var i = 0; i < arguments.length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(); + } finally { + this.currentlyExecuting = false; + } + } + } +} + +/*--------------------------------------------------------------------------*/ + +function $() { + var elements = new Array(); + + for (var i = 0; i < arguments.length; i++) { + var element = arguments[i]; + if (typeof element == 'string') + element = document.getElementById(element); + + if (arguments.length == 1) + return element; + + elements.push(element); + } + + return elements; +} +Object.extend(String.prototype, { + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(eval); + }, + + escapeHTML: function() { + var div = document.createElement('div'); + var text = document.createTextNode(this); + div.appendChild(text); + return div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; + }, + + toQueryParams: function() { + var pairs = this.match(/^\??(.*)$/)[1].split('&'); + return pairs.inject({}, function(params, pairString) { + var pair = pairString.split('='); + params[pair[0]] = pair[1]; + return params; + }); + }, + + toArray: function() { + return this.split(''); + }, + + camelize: function() { + var oStringList = this.split('-'); + if (oStringList.length == 1) return oStringList[0]; + + var camelizedString = this.indexOf('-') == 0 + ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) + : oStringList[0]; + + for (var i = 1, len = oStringList.length; i < len; i++) { + var s = oStringList[i]; + camelizedString += s.charAt(0).toUpperCase() + s.substring(1); + } + + return camelizedString; + }, + + inspect: function() { + return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'"; + } +}); + +String.prototype.parseQuery = String.prototype.toQueryParams; + +var $break = new Object(); +var $continue = new Object(); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + try { + iterator(value, index++); + } catch (e) { + if (e != $continue) throw e; + } + }); + } catch (e) { + if (e != $break) throw e; + } + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + result = result && !!(iterator || Prototype.K)(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator) { + var result = true; + this.each(function(value, index) { + if (result = !!(iterator || Prototype.K)(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function (iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inject: function(memo, iterator) { + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.collect(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (value >= (result || value)) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (value <= (result || value)) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.collect(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.collect(Prototype.K); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (typeof args.last() == 'function') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + iterator(value = collections.pluck(index)); + return value; + }); + }, + + inspect: function() { + return '#'; + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); +var $A = Array.from = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0; i < iterable.length; i++) + results.push(iterable[i]); + return results; + } +} + +Object.extend(Array.prototype, Enumerable); + +Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0; i < this.length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != undefined || value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(value.constructor == Array ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + indexOf: function(object) { + for (var i = 0; i < this.length; i++) + if (this[i] == object) return i; + return -1; + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + shift: function() { + var result = this[0]; + for (var i = 0; i < this.length - 1; i++) + this[i] = this[i + 1]; + this.length--; + return result; + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } +}); +var Hash = { + _each: function(iterator) { + for (key in this) { + var value = this[key]; + if (typeof value == 'function') continue; + + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + merge: function(hash) { + return $H(hash).inject($H(this), function(mergedHash, pair) { + mergedHash[pair.key] = pair.value; + return mergedHash; + }); + }, + + toQueryString: function() { + return this.map(function(pair) { + return pair.map(encodeURIComponent).join('='); + }).join('&'); + }, + + inspect: function() { + return '#'; + } +} + +function $H(object) { + var hash = Object.extend({}, object || {}); + Object.extend(hash, Enumerable); + Object.extend(hash, Hash); + return hash; +} +ObjectRange = Class.create(); +Object.extend(ObjectRange.prototype, Enumerable); +Object.extend(ObjectRange.prototype, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + do { + iterator(value); + value = value.succ(); + } while (this.include(value)); + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')}, + function() {return new XMLHttpRequest()} + ) || false; + }, + + activeRequestCount: 0 +} + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responderToAdd) { + if (!this.include(responderToAdd)) + this.responders.push(responderToAdd); + }, + + unregister: function(responderToRemove) { + this.responders = this.responders.without(responderToRemove); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (responder[callback] && typeof responder[callback] == 'function') { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) {} + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { + Ajax.activeRequestCount++; + }, + + onComplete: function() { + Ajax.activeRequestCount--; + } +}); + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + parameters: '' + } + Object.extend(this.options, options || {}); + }, + + responseIsSuccess: function() { + return this.transport.status == undefined + || this.transport.status == 0 + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + responseIsFailure: function() { + return !this.responseIsSuccess(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + var parameters = this.options.parameters || ''; + if (parameters.length > 0) parameters += '&_='; + + try { + this.url = url; + if (this.options.method == 'get' && parameters.length > 0) + this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; + + Ajax.Responders.dispatch('onCreate', this, this.transport); + + this.transport.open(this.options.method, this.url, + this.options.asynchronous); + + if (this.options.asynchronous) { + this.transport.onreadystatechange = this.onStateChange.bind(this); + setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); + } + + this.setRequestHeaders(); + + var body = this.options.postBody ? this.options.postBody : parameters; + this.transport.send(this.options.method == 'post' ? body : null); + + } catch (e) { + this.dispatchException(e); + } + }, + + setRequestHeaders: function() { + var requestHeaders = + ['X-Requested-With', 'XMLHttpRequest', + 'X-Prototype-Version', Prototype.Version]; + + if (this.options.method == 'post') { + requestHeaders.push('Content-type', + 'application/x-www-form-urlencoded'); + + /* Force "Connection: close" for Mozilla browsers to work around + * a bug where XMLHttpReqeuest sends an incorrect Content-length + * header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType) + requestHeaders.push('Connection', 'close'); + } + + if (this.options.requestHeaders) + requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); + + for (var i = 0; i < requestHeaders.length; i += 2) + this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState != 1) + this.respondToReadyState(this.transport.readyState); + }, + + header: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) {} + }, + + evalJSON: function() { + try { + return eval(this.header('X-JSON')); + } catch (e) {} + }, + + evalResponse: function() { + try { + return eval(this.transport.responseText); + } catch (e) { + this.dispatchException(e); + } + }, + + respondToReadyState: function(readyState) { + var event = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.evalJSON(); + + if (event == 'Complete') { + try { + (this.options['on' + this.transport.status] + || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(transport, json); + } catch (e) { + this.dispatchException(e); + } + + if ((this.header('Content-type') || '').match(/^text\/javascript/i)) + this.evalResponse(); + } + + try { + (this.options['on' + event] || Prototype.emptyFunction)(transport, json); + Ajax.Responders.dispatch('on' + event, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ + if (event == 'Complete') + this.transport.onreadystatechange = Prototype.emptyFunction; + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Updater = Class.create(); + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { + initialize: function(container, url, options) { + this.containers = { + success: container.success ? $(container.success) : $(container), + failure: container.failure ? $(container.failure) : + (container.success ? null : $(container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function(transport, object) { + this.updateContent(); + onComplete(transport, object); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.responseIsSuccess() ? + this.containers.success : this.containers.failure; + var response = this.transport.responseText; + + if (!this.options.evalScripts) + response = response.stripScripts(); + + if (receiver) { + if (this.options.insertion) { + new this.options.insertion(receiver, response); + } else { + Element.update(receiver, response); + } + } + + if (this.responseIsSuccess()) { + if (this.onComplete) + setTimeout(this.onComplete.bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +document.getElementsByClassName = function(className, parentElement) { + var children = ($(parentElement) || document.body).getElementsByTagName('*'); + return $A(children).inject([], function(elements, child) { + if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) + elements.push(child); + return elements; + }); +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) { + var Element = new Object(); +} + +Object.extend(Element, { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + Element[Element.visible(element) ? 'hide' : 'show'](element); + } + }, + + hide: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = 'none'; + } + }, + + show: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = ''; + } + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + }, + + update: function(element, html) { + $(element).innerHTML = html.stripScripts(); + setTimeout(function() {html.evalScripts()}, 10); + }, + + getHeight: function(element) { + element = $(element); + return element.offsetHeight; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).include(className); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).add(className); + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).remove(className); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + for (var i = 0; i < element.childNodes.length; i++) { + var node = element.childNodes[i]; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + Element.remove(node); + } + }, + + empty: function(element) { + return $(element).innerHTML.match(/^\s*$/); + }, + + scrollTo: function(element) { + element = $(element); + var x = element.x ? element.x : element.offsetLeft, + y = element.y ? element.y : element.offsetTop; + window.scrollTo(x, y); + }, + + getStyle: function(element, style) { + element = $(element); + var value = element.style[style.camelize()]; + if (!value) { + if (document.defaultView && document.defaultView.getComputedStyle) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css.getPropertyValue(style) : null; + } else if (element.currentStyle) { + value = element.currentStyle[style.camelize()]; + } + } + + if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) + if (Element.getStyle(element, 'position') == 'static') value = 'auto'; + + return value == 'auto' ? null : value; + }, + + setStyle: function(element, style) { + element = $(element); + for (name in style) + element.style[name.camelize()] = style[name]; + }, + + getDimensions: function(element) { + element = $(element); + if (Element.getStyle(element, 'display') != 'none') + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = ''; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = 'none'; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return; + element._overflow = element.style.overflow; + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') + element.style.overflow = 'hidden'; + }, + + undoClipping: function(element) { + element = $(element); + if (element._overflow) return; + element.style.overflow = element._overflow; + element._overflow = undefined; + } +}); + +var Toggle = new Object(); +Toggle.display = Element.toggle; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content.stripScripts(); + + if (this.adjacency && this.element.insertAdjacentHTML) { + try { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } catch (e) { + if (this.element.tagName.toLowerCase() == 'tbody') { + this.insertContent(this.contentFromAnonymousTable()); + } else { + throw e; + } + } + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.insertContent([this.range.createContextualFragment(this.content)]); + } + + setTimeout(function() {content.evalScripts()}, 10); + }, + + contentFromAnonymousTable: function() { + var div = document.createElement('div'); + div.innerHTML = '' + this.content + '
'; + return $A(div.childNodes[0].childNodes[0].childNodes); + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function(fragments) { + fragments.reverse(false).each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); + } +}); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set(this.toArray().concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set(this.select(function(className) { + return className != classNameToRemove; + }).join(' ')); + }, + + toString: function() { + return this.toArray().join(' '); + } +} + +Object.extend(Element.ClassNames.prototype, Enumerable); +var Field = { + clear: function() { + for (var i = 0; i < arguments.length; i++) + $(arguments[i]).value = ''; + }, + + focus: function(element) { + $(element).focus(); + }, + + present: function() { + for (var i = 0; i < arguments.length; i++) + if ($(arguments[i]).value == '') return false; + return true; + }, + + select: function(element) { + $(element).select(); + }, + + activate: function(element) { + element = $(element); + element.focus(); + if (element.select) + element.select(); + } +} + +/*--------------------------------------------------------------------------*/ + +var Form = { + serialize: function(form) { + var elements = Form.getElements($(form)); + var queryComponents = new Array(); + + for (var i = 0; i < elements.length; i++) { + var queryComponent = Form.Element.serialize(elements[i]); + if (queryComponent) + queryComponents.push(queryComponent); + } + + return queryComponents.join('&'); + }, + + getElements: function(form) { + form = $(form); + var elements = new Array(); + + for (tagName in Form.Element.Serializers) { + var tagElements = form.getElementsByTagName(tagName); + for (var j = 0; j < tagElements.length; j++) + elements.push(tagElements[j]); + } + return elements; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) + return inputs; + + var matchingInputs = new Array(); + for (var i = 0; i < inputs.length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || + (name && input.name != name)) + continue; + matchingInputs.push(input); + } + + return matchingInputs; + }, + + disable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.blur(); + element.disabled = 'true'; + } + }, + + enable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.disabled = ''; + } + }, + + findFirstElement: function(form) { + return Form.getElements(form).find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + Field.activate(Form.findFirstElement(form)); + }, + + reset: function(form) { + $(form).reset(); + } +} + +Form.Element = { + serialize: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) { + var key = encodeURIComponent(parameter[0]); + if (key.length == 0) return; + + if (parameter[1].constructor != Array) + parameter[1] = [parameter[1]]; + + return parameter[1].map(function(value) { + return key + '=' + encodeURIComponent(value); + }).join('&'); + } + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return parameter[1]; + } +} + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'submit': + case 'hidden': + case 'password': + case 'text': + return Form.Element.Serializers.textarea(element); + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + } + return false; + }, + + inputSelector: function(element) { + if (element.checked) + return [element.name, element.value]; + }, + + textarea: function(element) { + return [element.name, element.value]; + }, + + select: function(element) { + return Form.Element.Serializers[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var value = '', opt, index = element.selectedIndex; + if (index >= 0) { + opt = element.options[index]; + value = opt.value; + if (!value && !('value' in opt)) + value = opt.text; + } + return [element.name, value]; + }, + + selectMany: function(element) { + var value = new Array(); + for (var i = 0; i < element.length; i++) { + var opt = element.options[i]; + if (opt.selected) { + var optValue = opt.value; + if (!optValue && !('value' in opt)) + optValue = opt.text; + value.push(optValue); + } + } + return [element.name, value]; + } +} + +/*--------------------------------------------------------------------------*/ + +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + var elements = Form.getElements(this.element); + for (var i = 0; i < elements.length; i++) + this.registerCallback(elements[i]); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + + element: function(event) { + return event.target || event.srcElement; + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + event.cancelBubble = true; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0; i < Event.observers.length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.attachEvent)) + name = 'keydown'; + + this._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.detachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + element.detachEvent('on' + name, observer); + } + } +}); + +/* prevent memory leaks in IE */ +Event.observe(window, 'unload', Event.unloadCache, false); +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + offsetParent: function(element) { + if (element.offsetParent) return element.offsetParent; + if (element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return element; + + return document.body; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + clone: function(source, target) { + source = $(source); + target = $(target); + target.style.position = 'absolute'; + var offsets = this.cumulativeOffset(source); + target.style.top = offsets[1] + 'px'; + target.style.left = offsets[0] + 'px'; + target.style.width = source.offsetWidth + 'px'; + target.style.height = source.offsetHeight + 'px'; + }, + + page: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent==document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + clone: function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; + }, + + absolutize: function(element) { + element = $(element); + if (element.style.position == 'absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px';; + element.style.left = left + 'px';; + element.style.width = width + 'px';; + element.style.height = height + 'px';; + }, + + relativize: function(element) { + element = $(element); + if (element.style.position == 'relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return [valueL, valueT]; + } +} \ No newline at end of file diff --git a/svn-commit.2.tmp b/svn-commit.2.tmp new file mode 100644 index 0000000000..168a752b70 --- /dev/null +++ b/svn-commit.2.tmp @@ -0,0 +1,4 @@ +Created new trunk. +--This line, and those below, will be ignored-- + +A http://svn.openlayers.org/trunk diff --git a/svn-commit.tmp b/svn-commit.tmp new file mode 100644 index 0000000000..80b1c8914f --- /dev/null +++ b/svn-commit.tmp @@ -0,0 +1,4 @@ +Created new trunk directory. +--This line, and those below, will be ignored-- + +A http://svn.openlayers.org/trunk