define([ "dojo/_base/kernel", // kernel.deprecated "dojo/_base/declare", // declare "dojo/_base/array", // array.indexOf "dojo/_base/lang", // lang.isArray, lang.isString "dojo/_base/event", // event.stop "dojo/query", // query() "dojo/dom-attr", // domAttr.set "dojo/text!./resources/TriStateCheckBox.html", "dijit/form/Button", "dijit/form/_ToggleButtonMixin", "dojo/NodeList-dom" // NodeList.addClass/removeClass ], function(kernel, declare, array, lang, event, query, domAttr, template, Button, _ToggleButtonMixin){ return declare("dojox.form.TriStateCheckBox", [Button, _ToggleButtonMixin], { // summary: // Checkbox with three states templateString: template, baseClass: "dojoxTriStateCheckBox", // type: [private] String // type attribute on `` node. // Overrides `dijit/form/Button.type`. Users should not change this value. type: "checkbox", // states: Array // States of TriStateCheckBox. // The value of This.checked should be one of these three states: // [false, true, "mixed"] states: "", // _stateLabels: Object // These characters are used to replace the image to show // current state of TriStateCheckBox in high contrast mode. This is an associate array of // states with their corresponding replacing characters. State can either be "False", "True" or "Mixed". _stateLabels: null, // stateValues: Object // The values of the TriStateCheckBox in corresponding states. This is an associate array of // states with their corresponding values. State can either be "False", "True" or "Mixed". stateValue: null, // _currentState: Integer // The current state of the TriStateCheckBox _currentState: 0, // _stateType: String // The current state type of the TriStateCheckBox // Could be "False", "True" or "Mixed" _stateType: "False", // readOnly: Boolean // Should this widget respond to user input? // In markup, this is specified as "readOnly". // Similar to disabled except readOnly form values are submitted. readOnly: false, // checked: Boolean|String // Current check state of the check box. checked: "", // aria-pressed for toggle buttons, and aria-checked for checkboxes _aria_attr: "aria-checked", constructor: function(){ // summary: // Runs on widget initialization to setup arrays etc. // tags: // private this.states = [false, "mixed", true]; this.checked = false; this._stateLabels = { "False": '□', "True": '√', "Mixed": '■' }; this.stateValues = { "False": false, "True": "on", "Mixed": "mixed" }; }, _fillContent: function(/*DomNode*/ source){ // Override Button::_fillContent() since it doesn't make sense for CheckBox, // since CheckBox doesn't even have a container }, postCreate: function(){ domAttr.set(this.stateLabelNode, 'innerHTML', this._stateLabels[this._stateType]); this.inherited(arguments); }, startup: function(){ this.set("checked", this.params.checked || this.states[this._currentState]); domAttr.set(this.stateLabelNode, 'innerHTML', this._stateLabels[this._stateType]); this.inherited(arguments); }, // Override behavior from Button, since we don't have an iconNode _setIconClassAttr: null, _setCheckedAttr: function(/*String|Boolean*/ checked, /*Boolean?*/ priorityChange){ // summary: // Handler for checked = attribute to constructor, and also calls to // set('checked', val). // checked: // true, false or 'mixed' // description: // Controls the state of the TriStateCheckBox. Set this.checked, // this._currentState, value attribute of the `` // according to the value of 'checked'. var stateIndex = array.indexOf(this.states, checked), changed = false; if(stateIndex >= 0){ this._currentState = stateIndex; this._stateType = this._getStateType(checked); domAttr.set(this.focusNode, "value", this.stateValues[this._stateType]); domAttr.set(this.stateLabelNode, 'innerHTML', this._stateLabels[this._stateType]); this.inherited(arguments); }else{ console.warn("Invalid state!"); } }, setChecked: function(/*String|Boolean*/ checked){ // summary: // Deprecated. Use set('checked', true/false) instead. kernel.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0"); this.set('checked', checked); }, _setStatesAttr: function(/*Array|String*/ states){ if(lang.isArray(states)){ this._set("states", states); }else if(lang.isString(states)){ var map = { "true": true, "false": false, "mixed": "mixed" }; states = states.split(/\s*,\s*/); for(var i = 0; i < states.length; i++){ states[i] = map[states[i]] !== undefined ? map[states[i]] : false; } this._set("states", states); } }, _setReadOnlyAttr: function(/*Boolean*/ value){ this._set("readOnly", value); domAttr.set(this.focusNode, "readOnly", value); }, _setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){ // summary: // Handler for value = attribute to constructor, and also calls to // set('value', val). // description: // During initialization, just saves as attribute to the ``. // // After initialization, // when passed a boolean or the string 'mixed', controls the state of the // TriStateCheckBox. // If passed a string except 'mixed', changes the value attribute of the // TriStateCheckBox. Sets the state of the TriStateCheckBox to checked. if(typeof newValue == "string" && (array.indexOf(this.states, newValue) < 0)){ if(newValue == ""){ newValue = "on"; } this.stateValues["True"] = newValue; newValue = true; } if(this._created){ this._currentState = array.indexOf(this.states, newValue); this.set('checked', newValue, priorityChange); domAttr.set(this.focusNode, "value", this.stateValues[this._stateType]); } }, _setValuesAttr: function(/*Array*/ newValues){ // summary: // Handler for values = attribute to constructor, and also calls to // set('values', val). // newValues: // If the length of newValues is 1, it will replace the value of // the TriStateCheckBox in true state. Otherwise, the values of // the TriStateCheckBox in true state and 'mixed' state will be // replaced by the first two values in newValues. // description: // Change the value of the TriStateCheckBox in 'mixed' and true states. this.stateValues["True"] = newValues[0] ? newValues[0] : this.stateValues["True"]; this.stateValues["Mixed"] = newValues[1] ? newValues[1] : this.stateValues["Mixed"]; }, _getValueAttr: function(){ // summary: // Hook so get('value') works. // description: // Returns value according to current state of the TriStateCheckBox. return this.stateValues[this._stateType]; }, reset: function(){ this._hasBeenBlurred = false; this.set("states", this.params.states || [false, "mixed", true]); this.stateValues = this.params.stateValues || { "False" : false, "True" : "on", "Mixed" : "mixed" }; this.set("values", this.params.values || []); this.set('checked', this.params.checked || this.states[0]); }, _onFocus: function(){ if(this.id){ query("label[for='"+this.id+"']").addClass("dijitFocusedLabel"); } this.inherited(arguments); }, _onBlur: function(){ if(this.id){ query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel"); } this.mouseFocus = false; this.inherited(arguments); }, _onClick: function(/*Event*/ e){ // summary: // Internal function to handle click actions - need to check // readOnly and disabled if(this.readOnly || this.disabled){ event.stop(e); return false; } this.click(); return this.onClick(e); // user click actions }, click: function(){ // summary: // Emulate a click on the check box, but will not trigger the // onClick method. if(this._currentState >= this.states.length - 1){ this._currentState = 0; }else{ if(this._currentState == -1){ this.fixState(); }else{ this._currentState++; } } var oldState = this._currentState; this.set("checked", this.states[this._currentState]); this._currentState = oldState; domAttr.set(this.stateLabelNode, 'innerHTML', this._stateLabels[this._stateType]); }, fixState: function(){ // summary: // Fix _currentState property if it's out of bound. this._currentState = this.states.length - 1; }, _getStateType: function(/*String|Boolean*/ state){ // summary: // Internal function to return the type of a certain state: // // - false: False // - true: True // - "mixed": Mixed return state ? (state == "mixed" ? "Mixed" : "True") : "False"; }, _onMouseDown: function(){ this.mouseFocus = true; } }); });