Fix for bug #1549, where the DOM conversion of <code/> and similar elements would...
[jquery.git] / build / runtest / env.js
1 /*
2  * Simulated browser environment for Rhino
3  *   By John Resig <http://ejohn.org/>
4  * Copyright 2007 John Resig, under the MIT License
5  */
6
7 // The window Object
8 var window = this;
9
10 (function(){
11
12         // Browser Navigator
13
14         window.navigator = {
15                 get userAgent(){
16                         return "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3";
17                 }
18         };
19         
20         var curLocation = (new java.io.File("./")).toURL();
21         
22         window.__defineSetter__("location", function(url){
23                 var xhr = new XMLHttpRequest();
24                 xhr.open("GET", url);
25                 xhr.onreadystatechange = function(){
26                         curLocation = new java.net.URL( curLocation, url );
27                         window.document = xhr.responseXML;
28
29                         var event = document.createEvent();
30                         event.initEvent("load");
31                         window.dispatchEvent( event );
32                 };
33                 xhr.send();
34         });
35         
36         window.__defineGetter__("location", function(url){
37                 return {
38                         get protocol(){
39                                 return curLocation.getProtocol() + ":";
40                         },
41                         get href(){
42                                 return curLocation.toString();
43                         },
44                         toString: function(){
45                                 return this.href;
46                         }
47                 };
48         });
49         
50         // Timers
51
52         var timers = [];
53         
54         window.setTimeout = function(fn, time){
55                 var num;
56                 return num = setInterval(function(){
57                         fn();
58                         clearInterval(num);
59                 }, time);
60         };
61         
62         window.setInterval = function(fn, time){
63                 var num = timers.length;
64                 
65                 timers[num] = new java.lang.Thread(new java.lang.Runnable({
66                         run: function(){
67                                 while (true){
68                                         java.lang.Thread.currentThread().sleep(time);
69                                         fn();
70                                 }
71                         }
72                 }));
73                 
74                 timers[num].start();
75         
76                 return num;
77         };
78         
79         window.clearInterval = function(num){
80                 if ( timers[num] ) {
81                         timers[num].stop();
82                         delete timers[num];
83                 }
84         };
85         
86         // Window Events
87         
88         var events = [{}];
89
90         window.addEventListener = function(type, fn){
91                 if ( !this.uuid || this == window ) {
92                         this.uuid = events.length;
93                         events[this.uuid] = {};
94                 }
95            
96                 if ( !events[this.uuid][type] )
97                         events[this.uuid][type] = [];
98                 
99                 if ( events[this.uuid][type].indexOf( fn ) < 0 )
100                         events[this.uuid][type].push( fn );
101         };
102         
103         window.removeEventListener = function(type, fn){
104            if ( !this.uuid || this == window ) {
105                this.uuid = events.length;
106                events[this.uuid] = {};
107            }
108            
109            if ( !events[this.uuid][type] )
110                         events[this.uuid][type] = [];
111                         
112                 events[this.uuid][type] =
113                         events[this.uuid][type].filter(function(f){
114                                 return f != fn;
115                         });
116         };
117         
118         window.dispatchEvent = function(event){
119                 if ( event.type ) {
120                         if ( this.uuid && events[this.uuid][event.type] ) {
121                                 var self = this;
122                         
123                                 events[this.uuid][event.type].forEach(function(fn){
124                                         fn.call( self, event );
125                                 });
126                         }
127                         
128                         if ( this["on" + event.type] )
129                                 this["on" + event.type].call( self, event );
130                 }
131         };
132         
133         // DOM Document
134         
135         window.DOMDocument = function(file){
136                 this._file = file;
137                 this._dom = Packages.javax.xml.parsers.
138                         DocumentBuilderFactory.newInstance()
139                                 .newDocumentBuilder().parse(file);
140                 
141                 if ( !obj_nodes.containsKey( this._dom ) )
142                         obj_nodes.put( this._dom, this );
143         };
144         
145         DOMDocument.prototype = {
146                 createTextNode: function(text){
147                         return makeNode( this._dom.createTextNode(
148                                 text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) );
149                 },
150                 createElement: function(name){
151                         return makeNode( this._dom.createElement(name.toLowerCase()) );
152                 },
153                 getElementsByTagName: function(name){
154                         return new DOMNodeList( this._dom.getElementsByTagName(
155                                 name.toLowerCase()) );
156                 },
157                 getElementById: function(id){
158                         var elems = this._dom.getElementsByTagName("*");
159                         
160                         for ( var i = 0; i < elems.length; i++ ) {
161                                 var elem = elems.item(i);
162                                 if ( elem.getAttribute("id") == id )
163                                         return makeNode(elem);
164                         }
165                         
166                         return null;
167                 },
168                 get body(){
169                         return this.getElementsByTagName("body")[0];
170                 },
171                 get documentElement(){
172                         return makeNode( this._dom.getDocumentElement() );
173                 },
174                 get ownerDocument(){
175                         return null;
176                 },
177                 addEventListener: window.addEventListener,
178                 removeEventListener: window.removeEventListener,
179                 dispatchEvent: window.dispatchEvent,
180                 get nodeName() {
181                         return "#document";
182                 },
183                 importNode: function(node, deep){
184                         return makeNode( this._dom.importNode(node._dom, deep) );
185                 },
186                 toString: function(){
187                         return "Document" + (typeof this._file == "string" ?
188                                 ": " + this._file : "");
189                 },
190                 get innerHTML(){
191                         return this.documentElement.outerHTML;
192                 },
193                 
194                 get defaultView(){
195                         return {
196                                 getComputedStyle: function(elem){
197                                         return {
198                                                 getPropertyValue: function(prop){
199                                                         prop = prop.replace(/\-(\w)/g,function(m,c){
200                                                                 return c.toUpperCase();
201                                                         });
202                                                         var val = elem.style[prop];
203                                                         
204                                                         if ( prop == "opacity" && val == "" )
205                                                                 val = "1";
206                                                                 
207                                                         return val;
208                                                 }
209                                         };
210                                 }
211                         };
212                 },
213                 
214                 createEvent: function(){
215                         return {
216                                 type: "",
217                                 initEvent: function(type){
218                                         this.type = type;
219                                 }
220                         };
221                 }
222         };
223         
224         function getDocument(node){
225                 return obj_nodes.get(node);
226         }
227         
228         // DOM NodeList
229         
230         window.DOMNodeList = function(list){
231                 this._dom = list;
232                 this.length = list.getLength();
233                 
234                 for ( var i = 0; i < this.length; i++ ) {
235                         var node = list.item(i);
236                         this[i] = makeNode( node );
237                 }
238         };
239         
240         DOMNodeList.prototype = {
241                 toString: function(){
242                         return "[ " +
243                                 Array.prototype.join.call( this, ", " ) + " ]";
244                 },
245                 get outerHTML(){
246                         return Array.prototype.map.call(
247                                 this, function(node){return node.outerHTML;}).join('');
248                 }
249         };
250         
251         // DOM Node
252         
253         window.DOMNode = function(node){
254                 this._dom = node;
255         };
256         
257         DOMNode.prototype = {
258                 get nodeType(){
259                         return this._dom.getNodeType();
260                 },
261                 get nodeValue(){
262                         return this._dom.getNodeValue();
263                 },
264                 get nodeName() {
265                         return this._dom.getNodeName();
266                 },
267                 cloneNode: function(deep){
268                         return makeNode( this._dom.cloneNode(deep) );
269                 },
270                 get ownerDocument(){
271                         return getDocument( this._dom.ownerDocument );
272                 },
273                 get documentElement(){
274                         return makeNode( this._dom.documentElement );
275                 },
276                 get parentNode() {
277                         return makeNode( this._dom.getParentNode() );
278                 },
279                 get nextSibling() {
280                         return makeNode( this._dom.getNextSibling() );
281                 },
282                 get previousSibling() {
283                         return makeNode( this._dom.getPreviousSibling() );
284                 },
285                 toString: function(){
286                         return '"' + this.nodeValue + '"';
287                 },
288                 get outerHTML(){
289                         return this.nodeValue;
290                 }
291         };
292
293         // DOM Element
294
295         window.DOMElement = function(elem){
296                 this._dom = elem;
297                 this.style = {
298                         get opacity(){ return this._opacity; },
299                         set opacity(val){ this._opacity = val + ""; }
300                 };
301                 
302                 // Load CSS info
303                 var styles = (this.getAttribute("style") || "").split(/\s*;\s*/);
304                 
305                 for ( var i = 0; i < styles.length; i++ ) {
306                         var style = styles[i].split(/\s*:\s*/);
307                         if ( style.length == 2 )
308                                 this.style[ style[0] ] = style[1];
309                 }
310         };
311         
312         DOMElement.prototype = extend( new DOMNode(), {
313                 get nodeName(){
314                         return this.tagName.toUpperCase();
315                 },
316                 get tagName(){
317                         return this._dom.getTagName();
318                 },
319                 toString: function(){
320                         return "<" + this.tagName + (this.id ? "#" + this.id : "" ) + ">";
321                 },
322                 get outerHTML(){
323                         var ret = "<" + this.tagName, attr = this.attributes;
324                         
325                         for ( var i in attr )
326                                 ret += " " + i + "='" + attr[i] + "'";
327                                 
328                         if ( this.childNodes.length || this.nodeName == "SCRIPT" )
329                                 ret += ">" + this.childNodes.outerHTML + 
330                                         "</" + this.tagName + ">";
331                         else
332                                 ret += "/>";
333                         
334                         return ret;
335                 },
336                 
337                 get attributes(){
338                         var attr = {}, attrs = this._dom.getAttributes();
339                         
340                         for ( var i = 0; i < attrs.getLength(); i++ )
341                                 attr[ attrs.item(i).nodeName ] = attrs.item(i).nodeValue;
342                                 
343                         return attr;
344                 },
345                 
346                 get innerHTML(){
347                         return this.childNodes.outerHTML;       
348                 },
349                 set innerHTML(html){
350                         html = html.replace(/<\/?([A-Z]+)/g, function(m){
351                                 return m.toLowerCase();
352                         });
353                         
354                         var nodes = this.ownerDocument.importNode(
355                                 new DOMDocument( new java.io.ByteArrayInputStream(
356                                         (new java.lang.String("<wrap>" + html + "</wrap>"))
357                                                 .getBytes("UTF8"))).documentElement, true).childNodes;
358                                 
359                         while (this.firstChild)
360                                 this.removeChild( this.firstChild );
361                         
362                         for ( var i = 0; i < nodes.length; i++ )
363                                 this.appendChild( nodes[i] );
364                 },
365                 
366                 get textContent(){
367                         return nav(this.childNodes);
368                         
369                         function nav(nodes){
370                                 var str = "";
371                                 for ( var i = 0; i < nodes.length; i++ )
372                                         if ( nodes[i].nodeType == 3 )
373                                                 str += nodes[i].nodeValue;
374                                         else if ( nodes[i].nodeType == 1 )
375                                                 str += nav(nodes[i].childNodes);
376                                 return str;
377                         }
378                 },
379                 set textContent(text){
380                         while (this.firstChild)
381                                 this.removeChild( this.firstChild );
382                         this.appendChild( this.ownerDocument.createTextNode(text));
383                 },
384                 
385                 style: {},
386                 clientHeight: 0,
387                 clientWidth: 0,
388                 offsetHeight: 0,
389                 offsetWidth: 0,
390                 
391                 get disabled() {
392                         var val = this.getAttribute("disabled");
393                         return val != "false" && !!val;
394                 },
395                 set disabled(val) { return this.setAttribute("disabled",val); },
396                 
397                 get checked() {
398                         var val = this.getAttribute("checked");
399                         return val != "false" && !!val;
400                 },
401                 set checked(val) { return this.setAttribute("checked",val); },
402                 
403                 get selected() {
404                         if ( !this._selectDone ) {
405                                 this._selectDone = true;
406                                 
407                                 if ( this.nodeName == "OPTION" && !this.parentNode.getAttribute("multiple") ) {
408                                         var opt = this.parentNode.getElementsByTagName("option");
409                                         
410                                         if ( this == opt[0] ) {
411                                                 var select = true;
412                                                 
413                                                 for ( var i = 1; i < opt.length; i++ )
414                                                         if ( opt[i].selected ) {
415                                                                 select = false;
416                                                                 break;
417                                                         }
418                                                         
419                                                 if ( select )
420                                                         this.selected = true;
421                                         }
422                                 }
423                         }
424                         
425                         var val = this.getAttribute("selected");
426                         return val != "false" && !!val;
427                 },
428                 set selected(val) { return this.setAttribute("selected",val); },
429
430                 get className() { return this.getAttribute("class") || ""; },
431                 set className(val) {
432                         return this.setAttribute("class",
433                                 val.replace(/(^\s*|\s*$)/g,""));
434                 },
435                 
436                 get type() { return this.getAttribute("type") || ""; },
437                 set type(val) { return this.setAttribute("type",val); },
438                 
439                 get value() { return this.getAttribute("value") || ""; },
440                 set value(val) { return this.setAttribute("value",val); },
441                 
442                 get src() { return this.getAttribute("src") || ""; },
443                 set src(val) { return this.setAttribute("src",val); },
444                 
445                 get id() { return this.getAttribute("id") || ""; },
446                 set id(val) { return this.setAttribute("id",val); },
447                 
448                 getAttribute: function(name){
449                         return this._dom.hasAttribute(name) ?
450                                 new String( this._dom.getAttribute(name) ) :
451                                 null;
452                 },
453                 setAttribute: function(name,value){
454                         this._dom.setAttribute(name,value);
455                 },
456                 removeAttribute: function(name){
457                         this._dom.removeAttribute(name);
458                 },
459                 
460                 get childNodes(){
461                         return new DOMNodeList( this._dom.getChildNodes() );
462                 },
463                 get firstChild(){
464                         return makeNode( this._dom.getFirstChild() );
465                 },
466                 get lastChild(){
467                         return makeNode( this._dom.getLastChild() );
468                 },
469                 appendChild: function(node){
470                         this._dom.appendChild( node._dom );
471                 },
472                 insertBefore: function(node,before){
473                         this._dom.insertBefore( node._dom, before ? before._dom : before );
474                 },
475                 removeChild: function(node){
476                         this._dom.removeChild( node._dom );
477                 },
478
479                 getElementsByTagName: DOMDocument.prototype.getElementsByTagName,
480                 
481                 addEventListener: window.addEventListener,
482                 removeEventListener: window.removeEventListener,
483                 dispatchEvent: window.dispatchEvent,
484                 
485                 click: function(){
486                         var event = document.createEvent();
487                         event.initEvent("click");
488                         this.dispatchEvent(event);
489                 },
490                 submit: function(){
491                         var event = document.createEvent();
492                         event.initEvent("submit");
493                         this.dispatchEvent(event);
494                 },
495                 focus: function(){
496                         var event = document.createEvent();
497                         event.initEvent("focus");
498                         this.dispatchEvent(event);
499                 },
500                 blur: function(){
501                         var event = document.createEvent();
502                         event.initEvent("blur");
503                         this.dispatchEvent(event);
504                 },
505                 get elements(){
506                         return this.getElementsByTagName("*");
507                 },
508                 get contentWindow(){
509                         return this.nodeName == "IFRAME" ? {
510                                 document: this.contentDocument
511                         } : null;
512                 },
513                 get contentDocument(){
514                         if ( this.nodeName == "IFRAME" ) {
515                                 if ( !this._doc )
516                                         this._doc = new DOMDocument(
517                                                 new java.io.ByteArrayInputStream((new java.lang.String(
518                                                 "<html><head><title></title></head><body></body></html>"))
519                                                 .getBytes("UTF8")));
520                                 return this._doc;
521                         } else
522                                 return null;
523                 }
524         });
525         
526         // Helper method for extending one object with another
527         
528         function extend(a,b) {
529                 for ( var i in b ) {
530                         var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
531                         
532                         if ( g || s ) {
533                                 if ( g )
534                                         a.__defineGetter__(i, g);
535                                 if ( s )
536                                         a.__defineSetter__(i, s);
537                         } else
538                                 a[i] = b[i];
539                 }
540                 return a;
541         }
542         
543         // Helper method for generating the right
544         // DOM objects based upon the type
545         
546         var obj_nodes = new java.util.HashMap();
547         
548         function makeNode(node){
549                 if ( node ) {
550                         if ( !obj_nodes.containsKey( node ) )
551                                 obj_nodes.put( node, node.getNodeType() == 
552                                         Packages.org.w3c.dom.Node.ELEMENT_NODE ?
553                                                 new DOMElement( node ) : new DOMNode( node ) );
554                         
555                         return obj_nodes.get(node);
556                 } else
557                         return null;
558         }
559         
560         // XMLHttpRequest
561         // Originally implemented by Yehuda Katz
562
563         window.XMLHttpRequest = function(){
564                 this.headers = {};
565                 this.responseHeaders = {};
566         };
567         
568         XMLHttpRequest.prototype = {
569                 open: function(method, url, async, user, password){ 
570                         this.readyState = 1;
571                         if (async)
572                                 this.async = true;
573                         this.method = method || "GET";
574                         this.url = url;
575                         this.onreadystatechange();
576                 },
577                 setRequestHeader: function(header, value){
578                         this.headers[header] = value;
579                 },
580                 getResponseHeader: function(header){ },
581                 send: function(data){
582                         var self = this;
583                         
584                         function makeRequest(){
585                                 var url = new java.net.URL(curLocation, self.url);
586                                 
587                                 if ( url.getProtocol() == "file" ) {
588                                         if ( self.method == "PUT" ) {
589                                                 var out = new java.io.FileWriter( 
590                                                                 new java.io.File( new java.net.URI( url.toString() ) ) ),
591                                                         text = new java.lang.String( data || "" );
592                                                 
593                                                 out.write( text, 0, text.length() );
594                                                 out.flush();
595                                                 out.close();
596                                         } else if ( self.method == "DELETE" ) {
597                                                 var file = new java.io.File( new java.net.URI( url.toString() ) );
598                                                 file["delete"]();
599                                         } else {
600                                                 var connection = url.openConnection();
601                                                 connection.connect();
602                                                 handleResponse();
603                                         }
604                                 } else { 
605                                         var connection = url.openConnection();
606                                         
607                                         connection.setRequestMethod( self.method );
608                                         
609                                         // Add headers to Java connection
610                                         for (var header in self.headers)
611                                                 connection.addRequestProperty(header, self.headers[header]);
612                                 
613                                         connection.connect();
614                                         
615                                         // Stick the response headers into responseHeaders
616                                         for (var i = 0; ; i++) { 
617                                                 var headerName = connection.getHeaderFieldKey(i); 
618                                                 var headerValue = connection.getHeaderField(i); 
619                                                 if (!headerName && !headerValue) break; 
620                                                 if (headerName)
621                                                         self.responseHeaders[headerName] = headerValue;
622                                         }
623                                         
624                                         handleResponse();
625                                 }
626                                 
627                                 function handleResponse(){
628                                         self.readyState = 4;
629                                         self.status = parseInt(connection.responseCode) || undefined;
630                                         self.statusText = connection.responseMessage || "";
631                                         
632                                         var stream = new java.io.InputStreamReader(connection.getInputStream()),
633                                                 buffer = new java.io.BufferedReader(stream), line;
634                                         
635                                         while ((line = buffer.readLine()) != null)
636                                                 self.responseText += line;
637                                                 
638                                         self.responseXML = null;
639                                         
640                                         if ( self.responseText.match(/^\s*</) ) {
641                                                 try {
642                                                         self.responseXML = new DOMDocument(
643                                                                 new java.io.ByteArrayInputStream(
644                                                                         (new java.lang.String(
645                                                                                 self.responseText)).getBytes("UTF8")));
646                                                 } catch(e) {}
647                                         }
648                                 }
649                                 
650                                 self.onreadystatechange();
651                         }
652
653                         if (this.async)
654                                 (new java.lang.Thread(new java.lang.Runnable({
655                                         run: makeRequest
656                                 }))).start();
657                         else
658                                 makeRequest();
659                 },
660                 abort: function(){},
661                 onreadystatechange: function(){},
662                 getResponseHeader: function(header){
663                         if (this.readyState < 3)
664                                 throw new Error("INVALID_STATE_ERR");
665                         else {
666                                 var returnedHeaders = [];
667                                 for (var rHeader in this.responseHeaders) {
668                                         if (rHeader.match(new Regexp(header, "i")))
669                                                 returnedHeaders.push(this.responseHeaders[rHeader]);
670                                 }
671                         
672                                 if (returnedHeaders.length)
673                                         return returnedHeaders.join(", ");
674                         }
675                         
676                         return null;
677                 },
678                 getAllResponseHeaders: function(header){
679                         if (this.readyState < 3)
680                                 throw new Error("INVALID_STATE_ERR");
681                         else {
682                                 var returnedHeaders = [];
683                                 
684                                 for (var header in this.responseHeaders)
685                                         returnedHeaders.push( header + ": " + this.responseHeaders[header] );
686                                 
687                                 return returnedHeaders.join("\r\n");
688                         }
689                 },
690                 async: true,
691                 readyState: 0,
692                 responseText: "",
693                 status: 0
694         };
695 })();