ref: 52ebaa6c60bf90f3b7b326b94723c1999ea99655
dir: /domino-lib/htmlelts.js/
"use strict"; var Node = require('./Node'); var Element = require('./Element'); var CSSStyleDeclaration = require('./CSSStyleDeclaration'); var utils = require('./utils'); var URLUtils = require('./URLUtils'); var defineElement = require('./defineElement'); var htmlElements = exports.elements = {}; var htmlNameToImpl = Object.create(null); exports.createElement = function(doc, localName, prefix) { var impl = htmlNameToImpl[localName] || HTMLUnknownElement; return new impl(doc, localName, prefix); }; function define(spec) { return defineElement(spec, HTMLElement, htmlElements, htmlNameToImpl); } function URL(attr) { return { get: function() { var v = this._getattr(attr); if (v === null) { return ''; } var url = this.doc._resolve(v); return (url === null) ? v : url; }, set: function(value) { this._setattr(attr, value); } }; } function CORS(attr) { return { get: function() { var v = this._getattr(attr); if (v === null) { return null; } if (v.toLowerCase() === 'use-credentials') { return 'use-credentials'; } return 'anonymous'; }, set: function(value) { if (value===null || value===undefined) { this.removeAttribute(attr); } else { this._setattr(attr, value); } } }; } var REFERRER = { type: ["", "no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url"], missing: '', }; // XXX: the default value for tabIndex should be 0 if the element is // focusable and -1 if it is not. But the full definition of focusable // is actually hard to compute, so for now, I'll follow Firefox and // just base the default value on the type of the element. var focusableElements = { "A":true, "LINK":true, "BUTTON":true, "INPUT":true, "SELECT":true, "TEXTAREA":true, "COMMAND":true }; var HTMLFormElement = function(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); this._form = null; // Prevent later deoptimization }; var HTMLElement = exports.HTMLElement = define({ superclass: Element, ctor: function HTMLElement(doc, localName, prefix) { Element.call(this, doc, localName, utils.NAMESPACE.HTML, prefix); }, props: { innerHTML: { get: function() { return this.serialize(); }, set: function(v) { var parser = this.ownerDocument.implementation.mozHTMLParser( this.ownerDocument._address, this); parser.parse(v===null ? '' : String(v), true); // Remove any existing children of this node var target = (this instanceof htmlNameToImpl.template) ? this.content : this; while(target.hasChildNodes()) target.removeChild(target.firstChild); // Now copy newly parsed children to this node target.appendChild(parser._asDocumentFragment()); } }, style: { get: function() { if (!this._style) this._style = new CSSStyleDeclaration(this); return this._style; }, set: function(v) { if (v===null||v===undefined) { v = ''; } this._setattr('style', String(v)); }}, // These can't really be implemented server-side in a reasonable way. blur: { value: function() {}}, focus: { value: function() {}}, forceSpellCheck: { value: function() {}}, click: { value: function() { if (this._click_in_progress) return; this._click_in_progress = true; try { if (this._pre_click_activation_steps) this._pre_click_activation_steps(); var event = this.ownerDocument.createEvent("MouseEvent"); event.initMouseEvent("click", true, true, this.ownerDocument.defaultView, 1, 0, 0, 0, 0, // These 4 should be initialized with // the actually current keyboard state // somehow... false, false, false, false, 0, null ); // Dispatch this as an untrusted event since it is synthetic var success = this.dispatchEvent(event); if (success) { if (this._post_click_activation_steps) this._post_click_activation_steps(event); } else { if (this._cancelled_activation_steps) this._cancelled_activation_steps(); } } finally { this._click_in_progress = false; } }}, submit: { value: utils.nyi }, }, attributes: { title: String, lang: String, dir: {type: ["ltr", "rtl", "auto"], missing: ''}, accessKey: String, hidden: Boolean, tabIndex: {type: "long", default: function() { if (this.tagName in focusableElements || this.contentEditable) return 0; else return -1; }} }, events: [ "abort", "canplay", "canplaythrough", "change", "click", "contextmenu", "cuechange", "dblclick", "drag", "dragend", "dragenter", "dragleave", "dragover", "dragstart", "drop", "durationchange", "emptied", "ended", "input", "invalid", "keydown", "keypress", "keyup", "loadeddata", "loadedmetadata", "loadstart", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup", "mousewheel", "pause", "play", "playing", "progress", "ratechange", "readystatechange", "reset", "seeked", "seeking", "select", "show", "stalled", "submit", "suspend", "timeupdate", "volumechange", "waiting", // These last 5 event types will be overriden by HTMLBodyElement "blur", "error", "focus", "load", "scroll" ] }); // XXX: reflect contextmenu as contextMenu, with element type // style: the spec doesn't call this a reflected attribute. // may want to handle it manually. // contentEditable: enumerated, not clear if it is actually // reflected or requires custom getter/setter. Not listed as // "limited to known values". Raises syntax_err on bad setting, // so I think this is custom. // contextmenu: content is element id, idl type is an element // draggable: boolean, but not a reflected attribute // dropzone: reflected SettableTokenList, experimental, so don't // implement it right away. // data-* attributes: need special handling in setAttribute? // Or maybe that isn't necessary. Can I just scan the attribute list // when building the dataset? Liveness and caching issues? // microdata attributes: many are simple reflected attributes, but // I'm not going to implement this now. var HTMLUnknownElement = define({ ctor: function HTMLUnknownElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); } }); var formAssociatedProps = { // See http://www.w3.org/TR/html5/association-of-controls-and-forms.html#form-owner form: { get: function() { return this._form; }} }; define({ tag: 'a', ctor: function HTMLAnchorElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { _post_click_activation_steps: { value: function(e) { if (this.href) { // Follow the link // XXX: this is just a quick hack // XXX: the HTML spec probably requires more than this this.ownerDocument.defaultView.location = this.href; } }}, }, attributes: { href: URL, ping: String, download: String, target: String, rel: String, media: String, hreflang: String, type: String, referrerPolicy: REFERRER, // Obsolete coords: String, charset: String, name: String, rev: String, shape: String, } }); // Latest WhatWG spec says these methods come via HTMLHyperlinkElementUtils URLUtils._inherit(htmlNameToImpl.a.prototype); define({ tag: 'area', ctor: function HTMLAreaElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { alt: String, target: String, download: String, rel: String, media: String, href: URL, hreflang: String, type: String, shape: String, coords: String, ping: String, // XXX: also reflect relList referrerPolicy: REFERRER, // Obsolete noHref: Boolean, } }); // Latest WhatWG spec says these methods come via HTMLHyperlinkElementUtils URLUtils._inherit(htmlNameToImpl.area.prototype); define({ tag: 'br', ctor: function HTMLBRElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete clear: String }, }); define({ tag: 'base', ctor: function HTMLBaseElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { "target": String } }); define({ tag: 'body', ctor: function HTMLBodyElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, // Certain event handler attributes on a <body> tag actually set // handlers for the window rather than just that element. Define // getters and setters for those here. Note that some of these override // properties on HTMLElement.prototype. // XXX: If I add support for <frameset>, these have to go there, too // XXX // When the Window object is implemented, these attribute will have // to work with the same-named attributes on the Window. events: [ "afterprint", "beforeprint", "beforeunload", "blur", "error", "focus","hashchange", "load", "message", "offline", "online", "pagehide", "pageshow","popstate","resize","scroll","storage","unload", ], attributes: { // Obsolete text: { type: String, treatNullAsEmptyString: true }, link: { type: String, treatNullAsEmptyString: true }, vLink: { type: String, treatNullAsEmptyString: true }, aLink: { type: String, treatNullAsEmptyString: true }, bgColor: { type: String, treatNullAsEmptyString: true }, background: String, } }); define({ tag: 'button', ctor: function HTMLButtonElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: formAssociatedProps, attributes: { name: String, value: String, disabled: Boolean, autofocus: Boolean, type: { type:["submit", "reset", "button", "menu"], missing: 'submit' }, formTarget: String, formNoValidate: Boolean, formMethod: { type: ["get", "post", "dialog"], invalid: 'get', missing: '' }, formEnctype: { type: ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"], invalid: "application/x-www-form-urlencoded", missing: '' }, } }); define({ tag: 'dl', ctor: function HTMLDListElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete compact: Boolean, } }); define({ tag: 'data', ctor: function HTMLDataElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { value: String, } }); define({ tag: 'datalist', ctor: function HTMLDataListElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); } }); define({ tag: 'details', ctor: function HTMLDetailsElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { "open": Boolean } }); define({ tag: 'div', ctor: function HTMLDivElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete align: String } }); define({ tag: 'embed', ctor: function HTMLEmbedElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { src: URL, type: String, width: String, height: String, // Obsolete align: String, name: String, } }); define({ tag: 'fieldset', ctor: function HTMLFieldSetElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: formAssociatedProps, attributes: { disabled: Boolean, name: String } }); define({ tag: 'form', ctor: function HTMLFormElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { action: String, autocomplete: {type:['on', 'off'], missing: 'on'}, name: String, acceptCharset: {name: "accept-charset"}, target: String, noValidate: Boolean, method: { type: ["get", "post", "dialog"], invalid: 'get', missing: 'get' }, // Both enctype and encoding reflect the enctype content attribute enctype: { type: ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"], invalid: "application/x-www-form-urlencoded", missing: "application/x-www-form-urlencoded" }, encoding: {name: 'enctype', type: ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"], invalid: "application/x-www-form-urlencoded", missing: "application/x-www-form-urlencoded" }, } }); define({ tag: 'hr', ctor: function HTMLHRElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete align: String, color: String, noShade: Boolean, size: String, width: String, }, }); define({ tag: 'head', ctor: function HTMLHeadElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); } }); define({ tags: ['h1','h2','h3','h4','h5','h6'], ctor: function HTMLHeadingElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete align: String, }, }); define({ tag: 'html', ctor: function HTMLHtmlElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete version: String } }); define({ tag: 'iframe', ctor: function HTMLIFrameElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); var Window = require('./Window'); // Avoid circular dependencies. this._contentWindow = new Window(); }, props: { contentWindow: { get: function() { return this._contentWindow; } }, contentDocument: { get: function() { return this.contentWindow.document; } }, }, attributes: { src: URL, srcdoc: String, name: String, width: String, height: String, // XXX: sandbox is a reflected settable token list seamless: Boolean, allowFullscreen: Boolean, allowUserMedia: Boolean, allowPaymentRequest: Boolean, referrerPolicy: REFERRER, // Obsolete align: String, scrolling: String, frameBorder: String, longDesc: URL, marginHeight: { type: String, treatNullAsEmptyString: true }, marginWidth: { type: String, treatNullAsEmptyString: true }, } }); define({ tag: 'img', ctor: function HTMLImageElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { alt: String, src: URL, srcset: String, crossOrigin: CORS, useMap: String, isMap: Boolean, height: { type: "unsigned long", default: 0 }, width: { type: "unsigned long", default: 0 }, referrerPolicy: REFERRER, // Obsolete: name: String, lowsrc: URL, align: String, hspace: { type: "unsigned long", default: 0 }, vspace: { type: "unsigned long", default: 0 }, longDesc: URL, border: { type: String, treatNullAsEmptyString: true }, } }); define({ tag: 'input', ctor: function HTMLInputElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: { form: formAssociatedProps.form, _post_click_activation_steps: { value: function(e) { if (this.type === 'checkbox') { this.checked = !this.checked; } else if (this.type === 'radio') { var group = this.form.getElementsByName(this.name); for (var i=group.length-1; i >= 0; i--) { var el = group[i]; el.checked = (el === this); } } }}, }, attributes: { name: String, disabled: Boolean, autofocus: Boolean, accept: String, alt: String, max: String, min: String, pattern: String, placeholder: String, step: String, dirName: String, defaultValue: {name: 'value'}, multiple: Boolean, required: Boolean, readOnly: Boolean, checked: Boolean, value: String, src: URL, defaultChecked: {name: 'checked', type: Boolean}, size: {type: 'unsigned long', default: 20, min: 1, setmin: 1}, width: {type: 'unsigned long', min: 0, setmin: 0, default: 0}, height: {type: 'unsigned long', min: 0, setmin: 0, default: 0}, minLength: {type: 'unsigned long', min: 0, setmin: 0, default: -1}, maxLength: {type: 'unsigned long', min: 0, setmin: 0, default: -1}, autocomplete: String, // It's complicated type: { type: ["text", "hidden", "search", "tel", "url", "email", "password", "datetime", "date", "month", "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio", "file", "submit", "image", "reset", "button"], missing: 'text' }, formTarget: String, formNoValidate: Boolean, formMethod: { type: ["get", "post"], invalid: 'get', missing: '' }, formEnctype: { type: ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"], invalid: "application/x-www-form-urlencoded", missing: '' }, inputMode: { type: [ "verbatim", "latin", "latin-name", "latin-prose", "full-width-latin", "kana", "kana-name", "katakana", "numeric", "tel", "email", "url" ], missing: '' }, // Obsolete align: String, useMap: String, } }); define({ tag: 'keygen', ctor: function HTMLKeygenElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: formAssociatedProps, attributes: { name: String, disabled: Boolean, autofocus: Boolean, challenge: String, keytype: { type:["rsa"], missing: '' }, } }); define({ tag: 'li', ctor: function HTMLLIElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { value: {type: "long", default: 0}, // Obsolete type: String, } }); define({ tag: 'label', ctor: function HTMLLabelElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: formAssociatedProps, attributes: { htmlFor: {name: 'for', type: String} } }); define({ tag: 'legend', ctor: function HTMLLegendElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete align: String }, }); define({ tag: 'link', ctor: function HTMLLinkElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // XXX Reflect DOMSettableTokenList sizes also DOMTokenList relList href: URL, rel: String, media: String, hreflang: String, type: String, crossOrigin: CORS, nonce: String, integrity: String, referrerPolicy: REFERRER, // Obsolete charset: String, rev: String, target: String, } }); define({ tag: 'map', ctor: function HTMLMapElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { name: String } }); define({ tag: 'menu', ctor: function HTMLMenuElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // XXX: not quite right, default should be popup if parent element is // popup. type: { type: [ 'context', 'popup', 'toolbar' ], missing: 'toolbar' }, label: String, // Obsolete compact: Boolean, } }); define({ tag: 'meta', ctor: function HTMLMetaElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { name: String, content: String, httpEquiv: {name: 'http-equiv', type: String}, // Obsolete scheme: String, } }); define({ tag: 'meter', ctor: function HTMLMeterElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: formAssociatedProps }); define({ tags: ['ins', 'del'], ctor: function HTMLModElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { cite: URL, dateTime: String } }); define({ tag: 'ol', ctor: function HTMLOListElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { // Utility function (see the start attribute default value). Returns // the number of <li> children of this element _numitems: { get: function() { var items = 0; this.childNodes.forEach(function(n) { if (n.nodeType === Node.ELEMENT_NODE && n.tagName === "LI") items++; }); return items; }} }, attributes: { type: String, reversed: Boolean, start: { type: "long", default: function() { // The default value of the start attribute is 1 unless the list is // reversed. Then it is the # of li children if (this.reversed) return this._numitems; else return 1; } }, // Obsolete compact: Boolean, } }); define({ tag: 'object', ctor: function HTMLObjectElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: formAssociatedProps, attributes: { data: URL, type: String, name: String, useMap: String, typeMustMatch: Boolean, width: String, height: String, // Obsolete align: String, archive: String, code: String, declare: Boolean, hspace: { type: "unsigned long", default: 0 }, standby: String, vspace: { type: "unsigned long", default: 0 }, codeBase: URL, codeType: String, border: { type: String, treatNullAsEmptyString: true }, } }); define({ tag: 'optgroup', ctor: function HTMLOptGroupElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { disabled: Boolean, label: String } }); define({ tag: 'option', ctor: function HTMLOptionElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { form: { get: function() { var p = this.parentNode; while (p && p.nodeType === Node.ELEMENT_NODE) { if (p.localName === 'select') return p.form; p = p.parentNode; } }}, value: { get: function() { return this._getattr('value') || this.text; }, set: function(v) { this._setattr('value', v); }, }, text: { get: function() { // Strip and collapse whitespace return this.textContent.replace(/[ \t\n\f\r]+/g, ' ').trim(); }, set: function(v) { this.textContent = v; }, }, // missing: index }, attributes: { disabled: Boolean, defaultSelected: {name: 'selected', type: Boolean}, label: String, } }); define({ tag: 'output', ctor: function HTMLOutputElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: formAssociatedProps, attributes: { // XXX Reflect for/htmlFor as a settable token list name: String } }); define({ tag: 'p', ctor: function HTMLParagraphElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete align: String } }); define({ tag: 'param', ctor: function HTMLParamElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { name: String, value: String, // Obsolete type: String, valueType: String, } }); define({ tags: ['pre',/*legacy elements:*/'listing','xmp'], ctor: function HTMLPreElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete width: { type: "long", default: 0 }, } }); define({ tag: 'progress', ctor: function HTMLProgressElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: formAssociatedProps, attributes: { max: {type: Number, float: true, default: 1.0, min: 0} } }); define({ tags: ['q', 'blockquote'], ctor: function HTMLQuoteElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { cite: URL } }); define({ tag: 'script', ctor: function HTMLScriptElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { text: { get: function() { var s = ""; for(var i = 0, n = this.childNodes.length; i < n; i++) { var child = this.childNodes[i]; if (child.nodeType === Node.TEXT_NODE) s += child._data; } return s; }, set: function(value) { this.removeChildren(); if (value !== null && value !== "") { this.appendChild(this.ownerDocument.createTextNode(value)); } } } }, attributes: { src: URL, type: String, charset: String, defer: Boolean, async: Boolean, crossOrigin: CORS, nonce: String, integrity: String, } }); define({ tag: 'select', ctor: function HTMLSelectElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: { form: formAssociatedProps.form, options: { get: function() { return this.getElementsByTagName('option'); }} }, attributes: { autocomplete: String, // It's complicated name: String, disabled: Boolean, autofocus: Boolean, multiple: Boolean, required: Boolean, size: {type: "unsigned long", default: 0} } }); define({ tag: 'source', ctor: function HTMLSourceElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { src: URL, type: String, media: String } }); define({ tag: 'span', ctor: function HTMLSpanElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); } }); define({ tag: 'style', ctor: function HTMLStyleElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { media: String, type: String, scoped: Boolean } }); define({ tag: 'caption', ctor: function HTMLTableCaptionElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { // Obsolete align: String, } }); define({ ctor: function HTMLTableCellElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { colSpan: {type: "unsigned long", default: 1}, rowSpan: {type: "unsigned long", default: 1}, //XXX Also reflect settable token list headers scope: { type: ['row','col','rowgroup','colgroup'], missing: '' }, abbr: String, // Obsolete align: String, axis: String, height: String, width: String, ch: { name: 'char', type: String }, chOff: { name: 'charoff', type: String }, noWrap: Boolean, vAlign: String, bgColor: { type: String, treatNullAsEmptyString: true }, } }); define({ tags: ['col', 'colgroup'], ctor: function HTMLTableColElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { span: {type: 'limited unsigned long with fallback', default: 1, min: 1}, // Obsolete align: String, ch: { name: 'char', type: String }, chOff: { name: 'charoff', type: String }, vAlign: String, width: String, } }); define({ tag: 'table', ctor: function HTMLTableElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { rows: { get: function() { return this.getElementsByTagName('tr'); }} }, attributes: { // Obsolete align: String, border: String, frame: String, rules: String, summary: String, width: String, bgColor: { type: String, treatNullAsEmptyString: true }, cellPadding: { type: String, treatNullAsEmptyString: true }, cellSpacing: { type: String, treatNullAsEmptyString: true }, } }); define({ tag: 'template', ctor: function HTMLTemplateElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); this._contentFragment = doc._templateDoc.createDocumentFragment(); }, props: { content: { get: function() { return this._contentFragment; } }, serialize: { value: function() { return this.content.serialize(); } } } }); define({ tag: 'tr', ctor: function HTMLTableRowElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { cells: { get: function() { return this.querySelectorAll('td,th'); }} }, attributes: { // Obsolete align: String, ch: { name: 'char', type: String }, chOff: { name: 'charoff', type: String }, vAlign: String, bgColor: { type: String, treatNullAsEmptyString: true }, }, }); define({ tags: ['thead', 'tfoot', 'tbody'], ctor: function HTMLTableSectionElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { rows: { get: function() { return this.getElementsByTagName('tr'); }} }, attributes: { // Obsolete align: String, ch: { name: 'char', type: String }, chOff: { name: 'charoff', type: String }, vAlign: String, } }); define({ tag: 'textarea', ctor: function HTMLTextAreaElement(doc, localName, prefix) { HTMLFormElement.call(this, doc, localName, prefix); }, props: { form: formAssociatedProps.form, type: { get: function() { return 'textarea'; } }, defaultValue: { get: function() { return this.textContent; }, set: function(v) { this.textContent = v; }, }, value: { get: function() { return this.defaultValue; /* never dirty */ }, set: function(v) { // This isn't completely correct: according to the spec, this // should "dirty" the API value, and result in // `this.value !== this.defaultValue`. But for most of what // folks want to do, this implementation should be fine: this.defaultValue = v; }, }, textLength: { get: function() { return this.value.length; } }, }, attributes: { autocomplete: String, // It's complicated name: String, disabled: Boolean, autofocus: Boolean, placeholder: String, wrap: String, dirName: String, required: Boolean, readOnly: Boolean, rows: {type: 'limited unsigned long with fallback', default: 2 }, cols: {type: 'limited unsigned long with fallback', default: 20 }, maxLength: {type: 'unsigned long', min: 0, setmin: 0, default: -1}, minLength: {type: 'unsigned long', min: 0, setmin: 0, default: -1}, inputMode: { type: [ "verbatim", "latin", "latin-name", "latin-prose", "full-width-latin", "kana", "kana-name", "katakana", "numeric", "tel", "email", "url" ], missing: '' }, } }); define({ tag: 'time', ctor: function HTMLTimeElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { dateTime: String, pubDate: Boolean } }); define({ tag: 'title', ctor: function HTMLTitleElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { text: { get: function() { return this.textContent; }} } }); define({ tag: 'ul', ctor: function HTMLUListElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { type: String, // Obsolete compact: Boolean, } }); define({ ctor: function HTMLMediaElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { src: URL, crossOrigin: CORS, preload: { type:["metadata", "none", "auto", {value: "", alias: "auto"}], missing: 'auto' }, loop: Boolean, autoplay: Boolean, mediaGroup: String, controls: Boolean, defaultMuted: {name: "muted", type: Boolean} } }); define({ tag: 'audio', superclass: htmlElements.HTMLMediaElement, ctor: function HTMLAudioElement(doc, localName, prefix) { htmlElements.HTMLMediaElement.call(this, doc, localName, prefix); } }); define({ tag: 'video', superclass: htmlElements.HTMLMediaElement, ctor: function HTMLVideoElement(doc, localName, prefix) { htmlElements.HTMLMediaElement.call(this, doc, localName, prefix); }, attributes: { poster: URL, width: {type: "unsigned long", min: 0, default: 0 }, height: {type: "unsigned long", min: 0, default: 0 } } }); define({ tag: 'td', superclass: htmlElements.HTMLTableCellElement, ctor: function HTMLTableDataCellElement(doc, localName, prefix) { htmlElements.HTMLTableCellElement.call(this, doc, localName, prefix); } }); define({ tag: 'th', superclass: htmlElements.HTMLTableCellElement, ctor: function HTMLTableHeaderCellElement(doc, localName, prefix) { htmlElements.HTMLTableCellElement.call(this, doc, localName, prefix); }, }); define({ tag: 'frameset', ctor: function HTMLFrameSetElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); } }); define({ tag: 'frame', ctor: function HTMLFrameElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); } }); define({ tag: 'canvas', ctor: function HTMLCanvasElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { getContext: { value: utils.nyi }, probablySupportsContext: { value: utils.nyi }, setContext: { value: utils.nyi }, transferControlToProxy: { value: utils.nyi }, toDataURL: { value: utils.nyi }, toBlob: { value: utils.nyi } }, attributes: { width: { type: "unsigned long", default: 300}, height: { type: "unsigned long", default: 150} } }); define({ tag: 'dialog', ctor: function HTMLDialogElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { show: { value: utils.nyi }, showModal: { value: utils.nyi }, close: { value: utils.nyi } }, attributes: { open: Boolean, returnValue: String } }); define({ tag: 'menuitem', ctor: function HTMLMenuItemElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, props: { // The menuitem's label _label: { get: function() { var val = this._getattr('label'); if (val !== null && val !== '') { return val; } val = this.textContent; // Strip and collapse whitespace return val.replace(/[ \t\n\f\r]+/g, ' ').trim(); } }, // The menuitem label IDL attribute label: { get: function() { var val = this._getattr('label'); if (val !== null) { return val; } return this._label; }, set: function(v) { this._setattr('label', v); }, } }, attributes: { type: { type: ["command","checkbox","radio"], missing: 'command' }, icon: URL, disabled: Boolean, checked: Boolean, radiogroup: String, default: Boolean } }); define({ tag: 'source', ctor: function HTMLSourceElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { srcset: String, sizes: String, media: String, src: URL, type: String } }); define({ tag: 'track', ctor: function HTMLTrackElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { src: URL, srclang: String, label: String, default: Boolean, kind: { type: ["subtitles", "captions", "descriptions", "chapters", "metadata"], missing: 'subtitles', invalid: 'metadata' }, }, props: { NONE: { get: function() { return 0; } }, LOADING: { get: function() { return 1; } }, LOADED: { get: function() { return 2; } }, ERROR: { get: function() { return 3; } }, readyState: { get: utils.nyi }, track: { get: utils.nyi } } }); define({ // obsolete tag: 'font', ctor: function HTMLFontElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { color: { type: String, treatNullAsEmptyString: true }, face: { type: String }, size: { type: String }, }, }); define({ // obsolete tag: 'dir', ctor: function HTMLDirectoryElement(doc, localName, prefix) { HTMLElement.call(this, doc, localName, prefix); }, attributes: { compact: Boolean, }, }); define({ tags: [ "abbr", "address", "article", "aside", "b", "bdi", "bdo", "cite", "code", "dd", "dfn", "dt", "em", "figcaption", "figure", "footer", "header", "hgroup", "i", "kbd", "main", "mark", "nav", "noscript", "rb", "rp", "rt", "rtc", "ruby", "s", "samp", "section", "small", "strong", "sub", "summary", "sup", "u", "var", "wbr", // Legacy elements "acronym", "basefont", "big", "center", "nobr", "noembed", "noframes", "plaintext", "strike", "tt" ] });