Strabon
changeset 542:4f97fb6389a5
added support for viewing a KML in Strabon endpoint when in localhost (using geoxml3)
Fixes Ticket #8: http://bug.strabon.di.uoa.gr/ticket/8
Fixes Ticket #8: http://bug.strabon.di.uoa.gr/ticket/8
author | Babis Nikolaou <charnik@di.uoa.gr> |
---|---|
date | Fri Sep 14 13:03:24 2012 +0300 (2012-09-14) |
parents | f263e457fb35 |
children | f7e99907a8f1 4657ea5fa82d |
files | endpoint/WebContent/js/ProjectedOverlay.js endpoint/WebContent/js/ZipFile.complete.js endpoint/WebContent/js/geoxml3-kmz.js endpoint/WebContent/query.jsp endpoint/pom.xml endpoint/src/main/java/eu/earthobservatory/org/StrabonEndpoint/QueryBean.java |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/endpoint/WebContent/js/ProjectedOverlay.js Fri Sep 14 13:03:24 2012 +0300 1.3 @@ -0,0 +1,138 @@ 1.4 +// Create an overlay on the map from a projected image - Maps v3... 1.5 +// Author. John D. Coryat 05/2009 1.6 +// USNaviguide LLC - http://www.usnaviguide.com 1.7 +// Thanks go to Mile Williams EInsert: http://econym.googlepages.com/einsert.js, Google's GOverlay Example and Bratliff's suggestion... 1.8 +// Opacity code from TPhoto: http://gmaps.tommangan.us/addtphoto.html 1.9 +// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 1.10 +// 1.11 +// Parameters: 1.12 +// map: This Map 1.13 +// imageUrl: URL of the image (Mandatory) 1.14 +// bounds: Bounds object of image destination (Mandatory) 1.15 +// Options: 1.16 +// addZoom: Added Zoom factor as a parameter to the imageUrl (include complete parameter, including separater like '?zoom=' 1.17 +// percentOpacity: Default 50, percent opacity to use when the image is loaded 0-100. 1.18 +// id: Default imageUrl, ID of the div 1.19 +// 1.20 + 1.21 +function ProjectedOverlay(map, imageUrl, bounds, opts) 1.22 +{ 1.23 + google.maps.OverlayView.call(this); 1.24 + 1.25 + this.map_ = map; 1.26 + this.url_ = imageUrl ; 1.27 + this.bounds_ = bounds ; 1.28 + this.addZ_ = opts.addZoom || '' ; // Add the zoom to the image as a parameter 1.29 + this.id_ = opts.id || this.url_ ; // Added to allow for multiple images 1.30 + this.percentOpacity_ = opts.percentOpacity || 50 ; 1.31 + 1.32 + this.setMap(map); 1.33 +} 1.34 + 1.35 +ProjectedOverlay.prototype = new google.maps.OverlayView(); 1.36 + 1.37 +ProjectedOverlay.prototype.createElement = function() 1.38 +{ 1.39 + var panes = this.getPanes() ; 1.40 + var div = this.div_ ; 1.41 + 1.42 + if (!div) 1.43 + { 1.44 + div = this.div_ = document.createElement("div"); 1.45 + div.style.position = "absolute" ; 1.46 + div.setAttribute('id',this.id_) ; 1.47 + this.div_ = div ; 1.48 + this.lastZoom_ = -1 ; 1.49 + if( this.percentOpacity_ ) 1.50 + { 1.51 + this.setOpacity(this.percentOpacity_) ; 1.52 + } 1.53 + panes.overlayLayer.appendChild(div); 1.54 + } 1.55 +} 1.56 + 1.57 +// Remove the main DIV from the map pane 1.58 + 1.59 +ProjectedOverlay.prototype.remove = function() 1.60 +{ 1.61 + if (this.div_) 1.62 + { 1.63 + this.div_.parentNode.removeChild(this.div_); 1.64 + this.div_ = null; 1.65 + } 1.66 +} 1.67 + 1.68 +// Redraw based on the current projection and zoom level... 1.69 + 1.70 +ProjectedOverlay.prototype.draw = function(firstTime) 1.71 +{ 1.72 + // Creates the element if it doesn't exist already. 1.73 + 1.74 + this.createElement(); 1.75 + 1.76 + if (!this.div_) 1.77 + { 1.78 + return ; 1.79 + } 1.80 + 1.81 + var c1 = this.get('projection').fromLatLngToDivPixel(this.bounds_.getSouthWest()); 1.82 + var c2 = this.get('projection').fromLatLngToDivPixel(this.bounds_.getNorthEast()); 1.83 + 1.84 + if (!c1 || !c2) return; 1.85 + 1.86 + // Now position our DIV based on the DIV coordinates of our bounds 1.87 + 1.88 + this.div_.style.width = Math.abs(c2.x - c1.x) + "px"; 1.89 + this.div_.style.height = Math.abs(c2.y - c1.y) + "px"; 1.90 + this.div_.style.left = Math.min(c2.x, c1.x) + "px"; 1.91 + this.div_.style.top = Math.min(c2.y, c1.y) + "px"; 1.92 + 1.93 + // Do the rest only if the zoom has changed... 1.94 + 1.95 + if ( this.lastZoom_ == this.map_.getZoom() ) 1.96 + { 1.97 + return ; 1.98 + } 1.99 + 1.100 + this.lastZoom_ = this.map_.getZoom() ; 1.101 + 1.102 + var url = this.url_ ; 1.103 + 1.104 + if ( this.addZ_ ) 1.105 + { 1.106 + url += this.addZ_ + this.map_.getZoom() ; 1.107 + } 1.108 + 1.109 + this.div_.innerHTML = '<img src="' + url + '" width=' + this.div_.style.width + ' height=' + this.div_.style.height + ' >' ; 1.110 +} 1.111 + 1.112 +ProjectedOverlay.prototype.setOpacity=function(opacity) 1.113 +{ 1.114 + if (opacity < 0) 1.115 + { 1.116 + opacity = 0 ; 1.117 + } 1.118 + if(opacity > 100) 1.119 + { 1.120 + opacity = 100 ; 1.121 + } 1.122 + var c = opacity/100 ; 1.123 + 1.124 + if (typeof(this.div_.style.filter) =='string') 1.125 + { 1.126 + this.div_.style.filter = 'alpha(opacity:' + opacity + ')' ; 1.127 + } 1.128 + if (typeof(this.div_.style.KHTMLOpacity) == 'string' ) 1.129 + { 1.130 + this.div_.style.KHTMLOpacity = c ; 1.131 + } 1.132 + if (typeof(this.div_.style.MozOpacity) == 'string') 1.133 + { 1.134 + this.div_.style.MozOpacity = c ; 1.135 + } 1.136 + if (typeof(this.div_.style.opacity) == 'string') 1.137 + { 1.138 + this.div_.style.opacity = c ; 1.139 + } 1.140 +} 1.141 +
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/endpoint/WebContent/js/ZipFile.complete.js Fri Sep 14 13:03:24 2012 +0300 2.3 @@ -0,0 +1,2172 @@ 2.4 +// ZipFile.complete.js 2.5 +// 2.6 +// 2/17/2012 2.7 +// 2.8 +// ======================================================= 2.9 +// 2.10 + 2.11 +// JSIO.core.js 2.12 +// ------------------------------------------------------------------ 2.13 +// 2.14 +// core methods for Javascript IO. 2.15 +// 2.16 +// ======================================================= 2.17 +// 2.18 +// Copyleft (c) 2010, Dino Chiesa via MS-PL 2.19 +// Copyleft (c) 2012, Brendan Byrd via GPL 2.20 +// 2.21 +// This work is licensed under the GPLv3. 2.22 + 2.23 + 2.24 +(function(){ 2.25 + if (typeof JSIO == "object"){ 2.26 + var e1 = new Error("JSIO is already defined"); 2.27 + e1.source = "JSIO.core.js"; 2.28 + throw e1; 2.29 + } 2.30 + 2.31 + JSIO = {}; 2.32 + 2.33 + JSIO.version = "2.0 2012Feb"; 2.34 + 2.35 + JSIO.throwError = function(msg, source, sub) { 2.36 + var error = new Error("Error: " + msg); 2.37 + error.source = (source || this._typename || 'JSIO') + (sub ? '.'+sub : ''); 2.38 + throw error; 2.39 + } 2.40 + 2.41 + // Format a number as hex. Quantities over 7ffffff will be displayed properly. 2.42 + JSIO.decimalToHexString = function(number, digits) { 2.43 + if (number < 0) { 2.44 + number = 0xFFFFFFFF + number + 1; 2.45 + } 2.46 + var r1 = number.toString(16).toUpperCase(); 2.47 + if (digits) { 2.48 + r1 = "00000000" + r1; 2.49 + r1 = r1.substring(r1.length - digits); 2.50 + } 2.51 + return r1; 2.52 + }; 2.53 + 2.54 + JSIO.FileType = { 2.55 + Text : 0, 2.56 + Binary : 1, 2.57 + XML : 2, 2.58 + Unknown : 3 2.59 + }; 2.60 + 2.61 + 2.62 + JSIO.guessFileType = function(name) { 2.63 + if (name == "makefile") { return JSIO.FileType.Text; } 2.64 + 2.65 + var lastDot = name.lastIndexOf("."); 2.66 + if (lastDot <= 0) { return JSIO.FileType.Unknown; } 2.67 + 2.68 + var ext= name.substring(lastDot); 2.69 + if (ext == ".zip") { return JSIO.FileType.Binary; } 2.70 + if (ext == ".xlsx") { return JSIO.FileType.Binary; } 2.71 + if (ext == ".docx") { return JSIO.FileType.Binary; } 2.72 + if (ext == ".dll") { return JSIO.FileType.Binary; } 2.73 + if (ext == ".obj") { return JSIO.FileType.Binary; } 2.74 + if (ext == ".pdb") { return JSIO.FileType.Binary; } 2.75 + if (ext == ".exe") { return JSIO.FileType.Binary; } 2.76 + if (ext == ".kmz") { return JSIO.FileType.Binary; } 2.77 + 2.78 + if (ext == ".xml") { return JSIO.FileType.XML; } 2.79 + if (ext == ".xsl") { return JSIO.FileType.XML; } 2.80 + if (ext == ".kml") { return JSIO.FileType.XML; } 2.81 + if (ext == ".csproj") { return JSIO.FileType.XML; } 2.82 + if (ext == ".vbproj") { return JSIO.FileType.XML; } 2.83 + if (ext == ".shfbproj") { return JSIO.FileType.XML; } 2.84 + if (ext == ".resx") { return JSIO.FileType.XML; } 2.85 + if (ext == ".xslt") { return JSIO.FileType.XML; } 2.86 + 2.87 + if (ext == ".sln") { return JSIO.FileType.Text; } 2.88 + if (ext == ".htm") { return JSIO.FileType.Text; } 2.89 + if (ext == ".html") { return JSIO.FileType.Text; } 2.90 + if (ext == ".js") { return JSIO.FileType.Text; } 2.91 + if (ext == ".vb") { return JSIO.FileType.Text; } 2.92 + if (ext == ".txt") { return JSIO.FileType.Text; } 2.93 + if (ext == ".rels") { return JSIO.FileType.Text; } 2.94 + if (ext == ".css") { return JSIO.FileType.Text; } 2.95 + if (ext == ".cs") { return JSIO.FileType.Text; } 2.96 + if (ext == ".asp") { return JSIO.FileType.Text; } 2.97 + 2.98 + return JSIO.FileType.Unknown; 2.99 + }; 2.100 + 2.101 + JSIO.stringOfLength = function (charCode, length) { 2.102 + var s3 = ""; 2.103 + for (var i = 0; i < length; i++) { 2.104 + s3 += String.fromCharCode(charCode); 2.105 + } 2.106 + return s3; 2.107 + }; 2.108 + 2.109 + JSIO.formatByteArray = function(b) { 2.110 + var s1 = "0000 "; 2.111 + var s2 = ""; 2.112 + for (var i = 0; i < b.length; i++) { 2.113 + if (i !== 0 && i % 16 === 0) { 2.114 + s1 += " " + s2 +"\n" + JSIO.decimalToHexString(i, 4) + " "; 2.115 + s2 = ""; 2.116 + } 2.117 + s1 += JSIO.decimalToHexString(b[i], 2) + " "; 2.118 + if (b[i] >=32 && b[i] <= 126) { 2.119 + s2 += String.fromCharCode(b[i]); 2.120 + } else { 2.121 + s2 += "."; 2.122 + } 2.123 + } 2.124 + if (s2.length > 0) { 2.125 + s1 += JSIO.stringOfLength(32, ((i%16>0)? ((16 - i%16) * 3) : 0) + 4) + s2; 2.126 + } 2.127 + return s1; 2.128 + }; 2.129 + 2.130 + JSIO.htmlEscape = function(str) { 2.131 + return str 2.132 + .replace(new RegExp( "&", "g" ), "&") 2.133 + .replace(new RegExp( "<", "g" ), "<") 2.134 + .replace(new RegExp( ">", "g" ), ">") 2.135 + .replace(new RegExp( "\x13", "g" ), "<br/>") 2.136 + .replace(new RegExp( "\x10", "g" ), "<br/>"); 2.137 + }; 2.138 + 2.139 + JSIO.massApply = function(func, funcThis, arr, needReturn) { 2.140 + var arrayLimit = 99999; // Chrome has an apply/array limit of 99999; Firefox = 491519 2.141 + if (arr.length < arrayLimit) return func.apply(funcThis, arr); 2.142 + else { 2.143 + var newThis = funcThis; 2.144 + var offset = 0; 2.145 + var end = 99999; 2.146 + 2.147 + while (offset < arr.length) { 2.148 + var arrSlice; 2.149 + if (arr.subarray) arrSlice = arr.subarray(offset, end); 2.150 + else if (arr.slice) arrSlice = arr.slice(offset, end); 2.151 + 2.152 + if (needReturn) newThis += func.apply(newThis, arrSlice); 2.153 + else func.apply(funcThis, arrSlice); 2.154 + 2.155 + offset += arrayLimit; 2.156 + end += arrayLimit; 2.157 + end = Math.min(arr.length, end); 2.158 + } 2.159 + return newThis; 2.160 + } 2.161 + } 2.162 + 2.163 +})(); 2.164 + 2.165 +/// JSIO.core.js ends 2.166 + 2.167 + 2.168 +// JSIO.BasicByteReaders.js 2.169 +// ------------------------------------------------------------------ 2.170 +// 2.171 +// Part of the JSIO library. Adds a couple basic ByteReaders to JSIO. 2.172 +// ByteReaders are forward-only byte-wise readers. They read one byte at 2.173 +// a time from a source. 2.174 +// 2.175 +// ======================================================= 2.176 +// 2.177 +// A ByteReader exposes an interface with these functions: 2.178 +// 2.179 +// readByte() 2.180 +// must return null when EOF is reached. 2.181 +// 2.182 +// readToEnd() 2.183 +// returns an array of all bytes read, to EOF 2.184 +// 2.185 +// beginReadToEnd(callback) 2.186 +// async version of the above 2.187 +// 2.188 +// readBytes(n) 2.189 +// returns an array of the next n bytes from the source 2.190 +// 2.191 +// beginReadBytes(n, callback) 2.192 +// async version of the above 2.193 +// 2.194 +// ======================================================= 2.195 +// 2.196 +// Copyleft (c) 2010, Dino Chiesa via MS-PL 2.197 +// Copyleft (c) 2012, Brendan Byrd via GPL 2.198 +// 2.199 +// This work is licensed under the GPLv3. 2.200 + 2.201 + 2.202 +(function(){ 2.203 + var version = "2.0 2012Feb"; 2.204 + 2.205 + if (typeof JSIO !== "object") { JSIO = {}; } 2.206 + if ((typeof JSIO.version !== "string")) { 2.207 + JSIO.version = version; 2.208 + } 2.209 + else if ((JSIO.version.length < 3) || 2.210 + (JSIO.version.substring(0,3) !== "2.0")) { 2.211 + JSIO.version += " " + version; 2.212 + } 2.213 + 2.214 + // ======================================================= 2.215 + // the base object, used as the prototype of all ByteReader objects. 2.216 + var _byteReaderBase = function () { 2.217 + this.position = 0; 2.218 + // position must be incremented in .readByte() for all derived classes 2.219 + }; 2.220 + 2.221 + _byteReaderBase.prototype._throwError = JSIO.throwError; 2.222 + 2.223 + _byteReaderBase.prototype._limitCheck = function(len, startPos) { 2.224 + var LOE = { 2.225 + len: len, 2.226 + pos: startPos, 2.227 + end: startPos+len 2.228 + }; 2.229 + 2.230 + if (len === 0) return {len:0, pos:0, end:0}; 2.231 + if (len < 0) this._throwError("Invalid read length"); 2.232 + if (!this.length) return {len:len, pos:this.position, end:len+this.position}; 2.233 + if (!startPos >= 0) LOE.pos = this.position; 2.234 + if (this.length <= LOE.pos) this._throwError("EOF reached"); 2.235 + 2.236 + LOE.end = LOE.pos+len; 2.237 + if (this.length < LOE.end) LOE.end = LOE.pos+(LOE.len = this.length-this.position); 2.238 + return LOE; 2.239 + } 2.240 + 2.241 + JSIO.SeekOrigin = { 2.242 + Begin : 0, 2.243 + Current : 1, 2.244 + End : 2, 2.245 + SEEK_SET : 0, 2.246 + SEEK_CUR : 1, 2.247 + SEEK_END : 2 2.248 + }; 2.249 + 2.250 + _byteReaderBase.prototype.seek = function(offset, origin) { 2.251 + switch (origin) { 2.252 + case JSIO.SeekOrigin.Begin: 2.253 + if (offset == this.position) return this.position; 2.254 + if (!this.length) { 2.255 + if (offset < this.position) this._throwError('Uni-directional stream cannot seek backwards', null, 'seek'); 2.256 + else if (offset > this.position) return this.read(offset - this.position); // read will limit check 2.257 + } 2.258 + else { 2.259 + if (this.length < offset) this._throwError('Cannot seek past reader length', null, 'seek'); 2.260 + this.position = offset; 2.261 + } 2.262 + break; 2.263 + case JSIO.SeekOrigin.Current: 2.264 + return this.seek(this.position + offset, JSIO.SeekOrigin.Begin); 2.265 + break; 2.266 + case JSIO.SeekOrigin.End: 2.267 + if (!this.length) this._throwError('Uni-directional stream has no known end length for seek', null, 'seek'); 2.268 + return this.seek(this.length - 1 + offset, JSIO.SeekOrigin.Begin); 2.269 + break; 2.270 + default: 2.271 + this._throwError('Invalid seek method', null, 'seek'); 2.272 + break; 2.273 + } 2.274 + 2.275 + return this.position; 2.276 + }; 2.277 + 2.278 + _byteReaderBase.prototype.read = function(len, startPos) { 2.279 + var LOE = this._limitCheck(len, startPos); 2.280 + if (LOE.len === 0) return []; 2.281 + if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin); 2.282 + 2.283 + var bytesRead = []; 2.284 + 2.285 + // Faster methods with an array or stream 2.286 + if (this.array && this.array.subarray) bytesRead = this.array.subarray(LOE.pos, LOE.end); 2.287 + else if (this.array && this.array.slice) bytesRead = this.array.slice(LOE.pos, LOE.end); 2.288 + else if (this.stream) bytesRead = this.stream.read(LOE.len, LOE.pos); 2.289 + else if (this.length) { // Random-access stream 2.290 + for(var i=LOE.pos; i<LOE.end; i++) { bytesRead.push(this.readByteAt(i)); } 2.291 + } 2.292 + else { // Uni-directional stream 2.293 + for(var i=LOE.pos; i<LOE.end; i++) { 2.294 + var b = this.readByte(); 2.295 + if (b === null || b === undefined) break; 2.296 + bytesRead.push(b); 2.297 + } 2.298 + } 2.299 + this.position = LOE.end; 2.300 + return bytesRead; 2.301 + }; 2.302 + 2.303 + _byteReaderBase.prototype.beginRead = function(len, startPos, callback) { 2.304 + var LOE = this._limitCheck(len, startPos); 2.305 + if (LOE.len === 0) return setTimeout(function() { callback([]); }, 1); 2.306 + if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin); 2.307 + 2.308 + var bytesRead = []; 2.309 + var thisReader = this; 2.310 + var leftToRead = LOE.len; 2.311 + 2.312 + var readBatchAsync = function() { 2.313 + var c = 0; 2.314 + var pos = thisReader.position; 2.315 + 2.316 + // read a 32k batch 2.317 + var l = (leftToRead >= 32768) ? 32768 : leftToRead; 2.318 + var newBytes = thisReader.read(l); 2.319 + JSIO.massApply(bytesRead.push, bytesRead, newBytes); 2.320 + c += l; 2.321 + leftToRead -= l; 2.322 + if (newBytes.length < l) leftToRead = 0; 2.323 + 2.324 + if (leftToRead>0) setTimeout(readBatchAsync, 1); 2.325 + else callback(bytesRead); 2.326 + }; 2.327 + 2.328 + // kickoff 2.329 + setTimeout(readBatchAsync, 1); // always async, in ALL situations 2.330 + return null; 2.331 + }; 2.332 + 2.333 + _byteReaderBase.prototype.readToEnd = function() { 2.334 + if (this.array && this.array.subarray) return this.array.subarray(this.position); 2.335 + else if (this.array && this.array.slice) return this.array.slice(this.position); 2.336 + else if (this.length) return this.read(this.length - this.position); 2.337 + else return this.read(9000 * 9000); // over 9000 2.338 + }; 2.339 + 2.340 + _byteReaderBase.prototype.beginReadToEnd = function(callback) { 2.341 + if (this.array && this.array.subarray) setTimeout(function() { callback( this.array.subarray(this.position) ); }, 1); 2.342 + else if (this.array && this.array.slice) setTimeout(function() { callback( this.array.slice(this.position) ); }, 1); 2.343 + else if (this.length) return this.beginRead(this.length - this.position, this.position, callback); 2.344 + else return this.beginRead(9000 * 9000, this.position, callback); 2.345 + }; 2.346 + 2.347 + // Generic routines; one of these two MUST be overloaded (preferrably both) 2.348 + _byteReaderBase.prototype.readByte = function(){ 2.349 + if (this.length && this.position >= this.length) return null; // EOF 2.350 + 2.351 + var byte; 2.352 + if (this.array) byte = this.array[this.position++]; 2.353 + else if (this.length) byte = this.readByteAt(this.position++); 2.354 + else if (this.stream) byte = this.stream.read(1)[0]; 2.355 + else byte = this.read(1)[0]; 2.356 + return (byte === null || byte === undefined) ? null : byte; 2.357 + }; 2.358 + _byteReaderBase.prototype.readByteAt = function(i) { 2.359 + var pos = this.position; // no position changes on this one 2.360 + if (i === null || i === undefined) i = this.position; 2.361 + 2.362 + var byte; 2.363 + if (this.array) byte = this.array[i]; 2.364 + else if (i === pos) byte = this.readByte(); 2.365 + else if (this.stream) byte = this.stream.read(1, i)[0]; 2.366 + else byte = this.read(1, i)[0]; 2.367 + 2.368 + this.position = pos; 2.369 + return (byte === null || byte === undefined) ? null : byte; 2.370 + } 2.371 + 2.372 + _byteReaderBase.prototype.readBytes = _byteReaderBase.prototype.read; 2.373 + _byteReaderBase.prototype.beginReadBytes = function(len, callback) { return this.beginRead(len, this.position, callback); }; 2.374 + 2.375 + _byteReaderBase.prototype.readNumber = function(len, startPos){ 2.376 + var LOE = this._limitCheck(len, startPos); 2.377 + if (LOE.len === 0) LOE.len = 1; 2.378 + if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin); 2.379 + 2.380 + var result = 0; 2.381 + var bytes = this.read(LOE.len, LOE.pos); 2.382 + for (var i=bytes.length-1; i>=0; i--) { 2.383 + // IE only supports 32-bit integer shifting 2.384 + //result = result << 8 | bytes[i]; 2.385 + result = result*256 + bytes[i]; 2.386 + } 2.387 + return result; 2.388 + }; 2.389 + 2.390 + _byteReaderBase.prototype.readString = function(len, startPos){ 2.391 + var LOE = this._limitCheck(len, startPos); 2.392 + if (LOE.len === 0) return ''; 2.393 + if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin); 2.394 + 2.395 + var result = ''; 2.396 + var bytes = this.read(LOE.len, LOE.pos); 2.397 + for(var i=0; i<bytes.length; i++){ 2.398 + result += String.fromCharCode(bytes[i]); 2.399 + } 2.400 + return result; 2.401 + }; 2.402 + 2.403 + _byteReaderBase.prototype.readNullTerminatedString = function(startPos){ 2.404 + var pos = startPos || this.position; 2.405 + if (this.length && this.length < pos) this._throwError('EOF reached', null, 'readNullTerminatedString'); 2.406 + if (pos != this.position) this.seek(pos, JSIO.SeekOrigin.Begin); 2.407 + 2.408 + var slarge = ""; 2.409 + var s = ""; 2.410 + var c = 0; 2.411 + 2.412 + // Faster method with an array 2.413 + if (this.array && this.array.indexOf) { 2.414 + var len = pos - this.array.indexOf(0, pos); 2.415 + if (len > 0) return this.readString(len, pos); 2.416 + } 2.417 + 2.418 + var ch; 2.419 + while(1) { 2.420 + ch = String.fromCharCode(this.readByteAt(pos+c)); 2.421 + if (ch === null) break; 2.422 + 2.423 + s += ch; 2.424 + c++; 2.425 + if(c >= 32768) { 2.426 + slarge += s; 2.427 + s = ""; 2.428 + pos += c; 2.429 + this.position += c; 2.430 + c = 0; 2.431 + } 2.432 + }; 2.433 + this.position = pos + c; 2.434 + return slarge + s; 2.435 + }; 2.436 + 2.437 + _byteReaderBase.prototype.beginReadNullTerminatedString = function(callback, startPos){ 2.438 + var pos = startPos || this.position; 2.439 + if (this.length && this.length < pos) this._throwError('EOF reached', null, 'beginReadNullTerminatedString'); 2.440 + 2.441 + var slarge = ""; 2.442 + var s = ""; 2.443 + var thisBinStream = this; 2.444 + 2.445 + var readBatchAsync = function() { 2.446 + var c = 0; 2.447 + 2.448 + var ch; 2.449 + while(1) { 2.450 + ch = String.fromCharCode(this.readByteAt(pos+c)); 2.451 + if (ch === null) break; 2.452 + 2.453 + s += ch; 2.454 + c++; 2.455 + if(c >= 32768) { 2.456 + slarge += s; 2.457 + s = ""; 2.458 + pos += c; 2.459 + this.position += c; 2.460 + c = 0; 2.461 + } 2.462 + }; 2.463 + 2.464 + thisBinStream.position = pos + c; 2.465 + if (ch!==null) setTimeout(readBatchAsync, 1); 2.466 + else callback(slarge+s); 2.467 + }; 2.468 + 2.469 + // Faster method with an array 2.470 + if (this.array && this.array.indexOf) { 2.471 + var len = pos - this.array.indexOf(0, pos); 2.472 + if (len > 0) readBatchASync = function() { callback(thisBinStream.readString(len, pos)); }; 2.473 + } 2.474 + 2.475 + // kickoff 2.476 + setTimeout(readBatchAsync, 1); // always async, in ALL situations 2.477 + return null; 2.478 + }; 2.479 + 2.480 + 2.481 + 2.482 + JSIO._ByteReaderBase = _byteReaderBase; 2.483 + // ======================================================= 2.484 + 2.485 + 2.486 + 2.487 + 2.488 + // ======================================================= 2.489 + // reads from an array of bytes. 2.490 + // This basically wraps a readByte() fn onto array access. 2.491 + var _arrayReader = function(array) { 2.492 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.ArrayReader', 'ctor'); 2.493 + this.position = 0; 2.494 + this.array = array; 2.495 + this.length = array.length; 2.496 + this._typename = "JSIO.ArrayReader"; 2.497 + this._version = version; 2.498 + return this; 2.499 + }; 2.500 + 2.501 + _arrayReader.prototype = new JSIO._ByteReaderBase(); 2.502 + 2.503 + _arrayReader.prototype.readByte = function() { 2.504 + if (this.position >= this.array.length) return null; // EOF 2.505 + return this.array[this.position++]; 2.506 + }; 2.507 + _arrayReader.prototype.readByteAt = function(i) { 2.508 + return this.array[i]; 2.509 + }; 2.510 + 2.511 + // ======================================================= 2.512 + 2.513 + 2.514 + // ======================================================= 2.515 + // reads bytes at a time from a defined segment of a stream. 2.516 + var _streamSegmentReader = function(stream, offset, len) { 2.517 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.StreamSegmentReader', 'ctor'); 2.518 + if (!stream) this._throwError('You must pass a non-null stream', 'JSIO.StreamSegmentReader', 'ctor'); 2.519 + 2.520 + if (!(offset >= 1)) offset = 0; 2.521 + if (!(len >= 1)) len = 0; 2.522 + 2.523 + this.stream = stream; 2.524 + this.array = null; 2.525 + if (stream.array) { 2.526 + var end = len ? offset + len : null; 2.527 + if (stream.array.subarray) this.array = stream.array.subarray(offset, end); 2.528 + else if (stream.array.slice) this.array = stream.array.slice(offset, end); 2.529 + } 2.530 + this.length = this.array ? this.array.length : (stream.length ? stream.length - offset : null); 2.531 + this.offset = offset; 2.532 + this.limit = len; 2.533 + this.position = 0; 2.534 + this._typeName = 'JSIO.StreamSegmentReader'; 2.535 + this._version = version; 2.536 + 2.537 + if (this.array) { 2.538 + this.readByte = _arrayReader.prototype.readByte; 2.539 + this.readByteAt = _arrayReader.prototype.readByteAt; 2.540 + } 2.541 + return this; 2.542 + }; 2.543 + 2.544 + _streamSegmentReader.prototype = new JSIO._ByteReaderBase(); 2.545 + 2.546 + _streamSegmentReader.prototype.readByte = function() { 2.547 + if (this.limit && this.position >= this.limit) return null; // EOF 2.548 + this.position++; 2.549 + return this.stream.readByteAt(this.offset + this.position - 1); 2.550 + }; 2.551 + _streamSegmentReader.prototype.readByteAt = function(i) { 2.552 + if (this.limit && i >= this.limit) return null; // EOF 2.553 + return this.stream.readByteAt(this.offset + i); 2.554 + }; 2.555 + 2.556 + // ======================================================= 2.557 + 2.558 + JSIO.ArrayReader = _arrayReader; 2.559 + JSIO.StreamReader = _streamSegmentReader; 2.560 + JSIO.StreamSegmentReader = _streamSegmentReader; 2.561 + 2.562 +})(); 2.563 + 2.564 + 2.565 +/// JSIO.BasicByteReaders.js ends 2.566 + 2.567 +// JSIO.BinaryUrlStream.js 2.568 +// ------------------------------------------------------------------ 2.569 +// 2.570 +// a class that acts as a stream wrapper around binary files obtained from URLs. 2.571 +// 2.572 +// ======================================================= 2.573 +// 2.574 +// Copyleft (c) 2008, Andy G.P. Na <nagoon97@naver.com> via an MIT-style license 2.575 +// Copyleft (c) 2012, Brendan Byrd via GPL 2.576 +// 2.577 +// This work is licensed under the GPLv3. 2.578 + 2.579 +(function(){ 2.580 + var version = "2.0 2012Feb"; 2.581 + var typename = "JSIO.BinaryUrlStream"; 2.582 + 2.583 + if ((typeof JSIO !== "object") || 2.584 + (typeof JSIO.version !== "string") || 2.585 + (JSIO.version.length < 3) || 2.586 + (JSIO.version.substring(0,3) !== "2.0")) 2.587 + JSIO.throwError('This extension requires JSIO.core.js v2.0', typename); 2.588 + 2.589 + if (typeof JSIO._ByteReaderBase !== "function") 2.590 + JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); 2.591 + 2.592 + if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) { 2.593 + var IEBinaryToArray_ByteStr_Script = 2.594 + "<!-- IEBinaryToArray_ByteStr -->\r\n"+ 2.595 + "<script type='text/vbscript'>\r\n"+ 2.596 + "Function IEBinaryToArray_ByteStr(Binary)\r\n"+ 2.597 + " IEBinaryToArray_ByteStr = CStr(Binary)\r\n"+ 2.598 + "End Function\r\n"+ 2.599 + "Function IEBinaryToArray_ByteAsc_Last(Binary)\r\n"+ 2.600 + " Dim lastIndex\r\n"+ 2.601 + " lastIndex = LenB(Binary)\r\n"+ 2.602 + " if lastIndex mod 2 Then\r\n"+ 2.603 + " IEBinaryToArray_ByteAsc_Last = AscB( MidB( Binary, lastIndex, 1 ) )\r\n"+ 2.604 + " Else\r\n"+ 2.605 + " IEBinaryToArray_ByteAsc_Last = -1\r\n"+ 2.606 + " End If\r\n"+ 2.607 + "End Function\r\n"+ 2.608 + "</script>\r\n"; 2.609 + 2.610 + // inject VBScript 2.611 + document.write(IEBinaryToArray_ByteStr_Script); 2.612 + } 2.613 + 2.614 + JSIO.IEByteMapping = null; 2.615 + 2.616 + var bus = function(url, callback) { 2.617 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.BinaryUrlStream', 'ctor'); 2.618 + 2.619 + this.callback = callback; 2.620 + this.position = 0; 2.621 + this.length = null; 2.622 + this.readByte = JSIO.ArrayReader.prototype.readByte; 2.623 + this.readByteAt = JSIO.ArrayReader.prototype.readByteAt; 2.624 + this.req = null; 2.625 + this._typename = typename; 2.626 + this._version = version; 2.627 + this.status = "-none-"; 2.628 + 2.629 + var _IeGetBinResource = function(fileURL){ 2.630 + var binStream = this; 2.631 + // http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-for-binary-data-from-javascript-in-ie 2.632 + 2.633 + // my helper to convert from responseBody to a byte array 2.634 + var convertResponseBodyToArray = function (binary) { 2.635 + var byteArray = new Array; 2.636 + 2.637 + try { 2.638 + // very fast; very little work involved 2.639 + byteArray = new VBArray(binary).toArray(); 2.640 + } 2.641 + catch(err) { 2.642 + // use the BinaryToArray VBScript 2.643 + if (!JSIO.IEByteMapping) { 2.644 + JSIO.IEByteMapping = {}; 2.645 + for ( var i = 0; i < 256; i++ ) { 2.646 + for ( var j = 0; j < 256; j++ ) { 2.647 + JSIO.IEByteMapping[ String.fromCharCode( i + j * 256 ) ] = [ i, j ]; 2.648 + } 2.649 + } 2.650 + } 2.651 + var rawBytes = IEBinaryToArray_ByteStr(binary); 2.652 + var lastAsc = IEBinaryToArray_ByteAsc_Last(binary); 2.653 + 2.654 + for ( var i = 0; i < rawBytes.length; i++ ) { 2.655 + byteArray.push.apply(byteArray, JSIO.IEByteMapping[ rawBytes.substr(i,1) ]); 2.656 + } 2.657 + if (lastAsc >= 0) byteArray.push(lastAsc); 2.658 + } 2.659 + 2.660 + return byteArray; 2.661 + }; 2.662 + 2.663 + this.req = (function() { 2.664 + if (window.XMLHttpRequest) return new window.XMLHttpRequest(); 2.665 + else if (window.ActiveXObject) { 2.666 + // the many versions of IE's XML fetchers 2.667 + var AXOs = [ 2.668 + 'MSXML2.XMLHTTP.6.0', 2.669 + 'MSXML2.XMLHTTP.5.0', 2.670 + 'MSXML2.XMLHTTP.4.0', 2.671 + 'MSXML2.XMLHTTP.3.0', 2.672 + 'MSXML2.XMLHTTP', 2.673 + 'Microsoft.XMLHTTP', 2.674 + 'MSXML.XMLHTTP' 2.675 + ]; 2.676 + for (var i = 0; i < AXOs.length; i++) { 2.677 + try { return new ActiveXObject(AXOs[i]); } 2.678 + catch(e) { continue; } 2.679 + } 2.680 + } 2.681 + return null; 2.682 + })(); 2.683 + this.req.open("GET", fileURL, true); 2.684 + this.req.setRequestHeader("Accept-Charset", "x-user-defined"); 2.685 + this.req.onreadystatechange = function(event){ 2.686 + if (binStream.req.readyState == 4) { 2.687 + binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText; 2.688 + if (binStream.req.status == 200) { 2.689 + binStream.array = convertResponseBodyToArray(binStream.req.responseBody); 2.690 + binStream.length = binStream.array.length; 2.691 + if (binStream.length < 0) this._throwError('Failed to load "'+ fileURL + '" after converting'); 2.692 + 2.693 + if (typeof binStream.callback == "function") binStream.callback(binStream); 2.694 + } 2.695 + else { 2.696 + binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status); 2.697 + } 2.698 + } 2.699 + }; 2.700 + this.req.send(); 2.701 + }; 2.702 + 2.703 + var _NormalGetBinResource = function(fileURL){ 2.704 + var binStream= this; 2.705 + this.req = new XMLHttpRequest(); 2.706 + this.req.open('GET', fileURL, true); 2.707 + this.req.onreadystatechange = function(aEvt) { 2.708 + if (binStream.req.readyState == 4) { 2.709 + binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText; 2.710 + if(binStream.req.status == 200){ 2.711 + var fileContents = binStream.req.responseText; 2.712 + binStream.length = fileContents.byteLength; 2.713 + binStream.array = fileContents.split(''); 2.714 + for ( var i = 0; i < binStream.array.length; i++ ) { 2.715 + binStream.array[i] = binStream.array[i].charCodeAt(0) & 0xff; 2.716 + } 2.717 + 2.718 + if (typeof binStream.callback == "function") binStream.callback(binStream); 2.719 + } 2.720 + else { 2.721 + binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status); 2.722 + } 2.723 + } 2.724 + }; 2.725 + //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com] 2.726 + this.req.overrideMimeType('text/plain; charset=x-user-defined'); 2.727 + this.req.send(null); 2.728 + }; 2.729 + 2.730 + // http://stackoverflow.com/questions/327685/is-there-a-way-to-read-binary-data-into-javascript 2.731 + var _ArrayBufferGetBinResource = function(fileURL){ 2.732 + var binStream= this; 2.733 + this.req = new XMLHttpRequest(); 2.734 + this.req.open('GET', fileURL, true); 2.735 + this.req.onreadystatechange = function(aEvt) { 2.736 + if (binStream.req.readyState == 4) { 2.737 + binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText; 2.738 + if(binStream.req.status == 200){ 2.739 + var fileContents = binStream.req.response; 2.740 + binStream.length = fileContents.byteLength; 2.741 + binStream.array = new Uint8Array(fileContents); 2.742 + if (typeof binStream.callback == "function") binStream.callback(binStream); 2.743 + } 2.744 + else { 2.745 + binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status); 2.746 + } 2.747 + } 2.748 + }; 2.749 + this.req.responseType = 'arraybuffer'; 2.750 + // http://stackoverflow.com/questions/11284728/how-do-i-access-8-bit-binary-data-from-javascript-in-opera 2.751 + this.req.overrideMimeType('application/octet-stream; charset=x-user-defined'); 2.752 + this.req.send(null); 2.753 + }; 2.754 + 2.755 + 2.756 + if (typeof ArrayBuffer !== 'undefined') _ArrayBufferGetBinResource.apply(this, [url]); 2.757 + else if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) _IeGetBinResource.apply(this, [url]); 2.758 + else _NormalGetBinResource.apply(this, [url]); 2.759 + }; 2.760 + 2.761 + bus.prototype = new JSIO._ByteReaderBase(); 2.762 + 2.763 + bus.prototype.readByte = function(){ 2.764 + var byte = this.readByteAt(this.position++); 2.765 + return (byte === null || byte === undefined) ? null : byte; 2.766 + }; 2.767 + 2.768 + JSIO.BinaryUrlStream = bus; 2.769 + 2.770 +})(); 2.771 + 2.772 +/// JSIO.BinaryUrlStream.js ends 2.773 + 2.774 +// JSIO.TextDecoder.js 2.775 +// ------------------------------------------------------------------ 2.776 +// 2.777 +// Part of the JSIO library. Adds text decoders, for UTF-8 and UTF-16, 2.778 +// and plain text. 2.779 +// 2.780 +// ======================================================= 2.781 +// 2.782 +// Derived in part from work by notmasteryet. 2.783 +// http://www.codeproject.com/KB/scripting/Javascript_binaryenc.aspx 2.784 + 2.785 +// Copyleft (c) 2008, notmasteryet via an MIT-style license 2.786 +// Copyleft (c) 2010, Dino Chiesa via MS-PL 2.787 +// Copyleft (c) 2012, Brendan Byrd via GPL 2.788 +// 2.789 +// This work is licensed under the GPLv3. 2.790 + 2.791 +(function(){ 2.792 + var version = "2.0 2012Feb"; 2.793 + var typename = "JSIO.TextDecoder"; 2.794 + 2.795 + if ((typeof JSIO !== "object") || 2.796 + (typeof JSIO.version !== "string") || 2.797 + (JSIO.version.length < 3) || 2.798 + (JSIO.version.substring(0,3) !== "2.0")) 2.799 + JSIO.throwError('This extension requires JSIO.core.js v2.0', typename); 2.800 + 2.801 + if (typeof JSIO._ByteReaderBase !== "function") 2.802 + JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); 2.803 + 2.804 + var _ansi = function(reader) { 2.805 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.ANSI', 'ctor'); 2.806 + this.byteReader = reader; 2.807 + this.charWidth = 1; 2.808 + this._version = version; 2.809 + this._typename = typename + ".ANSI"; 2.810 + return this; 2.811 + }; 2.812 + 2.813 + _ansi.prototype.readChar = function() { 2.814 + var code = this.byteReader.readByte(); 2.815 + return (code < 0) ? null : String.fromCharCode(code); 2.816 + }; 2.817 + 2.818 + _ansi.prototype.parseChar = function(code) { 2.819 + return (code < 0) ? null : String.fromCharCode(code); 2.820 + }; 2.821 + 2.822 + var _utf16 = function(reader) { 2.823 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF16', 'ctor'); 2.824 + this.byteReader = reader; 2.825 + this.charWidth = 2; 2.826 + this.bomState = 0; 2.827 + this._version = version; 2.828 + this._typename = typename + ".UTF16"; 2.829 + return this; 2.830 + }; 2.831 + 2.832 + _utf16.prototype.readChar = function() { 2.833 + var b1 = this.byteReader.readByte(); 2.834 + if (b1 < 0) return null; 2.835 + var b2 = this.byteReader.readByte(); 2.836 + if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar'); 2.837 + 2.838 + if ((this.bomState === 0) && ((b1 + b2) == 509)) { 2.839 + this.bomState = (b2 == 254) ? 1 : 2; 2.840 + 2.841 + b1 = this.byteReader.readByte(); 2.842 + if (b1 < 0) return null; 2.843 + b2 = this.byteReader.readByte(); 2.844 + if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar'); 2.845 + } 2.846 + else { 2.847 + this.bomState = 1; 2.848 + } 2.849 + return this.parseChar(b1, b2); 2.850 + }; 2.851 + 2.852 + _utf16.prototype.parseChar = function(b1, b2) { 2.853 + return String.fromCharCode( this.bomState == 1 ? (b2 << 8 | b1) : (b1 << 8 | b2) ); 2.854 + } 2.855 + 2.856 + /* RFC 3629 */ 2.857 + var _utf8 = function(reader) { 2.858 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF8', 'ctor'); 2.859 + this.byteReader = reader; 2.860 + this.charWidth = null; 2.861 + this.waitBom = true; 2.862 + this.strict = false; 2.863 + this.pendingChar = null; 2.864 + this._version = version; 2.865 + this._typename = typename + ".UTF8"; 2.866 + return this; 2.867 + }; 2.868 + 2.869 + _utf8.prototype.readChar = function() { 2.870 + var ch = null; 2.871 + do { 2.872 + if (this.pendingChar !== null) { 2.873 + ch = this.pendingChar; 2.874 + this.pendingChar = null; 2.875 + } 2.876 + else { 2.877 + var b1 = this.byteReader.readByte(); 2.878 + if (b1 === null) return null; 2.879 + 2.880 + if ((b1 & 0x80) === 0) ch = String.fromCharCode(b1); 2.881 + else { 2.882 + var currentPrefix = 0xC0; 2.883 + var ttlBytes = 0; 2.884 + do { 2.885 + var mask = currentPrefix >> 1 | 0x80; 2.886 + if((b1 & mask) == currentPrefix) break; 2.887 + currentPrefix = currentPrefix >> 1 | 0x80; 2.888 + } while(++ttlBytes < 5); 2.889 + 2.890 + if (ttlBytes > 0) { 2.891 + var code; 2.892 + if (ttlBytes === 1) code = (b1 & 0x1F) << 6 | (this.byteReader.readByte() & 0x3F); 2.893 + else { 2.894 + code = code << 6*ttlBytes 2.895 + var bytes = this.byteReader.read(ttlBytes); 2.896 + for (var i = 0; i > ttlBytes; i++) { 2.897 + var bi = bytes[i]; 2.898 + if ((bi & 0xC0) != 0x80) this._throwError('Invalid sequence character', null, 'readChar'); 2.899 + code = (code << 6) | (bi & 0x3F); 2.900 + } 2.901 + } 2.902 + 2.903 + if (code <= 0xFFFF) { 2.904 + ch = (code == 0xFEFF && this.waitBom) ? null : String.fromCharCode(code); 2.905 + } 2.906 + else { 2.907 + var v = code - 0x10000; 2.908 + var w1 = 0xD800 | ((v >> 10) & 0x3FF); 2.909 + var w2 = 0xDC00 | (v & 0x3FF); 2.910 + this.pendingChar = String.fromCharCode(w2); 2.911 + ch = String.fromCharCode(w1); 2.912 + } 2.913 + } 2.914 + else { 2.915 + // a byte higher than 0x80. 2.916 + if (this.strict) this._throwError('Invalid character', null, 'readChar'); 2.917 + // fall back to "super ascii" (eg IBM-437) 2.918 + else ch = String.fromCharCode(b1); 2.919 + } 2.920 + } 2.921 + } 2.922 + this.waitBom = false; 2.923 + } while(ch === null); 2.924 + return ch; 2.925 + }; 2.926 + 2.927 + JSIO.TextDecoder = { 2.928 + Default : _ansi, 2.929 + ANSI : _ansi, 2.930 + UTF16 : _utf16, 2.931 + UTF8 : _utf8 2.932 + }; 2.933 + 2.934 +})(); 2.935 + 2.936 + 2.937 +/// JSIO.TextDecoder.js ends 2.938 + 2.939 +// JSIO.TextReader.js 2.940 +// ------------------------------------------------------------------ 2.941 +// 2.942 +// A reader class that decodes text as it reads. 2.943 +// 2.944 +// ======================================================= 2.945 +// 2.946 +// Methods: 2.947 +// readChar() = read 1 char 2.948 +// read(n) = read n chars 2.949 +// readLine() = read one line of data (to \n) 2.950 +// unreadChar(ch) = unread one char 2.951 +// readToEnd() = read all data in the reader; 2.952 +// return a string. 2.953 +// beginReadToEnd(cb) = asynchronously read all data. 2.954 +// 2.955 +// ======================================================= 2.956 +// 2.957 +// Derived in part from work by notmasteryet. 2.958 +// http://www.codeproject.com/KB/scripting/Javascript_binaryenc.aspx 2.959 +// 2.960 +// Copyleft (c) 2008, notmasteryet via an MIT-style license 2.961 +// Copyleft (c) 2010, Dino Chiesa via MS-PL 2.962 +// Copyleft (c) 2012, Brendan Byrd via GPL 2.963 +// 2.964 +// This work is licensed under the GPLv3. 2.965 + 2.966 + 2.967 +(function(){ 2.968 + var version = "2.0 2012Feb"; 2.969 + var typename = "JSIO.TextReader"; 2.970 + 2.971 + if (typeof JSIO._ByteReaderBase !== "function") 2.972 + JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); 2.973 + 2.974 + var tr = function(textDecoder) { 2.975 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor'); 2.976 + this.decoder = textDecoder; 2.977 + this._version = version; 2.978 + this._typename = typename; 2.979 + this.unreads = []; 2.980 + }; 2.981 + 2.982 + // read one char 2.983 + tr.prototype.readChar = function() { 2.984 + return (this.unreads.length > 0) ? this.unreads.pop() : this.decoder.readChar(); 2.985 + }; 2.986 + 2.987 + // read a length of data 2.988 + tr.prototype.read = function(n) { 2.989 + // ANSI makes this easy 2.990 + if (this.decoder.charWidth === 1) return JSIO.massApply(String.fromCharCode, new String, this.decoder.byteReader.read(n), true); 2.991 + 2.992 + var s = ""; 2.993 + for (vari=0; i<n; i++) { 2.994 + var ch = this.readChar(); 2.995 + if (ch !== null) s+= ch; 2.996 + else break; 2.997 + } 2.998 + return s; 2.999 + }; 2.1000 + 2.1001 + tr.prototype.unreadChar = function(ch) { 2.1002 + this.unreads.push(ch); 2.1003 + }; 2.1004 + 2.1005 + tr.prototype.readToEnd = function() { 2.1006 + // ANSI makes this easy 2.1007 + if (this.decoder.charWidth === 1) return JSIO.massApply(String.fromCharCode, new String, this.decoder.byteReader.readToEnd(n), true); 2.1008 + 2.1009 + var slarge = ""; 2.1010 + var s = ""; 2.1011 + var c = 0; 2.1012 + var ch = this.readChar(); 2.1013 + while(ch !== null) { 2.1014 + s += ch; 2.1015 + c++; 2.1016 + if(c >= 32768) { 2.1017 + slarge += s; 2.1018 + s = ""; 2.1019 + c = 0; 2.1020 + } 2.1021 + ch = this.readChar(); 2.1022 + } 2.1023 + return slarge + s; 2.1024 + }; 2.1025 + 2.1026 + tr.prototype.beginReadToEnd = function(callback) { 2.1027 + // ANSI makes this easy 2.1028 + if (this.decoder.charWidth === 1) { 2.1029 + this.decoder.byteReader.beginReadToEnd(function (bytes) { 2.1030 + callback( JSIO.massApply(String.fromCharCode, new String, bytes, true) ); 2.1031 + }); 2.1032 + return null; 2.1033 + } 2.1034 + 2.1035 + var slarge = ""; 2.1036 + var s = ""; 2.1037 + var txtrdr = this; 2.1038 + 2.1039 + var readBatchAsync = function() { 2.1040 + var c = 0; 2.1041 + var ch = txtrdr.readChar(); 2.1042 + while(ch !== null) { 2.1043 + s += ch;c++; 2.1044 + if(c >= 32768) { 2.1045 + slarge += s; 2.1046 + s = ""; 2.1047 + break; 2.1048 + } 2.1049 + ch = txtrdr.readChar(); 2.1050 + } 2.1051 + if (ch!==null){ 2.1052 + setTimeout(readBatchAsync, 1); 2.1053 + } 2.1054 + else { 2.1055 + callback(slarge+s); 2.1056 + } 2.1057 + }; 2.1058 + 2.1059 + // kickoff 2.1060 + setTimeout(readBatchAsync, 1); // always async, in ALL situations 2.1061 + return null; 2.1062 + }; 2.1063 + 2.1064 + tr.prototype.readLine = function() { 2.1065 + var s = ""; 2.1066 + var ch = this.readChar(); 2.1067 + if (ch === null) return null; 2.1068 + 2.1069 + while(ch != "\r" && ch != "\n") { 2.1070 + s += ch; 2.1071 + ch = this.readChar(); 2.1072 + if (ch === null) return s; 2.1073 + } 2.1074 + if(ch == "\r") { 2.1075 + ch = this.readChar(); 2.1076 + if(ch !== null && ch != "\n"){ 2.1077 + this.unreadChar(ch); 2.1078 + } 2.1079 + } 2.1080 + return s; 2.1081 + }; 2.1082 + 2.1083 + JSIO.TextReader = tr; 2.1084 + 2.1085 +})(); 2.1086 + 2.1087 + 2.1088 +/// JSIO.TextReader.js ends 2.1089 + 2.1090 +// JSIO.Crc32.js 2.1091 +// 2.1092 +// Part of the JSIO library. This adds an CRC32-calculating 2.1093 +// ByteReader to JSIO. 2.1094 +// 2.1095 +// ======================================================= 2.1096 +// 2.1097 +// A ByteReader exposes an interface with these functions: 2.1098 +// 2.1099 +// readByte() 2.1100 +// must return null when EOF is reached. 2.1101 +// 2.1102 +// readToEnd() 2.1103 +// returns an array of all bytes read, to EOF 2.1104 +// 2.1105 +// beginReadToEnd(callback) 2.1106 +// async version of the above 2.1107 +// 2.1108 +// readBytes(n) 2.1109 +// returns an array of all n bytes read from the source 2.1110 +// 2.1111 +// beginReadBytes(n, callback) 2.1112 +// async version of the above 2.1113 +// 2.1114 +// ======================================================= 2.1115 +// 2.1116 +// Copyleft (c) 2010, Dino Chiesa via MS-PL 2.1117 +// Copyleft (c) 2012, Brendan Byrd via GPL 2.1118 +// 2.1119 +// This work is licensed under the GPLv3. 2.1120 + 2.1121 +(function(){ 2.1122 + var version = "2.0 2012Feb"; 2.1123 + var typename = "JSIO.Crc32"; 2.1124 + 2.1125 + if (typeof JSIO._ByteReaderBase !== "function") 2.1126 + JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); 2.1127 + 2.1128 + JSIO.crc32Table = null; 2.1129 + JSIO.crc32Polynomial = 0xEDB88320; 2.1130 + 2.1131 + var crc32TableCalc = function () { 2.1132 + // do this once only, for all instances 2.1133 + if (JSIO.crc32Table) return; 2.1134 + JSIO.crc32Table = new Array(256); 2.1135 + for (var i = 0; i < 256; i++) { 2.1136 + var c=i; 2.1137 + for (var k = 0; k < 8; k++) { 2.1138 + if ((c & 1) == 1) c = JSIO.crc32Polynomial ^ (c>>>1); 2.1139 + else c >>>= 1; 2.1140 + } 2.1141 + JSIO.crc32Table[i] = c; 2.1142 + } 2.1143 + }; 2.1144 + 2.1145 + JSIO.computeCrc32 = function(str) { 2.1146 + crc32TableCalc(); // once 2.1147 + var c = 0xFFFFFFFF; 2.1148 + var sL = str.length; 2.1149 + if (typeof str == "object") { 2.1150 + for (var n1=0; n1<sL; n1++) { 2.1151 + c = JSIO.crc32Table[(c&0xff) ^ str[n1]] ^ (c>>>8); 2.1152 + } 2.1153 + } else { 2.1154 + for (var n2=0; n2<sL; n2++) { 2.1155 + c = JSIO.crc32Table[(c&0xff) ^ str.charCodeAt(n2)] ^ (c>>>8); 2.1156 + } 2.1157 + } 2.1158 + c ^= 0xFFFFFFFF; 2.1159 + if (c < 0) c += 0xFFFFFFFF+1; 2.1160 + return c; 2.1161 + }; 2.1162 + 2.1163 + // ======================================================= 2.1164 + var _crc32 = function() { 2.1165 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor'); 2.1166 + crc32TableCalc(); // once 2.1167 + this._typename = typename; 2.1168 + this._version = version; 2.1169 + this._runningCrc32 = 0xFFFFFFFF; 2.1170 + }; 2.1171 + 2.1172 + _crc32.prototype.slurpByte = function(b) { 2.1173 + var r = this._runningCrc32; 2.1174 + this._runningCrc32 = r>>>8 ^ JSIO.crc32Table[b ^ (r & 0x000000FF)]; 2.1175 + }; 2.1176 + 2.1177 + _crc32.prototype.result = function() { 2.1178 + var c = this._runningCrc32 ^ 0xFFFFFFFF; 2.1179 + if (c < 0) c += 0xFFFFFFFF+1; 2.1180 + return c; 2.1181 + }; 2.1182 + // ======================================================= 2.1183 + 2.1184 + 2.1185 + 2.1186 + var _crc32CalculatingReader = function(reader) { 2.1187 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.Crc32Reader', 'ctor'); 2.1188 + this._byteReader = reader; 2.1189 + this._typename = "JSIO.Crc32Reader"; 2.1190 + this._version = version; 2.1191 + this._crc32 = new JSIO.Crc32(); 2.1192 + }; 2.1193 + 2.1194 + _crc32CalculatingReader.prototype = new JSIO._ByteReaderBase(); 2.1195 + 2.1196 + _crc32CalculatingReader.prototype.readByte = function() { 2.1197 + var b = this._byteReader.readByte(); 2.1198 + if (b !== null) this._crc32.slurpByte(b); 2.1199 + this.position++; 2.1200 + return b; 2.1201 + }; 2.1202 + 2.1203 + _crc32CalculatingReader.prototype.read = function(len) { 2.1204 + if (len === 0) return []; 2.1205 + var bytes = this._byteReader.read(len); 2.1206 + len = bytes.length; 2.1207 + 2.1208 + var tbl = JSIO.crc32Table; 2.1209 + var r = this._crc32._runningCrc32; 2.1210 + var t; 2.1211 + for (var i = 0; i < len; i++) { 2.1212 + t = tbl[ bytes[i] ^ (r & 0x000000FF) ]; 2.1213 + r = (r >>> 8) ^ t; 2.1214 + } 2.1215 + this._crc32._runningCrc32 = r; 2.1216 + 2.1217 + this.position += len; 2.1218 + return bytes; 2.1219 + } 2.1220 + 2.1221 + _crc32CalculatingReader.prototype.crc32 = function() { 2.1222 + return this._crc32.result(); 2.1223 + }; 2.1224 + 2.1225 + JSIO.Crc32 = _crc32; 2.1226 + JSIO.Crc32Reader = _crc32CalculatingReader; 2.1227 + 2.1228 +})(); 2.1229 + 2.1230 +/// JSIO.CRC32.js ends 2.1231 +// JSIO.InflatingReader.js 2.1232 +// ------------------------------------------------------------------ 2.1233 +// 2.1234 +// Part of the JSIO library. This adds an Inflating ByteReader to 2.1235 +// JSIO. 2.1236 +// 2.1237 +// ======================================================= 2.1238 +// 2.1239 +// A ByteReader exposes an interface with these functions: 2.1240 +// 2.1241 +// readByte() 2.1242 +// must return null when EOF is reached. 2.1243 +// 2.1244 +// readToEnd() 2.1245 +// returns an array of all bytes read, to EOF 2.1246 +// 2.1247 +// beginReadToEnd(callback) 2.1248 +// async version of the above 2.1249 +// 2.1250 +// readBytes(n) 2.1251 +// returns an array of all n bytes read from the source 2.1252 +// 2.1253 +// beginReadBytes(n, callback) 2.1254 +// async version of the above 2.1255 +// 2.1256 +// ======================================================= 2.1257 +// 2.1258 +// Derived in part from work by notmasteryet. 2.1259 +// http://www.codeproject.com/KB/scripting/Javascript_binaryenc.aspx 2.1260 +// 2.1261 +// Copyleft (c) 2008, notmasteryet via an MIT-style license 2.1262 +// Copyleft (c) 2010, Dino Chiesa via MS-PL 2.1263 +// Copyleft (c) 2012, Brendan Byrd via GPL 2.1264 +// 2.1265 +// This work is licensed under the GPLv3. 2.1266 + 2.1267 + 2.1268 +(function(){ 2.1269 + var version = "2.0 2012Feb"; 2.1270 + var typename = "JSIO.InflatingReader"; 2.1271 + 2.1272 + if (typeof JSIO._ByteReaderBase !== "function") 2.1273 + JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); 2.1274 + 2.1275 + // ======================================================= 2.1276 + // _InternalBitReader is used internally in the InflatingReader class. 2.1277 + // 2.1278 + 2.1279 + JSIO.bitShiftTable = null; 2.1280 + 2.1281 + var bitShiftTableCalc = function () { 2.1282 + // do this once only, for all instances 2.1283 + if (JSIO.bitShiftTable) return; 2.1284 + 2.1285 + var bits = 8; 2.1286 + JSIO.bitShiftTable = { 2.1287 + LSB: new Array(bits), 2.1288 + MSB: new Array(bits) 2.1289 + } 2.1290 + for (var bp = 0; bp < bits; bp++) { 2.1291 + var lim = bits - bp; 2.1292 + JSIO.bitShiftTable.LSB[bp] = new Array(lim); 2.1293 + JSIO.bitShiftTable.MSB[bp] = new Array(lim); 2.1294 + 2.1295 + var maskLSB = 1 << bp; 2.1296 + var maskMSB = 1 << lim-1; 2.1297 + for (var len = 1; len <= lim; len++) { 2.1298 + JSIO.bitShiftTable.LSB[bp][len-1] = maskLSB; 2.1299 + JSIO.bitShiftTable.MSB[bp][len-1] = maskMSB; 2.1300 + maskLSB |= 1 << bp+len; 2.1301 + maskMSB |= 1 << lim-len-1; 2.1302 + } 2.1303 + } 2.1304 + }; 2.1305 + 2.1306 + var _InternalBitReader = function(reader) { 2.1307 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename + '._InternalBitReader.ctor'); 2.1308 + this.bitsLength = 0; 2.1309 + this.bits = 0; 2.1310 + this.byteReader = reader; 2.1311 + this._typeName = typename + "._InternalBitReader"; 2.1312 + this._version = version; 2.1313 + bitShiftTableCalc(); 2.1314 + }; 2.1315 + 2.1316 + _InternalBitReader.prototype._throwError = JSIO.throwError; 2.1317 + 2.1318 + _InternalBitReader.prototype.readBit = function() { 2.1319 + if (this.bitsLength === 0) { 2.1320 + var nextByte = this.byteReader.readByte(); 2.1321 + if (nextByte === null) this._throwError('Unexpected end of stream', null, 'readBit'); 2.1322 + this.bits = nextByte; 2.1323 + this.bitsLength = 8; 2.1324 + } 2.1325 + 2.1326 + var bit = (this.bits & 1 << 8-this.bitsLength) !== 0; 2.1327 + this.bitsLength--; 2.1328 + return bit; 2.1329 + }; 2.1330 + 2.1331 + _InternalBitReader.prototype.align = function() { this.bitsLength = 0; }; 2.1332 + 2.1333 + _InternalBitReader.prototype.readBits = function(len, type) { 2.1334 + var data = 0; 2.1335 + type = type || 'LSB'; 2.1336 + var tbl = JSIO.bitShiftTable[type]; 2.1337 + var dl = 0; 2.1338 + while (len > 0) { 2.1339 + if (this.bitsLength === 0) { 2.1340 + var nextByte = this.byteReader.readByte(); 2.1341 + if (nextByte === null) this._throwError('Unexpected end of stream', null, 'read'+type); 2.1342 + this.bits = nextByte; 2.1343 + this.bitsLength = 8; 2.1344 + } 2.1345 + 2.1346 + var maskLen = (this.bitsLength >= len) ? len : this.bitsLength; 2.1347 + var bitsPos = 8-this.bitsLength; 2.1348 + data |= (this.bits & tbl[bitsPos][maskLen-1]) >>> bitsPos << dl; 2.1349 + 2.1350 + dl += maskLen; 2.1351 + len -= maskLen; 2.1352 + this.bitsLength -= maskLen; 2.1353 + }; 2.1354 + return data; 2.1355 + }; 2.1356 + 2.1357 + _InternalBitReader.prototype.readLSB = function(len) { return this.readBits(len, 'LSB'); } 2.1358 + _InternalBitReader.prototype.readMSB = function(len) { return this.readBits(len, 'MSB'); } 2.1359 + 2.1360 + // 2.1361 + // ======================================================= 2.1362 + 2.1363 + 2.1364 + /* inflating ByteReader - RFC 1951 */ 2.1365 + var _inflatingReader = function(reader) { 2.1366 + if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor'); 2.1367 + this._byteReader = reader; 2.1368 + this._bitReader = new _InternalBitReader(reader); 2.1369 + this._buffer = []; 2.1370 + this._bufferPosition = 0; 2.1371 + this._state = 0; 2.1372 + this._blockFinal = false; 2.1373 + this._typeName = typename; 2.1374 + this._version = version; 2.1375 + return this; 2.1376 + }; 2.1377 + 2.1378 + 2.1379 + // shared fns and variables 2.1380 + 2.1381 + var staticCodes = null; 2.1382 + var staticDistances = null; 2.1383 + 2.1384 + var clenMap = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; 2.1385 + 2.1386 + var buildCodes = function(lengths){ 2.1387 + var i=0; 2.1388 + var codes = new Array(lengths.length); 2.1389 + var maxBits = lengths[0]; 2.1390 + for (i=1; i<lengths.length; i++) { 2.1391 + if (maxBits < lengths[i]) maxBits = lengths[i]; 2.1392 + } 2.1393 + 2.1394 + var bitLengthsCount = new Array(maxBits + 1); 2.1395 + for (i=0; i<=maxBits; i++) bitLengthsCount[i]=0; 2.1396 + 2.1397 + for (i=0; i<lengths.length; i++) { 2.1398 + ++bitLengthsCount[lengths[i]]; 2.1399 + } 2.1400 + 2.1401 + var nextCode = new Array(maxBits + 1); 2.1402 + var code = 0; 2.1403 + bitLengthsCount[0] = 0; 2.1404 + for (var bits=1; bits<=maxBits; bits++) { 2.1405 + code = (code + bitLengthsCount[bits - 1]) << 1; 2.1406 + nextCode[bits] = code; 2.1407 + } 2.1408 + 2.1409 + for (i=0; i<codes.length; i++) { 2.1410 + var len = lengths[i]; 2.1411 + if (len !== 0) { 2.1412 + codes[i] = nextCode[len]; 2.1413 + nextCode[len]++; 2.1414 + } 2.1415 + } 2.1416 + return codes; 2.1417 + }; 2.1418 + 2.1419 + var buildTree = function(codes, lengths){ 2.1420 + var nonEmptyCodes = []; 2.1421 + for(var i=0; i<codes.length; ++i) { 2.1422 + if(lengths[i] > 0) { 2.1423 + var code = {}; 2.1424 + code.bits = codes[i]; 2.1425 + code.length = lengths[i]; 2.1426 + code.index = i; 2.1427 + nonEmptyCodes.push(code); 2.1428 + } 2.1429 + } 2.1430 + return buildTreeBranch(nonEmptyCodes, 0, 0); 2.1431 + }; 2.1432 + 2.1433 + 2.1434 + var buildTreeBranch = function(codes, prefix, prefixLength){ 2.1435 + if (codes.length === 0) return null; 2.1436 + 2.1437 + var zeros = []; 2.1438 + var ones = []; 2.1439 + var branch = {}; 2.1440 + branch.isLeaf = false; 2.1441 + for(var i=0; i<codes.length; ++i) { 2.1442 + if (codes[i].length == prefixLength && codes[i].bits == prefix) { 2.1443 + branch.isLeaf = true; 2.1444 + branch.index = codes[i].index; 2.1445 + break; 2.1446 + } 2.1447 + else { 2.1448 + var nextBit = ((codes[i].bits >> (codes[i].length - prefixLength - 1)) & 1) > 0; 2.1449 + if (nextBit) ones.push(codes[i]); 2.1450 + else zeros.push(codes[i]); 2.1451 + } 2.1452 + } 2.1453 + if(!branch.isLeaf) { 2.1454 + branch.zero = buildTreeBranch(zeros, (prefix << 1), prefixLength + 1); 2.1455 + branch.one = buildTreeBranch(ones, (prefix << 1) | 1, prefixLength + 1); 2.1456 + } 2.1457 + return branch; 2.1458 + }; 2.1459 + 2.1460 + 2.1461 + var encodedLengthStart = [3,4,5,6,7,8,9,10, 2.1462 + 11,13,15,17,19,23,27,31,35,43,51,59,67,83,99, 2.1463 + 115,131,163,195,227,258]; 2.1464 + 2.1465 + var encodedLengthAdditionalBits = [0,0,0,0,0,0,0,0, 2.1466 + 1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]; 2.1467 + 2.1468 + var encodedDistanceStart = [1,2,3,4, 5,7,9, 13,17,25, 33,49,65, 2.1469 + 97,129,193,257,385,513,769,1025,1537,2049, 2.1470 + 3073,4097,6145,8193,12289,16385,24577]; 2.1471 + 2.1472 + var encodedDistanceAdditionalBits = [0,0,0,0,1,1,2,2,3,3,4,4, 2.1473 + 5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]; 2.1474 + 2.1475 + 2.1476 + var readDynamicTrees = function(bitReader){ 2.1477 + var hlit = bitReader.readLSB(5) + 257; 2.1478 + var hdist = bitReader.readLSB(5) + 1; 2.1479 + var hclen = bitReader.readLSB(4) + 4; 2.1480 + var clen = new Array(19); 2.1481 + var i; 2.1482 + for (i=0; i<clen.length; i++) { clen[i] = 0; } 2.1483 + for (i=0; i<hclen; i++) { clen[clenMap[i]] = bitReader.readLSB(3); } 2.1484 + 2.1485 + var clenCodes = buildCodes(clen); 2.1486 + var clenTree = buildTree(clenCodes, clen); 2.1487 + 2.1488 + var lengthsSequence = []; 2.1489 + while(lengthsSequence.length < hlit + hdist) { 2.1490 + var p = clenTree; 2.1491 + while(!p.isLeaf) { p = bitReader.readBit() ? p.one : p.zero; } 2.1492 + 2.1493 + var code = p.index; 2.1494 + if (code <= 15) lengthsSequence.push(code); 2.1495 + else if (code == 16) { 2.1496 + // TODO: replace with faster repeat algorythm 2.1497 + var repeat = bitReader.readLSB(2) + 3; 2.1498 + for(var q=0; q<repeat; ++q){ 2.1499 + lengthsSequence.push(lengthsSequence[lengthsSequence.length - 1]); 2.1500 + } 2.1501 + } 2.1502 + else if (code == 17) { 2.1503 + var repeat1 = bitReader.readLSB(3) + 3; 2.1504 + for(var q1=0; q1<repeat1; ++q1) { 2.1505 + lengthsSequence.push(0); 2.1506 + } 2.1507 + } 2.1508 + else if (code == 18) { 2.1509 + var repeat2 = bitReader.readLSB(7) + 11; 2.1510 + for(var q2=0; q2<repeat2; ++q2){ 2.1511 + lengthsSequence.push(0); 2.1512 + } 2.1513 + } 2.1514 + } 2.1515 + 2.1516 + var codesLengths = lengthsSequence.slice(0, hlit); 2.1517 + var codes = buildCodes(codesLengths); 2.1518 + var distancesLengths = lengthsSequence.slice(hlit, hlit + hdist); 2.1519 + var distances = buildCodes(distancesLengths); 2.1520 + 2.1521 + return { 2.1522 + codesTree : buildTree(codes, codesLengths), 2.1523 + distancesTree : buildTree(distances, distancesLengths) 2.1524 + }; 2.1525 + }; 2.1526 + 2.1527 + 2.1528 + _inflatingReader.prototype = new JSIO._ByteReaderBase(); 2.1529 + 2.1530 + 2.1531 + // internal instance fns 2.1532 + _inflatingReader.prototype._decodeItem = function() { 2.1533 + if (this._state == 2) return null; // end-of-blocks 2.1534 + 2.1535 + var item; 2.1536 + if(this._state === 0) { 2.1537 + this._blockFinal = this._bitReader.readBit(); 2.1538 + var blockType = this._bitReader.readLSB(2); 2.1539 + switch(blockType) { 2.1540 + case 0: 2.1541 + this._bitReader.align(); 2.1542 + var len = this._bitReader.readLSB(16); // low-byte first, as opposed to readNumber's HBF 2.1543 + var nlen = this._bitReader.readLSB(16); 2.1544 + if ((len & ~nlen) != len) this._throwError('Invalid block type 0 length', null, '_decodeItem'); 2.1545 + 2.1546 + item = {}; 2.1547 + item.itemType = 0; 2.1548 + item.array = this._bitReader.byteReader.read(len); 2.1549 + if (item.array.length < len) this._throwError('Incomplete block', null, '_decodeItem'); 2.1550 + if (this._blockFinal) this._state = 2; 2.1551 + return item; 2.1552 + case 1: 2.1553 + this._codesTree = staticCodes; 2.1554 + this._distancesTree = staticDistances; 2.1555 + this._state = 1; 2.1556 + break; 2.1557 + case 2: 2.1558 + var dTrees = readDynamicTrees(this._bitReader); 2.1559 + this._codesTree = dTrees.codesTree; 2.1560 + this._distancesTree = dTrees.distancesTree; 2.1561 + this._state = 1; 2.1562 + break; 2.1563 + default: 2.1564 + this._throwError('Invalid block type ('+ blockType +')', null, '_decodeItem'); 2.1565 + } 2.1566 + } 2.1567 + 2.1568 + item = {}; 2.1569 + 2.1570 + var p = this._codesTree; 2.1571 + while (!p.isLeaf) { p = this._bitReader.readBit() ? p.one : p.zero; } 2.1572 + if(p.index < 256) { 2.1573 + item.itemType = 2; 2.1574 + item.symbol = p.index; 2.1575 + } else if(p.index > 256) { 2.1576 + var lengthCode = p.index; 2.1577 + if(lengthCode > 285) this._throwError('Invalid length code', null, '_decodeItem'); 2.1578 + 2.1579 + var length = encodedLengthStart[lengthCode - 257]; 2.1580 + if(encodedLengthAdditionalBits[lengthCode - 257] > 0) { 2.1581 + length += this._bitReader.readLSB(encodedLengthAdditionalBits[lengthCode - 257]); 2.1582 + } 2.1583 + 2.1584 + p = this._distancesTree; 2.1585 + while (!p.isLeaf) { p = this._bitReader.readBit() ? p.one : p.zero; } 2.1586 + 2.1587 + var distanceCode = p.index; 2.1588 + var distance = encodedDistanceStart[distanceCode]; 2.1589 + if (encodedDistanceAdditionalBits[distanceCode] > 0) 2.1590 + distance += this._bitReader.readLSB(encodedDistanceAdditionalBits[distanceCode]); 2.1591 + 2.1592 + item.itemType = 3; 2.1593 + item.distance = distance; 2.1594 + item.length = length; 2.1595 + } else { 2.1596 + item.itemType = 1; 2.1597 + this._state = this._blockFinal ? 2 : 0; // EOB 2.1598 + } 2.1599 + return item; 2.1600 + }; 2.1601 + 2.1602 + 2.1603 + 2.1604 + // public instance functions 2.1605 + 2.1606 + _inflatingReader.prototype.readByte = function() { 2.1607 + var byte = this.read(1)[0]; 2.1608 + return (byte === null || byte === undefined) ? null : byte; 2.1609 + }; 2.1610 + 2.1611 + _inflatingReader.prototype.read = function(len) { 2.1612 + var b = this._buffer; // (since we use this so much...) 2.1613 + 2.1614 + // Keep reading until we get to the right length 2.1615 + while (this._bufferPosition+len > b.length) { 2.1616 + var item = this._decodeItem(); 2.1617 + if (item === null) { // EOF 2.1618 + len = b.length - this._bufferPosition; 2.1619 + break; 2.1620 + } 2.1621 + switch(item.itemType) { 2.1622 + case 0: 2.1623 + JSIO.massApply(b.push, b, item.array); 2.1624 + break; 2.1625 + case 2: 2.1626 + b.push(item.symbol); 2.1627 + break; 2.1628 + case 3: 2.1629 + var j = b.length - item.distance; 2.1630 + if (item.distance >= item.length) 2.1631 + JSIO.massApply(b.push, b, b.slice(j, j+item.length)); 2.1632 + // sometimes DEFLATE tries some trickery with "look-ahead" compression 2.1633 + else { 2.1634 + // this is basically just a repetition of the same string, plus some possible cutoff 2.1635 + var count = parseInt(item.length / item.distance); 2.1636 + var repArr = b.slice(j); 2.1637 + // http://stackoverflow.com/questions/202605/repeat-string-javascript/5450113#5450113 2.1638 + while (count > 0) { 2.1639 + if (count & 1) JSIO.massApply( b.push, b, repArr); 2.1640 + if (count >>= 1) JSIO.massApply(repArr.push, repArr, repArr); 2.1641 + } 2.1642 + // add any remaining cutoff 2.1643 + var r; 2.1644 + if (r = item.length % item.distance) 2.1645 + JSIO.massApply(b.push, b, b.slice(j, j+r)); 2.1646 + } 2.1647 + break; 2.1648 + } 2.1649 + } 2.1650 + var bytes = b.slice(this._bufferPosition, this._bufferPosition+len); 2.1651 + this._bufferPosition += len; 2.1652 + this.position += len; 2.1653 + 2.1654 + if (this._bufferPosition > 0xC000) { 2.1655 + var shift = b.length - 0x8000; 2.1656 + if (shift > this._bufferPosition) shift = this._bufferPosition; 2.1657 + b.splice(0, shift); 2.1658 + this._bufferPosition -= shift; 2.1659 + } 2.1660 + 2.1661 + return bytes; 2.1662 + }; 2.1663 + 2.1664 + // initialization routine - once per type 2.1665 + (function(){ 2.1666 + 2.1667 + var codes = new Array(288); 2.1668 + var codesLengths = new Array(288); 2.1669 + var i=0; 2.1670 + for ( i = 0; i <= 143; i++) { 2.1671 + codes[i] = 0x0030 + i; 2.1672 + codesLengths[i] = 8; 2.1673 + } 2.1674 + for ( i = 144; i <= 255; i++) { 2.1675 + codes[i] = 0x0190 + i - 144; 2.1676 + codesLengths[i] = 9; 2.1677 + } 2.1678 + for ( i = 256; i <= 279; i++) { 2.1679 + codes[i] = 0x0000 + i - 256; 2.1680 + codesLengths[i] = 7; 2.1681 + } 2.1682 + for ( i = 280; i <= 287; i++) { 2.1683 + codes[i] = 0x00C0 + i - 280; 2.1684 + codesLengths[i] = 8; 2.1685 + } 2.1686 + staticCodes = buildTree(codes, codesLengths); 2.1687 + 2.1688 + var distances = new Array(32); 2.1689 + var distancesLengths = new Array(32); 2.1690 + for ( i = 0; i <= 31; i++) { 2.1691 + distances[i] = i; 2.1692 + distancesLengths[i] = 5; 2.1693 + } 2.1694 + staticDistances = buildTree(distances, distancesLengths); 2.1695 + })(); 2.1696 + 2.1697 + 2.1698 + JSIO.InflatingReader = _inflatingReader; 2.1699 + 2.1700 +})(); 2.1701 + 2.1702 + 2.1703 +/// JSIO.InflatingReader.js ends 2.1704 + 2.1705 +// Zipfile.js 2.1706 +// ------------------------------------------------------------------ 2.1707 +// 2.1708 +// A class that reads Zip files. 2.1709 +// Depends on the JSIO library functions. 2.1710 +// 2.1711 +// ======================================================= 2.1712 +// 2.1713 +// Copyleft (c) 2010, Dino Chiesa via MS-PL 2.1714 +// Copyleft (c) 2012, Brendan Byrd via GPL 2.1715 +// 2.1716 +// This work is licensed under the GPLv3. 2.1717 + 2.1718 +(function(){ 2.1719 + var version = "2.0 2012Feb"; 2.1720 + var typename = "Zipfile"; 2.1721 + 2.1722 + if (typeof JSIO.BinaryUrlStream !== "function") JSIO.throwError('This extension requires JSIO.BinaryUrlStream.js v2.0', typename); 2.1723 + if (typeof JSIO.TextDecoder !== "object") JSIO.throwError('This extension requires JSIO.TextDecoder.js v2.0', typename); 2.1724 + if (typeof JSIO.TextReader !== "function") JSIO.throwError('This extension requires JSIO.TextReader.js v2.0', typename); 2.1725 + if (typeof JSIO.Crc32 !== "function") JSIO.throwError('This extension requires JSIO.Crc32.js v2.0', typename); 2.1726 + if (typeof JSIO.InflatingReader !== "function") JSIO.throwError('This extension requires JSIO.InflatingReader.js v2.0', typename); 2.1727 + 2.1728 + // ======================================================= 2.1729 + function ZipEntry(zip) { 2.1730 + this.zipfile = zip; 2.1731 + this._typename = "ZipEntry"; 2.1732 + this._version = version; 2.1733 + this._crcCalculator = null; 2.1734 + } 2.1735 + 2.1736 + ZipEntry.prototype._throwError = JSIO.throwError; 2.1737 + 2.1738 + // return byte array or string 2.1739 + ZipEntry.prototype.extract = function(callback, asString) { 2.1740 + this.contentType = JSIO.guessFileType(this.name); 2.1741 + asString = asString || ( this.contentType == JSIO.FileType.Text || 2.1742 + this.contentType == JSIO.FileType.XML); 2.1743 + var thisEntry = this; 2.1744 + 2.1745 + if (this.compressionMethod !== 0 && this.compressionMethod != 8) 2.1746 + this._throwError('Unsupported compression method: ' + this.compressionMethod, null, 'extract'); 2.1747 + 2.1748 + var reader = (asString) ? this.openTextReader(thisEntry.utf8 ? JSIO.TextDecoder.UTF8 : JSIO.TextDecoder.ANSI) : this.openBinaryReader(); 2.1749 + 2.1750 + // diagnostic purpose only; tag the reader with the entry name 2.1751 + reader.zipEntryName = thisEntry.name; 2.1752 + 2.1753 + if (typeof callback != "function") { 2.1754 + // synchronous 2.1755 + var result = reader.readToEnd(); 2.1756 + this.verifyCrc32(); 2.1757 + return result; 2.1758 + } 2.1759 + 2.1760 + // asynchronous 2.1761 + reader.beginReadToEnd(function(result){ 2.1762 + try { 2.1763 + thisEntry.verifyCrc32(); 2.1764 + callback(thisEntry, result); 2.1765 + } 2.1766 + catch (exc1) { 2.1767 + callback(thisEntry, exc1); 2.1768 + } 2.1769 + }); 2.1770 + return null; 2.1771 + }; 2.1772 + 2.1773 + 2.1774 + // open a ByteReader on the entry, which will read binary 2.1775 + // content from the compressed stream. 2.1776 + ZipEntry.prototype.openBinaryReader = function() { 2.1777 + var reader = 2.1778 + new JSIO.StreamSegmentReader(this.zipfile.binaryStream, 2.1779 + this.offset + this.lengthOfHeader, 2.1780 + this.compressedSize); 2.1781 + if (this.compressionMethod === 0) { 2.1782 + this._crcCalculator = new JSIO.Crc32Reader(reader); 2.1783 + } 2.1784 + else { 2.1785 + var inflator = new JSIO.InflatingReader(reader); 2.1786 + this._crcCalculator = new JSIO.Crc32Reader(inflator); 2.1787 + } 2.1788 + // Whether compressed or not, the source ByteReader in each case 2.1789 + // is wrapped in a second ByteReader object that calculates CRC 2.1790 + // as it reads. That way, after all reading is complete, the 2.1791 + // caller can check the calcuated CRC against the expected CRC. 2.1792 + return this._crcCalculator; 2.1793 + }; 2.1794 + 2.1795 + // open a TextReader on the entry, to read text from the 2.1796 + // compressed stream. 2.1797 + ZipEntry.prototype.openTextReader = function(decoderKind) { 2.1798 + var reader = this.openBinaryReader(); 2.1799 + decoderKind = decoderKind || JSIO.TextDecoder.UTF8; 2.1800 + var d = new decoderKind(reader); 2.1801 + var textReader = new JSIO.TextReader(d); 2.1802 + d._parent = textReader; // store a reference, for diagnostic purposes only 2.1803 + return textReader; 2.1804 + }; 2.1805 + 2.1806 + // verify the CRC on the entry. 2.1807 + // call this after all bytes have been read. 2.1808 + ZipEntry.prototype.verifyCrc32 = function() { 2.1809 + var computedCrc = this._crcCalculator.crc32(); 2.1810 + var rc = false; // CRC FAIL 2.1811 + if (this.crc32 != computedCrc) { 2.1812 + var msg = "WARNING: CRC check failed: " + 2.1813 + "entry(" + this.name + ") " + 2.1814 + "computed(" + JSIO.decimalToHexString(computedCrc,8) + ") " + 2.1815 + "expected(" + JSIO.decimalToHexString(this.crc32,8) + ") "; 2.1816 + this.zipfile.status.push(msg); 2.1817 + } else { 2.1818 + rc = true; // OK 2.1819 + if (this.zipfile.verbose>2) { 2.1820 + this.zipfile.status.push("INFO: CRC check ok: 0x" + 2.1821 + JSIO.decimalToHexString(this.crc32,8)); 2.1822 + } 2.1823 + } 2.1824 + return rc; 2.1825 + }; 2.1826 + 2.1827 + 2.1828 + // ctor 2.1829 + ZipFile = function(fileUrl, callback, verbosity) { 2.1830 + if (! (this instanceof arguments.callee) ) JSIO.throwError('You must use new to instantiate this class', typename, 'ctor'); 2.1831 + 2.1832 + this.verbose = verbosity || 0; 2.1833 + this.entries = []; 2.1834 + this.entryNames = []; 2.1835 + this.status = []; 2.1836 + this._version = version; 2.1837 + this._typename = "ZipFile"; 2.1838 + this._throwError = JSIO.throwError; 2.1839 + 2.1840 + var thisZipFile = this; 2.1841 + 2.1842 + // Could use a back-tracking reader for the central directory, but 2.1843 + // there's no point, since all the zip data is held in memory anyway. 2.1844 + 2.1845 + /* function ReadCentralDirectory(){ 2.1846 + var posn = thisZipFile.binaryStream.length - 64; 2.1847 + var maxSeekback = Math.Max(s.Length - 0x4000, 10); 2.1848 + var success = false; 2.1849 + var nTries = 0; 2.1850 + do 2.1851 + { 2.1852 + thisZipFile.binaryStream.Seek(posn, SeekOrigin.Begin); 2.1853 + var bytesRead = thisZipFile.binaryStream.findSignature(thisZipFile.Signatures.EndOfCentralDirectory); 2.1854 + if (bytesRead != -1) 2.1855 + success = true; 2.1856 + else 2.1857 + { 2.1858 + nTries++; 2.1859 + // increasingly larger 2.1860 + posn -= (32 * (nTries + 1) * nTries); 2.1861 + if (posn < 0) posn = 0; // BOF 2.1862 + } 2.1863 + } 2.1864 + while (!success && posn > maxSeekback); 2.1865 + if (!success) { 2.1866 + thisZipFile.status.push("cannot find End of Central Directory"); 2.1867 + return; 2.1868 + } 2.1869 + } */ 2.1870 + 2.1871 + 2.1872 + function DateFromPackedFormat(packed) { 2.1873 + if (packed == 0xFFFF || packed === 0) return new Date(1995, 0, 1, 0,0,0,0); 2.1874 + 2.1875 + var packedTime = packed & 0x0000ffff; 2.1876 + var packedDate = ((packed & 0xffff0000) >> 16); 2.1877 + 2.1878 + var year = 1980 + ((packedDate & 0xFE00) >> 9); 2.1879 + var month = ((packedDate & 0x01E0) >> 5) -1; 2.1880 + var day = packedDate & 0x001F; 2.1881 + 2.1882 + var hour = (packedTime & 0xF800) >> 11; 2.1883 + var minute = (packedTime & 0x07E0) >> 5; 2.1884 + var second = (packedTime & 0x001F) * 2; 2.1885 + 2.1886 + // Validation and error checking. 2.1887 + // This is not foolproof but will catch most errors. 2.1888 + 2.1889 + // I can't believe how many different ways applications 2.1890 + // can mess up a simple date format. 2.1891 + 2.1892 + if (second >= 60) { minute++; second = 0; } 2.1893 + if (minute >= 60) { hour++; minute = 0; } 2.1894 + if (hour >= 24) { day++; hour = 0; } 2.1895 + var success = false; 2.1896 + var d; 2.1897 + try { 2.1898 + d = new Date(year, month, day, hour, minute, second, 0); 2.1899 + success= true; 2.1900 + } 2.1901 + catch (exc1) { 2.1902 + if (year == 1980 && (month === 0 || day === 0)) { 2.1903 + try { 2.1904 + d = new Date(1980, 0, 1, hour, minute, second, 0); 2.1905 + success= true; 2.1906 + } 2.1907 + catch (exc2) { 2.1908 + try { 2.1909 + d = new Date(1980, 0, 1, 0, 0, 0, 0); 2.1910 + success= true; 2.1911 + } 2.1912 + catch (exc3) { } // how could this fail?? 2.1913 + } 2.1914 + } 2.1915 + else { 2.1916 + try { 2.1917 + if (year < 1980) year = 1980; 2.1918 + if (year > 2030) year = 2030; 2.1919 + if (month < 1) month = 1; 2.1920 + if (month > 12) month = 12; 2.1921 + if (day < 1) day = 1; 2.1922 + if (day > 31) day = 31; 2.1923 + if (minute < 0) minute = 0; 2.1924 + if (minute > 59) minute = 59; 2.1925 + if (second < 0) second = 0; 2.1926 + if (second > 59) second = 59; 2.1927 + d = new Date(year, month-1, day, hour, minute, second, 0); 2.1928 + success= true; 2.1929 + } 2.1930 + catch (exc4){} 2.1931 + } 2.1932 + } 2.1933 + if (!success) this._throwError('Bad date/time value in this ZIP file', null, 'DateFromPackedFormat'); 2.1934 + return d; 2.1935 + } 2.1936 + 2.1937 + 2.1938 + function ReadZipEntries () { 2.1939 + // read only once 2.1940 + if (thisZipFile.entryNames.length === 0){ 2.1941 + var e; 2.1942 + while ((e = ReadZipEntry()) !== null) { 2.1943 + thisZipFile.entries.push(e); 2.1944 + thisZipFile.entryNames.push(e.name); 2.1945 + } 2.1946 + } 2.1947 + } 2.1948 + 2.1949 + 2.1950 + function ReadZipEntry () { 2.1951 + var offset = thisZipFile.binaryStream.position; 2.1952 + var sig = thisZipFile.binaryStream.readNumber(4); 2.1953 + if (sig == ZipFile.Signatures.DirEntry) { 2.1954 + // after all entries, comes the central directory 2.1955 + if (thisZipFile.verbose > 0) { 2.1956 + thisZipFile.status.push("INFO: at offset 0x" + 2.1957 + JSIO.decimalToHexString(offset) + 2.1958 + ", found start of Zip Directory."); 2.1959 + } 2.1960 + // all done reading 2.1961 + return null; 2.1962 + } 2.1963 + if (sig != ZipFile.Signatures.Entry) { 2.1964 + thisZipFile.status.push("WARNING: at offset 0x" + 2.1965 + JSIO.decimalToHexString(offset) + 2.1966 + ", found unexpected signature: 0x" + 2.1967 + JSIO.decimalToHexString(sig)); 2.1968 + return null; 2.1969 + } 2.1970 + 2.1971 + var entry = new ZipEntry(thisZipFile); 2.1972 + entry.offset = offset; 2.1973 + entry.versionNeeded = thisZipFile.binaryStream.readNumber(2); 2.1974 + entry.bitField = thisZipFile.binaryStream.readNumber(2); 2.1975 + entry.compressionMethod = thisZipFile.binaryStream.readNumber(2); 2.1976 + var timeBlob = thisZipFile.binaryStream.readNumber(4); 2.1977 + entry.lastModified = DateFromPackedFormat(timeBlob); 2.1978 + entry.crc32 = thisZipFile.binaryStream.readNumber(4); 2.1979 + entry.compressedSize = thisZipFile.binaryStream.readNumber(4); 2.1980 + entry.uncompressedSize = thisZipFile.binaryStream.readNumber(4); 2.1981 + 2.1982 + if ((entry.bitField & 0x01) == 0x01){ 2.1983 + thisZipFile.status.push("This zipfile uses Encryption, which is not supported by ZipFile.js."); 2.1984 + return null; 2.1985 + } 2.1986 + 2.1987 + entry.utf8 = ((entry.bitField & 0x0800) == 0x0800); 2.1988 + 2.1989 + if ((entry.bitField & 0x0008) == 0x0008){ 2.1990 + thisZipFile.status.push("This zipfile uses a bit 3 trailing data descriptor, which is not supported by ZipFile.js."); 2.1991 + return null; 2.1992 + } 2.1993 + 2.1994 + if (entry.compressedSize == 0xFFFFFFFF || 2.1995 + entry.uncompressedSize == 0xFFFFFFFF) { 2.1996 + thisZipFile.status.push("This zipfile uses ZIP64, which is not supported by ZipFile.js"); 2.1997 + return null; 2.1998 + } 2.1999 + 2.2000 + var filenameLength = thisZipFile.binaryStream.readNumber(2); 2.2001 + var extraFieldLength = thisZipFile.binaryStream.readNumber(2); 2.2002 + 2.2003 + thisZipFile.status.push("INFO: filename length= " + filenameLength); 2.2004 + 2.2005 + // we've read 30 bytes of metadata so far 2.2006 + var bytesRead = 30 + filenameLength + extraFieldLength; 2.2007 + 2.2008 + if (entry.utf8) { 2.2009 + thisZipFile.status.push("INFO: before filename, position= 0x" + 2.2010 + JSIO.decimalToHexString( thisZipFile.binaryStream.position )); 2.2011 + var binReader = 2.2012 + new JSIO.StreamSegmentReader(thisZipFile.binaryStream, 2.2013 + thisZipFile.binaryStream.position, 2.2014 + filenameLength); 2.2015 + var utf8Decoder = new JSIO.TextDecoder.UTF8(binReader); 2.2016 + var textReader = new JSIO.TextReader(utf8Decoder); 2.2017 + entry.name = textReader.readToEnd(); 2.2018 + 2.2019 + // advance the filepointer: 2.2020 + thisZipFile.binaryStream.seek(filenameLength, 2.2021 + JSIO.SeekOrigin.Current, 2.2022 + thisZipFile); 2.2023 + 2.2024 + thisZipFile.status.push("INFO: after filename, position= 0x" + 2.2025 + JSIO.decimalToHexString( thisZipFile.binaryStream.position )); 2.2026 + } 2.2027 + else { 2.2028 + entry.name = thisZipFile.binaryStream.readString(filenameLength); 2.2029 + } 2.2030 + 2.2031 + // There are a bunch of things in the "extra" header, thisZipFile we 2.2032 + // could parse, like timestamps and other things. This class 2.2033 + // only identifies and separates them. 2.2034 + 2.2035 + // More info here: http://www.pkware.com/documents/casestudies/APPNOTE.TXT 2.2036 + 2.2037 + var extraPos = 0; 2.2038 + entry.extra = []; 2.2039 + while (extraPos < extraFieldLength) { 2.2040 + var extraBlock = { 2.2041 + type: thisZipFile.binaryStream.readNumber(2), 2.2042 + size: thisZipFile.binaryStream.readNumber(2) 2.2043 + }; 2.2044 + extraBlock.typeDescription = ZipFile.ExtraFieldTypes[extraBlock.type]; 2.2045 + extraBlock.data = thisZipFile.binaryStream.read(extraBlock.size); 2.2046 + entry.extra.push(extraBlock); 2.2047 + extraPos += 4 + extraBlock.size; 2.2048 + } 2.2049 + 2.2050 + if (thisZipFile.verbose > 1) { 2.2051 + thisZipFile.status.push("INFO: at offset 0x" + 2.2052 + JSIO.decimalToHexString(entry.offset) + 2.2053 + ", found entry '" + entry.name + "' fnl(" + 2.2054 + filenameLength + ") efl(" + 2.2055 + extraFieldLength +")"); 2.2056 + } 2.2057 + 2.2058 + if (extraFieldLength > 0) { 2.2059 + if (thisZipFile.verbose > 0) { 2.2060 + thisZipFile.status.push("INFO: entry " + entry.name + " has " + 2.2061 + extraFieldLength + " bytes of " + 2.2062 + "extra metadata (ID'd but ignored)"); 2.2063 + } 2.2064 + } 2.2065 + 2.2066 + entry.lengthOfHeader = bytesRead; 2.2067 + entry.totalEntrySize = entry.lengthOfHeader + entry.compressedSize; 2.2068 + 2.2069 + // seek past the data without reading it. We will read on Extract() 2.2070 + if (thisZipFile.verbose > 1) { 2.2071 + thisZipFile.status.push("INFO: seek 0x" + 2.2072 + JSIO.decimalToHexString(entry.compressedSize) + 2.2073 + " (" + entry.compressedSize + ") bytes"); 2.2074 + } 2.2075 + 2.2076 + thisZipFile.binaryStream.seek(entry.compressedSize, 2.2077 + JSIO.SeekOrigin.Current, 2.2078 + thisZipFile); 2.2079 + 2.2080 + return entry; 2.2081 + } 2.2082 + 2.2083 + 2.2084 + var parseZipFile = function(bfr){ 2.2085 + try { 2.2086 + if (bfr.req.status == 200) { 2.2087 + var sig = thisZipFile.binaryStream.readNumber(4); 2.2088 + if (sig != ZipFile.Signatures.Entry){ 2.2089 + thisZipFile.status.push("WARNING: this file does not appear to be a zip file"); 2.2090 + } else { 2.2091 + thisZipFile.binaryStream.seek(0, JSIO.SeekOrigin.Begin); 2.2092 + ReadZipEntries(); 2.2093 + if (thisZipFile.verbose > 0) { 2.2094 + thisZipFile.status.push("INFO: read " + thisZipFile.entries.length + " entries"); 2.2095 + } 2.2096 + } 2.2097 + } 2.2098 + else { 2.2099 + thisZipFile.status.push("ERROR: the URL could not be read (" + 2.2100 + bfr.req.status + " " + bfr.req.statusText + ")"); 2.2101 + } 2.2102 + callback(thisZipFile); 2.2103 + } 2.2104 + catch (exc1) 2.2105 + { 2.2106 + thisZipFile.status.push("Exception: " + exc1.message); 2.2107 + callback(thisZipFile); 2.2108 + } 2.2109 + }; 2.2110 + 2.2111 + this.binaryStream = new JSIO.BinaryUrlStream(fileUrl, parseZipFile); 2.2112 + 2.2113 + return this; 2.2114 + }; 2.2115 + 2.2116 + 2.2117 + ZipFile.Signatures = { 2.2118 + Entry : 0x04034b50, 2.2119 + EndOfCentralDirectory : 0x06054b50, 2.2120 + DirEntry : 0x02014b50 2.2121 + }; 2.2122 + 2.2123 + ZipFile.Version = version; 2.2124 + 2.2125 + ZipFile.EncryptionAlgorithm = { 2.2126 + None : 0, 2.2127 + PkzipWeak : 1, 2.2128 + WinZipAes : 2 2.2129 + }; 2.2130 + 2.2131 + ZipFile.ExtraFieldTypes = {}; 2.2132 + ZipFile.ExtraFieldTypes[0x0001] = 'Zip64 Extended Info'; 2.2133 + ZipFile.ExtraFieldTypes[0x0007] = 'AV Info'; 2.2134 + ZipFile.ExtraFieldTypes[0x0008] = 'Extended Language Encoding Data (PFS)'; 2.2135 + ZipFile.ExtraFieldTypes[0x0009] = 'OS/2'; 2.2136 + ZipFile.ExtraFieldTypes[0x000a] = 'NTFS '; 2.2137 + ZipFile.ExtraFieldTypes[0x000c] = 'OpenVMS'; 2.2138 + ZipFile.ExtraFieldTypes[0x000d] = 'UNIX'; 2.2139 + ZipFile.ExtraFieldTypes[0x000e] = 'File Stream and Fork Descriptors'; 2.2140 + ZipFile.ExtraFieldTypes[0x000f] = 'Patch Descriptor'; 2.2141 + ZipFile.ExtraFieldTypes[0x0014] = 'PKCS#7 Store for X.509 Certificates'; 2.2142 + ZipFile.ExtraFieldTypes[0x0015] = 'X.509 Certificate ID and Signature (Individual File)'; 2.2143 + ZipFile.ExtraFieldTypes[0x0016] = 'X.509 Certificate ID (Central Directory)'; 2.2144 + ZipFile.ExtraFieldTypes[0x0017] = 'Strong Encryption Header'; 2.2145 + ZipFile.ExtraFieldTypes[0x0018] = 'Record Management Controls'; 2.2146 + ZipFile.ExtraFieldTypes[0x0019] = 'PKCS#7 Encryption Recipient Certificate List'; 2.2147 + ZipFile.ExtraFieldTypes[0x0065] = 'IBM S/390 (Z390), AS/400 (I400) attributes (uncompressed)'; 2.2148 + ZipFile.ExtraFieldTypes[0x0066] = 'IBM S/390 (Z390), AS/400 (I400) attributes (compressed)'; 2.2149 + ZipFile.ExtraFieldTypes[0x4690] = 'POSZIP 4690 (reserved) '; 2.2150 + ZipFile.ExtraFieldTypes[0x07c8] = 'Macintosh'; 2.2151 + ZipFile.ExtraFieldTypes[0x2605] = 'ZipIt Macintosh'; 2.2152 + ZipFile.ExtraFieldTypes[0x2705] = 'ZipIt Macintosh 1.3.5+'; 2.2153 + ZipFile.ExtraFieldTypes[0x2805] = 'ZipIt Macintosh 1.3.5+'; 2.2154 + ZipFile.ExtraFieldTypes[0x334d] = 'Info-ZIP Macintosh'; 2.2155 + ZipFile.ExtraFieldTypes[0x4341] = 'Acorn/SparkFS '; 2.2156 + ZipFile.ExtraFieldTypes[0x4453] = 'Windows NT security descriptor (binary ACL)'; 2.2157 + ZipFile.ExtraFieldTypes[0x4704] = 'VM/CMS'; 2.2158 + ZipFile.ExtraFieldTypes[0x470f] = 'MVS'; 2.2159 + ZipFile.ExtraFieldTypes[0x4b46] = 'FWKCS MD5'; 2.2160 + ZipFile.ExtraFieldTypes[0x4c41] = 'OS/2 access control list (text ACL)'; 2.2161 + ZipFile.ExtraFieldTypes[0x4d49] = 'Info-ZIP OpenVMS'; 2.2162 + ZipFile.ExtraFieldTypes[0x4f4c] = 'Xceed original location extra field'; 2.2163 + ZipFile.ExtraFieldTypes[0x5356] = 'AOS/VS (ACL)'; 2.2164 + ZipFile.ExtraFieldTypes[0x5455] = 'extended timestamp'; 2.2165 + ZipFile.ExtraFieldTypes[0x554e] = 'Xceed unicode extra field'; 2.2166 + ZipFile.ExtraFieldTypes[0x5855] = 'Info-ZIP UNIX (original, also OS/2, NT, etc)'; 2.2167 + ZipFile.ExtraFieldTypes[0x6375] = 'Info-ZIP Unicode Comment Extra Field'; 2.2168 + ZipFile.ExtraFieldTypes[0x6542] = 'BeOS/BeBox'; 2.2169 + ZipFile.ExtraFieldTypes[0x7075] = 'Info-ZIP Unicode Path Extra Field'; 2.2170 + ZipFile.ExtraFieldTypes[0x756e] = 'ASi UNIX'; 2.2171 + ZipFile.ExtraFieldTypes[0x7855] = 'Info-ZIP UNIX (new)'; 2.2172 + ZipFile.ExtraFieldTypes[0xa220] = 'Microsoft Open Packaging Growth Hint'; 2.2173 + ZipFile.ExtraFieldTypes[0xfd4a] = 'SMS/QDOS'; 2.2174 + 2.2175 +})();
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/endpoint/WebContent/js/geoxml3-kmz.js Fri Sep 14 13:03:24 2012 +0300 3.3 @@ -0,0 +1,1889 @@ 3.4 +/** 3.5 + * @fileOverview Renders KML on the Google Maps JavaScript API Version 3 3.6 + * @name GeoXML3 3.7 + * @author Sterling Udell, Larry Ross, Brendan Byrd 3.8 + * @see http://code.google.com/p/geoxml3/ 3.9 + * 3.10 + * geoxml3.js 3.11 + * 3.12 + * Renders KML on the Google Maps JavaScript API Version 3 3.13 + * http://code.google.com/p/geoxml3/ 3.14 + * 3.15 + * Copyright 2010 Sterling Udell, Larry Ross 3.16 + * 3.17 + * Licensed under the Apache License, Version 2.0 (the "License"); 3.18 + * you may not use this file except in compliance with the License. 3.19 + * You may obtain a copy of the License at 3.20 + * 3.21 + * http://www.apache.org/licenses/LICENSE-2.0 3.22 + * 3.23 + * Unless required by applicable law or agreed to in writing, software 3.24 + * distributed under the License is distributed on an "AS IS" BASIS, 3.25 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3.26 + * See the License for the specific language governing permissions and 3.27 + * limitations under the License. 3.28 + * 3.29 + */ 3.30 + 3.31 +if (!String.prototype.trim) { 3.32 +/** 3.33 + * Remove leading and trailing whitespace. 3.34 + * 3.35 + * @augments String 3.36 + * @return {String} 3.37 + */ 3.38 + String.prototype.trim = function () { 3.39 + return this.replace(/^\s+|\s+$/g, ''); 3.40 + }; 3.41 +} 3.42 + 3.43 +/** 3.44 + * @namespace The GeoXML3 namespace. 3.45 + */ 3.46 +geoXML3 = window.geoXML3 || {instances: []}; 3.47 + 3.48 +/** 3.49 + * Constructor for the root KML parser object. 3.50 + * 3.51 + * <p>All top-level objects and functions are declared under a namespace of geoXML3. 3.52 + * The core object is geoXML3.parser; typically, you'll instantiate a one parser 3.53 + * per map.</p> 3.54 + * 3.55 + * @class Main XML parser. 3.56 + * @param {geoXML3.parserOptions} options 3.57 + */ 3.58 +geoXML3.parser = function (options) { 3.59 + // Private variables 3.60 + var parserOptions = new geoXML3.parserOptions(options); 3.61 + var docs = []; // Individual KML documents 3.62 + var docsByUrl = {}; // Same docs as an hash by cleanURL 3.63 + var kmzMetaData = {}; // Extra files from KMZ data 3.64 + var styles = {}; // Global list of styles 3.65 + var lastPlacemark; 3.66 + var parserName; 3.67 + if (!parserOptions.infoWindow && parserOptions.singleInfoWindow) 3.68 + parserOptions.infoWindow = new google.maps.InfoWindow(); 3.69 + 3.70 + var parseKmlString = function (kmlString, docSet) { 3.71 + // Internal values for the set of documents as a whole 3.72 + var internals = { 3.73 + parser: this, 3.74 + docSet: docSet || [], 3.75 + remaining: 1, 3.76 + parseOnly: !(parserOptions.afterParse || parserOptions.processStyles) 3.77 + }; 3.78 + thisDoc = new Object(); 3.79 + thisDoc.internals = internals; 3.80 + internals.docSet.push(thisDoc); 3.81 + render(geoXML3.xmlParse(kmlString),thisDoc); 3.82 + } 3.83 + 3.84 + var parse = function (urls, docSet) { 3.85 + // Process one or more KML documents 3.86 + if (!parserName) { 3.87 + parserName = 'geoXML3.instances[' + (geoXML3.instances.push(this) - 1) + ']'; 3.88 + } 3.89 + 3.90 + if (typeof urls === 'string') { 3.91 + // Single KML document 3.92 + urls = [urls]; 3.93 + } 3.94 + 3.95 + // Internal values for the set of documents as a whole 3.96 + var internals = { 3.97 + parser: this, 3.98 + docSet: docSet || [], 3.99 + remaining: urls.length, 3.100 + parseOnly: !(parserOptions.afterParse || parserOptions.processStyles) 3.101 + }; 3.102 + var thisDoc, j; 3.103 + for (var i = 0; i < urls.length; i++) { 3.104 + var baseUrl = cleanURL(defileURL(location.pathname), urls[i]); 3.105 + if (docsByUrl[baseUrl]) { 3.106 + // Reloading an existing document 3.107 + thisDoc = docsByUrl[baseUrl]; 3.108 + thisDoc.reload = true; 3.109 + } 3.110 + else { 3.111 + thisDoc = new Object(); 3.112 + thisDoc.baseUrl = baseUrl; 3.113 + internals.docSet.push(thisDoc); 3.114 + } 3.115 + thisDoc.url = urls[i]; 3.116 + thisDoc.internals = internals; 3.117 + fetchDoc(thisDoc.url, thisDoc); 3.118 + } 3.119 + }; 3.120 + 3.121 + function fetchDoc(url, doc, resFunc) { 3.122 + resFunc = resFunc || function (responseXML) { render(responseXML, doc); }; 3.123 + 3.124 + if (typeof ZipFile === 'function' && typeof JSIO === 'object' && typeof JSIO.guessFileType === 'function') { // KMZ support requires these modules loaded 3.125 + contentType = JSIO.guessFileType(doc.baseUrl); 3.126 + if (contentType == JSIO.FileType.Binary || contentType == JSIO.FileType.Unknown) { 3.127 + doc.isCompressed = true; 3.128 + doc.baseDir = doc.baseUrl + '/'; 3.129 + geoXML3.fetchZIP(url, resFunc, doc.internals.parser); 3.130 + return; 3.131 + } 3.132 + } 3.133 + doc.isCompressed = false; 3.134 + doc.baseDir = defileURL(doc.baseUrl); 3.135 + geoXML3.fetchXML(url, resFunc); 3.136 + } 3.137 + 3.138 + var hideDocument = function (doc) { 3.139 + if (!doc) doc = docs[0]; 3.140 + // Hide the map objects associated with a document 3.141 + var i; 3.142 + if (!!doc.markers) { 3.143 + for (i = 0; i < doc.markers.length; i++) { 3.144 + if(!!doc.markers[i].infoWindow) doc.markers[i].infoWindow.close(); 3.145 + doc.markers[i].setVisible(false); 3.146 + } 3.147 + } 3.148 + if (!!doc.ggroundoverlays) { 3.149 + for (i = 0; i < doc.ggroundoverlays.length; i++) { 3.150 + doc.ggroundoverlays[i].setOpacity(0); 3.151 + } 3.152 + } 3.153 + if (!!doc.gpolylines) { 3.154 + for (i=0;i<doc.gpolylines.length;i++) { 3.155 + if(!!doc.gpolylines[i].infoWindow) doc.gpolylines[i].infoWindow.close(); 3.156 + doc.gpolylines[i].setMap(null); 3.157 + } 3.158 + } 3.159 + if (!!doc.gpolygons) { 3.160 + for (i=0;i<doc.gpolygons.length;i++) { 3.161 + if(!!doc.gpolygons[i].infoWindow) doc.gpolygons[i].infoWindow.close(); 3.162 + doc.gpolygons[i].setMap(null); 3.163 + } 3.164 + } 3.165 + }; 3.166 + 3.167 + var showDocument = function (doc) { 3.168 + if (!doc) doc = docs[0]; 3.169 + // Show the map objects associated with a document 3.170 + var i; 3.171 + if (!!doc.markers) { 3.172 + for (i = 0; i < doc.markers.length; i++) { 3.173 + doc.markers[i].setVisible(true); 3.174 + } 3.175 + } 3.176 + if (!!doc.ggroundoverlays) { 3.177 + for (i = 0; i < doc.ggroundoverlays.length; i++) { 3.178 + doc.ggroundoverlays[i].setOpacity(doc.ggroundoverlays[i].percentOpacity_); 3.179 + } 3.180 + } 3.181 + if (!!doc.gpolylines) { 3.182 + for (i=0;i<doc.gpolylines.length;i++) { 3.183 + doc.gpolylines[i].setMap(parserOptions.map); 3.184 + } 3.185 + } 3.186 + if (!!doc.gpolygons) { 3.187 + for (i=0;i<doc.gpolygons.length;i++) { 3.188 + doc.gpolygons[i].setMap(parserOptions.map); 3.189 + } 3.190 + } 3.191 + }; 3.192 + 3.193 + var defaultStyle = { 3.194 + balloon: { 3.195 + bgColor: 'ffffffff', 3.196 + textColor: 'ff000000', 3.197 + text: "<h3>$[name]</h3>\n<div>$[description]</div>\n<div>$[geDirections]</div>", 3.198 + displayMode: 'default' 3.199 + }, 3.200 + icon: { 3.201 + scale: 1.0, 3.202 + dim: { 3.203 + x: 0, 3.204 + y: 0, 3.205 + w: -1, 3.206 + h: -1 3.207 + }, 3.208 + hotSpot: { 3.209 + x: 0.5, 3.210 + y: 0.5, 3.211 + xunits: 'fraction', 3.212 + yunits: 'fraction' 3.213 + } 3.214 + }, 3.215 + line: { 3.216 + color: 'ffffffff', // white (KML default) 3.217 + colorMode: 'normal', 3.218 + width: 1.0 3.219 + }, 3.220 + poly: { 3.221 + color: 'ffffffff', // white (KML default) 3.222 + colorMode: 'normal', 3.223 + fill: true, 3.224 + outline: true 3.225 + } 3.226 + }; 3.227 + 3.228 + var kmlNS = 'http://www.opengis.net/kml/2.2'; 3.229 + var gxNS = 'http://www.google.com/kml/ext/2.2'; 3.230 + var nodeValue = geoXML3.nodeValue; 3.231 + var getBooleanValue = geoXML3.getBooleanValue; 3.232 + var getElementsByTagNameNS = geoXML3.getElementsByTagNameNS; 3.233 + var getElementsByTagName = geoXML3.getElementsByTagName; 3.234 + 3.235 +function processStyleUrl(node) { 3.236 + var styleUrlStr = nodeValue(getElementsByTagName(node, 'styleUrl')[0]); 3.237 + if (!!styleUrlStr && styleUrlStr.indexOf('#') != -1) 3.238 + var styleUrl = styleUrlStr.split('#'); 3.239 + else var styleUrl = ["",""]; 3.240 + return styleUrl; 3.241 +} 3.242 + 3.243 + function processStyle(thisNode, baseUrl, styleID, baseDir) { 3.244 + var style = (baseUrl === '{inline}') ? clone(defaultStyle) : (styles[baseUrl][styleID] = styles[baseUrl][styleID] || clone(defaultStyle)); 3.245 + 3.246 + var styleNodes = getElementsByTagName(thisNode, 'BalloonStyle'); 3.247 + if (!!styleNodes && styleNodes.length > 0) { 3.248 + style.balloon.bgColor = nodeValue(getElementsByTagName(styleNodes[0], 'bgColor')[0], style.balloon.bgColor); 3.249 + style.balloon.textColor = nodeValue(getElementsByTagName(styleNodes[0], 'textColor')[0], style.balloon.textColor); 3.250 + style.balloon.text = nodeValue(getElementsByTagName(styleNodes[0], 'text')[0], style.balloon.text); 3.251 + style.balloon.displayMode = nodeValue(getElementsByTagName(styleNodes[0], 'displayMode')[0], style.balloon.displayMode); 3.252 + } 3.253 + 3.254 + // style.list = (unsupported; doesn't make sense in Google Maps) 3.255 + 3.256 + var styleNodes = getElementsByTagName(thisNode, 'IconStyle'); 3.257 + if (!!styleNodes && styleNodes.length > 0) { 3.258 + var icon = style.icon; 3.259 + 3.260 + icon.scale = parseFloat(nodeValue(getElementsByTagName(styleNodes[0], 'scale')[0], icon.scale)); 3.261 + // style.icon.heading = (unsupported; not supported in API) 3.262 + // style.icon.color = (unsupported; not supported in API) 3.263 + // style.icon.colorMode = (unsupported; not supported in API) 3.264 + 3.265 + styleNodes = getElementsByTagName(thisNode, 'Icon'); 3.266 + if (!!styleNodes && styleNodes.length > 0) { 3.267 + icon.href = nodeValue(getElementsByTagName(styleNodes[0], 'href')[0]); 3.268 + icon.url = cleanURL(baseDir, icon.href); 3.269 + // Detect images buried in KMZ files (and use a base64 encoded URL) 3.270 + if (kmzMetaData[icon.url]) icon.url = kmzMetaData[icon.url].dataUrl; 3.271 + 3.272 + // Support for icon palettes and exact size dimensions 3.273 + icon.dim = { 3.274 + x: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'x')[0], icon.dim.x)), 3.275 + y: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'y')[0], icon.dim.y)), 3.276 + w: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'w')[0], icon.dim.w)), 3.277 + h: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'h')[0], icon.dim.h)) 3.278 + }; 3.279 + 3.280 + styleNodes = getElementsByTagName(styleNodes[0], 'hotSpot')[0]; 3.281 + if (!!styleNodes && styleNodes.length > 0) { 3.282 + icon.hotSpot = { 3.283 + x: styleNodes[0].getAttribute('x'), 3.284 + y: styleNodes[0].getAttribute('y'), 3.285 + xunits: styleNodes[0].getAttribute('xunits'), 3.286 + yunits: styleNodes[0].getAttribute('yunits') 3.287 + }; 3.288 + } 3.289 + 3.290 + // certain occasions where we need the pixel size of the image (like the default settings...) 3.291 + // (NOTE: Scale is applied to entire image, not just the section of the icon palette. So, 3.292 + // if we need scaling, we'll need the img dimensions no matter what.) 3.293 + if ( (icon.dim.w < 0 || icon.dim.h < 0) && (icon.xunits != 'pixels' || icon.yunits == 'fraction') || icon.scale != 1.0) { 3.294 + // (hopefully, this will load by the time we need it...) 3.295 + icon.img = new Image(); 3.296 + icon.img.onload = function() { 3.297 + if (icon.dim.w < 0 || icon.dim.h < 0) { 3.298 + icon.dim.w = this.width; 3.299 + icon.dim.h = this.height; 3.300 + } 3.301 + }; 3.302 + icon.img.src = icon.url; 3.303 + 3.304 + // sometimes the file is already cached and it never calls onLoad 3.305 + if (icon.img.width > 0) { 3.306 + icon.dim.w = icon.img.width; 3.307 + icon.dim.h = icon.img.height; 3.308 + } 3.309 + } 3.310 + } 3.311 + } 3.312 + 3.313 + // style.label = (unsupported; may be possible but not with API) 3.314 + 3.315 + styleNodes = getElementsByTagName(thisNode, 'LineStyle'); 3.316 + if (!!styleNodes && styleNodes.length > 0) { 3.317 + style.line.color = nodeValue(getElementsByTagName(styleNodes[0], 'color')[0], style.line.color); 3.318 + style.line.colorMode = nodeValue(getElementsByTagName(styleNodes[0], 'colorMode')[0], style.line.colorMode); 3.319 + style.line.width = nodeValue(getElementsByTagName(styleNodes[0], 'width')[0], style.line.width); 3.320 + // style.line.outerColor = (unsupported; not supported in API) 3.321 + // style.line.outerWidth = (unsupported; not supported in API) 3.322 + // style.line.physicalWidth = (unsupported; unneccesary in Google Maps) 3.323 + // style.line.labelVisibility = (unsupported; possible to implement) 3.324 + } 3.325 + 3.326 + styleNodes = getElementsByTagName(thisNode, 'PolyStyle'); 3.327 + if (!!styleNodes && styleNodes.length > 0) { 3.328 + style.poly.color = nodeValue( getElementsByTagName(styleNodes[0], 'color')[0], style.poly.color); 3.329 + style.poly.colorMode = nodeValue( getElementsByTagName(styleNodes[0], 'colorMode')[0], style.poly.colorMode); 3.330 + style.poly.outline = getBooleanValue(getElementsByTagName(styleNodes[0], 'outline')[0], style.poly.outline); 3.331 + style.poly.fill = getBooleanValue(getElementsByTagName(styleNodes[0], 'fill')[0], style.poly.fill); 3.332 + } 3.333 + return style; 3.334 + } 3.335 + 3.336 + // from http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object 3.337 + // http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone 3.338 + function clone(obj){ 3.339 + if(obj == null || typeof(obj) != 'object') return obj; 3.340 + if (obj.cloneNode) return obj.cloneNode(true); 3.341 + var temp = new obj.constructor(); 3.342 + for(var key in obj) temp[key] = clone(obj[key]); 3.343 + return temp; 3.344 + } 3.345 + 3.346 + function processStyleMap(thisNode, baseUrl, styleID, baseDir) { 3.347 + var pairs = getElementsByTagName(thisNode, 'Pair'); 3.348 + var map = new Object(); 3.349 + 3.350 + // add each key to the map 3.351 + for (var pr=0;pr<pairs.length;pr++) { 3.352 + var pairKey = nodeValue(getElementsByTagName(pairs[pr], 'key')[0]); 3.353 + var pairStyle = nodeValue(getElementsByTagName(pairs[pr], 'Style')[0]); 3.354 + var pairStyleUrl = processStyleUrl(pairs[pr]); 3.355 + var pairStyleBaseUrl = pairStyleUrl[0] ? cleanURL(baseDir, pairStyleUrl[0]) : baseUrl; 3.356 + var pairStyleID = pairStyleUrl[1]; 3.357 + 3.358 + if (!!pairStyle) { 3.359 + map[pairKey] = processStyle(pairStyle, pairStyleBaseUrl, pairStyleID); 3.360 + } else if (!!pairStyleID && !!styles[pairStyleBaseUrl][pairStyleID]) { 3.361 + map[pairKey] = clone(styles[pairStyleBaseUrl][pairStyleID]); 3.362 + } 3.363 + } 3.364 + if (!!map["normal"]) { 3.365 + styles[baseUrl][styleID] = clone(map["normal"]); 3.366 + } else { 3.367 + styles[baseUrl][styleID] = clone(defaultStyle); 3.368 + } 3.369 + if (!!map["highlight"]) { 3.370 + processStyleID(map["highlight"]); 3.371 + } 3.372 + styles[baseUrl][styleID].map = clone(map); 3.373 + } 3.374 + 3.375 + function processPlacemarkCoords(node, tag) { 3.376 + var parent = getElementsByTagName(node, tag); 3.377 + var coordListA = []; 3.378 + for (var i=0; i<parent.length; i++) { 3.379 + var coordNodes = getElementsByTagName(parent[i], 'coordinates'); 3.380 + if (!coordNodes) { 3.381 + if (coordListA.length > 0) { 3.382 + break; 3.383 + } else { 3.384 + return [{coordinates: []}]; 3.385 + } 3.386 + } 3.387 + 3.388 + for (var j=0; j<coordNodes.length;j++) { 3.389 + var coords = nodeValue(coordNodes[j]).trim(); 3.390 + coords = coords.replace(/,\s+/g, ','); 3.391 + var path = coords.split(/\s+/g); 3.392 + var pathLength = path.length; 3.393 + var coordList = []; 3.394 + for (var k = 0; k < pathLength; k++) { 3.395 + coords = path[k].split(','); 3.396 + if (!isNaN(coords[0]) && !isNaN(coords[1])) { 3.397 + coordList.push({ 3.398 + lat: parseFloat(coords[1]), 3.399 + lng: parseFloat(coords[0]), 3.400 + alt: parseFloat(coords[2]) 3.401 + }); 3.402 + } 3.403 + } 3.404 + coordListA.push({coordinates: coordList}); 3.405 + } 3.406 + } 3.407 + return coordListA; 3.408 + } 3.409 + 3.410 + var render = function (responseXML, doc) { 3.411 + // Callback for retrieving a KML document: parse the KML and display it on the map 3.412 + if (!responseXML) { 3.413 + // Error retrieving the data 3.414 + geoXML3.log('Unable to retrieve ' + doc.url); 3.415 + if (parserOptions.failedParse) parserOptions.failedParse(doc); 3.416 + doc.failed = true; 3.417 + return; 3.418 + } else if (responseXML.parseError && responseXML.parseError.errorCode != 0) { 3.419 + // IE parse error 3.420 + var err = responseXML.parseError; 3.421 + var msg = 'Parse error in line ' + err.line + ', col ' + err.linePos + ' (error code: ' + err.errorCode + ")\n" + 3.422 + "\nError Reason: " + err.reason + 3.423 + 'Error Line: ' + err.srcText; 3.424 + 3.425 + geoXML3.log('Unable to retrieve ' + doc.url + ': ' + msg); 3.426 + if (parserOptions.failedParse) parserOptions.failedParse(doc); 3.427 + doc.failed = true; 3.428 + return; 3.429 + } else if (responseXML.documentElement && responseXML.documentElement.nodeName == 'parsererror') { 3.430 + // Firefox parse error 3.431 + geoXML3.log('Unable to retrieve ' + doc.url + ': ' + responseXML.documentElement.childNodes[0].nodeValue); 3.432 + if (parserOptions.failedParse) parserOptions.failedParse(doc); 3.433 + doc.failed = true; 3.434 + return; 3.435 + } else if (!doc) { 3.436 + throw 'geoXML3 internal error: render called with null document'; 3.437 + } else { //no errors 3.438 + var i; 3.439 + doc.placemarks = []; 3.440 + doc.groundoverlays = []; 3.441 + doc.ggroundoverlays = []; 3.442 + doc.networkLinks = []; 3.443 + doc.gpolygons = []; 3.444 + doc.gpolylines = []; 3.445 + 3.446 + // Check for dependent KML files 3.447 + var nodes = getElementsByTagName(responseXML, 'styleUrl'); 3.448 + var docSet = doc.internals.docSet; 3.449 + 3.450 + for (var i = 0; i < nodes.length; i++) { 3.451 + var url = nodeValue(nodes[i]).split('#')[0]; 3.452 + if (!url) continue; // #id (inside doc) 3.453 + var rUrl = cleanURL( doc.baseDir, url ); 3.454 + if (rUrl === doc.baseUrl) continue; // self 3.455 + if (docsByUrl[rUrl]) continue; // already loaded 3.456 + 3.457 + var thisDoc; 3.458 + var j = docSet.indexOfObjWithItem('baseUrl', rUrl); 3.459 + if (j != -1) { 3.460 + // Already listed to be loaded, but probably in the wrong order. 3.461 + // Load it right away to immediately resolve dependency. 3.462 + thisDoc = docSet[j]; 3.463 + if (thisDoc.failed) continue; // failed to load last time; don't retry it again 3.464 + } 3.465 + else { 3.466 + // Not listed at all; add it in 3.467 + thisDoc = new Object(); 3.468 + thisDoc.url = rUrl; // url can't be trusted inside KMZ files, since it may .. outside of the archive 3.469 + thisDoc.baseUrl = rUrl; 3.470 + thisDoc.internals = doc.internals; 3.471 + 3.472 + doc.internals.docSet.push(thisDoc); 3.473 + doc.internals.remaining++; 3.474 + } 3.475 + 3.476 + // render dependent KML first then re-run renderer 3.477 + fetchDoc(rUrl, thisDoc, function (thisResXML) { 3.478 + render(thisResXML, thisDoc); 3.479 + render(responseXML, doc); 3.480 + }); 3.481 + 3.482 + // to prevent cross-dependency issues, just load the one 3.483 + // file first and re-check the rest later 3.484 + return; 3.485 + } 3.486 + 3.487 + // Parse styles 3.488 + doc.styles = styles[doc.baseUrl] = styles[doc.baseUrl] || {}; 3.489 + var styleID, styleNodes; 3.490 + nodes = getElementsByTagName(responseXML, 'Style'); 3.491 + nodeCount = nodes.length; 3.492 + for (i = 0; i < nodeCount; i++) { 3.493 + thisNode = nodes[i]; 3.494 + var styleID = thisNode.getAttribute('id'); 3.495 + if (!!styleID) processStyle(thisNode, doc.baseUrl, styleID, doc.baseDir); 3.496 + } 3.497 + // Parse StyleMap nodes 3.498 + nodes = getElementsByTagName(responseXML, 'StyleMap'); 3.499 + for (i = 0; i < nodes.length; i++) { 3.500 + thisNode = nodes[i]; 3.501 + var styleID = thisNode.getAttribute('id'); 3.502 + if (!!styleID) processStyleMap(thisNode, doc.baseUrl, styleID, doc.baseDir); 3.503 + } 3.504 + 3.505 + if (!!parserOptions.processStyles || !parserOptions.createMarker) { 3.506 + // Convert parsed styles into GMaps equivalents 3.507 + processStyles(doc); 3.508 + } 3.509 + 3.510 + // Parse placemarks 3.511 + if (!!doc.reload && !!doc.markers) { 3.512 + for (i = 0; i < doc.markers.length; i++) { 3.513 + doc.markers[i].active = false; 3.514 + } 3.515 + } 3.516 + var placemark, node, coords, path, marker, poly; 3.517 + var placemark, coords, path, pathLength, marker, polygonNodes, coordList; 3.518 + var placemarkNodes = getElementsByTagName(responseXML, 'Placemark'); 3.519 + for (pm = 0; pm < placemarkNodes.length; pm++) { 3.520 + // Init the placemark object 3.521 + node = placemarkNodes[pm]; 3.522 + var styleUrl = processStyleUrl(node); 3.523 + placemark = { 3.524 + name: nodeValue(getElementsByTagName(node, 'name')[0]), 3.525 + description: nodeValue(getElementsByTagName(node, 'description')[0]), 3.526 + styleUrl: styleUrl.join('#'), 3.527 + styleBaseUrl: styleUrl[0] ? cleanURL(doc.baseDir, styleUrl[0]) : doc.baseUrl, 3.528 + styleID: styleUrl[1], 3.529 + visibility: getBooleanValue(getElementsByTagName(node, 'visibility')[0], true), 3.530 + balloonVisibility: getBooleanValue(getElementsByTagNameNS(node, gxNS, 'balloonVisibility')[0], !parserOptions.suppressInfoWindows) 3.531 + }; 3.532 + placemark.style = (styles[placemark.styleBaseUrl] && styles[placemark.styleBaseUrl][placemark.styleID]) || clone(defaultStyle); 3.533 + // inline style overrides shared style 3.534 + var inlineStyles = getElementsByTagName(node, 'Style'); 3.535 + if (inlineStyles && (inlineStyles.length > 0)) { 3.536 + var style = processStyle(node, '{inline}', '{inline}'); 3.537 + processStyleID(style); 3.538 + if (style) placemark.style = style; 3.539 + } 3.540 + 3.541 + if (/^https?:\/\//.test(placemark.description)) { 3.542 + placemark.description = ['<a href="', placemark.description, '">', placemark.description, '</a>'].join(''); 3.543 + } 3.544 + 3.545 + // record list of variables for substitution 3.546 + placemark.vars = { 3.547 + display: { 3.548 + name: 'Name', 3.549 + description: 'Description', 3.550 + address: 'Street Address', 3.551 + id: 'ID', 3.552 + Snippet: 'Snippet', 3.553 + geDirections: 'Directions' 3.554 + }, 3.555 + val: { 3.556 + name: placemark.name || '', 3.557 + description: placemark.description || '', 3.558 + address: nodeValue(getElementsByTagName(node, 'address')[0], ''), 3.559 + id: node.getAttribute('id') || '', 3.560 + Snippet: nodeValue(getElementsByTagName(node, 'Snippet')[0], '') 3.561 + }, 3.562 + directions: [ 3.563 + 'f=d', 3.564 + 'source=GeoXML3' 3.565 + ] 3.566 + }; 3.567 + 3.568 + // add extended data to variables 3.569 + var extDataNodes = getElementsByTagName(node, 'ExtendedData'); 3.570 + if (!!extDataNodes && extDataNodes.length > 0) { 3.571 + var dataNodes = getElementsByTagName(extDataNodes[0], 'Data'); 3.572 + for (var d = 0; d < dataNodes.length; d++) { 3.573 + var dn = dataNodes[d]; 3.574 + var name = dn.getAttribute('name'); 3.575 + if (!name) continue; 3.576 + var dName = nodeValue(getElementsByTagName(dn, 'displayName')[0], name); 3.577 + var val = nodeValue(getElementsByTagName(dn, 'value')[0]); 3.578 + 3.579 + placemark.vars.val[name] = val; 3.580 + placemark.vars.display[name] = dName; 3.581 + } 3.582 + } 3.583 + 3.584 + // process MultiGeometry 3.585 + var GeometryNodes = getElementsByTagName(node, 'coordinates'); 3.586 + var Geometry = null; 3.587 + if (!!GeometryNodes && (GeometryNodes.length > 0)) { 3.588 + for (var gn=0;gn<GeometryNodes.length;gn++) { 3.589 + if (GeometryNodes[gn].parentNode && 3.590 + GeometryNodes[gn].parentNode.nodeName) { 3.591 + var GeometryPN = GeometryNodes[gn].parentNode; 3.592 + Geometry = GeometryPN.nodeName; 3.593 + 3.594 + // Extract the coordinates 3.595 + // What sort of placemark? 3.596 + switch(Geometry) { 3.597 + case "Point": 3.598 + placemark.Point = processPlacemarkCoords(node, "Point")[0]; 3.599 + placemark.latlng = new google.maps.LatLng(placemark.Point.coordinates[0].lat, placemark.Point.coordinates[0].lng); 3.600 + pathLength = 1; 3.601 + break; 3.602 + case "LinearRing": 3.603 + // Polygon/line 3.604 + polygonNodes = getElementsByTagName(node, 'Polygon'); 3.605 + // Polygon 3.606 + if (!placemark.Polygon) 3.607 + placemark.Polygon = [{ 3.608 + outerBoundaryIs: {coordinates: []}, 3.609 + innerBoundaryIs: [{coordinates: []}] 3.610 + }]; 3.611 + for (var pg=0;pg<polygonNodes.length;pg++) { 3.612 + placemark.Polygon[pg] = { 3.613 + outerBoundaryIs: {coordinates: []}, 3.614 + innerBoundaryIs: [{coordinates: []}] 3.615 + } 3.616 + placemark.Polygon[pg].outerBoundaryIs = processPlacemarkCoords(polygonNodes[pg], "outerBoundaryIs"); 3.617 + placemark.Polygon[pg].innerBoundaryIs = processPlacemarkCoords(polygonNodes[pg], "innerBoundaryIs"); 3.618 + } 3.619 + coordList = placemark.Polygon[0].outerBoundaryIs; 3.620 + break; 3.621 + 3.622 + case "LineString": 3.623 + pathLength = 0; 3.624 + placemark.LineString = processPlacemarkCoords(node,"LineString"); 3.625 + break; 3.626 + 3.627 + default: 3.628 + break; 3.629 + } 3.630 + } 3.631 + } 3.632 + } 3.633 + 3.634 + // call the custom placemark parse function if it is defined 3.635 + if (!!parserOptions.pmParseFn) parserOptions.pmParseFn(node, placemark); 3.636 + doc.placemarks.push(placemark); 3.637 + 3.638 + // single marker 3.639 + if (placemark.Point) { 3.640 + if (!!google.maps) { 3.641 + doc.bounds = doc.bounds || new google.maps.LatLngBounds(); 3.642 + doc.bounds.extend(placemark.latlng); 3.643 + } 3.644 + 3.645 + // Potential user-defined marker handler 3.646 + var pointCreateFunc = parserOptions.createMarker || createMarker; 3.647 + var found = false; 3.648 + if (!parserOptions.createMarker) { 3.649 + // Check to see if this marker was created on a previous load of this document 3.650 + if (!!doc) { 3.651 + doc.markers = doc.markers || []; 3.652 + if (doc.reload) { 3.653 + for (var j = 0; j < doc.markers.length; j++) { 3.654 + if (doc.markers[j].getPosition().equals(placemark.latlng)) { 3.655 + found = doc.markers[j].active = true; 3.656 + break; 3.657 + } 3.658 + } 3.659 + } 3.660 + } 3.661 + } 3.662 + if (!found) { 3.663 + // Call the marker creator 3.664 + var marker = pointCreateFunc(placemark, doc); 3.665 + if (marker) marker.active = placemark.visibility; 3.666 + } 3.667 + } 3.668 + // polygon/line 3.669 + var poly, line; 3.670 + if (!!doc) { 3.671 + if (placemark.Polygon) doc.gpolygons = doc.gpolygons || []; 3.672 + if (placemark.LineString) doc.gpolylines = doc.gpolylines || []; 3.673 + } 3.674 + 3.675 + var polyCreateFunc = parserOptions.createPolygon || createPolygon; 3.676 + var lineCreateFunc = parserOptions.createLineString || createPolyline; 3.677 + if (placemark.Polygon) { 3.678 + poly = polyCreateFunc(placemark,doc); 3.679 + if (poly) poly.active = placemark.visibility; 3.680 + } 3.681 + if (placemark.LineString) { 3.682 + line = lineCreateFunc(placemark,doc); 3.683 + if (line) line.active = placemark.visibility; 3.684 + } 3.685 + if (!!google.maps) { 3.686 + doc.bounds = doc.bounds || new google.maps.LatLngBounds(); 3.687 + if (poly) doc.bounds.union(poly.bounds); 3.688 + if (line) doc.bounds.union(line.bounds); 3.689 + } 3.690 + 3.691 + } // placemark loop 3.692 + 3.693 + if (!!doc.reload && !!doc.markers) { 3.694 + for (i = doc.markers.length - 1; i >= 0 ; i--) { 3.695 + if (!doc.markers[i].active) { 3.696 + if (!!doc.markers[i].infoWindow) { 3.697 + doc.markers[i].infoWindow.close(); 3.698 + } 3.699 + doc.markers[i].setMap(null); 3.700 + doc.markers.splice(i, 1); 3.701 + } 3.702 + } 3.703 + } 3.704 + 3.705 + // Parse ground overlays 3.706 + if (!!doc.reload && !!doc.groundoverlays) { 3.707 + for (i = 0; i < doc.groundoverlays.length; i++) { 3.708 + doc.groundoverlays[i].active = false; 3.709 + } 3.710 + } 3.711 + 3.712 + if (!!doc) { 3.713 + doc.groundoverlays = doc.groundoverlays || []; 3.714 + } 3.715 + // doc.groundoverlays =[]; 3.716 + var groundOverlay, color, transparency, overlay; 3.717 + var groundNodes = getElementsByTagName(responseXML, 'GroundOverlay'); 3.718 + for (i = 0; i < groundNodes.length; i++) { 3.719 + node = groundNodes[i]; 3.720 + 3.721 + // Detect images buried in KMZ files (and use a base64 encoded URL) 3.722 + var gnUrl = cleanURL( doc.baseDir, nodeValue(getElementsByTagName(node, 'href')[0]) ); 3.723 + if (kmzMetaData[gnUrl]) gnUrl = kmzMetaData[gnUrl].dataUrl; 3.724 + 3.725 + // Init the ground overlay object 3.726 + groundOverlay = { 3.727 + name: nodeValue(getElementsByTagName(node, 'name')[0]), 3.728 + description: nodeValue(getElementsByTagName(node, 'description')[0]), 3.729 + icon: { href: gnUrl }, 3.730 + latLonBox: { 3.731 + north: parseFloat(nodeValue(getElementsByTagName(node, 'north')[0])), 3.732 + east: parseFloat(nodeValue(getElementsByTagName(node, 'east')[0])), 3.733 + south: parseFloat(nodeValue(getElementsByTagName(node, 'south')[0])), 3.734 + west: parseFloat(nodeValue(getElementsByTagName(node, 'west')[0])) 3.735 + } 3.736 + }; 3.737 + if (!!google.maps) { 3.738 + doc.bounds = doc.bounds || new google.maps.LatLngBounds(); 3.739 + doc.bounds.union(new google.maps.LatLngBounds( 3.740 + new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west), 3.741 + new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east) 3.742 + )); 3.743 + } 3.744 + 3.745 + // Opacity is encoded in the color node 3.746 + var colorNode = getElementsByTagName(node, 'color'); 3.747 + if (colorNode && colorNode.length > 0) { 3.748 + groundOverlay.opacity = geoXML3.getOpacity(nodeValue(colorNode[0])); 3.749 + } else { 3.750 + groundOverlay.opacity = 1.0; // KML default 3.751 + } 3.752 + 3.753 + doc.groundoverlays.push(groundOverlay); 3.754 + if (!!parserOptions.createOverlay) { 3.755 + // User-defined overlay handler 3.756 + parserOptions.createOverlay(groundOverlay, doc); 3.757 + } else { 3.758 + // Check to see if this overlay was created on a previous load of this document 3.759 + var found = false; 3.760 + if (!!doc) { 3.761 + doc.groundoverlays = doc.groundoverlays || []; 3.762 + if (doc.reload) { 3.763 + overlayBounds = new google.maps.LatLngBounds( 3.764 + new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west), 3.765 + new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east) 3.766 + ); 3.767 + var overlays = doc.groundoverlays; 3.768 + for (i = overlays.length; i--;) { 3.769 + if ((overlays[i].bounds().equals(overlayBounds)) && 3.770 + (overlays.url_ === groundOverlay.icon.href)) { 3.771 + found = overlays[i].active = true; 3.772 + break; 3.773 + } 3.774 + } 3.775 + } 3.776 + } 3.777 + 3.778 + if (!found) { 3.779 + // Call the built-in overlay creator 3.780 + overlay = createOverlay(groundOverlay, doc); 3.781 + overlay.active = true; 3.782 + } 3.783 + } 3.784 + if (!!doc.reload && !!doc.groundoverlays && !!doc.groundoverlays.length) { 3.785 + var overlays = doc.groundoverlays; 3.786 + for (i = overlays.length; i--;) { 3.787 + if (!overlays[i].active) { 3.788 + overlays[i].remove(); 3.789 + overlays.splice(i, 1); 3.790 + } 3.791 + } 3.792 + doc.groundoverlays = overlays; 3.793 + } 3.794 + } 3.795 + 3.796 + // Parse network links 3.797 + var networkLink; 3.798 + var docPath = document.location.pathname.split('/'); 3.799 + docPath = docPath.splice(0, docPath.length - 1).join('/'); 3.800 + var linkNodes = getElementsByTagName(responseXML, 'NetworkLink'); 3.801 + for (i = 0; i < linkNodes.length; i++) { 3.802 + node = linkNodes[i]; 3.803 + 3.804 + // Init the network link object 3.805 + networkLink = { 3.806 + name: nodeValue(getElementsByTagName(node, 'name')[0]), 3.807 + link: { 3.808 + href: nodeValue(getElementsByTagName(node, 'href')[0]), 3.809 + refreshMode: nodeValue(getElementsByTagName(node, 'refreshMode')[0]) 3.810 + } 3.811 + }; 3.812 + 3.813 + // Establish the specific refresh mode 3.814 + if (!networkLink.link.refreshMode) { 3.815 + networkLink.link.refreshMode = 'onChange'; 3.816 + } 3.817 + if (networkLink.link.refreshMode === 'onInterval') { 3.818 + networkLink.link.refreshInterval = parseFloat(nodeValue(getElementsByTagName(node, 'refreshInterval')[0])); 3.819 + if (isNaN(networkLink.link.refreshInterval)) { 3.820 + networkLink.link.refreshInterval = 0; 3.821 + } 3.822 + } else if (networkLink.link.refreshMode === 'onChange') { 3.823 + networkLink.link.viewRefreshMode = nodeValue(getElementsByTagName(node, 'viewRefreshMode')[0]); 3.824 + if (!networkLink.link.viewRefreshMode) { 3.825 + networkLink.link.viewRefreshMode = 'never'; 3.826 + } 3.827 + if (networkLink.link.viewRefreshMode === 'onStop') { 3.828 + networkLink.link.viewRefreshTime = nodeValue(getElementsByTagName(node, 'refreshMode')[0]); 3.829 + networkLink.link.viewFormat = nodeValue(getElementsByTagName(node, 'refreshMode')[0]); 3.830 + if (!networkLink.link.viewFormat) { 3.831 + networkLink.link.viewFormat = 'BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]'; 3.832 + } 3.833 + } 3.834 + } 3.835 + 3.836 + if (!/^[\/|http]/.test(networkLink.link.href)) { 3.837 + // Fully-qualify the HREF 3.838 + networkLink.link.href = docPath + '/' + networkLink.link.href; 3.839 + } 3.840 + 3.841 + // Apply the link 3.842 + if ((networkLink.link.refreshMode === 'onInterval') && 3.843 + (networkLink.link.refreshInterval > 0)) { 3.844 + // Reload at regular intervals 3.845 + setInterval(parserName + '.parse("' + networkLink.link.href + '")', 3.846 + 1000 * networkLink.link.refreshInterval); 3.847 + } else if (networkLink.link.refreshMode === 'onChange') { 3.848 + if (networkLink.link.viewRefreshMode === 'never') { 3.849 + // Load the link just once 3.850 + doc.internals.parser.parse(networkLink.link.href, doc.internals.docSet); 3.851 + } else if (networkLink.link.viewRefreshMode === 'onStop') { 3.852 + // Reload when the map view changes 3.853 + 3.854 + } 3.855 + } 3.856 + } 3.857 + } 3.858 + 3.859 + if (!!doc.bounds) { 3.860 + doc.internals.bounds = doc.internals.bounds || new google.maps.LatLngBounds(); 3.861 + doc.internals.bounds.union(doc.bounds); 3.862 + } 3.863 + if (!!doc.markers || !!doc.groundoverlays || !!doc.gpolylines || !!doc.gpolygons) { 3.864 + doc.internals.parseOnly = false; 3.865 + } 3.866 + 3.867 + if (!doc.internals.parseOnly) { 3.868 + // geoXML3 is not being used only as a real-time parser, so keep the processed documents around 3.869 + if (!docsByUrl[doc.baseUrl]) { 3.870 + docs.push(doc); 3.871 + docsByUrl[doc.baseUrl] = doc; 3.872 + } 3.873 + else { 3.874 + // internal replacement, which keeps the same memory ref loc in docs and docsByUrl 3.875 + for (var i in docsByUrl[doc.baseUrl]) { 3.876 + docsByUrl[doc.baseUrl][i] = doc[i]; 3.877 + } 3.878 + } 3.879 + } 3.880 + 3.881 + doc.internals.remaining--; 3.882 + if (doc.internals.remaining === 0) { 3.883 + // We're done processing this set of KML documents 3.884 + // Options that get invoked after parsing completes 3.885 + if (parserOptions.zoom && !!doc.internals.bounds && 3.886 + !doc.internals.bounds.isEmpty() && !!parserOptions.map) { 3.887 + parserOptions.map.fitBounds(doc.internals.bounds); 3.888 + } 3.889 + if (parserOptions.afterParse) { 3.890 + parserOptions.afterParse(doc.internals.docSet); 3.891 + } 3.892 + } 3.893 + }; 3.894 + 3.895 + var kmlColor = function (kmlIn, colorMode) { 3.896 + var kmlColor = {}; 3.897 + kmlIn = kmlIn || 'ffffffff'; // white (KML 2.2 default) 3.898 + 3.899 + var aa = kmlIn.substr(0,2); 3.900 + var bb = kmlIn.substr(2,2); 3.901 + var gg = kmlIn.substr(4,2); 3.902 + var rr = kmlIn.substr(6,2); 3.903 + 3.904 + kmlColor.opacity = parseInt(aa, 16) / 256; 3.905 + kmlColor.color = (colorMode === 'random') ? randomColor(rr, gg, bb) : '#' + rr + gg + bb; 3.906 + return kmlColor; 3.907 + }; 3.908 + 3.909 + // Implemented per KML 2.2 <ColorStyle> specs 3.910 + var randomColor = function(rr, gg, bb) { 3.911 + var col = { rr: rr, gg: gg, bb: bb }; 3.912 + for (var k in col) { 3.913 + var v = col[k]; 3.914 + if (v == null) v = 'ff'; 3.915 + 3.916 + // RGB values are limiters for random numbers (ie: 7f would be a random value between 0 and 7f) 3.917 + v = Math.round(Math.random() * parseInt(rr, 16)).toString(16); 3.918 + if (v.length === 1) v = '0' + v; 3.919 + col[k] = v; 3.920 + } 3.921 + 3.922 + return '#' + col.rr + col.gg + col.bb; 3.923 + }; 3.924 + 3.925 + var processStyleID = function (style) { 3.926 + var icon = style.icon; 3.927 + if (!icon.href) return; 3.928 + 3.929 + if (icon.img && !icon.img.complete && (icon.dim.w < 0) && (icon.dim.h < 0) ) { 3.930 + // we're still waiting on the image loading (probably because we've been blocking since the declaration) 3.931 + // so, let's queue this function on the onload stack 3.932 + icon.markerBacklog = []; 3.933 + icon.img.onload = function() { 3.934 + if (icon.dim.w < 0 || icon.dim.h < 0) { 3.935 + icon.dim.w = this.width; 3.936 + icon.dim.h = this.height; 3.937 + } 3.938 + processStyleID(style); 3.939 + 3.940 + // we will undoubtedly get some createMarker queuing, so set this up in advance 3.941 + for (var i = 0; i < icon.markerBacklog.length; i++) { 3.942 + var p = icon.markerBacklog[i][0]; 3.943 + var d = icon.markerBacklog[i][1]; 3.944 + createMarker(p, d); 3.945 + if (p.marker) p.marker.active = true; 3.946 + } 3.947 + delete icon.markerBacklog; 3.948 + }; 3.949 + return; 3.950 + } 3.951 + else if (icon.dim.w < 0 || icon.dim.h < 0) { 3.952 + if (icon.img && icon.img.complete) { 3.953 + // sometimes the file is already cached and it never calls onLoad 3.954 + icon.dim.w = icon.img.width; 3.955 + icon.dim.h = icon.img.height; 3.956 + } 3.957 + else { 3.958 + // settle for a default of 32x32 3.959 + icon.dim.whGuess = true; 3.960 + icon.dim.w = 32; 3.961 + icon.dim.h = 32; 3.962 + } 3.963 + } 3.964 + 3.965 + // pre-scaled variables 3.966 + var rnd = Math.round; 3.967 + var scaled = { 3.968 + x: icon.dim.x * icon.scale, 3.969 + y: icon.dim.y * icon.scale, 3.970 + w: icon.dim.w * icon.scale, 3.971 + h: icon.dim.h * icon.scale, 3.972 + aX: icon.hotSpot.x * icon.scale, 3.973 + aY: icon.hotSpot.y * icon.scale, 3.974 + iW: (icon.img ? icon.img.width : icon.dim.w) * icon.scale, 3.975 + iH: (icon.img ? icon.img.height : icon.dim.h) * icon.scale 3.976 + }; 3.977 + 3.978 + // Figure out the anchor spot 3.979 + var aX, aY; 3.980 + switch (icon.hotSpot.xunits) { 3.981 + case 'fraction': aX = rnd(scaled.aX * icon.dim.w); break; 3.982 + case 'insetPixels': aX = rnd(icon.dim.w * icon.scale - scaled.aX); break; 3.983 + default: aX = rnd(scaled.aX); break; // already pixels 3.984 + } 3.985 + aY = rnd( ((icon.hotSpot.yunits === 'fraction') ? icon.dim.h : 1) * scaled.aY ); // insetPixels Y = pixels Y 3.986 + var iconAnchor = new google.maps.Point(aX, aY); 3.987 + 3.988 + // Sizes 3.989 + // (NOTE: Scale is applied to entire image, not just the section of the icon palette.) 3.990 + var iconSize = icon.dim.whGuess ? null : new google.maps.Size(rnd(scaled.w), rnd(scaled.h)); 3.991 + var iconScale = icon.scale == 1.0 ? null : 3.992 + icon.dim.whGuess ? new google.maps.Size(rnd(scaled.w), rnd(scaled.h)) 3.993 + : new google.maps.Size(rnd(scaled.iW), rnd(scaled.iH)); 3.994 + var iconOrigin = new google.maps.Point(rnd(scaled.x), rnd(scaled.y)); 3.995 + 3.996 + // Detect images buried in KMZ files (and use a base64 encoded URL) 3.997 + if (kmzMetaData[icon.url]) icon.url = kmzMetaData[icon.url].dataUrl; 3.998 + 3.999 + // Init the style object with the KML icon 3.1000 + icon.marker = new google.maps.MarkerImage( 3.1001 + icon.url, // url 3.1002 + iconSize, // size 3.1003 + iconOrigin, // origin 3.1004 + iconAnchor, // anchor 3.1005 + iconScale // scaledSize 3.1006 + ); 3.1007 + 3.1008 + // Look for a predictable shadow 3.1009 + var stdRegEx = /\/(red|blue|green|yellow|lightblue|purple|pink|orange)(-dot)?\.png/; 3.1010 + var shadowSize = new google.maps.Size(59, 32); 3.1011 + var shadowPoint = new google.maps.Point(16, 32); 3.1012 + if (stdRegEx.test(icon.href)) { 3.1013 + // A standard GMap-style marker icon 3.1014 + icon.shadow = new google.maps.MarkerImage( 3.1015 + 'http://maps.google.com/mapfiles/ms/micons/msmarker.shadow.png', // url 3.1016 + shadowSize, // size 3.1017 + null, // origin 3.1018 + shadowPoint, // anchor 3.1019 + shadowSize // scaledSize 3.1020 + ); 3.1021 + } else if (icon.href.indexOf('-pushpin.png') > -1) { 3.1022 + // Pushpin marker icon 3.1023 + icon.shadow = new google.maps.MarkerImage( 3.1024 + 'http://maps.google.com/mapfiles/ms/micons/pushpin_shadow.png', // url 3.1025 + shadowSize, // size 3.1026 + null, // origin 3.1027 + shadowPoint, // anchor 3.1028 + shadowSize // scaledSize 3.1029 + ); 3.1030 + } /* else { 3.1031 + // Other MyMaps KML standard icon 3.1032 + icon.shadow = new google.maps.MarkerImage( 3.1033 + icon.href.replace('.png', '.shadow.png'), // url 3.1034 + shadowSize, // size 3.1035 + null, // origin 3.1036 + anchorPoint, // anchor 3.1037 + shadowSize // scaledSize 3.1038 + ); 3.1039 + } */ 3.1040 + } 3.1041 + 3.1042 + var processStyles = function (doc) { 3.1043 + for (var styleID in doc.styles) { 3.1044 + processStyleID(doc.styles[styleID]); 3.1045 + } 3.1046 + }; 3.1047 + 3.1048 + var createMarker = function (placemark, doc) { 3.1049 + // create a Marker to the map from a placemark KML object 3.1050 + var icon = placemark.style.icon; 3.1051 + 3.1052 + if ( !icon.marker && icon.img ) { 3.1053 + // yay, single point of failure is holding up multiple markers... 3.1054 + icon.markerBacklog = icon.markerBacklog || []; 3.1055 + icon.markerBacklog.push([placemark, doc]); 3.1056 + return; 3.1057 + } 3.1058 + 3.1059 + // Load basic marker properties 3.1060 + var markerOptions = geoXML3.combineOptions(parserOptions.markerOptions, { 3.1061 + map: parserOptions.map, 3.1062 + position: new google.maps.LatLng(placemark.Point.coordinates[0].lat, placemark.Point.coordinates[0].lng), 3.1063 + title: placemark.name, 3.1064 + zIndex: Math.round(placemark.Point.coordinates[0].lat * -100000)<<5, 3.1065 + icon: icon.marker, 3.1066 + shadow: icon.shadow, 3.1067 + flat: !icon.shadow, 3.1068 + visible: placemark.visibility 3.1069 + }); 3.1070 + 3.1071 + // Create the marker on the map 3.1072 + var marker = new google.maps.Marker(markerOptions); 3.1073 + if (!!doc) doc.markers.push(marker); 3.1074 + 3.1075 + // Set up and create the infowindow if it is not suppressed 3.1076 + createInfoWindow(placemark, doc, marker); 3.1077 + placemark.marker = marker; 3.1078 + return marker; 3.1079 + }; 3.1080 + 3.1081 + var createOverlay = function (groundOverlay, doc) { 3.1082 + // Add a ProjectedOverlay to the map from a groundOverlay KML object 3.1083 + 3.1084 + if (!window.ProjectedOverlay) { 3.1085 + throw 'geoXML3 error: ProjectedOverlay not found while rendering GroundOverlay from KML'; 3.1086 + } 3.1087 + 3.1088 + var bounds = new google.maps.LatLngBounds( 3.1089 + new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west), 3.1090 + new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east) 3.1091 + ); 3.1092 + var overlayOptions = geoXML3.combineOptions(parserOptions.overlayOptions, {percentOpacity: groundOverlay.opacity*100}); 3.1093 + var overlay = new ProjectedOverlay(parserOptions.map, groundOverlay.icon.href, bounds, overlayOptions); 3.1094 + 3.1095 + if (!!doc) { 3.1096 + doc.ggroundoverlays = doc.ggroundoverlays || []; 3.1097 + doc.ggroundoverlays.push(overlay); 3.1098 + } 3.1099 + 3.1100 + return overlay; 3.1101 + }; 3.1102 + 3.1103 + // Create Polyline 3.1104 + var createPolyline = function(placemark, doc) { 3.1105 + var path = []; 3.1106 + for (var j=0; j<placemark.LineString.length; j++) { 3.1107 + var coords = placemark.LineString[j].coordinates; 3.1108 + var bounds = new google.maps.LatLngBounds(); 3.1109 + for (var i=0;i<coords.length;i++) { 3.1110 + var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng); 3.1111 + path.push(pt); 3.1112 + bounds.extend(pt); 3.1113 + } 3.1114 + } 3.1115 + // point to open the infowindow if triggered 3.1116 + var point = path[Math.floor(path.length/2)]; 3.1117 + // Load basic polyline properties 3.1118 + var kmlStrokeColor = kmlColor(placemark.style.line.color, placemark.style.line.colorMode); 3.1119 + var polyOptions = geoXML3.combineOptions(parserOptions.polylineOptions, { 3.1120 + map: parserOptions.map, 3.1121 + path: path, 3.1122 + strokeColor: kmlStrokeColor.color, 3.1123 + strokeWeight: placemark.style.line.width, 3.1124 + strokeOpacity: kmlStrokeColor.opacity, 3.1125 + title: placemark.name, 3.1126 + visible: placemark.visibility 3.1127 + }); 3.1128 + var p = new google.maps.Polyline(polyOptions); 3.1129 + p.bounds = bounds; 3.1130 + 3.1131 + // setup and create the infoWindow if it is not suppressed 3.1132 + createInfoWindow(placemark, doc, p); 3.1133 + if (!!doc) doc.gpolylines.push(p); 3.1134 + placemark.polyline = p; 3.1135 + return p; 3.1136 + } 3.1137 + 3.1138 + // Create Polygon 3.1139 + var createPolygon = function(placemark, doc) { 3.1140 + var bounds = new google.maps.LatLngBounds(); 3.1141 + var pathsLength = 0; 3.1142 + var paths = []; 3.1143 + for (var polygonPart=0;polygonPart<placemark.Polygon.length;polygonPart++) { 3.1144 + for (var j=0; j<placemark.Polygon[polygonPart].outerBoundaryIs.length; j++) { 3.1145 + var coords = placemark.Polygon[polygonPart].outerBoundaryIs[j].coordinates; 3.1146 + var path = []; 3.1147 + for (var i=0;i<coords.length;i++) { 3.1148 + var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng); 3.1149 + path.push(pt); 3.1150 + bounds.extend(pt); 3.1151 + } 3.1152 + paths.push(path); 3.1153 + pathsLength += path.length; 3.1154 + } 3.1155 + for (var j=0; j<placemark.Polygon[polygonPart].innerBoundaryIs.length; j++) { 3.1156 + var coords = placemark.Polygon[polygonPart].innerBoundaryIs[j].coordinates; 3.1157 + var path = []; 3.1158 + for (var i=0;i<coords.length;i++) { 3.1159 + var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng); 3.1160 + path.push(pt); 3.1161 + bounds.extend(pt); 3.1162 + } 3.1163 + paths.push(path); 3.1164 + pathsLength += path.length; 3.1165 + } 3.1166 + } 3.1167 + 3.1168 + // Load basic polygon properties 3.1169 + var kmlStrokeColor = kmlColor(placemark.style.line.color, placemark.style.line.colorMode); 3.1170 + var kmlFillColor = kmlColor(placemark.style.poly.color, placemark.style.poly.colorMode); 3.1171 + if (!placemark.style.poly.fill) kmlFillColor.opacity = 0.0; 3.1172 + var strokeWeight = placemark.style.line.width; 3.1173 + if (!placemark.style.poly.outline) { 3.1174 + strokeWeight = 0; 3.1175 + kmlStrokeColor.opacity = 0.0; 3.1176 + } 3.1177 + var polyOptions = geoXML3.combineOptions(parserOptions.polygonOptions, { 3.1178 + map: parserOptions.map, 3.1179 + paths: paths, 3.1180 + title: placemark.name, 3.1181 + strokeColor: kmlStrokeColor.color, 3.1182 + strokeWeight: strokeWeight, 3.1183 + strokeOpacity: kmlStrokeColor.opacity, 3.1184 + fillColor: kmlFillColor.color, 3.1185 + fillOpacity: kmlFillColor.opacity, 3.1186 + visible: placemark.visibility 3.1187 + }); 3.1188 + var p = new google.maps.Polygon(polyOptions); 3.1189 + p.bounds = bounds; 3.1190 + 3.1191 + createInfoWindow(placemark, doc, p); 3.1192 + if (!!doc) doc.gpolygons.push(p); 3.1193 + placemark.polygon = p; 3.1194 + return p; 3.1195 + } 3.1196 + 3.1197 + var createInfoWindow = function(placemark, doc, gObj) { 3.1198 + var bStyle = placemark.style.balloon; 3.1199 + var vars = placemark.vars; 3.1200 + 3.1201 + if (!placemark.balloonVisibility || bStyle.displayMode === 'hide') return; 3.1202 + 3.1203 + // define geDirections 3.1204 + if (placemark.latlng) { 3.1205 + vars.directions.push('sll=' + placemark.latlng.toUrlValue()); 3.1206 + 3.1207 + var url = 'http://maps.google.com/maps?' + vars.directions.join('&'); 3.1208 + var address = encodeURIComponent( vars.val.address || placemark.latlng.toUrlValue() ).replace(/\%20/g, '+'); 3.1209 + 3.1210 + vars.val.geDirections = '<a href="' + url + '&daddr=' + address + '" target=_blank>To Here</a> - <a href="' + url + '&saddr=' + address + '" target=_blank>From Here</a>'; 3.1211 + } 3.1212 + else vars.val.geDirections = ''; 3.1213 + 3.1214 + // add in the variables 3.1215 + var iwText = bStyle.text.replace(/\$\[(\w+(\/displayName)?)\]/g, function(txt, n, dn) { return dn ? vars.display[n] : vars.val[n]; }); 3.1216 + var classTxt = 'geoxml3_infowindow geoxml3_style_' + placemark.styleID; 3.1217 + 3.1218 + // color styles 3.1219 + var styleArr = []; 3.1220 + if (bStyle.bgColor != 'ffffffff') styleArr.push('background: ' + kmlColor(bStyle.bgColor ).color + ';'); 3.1221 + if (bStyle.textColor != 'ff000000') styleArr.push('color: ' + kmlColor(bStyle.textColor).color + ';'); 3.1222 + var styleProp = styleArr.length ? ' style="' + styleArr.join(' ') + '"' : ''; 3.1223 + 3.1224 + var infoWindowOptions = geoXML3.combineOptions(parserOptions.infoWindowOptions, { 3.1225 + content: '<div class="' + classTxt + '"' + styleProp + '>' + iwText + '</div>', 3.1226 + pixelOffset: new google.maps.Size(0, 2) 3.1227 + }); 3.1228 + 3.1229 + gObj.infoWindow = parserOptions.infoWindow || new google.maps.InfoWindow(infoWindowOptions); 3.1230 + gObj.infoWindowOptions = infoWindowOptions; 3.1231 + 3.1232 + // Info Window-opening event handler 3.1233 + google.maps.event.addListener(gObj, 'click', function(e) { 3.1234 + var iW = this.infoWindow; 3.1235 + iW.close(); 3.1236 + iW.setOptions(this.infoWindowOptions); 3.1237 + 3.1238 + if (e && e.latLng) iW.setPosition(e.latLng); 3.1239 + else if (this.bounds) iW.setPosition(this.bounds.getCenter()); 3.1240 + 3.1241 + iW.setContent("<div id='geoxml3_infowindow'>"+iW.getContent()+"</div>"); 3.1242 + google.maps.event.addListenerOnce(iW, "domready", function() { 3.1243 + var node = document.getElementById('geoxml3_infowindow'); 3.1244 + var imgArray = node.getElementsByTagName('img'); 3.1245 + for (var i = 0; i < imgArray.length; i++) 3.1246 + { 3.1247 + var imgUrlIE = imgArray[i].getAttribute("src"); 3.1248 + var imgUrl = cleanURL(doc.baseDir, imgUrlIE); 3.1249 + 3.1250 + if (kmzMetaData[imgUrl]) { 3.1251 + imgArray[i].src = kmzMetaData[imgUrl].dataUrl; 3.1252 + } else if (kmzMetaData[imgUrlIE]) { 3.1253 + imgArray[i].src = kmzMetaData[imgUrlIE].dataUrl; 3.1254 + } 3.1255 + } 3.1256 + }); 3.1257 + iW.open(this.map, this.bounds ? null : this); 3.1258 + }); 3.1259 + 3.1260 + } 3.1261 + 3.1262 + return { 3.1263 + // Expose some properties and methods 3.1264 + 3.1265 + options: parserOptions, 3.1266 + docs: docs, 3.1267 + docsByUrl: docsByUrl, 3.1268 + kmzMetaData: kmzMetaData, 3.1269 + 3.1270 + parse: parse, 3.1271 + parseKmlString: parseKmlString, 3.1272 + hideDocument: hideDocument, 3.1273 + showDocument: showDocument, 3.1274 + processStyles: processStyles, 3.1275 + createMarker: createMarker, 3.1276 + createOverlay: createOverlay, 3.1277 + createPolyline: createPolyline, 3.1278 + createPolygon: createPolygon 3.1279 + }; 3.1280 +}; 3.1281 +// End of KML Parser 3.1282 + 3.1283 +// Helper objects and functions 3.1284 +geoXML3.getOpacity = function (kmlColor) { 3.1285 + // Extract opacity encoded in a KML color value. Returns a number between 0 and 1. 3.1286 + if (!!kmlColor && 3.1287 + (kmlColor !== '') && 3.1288 + (kmlColor.length == 8)) { 3.1289 + var transparency = parseInt(kmlColor.substr(0, 2), 16); 3.1290 + return transparency / 255; 3.1291 + } else { 3.1292 + return 1; 3.1293 + } 3.1294 +}; 3.1295 + 3.1296 +// Log a message to the debugging console, if one exists 3.1297 +geoXML3.log = function(msg) { 3.1298 + if (!!window.console) { 3.1299 + console.log(msg); 3.1300 + } else { alert("log:"+msg); } 3.1301 +}; 3.1302 + 3.1303 +/** 3.1304 + * Creates a new parserOptions object. 3.1305 + * @class GeoXML3 parser options. 3.1306 + * @param {Object} overrides Any options you want to declare outside of the defaults should be included here. 3.1307 + * @property {google.maps.Map} map The API map on which geo objects should be rendered. 3.1308 + * @property {google.maps.MarkerOptions} markerOptions If the parser is adding Markers to the map itself, any options specified here will be applied to them. 3.1309 + * @property {google.maps.InfoWindowOptions} infoWindowOptions If the parser is adding Markers to the map itself, any options specified here will be applied to their attached InfoWindows. 3.1310 + * @property {ProjectedOverlay.options} overlayOptions If the parser is adding ProjectedOverlays to the map itself, any options specified here will be applied to them. 3.1311 + */ 3.1312 +geoXML3.parserOptions = function (overrides) { 3.1313 + this.map = null, 3.1314 + /** If true, the parser will automatically move the map to a best-fit of the geodata after parsing of a KML document completes. 3.1315 + * @type Boolean 3.1316 + * @default true 3.1317 + */ 3.1318 + this.zoom = true, 3.1319 + /**#@+ @type Boolean 3.1320 + * @default false */ 3.1321 + /** If true, only a single Marker created by the parser will be able to have its InfoWindow open at once (simulating the behavior of GMaps API v2). */ 3.1322 + this.singleInfoWindow = false, 3.1323 + /** If true, suppresses the rendering of info windows. */ 3.1324 + this.suppressInfoWindows = false, 3.1325 + /** 3.1326 + * Control whether to process styles now or later. 3.1327 + * 3.1328 + * <p>By default, the parser only processes KML <Style> elements into their GMaps equivalents 3.1329 + * if it will be creating its own Markers (the createMarker option is null). Setting this option 3.1330 + * to true will force such processing to happen anyway, useful if you're going to be calling parser.createMarker 3.1331 + * yourself later. OTOH, leaving this option false removes runtime dependency on the GMaps API, enabling 3.1332 + * the use of geoXML3 as a standalone KML parser.</p> 3.1333 + */ 3.1334 + this.processStyles = false, 3.1335 + /**#@-*/ 3.1336 + 3.1337 + this.markerOptions = {}, 3.1338 + this.infoWindowOptions = {}, 3.1339 + this.overlayOptions = {}, 3.1340 + 3.1341 + /**#@+ @event */ 3.1342 + /** This function will be called when parsing of a KML document is complete. 3.1343 + * @param {geoXML3.parser#docs} doc Parsed KML data. */ 3.1344 + this.afterParse = null, 3.1345 + /** This function will be called when parsing of a KML document is complete. 3.1346 + * @param {geoXML3.parser#docs} doc Parsed KML data. */ 3.1347 + this.failedParse = null, 3.1348 + /** 3.1349 + * If supplied, this function will be called once for each marker <Placemark> in the KML document, instead of the parser adding its own Marker to the map. 3.1350 + * @param {geoXML3.parser.render#placemark} placemark Placemark object. 3.1351 + * @param {geoXML3.parser#docs} doc Parsed KML data. 3.1352 + */ 3.1353 + this.createMarker = null, 3.1354 + /** 3.1355 + * If supplied, this function will be called once for each <GroundOverlay> in the KML document, instead of the parser adding its own ProjectedOverlay to the map. 3.1356 + * @param {geoXML3.parser.render#groundOverlay} groundOverlay GroundOverlay object. 3.1357 + * @param {geoXML3.parser#docs} doc Parsed KML data. 3.1358 + */ 3.1359 + this.createOverlay = null 3.1360 + /**#@-*/ 3.1361 + 3.1362 + if (overrides) { 3.1363 + for (var prop in overrides) { 3.1364 + if (overrides.hasOwnProperty(prop)) this[prop] = overrides[prop]; 3.1365 + } 3.1366 + } 3.1367 + return this; 3.1368 +}; 3.1369 + 3.1370 +/** 3.1371 + * Combine two options objects: a set of default values and a set of override values. 3.1372 + * 3.1373 + * @deprecated This has been replaced with {@link geoXML3.parserOptions#combineOptions}. 3.1374 + * @param {geoXML3.parserOptions|Object} overrides Override values. 3.1375 + * @param {geoXML3.parserOptions|Object} defaults Default values. 3.1376 + * @return {geoXML3.parserOptions} Combined result. 3.1377 + */ 3.1378 +geoXML3.combineOptions = function (overrides, defaults) { 3.1379 + var result = {}; 3.1380 + if (!!overrides) { 3.1381 + for (var prop in overrides) { 3.1382 + if (overrides.hasOwnProperty(prop)) result[prop] = overrides[prop]; 3.1383 + } 3.1384 + } 3.1385 + if (!!defaults) { 3.1386 + for (prop in defaults) { 3.1387 + if (defaults.hasOwnProperty(prop) && result[prop] === undefined) result[prop] = defaults[prop]; 3.1388 + } 3.1389 + } 3.1390 + return result; 3.1391 +}; 3.1392 + 3.1393 +/** 3.1394 + * Combine two options objects: a set of default values and a set of override values. 3.1395 + * 3.1396 + * @function 3.1397 + * @param {geoXML3.parserOptions|Object} overrides Override values. 3.1398 + * @param {geoXML3.parserOptions|Object} defaults Default values. 3.1399 + * @return {geoXML3.parserOptions} Combined result. 3.1400 + */ 3.1401 +geoXML3.parserOptions.prototype.combineOptions = geoXML3.combineOptions; 3.1402 + 3.1403 +// Retrieve an XML document from url and pass it to callback as a DOM document 3.1404 +geoXML3.fetchers = []; 3.1405 + 3.1406 +/** 3.1407 + * Parses a XML string. 3.1408 + * 3.1409 + * <p>Parses the given XML string and returns the parsed document in a 3.1410 + * DOM data structure. This function will return an empty DOM node if 3.1411 + * XML parsing is not supported in this browser.</p> 3.1412 + * 3.1413 + * @param {String} str XML string. 3.1414 + * @return {Element|Document} DOM. 3.1415 + */ 3.1416 +geoXML3.xmlParse = function (str) { 3.1417 + if (typeof ActiveXObject != 'undefined' && typeof GetObject != 'undefined') { 3.1418 + var doc = new ActiveXObject('Microsoft.XMLDOM'); 3.1419 + doc.loadXML(str); 3.1420 + return doc; 3.1421 + } 3.1422 + 3.1423 + if (typeof DOMParser != 'undefined') { 3.1424 + return (new DOMParser()).parseFromString(str, 'text/xml'); 3.1425 + } 3.1426 + 3.1427 + return createElement('div', null); 3.1428 +} 3.1429 + 3.1430 +/** 3.1431 + * Fetches a XML document. 3.1432 + * 3.1433 + * <p>Fetches/parses the given XML URL and passes the parsed document (in a 3.1434 + * DOM data structure) to the given callback. Documents are downloaded 3.1435 + * and parsed asynchronously.</p> 3.1436 + * 3.1437 + * @param {String} url URL of XML document. Must be uncompressed XML only. 3.1438 + * @param {Function(Document)} callback Function to call when the document is processed. 3.1439 + */ 3.1440 +geoXML3.fetchXML = function (url, callback) { 3.1441 + function timeoutHandler() { callback(); }; 3.1442 + 3.1443 + var xhrFetcher = new Object(); 3.1444 + if (!!geoXML3.fetchers.length) xhrFetcher = geoXML3.fetchers.pop(); 3.1445 + else if (!!window.XMLHttpRequest) xhrFetcher.fetcher = new window.XMLHttpRequest(); // Most browsers 3.1446 + else if (!!window.ActiveXObject) { // Some IE 3.1447 + // the many versions of IE's XML fetchers 3.1448 + var AXOs = [ 3.1449 + 'MSXML2.XMLHTTP.6.0', 3.1450 + 'MSXML2.XMLHTTP.5.0', 3.1451 + 'MSXML2.XMLHTTP.4.0', 3.1452 + 'MSXML2.XMLHTTP.3.0', 3.1453 + 'MSXML2.XMLHTTP', 3.1454 + 'Microsoft.XMLHTTP', 3.1455 + 'MSXML.XMLHTTP' 3.1456 + ]; 3.1457 + for (var i = 0; i < AXOs.length; i++) { 3.1458 + try { xhrFetcher.fetcher = new ActiveXObject(AXOs[i]); break; } 3.1459 + catch(e) { continue; } 3.1460 + } 3.1461 + if (!xhrFetcher.fetcher) { 3.1462 + geoXML3.log('Unable to create XHR object'); 3.1463 + callback(null); 3.1464 + return null; 3.1465 + } 3.1466 + } 3.1467 + 3.1468 + if (!!xhrFetcher.fetcher.overrideMimeType) xhrFetcher.fetcher.overrideMimeType('text/xml'); 3.1469 + xhrFetcher.fetcher.open('GET', url, true); 3.1470 + xhrFetcher.fetcher.onreadystatechange = function () { 3.1471 + if (xhrFetcher.fetcher.readyState === 4) { 3.1472 + // Retrieval complete 3.1473 + if (!!xhrFetcher.xhrtimeout) clearTimeout(xhrFetcher.xhrtimeout); 3.1474 + if (xhrFetcher.fetcher.status >= 400) { 3.1475 + geoXML3.log('HTTP error ' + xhrFetcher.fetcher.status + ' retrieving ' + url); 3.1476 + callback(); 3.1477 + } 3.1478 + // Returned successfully 3.1479 + else { 3.1480 + if (xhrFetcher.fetcher.responseXML) { 3.1481 + // Sometimes IE will get the data, but won't bother loading it as an XML doc 3.1482 + var xmlDoc = xhrFetcher.fetcher.responseXML; 3.1483 + if (xmlDoc && !xmlDoc.documentElement && !xmlDoc.ownerElement) xmlDoc.loadXML(xhrFetcher.fetcher.responseText); 3.1484 + callback(xmlDoc); 3.1485 + } else // handle valid xml sent with wrong MIME type 3.1486 + callback(geoXML3.xmlParse(xhrFetcher.fetcher.responseText)); 3.1487 + } 3.1488 + 3.1489 + // We're done with this fetcher object 3.1490 + geoXML3.fetchers.push(xhrFetcher); 3.1491 + } 3.1492 + }; 3.1493 + 3.1494 + xhrFetcher.xhrtimeout = setTimeout(timeoutHandler, 60000); 3.1495 + xhrFetcher.fetcher.send(null); 3.1496 + return null; 3.1497 +}; 3.1498 + 3.1499 +var IEversion = function() { 3.1500 + // http://msdn.microsoft.com/workshop/author/dhtml/overview/browserdetection.asp 3.1501 + // Returns the version of Internet Explorer or a -1 3.1502 + // (indicating the use of another browser). 3.1503 + var rv = -1; // Return value assumes failure 3.1504 + if (navigator.appName == 'Microsoft Internet Explorer') { 3.1505 + var ua = navigator.userAgent; 3.1506 + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); 3.1507 + if (re.exec(ua) != null) { 3.1508 + rv = parseFloat( RegExp.$1 ); 3.1509 + } 3.1510 + } 3.1511 + return rv; 3.1512 +}; 3.1513 + 3.1514 +/** 3.1515 + * Fetches a KMZ document. 3.1516 + * 3.1517 + * <p>Fetches/parses the given ZIP URL, parses each image file, and passes 3.1518 + * the parsed KML document to the given callback. Documents are downloaded 3.1519 + * and parsed asynchronously, though the KML file is always passed after the 3.1520 + * images have been processed, in case the callback requires the image data.</p> 3.1521 + * 3.1522 + * @requires ZipFile.complete.js 3.1523 + * @param {String} url URL of KMZ document. Must be a valid KMZ/ZIP archive. 3.1524 + * @param {Function(Document)} callback Function to call when the document is processed. 3.1525 + * @param {geoXML3.parser} parser A geoXML3.parser object. This is used to populate the KMZ image data. 3.1526 + * @author Brendan Byrd 3.1527 + * @see http://code.google.com/apis/kml/documentation/kmzarchives.html 3.1528 + */ 3.1529 +geoXML3.fetchZIP = function (url, callback, parser) { 3.1530 + // Just need a single 'new' declaration with a really long function... 3.1531 + var zipFile = new ZipFile(url, function (zip) { 3.1532 + // Retrieval complete 3.1533 + 3.1534 + // Check for ERRORs in zip.status 3.1535 + for (var i = 0; i < zip.status.length; i++) { 3.1536 + var msg = zip.status[i]; 3.1537 + if (msg.indexOf("ERROR") == 0) { 3.1538 + geoXML3.log('HTTP/ZIP error retrieving ' + url + ': ' + msg); 3.1539 + callback(); 3.1540 + return; 3.1541 + } 3.1542 + else if (msg.indexOf("WARNING") == 0) { // non-fatal, but still might be useful 3.1543 + geoXML3.log('HTTP/ZIP warning retrieving ' + url + ': ' + msg); 3.1544 + } 3.1545 + } 3.1546 + 3.1547 + // Make sure KMZ structure is according to spec (with a single KML file in the root dir) 3.1548 + var KMLCount = 0; 3.1549 + var KML; 3.1550 + for (var i = 0; i < zip.entries.length; i++) { 3.1551 + var name = zip.entries[i].name; 3.1552 + if (!/\.kml$/.test(name)) continue; 3.1553 + 3.1554 + KMLCount++; 3.1555 + if (KMLCount == 1) KML = i; 3.1556 + else { 3.1557 + geoXML3.log('KMZ warning retrieving ' + url + ': found extra KML "' + name + '" in KMZ; discarding...'); 3.1558 + } 3.1559 + } 3.1560 + 3.1561 + // Returned successfully, but still needs extracting 3.1562 + var baseUrl = cleanURL(defileURL(url), url) + '/'; 3.1563 + var kmlProcessing = { // this is an object just so it gets passed properly 3.1564 + timer: null, 3.1565 + extractLeft: 0, 3.1566 + timerCalls: 0 3.1567 + }; 3.1568 + var extractCb = function(entry, entryContent) { 3.1569 + var mdUrl = cleanURL(baseUrl, entry.name); 3.1570 + var ext = entry.name.substring(entry.name.lastIndexOf(".") + 1).toLowerCase(); 3.1571 + kmlProcessing.extractLeft--; 3.1572 + 3.1573 + if ((typeof entryContent.description == "string") && (entryContent.name == "Error")) { 3.1574 + geoXML3.log('KMZ error extracting ' + mdUrl + ': ' + entryContent.description); 3.1575 + callback(); 3.1576 + return; 3.1577 + } 3.1578 + 3.1579 + // MIME types that can be used in KML 3.1580 + var mime; 3.1581 + if (ext === 'jpg') ext = 'jpeg'; 3.1582 + if (/^(gif|jpeg|png)$/.test(ext)) mime = 'image/' + ext; 3.1583 + else if (ext === 'mp3') mime = 'audio/mpeg'; 3.1584 + else if (ext === 'm4a') mime = 'audio/mp4'; 3.1585 + else if (ext === 'm4a') mime = 'audio/MP4-LATM'; 3.1586 + else mime = 'application/octet-stream'; 3.1587 + 3.1588 + parser.kmzMetaData[mdUrl] = {}; 3.1589 + parser.kmzMetaData[mdUrl].entry = entry; 3.1590 + // data:image/gif;base64,R0lGODlhEAAOALMA... 3.1591 + parser.kmzMetaData[mdUrl].dataUrl = 'data:' + mime + ';base64,' + base64Encode(entryContent); 3.1592 + // IE cannot handle GET requests beyond 2071 characters, even if it's an inline image 3.1593 + if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) 3.1594 + { 3.1595 + if (((IEversion() < 8.0) && 3.1596 + (parser.kmzMetaData[mdUrl].dataUrl.length > 2071)) || 3.1597 + ((IEversion < 9.0) && 3.1598 + (parser.kmzMetaData[mdUrl].dataUrl.length > 32767))) { 3.1599 + parser.kmzMetaData[mdUrl].dataUrl = 3.1600 + // this is a simple IE icon; to hint at the problem... 3.1601 + 'data:image/gif;base64,R0lGODlhDwAQAOMPADBPvSpQ1Dpoyz1p6FhwvU2A6ECP63CM04CWxYCk+V6x+UK++Jao3rvC3fj7+v///yH5BAEKAA8ALAAAAAAPABAAAASC8Mk5mwCAUMlWwcLRHEelLA' + 3.1602 + 'oGDMgzSsiyGCAhCETDPMh5XQCBwYBrNBIKWmg0MCQHj8MJU5IoroYCY6AAAgrDIbbQDGIK6DR5UPhlNo0JAlSUNAiDgH7eNAxEDWAKCQM2AAFheVxYAA0AIkFOJ1gBcQQaUQKKA5w7LpcEBwkJaKMUEQA7'; 3.1603 + } 3.1604 + } 3.1605 + parser.kmzMetaData[internalSrc(entry.name)]=parser.kmzMetaData[mdUrl]; 3.1606 + 3.1607 + }; 3.1608 + var kmlExtractCb = function(entry, entryContent) { 3.1609 + if ((typeof entryContent.description == "string") && (entryContent.name == "Error")) { 3.1610 + geoXML3.log('KMZ error extracting ' + mdUrl + ': ' + entryContent.description); 3.1611 + callback(); 3.1612 + return; 3.1613 + } 3.1614 + 3.1615 + // check to see if the KML is the last file extracted 3.1616 + clearTimeout(kmlProcessing.timer); 3.1617 + if (kmlProcessing.extractLeft <= 1) { 3.1618 + kmlProcessing.extractLeft--; 3.1619 + callback(geoXML3.xmlParse(entryContent)); 3.1620 + return; 3.1621 + } 3.1622 + else { 3.1623 + // KML file isn't last yet; it may need to use those files, so wait a bit (100ms) 3.1624 + kmlProcessing.timerCalls++; 3.1625 + if (kmlProcessing.timerCalls < 100) { 3.1626 + kmlProcessing.timer = setTimeout(function() { kmlExtractCb(entry, entryContent); }, 100); 3.1627 + } 3.1628 + else { 3.1629 + geoXML3.log('KMZ warning extracting ' + url + ': entire ZIP has not been extracted after 10 seconds; running through KML, anyway...'); 3.1630 + kmlProcessing.extractLeft--; 3.1631 + callback(geoXML3.xmlParse(entryContent)); 3.1632 + } 3.1633 + } 3.1634 + return; 3.1635 + }; 3.1636 + for (var i = 0; i < zip.entries.length; i++) { 3.1637 + var entry = zip.entries[i]; 3.1638 + var ext = entry.name.substring(entry.name.lastIndexOf(".") + 1).toLowerCase(); 3.1639 + if (!/^(gif|jpe?g|png|kml)$/.test(ext)) continue; // not going to bother to extract files we don't support 3.1640 + if (ext === "kml" && i != KML) continue; // extra KMLs get discarded 3.1641 + if (!parser && ext != "kml") continue; // cannot store images without a parser object 3.1642 + 3.1643 + // extract asynchronously 3.1644 + kmlProcessing.extractLeft++; 3.1645 + if (ext === "kml") entry.extract(kmlExtractCb); 3.1646 + else entry.extract(extractCb); 3.1647 + } 3.1648 + }); 3.1649 + 3.1650 +}; 3.1651 + 3.1652 +/** 3.1653 + * Extract the text value of a DOM node, with leading and trailing whitespace trimmed. 3.1654 + * 3.1655 + * @param {Element} node XML node/element. 3.1656 + * @param {Any} delVal Default value if the node doesn't exist. 3.1657 + * @return {String|Null} 3.1658 + */ 3.1659 +geoXML3.nodeValue = function(node, defVal) { 3.1660 + var retStr=""; 3.1661 + if (!node) { 3.1662 + return (typeof defVal === 'undefined' || defVal === null) ? null : defVal; 3.1663 + } 3.1664 + if(node.nodeType==3||node.nodeType==4||node.nodeType==2){ 3.1665 + retStr+=node.nodeValue; 3.1666 + }else if(node.nodeType==1||node.nodeType==9||node.nodeType==11){ 3.1667 + for(var i=0;i<node.childNodes.length;++i){ 3.1668 + retStr+=arguments.callee(node.childNodes[i]); 3.1669 + } 3.1670 + } 3.1671 + return retStr; 3.1672 +}; 3.1673 + 3.1674 +/** 3.1675 + * Loosely translate various values of a DOM node to a boolean. 3.1676 + * 3.1677 + * @param {Element} node XML node/element. 3.1678 + * @param {Boolean} delVal Default value if the node doesn't exist. 3.1679 + * @return {Boolean|Null} 3.1680 + */ 3.1681 +geoXML3.getBooleanValue = function(node, defVal) { 3.1682 + var nodeContents = geoXML3.nodeValue(node); 3.1683 + if (nodeContents === null) return defVal || false; 3.1684 + nodeContents = parseInt(nodeContents); 3.1685 + if (isNaN(nodeContents)) return true; 3.1686 + if (nodeContents == 0) return false; 3.1687 + else return true; 3.1688 +} 3.1689 + 3.1690 +/** 3.1691 + * Browser-normalized version of getElementsByTagNameNS. 3.1692 + * 3.1693 + * <p>Required because IE8 doesn't define it.</p> 3.1694 + * 3.1695 + * @param {Element|Document} node DOM object. 3.1696 + * @param {String} namespace Full namespace URL to search against. 3.1697 + * @param {String} tagname XML local tag name. 3.1698 + * @return {Array of Elements} 3.1699 + * @author Brendan Byrd 3.1700 + */ 3.1701 +geoXML3.getElementsByTagNameNS = function(node, namespace, tagname) { 3.1702 + if (node && typeof node.getElementsByTagNameNS != 'undefined') return node.getElementsByTagNameNS(namespace, tagname); 3.1703 + if (!node) return []; 3.1704 + 3.1705 + var root = node.documentElement || node.ownerDocument && node.ownerDocument.documentElement; 3.1706 + if (!root || !root.attributes) return []; 3.1707 + 3.1708 + // search for namespace prefix 3.1709 + for (var i = 0; i < root.attributes.length; i++) { 3.1710 + var attr = root.attributes[i]; 3.1711 + if (attr.prefix === 'xmlns' && attr.nodeValue === namespace) return node.getElementsByTagName(attr.baseName + ':' + tagname); 3.1712 + else if (attr.nodeName === 'xmlns' && attr.nodeValue === namespace) { 3.1713 + // default namespace 3.1714 + if (typeof node.selectNodes != 'undefined') { 3.1715 + // Newer IEs have the SelectionNamespace property that can be used with selectNodes 3.1716 + if (!root.ownerDocument.getProperty('SelectionNamespaces')) 3.1717 + root.ownerDocument.setProperty('SelectionNamespaces', "xmlns:defaultNS='" + namespace + "'"); 3.1718 + return node.selectNodes('.//defaultNS:' + tagname); 3.1719 + } 3.1720 + else { 3.1721 + // Otherwise, you can still try to tack on the 'xmlns' attribute to root 3.1722 + root.setAttribute('xmlns:defaultNS', namespace); 3.1723 + return node.getElementsByTagName('defaultNS:' + tagname); 3.1724 + } 3.1725 + } 3.1726 + } 3.1727 + return geoXML3.getElementsByTagName(node, tagname); // try the unqualified version 3.1728 +}; 3.1729 + 3.1730 +/** 3.1731 + * Browser-normalized version of getElementsByTagName. 3.1732 + * 3.1733 + * <p>Required because MSXML 6.0 will treat this function as a NS-qualified function, 3.1734 + * despite the missing NS parameter.</p> 3.1735 + * 3.1736 + * @param {Element|Document} node DOM object. 3.1737 + * @param {String} tagname XML local tag name. 3.1738 + * @return {Array of Elements} 3.1739 + * @author Brendan Byrd 3.1740 + */ 3.1741 +geoXML3.getElementsByTagName = function(node, tagname) { 3.1742 + if (node && typeof node.getElementsByTagNameNS != 'undefined') return node.getElementsByTagName(tagname); // if it has both functions, it should be accurate 3.1743 +// if (node && typeof node.selectNodes != 'undefined') return node.selectNodes(".//*[local-name()='" + tagname + "']"); 3.1744 + return node.getElementsByTagName(tagname); // hope for the best... 3.1745 +} 3.1746 + 3.1747 +/** 3.1748 + * Turn a directory + relative URL into an absolute one. 3.1749 + * 3.1750 + * @private 3.1751 + * @param {String} d Base directory. 3.1752 + * @param {String} s Relative URL. 3.1753 + * @return {String} Absolute URL. 3.1754 + * @author Brendan Byrd 3.1755 + */ 3.1756 +var toAbsURL = function (d, s) { 3.1757 + var p, f, i; 3.1758 + var h = location.protocol + "://" + location.host; 3.1759 + 3.1760 + if (!s.length) return ''; 3.1761 + if (/^\w+:/.test(s)) return s; 3.1762 + if (s.indexOf('/') == 0) return h + s; 3.1763 + 3.1764 + p = d.replace(/\/[^\/]*$/, ''); 3.1765 + f = s.match(/\.\.\//g); 3.1766 + if (f) { 3.1767 + s = s.substring(f.length * 3); 3.1768 + for (i = f.length; i--;) { p = p.substring(0, p.lastIndexOf('/')); } 3.1769 + } 3.1770 + 3.1771 + return h + p + '/' + s; 3.1772 +} 3.1773 + 3.1774 +var internalSrc = function(src) { 3.1775 + //this gets the full url 3.1776 + var url = document.location.href; 3.1777 + //this removes everything after the last slash in the path 3.1778 + url = url.substring(0,url.lastIndexOf("/") + 1); 3.1779 + var internalPath= url+src; 3.1780 + return internalPath; 3.1781 +} 3.1782 + 3.1783 +/** 3.1784 + * Remove current host from URL 3.1785 + * 3.1786 + * @private 3.1787 + * @param {String} s Absolute or relative URL. 3.1788 + * @return {String} Root-based relative URL. 3.1789 + * @author Brendan Byrd 3.1790 + */ 3.1791 +var dehostURL = function (s) { 3.1792 + var h = location.protocol + "://" + location.host; 3.1793 + h = h.replace(/([\.\\\+\*\?\[\^\]\$\(\)])/g, '\\$1'); // quotemeta 3.1794 + return s.replace(new RegExp('^' + h, 'i'), ''); 3.1795 +} 3.1796 + 3.1797 +/** 3.1798 + * Removes all query strings, #IDs, '../' references, and 3.1799 + * hosts from a URL. 3.1800 + * 3.1801 + * @private 3.1802 + * @param {String} d Base directory. 3.1803 + * @param {String} s Absolute or relative URL. 3.1804 + * @return {String} Root-based relative URL. 3.1805 + * @author Brendan Byrd 3.1806 + */ 3.1807 +var cleanURL = function (d, s) { return dehostURL(toAbsURL(d ? d.split('#')[0].split('?')[0] : defileURL(location.pathname), s ? s.split('#')[0].split('?')[0] : '')); } 3.1808 +/** 3.1809 + * Remove filename from URL 3.1810 + * 3.1811 + * @private 3.1812 + * @param {String} s Relative URL. 3.1813 + * @return {String} Base directory. 3.1814 + * @author Brendan Byrd 3.1815 + */ 3.1816 +var defileURL = function (s) { return s ? s.substr(0, s.lastIndexOf('/') + 1) : '/'; } 3.1817 + 3.1818 + 3.1819 +// Some extra Array subs for ease of use 3.1820 +// http://stackoverflow.com/questions/143847/best-way-to-find-an-item-in-a-javascript-array 3.1821 +Array.prototype.hasObject = ( 3.1822 + !Array.indexOf ? function (obj) { 3.1823 + var l = this.length + 1; 3.1824 + while (l--) { 3.1825 + if (this[l - 1] === obj) return true; 3.1826 + } 3.1827 + return false; 3.1828 + } : function (obj) { 3.1829 + return (this.indexOf(obj) !== -1); 3.1830 + } 3.1831 +); 3.1832 +Array.prototype.hasItemInObj = function (name, item) { 3.1833 + var l = this.length + 1; 3.1834 + while (l--) { 3.1835 + if (this[l - 1][name] === item) return true; 3.1836 + } 3.1837 + return false; 3.1838 +}; 3.1839 +if (!Array.prototype.indexOf) { 3.1840 + Array.prototype.indexOf = function (obj, fromIndex) { 3.1841 + if (fromIndex == null) { 3.1842 + fromIndex = 0; 3.1843 + } else if (fromIndex < 0) { 3.1844 + fromIndex = Math.max(0, this.length + fromIndex); 3.1845 + } 3.1846 + for (var i = fromIndex, j = this.length; i < j; i++) { 3.1847 + if (this[i] === obj) return i; 3.1848 + } 3.1849 + return -1; 3.1850 + }; 3.1851 +} 3.1852 +Array.prototype.indexOfObjWithItem = function (name, item, fromIndex) { 3.1853 + if (fromIndex == null) { 3.1854 + fromIndex = 0; 3.1855 + } else if (fromIndex < 0) { 3.1856 + fromIndex = Math.max(0, this.length + fromIndex); 3.1857 + } 3.1858 + for (var i = fromIndex, j = this.length; i < j; i++) { 3.1859 + if (this[i][name] === item) return i; 3.1860 + } 3.1861 + return -1; 3.1862 +}; 3.1863 + 3.1864 +/** 3.1865 + * Borrowed from jquery.base64.js, with some "Array as input" corrections 3.1866 + * 3.1867 + * @private 3.1868 + * @param {Array of charCodes} input An array of byte ASCII codes (0-255). 3.1869 + * @return {String} A base64-encoded string. 3.1870 + * @author Brendan Byrd 3.1871 + */ 3.1872 +var base64Encode = function(input) { 3.1873 + var keyString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 3.1874 + var output = ""; 3.1875 + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 3.1876 + var i = 0; 3.1877 + while (i < input.length) { 3.1878 + chr1 = input[i++]; 3.1879 + chr2 = input[i++]; 3.1880 + chr3 = input[i++]; 3.1881 + enc1 = chr1 >> 2; 3.1882 + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 3.1883 + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 3.1884 + enc4 = chr3 & 63; 3.1885 + 3.1886 + if (chr2 == undefined) enc3 = enc4 = 64; 3.1887 + else if (chr3 == undefined) enc4 = 64; 3.1888 + 3.1889 + output = output + keyString.charAt(enc1) + keyString.charAt(enc2) + keyString.charAt(enc3) + keyString.charAt(enc4); 3.1890 + } 3.1891 + return output; 3.1892 +};
4.1 --- a/endpoint/WebContent/query.jsp Mon Sep 10 15:38:09 2012 +0300 4.2 +++ b/endpoint/WebContent/query.jsp Fri Sep 14 13:03:24 2012 +0300 4.3 @@ -5,9 +5,7 @@ 4.4 <head> 4.5 <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> 4.6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 4.7 - <link href="http://code.google.com/apis/maps/documentation/javascript/examples/default.css" rel="stylesheet" type="text/css" /> 4.8 - <link rel="stylesheet" href="style.css" type="text/css" /> 4.9 - <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script> 4.10 + <link rel="stylesheet" href="style.css" type="text/css" /> 4.11 <script type="text/javascript"> 4.12 function toggleMe(a) { 4.13 var e = document.getElementById(a); 4.14 @@ -22,21 +20,41 @@ 4.15 return true; 4.16 } 4.17 </script> 4.18 +<% if (request.getAttribute("pathToKML") != null) { 4.19 + if ("map_local".equals(request.getAttribute("handle"))) { %> 4.20 + <script type="text/javascript" src="js/geoxml3-kmz.js"></script> 4.21 + <script type="text/javascript" src="js/ProjectedOverlay.js"></script> 4.22 + <%} %> 4.23 + <link href="http://code.google.com/apis/maps/documentation/javascript/examples/default.css" rel="stylesheet" type="text/css" /> 4.24 + <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script> 4.25 <script type="text/javascript"> 4.26 function initialize() { 4.27 + // center at Brahames 4.28 var brahames = new google.maps.LatLng(37.92253, 23.72275); 4.29 var myOptions = { 4.30 zoom: 11, 4.31 center: brahames, 4.32 mapTypeId: google.maps.MapTypeId.ROADMAP 4.33 }; 4.34 - 4.35 + 4.36 + // get KML filename 4.37 + var kml = '<%=request.getAttribute("pathToKML")%>'; 4.38 + // <%=request.getAttribute("handle")%> 4.39 + // create map 4.40 var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions); 4.41 4.42 - var ctaLayer = new google.maps.KmlLayer('<%=request.getAttribute("pathToKML") != null ? request.getAttribute("pathToKML"):""%>'); 4.43 + // display using geoxml3 4.44 + <%if ("map_local".equals(request.getAttribute("handle"))) { %> 4.45 + var myParser = new geoXML3.parser({map: map}); 4.46 + myParser.parse(kml); 4.47 + 4.48 + <%} else {%> 4.49 + var ctaLayer = new google.maps.KmlLayer(kml); 4.50 ctaLayer.setMap(map); 4.51 + <%}%> 4.52 } 4.53 </script> 4.54 +<%}%> 4.55 <title>TELEIOS: Strabon Endpoint</title> 4.56 </head> 4.57 <body topmargin="0" leftmargin="0" link="#FFFFFF" vlink="#FFFFFF" alink="#FFFFFF" onload="initialize()"> 4.58 @@ -101,6 +119,7 @@ 4.59 <OPTION value="plain">Plain result</OPTION> 4.60 <OPTION value="download">Download</OPTION> 4.61 <OPTION value="map">On a map</OPTION> 4.62 + <OPTION value="map_local">On a map (localhost)</OPTION> 4.63 </SELECT></center> 4.64 </td> 4.65 <td colspan=2> </td>
5.1 --- a/endpoint/pom.xml Mon Sep 10 15:38:09 2012 +0300 5.2 +++ b/endpoint/pom.xml Fri Sep 14 13:03:24 2012 +0300 5.3 @@ -195,6 +195,7 @@ 5.4 <directory>${basedir}/WebContent</directory> 5.5 <includes> 5.6 <include>images/**</include> 5.7 + <include>js/**</include> 5.8 </includes> 5.9 </resource> 5.10 </webResources>
6.1 --- a/endpoint/src/main/java/eu/earthobservatory/org/StrabonEndpoint/QueryBean.java Mon Sep 10 15:38:09 2012 +0300 6.2 +++ b/endpoint/src/main/java/eu/earthobservatory/org/StrabonEndpoint/QueryBean.java Fri Sep 14 13:03:24 2012 +0300 6.3 @@ -208,7 +208,7 @@ 6.4 6.5 out.flush(); 6.6 6.7 - } else if ("map".equals(handle) && 6.8 + } else if (("map".equals(handle) || "map_local".equals(handle)) && 6.9 (queryResultFormat == stSPARQLQueryResultFormat.KML || 6.10 queryResultFormat == stSPARQLQueryResultFormat.KMZ) ) { 6.11 // show map (only valid for KML/KMZ) 6.12 @@ -216,6 +216,9 @@ 6.13 // get dispatcher 6.14 dispatcher = request.getRequestDispatcher("query.jsp"); 6.15 6.16 + // re-assign handle 6.17 + request.setAttribute("handle", handle); 6.18 + 6.19 SecureRandom random = new SecureRandom(); 6.20 String temp = new BigInteger(130, random).toString(32); 6.21