mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5282 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
903 lines
32 KiB
JavaScript
903 lines
32 KiB
JavaScript
/*
|
|
Copyright (c) 2004-2006, The Dojo Foundation
|
|
All Rights Reserved.
|
|
|
|
Licensed under the Academic Free License version 2.1 or above OR the
|
|
modified BSD license. For more information on Dojo licensing, see:
|
|
|
|
http://dojotoolkit.org/community/licensing.shtml
|
|
*/
|
|
|
|
dojo.provide("dojo.widget.DomWidget");
|
|
|
|
dojo.require("dojo.event.*");
|
|
dojo.require("dojo.widget.Widget");
|
|
dojo.require("dojo.dom");
|
|
dojo.require("dojo.html.style");
|
|
dojo.require("dojo.xml.Parse");
|
|
dojo.require("dojo.uri.*");
|
|
dojo.require("dojo.lang.func");
|
|
dojo.require("dojo.lang.extras");
|
|
|
|
dojo.widget._cssFiles = {};
|
|
dojo.widget._cssStrings = {};
|
|
dojo.widget._templateCache = {};
|
|
|
|
dojo.widget.defaultStrings = {
|
|
// summary: a mapping of strings that are used in template variable replacement
|
|
dojoRoot: dojo.hostenv.getBaseScriptUri(),
|
|
baseScriptUri: dojo.hostenv.getBaseScriptUri()
|
|
};
|
|
|
|
dojo.widget.fillFromTemplateCache = function(obj, templatePath, templateString, avoidCache){
|
|
// summary:
|
|
// static method to build from a template w/ or w/o a real widget in
|
|
// place
|
|
// obj: DomWidget
|
|
// an instance of dojo.widget.DomWidget to initialize the template for
|
|
// 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
|
|
// avoidCache: Boolean?
|
|
// should the template system not use whatever is in the cache and
|
|
// always use the passed templatePath or templateString?
|
|
|
|
// dojo.debug("avoidCache:", avoidCache);
|
|
var tpath = templatePath || obj.templatePath;
|
|
|
|
var tmplts = dojo.widget._templateCache;
|
|
if(!tpath && !obj["widgetType"]) { // don't have a real template here
|
|
do {
|
|
var dummyName = "__dummyTemplate__" + dojo.widget._templateCache.dummyCount++;
|
|
} while(tmplts[dummyName]);
|
|
obj.widgetType = dummyName;
|
|
}
|
|
var wt = tpath?tpath.toString():obj.widgetType;
|
|
|
|
var ts = tmplts[wt];
|
|
if(!ts){
|
|
tmplts[wt] = {"string": null, "node": null};
|
|
if(avoidCache){
|
|
ts = {};
|
|
}else{
|
|
ts = tmplts[wt];
|
|
}
|
|
}
|
|
if((!obj.templateString)&&(!avoidCache)){
|
|
obj.templateString = templateString || ts["string"];
|
|
}
|
|
if((!obj.templateNode)&&(!avoidCache)){
|
|
obj.templateNode = ts["node"];
|
|
}
|
|
if((!obj.templateNode)&&(!obj.templateString)&&(tpath)){
|
|
// fetch a text fragment and assign it to templateString
|
|
// NOTE: we rely on blocking IO here!
|
|
var tstring = dojo.hostenv.getText(tpath);
|
|
if(tstring){
|
|
// strip <?xml ...?> declarations so that external SVG and XML
|
|
// documents can be added to a document without worry
|
|
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 = "";
|
|
}
|
|
|
|
obj.templateString = tstring;
|
|
if(!avoidCache){
|
|
tmplts[wt]["string"] = tstring;
|
|
}
|
|
}
|
|
if((!ts["string"])&&(!avoidCache)){
|
|
ts.string = obj.templateString;
|
|
}
|
|
}
|
|
dojo.widget._templateCache.dummyCount = 0;
|
|
|
|
// Array: list of properties to search for node-to-property mappings
|
|
dojo.widget.attachProperties = ["dojoAttachPoint", "id"];
|
|
|
|
// String: name of the property to use for mapping DOM events to widget functions
|
|
dojo.widget.eventAttachProperty = "dojoAttachEvent";
|
|
|
|
// String: property name of code to evaluate when the widget is constructed
|
|
dojo.widget.onBuildProperty = "dojoOnBuild";
|
|
|
|
// Array: possible accessibility values to set on widget elements - role or state
|
|
dojo.widget.waiNames = ["waiRole", "waiState"];
|
|
|
|
dojo.widget.wai = {
|
|
// summary: Contains functions to set accessibility roles and states
|
|
// onto widget elements
|
|
waiRole: {
|
|
// name: String:
|
|
// information for mapping accessibility role
|
|
name: "waiRole",
|
|
// namespace: String:
|
|
// URI of the namespace for the set of roles
|
|
"namespace": "http://www.w3.org/TR/xhtml2",
|
|
// alias: String:
|
|
// The alias to assign the namespace
|
|
alias: "x2",
|
|
// prefix: String:
|
|
// The prefix to assign to the role value
|
|
prefix: "wairole:"
|
|
},
|
|
waiState: {
|
|
// name: String:
|
|
// information for mapping accessibility state
|
|
name: "waiState",
|
|
// namespace: String:
|
|
// URI of the namespace for the set of states
|
|
"namespace": "http://www.w3.org/2005/07/aaa",
|
|
// alias: String:
|
|
// The alias to assign the namespace
|
|
alias: "aaa",
|
|
// prefix: String:
|
|
// empty string - state value does not require prefix
|
|
prefix: ""
|
|
},
|
|
setAttr: function(/*DomNode*/node, /*String*/ ns, /*String*/ attr, /*String|Boolean*/value){
|
|
// summary: Use appropriate API to set the role or state attribute onto the element.
|
|
// description: In IE use the generic setAttribute() api. Append a namespace
|
|
// alias to the attribute name and appropriate prefix to the value.
|
|
// Otherwise, use the setAttribueNS api to set the namespaced attribute. Also
|
|
// add the appropriate prefix to the attribute value.
|
|
if(dojo.render.html.ie){
|
|
node.setAttribute(this[ns].alias+":"+ attr, this[ns].prefix+value);
|
|
}else{
|
|
node.setAttributeNS(this[ns]["namespace"], attr, this[ns].prefix+value);
|
|
}
|
|
},
|
|
|
|
getAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
|
|
// Summary: Use the appropriate API to retrieve the role or state value
|
|
// Description: In IE use the generic getAttribute() api. An alias value
|
|
// was added to the attribute name to simulate a namespace when the attribute
|
|
// was set. Otherwise use the getAttributeNS() api to retrieve the state value
|
|
if(dojo.render.html.ie){
|
|
return node.getAttribute(this[ns].alias+":"+attr);
|
|
}else{
|
|
return node.getAttributeNS(this[ns]["namespace"], attr);
|
|
}
|
|
},
|
|
removeAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
|
|
// summary: Use the appropriate API to remove the role or state value
|
|
// description: In IE use the generic removeAttribute() api. An alias value
|
|
// was added to the attribute name to simulate a namespace when the attribute
|
|
// was set. Otherwise use the removeAttributeNS() api to remove the state value
|
|
var success = true; //only IE returns a value
|
|
if(dojo.render.html.ie){
|
|
success = node.removeAttribute(this[ns].alias+":"+attr);
|
|
}else{
|
|
node.removeAttributeNS(this[ns]["namespace"], attr);
|
|
}
|
|
return success;
|
|
}
|
|
};
|
|
|
|
dojo.widget.attachTemplateNodes = function(rootNode, /*Widget*/ targetObj, events ){
|
|
// summary:
|
|
// 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
|
|
// * any "dojoOn*" proprties passed in the events array
|
|
// rootNode: DomNode
|
|
// the node to search for properties. All children will be searched.
|
|
// events: Array
|
|
// a list of properties generated from getDojoEventsFromStr.
|
|
|
|
// FIXME: this method is still taking WAAAY too long. We need ways of optimizing:
|
|
// a.) what we are looking for on each node
|
|
// b.) the nodes that are subject to interrogation (use xpath instead?)
|
|
// c.) how expensive event assignment is (less eval(), more connect())
|
|
// var start = new Date();
|
|
var elementNodeType = dojo.dom.ELEMENT_NODE;
|
|
|
|
function trim(str){
|
|
return str.replace(/^\s+|\s+$/g, "");
|
|
}
|
|
|
|
if(!rootNode){
|
|
rootNode = targetObj.domNode;
|
|
}
|
|
|
|
if(rootNode.nodeType != elementNodeType){
|
|
return;
|
|
}
|
|
// alert(events.length);
|
|
|
|
var nodes = rootNode.all || rootNode.getElementsByTagName("*");
|
|
var _this = targetObj;
|
|
for(var x=-1; x<nodes.length; x++){
|
|
var baseNode = (x == -1) ? rootNode : nodes[x];
|
|
// FIXME: is this going to have capitalization problems? Could use getAttribute(name, 0); to get attributes case-insensitve
|
|
var attachPoint = [];
|
|
if(!targetObj.widgetsInTemplate || !baseNode.getAttribute('dojoType')){
|
|
for(var y=0; y<this.attachProperties.length; y++){
|
|
var tmpAttachPoint = baseNode.getAttribute(this.attachProperties[y]);
|
|
if(tmpAttachPoint){
|
|
attachPoint = tmpAttachPoint.split(";");
|
|
for(var z=0; z<attachPoint.length; z++){
|
|
if(dojo.lang.isArray(targetObj[attachPoint[z]])){
|
|
targetObj[attachPoint[z]].push(baseNode);
|
|
}else{
|
|
targetObj[attachPoint[z]]=baseNode;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
var attachEvent = baseNode.getAttribute(this.eventAttachProperty);
|
|
if(attachEvent){
|
|
// NOTE: we want to support attributes that have the form
|
|
// "domEvent: nativeEvent; ..."
|
|
var evts = attachEvent.split(";");
|
|
for(var y=0; y<evts.length; y++){
|
|
if((!evts[y])||(!evts[y].length)){ continue; }
|
|
var thisFunc = null;
|
|
var tevt = trim(evts[y]);
|
|
if(evts[y].indexOf(":") >= 0){
|
|
// oh, if only JS had tuple assignment
|
|
var funcNameArr = tevt.split(":");
|
|
tevt = trim(funcNameArr[0]);
|
|
thisFunc = trim(funcNameArr[1]);
|
|
}
|
|
if(!thisFunc){
|
|
thisFunc = tevt;
|
|
}
|
|
|
|
var tf = function(){
|
|
var ntf = new String(thisFunc);
|
|
return function(evt){
|
|
if(_this[ntf]){
|
|
_this[ntf](dojo.event.browser.fixEvent(evt, this));
|
|
}
|
|
};
|
|
}();
|
|
dojo.event.browser.addListener(baseNode, tevt, tf, false, true);
|
|
// dojo.event.browser.addListener(baseNode, tevt, dojo.lang.hitch(_this, thisFunc));
|
|
}
|
|
}
|
|
|
|
for(var y=0; y<events.length; y++){
|
|
//alert(events[x]);
|
|
var evtVal = baseNode.getAttribute(events[y]);
|
|
if((evtVal)&&(evtVal.length)){
|
|
var thisFunc = null;
|
|
var domEvt = events[y].substr(4); // clober the "dojo" prefix
|
|
thisFunc = trim(evtVal);
|
|
var funcs = [thisFunc];
|
|
if(thisFunc.indexOf(";")>=0){
|
|
funcs = dojo.lang.map(thisFunc.split(";"), trim);
|
|
}
|
|
for(var z=0; z<funcs.length; z++){
|
|
if(!funcs[z].length){ continue; }
|
|
var tf = function(){
|
|
var ntf = new String(funcs[z]);
|
|
return function(evt){
|
|
if(_this[ntf]){
|
|
_this[ntf](dojo.event.browser.fixEvent(evt, this));
|
|
}
|
|
}
|
|
}();
|
|
dojo.event.browser.addListener(baseNode, domEvt, tf, false, true);
|
|
// dojo.event.browser.addListener(baseNode, domEvt, dojo.lang.hitch(_this, funcs[z]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// continue;
|
|
|
|
// FIXME: we need to put this into some kind of lookup structure
|
|
// instead of direct assignment
|
|
var tmpltPoint = baseNode.getAttribute(this.templateProperty);
|
|
if(tmpltPoint){
|
|
targetObj[tmpltPoint]=baseNode;
|
|
}
|
|
|
|
dojo.lang.forEach(dojo.widget.waiNames, function(name){
|
|
var wai = dojo.widget.wai[name];
|
|
var val = baseNode.getAttribute(wai.name);
|
|
if(val){
|
|
if(val.indexOf('-') == -1){
|
|
dojo.widget.wai.setAttr(baseNode, wai.name, "role", val);
|
|
}else{
|
|
// this is a state-value pair
|
|
var statePair = val.split('-');
|
|
dojo.widget.wai.setAttr(baseNode, wai.name, statePair[0], statePair[1]);
|
|
}
|
|
}
|
|
}, this);
|
|
|
|
var onBuild = baseNode.getAttribute(this.onBuildProperty);
|
|
if(onBuild){
|
|
eval("var node = baseNode; var widget = targetObj; "+onBuild);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
dojo.widget.getDojoEventsFromStr = function(str){
|
|
// summary:
|
|
// generates a list of properties with names that match the form
|
|
// dojoOn*
|
|
// str: String
|
|
// the template string to search
|
|
|
|
// var lstr = str.toLowerCase();
|
|
var re = /(dojoOn([a-z]+)(\s?))=/gi;
|
|
var evts = str ? str.match(re)||[] : [];
|
|
var ret = [];
|
|
var lem = {};
|
|
for(var x=0; x<evts.length; x++){
|
|
if(evts[x].length < 1){ continue; }
|
|
var cm = evts[x].replace(/\s/, "");
|
|
cm = (cm.slice(0, cm.length-1));
|
|
if(!lem[cm]){
|
|
lem[cm] = true;
|
|
ret.push(cm);
|
|
}
|
|
}
|
|
return ret; // Array
|
|
}
|
|
|
|
dojo.declare("dojo.widget.DomWidget",
|
|
dojo.widget.Widget,
|
|
function(){
|
|
// summary:
|
|
// dojo.widget.DomWidget is the superclass that provides behavior for all
|
|
// DOM-based renderers, including HtmlWidget and SvgWidget. DomWidget
|
|
// implements the templating system that most widget authors use to define
|
|
// the UI for their widgets.
|
|
if((arguments.length>0)&&(typeof arguments[0] == "object")){
|
|
this.create(arguments[0]);
|
|
}
|
|
},
|
|
{
|
|
// 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,
|
|
|
|
// templateCssString String:
|
|
// a string that represents the CSS for the widgettemplate.
|
|
// Pre-empts the templateCssPath. In builds that have their
|
|
// strings "interned", the templateCssPath is converted to an
|
|
// inline templateCssString, thereby preventing a synchronous
|
|
// network call.
|
|
templateCssString: null,
|
|
|
|
// preventClobber Boolean:
|
|
// should the widget not replace the node from which it was
|
|
// constructed? Widgets that apply behaviors to pre-existing parts
|
|
// of a page can be implemented easily by setting this to "true".
|
|
// In these cases, the domNode property will point to the node
|
|
// which the widget was created from.
|
|
preventClobber: false,
|
|
|
|
// domNode DomNode:
|
|
// this is our visible representation of the widget! Other DOM
|
|
// Nodes may by assigned to other properties, usually through the
|
|
// template system's dojoAttachPonit syntax, but the domNode
|
|
// property is the canonical "top level" node in widget UI.
|
|
domNode: null,
|
|
|
|
// containerNode DomNode:
|
|
// holds child elements. "containerNode" is generally set via a
|
|
// dojoAttachPoint assignment and it designates where widgets that
|
|
// are defined as "children" of the parent will be placed
|
|
// visually.
|
|
containerNode: null,
|
|
|
|
// widgetsInTemplate Boolean:
|
|
// should we parse the template to find widgets that might be
|
|
// declared in markup inside it? false by default.
|
|
widgetsInTemplate: false,
|
|
|
|
addChild: function(/*Widget*/ widget, overrideContainerNode, pos, ref, insertIndex){
|
|
// summary:
|
|
// Process the given child widget, inserting it's dom node as
|
|
// a child of our dom node
|
|
// overrideContainerNode: DomNode?
|
|
// a non-default container node for the widget
|
|
// pos: String?
|
|
// can be one of "before", "after", "first", or "last". This
|
|
// has the same meaning as in dojo.dom.insertAtPosition()
|
|
// ref: DomNode?
|
|
// a node to place the widget relative to
|
|
// insertIndex: int?
|
|
// DOM index, same meaning as in dojo.dom.insertAtIndex()
|
|
// returns: the widget that was inserted
|
|
|
|
// FIXME: should we support addition at an index in the children arr and
|
|
// order the display accordingly? Right now we always append.
|
|
if(!this.isContainer){ // we aren't allowed to contain other widgets, it seems
|
|
dojo.debug("dojo.widget.DomWidget.addChild() attempted on non-container widget");
|
|
return null;
|
|
}else{
|
|
if(insertIndex == undefined){
|
|
insertIndex = this.children.length;
|
|
}
|
|
this.addWidgetAsDirectChild(widget, overrideContainerNode, pos, ref, insertIndex);
|
|
this.registerChild(widget, insertIndex);
|
|
}
|
|
return widget; // Widget
|
|
},
|
|
|
|
addWidgetAsDirectChild: function(/*Widget*/ widget, overrideContainerNode, pos, ref, insertIndex){
|
|
// summary:
|
|
// Process the given child widget, inserting it's dom node as
|
|
// a child of our dom node
|
|
// overrideContainerNode: DomNode
|
|
// a non-default container node for the widget
|
|
// pos: String?
|
|
// can be one of "before", "after", "first", or "last". This
|
|
// has the same meaning as in dojo.dom.insertAtPosition()
|
|
// ref: DomNode?
|
|
// a node to place the widget relative to
|
|
// insertIndex: int?
|
|
// DOM index, same meaning as in dojo.dom.insertAtIndex()
|
|
if((!this.containerNode)&&(!overrideContainerNode)){
|
|
this.containerNode = this.domNode;
|
|
}
|
|
var cn = (overrideContainerNode) ? overrideContainerNode : this.containerNode;
|
|
if(!pos){ pos = "after"; }
|
|
if(!ref){
|
|
if(!cn){ cn = dojo.body(); }
|
|
ref = cn.lastChild;
|
|
}
|
|
if(!insertIndex) { insertIndex = 0; }
|
|
widget.domNode.setAttribute("dojoinsertionindex", insertIndex);
|
|
|
|
// insert the child widget domNode directly underneath my domNode, in the
|
|
// specified position (by default, append to end)
|
|
if(!ref){
|
|
cn.appendChild(widget.domNode);
|
|
}else{
|
|
// FIXME: was this meant to be the (ugly hack) way to support insert @ index?
|
|
//dojo.dom[pos](widget.domNode, ref, insertIndex);
|
|
|
|
// CAL: this appears to be the intended way to insert a node at a given position...
|
|
if (pos == 'insertAtIndex'){
|
|
// dojo.debug("idx:", insertIndex, "isLast:", ref === cn.lastChild);
|
|
dojo.dom.insertAtIndex(widget.domNode, ref.parentNode, insertIndex);
|
|
}else{
|
|
// dojo.debug("pos:", pos, "isLast:", ref === cn.lastChild);
|
|
if((pos == "after")&&(ref === cn.lastChild)){
|
|
cn.appendChild(widget.domNode);
|
|
}else{
|
|
dojo.dom.insertAtPosition(widget.domNode, cn, pos);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
registerChild: function(widget, insertionIndex){
|
|
// summary: record that given widget descends from me
|
|
// widget: Widget
|
|
// the widget that is now a child
|
|
// insertionIndex: int
|
|
// where in the children[] array to place it
|
|
|
|
// we need to insert the child at the right point in the parent's
|
|
// 'children' array, based on the insertionIndex
|
|
|
|
widget.dojoInsertionIndex = insertionIndex;
|
|
|
|
var idx = -1;
|
|
for(var i=0; i<this.children.length; i++){
|
|
|
|
//This appears to fix an out of order issue in the case of mixed
|
|
//markup and programmatically added children. Previously, if a child
|
|
//existed from markup, and another child was addChild()d without specifying
|
|
//any additional parameters, it would end up first in the list, when in fact
|
|
//it should be after. I can't see cases where this would break things, but
|
|
//I could see no other obvious solution. -dustin
|
|
|
|
if (this.children[i].dojoInsertionIndex <= insertionIndex){
|
|
idx = i;
|
|
}
|
|
}
|
|
|
|
this.children.splice(idx+1, 0, widget);
|
|
|
|
widget.parent = this;
|
|
widget.addedTo(this, idx+1);
|
|
|
|
// If this widget was created programatically, then it was erroneously added
|
|
// to dojo.widget.manager.topWidgets. Fix that here.
|
|
delete dojo.widget.manager.topWidgets[widget.widgetId];
|
|
},
|
|
|
|
removeChild: function(/*Widget*/ widget){
|
|
// summary: detach child domNode from parent domNode
|
|
dojo.dom.removeNode(widget.domNode);
|
|
|
|
// remove child widget from parent widget
|
|
return dojo.widget.DomWidget.superclass.removeChild.call(this, widget); // Widget
|
|
},
|
|
|
|
getFragNodeRef: function(frag){
|
|
// summary:
|
|
// returns the source node, if any, that the widget was
|
|
// declared from
|
|
// frag: Object
|
|
// an opaque data structure generated by the first-pass parser
|
|
if(!frag){return null;} // null
|
|
if(!frag[this.getNamespacedType()]){
|
|
dojo.raise("Error: no frag for widget type " + this.getNamespacedType()
|
|
+ ", id " + this.widgetId
|
|
+ " (maybe a widget has set it's type incorrectly)");
|
|
}
|
|
return frag[this.getNamespacedType()]["nodeRef"]; // DomNode
|
|
},
|
|
|
|
postInitialize: function(/*Object*/ args, /*Object*/ frag, /*Widget*/ parentComp){
|
|
// summary:
|
|
// Replace the source domNode with the generated dom
|
|
// structure, and register the widget with its parent.
|
|
// This is an implementation of the stub function defined in
|
|
// dojo.widget.Widget.
|
|
|
|
//dojo.profile.start(this.widgetType + " postInitialize");
|
|
|
|
var sourceNodeRef = this.getFragNodeRef(frag);
|
|
// Stick my generated dom into the output tree
|
|
//alert(this.widgetId + ": replacing " + sourceNodeRef + " with " + this.domNode.innerHTML);
|
|
if (parentComp && (parentComp.snarfChildDomOutput || !sourceNodeRef)){
|
|
// Add my generated dom as a direct child of my parent widget
|
|
// This is important for generated widgets, and also cases where I am generating an
|
|
// <li> node that can't be inserted back into the original DOM tree
|
|
parentComp.addWidgetAsDirectChild(this, "", "insertAtIndex", "", args["dojoinsertionindex"], sourceNodeRef);
|
|
} else if (sourceNodeRef){
|
|
// Do in-place replacement of the my source node with my generated dom
|
|
if(this.domNode && (this.domNode !== sourceNodeRef)){
|
|
this._sourceNodeRef = dojo.dom.replaceNode(sourceNodeRef, this.domNode);
|
|
}
|
|
}
|
|
|
|
// Register myself with my parent, or with the widget manager if
|
|
// I have no parent
|
|
// TODO: the code below erroneously adds all programatically generated widgets
|
|
// to topWidgets (since we don't know who the parent is until after creation finishes)
|
|
if ( parentComp ) {
|
|
parentComp.registerChild(this, args.dojoinsertionindex);
|
|
} else {
|
|
dojo.widget.manager.topWidgets[this.widgetId]=this;
|
|
}
|
|
|
|
if(this.widgetsInTemplate){
|
|
var parser = new dojo.xml.Parse();
|
|
|
|
var subContainerNode;
|
|
//TODO: use xpath here?
|
|
var subnodes = this.domNode.getElementsByTagName("*");
|
|
for(var i=0;i<subnodes.length;i++){
|
|
if(subnodes[i].getAttribute('dojoAttachPoint') == 'subContainerWidget'){
|
|
subContainerNode = subnodes[i];
|
|
// break;
|
|
}
|
|
if(subnodes[i].getAttribute('dojoType')){
|
|
subnodes[i].setAttribute('isSubWidget', true);
|
|
}
|
|
}
|
|
if (this.isContainer && !this.containerNode){
|
|
//no containerNode is available, which means a widget is used as a container. find it here and move
|
|
//all dom nodes defined in the main html page as children of this.domNode into the actual container
|
|
//widget's node (at this point, the subwidgets defined in the template file is not parsed yet)
|
|
if(subContainerNode){
|
|
var src = this.getFragNodeRef(frag);
|
|
if (src){
|
|
dojo.dom.moveChildren(src, subContainerNode);
|
|
//do not need to follow children nodes in the main html page, as they
|
|
//will be dealt with in the subContainerWidget
|
|
frag['dojoDontFollow'] = true;
|
|
}
|
|
}else{
|
|
dojo.debug("No subContainerWidget node can be found in template file for widget "+this);
|
|
}
|
|
}
|
|
|
|
var templatefrag = parser.parseElement(this.domNode, null, true);
|
|
// createSubComponents not createComponents because frag has already been created
|
|
dojo.widget.getParser().createSubComponents(templatefrag, this);
|
|
|
|
//find all the sub widgets defined in the template file of this widget
|
|
var subwidgets = [];
|
|
var stack = [this];
|
|
var w;
|
|
while((w = stack.pop())){
|
|
for(var i = 0; i < w.children.length; i++){
|
|
var cwidget = w.children[i];
|
|
if(cwidget._processedSubWidgets || !cwidget.extraArgs['issubwidget']){ continue; }
|
|
subwidgets.push(cwidget);
|
|
if(cwidget.isContainer){
|
|
stack.push(cwidget);
|
|
}
|
|
}
|
|
}
|
|
|
|
//connect event to this widget/attach dom node
|
|
for(var i = 0; i < subwidgets.length; i++){
|
|
var widget = subwidgets[i];
|
|
if(widget._processedSubWidgets){
|
|
dojo.debug("This should not happen: widget._processedSubWidgets is already true!");
|
|
return;
|
|
}
|
|
widget._processedSubWidgets = true;
|
|
if(widget.extraArgs['dojoattachevent']){
|
|
var evts = widget.extraArgs['dojoattachevent'].split(";");
|
|
for(var j=0; j<evts.length; j++){
|
|
var thisFunc = null;
|
|
var tevt = dojo.string.trim(evts[j]);
|
|
if(tevt.indexOf(":") >= 0){
|
|
// oh, if only JS had tuple assignment
|
|
var funcNameArr = tevt.split(":");
|
|
tevt = dojo.string.trim(funcNameArr[0]);
|
|
thisFunc = dojo.string.trim(funcNameArr[1]);
|
|
}
|
|
if(!thisFunc){
|
|
thisFunc = tevt;
|
|
}
|
|
if(dojo.lang.isFunction(widget[tevt])){
|
|
dojo.event.kwConnect({
|
|
srcObj: widget,
|
|
srcFunc: tevt,
|
|
targetObj: this,
|
|
targetFunc: thisFunc
|
|
});
|
|
}else{
|
|
alert(tevt+" is not a function in widget "+widget);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(widget.extraArgs['dojoattachpoint']){
|
|
//don't attach widget.domNode here, as we do not know which
|
|
//dom node we should connect to (in checkbox widget case,
|
|
//it is inputNode). So we make the widget itself available
|
|
this[widget.extraArgs['dojoattachpoint']] = widget;
|
|
}
|
|
}
|
|
}
|
|
|
|
//dojo.profile.end(this.widgetType + " postInitialize");
|
|
|
|
// Expand my children widgets
|
|
/* dojoDontFollow is important for a very special case
|
|
* basically if you have a widget that you instantiate from script
|
|
* and that widget is a container, and it contains a reference to a parent
|
|
* instance, the parser will start recursively parsing until the browser
|
|
* complains. So the solution is to set an initialization property of
|
|
* dojoDontFollow: true and then it won't recurse where it shouldn't
|
|
*/
|
|
if(this.isContainer && !frag["dojoDontFollow"]){
|
|
//alert("recurse from " + this.widgetId);
|
|
// build any sub-components with us as the parent
|
|
dojo.widget.getParser().createSubComponents(frag, this);
|
|
}
|
|
},
|
|
|
|
// method over-ride
|
|
buildRendering: function(/*Object*/ args, /*Object*/ frag){
|
|
// summary:
|
|
// Construct the UI for this widget, generally from a
|
|
// template. This can be over-ridden for custom UI creation to
|
|
// to side-step the template system. This is an
|
|
// implementation of the stub function defined in
|
|
// dojo.widget.Widget.
|
|
|
|
// DOM widgets construct themselves from a template
|
|
var ts = dojo.widget._templateCache[this.widgetType];
|
|
|
|
// Handle style for this widget here, as even if templatePath
|
|
// is not set, style specified by templateCssString or templateCssPath
|
|
// should be applied. templateCssString has higher priority
|
|
// than templateCssPath
|
|
if(args["templatecsspath"]){
|
|
args["templateCssPath"] = args["templatecsspath"];
|
|
}
|
|
var cpath = args["templateCssPath"] || this.templateCssPath;
|
|
if(cpath && !dojo.widget._cssFiles[cpath.toString()]){
|
|
if((!this.templateCssString)&&(cpath)){
|
|
this.templateCssString = dojo.hostenv.getText(cpath);
|
|
this.templateCssPath = null;
|
|
}
|
|
dojo.widget._cssFiles[cpath.toString()] = true;
|
|
}
|
|
|
|
if((this["templateCssString"])&&(!dojo.widget._cssStrings[this.templateCssString])){
|
|
dojo.html.insertCssText(this.templateCssString, null, cpath);
|
|
dojo.widget._cssStrings[this.templateCssString] = true;
|
|
}
|
|
if(
|
|
(!this.preventClobber)&&(
|
|
(this.templatePath)||
|
|
(this.templateNode)||
|
|
(
|
|
(this["templateString"])&&(this.templateString.length)
|
|
)||
|
|
(
|
|
(typeof ts != "undefined")&&( (ts["string"])||(ts["node"]) )
|
|
)
|
|
)
|
|
){
|
|
// if it looks like we can build the thing from a template, do it!
|
|
this.buildFromTemplate(args, frag);
|
|
}else{
|
|
// otherwise, assign the DOM node that was the source of the widget
|
|
// parsing to be the root node
|
|
this.domNode = this.getFragNodeRef(frag);
|
|
}
|
|
this.fillInTemplate(args, frag); // this is where individual widgets
|
|
// will handle population of data
|
|
// from properties, remote data
|
|
// sets, etc.
|
|
},
|
|
|
|
buildFromTemplate: function(/*Object*/ args, /*Object*/ frag){
|
|
// summary:
|
|
// Called by buildRendering, creates the actual UI in a DomWidget.
|
|
|
|
// var start = new Date();
|
|
// copy template properties if they're already set in the templates object
|
|
// dojo.debug("buildFromTemplate:", this);
|
|
var avoidCache = false;
|
|
if(args["templatepath"]){
|
|
// avoidCache = true;
|
|
args["templatePath"] = args["templatepath"];
|
|
}
|
|
dojo.widget.fillFromTemplateCache( this,
|
|
args["templatePath"],
|
|
null,
|
|
avoidCache);
|
|
var ts = dojo.widget._templateCache[this.templatePath?this.templatePath.toString():this.widgetType];
|
|
if((ts)&&(!avoidCache)){
|
|
if(!this.templateString.length){
|
|
this.templateString = ts["string"];
|
|
}
|
|
if(!this.templateNode){
|
|
this.templateNode = ts["node"];
|
|
}
|
|
}
|
|
var matches = false;
|
|
var node = null;
|
|
// var tstr = new String(this.templateString);
|
|
var tstr = this.templateString;
|
|
// attempt to clone a template node, if there is one
|
|
if((!this.templateNode)&&(this.templateString)){
|
|
matches = this.templateString.match(/\$\{([^\}]+)\}/g);
|
|
if(matches) {
|
|
// if we do property replacement, don't create a templateNode
|
|
// to clone from.
|
|
var hash = this.strings || {};
|
|
// FIXME: should this hash of default replacements be cached in
|
|
// templateString?
|
|
for(var key in dojo.widget.defaultStrings) {
|
|
if(dojo.lang.isUndefined(hash[key])) {
|
|
hash[key] = dojo.widget.defaultStrings[key];
|
|
}
|
|
}
|
|
// FIXME: this is a lot of string munging. Can we make it faster?
|
|
for(var i = 0; i < matches.length; i++) {
|
|
var key = matches[i];
|
|
key = key.substring(2, key.length-1);
|
|
var kval = (key.substring(0, 5) == "this.") ? dojo.lang.getObjPathValue(key.substring(5), this) : hash[key];
|
|
var value;
|
|
if((kval)||(dojo.lang.isString(kval))){
|
|
value = new String((dojo.lang.isFunction(kval)) ? kval.call(this, key, this.templateString) : kval);
|
|
// Safer substitution, see heading "Attribute values" in
|
|
// http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
|
|
while (value.indexOf("\"") > -1) {
|
|
value=value.replace("\"",""");
|
|
}
|
|
tstr = tstr.replace(matches[i], value);
|
|
}
|
|
}
|
|
}else{
|
|
// otherwise, we are required to instantiate a copy of the template
|
|
// string if one is provided.
|
|
|
|
// FIXME: need to be able to distinguish here what should be done
|
|
// or provide a generic interface across all DOM implementations
|
|
// FIMXE: this breaks if the template has whitespace as its first
|
|
// characters
|
|
// node = this.createNodesFromText(this.templateString, true);
|
|
// this.templateNode = node[0].cloneNode(true); // we're optimistic here
|
|
this.templateNode = this.createNodesFromText(this.templateString, true)[0];
|
|
if(!avoidCache){
|
|
ts.node = this.templateNode;
|
|
}
|
|
}
|
|
}
|
|
if((!this.templateNode)&&(!matches)){
|
|
dojo.debug("DomWidget.buildFromTemplate: could not create template");
|
|
return false;
|
|
}else if(!matches){
|
|
node = this.templateNode.cloneNode(true);
|
|
if(!node){ return false; }
|
|
}else{
|
|
node = this.createNodesFromText(tstr, true)[0];
|
|
}
|
|
|
|
// recurse through the node, looking for, and attaching to, our
|
|
// attachment points which should be defined on the template node.
|
|
|
|
this.domNode = node;
|
|
// dojo.profile.start("attachTemplateNodes");
|
|
this.attachTemplateNodes();
|
|
// dojo.profile.end("attachTemplateNodes");
|
|
|
|
// relocate source contents to templated container node
|
|
// this.containerNode must be able to receive children, or exceptions will be thrown
|
|
if (this.isContainer && this.containerNode){
|
|
var src = this.getFragNodeRef(frag);
|
|
if (src){
|
|
dojo.dom.moveChildren(src, this.containerNode);
|
|
}
|
|
}
|
|
},
|
|
|
|
attachTemplateNodes: function(baseNode, targetObj){
|
|
// summary:
|
|
// hooks up event handlers and property/node linkages. Calls
|
|
// dojo.widget.attachTemplateNodes to do all the hard work.
|
|
// baseNode: DomNode
|
|
// defaults to "this.domNode"
|
|
// targetObj: Widget
|
|
// defaults to "this"
|
|
if(!baseNode){ baseNode = this.domNode; }
|
|
if(!targetObj){ targetObj = this; }
|
|
return dojo.widget.attachTemplateNodes(baseNode, targetObj,
|
|
dojo.widget.getDojoEventsFromStr(this.templateString));
|
|
},
|
|
|
|
fillInTemplate: function(){
|
|
// summary:
|
|
// stub function! sub-classes may use as a default UI
|
|
// initializer function. The UI rendering will be available by
|
|
// the time this is called from buildRendering. If
|
|
// buildRendering is over-ridden, this function may not be
|
|
// fired!
|
|
|
|
// dojo.unimplemented("dojo.widget.DomWidget.fillInTemplate");
|
|
},
|
|
|
|
// method over-ride
|
|
destroyRendering: function(){
|
|
// summary: UI destructor. Destroy the dom nodes associated w/this widget.
|
|
try{
|
|
dojo.dom.destroyNode(this.domNode);
|
|
delete this.domNode;
|
|
}catch(e){ /* squelch! */ }
|
|
if(this._sourceNodeRef){
|
|
try{
|
|
dojo.dom.destroyNode(this._sourceNodeRef);
|
|
}catch(e){ /* squelch! */ }
|
|
}
|
|
},
|
|
|
|
createNodesFromText: function(){
|
|
// summary
|
|
// Attempts to create a set of nodes based on the structure of the passed text.
|
|
// Implemented in HtmlWidget and SvgWidget.
|
|
dojo.unimplemented("dojo.widget.DomWidget.createNodesFromText");
|
|
}
|
|
}
|
|
);
|