1
|
/*
|
2
|
License: LGPL as per: http://www.gnu.org/copyleft/lesser.html
|
3
|
Dependancies: Sarissa
|
4
|
|
5
|
$Id$
|
6
|
*/
|
7
|
|
8
|
// some basic browser detection
|
9
|
var MB_IS_MOZ = (document.implementation && document.implementation.createDocument)?true:false;
|
10
|
|
11
|
/**
|
12
|
Transform an XML document using the provided XSL and use the results to build
|
13
|
a web page.
|
14
|
@constructor
|
15
|
@param xslUrl The URL of an XSL stylesheet.
|
16
|
@author Cameron Shorter - Cameron AT Shorter.net
|
17
|
*/
|
18
|
function XslProcessor(xslUrl,docNSUri) {
|
19
|
// get the stylesheet document
|
20
|
this.xslUrl=xslUrl;
|
21
|
this.xslDom = Sarissa.getDomDocument();
|
22
|
this.xslDom.async = false;
|
23
|
this.xslDom.validateOnParse=false; //IE6 SP2 parsing bug
|
24
|
this.xslDom.load(xslUrl);
|
25
|
if ( this.xslDom.parseError < 0 )
|
26
|
alert("error loading XSL stylesheet: " + xslUrl);
|
27
|
|
28
|
this.processor = new XSLTProcessor();
|
29
|
this.processor.importStylesheet(this.xslDom);
|
30
|
|
31
|
this.docNSUri = docNSUri;
|
32
|
|
33
|
/**
|
34
|
* Transforms XML in the provided xml node according to this XSL.
|
35
|
* @param xmlNode The XML node to be transformed.
|
36
|
* @return The transformed String.
|
37
|
*/
|
38
|
this.transformNodeToString = function(xmlNode) {
|
39
|
try {
|
40
|
// transform and build a web page with result
|
41
|
var newDoc = this.transformNodeToObject(xmlNode);
|
42
|
var s = Sarissa.serialize(newDoc);
|
43
|
return Sarissa.unescape(s);
|
44
|
} catch(e){
|
45
|
alert("Exception transforming doc with XSL: " + this.xslUrl);
|
46
|
alert("XSL="+Sarissa.serialize(this.xslDom));
|
47
|
alert("XML="+Sarissa.serialize(xmlNode));
|
48
|
}
|
49
|
}
|
50
|
|
51
|
/**
|
52
|
* Transforms XML in the provided xml node according to this XSL.
|
53
|
* @param xmlNode The XML node to be transformed.
|
54
|
* @return a DOM document object
|
55
|
*/
|
56
|
this.transformNodeToObject=function(xmlNode) {
|
57
|
var newFragment = this.processor.transformToDocument(xmlNode);
|
58
|
return newFragment;
|
59
|
}
|
60
|
|
61
|
/**
|
62
|
* Set XSL parameter.
|
63
|
*/
|
64
|
this.setParameter=function(paramName, paramValue, nsUri) {
|
65
|
//if ( typeof paramValue == "string" || typeof paramValue == "number") paramValue="'"+paramValue+"'";
|
66
|
if (!paramValue) {
|
67
|
//alert("null value for stylesheet param:"+paramName+":"+paramValue);
|
68
|
return;
|
69
|
}
|
70
|
this.processor.setParameter( null, paramName, paramValue);
|
71
|
}
|
72
|
}
|
73
|
|
74
|
/**
|
75
|
* A more flexible interface for loading docs that allows POST and async loading
|
76
|
*/
|
77
|
function postLoad(sUri, docToSend, contentType ) {
|
78
|
var xmlHttp = new XMLHttpRequest();
|
79
|
if ( sUri.indexOf("http://")==0 ) {
|
80
|
xmlHttp.open("POST", config.proxyUrl, false);
|
81
|
xmlHttp.setRequestHeader("serverUrl",sUri);//escape(sUri).replace(/\+/g, '%2C').replace(/\"/g,'%22').replace(/\'/g, '%27'));
|
82
|
} else {
|
83
|
xmlHttp.open("POST", sUri, false);
|
84
|
}
|
85
|
xmlHttp.setRequestHeader("content-type","text/xml");
|
86
|
if (contentType) xmlHttp.setRequestHeader("content-type",contentType);
|
87
|
//alert("sending:"+docToSend.xml);
|
88
|
xmlHttp.send( docToSend );
|
89
|
/*
|
90
|
if (_SARISSA_IS_IE) {
|
91
|
alert("before");
|
92
|
xmlHttp.status = xmlHttp.Status;
|
93
|
alert("after");
|
94
|
xmlHttp.statusText = xmlHttp.StatusText;
|
95
|
xmlHttp.responseText = xmlHttp.ResponseText;
|
96
|
}
|
97
|
*/
|
98
|
if (xmlHttp.status >= 400) { //http errors status start at 400
|
99
|
alert("error loading document: " + sUri + " - " + xmlHttp.statusText + "-" + xmlHttp.responseText );
|
100
|
var outDoc = Sarissa.getDomDocument();
|
101
|
outDoc.parseError = -1;
|
102
|
return outDoc;
|
103
|
} else {
|
104
|
//alert(xmlHttp.getResponseHeader("Content-Type"));
|
105
|
if ( null==xmlHttp.responseXML ) alert( "null XML response:" + xmlHttp.responseText );
|
106
|
return xmlHttp.responseXML;
|
107
|
}
|
108
|
}
|
109
|
|
110
|
/**
|
111
|
* If URL is local, then return URL unchanged,
|
112
|
* else return URL of http://proxy?url=URL , or null if proxy not defined.
|
113
|
* @param url Url of the file to access.
|
114
|
* @return Url of the proxy and service in the form http://host/proxy?url=service
|
115
|
*/
|
116
|
function getProxyPlusUrl(url) {
|
117
|
if ( url.indexOf("http://")==0 ) {
|
118
|
if ( config.proxyUrl ) {
|
119
|
url=config.proxyUrl+"?url="+escape(url).replace(/\+/g, '%2C').replace(/\"/g,'%22').replace(/\'/g, '%27');
|
120
|
} else {
|
121
|
alert("unable to load external document:"+url+" Set the proxyUrl property in config.");
|
122
|
url=null;
|
123
|
}
|
124
|
}
|
125
|
return url;
|
126
|
}
|
127
|
|
128
|
/**
|
129
|
* Browser independant version of createElementNS() because creating elements
|
130
|
* with namespaces other than the defalut namespace isn't dupported in IE,
|
131
|
* or at least I can't figure out how to do it.
|
132
|
* Caution: In IE the new element doesn't appear to a namespace!!
|
133
|
* @param doc the owner document for the new element
|
134
|
* @param name the name for the new element
|
135
|
* @param ns the URL for the namespace (without a prefix)
|
136
|
* @return element in the document with the specified namespace
|
137
|
*/
|
138
|
function createElementWithNS(doc,name,nsUri) {
|
139
|
if (_SARISSA_IS_IE) {
|
140
|
var newElement = doc.createElement(name);
|
141
|
//newElement.namespaceURI = nsUri; //can't do this for some reason?
|
142
|
return newElement;
|
143
|
} else {
|
144
|
return doc.createElementNS(nsUri,name);
|
145
|
}
|
146
|
}
|
147
|
|
148
|
/**
|
149
|
* Create a unique Id which can be used for classes to link themselves to HTML
|
150
|
* Ids.
|
151
|
* @constructor
|
152
|
*/
|
153
|
function UniqueId(){
|
154
|
this.lastId=0;
|
155
|
|
156
|
/** Return a numeric unique Id. */
|
157
|
this.getId=function() {
|
158
|
this.lastId++;
|
159
|
return this.lastId;
|
160
|
}
|
161
|
}
|
162
|
//use this global object to generate a unique ID via the getId function
|
163
|
var mbIds = new UniqueId();
|
164
|
|
165
|
function setISODate(isoDateStr) {
|
166
|
var parts = isoDateStr.match(/(\d{4})-?(\d{2})?-?(\d{2})?T?(\d{2})?:?(\d{2})?:?(\d{2})?\.?(\d{0,3})?(Z)?/);
|
167
|
var res = null;
|
168
|
for (var i=1;i<parts.length;++i){
|
169
|
if (!parts[i]) {
|
170
|
parts[i] = (i==3)?1:0; //months start with day number 1, not 0
|
171
|
if (!res) res = i;
|
172
|
}
|
173
|
}
|
174
|
var isoDate = new Date();
|
175
|
isoDate.setFullYear(parseInt(parts[1],10));
|
176
|
isoDate.setMonth(parseInt(parts[2]-1,10));
|
177
|
isoDate.setDate(parseInt(parts[3],10));
|
178
|
isoDate.setHours(parseInt(parts[4],10));
|
179
|
isoDate.setMinutes(parseInt(parts[5],10));
|
180
|
isoDate.setSeconds(parseFloat(parts[6],10));
|
181
|
if (!res) res = 6;
|
182
|
isoDate.res = res;
|
183
|
return isoDate;
|
184
|
}
|
185
|
|
186
|
function getISODate(isoDate) {
|
187
|
var res = isoDate.res?isoDate.res:6;
|
188
|
var dateStr = "";
|
189
|
dateStr += res>1?isoDate.getFullYear():"";
|
190
|
dateStr += res>2?"-"+leadingZeros(isoDate.getMonth()+1,2):"";
|
191
|
dateStr += res>3?"-"+leadingZeros(isoDate.getDate(),2):"";
|
192
|
dateStr += res>4?"T"+leadingZeros(isoDate.getHours(),2):"";
|
193
|
dateStr += res>5?":"+leadingZeros(isoDate.getMinutes(),2):"";
|
194
|
dateStr += res>6?":"+leadingZeros(isoDate.getSeconds(),2):"";
|
195
|
return dateStr;
|
196
|
}
|
197
|
|
198
|
function leadingZeros(num,digits) {
|
199
|
var intNum = parseInt(num,10);
|
200
|
var base = Math.pow(10,digits);
|
201
|
if (intNum<base) intNum += base;
|
202
|
return intNum.toString().substr(1);
|
203
|
}
|
204
|
|
205
|
|
206
|
// Correctly handle PNG transparency in Win IE 5.5 or higher.
|
207
|
// this method should be set as an IMG onload handler for PNG map layers
|
208
|
// thanks to Caroklyn Cole for this fix. For an explanation see:
|
209
|
// http://homepage.ntlworld.com/bobosola. Updated 02-March-2004
|
210
|
// modified to the images as visible after this has been called.
|
211
|
function fixPNG(myImage,myId) {
|
212
|
if (_SARISSA_IS_IE) {
|
213
|
var imgID = "id='" + myId + "' ";
|
214
|
var imgClass = (myImage.className) ? "class='" + myImage.className + "' " : ""
|
215
|
var imgTitle = (myImage.title) ? "title='" + myImage.title + "' " : "title='" + myImage.alt + "' "
|
216
|
var imgStyle = "display:inline-block;" + myImage.style.cssText
|
217
|
var strNewHTML = "<span " + imgID + imgClass + imgTitle
|
218
|
strNewHTML += " style=\"" + "width:" + myImage.width + "px; height:" + myImage.height + "px;" + imgStyle + ";"
|
219
|
// Escape some chars (don't use encode() that would escape %xx previously used in XSL)
|
220
|
var src = myImage.src;
|
221
|
src = src.replace(/\(/g,'%28');
|
222
|
src = src.replace(/\)/g,'%29');
|
223
|
src = src.replace(/'/g,'%27');
|
224
|
// AlphaImageLoader converts '%23' in src to '#' and cuts URL on '#'
|
225
|
src = src.replace(/%23/g,'%2523');
|
226
|
strNewHTML += "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader";
|
227
|
strNewHTML += "(src=\'" + src + "\', sizingMethod='scale'); \"></span>" ;
|
228
|
//myImage.outerHTML = strNewHTML;
|
229
|
//alert(strNewHTML);
|
230
|
return strNewHTML;
|
231
|
}
|
232
|
}
|
233
|
|
234
|
|
235
|
|
236
|
/**
|
237
|
* get the absolute position of HTML element NS4, IE4/5 & NS6, even if it's in a table.
|
238
|
* @param element The HTML element.
|
239
|
* @return Top left X position.
|
240
|
*/
|
241
|
function getAbsX(elt) {
|
242
|
return (elt.x) ? elt.x : getAbsPos(elt,"Left") + 2;
|
243
|
}
|
244
|
|
245
|
/**
|
246
|
* get the absolute position of HTML element NS4, IE4/5 & NS6, even if it's in a table.
|
247
|
* @param element The HTML element.
|
248
|
* @return Top left Y position.
|
249
|
*/
|
250
|
function getAbsY(elt) {
|
251
|
return (elt.y) ? elt.y : getAbsPos(elt,"Top") + 2;
|
252
|
}
|
253
|
|
254
|
/**
|
255
|
* TBD: Comment me.
|
256
|
* @param elt TBD
|
257
|
* @param which TBD
|
258
|
*/
|
259
|
function getAbsPos(elt,which) {
|
260
|
iPos = 0;
|
261
|
while (elt != null) {
|
262
|
iPos += elt["offset" + which];
|
263
|
elt = elt.offsetParent;
|
264
|
}
|
265
|
return iPos;
|
266
|
}
|
267
|
|
268
|
/**
|
269
|
* get the absolute position of a user event (e.g., a mousedown).
|
270
|
* @param e The user event.
|
271
|
* @return Left or top position.
|
272
|
*/
|
273
|
function getPageX(e){
|
274
|
var posx = 0;
|
275
|
if (!e) var e = window.event;
|
276
|
if (e.pageX) {
|
277
|
posx = e.pageX;
|
278
|
}
|
279
|
else if (e.clientX) {
|
280
|
posx = e.clientX;
|
281
|
}
|
282
|
if (document.body && document.body.scrollLeft){
|
283
|
posx += document.body.scrollLeft;
|
284
|
}
|
285
|
else if (document.documentElement && document.documentElement.scrollLeft){
|
286
|
posx += document.documentElement.scrollLeft;
|
287
|
}
|
288
|
return posx;
|
289
|
}
|
290
|
|
291
|
/**
|
292
|
* get the absolute position of a user event (e.g., a mousedown).
|
293
|
* @param e The user event.
|
294
|
* @return Left or top position.
|
295
|
*/
|
296
|
function getPageY(e){
|
297
|
var posy = 0;
|
298
|
if (!e) var e = window.event;
|
299
|
if (e.pageY) {
|
300
|
posy = e.pageY;
|
301
|
}
|
302
|
else if (e.clientY) {
|
303
|
posy = e.clientY;
|
304
|
}
|
305
|
if (document.body && document.body.scrollTop){
|
306
|
posy += document.body.scrollTop;
|
307
|
}
|
308
|
else if (document.documentElement && document.documentElement.scrollTop){
|
309
|
posy += document.documentElement.scrollTop;
|
310
|
}
|
311
|
return posy;
|
312
|
}
|
313
|
|
314
|
/**
|
315
|
* parse comma-separated name=value argument pairs from the query string of the URL; the function stores name=value pairs in properties of an object and returns that object.
|
316
|
* @return args Array of arguments passed to page, in form args[argname] = value.
|
317
|
*/
|
318
|
function getArgs(){
|
319
|
var args = new Object();
|
320
|
var query = location.search.substring(1);
|
321
|
var pairs = query.split("&");
|
322
|
for(var i = 0; i < pairs.length; i++) {
|
323
|
var pos = pairs[i].indexOf('=');
|
324
|
if (pos == -1) continue;
|
325
|
var argname = pairs[i].substring(0,pos);
|
326
|
var value = pairs[i].substring(pos+1);
|
327
|
args[argname] = unescape(value.replace(/\+/g, " "));
|
328
|
}
|
329
|
return args;
|
330
|
}
|
331
|
//initialize the array once
|
332
|
window.cgiArgs = getArgs();
|
333
|
|
334
|
/**
|
335
|
* convert geographic x coordinate to screen coordinate.
|
336
|
* @param context The context object.
|
337
|
* @param xCoord The geographic x coordinate to be converted.
|
338
|
* @return x the converted x coordinate.
|
339
|
*/
|
340
|
function getScreenX(context, xCoord){
|
341
|
bbox=context.getBoundingBox();
|
342
|
width=context.getWindowWidth();
|
343
|
bbox[0]=parseFloat(bbox[0]);
|
344
|
bbox[2]=parseFloat(bbox[2]);
|
345
|
var xfac = (width/(bbox[2]-bbox[0]));
|
346
|
x=xfac*(xCoord-bbox[0]);
|
347
|
return x;
|
348
|
}
|
349
|
|
350
|
/**
|
351
|
* convert geographic y coordinate to screen coordinate.
|
352
|
* @param context The context object.
|
353
|
* @param yCoord The geographic x coordinate to be converted.
|
354
|
* @return y the converted x coordinate.
|
355
|
*/
|
356
|
function getScreenY(context, yCoord){
|
357
|
var bbox=context.getBoundingBox();
|
358
|
var height=context.getWindowHeight();
|
359
|
bbox[1]=parseFloat(bbox[1]);
|
360
|
bbox[3]=parseFloat(bbox[3]);
|
361
|
var yfac = (heighteight/(bbox[3]-bbox[1]));
|
362
|
var y=height-(yfac*(pt.y-bbox[1]));
|
363
|
return y;
|
364
|
}
|
365
|
|
366
|
/**
|
367
|
* convert screen x coordinate to geographic coordinate.
|
368
|
* @param context The context object.
|
369
|
* @param xCoord The screen x coordinate to be converted.
|
370
|
* @return x the converted x coordinate.
|
371
|
*/
|
372
|
function getGeoCoordX(context, xCooord) {
|
373
|
var bbox=context.getBoundingBox();
|
374
|
var width=context.getWindowWidth();
|
375
|
bbox[0]=parseFloat(bbox[0]);
|
376
|
bbox[2]=parseFloat(bbox[2]);
|
377
|
var xfac = ((bbox[2]-bbox[0]) / width);
|
378
|
var x=bbox[0] + xfac*(xCoord);
|
379
|
return x;
|
380
|
}
|
381
|
|
382
|
/**
|
383
|
* convert screen coordinate to screen coordinate.
|
384
|
* @param context The context object.
|
385
|
* @param yCoord The geographic y coordinate to be converted.
|
386
|
* @return y the converted y coordinate.
|
387
|
*/
|
388
|
function getGeoCoordY(yCoord){
|
389
|
var bbox=context.getBoundingBox();
|
390
|
var height=context.getWindowHeight();
|
391
|
bbox[1]=parseFloat(bbox[1]);
|
392
|
bbox[3]=parseFloat(bbox[3]);
|
393
|
var yfac = ((bbox[3]-bbox[1]) / height);
|
394
|
var y=bbox[1] + yfac*(height-yCoord);
|
395
|
return y;
|
396
|
}
|
397
|
|
398
|
/**
|
399
|
* create an element and append it to the document body element.
|
400
|
* @param type The type of element to be created.
|
401
|
* @return node The node created and appended.
|
402
|
*/
|
403
|
function makeElt(type) {
|
404
|
var node=document.createElement(type);
|
405
|
document.getElementsByTagName("body").item(0).appendChild(node);
|
406
|
return node;
|
407
|
}
|
408
|
|
409
|
// variable needed to determine if a popup window is currently open
|
410
|
var newWindow = '';
|
411
|
/**
|
412
|
* open a popup window, adapted from http://www.quirksmode.org/js/croswin.html
|
413
|
* @param url The url of the page to be opened.
|
414
|
* @param width Width of the popup window, in pixels.
|
415
|
* @param height Height of the popup window, in pixels.
|
416
|
*/
|
417
|
function openPopup(url, width, height) {
|
418
|
if(width == null) {
|
419
|
width = 300;
|
420
|
}
|
421
|
if(height == null) {
|
422
|
height = 200;
|
423
|
}
|
424
|
if (!newWindow.closed && newWindow.location) {
|
425
|
newWindow.location.href = url;
|
426
|
}
|
427
|
else {
|
428
|
newWindow=window.open(url,'name','height=' + height + ',width=' + width);
|
429
|
if (!newWindow.opener) newwindow.opener = self;
|
430
|
}
|
431
|
if (window.focus) {newWindow.focus()}
|
432
|
return false;
|
433
|
}
|
434
|
|
435
|
/**
|
436
|
* write debugging info to a textbox onscreen.
|
437
|
* @param output String to be output.
|
438
|
*/
|
439
|
function debug(output){
|
440
|
tarea=makeElt("textarea");
|
441
|
tarea.setAttribute("rows","3");
|
442
|
tarea.setAttribute("cols","40");
|
443
|
tnode=document.createTextNode(output);
|
444
|
tarea.appendChild(tnode);
|
445
|
}
|
446
|
|
447
|
/**
|
448
|
* determine and return the target element of an event.
|
449
|
* @param evt The event.
|
450
|
* @return elt The element.
|
451
|
*/
|
452
|
function returnTarget(evt){
|
453
|
evt = (evt) ? evt : ((window.event) ? window.event : "");
|
454
|
var elt=null;
|
455
|
if(evt.target){
|
456
|
elt=evt.target;
|
457
|
}
|
458
|
else if(evt.srcElement){
|
459
|
elt=evt.srcElement;
|
460
|
}
|
461
|
return elt;
|
462
|
}
|
463
|
|
464
|
/**
|
465
|
* attach an event to an element.
|
466
|
* @param elementObject The object.
|
467
|
* @param eventName The name of the event.
|
468
|
* @param functionObject The function to be called.
|
469
|
*/
|
470
|
function addEvent(elementObject, eventName, functionObject) {
|
471
|
if(document.addEventListener) {
|
472
|
elementObject.addEventListener(eventName, functionObject, false);
|
473
|
}
|
474
|
else if(document.attachEvent) {
|
475
|
elementObject.attachEvent("on" + eventName, functionObject);
|
476
|
}
|
477
|
}
|
478
|
|
479
|
/**
|
480
|
* handle event attached to an object.
|
481
|
* @param evt The event.
|
482
|
*/
|
483
|
function handleEventWithObject(evt){
|
484
|
var elt = returnTarget(evt);
|
485
|
var obj = elt.ownerObj;
|
486
|
if (obj!=null) obj.handleEvent(evt);
|
487
|
}
|
488
|
|