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;
});