330 lines
10 KiB
JavaScript
330 lines
10 KiB
JavaScript
|
if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
||
|
dojo._hasResource["dijit._Templated"] = true;
|
||
|
dojo.provide("dijit._Templated");
|
||
|
|
||
|
dojo.require("dijit._Widget");
|
||
|
dojo.require("dojo.string");
|
||
|
dojo.require("dojo.parser");
|
||
|
|
||
|
dojo.declare("dijit._Templated",
|
||
|
null,
|
||
|
{
|
||
|
// summary:
|
||
|
// Mixin for widgets that are instantiated from a template
|
||
|
//
|
||
|
// templateNode: DomNode
|
||
|
// a node that represents the widget template. Pre-empts both templateString and templatePath.
|
||
|
templateNode: null,
|
||
|
|
||
|
// templateString: String
|
||
|
// a string that represents the widget template. Pre-empts the
|
||
|
// templatePath. In builds that have their strings "interned", the
|
||
|
// templatePath is converted to an inline templateString, thereby
|
||
|
// preventing a synchronous network call.
|
||
|
templateString: null,
|
||
|
|
||
|
// templatePath: String
|
||
|
// Path to template (HTML file) for this widget relative to dojo.baseUrl
|
||
|
templatePath: null,
|
||
|
|
||
|
// widgetsInTemplate: Boolean
|
||
|
// should we parse the template to find widgets that might be
|
||
|
// declared in markup inside it? false by default.
|
||
|
widgetsInTemplate: false,
|
||
|
|
||
|
// containerNode: DomNode
|
||
|
// holds child elements. "containerNode" is generally set via a
|
||
|
// dojoAttachPoint assignment and it designates where children of
|
||
|
// the src dom node will be placed
|
||
|
containerNode: null,
|
||
|
|
||
|
// skipNodeCache: Boolean
|
||
|
// if using a cached widget template node poses issues for a
|
||
|
// particular widget class, it can set this property to ensure
|
||
|
// that its template is always re-built from a string
|
||
|
_skipNodeCache: false,
|
||
|
|
||
|
_stringRepl: function(tmpl){
|
||
|
var className = this.declaredClass, _this = this;
|
||
|
// Cache contains a string because we need to do property replacement
|
||
|
// do the property replacement
|
||
|
return dojo.string.substitute(tmpl, this, function(value, key){
|
||
|
if(key.charAt(0) == '!'){ value = _this[key.substr(1)]; }
|
||
|
if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
|
||
|
if(!value){ return ""; }
|
||
|
|
||
|
// Substitution keys beginning with ! will skip the transform step,
|
||
|
// in case a user wishes to insert unescaped markup, e.g. ${!foo}
|
||
|
return key.charAt(0) == "!" ? value :
|
||
|
// Safer substitution, see heading "Attribute values" in
|
||
|
// http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
|
||
|
value.toString().replace(/"/g,"""); //TODO: add &? use encodeXML method?
|
||
|
}, this);
|
||
|
},
|
||
|
|
||
|
// method over-ride
|
||
|
buildRendering: function(){
|
||
|
// summary:
|
||
|
// Construct the UI for this widget from a template, setting this.domNode.
|
||
|
|
||
|
// Lookup cached version of template, and download to cache if it
|
||
|
// isn't there already. Returns either a DomNode or a string, depending on
|
||
|
// whether or not the template contains ${foo} replacement parameters.
|
||
|
var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);
|
||
|
|
||
|
var node;
|
||
|
if(dojo.isString(cached)){
|
||
|
node = dijit._Templated._createNodesFromText(this._stringRepl(cached))[0];
|
||
|
}else{
|
||
|
// if it's a node, all we have to do is clone it
|
||
|
node = cached.cloneNode(true);
|
||
|
}
|
||
|
|
||
|
// recurse through the node, looking for, and attaching to, our
|
||
|
// attachment points which should be defined on the template node.
|
||
|
this._attachTemplateNodes(node);
|
||
|
|
||
|
var source = this.srcNodeRef;
|
||
|
if(source && source.parentNode){
|
||
|
source.parentNode.replaceChild(node, source);
|
||
|
}
|
||
|
|
||
|
this.domNode = node;
|
||
|
if(this.widgetsInTemplate){
|
||
|
var cw = this._supportingWidgets = dojo.parser.parse(node);
|
||
|
this._attachTemplateNodes(cw, function(n,p){
|
||
|
return n[p];
|
||
|
});
|
||
|
}
|
||
|
|
||
|
this._fillContent(source);
|
||
|
},
|
||
|
|
||
|
_fillContent: function(/*DomNode*/ source){
|
||
|
// summary:
|
||
|
// relocate source contents to templated container node
|
||
|
// this.containerNode must be able to receive children, or exceptions will be thrown
|
||
|
var dest = this.containerNode;
|
||
|
if(source && dest){
|
||
|
while(source.hasChildNodes()){
|
||
|
dest.appendChild(source.firstChild);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_attachTemplateNodes: function(rootNode, getAttrFunc){
|
||
|
// summary: Iterate through the template and attach functions and nodes accordingly.
|
||
|
// description:
|
||
|
// Map widget properties and functions to the handlers specified in
|
||
|
// the dom node and it's descendants. This function iterates over all
|
||
|
// nodes and looks for these properties:
|
||
|
// * dojoAttachPoint
|
||
|
// * dojoAttachEvent
|
||
|
// * waiRole
|
||
|
// * waiState
|
||
|
// rootNode: DomNode|Array[Widgets]
|
||
|
// the node to search for properties. All children will be searched.
|
||
|
// getAttrFunc: function?
|
||
|
// a function which will be used to obtain property for a given
|
||
|
// DomNode/Widget
|
||
|
|
||
|
getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };
|
||
|
|
||
|
var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
|
||
|
var x=dojo.isArray(rootNode)?0:-1;
|
||
|
for(; x<nodes.length; x++){
|
||
|
var baseNode = (x == -1) ? rootNode : nodes[x];
|
||
|
if(this.widgetsInTemplate && getAttrFunc(baseNode,'dojoType')){
|
||
|
continue;
|
||
|
}
|
||
|
// Process dojoAttachPoint
|
||
|
var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint");
|
||
|
if(attachPoint){
|
||
|
var point, points = attachPoint.split(/\s*,\s*/);
|
||
|
while((point = points.shift())){
|
||
|
if(dojo.isArray(this[point])){
|
||
|
this[point].push(baseNode);
|
||
|
}else{
|
||
|
this[point]=baseNode;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process dojoAttachEvent
|
||
|
var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent");
|
||
|
if(attachEvent){
|
||
|
// NOTE: we want to support attributes that have the form
|
||
|
// "domEvent: nativeEvent; ..."
|
||
|
var event, events = attachEvent.split(/\s*,\s*/);
|
||
|
var trim = dojo.trim;
|
||
|
while((event = events.shift())){
|
||
|
if(event){
|
||
|
var thisFunc = null;
|
||
|
if(event.indexOf(":") != -1){
|
||
|
// oh, if only JS had tuple assignment
|
||
|
var funcNameArr = event.split(":");
|
||
|
event = trim(funcNameArr[0]);
|
||
|
thisFunc = trim(funcNameArr[1]);
|
||
|
}else{
|
||
|
event = trim(event);
|
||
|
}
|
||
|
if(!thisFunc){
|
||
|
thisFunc = event;
|
||
|
}
|
||
|
this.connect(baseNode, event, thisFunc);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// waiRole, waiState
|
||
|
var role = getAttrFunc(baseNode, "waiRole");
|
||
|
if(role){
|
||
|
dijit.setWaiRole(baseNode, role);
|
||
|
}
|
||
|
var values = getAttrFunc(baseNode, "waiState");
|
||
|
if(values){
|
||
|
dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
|
||
|
if(stateValue.indexOf('-') != -1){
|
||
|
var pair = stateValue.split('-');
|
||
|
dijit.setWaiState(baseNode, pair[0], pair[1]);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
|
||
|
// key is either templatePath or templateString; object is either string or DOM tree
|
||
|
dijit._Templated._templateCache = {};
|
||
|
|
||
|
dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
|
||
|
// summary:
|
||
|
// Static method to get a template based on the templatePath or
|
||
|
// templateString key
|
||
|
// templatePath: String
|
||
|
// The URL to get the template from. dojo.uri.Uri is often passed as well.
|
||
|
// templateString: String?
|
||
|
// a string to use in lieu of fetching the template from a URL. Takes precedence
|
||
|
// over templatePath
|
||
|
// Returns: Mixed
|
||
|
// Either string (if there are ${} variables that need to be replaced) or just
|
||
|
// a DOM tree (if the node can be cloned directly)
|
||
|
|
||
|
// is it already cached?
|
||
|
var tmplts = dijit._Templated._templateCache;
|
||
|
var key = templateString || templatePath;
|
||
|
var cached = tmplts[key];
|
||
|
if(cached){
|
||
|
return cached;
|
||
|
}
|
||
|
|
||
|
// If necessary, load template string from template path
|
||
|
if(!templateString){
|
||
|
templateString = dijit._Templated._sanitizeTemplateString(dojo._getText(templatePath));
|
||
|
}
|
||
|
|
||
|
templateString = dojo.string.trim(templateString);
|
||
|
|
||
|
if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
|
||
|
// there are variables in the template so all we can do is cache the string
|
||
|
return (tmplts[key] = templateString); //String
|
||
|
}else{
|
||
|
// there are no variables in the template so we can cache the DOM tree
|
||
|
return (tmplts[key] = dijit._Templated._createNodesFromText(templateString)[0]); //Node
|
||
|
}
|
||
|
};
|
||
|
|
||
|
dijit._Templated._sanitizeTemplateString = function(/*String*/tString){
|
||
|
// summary:
|
||
|
// Strips <?xml ...?> declarations so that external SVG and XML
|
||
|
// documents can be added to a document without worry. Also, if the string
|
||
|
// is an HTML document, only the part inside the body tag is returned.
|
||
|
if(tString){
|
||
|
tString = tString.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
|
||
|
var matches = tString.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
|
||
|
if(matches){
|
||
|
tString = matches[1];
|
||
|
}
|
||
|
}else{
|
||
|
tString = "";
|
||
|
}
|
||
|
return tString; //String
|
||
|
};
|
||
|
|
||
|
|
||
|
if(dojo.isIE){
|
||
|
dojo.addOnUnload(function(){
|
||
|
var cache = dijit._Templated._templateCache;
|
||
|
for(var key in cache){
|
||
|
var value = cache[key];
|
||
|
if(!isNaN(value.nodeType)){ // isNode equivalent
|
||
|
dojo._destroyElement(value);
|
||
|
}
|
||
|
delete cache[key];
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
(function(){
|
||
|
var tagMap = {
|
||
|
cell: {re: /^<t[dh][\s\r\n>]/i, pre: "<table><tbody><tr>", post: "</tr></tbody></table>"},
|
||
|
row: {re: /^<tr[\s\r\n>]/i, pre: "<table><tbody>", post: "</tbody></table>"},
|
||
|
section: {re: /^<(thead|tbody|tfoot)[\s\r\n>]/i, pre: "<table>", post: "</table>"}
|
||
|
};
|
||
|
|
||
|
// dummy container node used temporarily to hold nodes being created
|
||
|
var tn;
|
||
|
|
||
|
dijit._Templated._createNodesFromText = function(/*String*/text){
|
||
|
// summary:
|
||
|
// Attempts to create a set of nodes based on the structure of the passed text.
|
||
|
|
||
|
if(!tn){
|
||
|
tn = dojo.doc.createElement("div");
|
||
|
tn.style.display="none";
|
||
|
dojo.body().appendChild(tn);
|
||
|
}
|
||
|
var tableType = "none";
|
||
|
var rtext = text.replace(/^\s+/, "");
|
||
|
for(var type in tagMap){
|
||
|
var map = tagMap[type];
|
||
|
if(map.re.test(rtext)){
|
||
|
tableType = type;
|
||
|
text = map.pre + text + map.post;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tn.innerHTML = text;
|
||
|
if(tn.normalize){
|
||
|
tn.normalize();
|
||
|
}
|
||
|
|
||
|
var tag = { cell: "tr", row: "tbody", section: "table" }[tableType];
|
||
|
var _parent = (typeof tag != "undefined") ?
|
||
|
tn.getElementsByTagName(tag)[0] :
|
||
|
tn;
|
||
|
|
||
|
var nodes = [];
|
||
|
while(_parent.firstChild){
|
||
|
nodes.push(_parent.removeChild(_parent.firstChild));
|
||
|
}
|
||
|
tn.innerHTML="";
|
||
|
return nodes; // Array
|
||
|
}
|
||
|
})();
|
||
|
|
||
|
// These arguments can be specified for widgets which are used in templates.
|
||
|
// Since any widget can be specified as sub widgets in template, mix it
|
||
|
// into the base widget class. (This is a hack, but it's effective.)
|
||
|
dojo.extend(dijit._Widget,{
|
||
|
dojoAttachEvent: "",
|
||
|
dojoAttachPoint: "",
|
||
|
waiRole: "",
|
||
|
waiState:""
|
||
|
})
|
||
|
|
||
|
}
|