summaryrefslogtreecommitdiff
path: root/lib/dojo/html.js
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2011-03-04 19:02:28 +0300
committerAndrew Dolgov <[email protected]>2011-03-04 19:02:59 +0300
commita089699c8915636ba4f158d77dba9b012bc93208 (patch)
treeb2d7d051f1f55d44a6be07d3ee137e5a7ccfcefb /lib/dojo/html.js
parentcfad9259a6feacfa8194b1312770ae6db1ecce50 (diff)
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
Diffstat (limited to 'lib/dojo/html.js')
-rw-r--r--lib/dojo/html.js456
1 files changed, 321 insertions, 135 deletions
diff --git a/lib/dojo/html.js b/lib/dojo/html.js
index 7c15e5812..ec5c4986e 100644
--- a/lib/dojo/html.js
+++ b/lib/dojo/html.js
@@ -5,141 +5,327 @@
*/
-if(!dojo._hasResource["dojo.html"]){
-dojo._hasResource["dojo.html"]=true;
+if(!dojo._hasResource["dojo.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo.html"] = true;
dojo.provide("dojo.html");
-dojo.require("dojo.parser");
-(function(){
-var _1=0,d=dojo;
-dojo.html._secureForInnerHtml=function(_2){
-return _2.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig,"");
-};
-dojo.html._emptyNode=dojo.empty;
-dojo.html._setNodeContent=function(_3,_4){
-d.empty(_3);
-if(_4){
-if(typeof _4=="string"){
-_4=d._toDom(_4,_3.ownerDocument);
-}
-if(!_4.nodeType&&d.isArrayLike(_4)){
-for(var _5=_4.length,i=0;i<_4.length;i=_5==_4.length?i+1:0){
-d.place(_4[i],_3,"last");
-}
-}else{
-d.place(_4,_3,"last");
-}
-}
-return _3;
-};
-dojo.declare("dojo.html._ContentSetter",null,{node:"",content:"",id:"",cleanContent:false,extractContent:false,parseContent:false,constructor:function(_6,_7){
-dojo.mixin(this,_6||{});
-_7=this.node=dojo.byId(this.node||_7);
-if(!this.id){
-this.id=["Setter",(_7)?_7.id||_7.tagName:"",_1++].join("_");
-}
-},set:function(_8,_9){
-if(undefined!==_8){
-this.content=_8;
-}
-if(_9){
-this._mixin(_9);
-}
-this.onBegin();
-this.setContent();
-this.onEnd();
-return this.node;
-},setContent:function(){
-var _a=this.node;
-if(!_a){
-throw new Error(this.declaredClass+": setContent given no node");
-}
-try{
-_a=dojo.html._setNodeContent(_a,this.content);
-}
-catch(e){
-var _b=this.onContentError(e);
-try{
-_a.innerHTML=_b;
-}
-catch(e){
-console.error("Fatal "+this.declaredClass+".setContent could not change content due to "+e.message,e);
-}
-}
-this.node=_a;
-},empty:function(){
-if(this.parseResults&&this.parseResults.length){
-dojo.forEach(this.parseResults,function(w){
-if(w.destroy){
-w.destroy();
-}
-});
-delete this.parseResults;
-}
-dojo.html._emptyNode(this.node);
-},onBegin:function(){
-var _c=this.content;
-if(dojo.isString(_c)){
-if(this.cleanContent){
-_c=dojo.html._secureForInnerHtml(_c);
-}
-if(this.extractContent){
-var _d=_c.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
-if(_d){
-_c=_d[1];
-}
-}
-}
-this.empty();
-this.content=_c;
-return this.node;
-},onEnd:function(){
-if(this.parseContent){
-this._parse();
-}
-return this.node;
-},tearDown:function(){
-delete this.parseResults;
-delete this.node;
-delete this.content;
-},onContentError:function(_e){
-return "Error occured setting content: "+_e;
-},_mixin:function(_f){
-var _10={},key;
-for(key in _f){
-if(key in _10){
-continue;
-}
-this[key]=_f[key];
-}
-},_parse:function(){
-var _11=this.node;
-try{
-this.parseResults=dojo.parser.parse({rootNode:_11,dir:this.dir,lang:this.lang});
-}
-catch(e){
-this._onError("Content",e,"Error parsing in _ContentSetter#"+this.id);
-}
-},_onError:function(_12,err,_13){
-var _14=this["on"+_12+"Error"].call(this,err);
-if(_13){
-console.error(_13,err);
-}else{
-if(_14){
-dojo.html._setNodeContent(this.node,_14,true);
-}
-}
-}});
-dojo.html.set=function(_15,_16,_17){
-if(undefined==_16){
-console.warn("dojo.html.set: no cont argument provided, using empty string");
-_16="";
-}
-if(!_17){
-return dojo.html._setNodeContent(_15,_16,true);
-}else{
-var op=new dojo.html._ContentSetter(dojo.mixin(_17,{content:_16,node:_15}));
-return op.set();
-}
-};
+
+// the parser might be needed..
+dojo.require("dojo.parser");
+
+(function(){ // private scope, sort of a namespace
+
+ // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
+ var idCounter = 0,
+ d = dojo;
+
+ dojo.html._secureForInnerHtml = function(/*String*/ cont){
+ // summary:
+ // removes !DOCTYPE and title elements from the html string.
+ //
+ // khtml is picky about dom faults, you can't attach a style or <title> node as child of body
+ // must go into head, so we need to cut out those tags
+ // cont:
+ // An html string for insertion into the dom
+ //
+ return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
+ };
+
+/*====
+ dojo.html._emptyNode = function(node){
+ // summary:
+ // removes all child nodes from the given node
+ // node: DOMNode
+ // the parent element
+ };
+=====*/
+ dojo.html._emptyNode = dojo.empty;
+
+ dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){
+ // summary:
+ // inserts the given content into the given node
+ // node:
+ // the parent element
+ // content:
+ // the content to be set on the parent element.
+ // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
+
+ // always empty
+ d.empty(node);
+
+ if(cont) {
+ if(typeof cont == "string") {
+ cont = d._toDom(cont, node.ownerDocument);
+ }
+ if(!cont.nodeType && d.isArrayLike(cont)) {
+ // handle as enumerable, but it may shrink as we enumerate it
+ for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) {
+ d.place( cont[i], node, "last");
+ }
+ } else {
+ // pass nodes, documentFragments and unknowns through to dojo.place
+ d.place(cont, node, "last");
+ }
+ }
+
+ // return DomNode
+ return node;
+ };
+
+ // we wrap up the content-setting operation in a object
+ dojo.declare("dojo.html._ContentSetter", null,
+ {
+ // node: DomNode|String
+ // An node which will be the parent element that we set content into
+ node: "",
+
+ // content: String|DomNode|DomNode[]
+ // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
+ content: "",
+
+ // id: String?
+ // Usually only used internally, and auto-generated with each instance
+ id: "",
+
+ // cleanContent: Boolean
+ // Should the content be treated as a full html document,
+ // and the real content stripped of <html>, <body> wrapper before injection
+ cleanContent: false,
+
+ // extractContent: Boolean
+ // Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection
+ extractContent: false,
+
+ // parseContent: Boolean
+ // Should the node by passed to the parser after the new content is set
+ parseContent: false,
+
+ // lifecyle methods
+ constructor: function(/* Object */params, /* String|DomNode */node){
+ // summary:
+ // Provides a configurable, extensible object to wrap the setting on content on a node
+ // call the set() method to actually set the content..
+
+ // the original params are mixed directly into the instance "this"
+ dojo.mixin(this, params || {});
+
+ // give precedence to params.node vs. the node argument
+ // and ensure its a node, not an id string
+ node = this.node = dojo.byId( this.node || node );
+
+ if(!this.id){
+ this.id = [
+ "Setter",
+ (node) ? node.id || node.tagName : "",
+ idCounter++
+ ].join("_");
+ }
+ },
+ set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){
+ // summary:
+ // front-end to the set-content sequence
+ // cont:
+ // An html string, node or enumerable list of nodes for insertion into the dom
+ // If not provided, the object's content property will be used
+ if(undefined !== cont){
+ this.content = cont;
+ }
+ // in the re-use scenario, set needs to be able to mixin new configuration
+ if(params){
+ this._mixin(params);
+ }
+
+ this.onBegin();
+ this.setContent();
+ this.onEnd();
+
+ return this.node;
+ },
+ setContent: function(){
+ // summary:
+ // sets the content on the node
+
+ var node = this.node;
+ if(!node) {
+ // can't proceed
+ throw new Error(this.declaredClass + ": setContent given no node");
+ }
+ try{
+ node = dojo.html._setNodeContent(node, this.content);
+ }catch(e){
+ // check if a domfault occurs when we are appending this.errorMessage
+ // like for instance if domNode is a UL and we try append a DIV
+
+ // FIXME: need to allow the user to provide a content error message string
+ var errMess = this.onContentError(e);
+ try{
+ node.innerHTML = errMess;
+ }catch(e){
+ console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
+ }
+ }
+ // always put back the node for the next method
+ this.node = node; // DomNode
+ },
+
+ empty: function() {
+ // summary
+ // cleanly empty out existing content
+
+ // destroy any widgets from a previous run
+ // NOTE: if you dont want this you'll need to empty
+ // the parseResults array property yourself to avoid bad things happenning
+ if(this.parseResults && this.parseResults.length) {
+ dojo.forEach(this.parseResults, function(w) {
+ if(w.destroy){
+ w.destroy();
+ }
+ });
+ delete this.parseResults;
+ }
+ // this is fast, but if you know its already empty or safe, you could
+ // override empty to skip this step
+ dojo.html._emptyNode(this.node);
+ },
+
+ onBegin: function(){
+ // summary
+ // Called after instantiation, but before set();
+ // It allows modification of any of the object properties
+ // - including the node and content provided - before the set operation actually takes place
+ // This default implementation checks for cleanContent and extractContent flags to
+ // optionally pre-process html string content
+ var cont = this.content;
+
+ if(dojo.isString(cont)){
+ if(this.cleanContent){
+ cont = dojo.html._secureForInnerHtml(cont);
+ }
+
+ if(this.extractContent){
+ var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
+ if(match){ cont = match[1]; }
+ }
+ }
+
+ // clean out the node and any cruft associated with it - like widgets
+ this.empty();
+
+ this.content = cont;
+ return this.node; /* DomNode */
+ },
+
+ onEnd: function(){
+ // summary
+ // Called after set(), when the new content has been pushed into the node
+ // It provides an opportunity for post-processing before handing back the node to the caller
+ // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
+ if(this.parseContent){
+ // populates this.parseResults if you need those..
+ this._parse();
+ }
+ return this.node; /* DomNode */
+ },
+
+ tearDown: function(){
+ // summary
+ // manually reset the Setter instance if its being re-used for example for another set()
+ // description
+ // tearDown() is not called automatically.
+ // In normal use, the Setter instance properties are simply allowed to fall out of scope
+ // but the tearDown method can be called to explicitly reset this instance.
+ delete this.parseResults;
+ delete this.node;
+ delete this.content;
+ },
+
+ onContentError: function(err){
+ return "Error occured setting content: " + err;
+ },
+
+ _mixin: function(params){
+ // mix properties/methods into the instance
+ // TODO: the intention with tearDown is to put the Setter's state
+ // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
+ // so we could do something here to move the original properties aside for later restoration
+ var empty = {}, key;
+ for(key in params){
+ if(key in empty){ continue; }
+ // TODO: here's our opportunity to mask the properties we dont consider configurable/overridable
+ // .. but history shows we'll almost always guess wrong
+ this[key] = params[key];
+ }
+ },
+ _parse: function(){
+ // summary:
+ // runs the dojo parser over the node contents, storing any results in this.parseResults
+ // Any errors resulting from parsing are passed to _onError for handling
+
+ var rootNode = this.node;
+ try{
+ // store the results (widgets, whatever) for potential retrieval
+ this.parseResults = dojo.parser.parse({
+ rootNode: rootNode,
+ dir: this.dir,
+ lang: this.lang
+ });
+ }catch(e){
+ this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
+ }
+ },
+
+ _onError: function(type, err, consoleText){
+ // summary:
+ // shows user the string that is returned by on[type]Error
+ // overide/implement on[type]Error and return your own string to customize
+ var errText = this['on' + type + 'Error'].call(this, err);
+ if(consoleText){
+ console.error(consoleText, err);
+ }else if(errText){ // a empty string won't change current content
+ dojo.html._setNodeContent(this.node, errText, true);
+ }
+ }
+ }); // end dojo.declare()
+
+ dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
+ // summary:
+ // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
+ // may be a better choice for simple HTML insertion.
+ // description:
+ // Unless you need to use the params capabilities of this method, you should use
+ // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
+ // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
+ // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
+ // or the other capabilities as defined by the params object for this method.
+ // node:
+ // the parent element that will receive the content
+ // cont:
+ // the content to be set on the parent element.
+ // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
+ // params:
+ // Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
+ // example:
+ // A safe string/node/nodelist content replacement/injection with hooks for extension
+ // Example Usage:
+ // dojo.html.set(node, "some string");
+ // dojo.html.set(node, contentNode, {options});
+ // dojo.html.set(node, myNode.childNodes, {options});
+ if(undefined == cont){
+ console.warn("dojo.html.set: no cont argument provided, using empty string");
+ cont = "";
+ }
+ if(!params){
+ // simple and fast
+ return dojo.html._setNodeContent(node, cont, true);
+ }else{
+ // more options but slower
+ // note the arguments are reversed in order, to match the convention for instantiation via the parser
+ var op = new dojo.html._ContentSetter(dojo.mixin(
+ params,
+ { content: cont, node: node }
+ ));
+ return op.set();
+ }
+ };
})();
+
}