From a0342a66fa0c2556e481ffdfd546f52b510f3ec5 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Apr 2010 22:45:05 -0400 Subject: [PATCH 01/10] convert the named form elements to use new dispatch table thingy --- src/html/button.js | 3 +++ src/html/element.js | 41 +------------------------------------- src/html/fieldset.js | 4 ++++ src/html/input-elements.js | 15 +++++++++++--- src/html/input.js | 4 ++++ src/html/select.js | 4 ++++ src/html/textarea.js | 4 ++++ 7 files changed, 32 insertions(+), 43 deletions(-) diff --git a/src/html/button.js b/src/html/button.js index b6a72735..5237ae14 100644 --- a/src/html/button.js +++ b/src/html/button.js @@ -28,3 +28,6 @@ __extend__(HTMLButtonElement.prototype, { } }); +// Named Element Support +HTMLElement.registerSetAttribute('BUTTON', 'name', + __updateFormForNamedElement__); diff --git a/src/html/element.js b/src/html/element.js index 4dee6cb6..30e92fec 100644 --- a/src/html/element.js +++ b/src/html/element.js @@ -165,41 +165,6 @@ __extend__(HTMLElement.prototype, { return ret; }, - /** - * Named Element Support - */ - - /** - * Not all children of a form are named elements - * returns the parent form element or null if - * there is no parent form or if not a named element - */ - _isFormNamedElement: function(node) { - if (node.nodeType === Node.ELEMENT_NODE) { - switch (node.nodeName.toLowerCase()) { - case 'button': - case 'fieldset': - case 'input': - case 'keygen': - case 'select': - case 'output': - case 'select': - case 'textarea': - return true; - } - } - return false; - }, - _updateFormForNamedElement: function() { - if (this._isFormNamedElement(this)) { - if (this.form) { - // to check for ID or NAME attribute too - // not, then nothing to do - this.form._updateElements(); - } - } - }, - /** * setAttribute use a dispatch table that other tags can set to * "listen" to various values being set. The dispatch table @@ -211,8 +176,6 @@ __extend__(HTMLElement.prototype, { setAttribute: function(name, value) { var result = Element.prototype.setAttribute.apply(this, arguments); this.ownerDocument._addNamedMap(this); - this._updateFormForNamedElement(); - var tagname = this.tagName; var callback = HTMLElement.getAttributeCallback('set', tagname, name); if (callback) { @@ -222,7 +185,6 @@ __extend__(HTMLElement.prototype, { setAttributeNS: function(namespaceURI, name, value) { var result = Element.prototype.setAttributeNS.apply(this, arguments); this.ownerDocument._addNamedMap(this); - this._updateFormForNamedElement(); var tagname = this.tagName; var callback = HTMLElement.getAttributeCallback('set', tagname, name); @@ -235,7 +197,6 @@ __extend__(HTMLElement.prototype, { setAttributeNode: function(newnode) { var result = Element.prototype.setAttributeNode.apply(this, arguments); this.ownerDocument._addNamedMap(this); - this._updateFormForNamedElement(); var tagname = this.tagName; var callback = HTMLElement.getAttributeCallback('set', tagname, newnode.name); @@ -247,7 +208,7 @@ __extend__(HTMLElement.prototype, { setAttributeNodeNS: function(newnode) { var result = Element.prototype.setAttributeNodeNS.apply(this, arguments); this.ownerDocument._addNamedMap(this); - this._updateFormForNamedElement(); + var tagname = this.tagName; var callback = HTMLElement.getAttributeCallback('set', tagname, newnode.name); if (callback) { diff --git a/src/html/fieldset.js b/src/html/fieldset.js index 484cc000..0168e64a 100644 --- a/src/html/fieldset.js +++ b/src/html/fieldset.js @@ -20,3 +20,7 @@ __extend__(HTMLFieldSetElement.prototype, { return '[object HTMLFieldSetElement]'; } }); + +// Named Element Support +HTMLElement.registerSetAttribute('FIELDSET', 'name', + __updateFormForNamedElement__); \ No newline at end of file diff --git a/src/html/input-elements.js b/src/html/input-elements.js index 3715d37b..b22e39b8 100644 --- a/src/html/input-elements.js +++ b/src/html/input-elements.js @@ -10,12 +10,12 @@ * * legent (no value attr) * * fieldset (no value attr) * * label (no value attr) - * * option (custom) + * * option (custom value) * HTMLTypeValueInputs (extends InputCommon) - * * select (custom) + * * select (custom value) * * button (just sets value) * HTMLInputAreaCommon (extends TypeValueIput) - * * input (X) + * * input (custom) * * textarea (just sets value) * * ----------------------- @@ -264,3 +264,12 @@ __extend__(HTMLInputAreaCommon.prototype, { } }); + + +var __updateFormForNamedElement__ = function(node, value) { + if (node.form) { + // to check for ID or NAME attribute too + // not, then nothing to do + node.form._updateElements(); + } +}; diff --git a/src/html/input.js b/src/html/input.js index e7f36a5b..21b58b74 100644 --- a/src/html/input.js +++ b/src/html/input.js @@ -69,3 +69,7 @@ HTMLElement.registerSetAttribute('INPUT', 'value', function(node, value) { node.defaultValue = value; } }); + +// Named Element Support +HTMLElement.registerSetAttribute('INPUT', 'name', + __updateFormForNamedElement__); diff --git a/src/html/select.js b/src/html/select.js index e20adb38..836669fd 100644 --- a/src/html/select.js +++ b/src/html/select.js @@ -108,3 +108,7 @@ __extend__(HTMLSelectElement.prototype, { return '[object HTMLSelectElement]'; } }); + +// Named Element Support +HTMLElement.registerSetAttribute('SELECT', 'name', + __updateFormForNamedElement__); \ No newline at end of file diff --git a/src/html/textarea.js b/src/html/textarea.js index ad5ee4a2..18f47a22 100644 --- a/src/html/textarea.js +++ b/src/html/textarea.js @@ -38,3 +38,7 @@ HTMLElement.registerSetAttribute('TEXTAREA', 'value', function(node, value) { // complicated. For now, do nothing }); */ + +// Named Element Support +HTMLElement.registerSetAttribute('TEXTAREA', 'name', + __updateFormForNamedElement__); From 97e7c3b6825425c2a89c66107f91b090d8ed6717 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 22 Apr 2010 23:09:03 -0400 Subject: [PATCH 02/10] fix bad checkin --- src/html/element.js | 2 -- src/html/form.js | 13 ++++++++++++- src/html/object.js | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/html/element.js b/src/html/element.js index 30e92fec..3c93bff1 100644 --- a/src/html/element.js +++ b/src/html/element.js @@ -235,7 +235,6 @@ __extend__(HTMLElement.prototype, { importNode: function(othernode, deep) { var newnode = Element.prototype.importNode.apply(this, arguments); this.ownerDocument._addNamedMap(newnode); - this._updateFormForNamedElement(newnode); return newnode; }, @@ -244,7 +243,6 @@ __extend__(HTMLElement.prototype, { var newnode = Element.prototype.replaceNode.apply(this, arguments); this.ownerDocument._removeNamedMap(oldchild); this.ownerDocument._addNamedMap(newnode); - this._updateFormForNamedElement(newnode); return newnode; } }); diff --git a/src/html/form.js b/src/html/form.js index 9b3d2dc2..a521cb43 100644 --- a/src/html/form.js +++ b/src/html/form.js @@ -66,7 +66,18 @@ __extend__(HTMLFormElement.prototype,{ var alist = []; var i; for (i = 0; i < nodes.length; ++i) { - if (HTMLFormElement.prototype._isFormNamedElement(nodes[i])) { + nodename = nodes[i].nodeName; + // would like to replace switch with something else + // since it's redundant with the SetAttribute callbacks + switch (nodes[i].nodeName) { + case 'BUTTON': + case 'FIELDSET': + case 'INPUT': + case 'KEYGEN': + case 'OBJECT': + case 'OUTPUT': + case 'SELECT': + case 'TEXTAREA': alist.push(nodes[i]); this[i] = nodes[i]; if ('name' in nodes[i]) { diff --git a/src/html/object.js b/src/html/object.js index 8d3acbfa..342ff5c2 100644 --- a/src/html/object.js +++ b/src/html/object.js @@ -89,3 +89,6 @@ __extend__(HTMLObjectElement.prototype, { } }); +// Named Element Support +HTMLElement.registerSetAttribute('OBJECT', 'name', + __updateFormForNamedElement__); From 27318d441a0c858535a88aa728b85bea164f5029 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Apr 2010 00:03:07 -0400 Subject: [PATCH 03/10] moved named elements tests from window to parser (since it require innerHTML+parsing) --- specs/parser/spec.js | 45 +++++++++++++++++++++++++++++++++++++++++++- specs/window/spec.js | 36 ----------------------------------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/specs/parser/spec.js b/specs/parser/spec.js index cb99b111..dfca7a8a 100644 --- a/specs/parser/spec.js +++ b/specs/parser/spec.js @@ -357,4 +357,47 @@ test('Link Loading', function(){ doc.write(''); doc.close(); stop(); -}); \ No newline at end of file +}); + +test('Form Named Element Lookup', function(){ + expect(8); + if ((typeof Envjs == 'undefined') || !Envjs) { + Envjs = {}; + } + var iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + + iframe.addEventListener('load', function(){ + var doc = iframe.contentDocument; + ok(true, 'frame loaded'); + var form = doc.foo; + var elements = form.elements; + ok(elements instanceof HTMLCollection, "form.elements is an HTMLCollection"); + equals(elements.length, 0, "form.elements does not include non-form elements"); + equals(form.length, 0, "form.length is 0"); + + // ok now let's try to use innerHTML + var str = '
'; + doc.body.innerHTML = str; + form = doc.bar; + elements = doc.bar.elements; + equals(elements.length, 1, 'elements length is 1'); + equals(form.length, 1, 'form length is 1'); + //print('element is : ' + elements.input1); + ok(elements.input1 instanceof HTMLInputElement, 'is HTMLInputElement'); + ok(form.input1 instanceof HTMLInputElement, 'is HTMLInputElement'); + + /* + // the other one should be zapped + node2 = doc.foo; + ok(! node2, 'old named element is gone'); + */ + document.body.removeChild(iframe); + start(); + }, false); + + var doc = iframe.contentDocument; + doc.write('
'); + doc.close(); + stop(); +}); diff --git a/specs/window/spec.js b/specs/window/spec.js index 5b00f914..99248d3b 100644 --- a/specs/window/spec.js +++ b/specs/window/spec.js @@ -11,7 +11,6 @@ function giveAHoot(){ var def = 456; } - test('Window Interfaces Available', function(){ ok(Window, 'Window available'); @@ -580,38 +579,3 @@ test('Document Named Element Lookup', function(){ ok(!doc.foo, 'old named element not found via named lookup'); }); -test('Form Named Element Lookup', function(){ - expect(7); - var iframe = document.createElement("iframe"); - var doc; - - document.body.appendChild(iframe); - doc = iframe.contentDocument; - doc.open(); - doc.write('
'); - doc.close(); - - var form = doc.foo; - var elements = form.elements; - ok(elements instanceof HTMLCollection, "form.elements is an HTMLCollection"); - equals(elements.length, 0, "form.elements does not include non-form elements"); - equals(form.length, 0, "form.length is 0"); - - - // ok now let's try to use innerHTML - var str = '
'; - doc.body.innerHTML = str; - form = doc.bar; - elements = doc.bar.elements; - equals(elements.length, 1); - equals(form.length, 1); - //print('element is : ' + elements.input1); - ok(elements.input1 instanceof HTMLInputElement); - ok(form.input1 instanceof HTMLInputElement); - - /* - // the other one should be zapped - node2 = doc.foo; - ok(! node2, 'old named element is gone'); - */ -}); From 5af9a67e5e1ab89a9eddb78432bf5796bff04c33 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Apr 2010 00:12:08 -0400 Subject: [PATCH 04/10] add tests --- specs/parser/spec.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/specs/parser/spec.js b/specs/parser/spec.js index dfca7a8a..11497678 100644 --- a/specs/parser/spec.js +++ b/specs/parser/spec.js @@ -360,7 +360,7 @@ test('Link Loading', function(){ }); test('Form Named Element Lookup', function(){ - expect(8); + expect(10); if ((typeof Envjs == 'undefined') || !Envjs) { Envjs = {}; } @@ -387,6 +387,12 @@ test('Form Named Element Lookup', function(){ ok(elements.input1 instanceof HTMLInputElement, 'is HTMLInputElement'); ok(form.input1 instanceof HTMLInputElement, 'is HTMLInputElement'); + // let's change the name + var node = form.input1; + node.name = 'input2'; + ok(elements.input1 instanceof HTMLInputElement, 'is HTMLInputElement'); + ok(form.input1 instanceof HTMLInputElement, 'is HTMLInputElement'); + /* // the other one should be zapped node2 = doc.foo; From a2e8203d74ebc4006dcae75de6eeebd464b6db1e Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Apr 2010 00:55:25 -0400 Subject: [PATCH 05/10] add checker-outer for html5 parser from mozillas hg repo --- htmlparser/gwt2/checkout.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100755 htmlparser/gwt2/checkout.sh diff --git a/htmlparser/gwt2/checkout.sh b/htmlparser/gwt2/checkout.sh new file mode 100755 index 00000000..316560b1 --- /dev/null +++ b/htmlparser/gwt2/checkout.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# This is where active development occurs +# +hg clone https://hg.mozilla.org/projects/htmlparser \ No newline at end of file From 528d62dff28f08e54b3f52b5109b0d1e4adf44ec Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Apr 2010 09:38:28 -0400 Subject: [PATCH 06/10] fix HTMLInputElement, add 28 tests, runs in FF --- specs/html/spec.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++ src/html/input.js | 56 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 106 insertions(+), 10 deletions(-) diff --git a/specs/html/spec.js b/specs/html/spec.js index bac1acd9..61316c8f 100644 --- a/specs/html/spec.js +++ b/specs/html/spec.js @@ -421,6 +421,66 @@ test('HTMLHtmlElement', function() { equals(a.toString(), '[object HTMLHtmlElement]'); }); +test('HTMLInputElement', function() { + var a = document.createElement('input'); + ok(a, 'element created'); + equals(a.toString(), '[object HTMLInputElement]'); + equals(a.alt, '', 'empty alt is string'); + a.alt = 'foo'; + equals(a.alt, 'foo', 'set alt'); + + equals(a.src, '', 'empty src is string'); + + /** + * Checked is a virtual state, NOT an attribute + * + */ + equals(a.defaultChecked, false, 'defaultChecked value is false'); + equals(a.checked, false, 'default checked value is false'); + equals(a.getAttribute('checked'), null, 'getAttribte(checked) is null'); + equals(a.hasAttribute('checked'), false, 'hasAttribute(checked) is false'); + + equals(typeof a.checked, 'boolean', 'default checked value is boolean'); + a.checked = true; + equals(a.checked, true, 'set checked value is true'); + equals(a.getAttribute('checked'), null, 'getAttribte(checked) is null'); + equals(a.hasAttribute('checked'), false, 'hasAttribute(checked) is false'); + + a.checked = false; + equals(a.defaultChecked, false, 'defaultChecked value is still false'); + a.defaultChecked = true; + equals(a.defaultChecked, true, 'set defaultChecked value is true'); + equals(a.checked, false, 'set checked is still false'); + equals(a.getAttribute('checked'), '', 'getAttribte(checked) is null'); + equals(a.hasAttribute('checked'), true, 'hasAttribute(checked) is false'); + + equals(a.useMap, '', 'useMap is false'); + equals(typeof a.useMap, 'string', 'default useMap value is boolean'); + + /** + * Numeric-like things + */ + equals(a.maxLength, -1, 'default maxLength'); + equals(typeof a.maxLength, 'number', 'default maxLegth is number'); + + // FF says it's undefined! + //equals(typeof a.height, 'undefined', 'default height is undefined'); + //equals(typeof a.width,'undefined', 'default width is undefined'); + + a.maxLength = '10'; + equals(a.maxLength, 10, 'set maxLength'); + equals(typeof a.maxLength, 'number', 'maxLength is number'); + + a.width = '10'; + equals(a.width, 10, 'set width'); + equals(typeof a.width, 'string', 'width is number'); + + a.height = '10'; + equals(a.height, 10, 'set height'); + equals(typeof a.height, 'string', 'height is number'); + +}); + test('HTMLLabelElement', function() { var element; diff --git a/src/html/input.js b/src/html/input.js index 21b58b74..b50a734e 100644 --- a/src/html/input.js +++ b/src/html/input.js @@ -1,52 +1,88 @@ /** - * HTMLInputElement - DOM Level 2 + * HTMLInputElement * * HTML5: 4.10.5 The input element * http://dev.w3.org/html5/spec/Overview.html#the-input-element */ HTMLInputElement = function(ownerDocument) { HTMLInputAreaCommon.apply(this, arguments); + this._checked = null; }; HTMLInputElement.prototype = new HTMLInputAreaCommon(); __extend__(HTMLInputElement.prototype, { get alt(){ - return this.getAttribute('alt'); + return this.getAttribute('alt') || ''; }, set alt(value){ this.setAttribute('alt', value); }, + + /** + * 'checked' returns state, NOT the value of the attribute + */ get checked(){ - return (this.getAttribute('checked') === 'checked'); + if (this._checked === null) { + this._checked = this.defaultChecked; + } + return this._checked; }, set checked(value){ - this.setAttribute('checked', (value ? 'checked' :'')); + // force to boolean value + this._checked = (value) ? true : false; }, + + /** + * 'defaultChecked' actually reflects if the 'checked' attribute + * is present or not + */ get defaultChecked(){ - return this.getAttribute('defaultChecked'); + return this.hasAttribute('checked'); + }, + set defaultChecked(val){ + if (val) { + this.setAttribute('checked', ''); + } else { + if (this.defaultChecked) { + this.removeAttribute('checked'); + } + } }, + + /** + * Height is a string + */ get height(){ - return this.getAttribute('height'); + // spec says it is a string + return this.getAttribute('height') || ''; }, set height(value){ this.setAttribute('height',value); }, + + /** + * MaxLength is a number + */ get maxLength(){ - return Number(this.getAttribute('maxlength')||'0'); + return Number(this.getAttribute('maxlength')||'-1'); }, set maxLength(value){ this.setAttribute('maxlength', value); }, get src(){ - return this.getAttribute('src'); + return this.getAttribute('src') || ''; }, set src(value){ this.setAttribute('src', value); }, get useMap(){ - return this.getAttribute('map'); + return this.getAttribute('map') || ''; }, + + /** + * Width: spec says it is a string + */ get width(){ - return this.getAttribute('width'); + return this.getAttribute('width') || ''; }, set width(value){ this.setAttribute('width',value); From 5e46d2c27a54615d3abb9f9b1a58aacd7e5f7000 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Apr 2010 09:44:34 -0400 Subject: [PATCH 07/10] add unit test for input --- specs/html/spec.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/html/spec.js b/specs/html/spec.js index 61316c8f..5e862eca 100644 --- a/specs/html/spec.js +++ b/specs/html/spec.js @@ -453,6 +453,9 @@ test('HTMLInputElement', function() { equals(a.checked, false, 'set checked is still false'); equals(a.getAttribute('checked'), '', 'getAttribte(checked) is null'); equals(a.hasAttribute('checked'), true, 'hasAttribute(checked) is false'); + a.defaultChecked = false + equals(a.defaultChecked, false, 'set defaultChecked value is false'); + equals(a.hasAttribute('checked'), false, 'hasAttribute(checked) is false'); equals(a.useMap, '', 'useMap is false'); equals(typeof a.useMap, 'string', 'default useMap value is boolean'); From b962a89c2f387ab41a6569e43e15c4fc103b1b1f Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Apr 2010 09:48:05 -0400 Subject: [PATCH 08/10] another unit test for input --- specs/html/spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/html/spec.js b/specs/html/spec.js index 5e862eca..49b99f52 100644 --- a/specs/html/spec.js +++ b/specs/html/spec.js @@ -430,6 +430,8 @@ test('HTMLInputElement', function() { equals(a.alt, 'foo', 'set alt'); equals(a.src, '', 'empty src is string'); + a.src = 'foo'; + equals(a.src, 'foo', 'set src'); /** * Checked is a virtual state, NOT an attribute From a9dcc275317f1793d5895c295af1105f858c41f4 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Apr 2010 09:51:30 -0400 Subject: [PATCH 09/10] got rule wrong for "src"... it a URL and is made absolute --- specs/html/spec.js | 5 +++-- src/html/input.js | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/specs/html/spec.js b/specs/html/spec.js index 49b99f52..6e07c831 100644 --- a/specs/html/spec.js +++ b/specs/html/spec.js @@ -430,8 +430,9 @@ test('HTMLInputElement', function() { equals(a.alt, 'foo', 'set alt'); equals(a.src, '', 'empty src is string'); - a.src = 'foo'; - equals(a.src, 'foo', 'set src'); + a.src = 'http://envjs.com/'; + equals(a.src, 'http://envjs.com/', 'set src'); + // TODO, src should make absolute any relative links /** * Checked is a virtual state, NOT an attribute diff --git a/src/html/input.js b/src/html/input.js index b50a734e..fca718be 100644 --- a/src/html/input.js +++ b/src/html/input.js @@ -68,12 +68,18 @@ __extend__(HTMLInputElement.prototype, { set maxLength(value){ this.setAttribute('maxlength', value); }, + + /** + * Src is a URL string + */ get src(){ return this.getAttribute('src') || ''; }, set src(value){ + // TODO: make absolute any relative URLS this.setAttribute('src', value); }, + get useMap(){ return this.getAttribute('map') || ''; }, From 15ce6028fb75281a5d84ac98f5f456996cca6dca Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 23 Apr 2010 10:00:53 -0400 Subject: [PATCH 10/10] add tests and fixes for testarea. Runs in FF --- specs/html/spec.js | 18 ++++++++++++++++++ src/html/textarea.js | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/specs/html/spec.js b/specs/html/spec.js index 6e07c831..d01141e4 100644 --- a/specs/html/spec.js +++ b/specs/html/spec.js @@ -632,6 +632,24 @@ test('HTMLTableSectionElement', function() { equals(element.toString(), '[object HTMLTableSectionElement]', 'toString'); }); +test('HTMLTextArea', function() { + var e; + e = document.createElement('textarea'); + ok(e, 'element created'); + equals(e.toString(), '[object HTMLTextAreaElement]', 'toString'); + + equals(e.cols, -1, 'default cols is -1'); + e.cols = '10'; + equals(e.cols, 10, 'set cols'); + equals(typeof e.cols, 'number', 'cols is a number'); + + equals(e.rows, -1, 'default rows is -1'); + e.rows = '11'; + equals(e.rows, 11, 'set row'); + equals(typeof e.rows, 'number', 'rows is a number'); + +}); + test('HTMLTitleElement', function() { var element; element = document.createElement('title'); diff --git a/src/html/textarea.js b/src/html/textarea.js index 18f47a22..faad5c2c 100644 --- a/src/html/textarea.js +++ b/src/html/textarea.js @@ -10,13 +10,13 @@ HTMLTextAreaElement = function(ownerDocument) { HTMLTextAreaElement.prototype = new HTMLInputAreaCommon(); __extend__(HTMLTextAreaElement.prototype, { get cols(){ - return this.getAttribute('cols'); + return Number(this.getAttribute('cols')||'-1'); }, set cols(value){ this.setAttribute('cols', value); }, get rows(){ - return this.getAttribute('rows'); + return Number(this.getAttribute('rows')||'-1'); }, set rows(value){ this.setAttribute('rows', value);