UNPKG

d3

Version:

A small, free JavaScript library for manipulating documents based on data.

1,552 lines (1,450 loc) 136 kB
/* * Pure JavaScript Browser Environment * By John Resig <http://ejohn.org/> and the Envjs Team * Copyright 2008-2010 John Resig, under the MIT License * * This file simply provides the global definitions we need to * be able to correctly implement to core browser DOM HTML interfaces. // These are exported/leaked globally HTMLDocument, HTMLElement, HTMLCollection, HTMLAnchorElement, HTMLAreaElement, HTMLBaseElement, HTMLQuoteElement, HTMLBodyElement, HTMLBRElement, HTMLButtonElement, CanvasRenderingContext2D, HTMLCanvasElement, HTMLTableColElement, HTMLModElement, HTMLDivElement, HTMLDListElement, HTMLFieldSetElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement, HTMLHeadElement, HTMLHeadingElement, HTMLHRElement, HTMLHtmlElement, HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLLabelElement, HTMLLegendElement, HTMLLIElement, HTMLLinkElement, HTMLMapElement, HTMLMetaElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement, HTMLOptionElement, HTMLParagraphElement, HTMLParamElement, HTMLPreElement, HTMLScriptElement, HTMLSelectElement, HTMLSpanElement, HTMLStyleElement, HTMLTableElement, HTMLTableSectionElement, HTMLTableCellElement, HTMLTableDataCellElement, HTMLTableHeaderCellElement, HTMLTableRowElement, HTMLTextAreaElement, HTMLTitleElement, HTMLUListElement, HTMLUnknownElement, Image, Option, __loadImage__, __loadLink__; */ var Envjs = Envjs || require('./platform/core').Envjs, After = After || require('./platform/core').After, Document = Document || require('./dom').Document, Element = Element || require('./dom').Element, NodeList = NodeList || require('./dom').NodeList, Node = Node || require('./dom').Node, Event = Event || require('./event').Event; /* * Envjs html.1.3.pre03 * Pure JavaScript Browser Environment * By John Resig <http://ejohn.org/> and the Envjs Team * Copyright 2008-2010 John Resig, under the MIT License */ //CLOSURE_START (function(){ /** * @author ariel flesler * http://flesler.blogspot.com/2008/11/fast-trim-function-for-javascript.html * @param {Object} str */ function __trim__( str ){ return (str || "").replace( /^\s+|\s+$/g, "" ); } /** * @author john resig */ // Helper method for extending one object with another. function __extend__(a,b) { for ( var i in b ) { if(b.hasOwnProperty(i)){ var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i); if ( g || s ) { if ( g ) { a.__defineGetter__(i, g); } if ( s ) { a.__defineSetter__(i, s); } } else { a[i] = b[i]; } } } return a; } /** * @author john resig */ //from jQuery function __setArray__( target, array ) { // Resetting the length to 0, then using the native Array push // is a super-fast way to populate an object with array-like properties target.length = 0; Array.prototype.push.apply( target, array ); } var __addNamedMap__, __removeNamedMap__, __isNamedElement__, __selectparent__ ,//see option.js __updateoptions__, //see option.js __loadLink__; //see link.js /** * @class HTMLDocument * The Document interface represents the entire HTML or XML document. * Conceptually, it is the root of the document tree, and provides * the primary access to the document's data. * * @extends Document */ (function(){ var log = Envjs.logger(); Envjs.once('tick', function(){ log = Envjs.logger('Envjs.HTML.HTMLDocument'). debug('HTMLDocument available'); }); exports.HTMLDocument = HTMLDocument = function(implementation, ownerWindow, referrer) { Document.apply(this, arguments); this.referrer = referrer || ''; this.baseURI = "about:blank"; this.ownerWindow = ownerWindow; }; HTMLDocument.prototype = new Document(); __extend__(HTMLDocument.prototype, { createElement: function(tagName){ var node; tagName = tagName.toUpperCase(); // create Element specifying 'this' as ownerDocument // This is an html document so we need to use explicit interfaces per the //TODO: would be much faster as a big switch switch(tagName){ case "A": node = new HTMLAnchorElement(this);break; case "AREA": node = new HTMLAreaElement(this);break; case "BASE": node = new HTMLBaseElement(this);break; case "BLOCKQUOTE": node = new HTMLQuoteElement(this);break; case "CANVAS": node = new HTMLCanvasElement(this);break; case "Q": node = new HTMLQuoteElement(this);break; case "BODY": node = new HTMLBodyElement(this);break; case "BR": node = new HTMLBRElement(this);break; case "BUTTON": node = new HTMLButtonElement(this);break; case "CAPTION": node = new HTMLElement(this);break; case "COL": node = new HTMLTableColElement(this);break; case "COLGROUP": node = new HTMLTableColElement(this);break; case "DEL": node = new HTMLModElement(this);break; case "INS": node = new HTMLModElement(this);break; case "DIV": node = new HTMLDivElement(this);break; case "DL": node = new HTMLDListElement(this);break; case "DT": node = new HTMLElement(this); break; case "FIELDSET": node = new HTMLFieldSetElement(this);break; case "FORM": node = new HTMLFormElement(this);break; case "FRAME": node = new HTMLFrameElement(this);break; case "FRAMESET": node = new HTMLFrameSetElement(this);break; case "H1": node = new HTMLHeadingElement(this);break; case "H2": node = new HTMLHeadingElement(this);break; case "H3": node = new HTMLHeadingElement(this);break; case "H4": node = new HTMLHeadingElement(this);break; case "H5": node = new HTMLHeadingElement(this);break; case "H6": node = new HTMLHeadingElement(this);break; case "HEAD": node = new HTMLHeadElement(this);break; case "HR": node = new HTMLHRElement(this);break; case "HTML": node = new HTMLHtmlElement(this);break; case "IFRAME": node = new HTMLIFrameElement(this);break; case "IMG": node = new HTMLImageElement(this);break; case "INPUT": node = new HTMLInputElement(this);break; case "LABEL": node = new HTMLLabelElement(this);break; case "LEGEND": node = new HTMLLegendElement(this);break; case "LI": node = new HTMLLIElement(this);break; case "LINK": node = new HTMLLinkElement(this);break; case "MAP": node = new HTMLMapElement(this);break; case "META": node = new HTMLMetaElement(this);break; case "NOSCRIPT": node = new HTMLElement(this);break; case "OBJECT": node = new HTMLObjectElement(this);break; case "OPTGROUP": node = new HTMLOptGroupElement(this);break; case "OL": node = new HTMLOListElement(this); break; case "OPTION": node = new HTMLOptionElement(this);break; case "P": node = new HTMLParagraphElement(this);break; case "PARAM": node = new HTMLParamElement(this);break; case "PRE": node = new HTMLPreElement(this);break; case "SCRIPT": node = new HTMLScriptElement(this);break; case "SELECT": node = new HTMLSelectElement(this);break; case "SMALL": node = new HTMLElement(this);break; case "SPAN": node = new HTMLSpanElement(this);break; case "STRONG": node = new HTMLElement(this);break; case "STYLE": node = new HTMLStyleElement(this);break; case "TABLE": node = new HTMLTableElement(this);break; case "TBODY": node = new HTMLTableSectionElement(this);break; case "TFOOT": node = new HTMLTableSectionElement(this);break; case "THEAD": node = new HTMLTableSectionElement(this);break; case "TD": node = new HTMLTableDataCellElement(this);break; case "TH": node = new HTMLTableHeaderCellElement(this);break; case "TEXTAREA": node = new HTMLTextAreaElement(this);break; case "TITLE": node = new HTMLTitleElement(this);break; case "TR": node = new HTMLTableRowElement(this);break; case "UL": node = new HTMLUListElement(this);break; default: node = new HTMLUnknownElement(this); } // assign values to properties (and aliases) node.nodeName = tagName; return node; }, createElementNS : function (uri, local) { //print('createElementNS :'+uri+" "+local); if(!uri){ return this.createElement(local); }else if ("http://www.w3.org/1999/xhtml" == uri) { return this.createElement(local); } else if ("http://www.w3.org/1998/Math/MathML" == uri) { return this.createElement(local); } else if ("http://www.w3.org/2000/svg" == uri) { return this.createElement(local); } else { return Document.prototype.createElementNS.apply(this,[uri, local]); } }, get anchors(){ return new HTMLCollection(this.getElementsByTagName('a')); }, get applets(){ return new HTMLCollection(this.getElementsByTagName('applet')); }, get documentElement(){ var html = Document.prototype.__lookupGetter__('documentElement').apply(this,[]); if( html === null){ html = this.createElement('html'); this.appendChild(html); html.appendChild(this.createElement('head')); html.appendChild(this.createElement('body')); } return html; }, //document.head is non-standard get head(){ //console.log('get head'); if (!this.documentElement) { this.appendChild(this.createElement('html')); } var element = this.documentElement, length = element.childNodes.length, i; //check for the presence of the head element in this html doc for(i=0;i<length;i++){ if(element.childNodes[i].nodeType === Node.ELEMENT_NODE){ if(element.childNodes[i].tagName.toLowerCase() === 'head'){ return element.childNodes[i]; } } } //no head? ugh bad news html.. I guess we'll force the issue? var head = element.appendChild(this.createElement('head')); return head; }, get title(){ //console.log('get title'); if (!this.documentElement) { this.appendChild(this.createElement('html')); } var title, head = this.head, length = head.childNodes.length, i; //check for the presence of the title element in this head element for(i=0;i<length;i++){ if(head.childNodes[i].nodeType === Node.ELEMENT_NODE){ if(head.childNodes[i].tagName.toLowerCase() === 'title'){ return head.childNodes[i].textContent; } } } //no title? ugh bad news html.. I guess we'll force the issue? title = head.appendChild(this.createElement('title')); return title.appendChild(this.createTextNode('Untitled Document')).nodeValue; }, set title(titleStr){ //console.log('set title %s', titleStr); if (!this.documentElement) { this.appendChild(this.createElement('html')); } var title = this.title; title.textContent = titleStr; }, get body() { var element = this.documentElement, length = element.childNodes.length, i; for (i=0; i<length; i++) { if (element.childNodes[i].nodeType === Node.ELEMENT_NODE && (element.childNodes[i].tagName === 'BODY' || element.childNodes[i].tagName === 'FRAMESET')) { return element.childNodes[i]; } } return null; }, set body() { /* in firefox this is a benevolent do nothing*/ console.log('set body'); }, get cookie(){ return Envjs.getCookies(this.location+''); }, set cookie(cookie){ return Envjs.setCookie(this.location+'', cookie); }, /** * document.location * * should be identical to window.location * * HTML5: * http://dev.w3.org/html5/spec/Overview.html#the-location-interface * * Mozilla MDC: * https://developer.mozilla.org/en/DOM/document.location * */ get location() { if (this.ownerWindow) { return this.ownerWindow.location; } else { return this.baseURI; } }, set location(url) { this.baseURI = url; if (this.ownerWindow) { this.ownerWindow.location = url; } }, /** * document.URL (read-only) * * HTML DOM Level 2: * http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-46183437 * * HTML5: * http://dev.w3.org/html5/spec/Overview.html#dom-document-url * * Mozilla MDC: * https://developer.mozilla.org/en/DOM/document.URL */ get URL() { return this.location.href; }, /** * document.domain * * HTML5 Spec: * http://dev.w3.org/html5/spec/Overview.html#dom-document-domain * * Mozilla MDC: * https://developer.mozilla.org/en/DOM/document.domain * */ get domain(){ var HOSTNAME = new RegExp('//([^:/]+)'), matches = HOSTNAME.exec(this.baseURI); return matches&&matches.length>1?matches[1]:""; }, set domain(value){ var i, domainParts = this.domain.split('.').reverse(), newDomainParts = value.split('.').reverse(); if(newDomainParts.length > 1){ for(i=0;i<newDomainParts.length;i++){ if(newDomainParts[i] !== domainParts[i]){ return; } } this.baseURI = this.baseURI.replace(domainParts.join('.'), value); } }, get forms(){ return new HTMLCollection(this.getElementsByTagName('form')); }, get images(){ return new HTMLCollection(this.getElementsByTagName('img')); }, get lastModified(){ /* TODO */ return this._lastModified; }, get links(){ return new HTMLCollection(this.getElementsByTagName('a')); }, getElementsByName : function(name){ //console.log('getting elements for name %s', name); if(!(this._indexes_['@'+name])){ this._indexes_["@"+name] = new NodeList(this, this.documentElement); } return this._indexes_["@"+name]; }, toString: function(){ return "[object HTMLDocument]"; }, get innerHTML(){ return this.documentElement.outerHTML; } }); }(/*HTMLDocument*/)); Aspect.around({ target: Node, method:"appendChild" }, function(invocation) { var event, okay, node = invocation.proceed(), doc = node.ownerDocument, target; //console.log('element appended: %s %s %s', node+'', node.nodeName, node.namespaceURI); if((node.nodeType !== Node.ELEMENT_NODE)){ //for now we are only handling element insertions. probably //we will need to handle text node changes to script tags and //changes to src attributes return node; } if(node.tagName&&node.tagName.toLowerCase()=="input"){ target = node.parentNode; //console.log('adding named map for input'); while(target&&target.tagName&&target.tagName.toLowerCase()!="form"){ //console.log('possible target for named map for input is %s', target); target = target.parentNode; } if(target){ //console.log('target for named map for input is %s', target); __addNamedMap__(target, node); } } //console.log('appended html element %s %s %s', node.namespaceURI, node.nodeName, node); switch(doc.parsing){ case true: /** * Very special case. While in parsing mode, in head, a * script can add another script tag to the head, and it will * be evaluated. This is tested in 'ant fulldoc-spec' tests. * * Not quite sure if the require that the new script tag must * be in the head is correct or not. NamespaceURI == null * might also need to corrected too. */ if (node.tagName.toLowerCase() === 'script' && (node.namespaceURI === "" || node.namespaceURI === "http://www.w3.org/1999/xhtml" || node.namespaceURI === null) ) { //console.log('appending script while parsing'); if((this.nodeName.toLowerCase() === 'head')){ try{ okay = Envjs.loadLocalScript(node, null); //console.log('loaded script? %s %s', node.uuid, okay); // only fire event if we actually had something to load if (node.src && node.src.length > 0){ event = doc.createEvent('HTMLEvents'); event.initEvent( okay ? "load" : "error", false, false ); node.dispatchEvent( event, false ); } }catch(e){ console.log('error loading html element %s %e', node, e.toString()); } } } break; case false: switch(node.namespaceURI){ case null: //fall through case "": //fall through case "http://www.w3.org/1999/xhtml": switch(node.tagName.toLowerCase()){ case 'style': document.styleSheets.push(new CSSStyleSheet(node)); break; case 'script': //console.log('appending script %s', node.src); if((this.nodeName.toLowerCase() === 'head')){ try{ okay = Envjs.loadLocalScript(node, null); //console.log('loaded script? %s %s', node.uuid, okay); // only fire event if we actually had something to load if (node.src && node.src.length > 0){ event = doc.createEvent('HTMLEvents'); event.initEvent( okay ? "load" : "error", false, false ); node.dispatchEvent( event, false ); } }catch(ee){ console.log('error loading html element %s %e', node, ee.toString()); } } break; case 'frame': case 'iframe': node.contentWindow = { }; node.contentDocument = new HTMLDocument(new DOMImplementation(), node.contentWindow); node.contentWindow.document = node.contentDocument; try{ if(Window){ //console.log("iframe appended to document %s", node); } }catch(eee){ node.contentDocument.addEventListener('DOMContentLoaded', function(){ event = node.contentDocument.createEvent('HTMLEvents'); event.initEvent("load", false, false); node.dispatchEvent( event, false ); }); console.log('error loading html element %s %e', node, eee.toString()); } try{ if (node.src && node.src.length > 0){ //console.log("trigger load on frame from appendChild %s", node.src); Envjs.loadFrame(node, Envjs.uri(node.src, doc.location+'')); }else{ Envjs.loadFrame(node); } }catch(_e){ console.log('error loading html element %s %e', node, _e.toString()); } break; case 'link': if (node.href && node.href.length > 0) { Envjs.loadLink(node, node.href); } break; /* case 'img': if (node.src && node.src.length > 0){ // don't actually load anything, so we're "done" immediately: event = doc.createEvent('HTMLEvents'); event.initEvent("load", false, false); node.dispatchEvent( event, false ); } break; */ case 'option': __updateoptions__(node); break; default: if(node.getAttribute('onload')){ //console.log('calling attribute onload %s | %s', node.onload, node.tagName); node.onload(); } }//switch on name break; default: break; }//switch on ns break; default: break; // console.log('element appended: %s %s', node+'', node.namespaceURI); }//switch on doc.parsing return node; }); Aspect.around({ target: Node, method:"removeChild" }, function(invocation) { var event, okay, target, node = invocation.proceed(), doc = node.ownerDocument; if((node.nodeType !== Node.ELEMENT_NODE)){ //for now we are only handling element insertions. probably we will need //to handle text node changes to script tags and changes to src //attributes if(node.nodeType !== Node.DOCUMENT_NODE && node.uuid){ //console.log('removing event listeners, %s', node, node.uuid); node.removeEventListener('*', null, null); } return node; } //console.log('appended html element %s %s %s', node.namespaceURI, node.nodeName, node); if(node.tagName&&node.tagName.toLowerCase()=="input"){ target = node.parentNode; //console.log('adding named map for input'); while(target&&target.tagName&&target.tagName.toLowerCase()!="form"){ //console.log('possible target for named map for input is %s', target); target = target.parentNode; } if(target){ //console.log('target for named map for input is %s', target); __removeNamedMap__(target, node); } } switch(doc.parsing){ case true: //handled by parser if included break; case false: switch(node.namespaceURI){ case null: //fall through case "": //fall through case "http://www.w3.org/1999/xhtml": //this is interesting dillema since our event engine is //storing the registered events in an array accessed //by the uuid property of the node. unforunately this //means listeners hang out way after(forever ;)) the node //has been removed and gone out of scope. //console.log('removing event listeners, %s', node, node.uuid); node.removeEventListener('*', null, null); switch(node.tagName.toLowerCase()){ case 'frame': case 'iframe': try{ //console.log('removing iframe document'); try{ Envjs.unloadFrame(node); }catch(ee){ console.log('error freeing resources from frame %s', ee); } node.contentWindow = null; node.contentDocument = null; }catch(e){ console.log('error unloading html element %s %e', node, e.toString()); } break; default: }//switch on name break; default: }//switch on ns break; default: console.log('element appended: %s %s', node+'', node.namespaceURI); }//switch on doc.parsing return node; }); /** * Named Element Support * * */ /* * * @returns 'name' if the node has a appropriate name * null if node does not have a name */ __isNamedElement__ = function(node) { if (node.nodeType !== Node.ELEMENT_NODE) { return null; } var tagName = node.tagName.toLowerCase(); var nodename = null; switch (tagName) { case 'embed': case 'form': case 'iframe': case 'input': nodename = node.getAttribute('name'); break; case 'applet': nodename = node.id; break; case 'object': // TODO: object needs to be 'fallback free' nodename = node.id; break; case 'img': nodename = node.id; if (!nodename || ! node.getAttribute('name')) { nodename = null; } break; } return (nodename) ? nodename : null; }; __addNamedMap__ = function(target, node) { var nodename = __isNamedElement__(node); if (nodename) { target.__defineGetter__(nodename, function() { return node; }); target.__defineSetter__(nodename, function(value) { return value; }); } }; __removeNamedMap__ = function(target, node) { if (!node) { return; } var nodename = __isNamedElement__(node); if (nodename) { delete target[nodename]; } }; /** * @name HTMLEvents * @w3c:domlevel 2 * @uri http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html */ var __eval__ = function(script, node){ if (script !== "" && Envjs.scriptTypes['']){ // don't assemble environment if no script... try{ Envjs['eval'](node.ownerDocument.ownerWindow, script, script+" ("+node+")"); }catch(e){ console.log('error evaluating %s', e); } } }; var HTMLEvents = function(){}; HTMLEvents.prototype = { onload: function(event){ __eval__(this.getAttribute('onload')||'', this); }, onunload: function(event){ __eval__(this.getAttribute('onunload')||'', this); }, onabort: function(event){ __eval__(this.getAttribute('onabort')||'', this); }, onerror: function(event){ __eval__(this.getAttribute('onerror')||'', this); }, onselect: function(event){ __eval__(this.getAttribute('onselect')||'', this); }, onchange: function(event){ __eval__(this.getAttribute('onchange')||'', this); }, onsubmit: function(event){ if (__eval__(this.getAttribute('onsubmit')||'', this)) { this.submit(); } }, onreset: function(event){ __eval__(this.getAttribute('onreset')||'', this); }, onfocus: function(event){ __eval__(this.getAttribute('onfocus')||'', this); }, onblur: function(event){ __eval__(this.getAttribute('onblur')||'', this); }, onresize: function(event){ __eval__(this.getAttribute('onresize')||'', this); }, onscroll: function(event){ __eval__(this.getAttribute('onscroll')||'', this); } }; //HTMLDocument, HTMLFramesetElement, HTMLObjectElement var __load__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("load", false, false); element.dispatchEvent(event); return event; }; //HTMLFramesetElement, HTMLBodyElement var __unload__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("unload", false, false); element.dispatchEvent(event); return event; }; //HTMLObjectElement var __abort__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("abort", true, false); element.dispatchEvent(event); return event; }; //HTMLFramesetElement, HTMLObjectElement, HTMLBodyElement var __error__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("error", true, false); element.dispatchEvent(event); return event; }; //HTMLInputElement, HTMLTextAreaElement var __select__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("select", true, false); element.dispatchEvent(event); return event; }; //HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement var __change__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("change", true, false); element.dispatchEvent(event); return event; }; //HtmlFormElement var __submit__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("submit", true, true); element.dispatchEvent(event); return event; }; //HtmlFormElement var __reset__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("reset", false, false); element.dispatchEvent(event); return event; }; //LABEL, INPUT, SELECT, TEXTAREA, and BUTTON var __focus__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("focus", false, false); element.dispatchEvent(event); return event; }; //LABEL, INPUT, SELECT, TEXTAREA, and BUTTON var __blur__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("blur", false, false); element.dispatchEvent(event); return event; }; //Window var __resize__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("resize", true, false); element.dispatchEvent(event); return event; }; //Window var __scroll__ = function(element){ var event = new Event('HTMLEvents'); event.initEvent("scroll", true, false); element.dispatchEvent(event); return event; }; /** * @name KeyboardEvents * @w3c:domlevel 2 * @uri http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html */ var KeyboardEvents= function(){}; KeyboardEvents.prototype = { onkeydown: function(event){ __eval__(this.getAttribute('onkeydown')||'', this); }, onkeypress: function(event){ __eval__(this.getAttribute('onkeypress')||'', this); }, onkeyup: function(event){ __eval__(this.getAttribute('onkeyup')||'', this); } }; var __registerKeyboardEventAttrs__ = function(elm){ if(elm.hasAttribute('onkeydown')){ elm.addEventListener('keydown', elm.onkeydown, false); } if(elm.hasAttribute('onkeypress')){ elm.addEventListener('keypress', elm.onkeypress, false); } if(elm.hasAttribute('onkeyup')){ elm.addEventListener('keyup', elm.onkeyup, false); } return elm; }; //HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement var __keydown__ = function(element){ var event = new Event('KeyboardEvents'); event.initEvent("keydown", false, false); element.dispatchEvent(event); }; //HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement var __keypress__ = function(element){ var event = new Event('KeyboardEvents'); event.initEvent("keypress", false, false); element.dispatchEvent(event); }; //HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement var __keyup__ = function(element){ var event = new Event('KeyboardEvents'); event.initEvent("keyup", false, false); element.dispatchEvent(event); }; /** * @name MaouseEvents * @w3c:domlevel 2 * @uri http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html */ var MouseEvents= function(){}; MouseEvents.prototype = { onclick: function(event){ __eval__(this.getAttribute('onclick')||'', this); }, ondblclick: function(event){ __eval__(this.getAttribute('ondblclick')||'', this); }, onmousedown: function(event){ __eval__(this.getAttribute('onmousedown')||'', this); }, onmousemove: function(event){ __eval__(this.getAttribute('onmousemove')||'', this); }, onmouseout: function(event){ __eval__(this.getAttribute('onmouseout')||'', this); }, onmouseover: function(event){ __eval__(this.getAttribute('onmouseover')||'', this); }, onmouseup: function(event){ __eval__(this.getAttribute('onmouseup')||'', this); } }; var __registerMouseEventAttrs__ = function(elm){ if(elm.hasAttribute('onclick')){ elm.addEventListener('click', elm.onclick, false); } if(elm.hasAttribute('ondblclick')){ elm.addEventListener('dblclick', elm.ondblclick, false); } if(elm.hasAttribute('onmousedown')){ elm.addEventListener('mousedown', elm.onmousedown, false); } if(elm.hasAttribute('onmousemove')){ elm.addEventListener('mousemove', elm.onmousemove, false); } if(elm.hasAttribute('onmouseout')){ elm.addEventListener('mouseout', elm.onmouseout, false); } if(elm.hasAttribute('onmouseover')){ elm.addEventListener('mouseover', elm.onmouseover, false); } if(elm.hasAttribute('onmouseup')){ elm.addEventListener('mouseup', elm.onmouseup, false); } return elm; }; var __click__ = function(element){ var event = new Event('MouseEvents'); event.initEvent("click", true, true, null, 0, 0, 0, 0, 0, false, false, false, false, null, null); element.dispatchEvent(event); }; var __mousedown__ = function(element){ var event = new Event('MouseEvents'); event.initEvent("mousedown", true, true, null, 0, 0, 0, 0, 0, false, false, false, false, null, null); element.dispatchEvent(event); }; var __mouseup__ = function(element){ var event = new Event('MouseEvents'); event.initEvent("mouseup", true, true, null, 0, 0, 0, 0, 0, false, false, false, false, null, null); element.dispatchEvent(event); }; var __mouseover__ = function(element){ var event = new Event('MouseEvents'); event.initEvent("mouseover", true, true, null, 0, 0, 0, 0, 0, false, false, false, false, null, null); element.dispatchEvent(event); }; var __mousemove__ = function(element){ var event = new Event('MouseEvents'); event.initEvent("mousemove", true, true, null, 0, 0, 0, 0, 0, false, false, false, false, null, null); element.dispatchEvent(event); }; var __mouseout__ = function(element){ var event = new Event('MouseEvents'); event.initEvent("mouseout", true, true, null, 0, 0, 0, 0, 0, false, false, false, false, null, null); element.dispatchEvent(event); }; /** * HTMLElement - DOM Level 2 */ (function(){ var log = Envjs.logger(); Envjs.once('tick', function(){ log = Envjs.logger('Envjs.HTML.HTMLElement'). debug('HTMLElement available'); }); /* Hack for http://www.prototypejs.org/ * * Prototype 1.6 (the library) creates a new global Element, which causes * envjs to use the wrong Element. * * http://envjs.lighthouseapp.com/projects/21590/tickets/108-prototypejs-wont-load-due-it-clobbering-element * * Options: * (1) Rename the dom/element to something else * rejected: been done before. people want Element. * (2) merge dom+html and not export Element to global namespace * (meaning we would use a local var Element in a closure, so prototype * can do what ever it wants) * rejected: want dom and html separate * (3) use global namespace (put everything under Envjs = {}) * rejected: massive change * (4) use commonjs modules (similar to (3) in spirit) * rejected: massive change * * or * * (5) take a reference to Element during initial loading ("compile * time"), and use the reference instead of "Element". That's * what the next line does. We use __DOMElement__ if we need to * reference the parent class. Only this file explcity uses * Element so this should work, and is the most minimal change I * could think of with no external API changes. * */ var __DOMElement__ = Element; exports.HTMLElement = HTMLElement = function(ownerDocument) { __DOMElement__.apply(this, arguments); }; HTMLElement.prototype = new Element(); __extend__(HTMLElement.prototype, HTMLEvents.prototype); __extend__(HTMLElement.prototype, { get className() { return this.getAttribute("class")||''; }, set className(value) { return this.setAttribute("class",__trim__(value)); }, get dir() { return this.getAttribute("dir")||"ltr"; }, set dir(val) { return this.setAttribute("dir",val); }, get id(){ return this.getAttribute('id') || ''; }, set id(id){ this.setAttribute('id', id); }, get innerHTML(){ var ret = "", i; // create string containing the concatenation of the string // values of each child for (i=0; i < this.childNodes.length; i++) { if(this.childNodes[i]){ if(this.childNodes[i].nodeType === Node.ELEMENT_NODE){ ret += this.childNodes[i].xhtml; } else if (this.childNodes[i].nodeType === Node.TEXT_NODE && i>0 && this.childNodes[i-1].nodeType === Node.TEXT_NODE){ //add a single space between adjacent text nodes ret += " "+this.childNodes[i].xml; }else{ ret += this.childNodes[i].xml; } } } return ret; }, get lang() { return this.getAttribute("lang"); }, set lang(val) { return this.setAttribute("lang",val); }, get offsetHeight(){ return Number((this.style.height || '').replace("px","")); }, get offsetWidth(){ return Number((this.style.width || '').replace("px","")); }, offsetLeft: 0, offsetRight: 0, get offsetParent(){ /* TODO */ return; }, set offsetParent(element){ /* TODO */ return; }, scrollHeight: 0, scrollWidth: 0, scrollLeft: 0, scrollRight: 0, get style(){ return this.getAttribute('style')||''; }, get title() { return this.getAttribute("title"); }, set title(value) { return this.setAttribute("title", value); }, get tabIndex(){ var tabindex = this.getAttribute('tabindex'); if(tabindex!==null){ return Number(tabindex); } else { return 0; } }, set tabIndex(value){ if (value === undefined || value === null) { value = 0; } this.setAttribute('tabindex',Number(value)); }, get outerHTML(){ //Not in the specs but I'll leave it here for now. return this.xhtml; }, scrollIntoView: function(){ /*TODO*/ return; }, toString: function(){ return '[object HTMLElement]'; }, get xhtml() { // HTMLDocument.xhtml is non-standard // This is exactly like Document.xml except the tagName has to be // lower cased. I dont like to duplicate this but its really not // a simple work around between xml and html serialization via // XMLSerializer (which uppercases html tags) and innerHTML (which // lowercases tags) var ret = "", ns = "", name = (this.tagName+"").toLowerCase(), attrs, attrstring = "", style = false, i; // serialize namespace declarations if (this.namespaceURI){ if((this === this.ownerDocument.documentElement) || (!this.parentNode) || (this.parentNode && (this.parentNode.namespaceURI !== this.namespaceURI))) { ns = ' xmlns' + (this.prefix ? (':' + this.prefix) : '') + '="' + this.namespaceURI + '"'; } } // serialize Attribute declarations attrs = this.attributes; for(i=0;i< attrs.length;i++){ attrstring += " "+attrs[i].name+'="'+attrs[i].xml+'"'; if(attrs[i].name == 'style'){ style = true; } } if(!style ){ style = this.getAttribute('style'); if(style){ attrstring += ' style="'+style+'"'; } } if(this.hasChildNodes()){ // serialize this Element //console.log('serializing childNodes for %s', name); ret += "<" + name + ns + attrstring +">"; for(i=0;i< this.childNodes.length;i++){ //console.debug('xhtml for '+ this); ret += 'xhtml' in this.childNodes[i] ? this.childNodes[i].xhtml : this.childNodes[i].xml; } ret += "</" + name + ">"; }else{ //console.log('no childNodes to serialize for %s', name); switch(name){ case 'script': case 'noscript': ret += "<" + name + ns + attrstring +"></"+name+">"; break; default: ret += "<" + name + ns + attrstring +"/>"; } } return ret; }, /** * setAttribute use a dispatch table that other tags can set to * "listen" to various values being set. The dispatch table * and registration functions are at the end of the file. * */ setAttribute: function(name, value) { var result = __DOMElement__.prototype.setAttribute.apply(this, arguments); __addNamedMap__(this.ownerDocument, this); var tagname = this.tagName; var callback = HTMLElement.getAttributeCallback('set', tagname, name); if (callback) { callback(this, value); } }, setAttributeNS: function(namespaceURI, name, value) { var result = __DOMElement__.prototype.setAttributeNS.apply(this, arguments); __addNamedMap__(this.ownerDocument, this); var tagname = this.tagName; var callback = HTMLElement.getAttributeCallback('set', tagname, name); if (callback) { callback(this, value); } return result; }, setAttributeNode: function(newnode) { var result = __DOMElement__.prototype.setAttributeNode.apply(this, arguments); __addNamedMap__(this.ownerDocument, this); var tagname = this.tagName; var callback = HTMLElement.getAttributeCallback('set', tagname, newnode.name); if (callback) { callback(this, newnode.value); } return result; }, setAttributeNodeNS: function(newnode) { var result = __DOMElement__.prototype.setAttributeNodeNS.apply(this, arguments); __addNamedMap__(this.ownerDocument, this); var tagname = this.tagName; var callback = HTMLElement.getAttributeCallback('set', tagname, newnode.name); if (callback) { callback(this, newnode.value); } return result; }, removeAttribute: function(name) { __removeNamedMap__(this.ownerDocument, this); return __DOMElement__.prototype.removeAttribute.apply(this, arguments); }, removeAttributeNS: function(namespace, localname) { __removeNamedMap__(this.ownerDocument, this); return __DOMElement__.prototype.removeAttributeNS.apply(this, arguments); }, removeAttributeNode: function(name) { __removeNamedMap__(this.ownerDocument, this); return __DOMElement__.prototype.removeAttributeNode.apply(this, arguments); }, removeChild: function(oldChild) { __removeNamedMap__(this.ownerDocument, oldChild); return __DOMElement__.prototype.removeChild.apply(this, arguments); }, importNode: function(othernode, deep) { var newnode = __DOMElement__.prototype.importNode.apply(this, arguments); __addNamedMap__(this.ownerDocument, newnode); return newnode; }, // not actually sure if this is needed or not replaceNode: function(newchild, oldchild) { var newnode = __DOMElement__.prototype.replaceNode.apply(this, arguments); __removeNamedMap__(this.ownerDocument, oldchild); __addNamedMap__(this.ownerDocument, newnode); return newnode; } }); HTMLElement.attributeCallbacks = {}; HTMLElement.registerSetAttribute = function(tag, attrib, callbackfn) { HTMLElement.attributeCallbacks[tag + ':set:' + attrib] = callbackfn; }; HTMLElement.registerRemoveAttribute = function(tag, attrib, callbackfn) { HTMLElement.attributeCallbacks[tag + ':remove:' + attrib] = callbackfn; }; /** * This is really only useful internally * */ HTMLElement.getAttributeCallback = function(type, tag, attrib) { return HTMLElement.attributeCallbacks[tag + ':' + type + ':' + attrib] || null; }; }(/*HTMLElement*/)); /* * HTMLCollection * * HTML5 -- 2.7.2.1 HTMLCollection * http://dev.w3.org/html5/spec/Overview.html#htmlcollection * http://dev.w3.org/html5/spec/Overview.html#collections */ (function(){ var log = Envjs.logger(); Envjs.once('tick', function(){ log = Envjs.logger('Envjs.HTML.HTMLCollection'). debug('HTMLCollection available'); }); exports.HTMLCollection = HTMLCollection = function(nodelist, type) { __extend__(nodelist,{ namedItem: function (name) { return this[name] || null; }, toString: function() { return '[object HTMLCollection]'; } }); var n; for (var i=0; i<nodelist.length; i++) { n = nodelist[i].id; if (n && !nodelist[n]) { nodelist[n] = nodelist[i]; } n = nodelist[i].name; if (n && !nodelist[n]) { nodelist[n] = nodelist[i]; } } return nodelist; }; HTMLCollection.prototype = new NodeList(); __extend__(HTMLCollection.prototype, { namedItem: function (name) { return this[name] || null; }, toString: function() { return '[object HTMLCollection]'; } }); }(/*Envjs.HTML.HTMLCollection*/)); /* * a set of convenience classes to centralize implementation of * properties and methods across multiple in-form elements * * the hierarchy of related HTML elements and their members is as follows: * * Condensed Version * * HTMLInputCommon * * legent (no value attr) * * fieldset (no value attr) * * label (no value attr) * * option (custom value) * HTMLTypeValueInputs (extends InputCommon) * * select (custom value) * * button (just sets value) * HTMLInputAreaCommon (extends TypeValueIput) * * input (custom) * * textarea (just sets value) * * ----------------------- * HTMLInputCommon: common to all elements * .form * * <legend> * [common plus:] * .align * * <fieldset> * [identical to "legend" plus:] * .margin * * * **** * * <label> * [common plus:] * .dataFormatAs * .htmlFor * [plus data properties] * * <option> * [common plus:] * .defaultSelected * .index * .label * .selected * .text * .value // unique implementation, not duplicated * .form // unique implementation, not duplicated * **** * * HTMLTypeValueInputs: common to remaining elements * [common plus:] * .name *