/* Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ if(!dojo._hasResource["dojox.form.manager._Mixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["dojox.form.manager._Mixin"] = true; dojo.provide("dojox.form.manager._Mixin"); dojo.require("dijit._Widget"); (function(){ var fm = dojox.form.manager, aa = fm.actionAdapter = function(action){ // summary: // Adapter that automates application of actions to arrays. // action: Function: // Function that takes three parameters: a name, an object // (usually node or widget), and a value. This action will // be applied to all elements of array. return function(name, elems, value){ if(dojo.isArray(elems)){ dojo.forEach(elems, function(elem){ action.call(this, name, elem, value); }, this); }else{ action.apply(this, arguments); } }; }, ia = fm.inspectorAdapter = function(inspector){ // summary: // Adapter that applies an inspector only to the first item of the array. // inspector: Function: // Function that takes three parameters: a name, an object // (usually node or widget), and a value. return function(name, elem, value){ return inspector.call(this, name, dojo.isArray(elem) ? elem[0] : elem, value); }; }, skipNames = {domNode: 1, containerNode: 1, srcNodeRef: 1, bgIframe: 1}, keys = fm._keys = function(o){ // similar to dojox.lang.functional.keys var list = [], key; for(key in o){ if(o.hasOwnProperty(key)){ list.push(key); } } return list; }, registerWidget = function(widget){ var name = widget.get("name"); if(name && widget instanceof dijit.form._FormWidget){ if(name in this.formWidgets){ var a = this.formWidgets[name].widget; if(dojo.isArray(a)){ a.push(widget); }else{ this.formWidgets[name].widget = [a, widget]; } }else{ this.formWidgets[name] = {widget: widget, connections: []}; } }else{ name = null; } return name; }, getObserversFromWidget = function(name){ var observers = {}; aa(function(_, w){ var o = w.get("observer"); if(o && typeof o == "string"){ dojo.forEach(o.split(","), function(o){ o = dojo.trim(o); if(o && dojo.isFunction(this[o])){ observers[o] = 1; } }, this); } }).call(this, null, this.formWidgets[name].widget); return keys(observers); }, connectWidget = function(name, observers){ var t = this.formWidgets[name], w = t.widget, c = t.connections; if(c.length){ dojo.forEach(c, dojo.disconnect); c = t.connections = []; } if(dojo.isArray(w)){ // radio buttons dojo.forEach(w, function(w){ dojo.forEach(observers, function(o){ c.push(dojo.connect(w, "onChange", this, function(evt){ // TODO: for some reason for radio button widgets // w.checked != w.focusNode.checked when value changes. // We test the underlying value to be 100% sure. if(this.watching && dojo.attr(w.focusNode, "checked")){ this[o](w.get("value"), name, w, evt); } })); }, this); }, this); }else{ // the rest // the next line is a crude workaround for dijit.form.Button that fires onClick instead of onChange var eventName = w.declaredClass == "dijit.form.Button" ? "onClick" : "onChange"; dojo.forEach(observers, function(o){ c.push(dojo.connect(w, eventName, this, function(evt){ if(this.watching){ this[o](w.get("value"), name, w, evt); } })); }, this); } }; dojo.declare("dojox.form.manager._Mixin", null, { // summary: // Mixin to orchestrate dynamic forms. // description: // This mixin provideas a foundation for an enhanced form // functionality: unified access to individual form elements, // unified "onchange" event processing, general event // processing, I/O orchestration, and common form-related // functionality. See additional mixins in dojox.form.manager // namespace. watching: true, startup: function(){ // summary: // Called after all the widgets have been instantiated and their // dom nodes have been inserted somewhere under dojo.doc.body. if(this._started){ return; } this.formWidgets = {}; this.formNodes = {}; this.registerWidgetDescendants(this); this.inherited(arguments); }, destroy: function(){ // summary: // Called when the widget is being destroyed for(var name in this.formWidgets){ dojo.forEach(this.formWidgets[name].connections, dojo.disconnect); } this.formWidgets = {}; this.inherited(arguments); }, // register/unregister widgets and nodes registerWidget: function(widget){ // summary: // Register a widget with the form manager // widget: String|Node|dijit.form._FormWidget: // A widget, or its widgetId, or its DOM node // returns: Object: // Returns self if(typeof widget == "string"){ widget = dijit.byId(widget); }else if(widget.tagName && widget.cloneNode){ widget = dijit.byNode(widget); } var name = registerWidget.call(this, widget); if(name){ connectWidget.call(this, name, getObserversFromWidget.call(this, name)); } return this; }, unregisterWidget: function(name){ // summary: // Removes the widget by name from internal tables unregistering // connected observers // name: String: // Name of the to unregister // returns: Object: // Returns self if(name in this.formWidgets){ dojo.forEach(this.formWidgets[name].connections, this.disconnect, this); delete this.formWidgets[name]; } return this; }, registerWidgetDescendants: function(widget){ // summary: // Register widget's descendants with the form manager // widget: String|Node|dijit._Widget: // A widget, or its widgetId, or its DOM node // returns: Object: // Returns self // convert to widget, if required if(typeof widget == "string"){ widget = dijit.byId(widget); }else if(widget.tagName && widget.cloneNode){ widget = dijit.byNode(widget); } // build the map of widgets var widgets = dojo.map(widget.getDescendants(), registerWidget, this); // process observers for widgets dojo.forEach(widgets, function(name){ if(name){ connectWidget.call(this, name, getObserversFromWidget.call(this, name)); } }, this); // do the same with nodes, if available return this.registerNodeDescendants ? this.registerNodeDescendants(widget.domNode) : this; }, unregisterWidgetDescendants: function(widget){ // summary: // Unregister widget's descendants with the form manager // widget: String|Node|dijit._Widget: // A widget, or its widgetId, or its DOM node // returns: Object: // Returns self // convert to widget, if required if(typeof widget == "string"){ widget = dijit.byId(widget); }else if(widget.tagName && widget.cloneNode){ widget = dijit.byNode(widget); } // unregister widgets by names dojo.forEach( dojo.map( widget.getDescendants(), function(w){ return w instanceof dijit.form._FormWidget && w.get("name") || null; } ), function(name){ if(name){ this.unregisterNode(name); } }, this ); // do the same with nodes, if available return this.unregisterNodeDescendants ? this.unregisterNodeDescendants(widget.domNode) : this; }, // value accessors formWidgetValue: function(elem, value){ // summary: // Set or get a form widget by name. // elem: String|Object|Array: // Form element's name, widget object, or array or radio widgets. // 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.formWidgets[elem]; if(elem){ elem = elem.widget; } } if(!elem){ return null; // Object } if(dojo.isArray(elem)){ // input/radio array of widgets if(isSetter){ dojo.forEach(elem, function(widget){ widget.set("checked", false, !this.watching); }, this); dojo.forEach(elem, function(widget){ widget.set("checked", widget.value === value, !this.watching); }, this); return this; // self } // getter dojo.some(elem, function(widget){ // TODO: for some reason for radio button widgets // w.checked != w.focusNode.checked when value changes. // We test the underlying value to be 100% sure. if(dojo.attr(widget.focusNode, "checked")){ //if(widget.get("checked")){ result = widget; return true; } return false; }); return result ? result.get("value") : ""; // String } // checkbox widget is a special case :-( if(elem.declaredClass == "dijit.form.CheckBox"){ if(isSetter){ elem.set("value", Boolean(value), !this.watching); return this; // self } return Boolean(elem.get("value")); // Object } // all other elements if(isSetter){ elem.set("value", value, !this.watching); return this; // self } return elem.get("value"); // Object }, formPointValue: function(elem, value){ // summary: // Set or get a node context by name (using dojoAttachPoint). // elem: String|Object|Array: // A node. // 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. if(elem && typeof elem == "string"){ elem = this[elem]; } if(!elem || !elem.tagName || !elem.cloneNode){ return null; // Object } if(!dojo.hasClass(elem, "dojoFormValue")){ // accessing the value of the attached point not marked with CSS class 'dojoFormValue' return null; } if(arguments.length == 2 && value !== undefined){ // setter elem.innerHTML = value; return this; // self } // getter return elem.innerHTML; // String }, // inspectors inspectFormWidgets: function(inspector, state, defaultValue){ // summary: // Run an inspector function on controlled widgets returning a result object. // inspector: Function: // A function to be called on a widget. Takes three arguments: a name, a widget object // or an array of widget objects, 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 widgets will be processed with defaultValue. // defaultValue: Object?: // Optional. The default state (true, if omitted). var name, result = {}; if(state){ if(dojo.isArray(state)){ dojo.forEach(state, function(name){ if(name in this.formWidgets){ result[name] = inspector.call(this, name, this.formWidgets[name].widget, defaultValue); } }, this); }else{ for(name in state){ if(name in this.formWidgets){ result[name] = inspector.call(this, name, this.formWidgets[name].widget, state[name]); } } } }else{ for(name in this.formWidgets){ result[name] = inspector.call(this, name, this.formWidgets[name].widget, defaultValue); } } return result; // Object }, inspectAttachedPoints: function(inspector, state, defaultValue){ // summary: // Run an inspector function on "dojoAttachPoint" nodes returning a result object. // inspector: Function: // A function to be called on a node. 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 attached point nodes will be processed with defaultValue. // defaultValue: Object?: // Optional. The default state (true, if omitted). var name, result = {}; if(state){ if(dojo.isArray(state)){ dojo.forEach(state, function(name){ var elem = this[name]; if(elem && elem.tagName && elem.cloneNode){ result[name] = inspector.call(this, name, elem, defaultValue); } }, this); }else{ for(name in state){ var elem = this[name]; if(elem && elem.tagName && elem.cloneNode){ result[name] = inspector.call(this, name, elem, state[name]); } } } }else{ for(name in this){ if(!(name in skipNames)){ var elem = this[name]; if(elem && elem.tagName && elem.cloneNode){ result[name] = inspector.call(this, name, elem, defaultValue); } } } } return result; // Object }, inspect: function(inspector, state, defaultValue){ // summary: // Run an inspector function on controlled elements returning a result object. // inspector: Function: // A function to be called on a widget, form element, and an attached node. // Takes three arguments: a name, a node (domNode in the case of widget) or // an array of such objects, 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 controlled elements will be processed with defaultValue. // defaultValue: Object?: // Optional. The default state (true, if omitted). var result = this.inspectFormWidgets(function(name, widget, value){ if(dojo.isArray(widget)){ return inspector.call(this, name, dojo.map(widget, function(w){ return w.domNode; }), value); } return inspector.call(this, name, widget.domNode, value); }, state, defaultValue); if(this.inspectFormNodes){ dojo.mixin(result, this.inspectFormNodes(inspector, state, defaultValue)); } return dojo.mixin(result, this.inspectAttachedPoints(inspector, state, defaultValue)); // Object } }); })(); // These arguments can be specified for widgets which are used in forms. // Since any widget can be specified as sub widgets, mix it into the base // widget class. (This is a hack, but it's effective.) dojo.extend(dijit._Widget, { observer: "" }); }