webr 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/README.md +4 -0
  2. data/Rakefile +19 -0
  3. data/app/webr.rb +57 -0
  4. data/bin/webr +6 -0
  5. data/ext/jasmine/lib/jasmine.js +2423 -0
  6. data/ext/jsdom/lib/jsdom.js +70 -0
  7. data/ext/jsdom/lib/jsdom/browser/domtohtml.js +198 -0
  8. data/ext/jsdom/lib/jsdom/browser/htmlencoding.js +381 -0
  9. data/ext/jsdom/lib/jsdom/browser/htmltodom.js +151 -0
  10. data/ext/jsdom/lib/jsdom/browser/index.js +484 -0
  11. data/ext/jsdom/lib/jsdom/level1/core.js +1610 -0
  12. data/ext/jsdom/lib/jsdom/level2/core.js +406 -0
  13. data/ext/jsdom/lib/jsdom/level2/events.js +358 -0
  14. data/ext/jsdom/lib/jsdom/level2/html.js +1424 -0
  15. data/ext/jsdom/lib/jsdom/level2/index.js +7 -0
  16. data/ext/jsdom/lib/jsdom/level2/languages/javascript.js +17 -0
  17. data/ext/jsdom/lib/jsdom/level3/core.js +514 -0
  18. data/ext/jsdom/lib/jsdom/level3/events.js +296 -0
  19. data/ext/jsdom/lib/jsdom/level3/html.js +5 -0
  20. data/ext/jsdom/lib/jsdom/level3/index.js +7 -0
  21. data/ext/node-htmlparser/lib/node-htmlparser.js +769 -0
  22. data/ext/node-htmlparser/lib/node-htmlparser.min.js +22 -0
  23. data/ext/request/request.js +116 -0
  24. data/js/jasmine-start.js +10 -0
  25. data/js/webr.js +97 -0
  26. data/jspec/jasmine_spec.js +23 -0
  27. data/lib/webr.rb +17 -0
  28. data/lib/webr/browser.rb +44 -0
  29. data/lib/webr/jasmine.rb +6 -0
  30. data/lib/webr/jasmine/browser.rb +15 -0
  31. data/lib/webr/jasmine/reporter.rb +16 -0
  32. data/lib/webr/jasmine/reporter/base.rb +40 -0
  33. data/lib/webr/jasmine/reporter/console.rb +79 -0
  34. data/lib/webr/jasmine/reporter/html.rb +179 -0
  35. data/lib/webr/portal.rb +19 -0
  36. data/lib/webr/runtime.rb +23 -0
  37. data/lib/webr/version.rb +3 -0
  38. data/spec/data/plain.html +13 -0
  39. data/spec/data/script-embedded.html +17 -0
  40. data/spec/data/script-external-onload.html +11 -0
  41. data/spec/data/script-external-onload.js +11 -0
  42. data/spec/data/script-external.html +11 -0
  43. data/spec/data/script-external.js +1 -0
  44. data/spec/data/script-jquery-1.4.2.html +12 -0
  45. data/spec/data/script-jquery-1.4.3.html +12 -0
  46. data/spec/data/script-jquery.js +3 -0
  47. data/spec/lib/webr/browser_spec.rb +133 -0
  48. data/spec/lib/webr/jasmine/browser_spec.rb +22 -0
  49. data/spec/lib/webr/jasmine/reporter/html_spec.rb +15 -0
  50. data/spec/spec_helper.rb +4 -0
  51. data/tasks/spec.rake +16 -0
  52. data/webr.gemspec +30 -0
  53. metadata +207 -0
@@ -0,0 +1,1424 @@
1
+ var core = require("./core").dom.level2.core,
2
+ urlParse = require("url").parse,
3
+ request = require("request");
4
+ readFile = require("fs").readFile,
5
+ http = require('http');
6
+
7
+ // Setup the javascript language processor
8
+ core.languageProcessors = {
9
+ javascript : require(__dirname + "/languages/javascript").javascript
10
+ };
11
+
12
+ require("./core").dom.level2.events;
13
+
14
+ function define(elementClass, def) {
15
+ var tagName = def.tagName,
16
+ tagNames = def.tagNames || (tagName? [tagName] : []),
17
+ parentClass = def.parentClass || core.HTMLElement,
18
+ attrs = def.attributes || [],
19
+ proto = def.proto || {};
20
+
21
+ var elem = core[elementClass] = function(document, name) {
22
+ parentClass.call(this, document, name || tagName.toUpperCase());
23
+ };
24
+
25
+ elem.prototype = proto;
26
+ elem.prototype.__proto__ = parentClass.prototype;
27
+
28
+ attrs.forEach(function(n) {
29
+ var prop = n.prop || n,
30
+ attr = n.attr || prop.toLowerCase();
31
+
32
+ if (!n.prop || n.read !== false) {
33
+ elem.prototype.__defineGetter__(prop, function() {
34
+ var s = this.getAttribute(attr);
35
+ if (n.type && n.type == 'boolean') {
36
+ return !!s;
37
+ }
38
+ if (n.type && n.type == 'long') {
39
+ return +s;
40
+ }
41
+ return s;
42
+ });
43
+ }
44
+
45
+ if (!n.prop || n.write !== false) {
46
+ elem.prototype.__defineSetter__(prop, function(val) {
47
+ if (!val) {
48
+ this.removeAttribute(attr);
49
+ }
50
+ else {
51
+ this.setAttribute(attr, val.toString());
52
+ }
53
+ });
54
+ }
55
+ });
56
+
57
+ tagNames.forEach(function(tag) {
58
+ core.Document.prototype._elementBuilders[tag.toLowerCase()] = function(doc, s) {
59
+
60
+ if (def && def.init) {
61
+ def.init(el);
62
+ }
63
+
64
+ var el = new elem(doc, s);
65
+ return el;
66
+ };
67
+ });
68
+ }
69
+
70
+ core.DOMImplementation.prototype._features = {
71
+ core : '2.0',
72
+ html : '2.0',
73
+ xhtml: '2.0',
74
+ xml : '2.0'
75
+ };
76
+
77
+
78
+ core.HTMLCollection = function(query) {
79
+ core.NodeList.call(this, query);
80
+ };
81
+
82
+ core.HTMLCollection.prototype = {
83
+ namedItem : function(name) {
84
+ var results = this.toArray(),
85
+ l = results.length,
86
+ node,
87
+ matchingName = null;
88
+
89
+ for (var i=0; i<l; i++) {
90
+ node = results[i];
91
+ if (node.getAttribute('id') === name) {
92
+ return node;
93
+ } else if (node.getAttribute('name') === name) {
94
+ matchingName = node;
95
+ }
96
+ }
97
+ return matchingName;
98
+ },
99
+ toString: function() {
100
+ return '[ jsdom HTMLCollection ]: contains ' + this.length + ' items';
101
+ }
102
+ };
103
+ core.HTMLCollection.prototype.__proto__ = core.NodeList.prototype;
104
+
105
+ core.HTMLOptionsCollection = core.HTMLCollection;
106
+
107
+ function closest(e, tagName) {
108
+ tagName = tagName.toUpperCase();
109
+ while (e) {
110
+ if (e.nodeName.toUpperCase() == tagName ||
111
+ (e.tagName && e.tagName.toUpperCase() === tagName))
112
+ {
113
+ return e;
114
+ }
115
+ e = e.parentNode;
116
+ }
117
+ return null;
118
+ }
119
+
120
+ function descendants(e, tagName, recursive) {
121
+ return new core.HTMLCollection(core.mapper(e, function(n) {
122
+ return n.nodeName === tagName;
123
+ }, recursive));
124
+ }
125
+
126
+ function firstChild(e, tagName) {
127
+ var c = descendants(e, tagName, false);
128
+ return c.length > 0 ? c[0] : null;
129
+ }
130
+
131
+
132
+ core.HTMLDocument = function(url) {
133
+ this._URL = url;
134
+ core.Document.call(this);
135
+ };
136
+ core.HTMLDocument.prototype = {
137
+ get referrer() {
138
+ return "";
139
+ },
140
+ get domain() {
141
+ return "";
142
+ },
143
+ _URL : "",
144
+ get URL() {
145
+ return this._URL;
146
+ },
147
+
148
+ // TODO TODO TODO TODO
149
+ // WARNING: THIS IS NOT COVERED BY THE SPEC!
150
+ set URL(val) {
151
+ this._URL = val;
152
+ },
153
+ // TODO TODO TODO TODO
154
+
155
+ // Handle blocking elements ((i)frame, script, img, etc)
156
+ // TODO: implement this in script, img, etc
157
+ _onloadHandlers : [],
158
+ _blockers : [],
159
+ set onload(handler) {
160
+
161
+ },
162
+ addBlocker : function(el) {
163
+ this._blockers.push(el);
164
+ },
165
+ removeBlocker : function(el) {
166
+ var idx = this._blockers.indexOf(el);
167
+ if (idx !== -1) {
168
+ this._blockers = this._blockers.splice(idx);
169
+ }
170
+ if (this._blockers.length === 0) {
171
+ while(_onloadHandlers.length > 0) {
172
+ var fn = this._onloadHandlers.pop();
173
+ if (typeof fn === "function") {
174
+ fn();
175
+ }
176
+ }
177
+ }
178
+ },
179
+
180
+ get images() {
181
+ return this.getElementsByTagName('IMG');
182
+ },
183
+ get applets() {
184
+ return new core.HTMLCollection(core.mapper(this, function(el) {
185
+ if (el && el.tagName) {
186
+ var upper = el.tagName.toUpperCase();
187
+ if (upper === "APPLET") {
188
+ return true;
189
+ } else if (upper === "OBJECT" &&
190
+ el.getElementsByTagName('APPLET').length > 0)
191
+ {
192
+ return true;
193
+ }
194
+ }
195
+ }));
196
+ },
197
+ get links() {
198
+ return new core.HTMLCollection(core.mapper(this, function(el) {
199
+ if (el && el.tagName) {
200
+ var upper = el.tagName.toUpperCase();
201
+ if (upper === "AREA" || (upper === "A" && el.href)) {
202
+ return true;
203
+ }
204
+ }
205
+ }));
206
+ },
207
+ get forms() {
208
+ return this.getElementsByTagName('FORM');
209
+ },
210
+ get anchors() {
211
+ return this.getElementsByTagName('A');
212
+ },
213
+ open : function() {
214
+ this._children = [];
215
+ },
216
+ close : function() {
217
+ },
218
+ write : function(text) {
219
+
220
+ },
221
+
222
+ writeln : function(text) {
223
+
224
+ },
225
+
226
+ getElementsByName : function(elementName) {
227
+ return new core.HTMLCollection(core.mapper(this, function(el) {
228
+ return (el.getAttribute && el.getAttribute("name") === elementName);
229
+ }));
230
+ },
231
+
232
+ _title : "",
233
+ get title() {
234
+ var title = this.getElementsByTagName("title").item(0);
235
+ if (!title) {
236
+ return "";
237
+ }
238
+ return title.innerHTML;
239
+ },
240
+ set title(val) {
241
+
242
+
243
+ },
244
+
245
+ _body : null,
246
+ get body() { return this.getElementsByTagName('BODY').item(0); },
247
+ set body(el) { this.documentElement = el; },
248
+
249
+ _cookie : "",
250
+ get cookie() { return this._cookie; },
251
+ set cookie(val) { this._cookie = val; }
252
+ };
253
+ core.HTMLDocument.prototype.__proto__ = core.Document.prototype;
254
+
255
+
256
+ define('HTMLElement', {
257
+ parentClass: core.Element,
258
+ attributes: [
259
+ 'id',
260
+ 'title',
261
+ 'lang',
262
+ 'dir',
263
+ {prop: 'className', attr: 'class'}
264
+ ]
265
+ });
266
+
267
+ core.Document.prototype._defaultElementBuilder = function(document, tagName) {
268
+ return new core.HTMLElement(document, tagName);
269
+ };
270
+
271
+ //http://www.w3.org/TR/html5/forms.html#category-listed
272
+ var listedElements = /button|fieldset|input|keygen|object|select|textarea/i;
273
+
274
+ define('HTMLFormElement', {
275
+ tagName: 'FORM',
276
+ proto: {
277
+ get elements() {
278
+ return new core.HTMLCollection(core.mapper(this, function(e) {
279
+ return listedElements.test(e.nodeName) ; // TODO exclude <input type="image">
280
+ }));
281
+ },
282
+ get length() {
283
+ return this.elements.length;
284
+ },
285
+ submit: function() {
286
+ },
287
+ reset: function() {
288
+ }
289
+ },
290
+ attributes: [
291
+ 'name',
292
+ {prop: 'acceptCharset', attr: 'accept-charset'},
293
+ 'action',
294
+ 'enctype',
295
+ 'method',
296
+ 'target'
297
+ ]
298
+ });
299
+
300
+ define('HTMLLinkElement', {
301
+ tagName: 'LINK',
302
+ attributes: [
303
+ {prop: 'disabled', type: 'boolean'},
304
+ 'charset',
305
+ 'href',
306
+ 'hreflang',
307
+ 'media',
308
+ 'rel',
309
+ 'rev',
310
+ 'target',
311
+ 'type'
312
+ ]
313
+ });
314
+
315
+ define('HTMLMetaElement', {
316
+ tagName: 'META',
317
+ attributes: [
318
+ 'content',
319
+ {prop: 'httpEquiv', attr: 'http-equiv'},
320
+ 'name',
321
+ 'scheme'
322
+ ]
323
+ });
324
+
325
+ define('HTMLHtmlElement', {
326
+ tagName: 'HTML',
327
+ attributes: [
328
+ 'version'
329
+ ]
330
+ });
331
+
332
+ define('HTMLHeadElement', {
333
+ tagName: 'HEAD',
334
+ attributes: [
335
+ 'profile'
336
+ ]
337
+ });
338
+
339
+ define('HTMLTitleElement', {
340
+ tagName: 'TITLE',
341
+ proto: {
342
+ get text() {
343
+ return this.innerHTML;
344
+ },
345
+ set text(s) {
346
+ this.innerHTML = s;
347
+ }
348
+ }
349
+ });
350
+
351
+ define('HTMLBaseElement', {
352
+ tagName: 'BASE',
353
+ attributes: [
354
+ 'href',
355
+ 'target'
356
+ ]
357
+ });
358
+
359
+
360
+ //**Deprecated**
361
+ define('HTMLIsIndexElement', {
362
+ tagName : 'ISINDEX',
363
+ parentClass : core.Element,
364
+ proto : {
365
+ get form() {
366
+ return closest(this, 'FORM');
367
+ }
368
+ },
369
+ attributes : [
370
+ 'prompt'
371
+ ]
372
+ });
373
+
374
+
375
+ define('HTMLStyleElement', {
376
+ tagName: 'STYLE',
377
+ attributes: [
378
+ {prop: 'disabled', type: 'boolean'},
379
+ 'media',
380
+ 'type',
381
+ ]
382
+ });
383
+
384
+ define('HTMLBodyElement', {
385
+ tagName: 'BODY',
386
+ attributes: [
387
+ 'aLink',
388
+ 'background',
389
+ 'bgColor',
390
+ 'link',
391
+ 'text',
392
+ 'vLink'
393
+ ]
394
+ });
395
+
396
+ define('HTMLSelectElement', {
397
+ tagName: 'SELECT',
398
+ proto: {
399
+ get options() {
400
+ return new core.HTMLOptionsCollection(core.mapper(this, function(n) {
401
+ return n.nodeName == 'OPTION';
402
+ }));
403
+ },
404
+
405
+ get length() {
406
+ return this.options.length;
407
+ },
408
+
409
+ get selectedIndex() {
410
+ return this.options.toArray().reduceRight(function(prev, option, i) {
411
+ return option.selected ? i : prev;
412
+ }, -1);
413
+ },
414
+
415
+ set selectedIndex(index) {
416
+ this.options.toArray().forEach(function(option, i) {
417
+ option.selected = i === index;
418
+ });
419
+ },
420
+
421
+ get value() {
422
+ var i = this.selectedIndex;
423
+ if (this.options.length && (i === -1)) {
424
+ i = 0;
425
+ }
426
+ if (i === -1) {
427
+ return '';
428
+ }
429
+ return this.options[i].value;
430
+ },
431
+
432
+ set value(val) {
433
+ this.options.toArray().forEach(function(option) {
434
+ if (option.value === val) {
435
+ option.selected = true;
436
+ }
437
+ });
438
+ },
439
+
440
+ get form() {
441
+ return closest(this, 'FORM');
442
+ },
443
+
444
+ get type() {
445
+ return this.multiple ? 'select-multiple' : 'select';
446
+ },
447
+
448
+ add: function(opt, before) {
449
+ if (before) {
450
+ this.insertBefore(opt, before);
451
+ }
452
+ else {
453
+ this.appendChild(opt);
454
+ }
455
+ },
456
+
457
+ remove: function(index) {
458
+ var opts = this.options.toArray();
459
+ if (index >= 0 && index < opts.length) {
460
+ var el = opts[index];
461
+ el.parentNode.removeChild(el);
462
+ }
463
+ },
464
+
465
+ blur: function() {
466
+ //TODO
467
+ },
468
+
469
+ focus: function() {
470
+ //TODO
471
+ }
472
+ },
473
+ attributes: [
474
+ {prop: 'disabled', type: 'boolean'},
475
+ {prop: 'multiple', type: 'boolean'},
476
+ 'name',
477
+ {prop: 'size', type: 'long'},
478
+ {prop: 'tabIndex', type: 'long'},
479
+ ]
480
+ });
481
+
482
+ define('HTMLOptGroupElement', {
483
+ tagName: 'OPTGROUP',
484
+ attributes: [
485
+ {prop: 'disabled', type: 'boolean'},
486
+ 'label'
487
+ ]
488
+ });
489
+
490
+ define('HTMLOptionElement', {
491
+ tagName: 'OPTION',
492
+ proto: {
493
+ _initDefaultSelected: function() {
494
+ if (this._defaultSelected === undefined) {
495
+ this._defaultSelected = !!this.getAttribute('selected');
496
+ }
497
+ return this._defaultSelected;
498
+ },
499
+ get form() {
500
+ return closest(this, 'FORM');
501
+ },
502
+ get defaultSelected() {
503
+ return this._initDefaultSelected();
504
+ },
505
+ get text() {
506
+ return (this.hasAttribute('value')) ? this.getAttribute('value') : this.innerHTML;
507
+ },
508
+ get value() {
509
+ return (this.hasAttribute('value')) ? this.getAttribute('value') : this.innerHTML;
510
+ },
511
+ set value(val) {
512
+ this.setAttribute('value', val);
513
+ },
514
+ get index() {
515
+ return closest(this, 'SELECT').options.toArray().indexOf(this);
516
+ },
517
+ get selected() {
518
+ return !!this.getAttribute('selected');
519
+ },
520
+ set selected(s) {
521
+ this._initDefaultSelected();
522
+ if (s) {
523
+ this.setAttribute('selected', 'selected');
524
+ }
525
+ else {
526
+ this.removeAttribute('selected');
527
+ }
528
+ }
529
+ },
530
+ attributes: [
531
+ {prop: 'disabled', type: 'boolean'},
532
+ 'label'
533
+ ]
534
+ });
535
+
536
+ define('HTMLInputElement', {
537
+ tagName: 'INPUT',
538
+ proto: {
539
+ _initDefaultValue: function() {
540
+ if (this._defaultValue === undefined) {
541
+ this._defaultValue = this.getAttribute('value');
542
+ }
543
+ return this._defaultValue;
544
+ },
545
+ _initDefaultChecked: function() {
546
+ if (this._defaultChecked === undefined) {
547
+ this._defaultChecked = !!this.getAttribute('checked');
548
+ }
549
+ return this._defaultChecked;
550
+ },
551
+ get form() {
552
+ return closest(this, 'FORM');
553
+ },
554
+ get defaultValue() {
555
+ return this._initDefaultValue();
556
+ },
557
+ get defaultChecked() {
558
+ return this._initDefaultChecked();
559
+ },
560
+ get checked() {
561
+ return !!this.getAttribute('checked');
562
+ },
563
+ set checked(checked) {
564
+ this._initDefaultChecked();
565
+ this.setAttribute('checked', checked);
566
+ },
567
+ get value() {
568
+ return this.getAttribute('value');
569
+ },
570
+ set value(val) {
571
+ this._initDefaultValue();
572
+ this.setAttribute('value', value);
573
+ },
574
+ blur: function() {
575
+ },
576
+ focus: function() {
577
+ },
578
+ select: function() {
579
+ },
580
+ click: function() {
581
+ this.checked = !this.checked;
582
+ }
583
+ },
584
+ attributes: [
585
+ 'accept',
586
+ 'accessKey',
587
+ 'align',
588
+ 'alt',
589
+ {prop: 'disabled', type: 'boolean'},
590
+ {prop: 'maxLength', type: 'long'},
591
+ 'name',
592
+ {prop: 'readOnly', type: 'boolean'},
593
+ {prop: 'size', type: 'long'},
594
+ 'src',
595
+ {prop: 'tabIndex', type: 'long'},
596
+ 'type',
597
+ 'useMap',
598
+ 'value'
599
+ ]
600
+ });
601
+
602
+ define('HTMLTextAreaElement', {
603
+ tagName: 'TEXTAREA',
604
+ proto: {
605
+ _initDefaultValue: function() {
606
+ if (this._defaultValue === undefined) {
607
+ this._defaultValue = this.innerHTML;
608
+ }
609
+ return this._defaultValue;
610
+ },
611
+ get form() {
612
+ return closest(this, 'FORM');
613
+ },
614
+ get defaultValue() {
615
+ return this._initDefaultValue();
616
+ },
617
+ get value() {
618
+ return this.innerHTML;
619
+ },
620
+ set value(val) {
621
+ this._initDefaultValue();
622
+ this.innerHTML = val;
623
+ },
624
+ get type() {
625
+ return 'textarea';
626
+ },
627
+ blur: function() {
628
+ },
629
+ focus: function() {
630
+ },
631
+ select: function() {
632
+ }
633
+ },
634
+ attributes: [
635
+ 'accessKey',
636
+ {prop: 'cols', type: 'long'},
637
+ {prop: 'disabled', type: 'boolean'},
638
+ {prop: 'maxLength', type: 'long'},
639
+ 'name',
640
+ {prop: 'readOnly', type: 'boolean'},
641
+ {prop: 'rows', type: 'long'},
642
+ {prop: 'tabIndex', type: 'long'}
643
+ ]
644
+ });
645
+
646
+ define('HTMLButtonElement', {
647
+ tagName: 'BUTTON',
648
+ proto: {
649
+ get form() {
650
+ return closest(this, 'FORM');
651
+ }
652
+ },
653
+ attributes: [
654
+ 'accessKey',
655
+ {prop: 'disabled', type: 'boolean'},
656
+ 'name',
657
+ {prop: 'tabIndex', type: 'long'},
658
+ 'type',
659
+ 'value'
660
+ ]
661
+ });
662
+
663
+ define('HTMLLabelElement', {
664
+ tagName: 'LABEL',
665
+ proto: {
666
+ get form() {
667
+ return closest(this, 'FORM');
668
+ }
669
+ },
670
+ attributes: [
671
+ 'accessKey',
672
+ {prop: 'htmlFor', attr: 'for'}
673
+ ]
674
+ });
675
+
676
+ define('HTMLFieldSetElement', {
677
+ tagName: 'FIELDSET',
678
+ proto: {
679
+ get form() {
680
+ return closest(this, 'FORM');
681
+ }
682
+ }
683
+ });
684
+
685
+ define('HTMLLegendElement', {
686
+ tagName: 'LEGEND',
687
+ proto: {
688
+ get form() {
689
+ return closest(this, 'FORM');
690
+ }
691
+ },
692
+ attributes: [
693
+ 'accessKey',
694
+ 'align'
695
+ ]
696
+ });
697
+
698
+ define('HTMLUListElement', {
699
+ tagName: 'UL',
700
+ attributes: [
701
+ {prop: 'compact', type: 'boolean'},
702
+ 'type'
703
+ ]
704
+ });
705
+
706
+ define('HTMLOListElement', {
707
+ tagName: 'OL',
708
+ attributes: [
709
+ {prop: 'compact', type: 'boolean'},
710
+ {prop: 'start', type: 'long'},
711
+ 'type'
712
+ ]
713
+ });
714
+
715
+ define('HTMLDListElement', {
716
+ tagName: 'DL',
717
+ attributes: [
718
+ {prop: 'compact', type: 'boolean'}
719
+ ]
720
+ });
721
+
722
+ define('HTMLDirectoryElement', {
723
+ tagName: 'DIR',
724
+ attributes: [
725
+ {prop: 'compact', type: 'boolean'}
726
+ ]
727
+ });
728
+
729
+ define('HTMLMenuElement', {
730
+ tagName: 'MENU',
731
+ attributes: [
732
+ {prop: 'compact', type: 'boolean'}
733
+ ]
734
+ });
735
+
736
+ define('HTMLLIElement', {
737
+ tagName: 'LI',
738
+ attributes: [
739
+ 'type',
740
+ {prop: 'value', type: 'long'}
741
+ ]
742
+ });
743
+
744
+ define('HTMLDivElement', {
745
+ tagName: 'DIV',
746
+ attributes: [
747
+ 'align'
748
+ ]
749
+ });
750
+
751
+ define('HTMLParagraphElement', {
752
+ tagName: 'P',
753
+ attributes: [
754
+ 'align'
755
+ ]
756
+ });
757
+
758
+ define('HTMLHeadingElement', {
759
+ tagNames: ['H1','H2','H3','H4','H5','H6'],
760
+ attributes: [
761
+ 'align'
762
+ ]
763
+ });
764
+
765
+ define('HTMLQuoteElement', {
766
+ tagNames: ['Q','BLOCKQUOTE'],
767
+ attributes: [
768
+ 'cite'
769
+ ]
770
+ });
771
+
772
+ define('HTMLPreElement', {
773
+ tagName: 'PRE',
774
+ attributes: [
775
+ {prop: 'width', type: 'long'}
776
+ ]
777
+ });
778
+
779
+ define('HTMLBRElement', {
780
+ tagName: 'BR',
781
+ attributes: [
782
+ 'clear'
783
+ ]
784
+ });
785
+
786
+ define('HTMLBaseFontElement', {
787
+ tagName: 'BASEFONT',
788
+ attributes: [
789
+ 'color',
790
+ 'face',
791
+ {prop: 'size', type: 'long'}
792
+ ]
793
+ });
794
+
795
+ define('HTMLFontElement', {
796
+ tagName: 'FONT',
797
+ attributes: [
798
+ 'color',
799
+ 'face',
800
+ 'size'
801
+ ]
802
+ });
803
+
804
+ define('HTMLHRElement', {
805
+ tagName: 'HR',
806
+ attributes: [
807
+ 'align',
808
+ {prop: 'noShade', type: 'boolean'},
809
+ 'size',
810
+ 'width'
811
+ ]
812
+ });
813
+
814
+ define('HTMLModElement', {
815
+ tagNames: ['INS', 'DEL'],
816
+ attributes: [
817
+ 'cite',
818
+ 'dateTime'
819
+ ]
820
+ });
821
+
822
+ define('HTMLAnchorElement', {
823
+ tagName: 'A',
824
+
825
+ proto: {
826
+ blur: function() {
827
+ },
828
+ focus: function() {
829
+ },
830
+ get href() {
831
+ return this.getAttribute('href').replace(/^\.\//,'/');
832
+ }
833
+ },
834
+ attributes: [
835
+ 'accessKey',
836
+ 'charset',
837
+ 'coords',
838
+ {prop: 'href', type: 'string', read: false},
839
+ 'hreflang',
840
+ 'name',
841
+ 'rel',
842
+ 'rev',
843
+ 'shape',
844
+ {prop: 'tabIndex', type: 'long'},
845
+ 'target',
846
+ 'type'
847
+ ]
848
+ });
849
+
850
+ define('HTMLImageElement', {
851
+ tagName: 'IMG',
852
+ attributes: [
853
+ 'name',
854
+ 'align',
855
+ 'alt',
856
+ 'border',
857
+ {prop: 'height', type: 'long'},
858
+ {prop: 'hspace', type: 'long'},
859
+ {prop: 'isMap', type: 'boolean'},
860
+ 'longDesc',
861
+ 'src',
862
+ 'useMap',
863
+ {prop: 'vspace', type: 'long'},
864
+ {prop: 'width', type: 'long'}
865
+ ]
866
+ });
867
+
868
+ define('HTMLObjectElement', {
869
+ tagName: 'OBJECT',
870
+ proto: {
871
+ get form() {
872
+ return closest(this, 'FORM');
873
+ },
874
+ get contentDocument() {
875
+ return null;
876
+ }
877
+ },
878
+ attributes: [
879
+ 'code',
880
+ 'align',
881
+ 'archive',
882
+ 'border',
883
+ 'codeBase',
884
+ 'codeType',
885
+ 'data',
886
+ {prop: 'declare', type: 'boolean'},
887
+ {prop: 'height', type: 'long'},
888
+ {prop: 'hspace', type: 'long'},
889
+ 'name',
890
+ 'standby',
891
+ {prop: 'tabIndex', type: 'long'},
892
+ 'type',
893
+ 'useMap',
894
+ {prop: 'vspace', type: 'long'},
895
+ {prop: 'width', type: 'long'}
896
+ ]
897
+ });
898
+
899
+ define('HTMLParamElement', {
900
+ tagName: 'PARAM',
901
+ attributes: [
902
+ 'name',
903
+ 'type',
904
+ 'value',
905
+ 'valueType'
906
+ ]
907
+ });
908
+
909
+ define('HTMLAppletElement', {
910
+ tagName: 'APPLET',
911
+ attributes: [
912
+ 'align',
913
+ 'alt',
914
+ 'archive',
915
+ 'code',
916
+ 'codeBase',
917
+ 'height',
918
+ {prop: 'hspace', type: 'long'},
919
+ 'name',
920
+ 'object',
921
+ {prop: 'vspace', type: 'long'},
922
+ 'width'
923
+ ]
924
+ });
925
+
926
+ define('HTMLMapElement', {
927
+ tagName: 'MAP',
928
+ proto: {
929
+ get areas() {
930
+ return this.getElementsByTagName("AREA");
931
+ }
932
+ },
933
+ attributes: [
934
+ 'name'
935
+ ]
936
+ });
937
+
938
+ define('HTMLAreaElement', {
939
+ tagName: 'AREA',
940
+ attributes: [
941
+ 'accessKey',
942
+ 'alt',
943
+ 'coords',
944
+ 'href',
945
+ {prop: 'noHref', type: 'boolean'},
946
+ 'shape',
947
+ {prop: 'tabIndex', type: 'long'},
948
+ 'target'
949
+ ]
950
+ });
951
+
952
+ define('HTMLScriptElement', {
953
+ tagName: 'SCRIPT',
954
+ proto : {
955
+ get src() {},
956
+ set src(value) {
957
+ this.setAttribute('src', value);
958
+ var self = this;
959
+ if (value.substring(0,7) === "file://") {
960
+ realvalue = value.substring(7);
961
+ } else {
962
+ realvalue = value;
963
+ }
964
+
965
+ if (value.substring(0,7) === "file://") {
966
+
967
+ require("fs").readFile(value.replace("file://",""), function(err, data)
968
+ {
969
+ if (err) throw err;
970
+ core.languageProcessors.javascript(self, data);
971
+ });
972
+
973
+ } else {
974
+ var host = urlParse(value);
975
+
976
+ server = http.createClient(host.port || 80, host.hostname);
977
+ path = (host.search) ?
978
+ host.pathname + host.search :
979
+ host.pathname,
980
+ request = server.request('GET', path, {'host': host.hostname });
981
+ request.end();
982
+ request.on('response', function (response) {
983
+ response.setEncoding('utf8');
984
+ var data = "";
985
+ response.on('data', function (chunk) {
986
+ data += chunk.toString();
987
+ });
988
+ response.on('end', function() {
989
+ core.languageProcessors.javascript(self, data);
990
+ });
991
+ });
992
+ }
993
+ },
994
+ get text() {
995
+ return this.children.item(0).value;
996
+ },
997
+ set text(text) {
998
+ if (!this.children.item(0)) {
999
+ this.appendChild(this.ownerDocument.createTextNode(text));
1000
+ }
1001
+
1002
+ // Execute the javascript
1003
+ var type = this.type || "text/javascript",
1004
+ lang = type.split("/").pop();
1005
+
1006
+ if (!core.languageProcessors[lang]) {
1007
+ core.languageProcessors[lang](this.ownerDocument, text);
1008
+ }
1009
+ }
1010
+ },
1011
+ attributes : [
1012
+ {prop: 'defer', 'type': 'boolean'},
1013
+ 'htmlFor',
1014
+ 'event',
1015
+ 'charset',
1016
+ 'type',
1017
+ {prop: 'src', write: false, type:'string'},
1018
+ ]
1019
+ })
1020
+
1021
+ define('HTMLTableElement', {
1022
+ tagName: 'TABLE',
1023
+ proto: {
1024
+ get caption() {
1025
+ return firstChild(this, 'CAPTION');
1026
+ },
1027
+ get tHead() {
1028
+ return firstChild(this, 'THEAD');
1029
+ },
1030
+ get tFoot() {
1031
+ return firstChild(this, 'TFOOT');
1032
+ },
1033
+ get rows() {
1034
+ if (!this._rows) {
1035
+ var table = this;
1036
+ this._rows = new core.HTMLCollection(function() {
1037
+ var sections = [table.tHead].concat(table.tBodies.toArray(), table.tFoot).filter(function(s) { return !!s });
1038
+
1039
+ if (sections.length == 0) {
1040
+ return core.mapDOMNodes(table, false, function(el) {
1041
+ return el.tagName == 'TR';
1042
+ });
1043
+ }
1044
+
1045
+ return sections.reduce(function(prev, s) {
1046
+ return prev.concat(s.rows.toArray());
1047
+ }, []);
1048
+
1049
+ });
1050
+ }
1051
+ return this._rows;
1052
+ },
1053
+ get tBodies() {
1054
+ if (!this._tBodies) {
1055
+ this._tBodies = descendants(this, 'TBODY', false);
1056
+ }
1057
+ return this._tBodies;
1058
+ },
1059
+ createTHead: function() {
1060
+ var el = this.tHead;
1061
+ if (!el) {
1062
+ el = this.ownerDocument.createElement('THEAD');
1063
+ this.appendChild(el);
1064
+ }
1065
+ return el;
1066
+ },
1067
+ deleteTHead: function() {
1068
+ var el = this.tHead;
1069
+ if (el) {
1070
+ debugger;
1071
+ el.parentNode.removeChild(el);
1072
+ }
1073
+ },
1074
+ createTFoot: function() {
1075
+ var el = this.tFoot;
1076
+ if (!el) {
1077
+ el = this.ownerDocument.createElement('TFOOT');
1078
+ this.appendChild(el);
1079
+ }
1080
+ return el;
1081
+ },
1082
+ deleteTFoot: function() {
1083
+ var el = this.tFoot;
1084
+ if (el) {
1085
+ el.parentNode.removeChild(el);
1086
+ }
1087
+ },
1088
+ createCaption: function() {
1089
+ var el = this.caption;
1090
+ if (!el) {
1091
+ el = this.ownerDocument.createElement('CAPTION');
1092
+ this.appendChild(el);
1093
+ }
1094
+ return el;
1095
+ },
1096
+ deleteCaption: function() {
1097
+ var c = this.caption;
1098
+ if (c) {
1099
+ c.parentNode.removeChild(c);
1100
+ }
1101
+ },
1102
+ insertRow: function(index) {
1103
+ var tr = this.ownerDocument.createElement('TR');
1104
+ if (this.childNodes.length === 0) {
1105
+ this.appendChild(this.ownerDocument.createElement('TBODY'));
1106
+ }
1107
+ var rows = this.rows.toArray();
1108
+ if (index < -1 || index > rows.length) {
1109
+ throw new core.DOMException(core.INDEX_SIZE_ERR);
1110
+ }
1111
+ if (index == -1 || (index === 0 && rows.length === 0)) {
1112
+ this.tBodies.item(0).appendChild(tr);
1113
+ }
1114
+ else if (index == rows.length) {
1115
+ var ref = rows[index-1];
1116
+ ref.parentNode.appendChild(tr);
1117
+ }
1118
+ else {
1119
+ var ref = rows[index];
1120
+ ref.parentNode.insertBefore(tr, ref);
1121
+ }
1122
+ return tr;
1123
+ },
1124
+ deleteRow: function(index) {
1125
+ var rows = this.rows.toArray();
1126
+ if (index == -1) {
1127
+ index = rows.length-1;
1128
+ }
1129
+ if (index < 0 || index >= rows.length) {
1130
+ throw new core.DOMException(core.INDEX_SIZE_ERR);
1131
+ }
1132
+ var tr = this.rows[index];
1133
+ tr.parentNode.removeChild(tr);
1134
+ }
1135
+ },
1136
+ attributes: [
1137
+ 'align',
1138
+ 'bgColor',
1139
+ 'border',
1140
+ 'cellPadding',
1141
+ 'cellSpacing',
1142
+ 'frame',
1143
+ 'rules',
1144
+ 'summary',
1145
+ 'width'
1146
+ ]
1147
+ });
1148
+
1149
+ define('HTMLTableCaptionElement', {
1150
+ tagName: 'CAPTION',
1151
+ attributes: [
1152
+ 'align'
1153
+ ]
1154
+ });
1155
+
1156
+ define('HTMLTableColElement', {
1157
+ tagNames: ['COL','COLGROUP'],
1158
+ attributes: [
1159
+ 'align',
1160
+ {prop: 'ch', attr: 'char'},
1161
+ {prop: 'chOff', attr: 'charoff'},
1162
+ {prop: 'span', type: 'long'},
1163
+ 'vAlign',
1164
+ 'width',
1165
+ ]
1166
+ });
1167
+
1168
+ define('HTMLTableSectionElement', {
1169
+ tagNames: ['THEAD','TBODY','TFOOT'],
1170
+ proto: {
1171
+ get rows() {
1172
+ if (!this._rows) {
1173
+ this._rows = descendants(this, 'TR', false);
1174
+ }
1175
+ return this._rows;
1176
+ },
1177
+ insertRow: function(index) {
1178
+ var tr = this.ownerDocument.createElement('TR');
1179
+ var rows = this.rows.toArray();
1180
+ if (index < -1 || index > rows.length) {
1181
+ throw new core.DOMException(core.INDEX_SIZE_ERR);
1182
+ }
1183
+ if (index == -1 || index == rows.length) {
1184
+ this.appendChild(tr);
1185
+ }
1186
+ else {
1187
+ var ref = rows[index];
1188
+ this.insertBefore(tr, ref);
1189
+ }
1190
+ return tr;
1191
+ },
1192
+ deleteRow: function(index) {
1193
+ var rows = this.rows.toArray();
1194
+ if (index == -1) {
1195
+ index = rows.length-1;
1196
+ }
1197
+ if (index < 0 || index >= rows.length) {
1198
+ throw new core.DOMException(core.INDEX_SIZE_ERR);
1199
+ }
1200
+ var tr = this.rows[index];
1201
+ this.removeChild(tr);
1202
+ }
1203
+ },
1204
+ attributes: [
1205
+ 'align',
1206
+ {prop: 'ch', attr: 'char'},
1207
+ {prop: 'chOff', attr: 'charoff'},
1208
+ {prop: 'span', type: 'long'},
1209
+ 'vAlign',
1210
+ 'width',
1211
+ ]
1212
+ });
1213
+
1214
+ define('HTMLTableRowElement', {
1215
+ tagName: 'TR',
1216
+ proto: {
1217
+ get cells() {
1218
+ if (!this._cells) {
1219
+ this._cells = new core.HTMLCollection(core.mapper(this, function(n) {
1220
+ return n.nodeName == 'TD' || n.nodeName == 'TH';
1221
+ }, false));
1222
+ }
1223
+ return this._cells;
1224
+ },
1225
+ get rowIndex() {
1226
+ return closest(this, 'TABLE').rows.toArray().indexOf(this);
1227
+ },
1228
+
1229
+ get sectionRowIndex() {
1230
+ return this.parentNode.rows.toArray().indexOf(this);
1231
+ },
1232
+ insertCell: function(index) {
1233
+ var td = this.ownerDocument.createElement('TD');
1234
+ var cells = this.cells.toArray();
1235
+ if (index < -1 || index > cells.length) {
1236
+ throw new core.DOMException(core.INDEX_SIZE_ERR);
1237
+ }
1238
+ if (index == -1 || index == cells.length) {
1239
+ this.appendChild(td);
1240
+ }
1241
+ else {
1242
+ var ref = cells[index];
1243
+ this.insertBefore(td, ref);
1244
+ }
1245
+ return td;
1246
+ },
1247
+ deleteCell: function(index) {
1248
+ var cells = this.cells.toArray();
1249
+ if (index == -1) {
1250
+ index = cells.length-1;
1251
+ }
1252
+ if (index < 0 || index >= cells.length) {
1253
+ throw new core.DOMException(core.INDEX_SIZE_ERR);
1254
+ }
1255
+ var td = this.cells[index];
1256
+ this.removeChild(td);
1257
+ }
1258
+ },
1259
+ attributes: [
1260
+ 'align',
1261
+ 'bgColor',
1262
+ {prop: 'ch', attr: 'char'},
1263
+ {prop: 'chOff', attr: 'charoff'},
1264
+ 'vAlign'
1265
+ ]
1266
+ });
1267
+
1268
+ define('HTMLTableCellElement', {
1269
+ tagNames: ['TH','TD'],
1270
+ proto: {
1271
+ get headers() {
1272
+ var cellIndex = this.cellIndex,
1273
+ headings = [],
1274
+ siblings = this.parentNode.getElementsByTagName(this.tagName);
1275
+
1276
+ for (var i=0; i<siblings.length; i++) {
1277
+ if (siblings.item(i).cellIndex >= cellIndex) {
1278
+ break;
1279
+ }
1280
+ headings.push(siblings.item(i).id);
1281
+ }
1282
+
1283
+ return headings.join(' ');
1284
+ },
1285
+ get cellIndex() {
1286
+ return closest(this, 'TR').cells.toArray().indexOf(this);
1287
+ }
1288
+ },
1289
+ attributes: [
1290
+ 'abbr',
1291
+ 'align',
1292
+ 'axis',
1293
+ 'bgColor',
1294
+ {prop: 'ch', attr: 'char'},
1295
+ {prop: 'chOff', attr: 'charoff'},
1296
+ {prop: 'colSpan', type: 'long'},
1297
+ 'height',
1298
+ {prop: 'noWrap', type: 'boolean'},
1299
+ {prop: 'rowSpan', type: 'long'},
1300
+ 'scope',
1301
+ 'vAlign',
1302
+ 'width'
1303
+ ]
1304
+ });
1305
+
1306
+ define('HTMLFrameSetElement', {
1307
+ tagName: 'FRAMESET',
1308
+ attributes: [
1309
+ 'cols',
1310
+ 'rows'
1311
+ ]
1312
+ });
1313
+
1314
+ define('HTMLFrameElement', {
1315
+ tagName: 'FRAME',
1316
+ init : function() {
1317
+ /*
1318
+ This is my shoddy attempt at frames. There are two problems here:
1319
+ + keeping the DOM free of BOM stuff (innerHTML, etc)
1320
+ + asynchronously loading the contents of the frame which should be fixable
1321
+ using the test suite's 'checkInitialization' method
1322
+ */
1323
+ var _setAttribute = core.HTMLFrameElement.prototype.setAttribute
1324
+
1325
+ core.HTMLFrameElement.prototype.setAttribute = function(name, value) {
1326
+ _setAttribute.call(this, name, value);
1327
+ if (name === "src") {
1328
+
1329
+ if (this._contentDocument) {
1330
+ delete this._contentDocument; // TODO: better cleanup
1331
+ }
1332
+ this._contentDocument = new (core.HTMLDocument)();
1333
+ this._contentDocument.URL = value;
1334
+ var self = this;
1335
+
1336
+ this.ownerDocument.addBlocker(this);
1337
+
1338
+ if (this.ownerDocument.URL.indexOf("file://") !== -1) {
1339
+ var pathToFile = this.ownerDocument.URL.replace("file://","");
1340
+
1341
+ // handle relative links
1342
+ if (value.indexOf("://") === -1) {
1343
+ // Prepare for hack sauce
1344
+ var split = pathToFile.split('/');
1345
+ split.pop(); // clean off the filename
1346
+ split.push(value);
1347
+ pathToFile = split.join("/");
1348
+ }
1349
+
1350
+ readFile(function(err, data) {
1351
+ if (err) {
1352
+ console.log(err);
1353
+ this.ownerDocument.removeBlocker(this);
1354
+ return;
1355
+ }
1356
+
1357
+ parser = require('htmlparser'),
1358
+ HtmlToDom = require(__dirname + '/../../../lib/jsdom/' +
1359
+ 'browser/htmltodom').HtmlToDom,
1360
+ html2dom = new HtmlToDom(parser);
1361
+
1362
+ html2dom.appendHtmlToElement(data.toString(), self._contentDocument);
1363
+ this.ownerDocument.removeBlocker(this);
1364
+ });
1365
+
1366
+ // Handle Urls
1367
+ } else {
1368
+
1369
+ }
1370
+ }
1371
+ };
1372
+ },
1373
+ proto: {
1374
+ _contentDocument : null,
1375
+ get contentDocument() {
1376
+ return this._contentDocument;
1377
+ },
1378
+ set src() {
1379
+ console.log("OH MAH GAWDDD");
1380
+ }
1381
+ },
1382
+ attributes: [
1383
+ 'frameBorder',
1384
+ 'longDesc',
1385
+ 'marginHeight',
1386
+ 'marginWidth',
1387
+ 'name',
1388
+ {prop: 'noResize', type: 'boolean'},
1389
+ 'scrolling',
1390
+ {prop: 'src', type: 'string', write: false}
1391
+ ]
1392
+ });
1393
+
1394
+ define('HTMLIFrameElement', {
1395
+ tagName: 'IFRAME',
1396
+ proto: {
1397
+ _contentDocument : null,
1398
+ get contentDocument() {
1399
+ if (this._contentDocument === null) {
1400
+ this._contentDocument = new HTMLDocument();
1401
+ }
1402
+ return this._contentDocument;
1403
+ }
1404
+ },
1405
+ attributes: [
1406
+ 'align',
1407
+ 'frameBorder',
1408
+ 'height',
1409
+ 'longDesc',
1410
+ 'marginHeight',
1411
+ 'marginWidth',
1412
+ 'name',
1413
+ 'scrolling',
1414
+ 'src',
1415
+ 'width'
1416
+ ]
1417
+ });
1418
+
1419
+ exports.define = define;
1420
+ exports.dom = {
1421
+ level2 : {
1422
+ html : core
1423
+ }
1424
+ }