define("dojox/grid/enhanced/plugins/filter/FilterDefDialog", [ "dojo/_base/declare", "dojo/_base/array", "dojo/_base/connect", "dojo/_base/lang", "dojo/_base/event", "dojo/_base/html", "dojo/_base/sniff", "dojo/cache", "dojo/keys", "dojo/string", "dojo/window", "dojo/date/locale", "./FilterBuilder", "../Dialog", "dijit/form/ComboBox", "dijit/form/TextBox", "dijit/form/NumberTextBox", "dijit/form/DateTextBox", "dijit/form/TimeTextBox", "dijit/form/Button", "dijit/layout/AccordionContainer", "dijit/layout/ContentPane", "dijit/_Widget", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", "dijit/focus", "dojox/html/metrics", "dijit/a11y", "dijit/Tooltip", "dijit/form/Select", "dijit/form/RadioButton", "dojox/html/ellipsis", "../../../cells/dijit" ], function(declare, array, connect, lang, event, html, has, cache, keys, string, win, dateLocale, FilterBuilder, Dialog, ComboBox, TextBox, NumberTextBox, DateTextBox, TimeTextBox, Button, AccordionContainer, ContentPane, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, dijitFocus, metrics, dijitA11y){ var _tabIdxes = { // summary: // Define tabindexes for elements in the filter definition dialog relSelect: 60, accordionTitle: 70, removeCBoxBtn: -1, colSelect: 90, condSelect: 95, valueBox: 10, addCBoxBtn: 20, filterBtn: 30, clearBtn: 40, cancelBtn: 50 }; var FilterAccordionContainer = declare("dojox.grid.enhanced.plugins.filter.AccordionContainer", AccordionContainer, { nls: null, addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ var pane = arguments[0] = child._pane = new ContentPane({ content: child }); this.inherited(arguments); this._modifyChild(pane); }, removeChild: function(child){ var pane = child, isRemoveByUser = false; if(child._pane){ isRemoveByUser = true; pane = arguments[0] = child._pane; } this.inherited(arguments); if(isRemoveByUser){ this._hackHeight(false, this._titleHeight); var children = this.getChildren(); if(children.length === 1){ html.style(children[0]._removeCBoxBtn.domNode, "display", "none"); } } pane.destroyRecursive(); }, selectChild: function(child){ if(child._pane){ arguments[0] = child._pane; } this.inherited(arguments); }, resize: function(){ this.inherited(arguments); array.forEach(this.getChildren(), this._setupTitleDom); }, startup: function(){ if(this._started){ return; } this.inherited(arguments); if(parseInt(has('ie'), 10) == 7){ //IE7 will fire a lot of "onresize" event during initialization. array.some(this._connects, function(cnnt){ if((cnnt[0] || {})[1] == "onresize"){ this.disconnect(cnnt); return true; } }, this); } array.forEach(this.getChildren(), function(child){ this._modifyChild(child, true); }, this); }, _onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){ // summary: // Overrides base class method, make left/right button do other things. if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){ return; } var k = keys, c = e.charOrCode, ltr = html._isBodyLtr(), toNext = null; if((fromTitle && c == k.UP_ARROW) || (e.ctrlKey && c == k.PAGE_UP)){ toNext = false; }else if((fromTitle && c == k.DOWN_ARROW) || (e.ctrlKey && (c == k.PAGE_DOWN || c == k.TAB))){ toNext = true; }else if(c == (ltr ? k.LEFT_ARROW : k.RIGHT_ARROW)){ toNext = this._focusOnRemoveBtn ? null : false; this._focusOnRemoveBtn = !this._focusOnRemoveBtn; }else if(c == (ltr ? k.RIGHT_ARROW : k.LEFT_ARROW)){ toNext = this._focusOnRemoveBtn ? true : null; this._focusOnRemoveBtn = !this._focusOnRemoveBtn; }else{ return; } if(toNext !== null){ this._adjacent(toNext)._buttonWidget._onTitleClick(); } event.stop(e); win.scrollIntoView(this.selectedChildWidget._buttonWidget.domNode.parentNode); if(has('ie')){ //IE will not show focus indicator if tabIndex is -1 this.selectedChildWidget._removeCBoxBtn.focusNode.setAttribute("tabIndex", this._focusOnRemoveBtn ? _tabIdxes.accordionTitle : -1); } dijitFocus.focus(this.selectedChildWidget[this._focusOnRemoveBtn ? "_removeCBoxBtn" : "_buttonWidget"].focusNode); }, _modifyChild: function(child, isFirst){ if(!child || !this._started){ return; } html.style(child.domNode, "overflow", "hidden"); child._buttonWidget.connect(child._buttonWidget, "_setSelectedAttr", function(){ this.focusNode.setAttribute("tabIndex", this.selected ? _tabIdxes.accordionTitle : "-1"); }); var _this = this; child._buttonWidget.connect(child._buttonWidget.domNode, "onclick", function(){ _this._focusOnRemoveBtn = false; }); (child._removeCBoxBtn = new Button({ label: this.nls.removeRuleButton, showLabel: false, iconClass: "dojoxGridFCBoxRemoveCBoxBtnIcon", tabIndex: _tabIdxes.removeCBoxBtn, onClick: lang.hitch(child.content, "onRemove"), onKeyPress: function(e){ _this._onKeyPress(e, child._buttonWidget.contentWidget); } })).placeAt(child._buttonWidget.domNode); var i, children = this.getChildren(); if(children.length === 1){ child._buttonWidget.set("selected", true); html.style(child._removeCBoxBtn.domNode, "display", "none"); }else{ for(i = 0; i < children.length; ++i){ if(children[i]._removeCBoxBtn){ html.style(children[i]._removeCBoxBtn.domNode, "display", ""); } } } this._setupTitleDom(child); if(!this._titleHeight){ for(i = 0; i < children.length; ++i){ if(children[i] != this.selectedChildWidget){ this._titleHeight = html.marginBox(children[i]._buttonWidget.domNode.parentNode).h; break; } } } if(!isFirst){ this._hackHeight(true, this._titleHeight); } }, _hackHeight: function(/* bool */toGrow,/* int */heightDif){ var children = this.getChildren(), dn = this.domNode, h = html.style(dn, "height"); if(!toGrow){ dn.style.height = (h - heightDif) + 'px'; }else if(children.length > 1){ dn.style.height = (h + heightDif) + 'px'; }else{ //Only one rule, no need to do anything. return; } this.resize(); }, _setupTitleDom: function(child){ var w = html.contentBox(child._buttonWidget.titleNode).w; if(has('ie') < 8){ w -= 8; } html.style(child._buttonWidget.titleTextNode, "width", w + "px"); } }); var FilterDefPane = declare("dojox.grid.enhanced.plugins.filter.FilterDefPane",[_Widget, _TemplatedMixin, _WidgetsInTemplateMixin],{ templateString: cache("dojox.grid","enhanced/templates/FilterDefPane.html"), widgetsInTemplate: true, dlg: null, postMixInProperties: function(){ this.plugin = this.dlg.plugin; var nls = this.plugin.nls; this._addRuleBtnLabel = nls.addRuleButton; this._cancelBtnLabel = nls.cancelButton; this._clearBtnLabel = nls.clearButton; this._filterBtnLabel = nls.filterButton; this._relAll = nls.relationAll; this._relAny = nls.relationAny; this._relMsgFront = nls.relationMsgFront; this._relMsgTail = nls.relationMsgTail; }, postCreate: function(){ this.inherited(arguments); this.connect(this.domNode, "onkeypress", "_onKey"); (this.cboxContainer = new FilterAccordionContainer({ nls: this.plugin.nls })).placeAt(this.criteriaPane); this._relSelect.set("tabIndex", _tabIdxes.relSelect); this._addCBoxBtn.set("tabIndex", _tabIdxes.addCBoxBtn); this._cancelBtn.set("tabIndex", _tabIdxes.cancelBtn); this._clearFilterBtn.set("tabIndex", _tabIdxes.clearBtn); this._filterBtn.set("tabIndex", _tabIdxes.filterBtn); var nls = this.plugin.nls; this._relSelect.domNode.setAttribute("aria-label", nls.waiRelAll); this._addCBoxBtn.domNode.setAttribute("aria-label", nls.waiAddRuleButton); this._cancelBtn.domNode.setAttribute("aria-label", nls.waiCancelButton); this._clearFilterBtn.domNode.setAttribute("aria-label", nls.waiClearButton); this._filterBtn.domNode.setAttribute("aria-label", nls.waiFilterButton); this._relSelect.set("value", this.dlg._relOpCls === "logicall" ? "0" : "1"); }, uninitialize: function(){ this.cboxContainer.destroyRecursive(); this.plugin = null; this.dlg = null; }, _onRelSelectChange: function(val){ this.dlg._relOpCls = val == "0" ? "logicall" : "logicany"; this._relSelect.domNode.setAttribute("aria-label", this.plugin.nls[val == "0" ? "waiRelAll" : "waiRelAny"]); }, _onAddCBox: function(){ this.dlg.addCriteriaBoxes(1); }, _onCancel: function(){ this.dlg.onCancel(); }, _onClearFilter: function(){ this.dlg.onClearFilter(); }, _onFilter: function(){ this.dlg.onFilter(); }, _onKey: function(e){ if(e.keyCode == keys.ENTER){ this.dlg.onFilter(); } } }); var CriteriaBox = declare("dojox.grid.enhanced.plugins.filter.CriteriaBox",[_Widget, _TemplatedMixin, _WidgetsInTemplateMixin],{ templateString: cache("dojox.grid","enhanced/templates/CriteriaBox.html"), widgetsInTemplate: true, dlg: null, postMixInProperties: function(){ this.plugin = this.dlg.plugin; this._curValueBox = null; var nls = this.plugin.nls; this._colSelectLabel = nls.columnSelectLabel; this._condSelectLabel = nls.conditionSelectLabel; this._valueBoxLabel = nls.valueBoxLabel; this._anyColumnOption = nls.anyColumnOption; }, postCreate: function(){ var dlg = this.dlg, g = this.plugin.grid; //Select Column this._colSelect.set("tabIndex", _tabIdxes.colSelect); this._colOptions = this._getColumnOptions(); this._colSelect.addOption([ {label: this.plugin.nls.anyColumnOption, value: "anycolumn", selected: dlg.curColIdx < 0}, {value: ""} ].concat(this._colOptions)); //Select Condition this._condSelect.set("tabIndex", _tabIdxes.condSelect); this._condSelect.addOption(this._getUsableConditions(dlg.getColumnType(dlg.curColIdx))); this._showSelectOrLabel(this._condSelect, this._condSelectAlt); this.connect(g.layout, "moveColumn", "onMoveColumn"); var _this = this; setTimeout(function(){ var type = dlg.getColumnType(dlg.curColIdx); _this._setValueBoxByType(type); }, 0); }, _getColumnOptions: function(){ var colIdx = this.dlg.curColIdx >= 0 ? String(this.dlg.curColIdx) : "anycolumn"; return array.map(array.filter(this.plugin.grid.layout.cells, function(cell){ return !(cell.filterable === false || cell.hidden); }), function(cell){ return { label: cell.name || cell.field, value: String(cell.index), selected: colIdx == String(cell.index) }; }); }, onMoveColumn: function(){ var tmp = this._onChangeColumn; this._onChangeColumn = function(){}; var option = this._colSelect.get("selectedOptions"); this._colSelect.removeOption(this._colOptions); this._colOptions = this._getColumnOptions(); this._colSelect.addOption(this._colOptions); var i = 0; for(; i < this._colOptions.length; ++i){ if(this._colOptions[i].label == option.label){ break; } } if(i < this._colOptions.length){ this._colSelect.set("value", this._colOptions[i].value); } var _this = this; setTimeout(function(){ _this._onChangeColumn = tmp; }, 0); }, onRemove: function(){ this.dlg.removeCriteriaBoxes(this); }, uninitialize: function(){ if(this._curValueBox){ this._curValueBox.destroyRecursive(); this._curValueBox = null; } this.plugin = null; this.dlg = null; }, _showSelectOrLabel: function(sel, alt){ var options = sel.getOptions(); if(options.length == 1){ alt.innerHTML = options[0].label; html.style(sel.domNode, "display", "none"); html.style(alt, "display", ""); }else{ html.style(sel.domNode, "display", ""); html.style(alt, "display", "none"); } }, _onChangeColumn: function(val){ this._checkValidCriteria(); var type = this.dlg.getColumnType(val); this._setConditionsByType(type); this._setValueBoxByType(type); this._updateValueBox(); }, _onChangeCondition: function(val){ this._checkValidCriteria(); var f = (val == "range"); if(f ^ this._isRange){ this._isRange = f; this._setValueBoxByType(this.dlg.getColumnType(this._colSelect.get("value"))); } this._updateValueBox(); }, _updateValueBox: function(cond){ this._curValueBox.set("disabled", this._condSelect.get("value") == "isempty"); }, _checkValidCriteria: function(){ // summary: // Check whether the given criteria box is completed. If it is, mark it. setTimeout(lang.hitch(this, function(){ this.updateRuleTitle(); this.dlg._updatePane(); }),0); }, _createValueBox: function(/* widget constructor */cls,/* object */arg){ // summary: // Create a value input box with given class and arguments var func = lang.hitch(arg.cbox, "_checkValidCriteria"); return new cls(lang.mixin(arg,{ tabIndex: _tabIdxes.valueBox, onKeyPress: func, onChange: func, "class": "dojoxGridFCBoxValueBox" })); }, _createRangeBox: function(/* widget constructor */cls,/* object */arg){ // summary: // Create a DIV containing 2 input widgets, which represents a range, with the given class and arguments var func = lang.hitch(arg.cbox, "_checkValidCriteria"); lang.mixin(arg,{ tabIndex: _tabIdxes.valueBox, onKeyPress: func, onChange: func }); var div = html.create("div", {"class": "dojoxGridFCBoxValueBox"}), start = new cls(arg), txt = html.create("span", {"class": "dojoxGridFCBoxRangeValueTxt", "innerHTML": this.plugin.nls.rangeTo}), end = new cls(arg); html.addClass(start.domNode, "dojoxGridFCBoxStartValue"); html.addClass(end.domNode, "dojoxGridFCBoxEndValue"); div.appendChild(start.domNode); div.appendChild(txt); div.appendChild(end.domNode); div.domNode = div; //Mock functions for set and get (in place of the old attr function) div.set = function(dummy, args){ if(lang.isObject(args)){ start.set("value", args.start); end.set("value", args.end); } }; div.get = function(){ var s = start.get("value"), e = end.get("value"); return s && e ? {start: s, end: e} : ""; }; return div; }, changeCurrentColumn: function(/* bool */selectCurCol){ var colIdx = this.dlg.curColIdx; //Re-populate the columns in case some of them are set to hidden. this._colSelect.removeOption(this._colOptions); this._colOptions = this._getColumnOptions(); this._colSelect.addOption(this._colOptions); this._colSelect.set('value', colIdx >= 0 ? String(colIdx) : "anycolumn"); this.updateRuleTitle(true); }, curColumn: function(){ return this._colSelect.getOptions(this._colSelect.get("value")).label; }, curCondition: function(){ return this._condSelect.getOptions(this._condSelect.get("value")).label; }, curValue: function(){ var cond = this._condSelect.get("value"); if(cond == "isempty"){return "";} return this._curValueBox ? this._curValueBox.get("value") : ""; }, save: function(){ if(this.isEmpty()){ return null; } var colIdx = this._colSelect.get("value"), type = this.dlg.getColumnType(colIdx), value = this.curValue(), cond = this._condSelect.get("value"); return { "column": colIdx, "condition": cond, "value": value, "formattedVal": this.formatValue(type, cond, value), "type": type, "colTxt": this.curColumn(), "condTxt": this.curCondition() }; }, load: function(obj){ var tmp = [ this._onChangeColumn, this._onChangeCondition ]; this._onChangeColumn = this._onChangeCondition = function(){}; if(obj.column){ this._colSelect.set("value", obj.column); } if(obj.type){ this._setConditionsByType(obj.type); this._setValueBoxByType(obj.type); }else{ obj.type = this.dlg.getColumnType(this._colSelect.get("value")); } if(obj.condition){ this._condSelect.set("value", obj.condition); } var value = obj.value || ""; if(value || (obj.type != "date" && obj.type != "time")){ this._curValueBox.set("value", value); } this._updateValueBox(); setTimeout(lang.hitch(this, function(){ this._onChangeColumn = tmp[0]; this._onChangeCondition = tmp[1]; }), 0); }, getExpr: function(){ if(this.isEmpty()){ return null; } var colval = this._colSelect.get("value"); return this.dlg.getExprForCriteria({ "type": this.dlg.getColumnType(colval), "column": colval, "condition": this._condSelect.get("value"), "value": this.curValue() }); }, isEmpty: function(){ var cond = this._condSelect.get("value"); if(cond == "isempty"){return false;} var v = this.curValue(); return v === "" || v === null || typeof v == "undefined" || (typeof v == "number" && isNaN(v)); }, updateRuleTitle: function(isEmpty){ var node = this._pane._buttonWidget.titleTextNode; var title = [ "
" ]; if(isEmpty || this.isEmpty()){ node.title = string.substitute(this.plugin.nls.ruleTitleTemplate, [this._ruleIndex || 1]); title.push(node.title); }else{ var type = this.dlg.getColumnType(this._colSelect.get("value")); var column = this.curColumn(); var condition = this.curCondition(); var value = this.formatValue(type, this._condSelect.get("value"), this.curValue()); title.push( column, " ", condition, " ", value ); node.title = [column, " ", condition, " ", value].join(''); } node.innerHTML = title.join(''); if(has('mozilla')){ var tt = html.create("div", { "style": "width: 100%; height: 100%; position: absolute; top: 0; left: 0; z-index: 9999;" }, node); tt.title = node.title; } }, updateRuleIndex: function(index){ if(this._ruleIndex != index){ this._ruleIndex = index; if(this.isEmpty()){ this.updateRuleTitle(); } } }, setAriaInfo: function(idx){ var dss = string.substitute, nls = this.plugin.nls; this._colSelect.domNode.setAttribute("aria-label", dss(nls.waiColumnSelectTemplate, [idx])); this._condSelect.domNode.setAttribute("aria-label", dss(nls.waiConditionSelectTemplate, [idx])); this._pane._removeCBoxBtn.domNode.setAttribute("aria-label", dss(nls.waiRemoveRuleButtonTemplate, [idx])); this._index = idx; }, _getUsableConditions: function(type){ var conditions = lang.clone(this.dlg._dataTypeMap[type].conditions); var typeDisabledConds = (this.plugin.args.disabledConditions || {})[type]; var colIdx = parseInt(this._colSelect.get("value"), 10); var colDisabledConds = isNaN(colIdx) ? (this.plugin.args.disabledConditions || {})["anycolumn"] : this.plugin.grid.layout.cells[colIdx].disabledConditions; if(!lang.isArray(typeDisabledConds)){ typeDisabledConds = []; } if(!lang.isArray(colDisabledConds)){ colDisabledConds = []; } var arr = typeDisabledConds.concat(colDisabledConds); if(arr.length){ var disabledConds = {}; array.forEach(arr, function(c){ if(lang.isString(c)){ disabledConds[c.toLowerCase()] = true; } }); return array.filter(conditions, function(condOption){ return !(condOption.value in disabledConds); }); } return conditions; }, _setConditionsByType: function(/* string */type){ var condSelect = this._condSelect; condSelect.removeOption(condSelect.options); condSelect.addOption(this._getUsableConditions(type)); this._showSelectOrLabel(this._condSelect, this._condSelectAlt); }, _setValueBoxByType: function(/* string */type){ if(this._curValueBox){ this.valueNode.removeChild(this._curValueBox.domNode); try{ this._curValueBox.destroyRecursive(); }catch(e){} delete this._curValueBox; } //value box class var vbcls = this.dlg._dataTypeMap[type].valueBoxCls[this._getValueBoxClsInfo(this._colSelect.get("value"), type)], vboxArg = this._getValueBoxArgByType(type); this._curValueBox = this[this._isRange ? "_createRangeBox" : "_createValueBox"](vbcls, vboxArg); this.valueNode.appendChild(this._curValueBox.domNode); //Can not move to setAriaInfo, 'cause the value box is created after the defpane is loaded. this._curValueBox.domNode.setAttribute("aria-label", string.substitute(this.plugin.nls.waiValueBoxTemplate,[this._index])); //Now our cbox is completely ready this.dlg.onRendered(this); }, //--------------------------UI Configuration-------------------------------------- _getValueBoxArgByType: function(/* string */type){ // summary: // Get the arguments for the value box construction. var g = this.plugin.grid, cell = g.layout.cells[parseInt(this._colSelect.get("value"), 10)], res = { cbox: this }; if(type == "string"){ if(cell && (cell.suggestion || cell.autoComplete)){ lang.mixin(res, { store: g.store, searchAttr: cell.field || cell.name, query: g.query || {}, fetchProperties: { sort: [{"attribute": cell.field || cell.name}], queryOptions: lang.mixin({ ignoreCase: true, deep: true }, g.queryOptions || {}) } }); } }else if(type == "boolean"){ lang.mixin(res, this.dlg.builder.defaultArgs["boolean"]); } if(cell && cell.dataTypeArgs){ lang.mixin(res, cell.dataTypeArgs); } return res; }, formatValue: function(type, cond, v){ // summary: // Format the value to be shown in tooltip. if(cond == "isempty"){return "";} if(type == "date" || type == "time"){ var opt = {selector: type}, fmt = dateLocale.format; if(cond == "range"){ return string.substitute(this.plugin.nls.rangeTemplate, [fmt(v.start, opt), fmt(v.end, opt)]); } return fmt(v, opt); }else if(type == "boolean"){ return v ? this._curValueBox._lblTrue : this._curValueBox._lblFalse; } return v; }, _getValueBoxClsInfo: function(/* int|string */colIndex, /* string */type){ // summary: // Decide which value box to use given data type and column index. var cell = this.plugin.grid.layout.cells[parseInt(colIndex, 10)]; //Now we only need to handle string. But maybe we need to handle more types here in the future. if(type == "string"){ return (cell && (cell.suggestion || cell.autoComplete)) ? "ac" : "dft"; } return "dft"; } }); var UniqueComboBox = declare("dojox.grid.enhanced.plugins.filter.UniqueComboBox", ComboBox, { _openResultList: function(results){ var cache = {}, s = this.store, colName = this.searchAttr; arguments[0] = array.filter(results, function(item){ var key = s.getValue(item, colName), existed = cache[key]; cache[key] = true; return !existed; }); this.inherited(arguments); }, _onKey: function(evt){ if(evt.charOrCode === keys.ENTER && this._opened){ event.stop(evt); } this.inherited(arguments); } }); var BooleanValueBox = declare("dojox.grid.enhanced.plugins.filter.BooleanValueBox", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], { templateString: cache("dojox.grid","enhanced/templates/FilterBoolValueBox.html"), widgetsInTemplate: true, constructor: function(args){ var nls = args.cbox.plugin.nls; this._baseId = args.cbox.id; this._lblTrue = args.trueLabel || nls.trueLabel || "true"; this._lblFalse = args.falseLabel || nls.falseLabel || "false"; this.args = args; }, postCreate: function(){ this.onChange(); }, onChange: function(){}, get: function(prop){ return this.rbTrue.get("checked"); }, set: function(prop, v){ this.inherited(arguments); if(prop == "value"){ this.rbTrue.set("checked", !!v); this.rbFalse.set("checked", !v); } } }); var FilterDefDialog = declare("dojox.grid.enhanced.plugins.filter.FilterDefDialog", null, { // summary: // Create the filter definition UI. curColIdx: -1, _relOpCls: "logicall", _savedCriterias: null, plugin: null, constructor: function(args){ var plugin = this.plugin = args.plugin; this.builder = new FilterBuilder(); this._setupData(); this._cboxes = []; this.defaultType = plugin.args.defaultType || "string"; (this.filterDefPane = new FilterDefPane({ "dlg": this })).startup(); (this._defPane = new Dialog({ "refNode": this.plugin.grid.domNode, "title": plugin.nls.filterDefDialogTitle, "class": "dojoxGridFDTitlePane", "iconClass": "dojoxGridFDPaneIcon", "content": this.filterDefPane })).startup(); this._defPane.connect(plugin.grid.layer('filter'), "filterDef", lang.hitch(this, "_onSetFilter")); plugin.grid.setFilter = lang.hitch(this, "setFilter"); plugin.grid.getFilter = lang.hitch(this, "getFilter"); plugin.grid.getFilterRelation = lang.hitch(this, function(){ return this._relOpCls; }); plugin.connect(plugin.grid.layout, "moveColumn", lang.hitch(this, "onMoveColumn")); }, onMoveColumn: function(sourceViewIndex, destViewIndex, cellIndex, targetIndex, before){ if(this._savedCriterias && cellIndex != targetIndex){ if(before){ --targetIndex; } var min = cellIndex < targetIndex ? cellIndex : targetIndex; var max = cellIndex < targetIndex ? targetIndex : cellIndex; var dir = targetIndex > min ? 1 : -1; array.forEach(this._savedCriterias, function(sc){ var idx = parseInt(sc.column, 10); if(!isNaN(idx) && idx >= min && idx <= max){ sc.column = String(idx == cellIndex ? idx + (max - min) * dir : idx - dir); } }); } }, destroy: function(){ this._defPane.destroyRecursive(); this._defPane = null; this.filterDefPane = null; this.builder = null; this._dataTypeMap = null; this._cboxes = null; var g = this.plugin.grid; g.setFilter = null; g.getFilter = null; g.getFilterRelation = null; this.plugin = null; }, _setupData: function(){ var nls = this.plugin.nls; this._dataTypeMap = { // summary: // All supported data types "number":{ valueBoxCls: { dft: NumberTextBox }, conditions:[ {label: nls.conditionEqual, value: "equalto", selected: true}, {label: nls.conditionNotEqual, value: "notequalto"}, {label: nls.conditionLess, value: "lessthan"}, {label: nls.conditionLessEqual, value: "lessthanorequalto"}, {label: nls.conditionLarger, value: "largerthan"}, {label: nls.conditionLargerEqual, value: "largerthanorequalto"}, {label: nls.conditionIsEmpty, value: "isempty"} ] }, "string":{ valueBoxCls: { dft: TextBox, ac: UniqueComboBox //For autoComplete }, conditions:[ {label: nls.conditionContains, value: "contains", selected: true}, {label: nls.conditionIs, value: "equalto"}, {label: nls.conditionStartsWith, value: "startswith"}, {label: nls.conditionEndWith, value: "endswith"}, {label: nls.conditionNotContain, value: "notcontains"}, {label: nls.conditionIsNot, value: "notequalto"}, {label: nls.conditionNotStartWith, value: "notstartswith"}, {label: nls.conditionNotEndWith, value: "notendswith"}, {label: nls.conditionIsEmpty, value: "isempty"} ] }, "date":{ valueBoxCls: { dft: DateTextBox }, conditions:[ {label: nls.conditionIs, value: "equalto", selected: true}, {label: nls.conditionBefore, value: "lessthan"}, {label: nls.conditionAfter, value: "largerthan"}, {label: nls.conditionRange, value: "range"}, {label: nls.conditionIsEmpty, value: "isempty"} ] }, "time":{ valueBoxCls: { dft: TimeTextBox }, conditions:[ {label: nls.conditionIs, value: "equalto", selected: true}, {label: nls.conditionBefore, value: "lessthan"}, {label: nls.conditionAfter, value: "largerthan"}, {label: nls.conditionRange, value: "range"}, {label: nls.conditionIsEmpty, value: "isempty"} ] }, "boolean": { valueBoxCls: { dft: BooleanValueBox }, conditions: [ {label: nls.conditionIs, value: "equalto", selected: true}, {label: nls.conditionIsEmpty, value: "isempty"} ] } }; }, setFilter: function(rules, ruleRelation){ rules = rules || []; if(!lang.isArray(rules)){ rules = [rules]; } var func = function(){ if(rules.length){ this._savedCriterias = array.map(rules, function(rule){ var type = rule.type || this.defaultType; return { "type": type, "column": String(rule.column), "condition": rule.condition, "value": rule.value, "colTxt": this.getColumnLabelByValue(String(rule.column)), "condTxt": this.getConditionLabelByValue(type, rule.condition), "formattedVal": rule.formattedVal || rule.value }; }, this); this._criteriasChanged = true; if(ruleRelation === "logicall" || ruleRelation === "logicany"){ this._relOpCls = ruleRelation; } var exprs = array.map(rules, this.getExprForCriteria, this); exprs = this.builder.buildExpression(exprs.length == 1 ? exprs[0] : { "op": this._relOpCls, "data": exprs }); this.plugin.grid.layer("filter").filterDef(exprs); this.plugin.filterBar.toggleClearFilterBtn(false); } this._closeDlgAndUpdateGrid(); }; if(this._savedCriterias){ this._clearWithoutRefresh = true; var handle = connect.connect(this, "clearFilter", this, function(){ connect.disconnect(handle); this._clearWithoutRefresh = false; func.apply(this); }); this.onClearFilter(); }else{ func.apply(this); } }, getFilter: function(){ return lang.clone(this._savedCriterias) || []; }, getColumnLabelByValue: function(v){ var nls = this.plugin.nls; if(v.toLowerCase() == "anycolumn"){ return nls["anyColumnOption"]; }else{ var cell = this.plugin.grid.layout.cells[parseInt(v, 10)]; return cell ? (cell.name || cell.field) : ""; } }, getConditionLabelByValue: function(type, c){ var conditions = this._dataTypeMap[type].conditions; for(var i = conditions.length - 1; i >= 0; --i){ var cond = conditions[i]; if(cond.value == c.toLowerCase()){ return cond.label; } } return ""; }, addCriteriaBoxes: function(/* int */cnt){ // summary: // Add *cnt* criteria boxes to the filter definition pane. // Check overflow if necessary. if(typeof cnt != "number" || cnt <= 0){ return; } var cbs = this._cboxes, cc = this.filterDefPane.cboxContainer, total = this.plugin.args.ruleCount, len = cbs.length, cbox; //If overflow, add to max rule count. if(total > 0 && len + cnt > total){ cnt = total - len; } for(; cnt > 0; --cnt){ cbox = new CriteriaBox({ dlg: this }); cbs.push(cbox); cc.addChild(cbox); } //If there's no content box in it , AccordionContainer can not startup cc.startup(); this._updatePane(); this._updateCBoxTitles(); cc.selectChild(cbs[cbs.length-1]); //Asign an impossibly large scrollTop to scroll the criteria pane to the bottom. this.filterDefPane.criteriaPane.scrollTop = 1000000; if(cbs.length === 4){ if(has('ie') <= 6 && !this.__alreadyResizedForIE6){ var size = html.position(cc.domNode); size.w -= metrics.getScrollbar().w; cc.resize(size); this.__alreadyResizedForIE6 = true; }else{ cc.resize(); } } }, removeCriteriaBoxes: function(/* int|CriteriaBox|int[] */cnt,/* bool? */isIdx){ // summary: // Remove criteria boxes from the filter definition pane. var cbs = this._cboxes, cc = this.filterDefPane.cboxContainer, len = cbs.length, start = len - cnt, end = len - 1, cbox, curIdx = array.indexOf(cbs, cc.selectedChildWidget.content); if(lang.isArray(cnt)){ var i, idxes = cnt; idxes.sort(); cnt = idxes.length; //find a rule that's not deleted. //must find and focus the last one, or the hack will not work. for(i = len - 1; i >= 0 && array.indexOf(idxes, i) >= 0; --i){} if(i >= 0){ //must select before remove if(i != curIdx){ cc.selectChild(cbs[i]); } //idxes is sorted from small to large, //so travel reversely won't need change index after delete from array. for(i = cnt-1; i >= 0; --i){ if(idxes[i] >= 0 && idxes[i] < len){ cc.removeChild(cbs[idxes[i]]); cbs.splice(idxes[i],1); } } } start = cbs.length; }else{ if(isIdx === true){ if(cnt >= 0 && cnt < len){ start = end = cnt; cnt = 1; }else{ return; } }else{ if(cnt instanceof CriteriaBox){ cbox = cnt; cnt = 1; start = end = array.indexOf(cbs, cbox); }else if(typeof cnt != "number" || cnt <= 0){ return; }else if(cnt >= len){ cnt = end; start = 1; } } if(end < start){ return; } //must select before remove if(curIdx >= start && curIdx <= end){ cc.selectChild(cbs[start ? start-1 : end+1]); } for(; end >= start; --end){ cc.removeChild(cbs[end]); } cbs.splice(start, cnt); } this._updatePane(); this._updateCBoxTitles(); if(cbs.length === 3){ //In ie6, resize back to the normal width will cause the title button look strange. cc.resize(); } }, getCriteria: function(/* int */idx){ // summary: // Get the *idx*-th criteria. if(typeof idx != "number"){ return this._savedCriterias ? this._savedCriterias.length : 0; } if(this._savedCriterias && this._savedCriterias[idx]){ return lang.mixin({ relation: this._relOpCls == "logicall" ? this.plugin.nls.and : this.plugin.nls.or },this._savedCriterias[idx]); } return null; }, getExprForCriteria: function(rule){ if(rule.column == "anycolumn"){ var cells = array.filter(this.plugin.grid.layout.cells, function(cell){ return !(cell.filterable === false || cell.hidden); }); return { "op": "logicany", "data": array.map(cells, function(cell){ return this.getExprForColumn(rule.value, cell.index, rule.type, rule.condition); }, this) }; }else{ return this.getExprForColumn(rule.value, rule.column, rule.type, rule.condition); } }, getExprForColumn: function(value, colIdx, type, condition){ colIdx = parseInt(colIdx, 10); var cell = this.plugin.grid.layout.cells[colIdx], colName = cell.field || cell.name, obj = { "datatype": type || this.getColumnType(colIdx), "args": cell.dataTypeArgs, "isColumn": true }, operands = [lang.mixin({"data": this.plugin.args.isServerSide ? colName : cell}, obj)]; obj.isColumn = false; if(condition == "range"){ operands.push(lang.mixin({"data": value.start}, obj), lang.mixin({"data": value.end}, obj)); }else if(condition != "isempty"){ operands.push(lang.mixin({"data": value}, obj)); } return { "op": condition, "data": operands }; }, getColumnType: function(/* int */colIndex){ var cell = this.plugin.grid.layout.cells[parseInt(colIndex, 10)]; if(!cell || !cell.datatype){ return this.defaultType; } var type = String(cell.datatype).toLowerCase(); return this._dataTypeMap[type] ? type : this.defaultType; }, ////////////////////////////////////////////////////////////////////////////////////////////////////////// clearFilter: function(noRefresh){ // summary: // Clear filter definition. if(!this._savedCriterias){ return; } this._savedCriterias = null; this.plugin.grid.layer("filter").filterDef(null); try{ this.plugin.filterBar.toggleClearFilterBtn(true); this.filterDefPane._clearFilterBtn.set("disabled", true); this.removeCriteriaBoxes(this._cboxes.length-1); this._cboxes[0].load({}); }catch(e){ //Any error means the filter is defined outside this plugin. } if(noRefresh){ this.closeDialog(); }else{ this._closeDlgAndUpdateGrid(); } }, showDialog: function(/* int */colIndex){ // summary: // Show the filter defintion dialog. this._defPane.show(); this.plugin.filterStatusTip.closeDialog(); this._prepareDialog(colIndex); }, closeDialog: function(){ // summary: // Close the filter definition dialog. if(this._defPane.open){ this._defPane.hide(); } }, onFilter: function(e){ // summary: // Triggered when the "Filter" button is clicked. if(this.canFilter()){ this._defineFilter(); this._closeDlgAndUpdateGrid(); this.plugin.filterBar.toggleClearFilterBtn(false); } }, onClearFilter: function(e){ // summary: // Triggered when the "Clear" button is clicked. if(this._savedCriterias){ if(this._savedCriterias.length >= this.plugin.ruleCountToConfirmClearFilter){ this.plugin.clearFilterDialog.show(); }else{ this.clearFilter(this._clearWithoutRefresh); } } }, onCancel: function(e){ // summary: // Triggered when the "Cancel" buttton is clicked. var sc = this._savedCriterias; var cbs = this._cboxes; if(sc){ this.addCriteriaBoxes(sc.length - cbs.length); this.removeCriteriaBoxes(cbs.length - sc.length); array.forEach(sc, function(c, i){ cbs[i].load(c); }); }else{ this.removeCriteriaBoxes(cbs.length - 1); cbs[0].load({}); } this.closeDialog(); }, onRendered: function(cbox){ // summary: // Triggered when the rendering of the filter definition dialog is completely finished. // cbox: // Current visible criteria box if(!has('ff')){ var elems = dijitA11y._getTabNavigable(html.byId(cbox.domNode)); dijitFocus.focus(elems.lowest || elems.first); }else{ var dp = this._defPane; dp._getFocusItems(dp.domNode); dijitFocus.focus(dp._firstFocusItem); } }, _onSetFilter: function(filterDef){ // summary: // If someone clear the filter def in the store directly, we must clear it in the UI. // If someone defines a filter, don't know how to handle it! if(filterDef === null && this._savedCriterias){ this.clearFilter(); } }, _prepareDialog: function(/* int */colIndex){ var sc = this._savedCriterias, cbs = this._cboxes, i, cbox; this.curColIdx = colIndex; if(!sc){ if(cbs.length === 0){ this.addCriteriaBoxes(1); }else{ //Re-populate columns anyway, because we don't know when the column is set to hidden. for(i = 0; (cbox = cbs[i]); ++i){ cbox.changeCurrentColumn(); } } }else if(this._criteriasChanged){ this.filterDefPane._relSelect.set("value", this._relOpCls === "logicall" ? "0" : "1"); this._criteriasChanged = false; var needNewCBox = sc.length > cbs.length ? sc.length - cbs.length : 0; this.addCriteriaBoxes(needNewCBox); this.removeCriteriaBoxes(cbs.length - sc.length); this.filterDefPane._clearFilterBtn.set("disabled", false); for(i = 0; i < cbs.length - needNewCBox; ++i){ cbs[i].load(sc[i]); } if(needNewCBox > 0){ var handled = [], handle = connect.connect(this, "onRendered", function(cbox){ var i = array.indexOf(cbs, cbox); if(!handled[i]){ handled[i] = true; if(--needNewCBox === 0){ connect.disconnect(handle); } cbox.load(sc[i]); } }); } } //Since we're allowed to remove cboxes when the definition pane is not shown, //we have to resize the container to have a correct _verticalSpace. this.filterDefPane.cboxContainer.resize(); }, _defineFilter: function(){ var cbs = this._cboxes, filterCboxes = function(method){ return array.filter(array.map(cbs, function(cbox){ return cbox[method](); }), function(result){ return !!result; }); }, exprs = filterCboxes("getExpr"); this._savedCriterias = filterCboxes("save"); exprs = exprs.length == 1 ? exprs[0] : { "op": this._relOpCls, "data": exprs }; exprs = this.builder.buildExpression(exprs); this.plugin.grid.layer("filter").filterDef(exprs); this.filterDefPane._clearFilterBtn.set("disabled", false); }, _updateCBoxTitles: function(){ for(var cbs = this._cboxes, i = cbs.length; i > 0; --i){ cbs[i - 1].updateRuleIndex(i); cbs[i - 1].setAriaInfo(i); } }, _updatePane: function(){ var cbs = this._cboxes, defPane = this.filterDefPane; defPane._addCBoxBtn.set("disabled", cbs.length == this.plugin.args.ruleCount); defPane._filterBtn.set("disabled", !this.canFilter()); }, canFilter: function(){ return array.filter(this._cboxes, function(cbox){ return !cbox.isEmpty(); }).length > 0; }, _closeDlgAndUpdateGrid: function(){ this.closeDialog(); var g = this.plugin.grid; g.showMessage(g.loadingMessage); setTimeout(lang.hitch(g, g._refresh), this._defPane.duration + 10); } }); return FilterDefDialog; });