You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
372 lines
9.7 KiB
372 lines
9.7 KiB
define([ |
|
"dojo/_base/lang", |
|
"dojo/_base/array", |
|
"dojo/on", |
|
"dojo/dom", |
|
"dojo/dom-attr", |
|
"dojo/query", |
|
"./_Mixin", |
|
"dijit/form/_FormWidget", |
|
"dijit/_base/manager", |
|
"dojo/_base/declare" |
|
], function(lang, array, on, dom, domAttr, query, _Mixin, _FormWidget, manager, declare){ |
|
var fm = lang.getObject("dojox.form.manager", true), |
|
aa = fm.actionAdapter, |
|
keys = fm._keys, |
|
|
|
ce = fm.changeEvent = function(node){ |
|
// summary: |
|
// Function that returns a valid "onchange" event for a given form node. |
|
// node: Node |
|
// Form node. |
|
|
|
var eventName = "click"; |
|
switch(node.tagName.toLowerCase()){ |
|
case "textarea": |
|
eventName = "keyup"; |
|
break; |
|
case "select": |
|
eventName = "change"; |
|
break; |
|
case "input": |
|
switch(node.type.toLowerCase()){ |
|
case "text": |
|
case "password": |
|
eventName = "keyup"; |
|
break; |
|
} |
|
break; |
|
// button, input/button, input/checkbox, input/radio, |
|
// input/file, input/image, input/submit, input/reset |
|
// use "onclick" (the default) |
|
} |
|
return eventName; // String |
|
}, |
|
|
|
registerNode = function(node, groupNode){ |
|
var name = domAttr.get(node, "name"); |
|
groupNode = groupNode || this.domNode; |
|
if(name && !(name in this.formWidgets)){ |
|
// verify that it is not part of any widget |
|
for(var n = node; n && n !== groupNode; n = n.parentNode){ |
|
if(domAttr.get(n, "widgetId") && manager.byNode(n).isInstanceOf(_FormWidget)){ |
|
// this is a child of some widget --- bail out |
|
return null; |
|
} |
|
} |
|
// register the node |
|
if(node.tagName.toLowerCase() == "input" && node.type.toLowerCase() == "radio"){ |
|
var a = this.formNodes[name]; |
|
a = a && a.node; |
|
if(a && lang.isArray(a)){ |
|
a.push(node); |
|
}else{ |
|
this.formNodes[name] = {node: [node], connections: []}; |
|
} |
|
}else{ |
|
this.formNodes[name] = {node: node, connections: []}; |
|
} |
|
}else{ |
|
name = null; |
|
} |
|
return name; |
|
}, |
|
|
|
getObserversFromNode = function(name){ |
|
var observers = {}; |
|
aa(function(_, n){ |
|
var o = domAttr.get(n, "data-dojo-observer") || domAttr.get(n, "observer"); |
|
if(o && typeof o == "string"){ |
|
array.forEach(o.split(","), function(o){ |
|
o = lang.trim(o); |
|
if(o && lang.isFunction(this[o])){ |
|
observers[o] = 1; |
|
} |
|
}, this); |
|
} |
|
}).call(this, null, this.formNodes[name].node); |
|
return keys(observers); |
|
}, |
|
|
|
connectNode = function(name, observers){ |
|
var t = this.formNodes[name], c = t.connections; |
|
if(c.length){ |
|
array.forEach(c, function(item){ item.remove(); }); |
|
c = t.connections = []; |
|
} |
|
aa(function(_, n){ |
|
// the next line is a crude workaround for Button that fires onClick instead of onChange |
|
var eventName = ce(n); |
|
array.forEach(observers, function(o){ |
|
c.push(on(n, eventName, lang.hitch(this, function(evt){ |
|
if(this.watching){ |
|
this[o](this.formNodeValue(name), name, n, evt); |
|
} |
|
}))); |
|
}, this); |
|
}).call(this, null, t.node); |
|
}; |
|
|
|
return declare("dojox.form.manager._NodeMixin", null, { |
|
// summary: |
|
// Mixin to orchestrate dynamic forms (works with DOM nodes). |
|
// description: |
|
// This mixin provides a foundation for an enhanced form |
|
// functionality: unified access to individual form elements, |
|
// unified "change" event processing, and general event |
|
// processing. It complements dojox/form/manager/_Mixin |
|
// extending the functionality to DOM nodes. |
|
|
|
destroy: function(){ |
|
// summary: |
|
// Called when the widget is being destroyed |
|
|
|
for(var name in this.formNodes){ |
|
array.forEach(this.formNodes[name].connections, function(item){ |
|
item.remove(); |
|
}); |
|
} |
|
this.formNodes = {}; |
|
|
|
this.inherited(arguments); |
|
}, |
|
|
|
// register/unregister widgets and nodes |
|
|
|
registerNode: function(node){ |
|
// summary: |
|
// Register a node with the form manager |
|
// node: String|Node |
|
// A node, or its id |
|
// returns: Object |
|
// Returns self |
|
if(typeof node == "string"){ |
|
node = dom.byId(node); |
|
} |
|
var name = registerNode.call(this, node); |
|
if(name){ |
|
connectNode.call(this, name, getObserversFromNode.call(this, name)); |
|
} |
|
return this; |
|
}, |
|
|
|
unregisterNode: function(name){ |
|
// summary: |
|
// Removes the node by name from internal tables unregistering |
|
// connected observers |
|
// name: String |
|
// Name of the to unregister |
|
// returns: Object |
|
// Returns self |
|
if(name in this.formNodes){ |
|
array.forEach(this.formNodes[name].connections, function(item){ |
|
item.remove(); |
|
}); |
|
delete this.formNodes[name]; |
|
} |
|
return this; |
|
}, |
|
|
|
registerNodeDescendants: function(node){ |
|
// summary: |
|
// Register node's descendants (form nodes) with the form manager |
|
// node: String|Node |
|
// A widget, or its widgetId, or its DOM node |
|
// returns: Object |
|
// Returns self |
|
|
|
if(typeof node == "string"){ |
|
node = dom.byId(node); |
|
} |
|
|
|
query("input, select, textarea, button", node). |
|
map(function(n){ |
|
return registerNode.call(this, n, node); |
|
}, this). |
|
forEach(function(name){ |
|
if(name){ |
|
connectNode.call(this, name, getObserversFromNode.call(this, name)); |
|
} |
|
}, this); |
|
|
|
return this; |
|
}, |
|
|
|
unregisterNodeDescendants: function(node){ |
|
// summary: |
|
// Unregister node's descendants (form nodes) with the form manager |
|
// node: String|Node |
|
// A widget, or its widgetId, or its DOM node |
|
// returns: Object |
|
// Returns self |
|
|
|
if(typeof node == "string"){ |
|
node = dom.byId(node); |
|
} |
|
|
|
query("input, select, textarea, button", node). |
|
map(function(n){ return domAttr.get(node, "name") || null; }). |
|
forEach(function(name){ |
|
if(name){ |
|
this.unregisterNode(name); |
|
} |
|
}, this); |
|
|
|
return this; |
|
}, |
|
|
|
// value accessors |
|
|
|
formNodeValue: function(elem, value){ |
|
// summary: |
|
// Set or get a form element by name. |
|
// elem: String|Node|Array |
|
// Form element's name, DOM node, or array or radio nodes. |
|
// value: Object? |
|
// Optional. The value to set. |
|
// returns: Object |
|
// For a getter it returns the value, for a setter it returns |
|
// self. If the elem is not valid, null will be returned. |
|
|
|
var isSetter = arguments.length == 2 && value !== undefined, result; |
|
|
|
if(typeof elem == "string"){ |
|
elem = this.formNodes[elem]; |
|
if(elem){ |
|
elem = elem.node; |
|
} |
|
} |
|
|
|
if(!elem){ |
|
return null; // Object |
|
} |
|
|
|
if(lang.isArray(elem)){ |
|
// input/radio array |
|
if(isSetter){ |
|
array.forEach(elem, function(node){ |
|
node.checked = ""; |
|
}); |
|
array.forEach(elem, function(node){ |
|
node.checked = node.value === value ? "checked" : ""; |
|
}); |
|
return this; // self |
|
} |
|
// getter |
|
array.some(elem, function(node){ |
|
if(node.checked){ |
|
result = node; |
|
return true; |
|
} |
|
return false; |
|
}); |
|
return result ? result.value : ""; // String |
|
} |
|
// all other elements |
|
switch(elem.tagName.toLowerCase()){ |
|
case "select": |
|
if(elem.multiple){ |
|
// multiple is allowed |
|
if(isSetter){ |
|
if(lang.isArray(value)){ |
|
var dict = {}; |
|
array.forEach(value, function(v){ |
|
dict[v] = 1; |
|
}); |
|
query("> option", elem).forEach(function(opt){ |
|
opt.selected = opt.value in dict; |
|
}); |
|
return this; // self |
|
} |
|
// singular property |
|
query("> option", elem).forEach(function(opt){ |
|
opt.selected = opt.value === value; |
|
}); |
|
return this; // self |
|
} |
|
// getter |
|
result = query("> option", elem).filter(function(opt){ |
|
return opt.selected; |
|
}).map(function(opt){ |
|
return opt.value; |
|
}); |
|
return result.length == 1 ? result[0] : result; // Object |
|
} |
|
// singular |
|
if(isSetter){ |
|
query("> option", elem).forEach(function(opt){ |
|
opt.selected = opt.value === value; |
|
}); |
|
return this; // self |
|
} |
|
// getter |
|
return elem.value || ""; // String |
|
case "button": |
|
if(isSetter){ |
|
elem.innerHTML = "" + value; |
|
return this; |
|
} |
|
// getter |
|
return elem.innerHTML; |
|
case "input": |
|
if(elem.type.toLowerCase() == "checkbox"){ |
|
// input/checkbox element |
|
if(isSetter){ |
|
elem.checked = value ? "checked" : ""; |
|
return this; |
|
} |
|
// getter |
|
return Boolean(elem.checked); |
|
} |
|
} |
|
// the rest of inputs |
|
if(isSetter){ |
|
elem.value = "" + value; |
|
return this; |
|
} |
|
// getter |
|
return elem.value; |
|
}, |
|
|
|
// inspectors |
|
|
|
inspectFormNodes: function(inspector, state, defaultValue){ |
|
// summary: |
|
// Run an inspector function on controlled form elements returning a result object. |
|
// inspector: Function |
|
// A function to be called on a form element. Takes three arguments: a name, a node or |
|
// an array of nodes, and a supplied value. Runs in the context of the form manager. |
|
// Returns a value that will be collected and returned as a state. |
|
// state: Object? |
|
// Optional. If a name-value dictionary --- only listed names will be processed. |
|
// If an array, all names in the array will be processed with defaultValue. |
|
// If omitted or null, all form elements will be processed with defaultValue. |
|
// defaultValue: Object? |
|
// Optional. The default state (true, if omitted). |
|
|
|
var name, result = {}; |
|
|
|
if(state){ |
|
if(lang.isArray(state)){ |
|
array.forEach(state, function(name){ |
|
if(name in this.formNodes){ |
|
result[name] = inspector.call(this, name, this.formNodes[name].node, defaultValue); |
|
} |
|
}, this); |
|
}else{ |
|
for(name in state){ |
|
if(name in this.formNodes){ |
|
result[name] = inspector.call(this, name, this.formNodes[name].node, state[name]); |
|
} |
|
} |
|
} |
|
}else{ |
|
for(name in this.formNodes){ |
|
result[name] = inspector.call(this, name, this.formNodes[name].node, defaultValue); |
|
} |
|
} |
|
|
|
return result; // Object |
|
} |
|
}); |
|
});
|
|
|