// vim: et:ts=4:sw=4:sts=4:ft=js Ox.Filter = function(options, self) { /*** Options: Methods: Events: ***/ var self = self || {}, that = new Ox.Element('div', self) .defaults({ findKeys: [], query: { conditions: [], operator: '&' }, sortKeys: [], viewKeys: [] }) .options(options || {}); Ox.print('Ox.Filter self.options', self.options) $.extend(self, { conditionOperators: { date: [ {id: '', title: 'is'}, {id: '!', title: 'is not'}, {id: '<', title: 'is before'}, {id: '>', title: 'is after'}, {id: '>&<', title: 'is between'}, {id: '<|>', title: 'is not between'} ], list: [ {id: '', title: 'is'}, {id: '!', title: 'is not'} ], number: [ {id: '', title: 'is'}, {id: '!', title: 'is not'}, {id: '<', title: 'is less than'}, {id: '>', title: 'is greater than'}, {id: '>&<', title: 'is between'}, {id: '<|>', title: 'is not between'} ], string: [ {id: '=', title: 'is'}, {id: '!=', title: 'is not'}, {id: '^', title: 'begins with'}, {id: '$', title: 'ends with'}, {id: '', title: 'contains'}, {id: '!', title: 'does not contain'} ], text: [ {id: '', title: 'contains'}, {id: '!', title: 'does not contain'} ] }, operators: [ {id: '&', title: 'all'}, {id: '|', title: 'any'} ] }); if (!self.options.query.conditions.length) { self.options.query.conditions = [{ key: self.options.findKeys[0].id, value: '', operator: self.conditionOperators[ getConditionType(self.options.findKeys[0].type) ][0].id }]; } self.$operator = new Ox.FormElementGroup({ elements: [ new Ox.Label({ title: 'Match', overlap: 'right', width: 48 }), new Ox.FormElementGroup({ elements: [ new Ox.Select({ items: self.operators, width: 48 }) .bindEvent({ change: changeOperator }), new Ox.Label({ overlap: 'left', title: 'of the following conditions', width: 160 }) ], float: 'right', width: 208 }) ], float: 'left', }); self.$buttons = []; self.$conditions = $.map(self.options.query.conditions, function(condition, i) { return constructCondition(condition, i); }); self.$limit = new Ox.InputGroup({ inputs: [ new Ox.Checkbox({ width: 16 }), new Ox.FormElementGroup({ elements: [ new Ox.Input({ width: 56 }), new Ox.Select({ items: [ {id: 'items', title: 'items'}, {}, {id: 'hours', title: 'hours'}, {id: 'days', title: 'days'}, {}, {id: 'GB', title: 'GB'} ], overlap: 'left', width: 64 }) ], float: 'right', width: 120 }), new Ox.Select({ items: self.options.sortKeys, width: 128 }), new Ox.FormElementGroup({ elements: [ new Ox.Select({ items: [ {id: 'ascending', title: 'ascending'}, {id: 'descending', title: 'descending'} ], width: 96 }), new Ox.Label({ overlap: 'left', title: 'order', width: 72 }) ], float: 'right', width: 168 }) ], separators: [ {title: 'Limit to', width: 56}, {title: 'sorted by', width: 64}, {title: 'in', width: 32} ] }); self.$view = new Ox.InputGroup({ inputs: [ new Ox.Checkbox({ width: 16 }), new Ox.Select({ items: self.options.viewKeys, width: 128 }) ], separators: [ {title: 'By default, view', width: 112} ] }); self.$save = new Ox.InputGroup({ inputs: [ new Ox.Checkbox({ width: 16 }), new Ox.Input({ id: 'list', width: 128 }) ], separators: [ {title: 'Save as Smart List', width: 112} ] }); self.$items = $.merge($.merge([self.$operator], self.$conditions), [self.$limit, self.$view, self.$save]); self.$form = new Ox.Form({ items: self.$items }); that.$element = self.$form.$element; function addCondition(pos) { var key = self.options.findKeys[0]; self.options.query.conditions.splice(pos, 0, { key: key.id, value: '', operator: self.conditionOperators[key.type][0].id }); self.$conditions.splice(pos, 0, constructCondition({}, pos)); updateConditions(); self.$form.addItem(pos + 1, self.$conditions[pos]); } function addGroup(pos) { self.$form.addItem(pos + 1, constructGroup(pos)) } function changeConditionKey(pos, key) { Ox.print('changeConditionKey', pos, key); var oldOperator = self.options.query.conditions[pos].operator, oldType = Ox.getObjectById( self.options.findKeys, self.options.query.conditions[pos].key ).type, newType = Ox.getObjectById( self.options.findKeys, key ).type, oldConditionType = getConditionType(oldType), newConditionType = getConditionType(newType); changeConditionType = oldConditionType != newConditionType; Ox.print('old new', oldConditionType, newConditionType) self.options.query.conditions[pos].key = key; if (changeConditionType) { self.$conditions[pos].replaceElement(1, constructConditionOperator(pos, oldOperator)); } } function changeConditionOperator(pos, operator) { self.options.query.conditions[pos].operator = operator; } function changeOperator(event, data) { self.options.query.operator = data.selected[0].id; } function constructCondition(condition, pos) { var $condition; return $condition = new Ox.FormElementGroup({ elements: [ new Ox.Select({ items: $.map(self.options.findKeys, function(key) { return { id: key.id, title: key.title }; }), //items: $.extend({}, self.options.findKeys), // fixme: Ox.Menu messes with keys overlap: 'right', width: 128 }) .bindEvent({ change: function(event, data) { Ox.print('event', event) changeConditionKey($condition.data('position'), data.selected[0].id); } }), constructConditionOperator(pos), new Ox.Input({ width: 256 }), new Ox.Button({ disabled: self.options.query.conditions.length == 1, id: 'remove', title: 'remove', type: 'image' }) .css({margin: '0 4px 0 8px'}) .bindEvent({ click: function() { removeCondition($condition.data('position')); } }), new Ox.Button({ id: 'add', title: 'add', type: 'image' }) .css({margin: '0 4px 0 4px'}) .bindEvent({ click: function() { Ox.print('add', $(this).parent().parent().data('position')) addCondition($condition.data('position') + 1) } }), new Ox.Button({ id: 'addgroup', title: 'more', type: 'image' }) .css({margin: '0 0 0 4px'}) .bindEvent({ click: function() { addGroup($condition.data('position') + 1) } }) ] }) .data({position: pos}); } function constructConditionOperator(pos, selected) { return new Ox.Select({ items: $.map(self.conditionOperators[getConditionType( Ox.getObjectById( self.options.findKeys, self.options.query.conditions[pos].key ).type )], function(operator) { return { checked: operator.id == selected, // fixme: should be "selected", not "checked" id: operator.operator, title: operator.title }; }), overlap: 'right', width: 128 }) .bindEvent({ change: function(event, data) { changeConditionOperator(/*$condition.data('position')*/ pos, data.selected[0].id) } }); } function constructGroup() { // fixme: duplicated return new Ox.FormElementGroup({ elements: [ new Ox.Label({ title: self.options.operator == '&' ? 'and' : 'or', overlap: 'right', width: 48 }), new Ox.FormElementGroup({ elements: [ new Ox.Select({ items: $.map(self.operators, function(operator) { Ox.print('!!!!', { checked: operator.id != self.options.operator, id: operator.id, title: operator.title }); return { //checked: operator.id != self.options.operator, id: operator.id, title: operator.title } }), width: 48 }) .bindEvent({ change: changeOperator }), new Ox.Label({ overlap: 'left', title: 'of the following conditions', width: 160 }) ], float: 'right', width: 208 }) ], float: 'left', }); } function getConditionType(type) { type = Ox.isArray(type) ? type[0] : type; if (['float', 'integer', 'year'].indexOf(type) > -1) { type = 'number'; } return type; } function removeCondition(pos) { self.options.query.conditions.splice(pos, 1); self.$conditions.splice(pos, 1); updateConditions(); self.$form.removeItem(pos + 1); } function updateConditions() { self.$conditions.forEach(function(condition, pos) { condition.data({position: pos}); }); self.$conditions[0].options('elements')[3].options({ disabled: self.options.query.conditions.length == 1 }); } return that; };