mirror of
https://git.tt-rss.org/git/tt-rss.git
synced 2025-12-13 20:45:55 +00:00
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
This commit is contained in:
456
lib/dojo/html.js
456
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();
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user