diff --git a/build/css/ox.ui.classic.css b/build/css/ox.ui.classic.css index 162e8ffd..4ab764a4 100644 --- a/build/css/ox.ui.classic.css +++ b/build/css/ox.ui.classic.css @@ -105,6 +105,11 @@ Forms -moz-box-shadow: 0 0 2px rgb(128, 128, 128); -webkit-box-shadow: 0 2 4px rgb(128, 128, 128); } +.OxThemeClassic .OxButton.OxDisabled { + background: rgb(192, 192, 192); + color: rgb(128, 128, 128); +} + .OxThemeClassic .OxButton.OxSelected, .OxThemeClassic .OxSelect.OxSelected { background: -moz-linear-gradient(top, rgb(160, 160, 160), rgb(192, 192, 192)); @@ -120,13 +125,10 @@ Forms color: rgb(192, 64, 64); } -.OxThemeClassic .OxButton.OxDisabled, .OxThemeClassic .OxLabel.OxDisabled { color: rgb(128, 128, 128); } -.OxThemeClassic .OxButton.OxDisabled { - background: rgb(192, 192, 192); -} + .OxThemeClassic input.OxCheckbox, .OxThemeClassic input.OxInput, diff --git a/build/css/ox.ui.css b/build/css/ox.ui.css index 0bb3e113..8837b8c2 100644 --- a/build/css/ox.ui.css +++ b/build/css/ox.ui.css @@ -624,6 +624,7 @@ OxSelect */ .OxButton.OxOverlapLeft, +.OxxxCheckbox.OxOverlapLeft, .OxLabel.OxOverlapLeft, .OxxxSelect.OxOverlapLeft { padding-left: 20px; @@ -631,6 +632,7 @@ OxSelect margin-left: -16px; } .OxButton.OxOverlapRight, +.OxxxCheckbox.OxOverlapRight, .OxLabel.OxOverlapRight, .OxxxSelect.OxOverlapRight { padding-left: 8px; @@ -645,6 +647,15 @@ OxSelect padding-left: 1px; padding-right: 15px; } +.OxCheckbox.OxOverlapLeft > .OxInput { + padding-left: 20px; + margin-left: -16px; +} +.OxCheckbox.OxOverlapRight > .OxLabel { + //padding-left: 8px; + padding-right: 20px; + margin-right: -16px; +} .OxSelect.OxOverlapLeft { //padding-left: 8px; padding-left: 16px; @@ -1469,7 +1480,7 @@ Miscellaneous padding: 1px 2px 1px 2px; font-size: 9px; opacity: 0; - z-index: 10; + z-index: 12; -moz-border-radius: 4px; -webkit-border-radius: 4px; } diff --git a/build/css/ox.ui.modern.css b/build/css/ox.ui.modern.css index 989a7cdb..29628e83 100644 --- a/build/css/ox.ui.modern.css +++ b/build/css/ox.ui.modern.css @@ -99,6 +99,11 @@ Forms background: rgb(80, 80, 80); //background: rgb(64, 64, 64); } +.OxThemeModern .OxCheckbox:active { + background: -moz-linear-gradient(top, rgb(16, 16, 16), rgb(48, 48, 48)); + background: -webkit-gradient(linear, left top, left bottom, from(rgb(16, 16, 16)), to(rgb(48, 48, 48))); +} + .OxThemeModern .OxButton.OxDisabled { background: rgb(80, 80, 80); color: rgb(128, 128, 128); @@ -118,6 +123,12 @@ Forms .OxThemeModern .OxFormMessage { color: rgb(255, 64, 64); } + +.OxThemeModern .OxLabel.OxDisabled { + color: rgb(128, 128, 128); +} + +.OxThemeModern input.OxCheckbox, .OxThemeModern input.OxInput, .OxThemeModern .OxTrack { background: -moz-linear-gradient(top, rgb(0, 0, 0), rgb(32, 32, 32)); @@ -141,6 +152,11 @@ Forms color: rgb(192, 192, 192); } +.OxThemeModern input.OxCheckbox.OxDisabled, +.OxThemeModern input.OxInput:disabled { + background: rgb(16, 16, 16); +} + /* ================================================================================ Lists diff --git a/build/js/ox.js b/build/js/ox.js index 17288070..fabc0f22 100644 --- a/build/js/ox.js +++ b/build/js/ox.js @@ -1,8 +1,12 @@ // todo: check http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ +/* +Ox = function(val) { + +}; +*/ Ox = { - //jQuery: jQuery || {}, // fixme: make this private - version: "0.1.2" + version: '0.1.2' }; /* @@ -27,15 +31,16 @@ Core functions */ Ox.getset = function(obj, args, callback, context) { - /* - generic getter and setter function + /*** + Generic getter and setter function + Ox.getset(obj) returns obj Ox.getset(obj, [key]) returns obj.key Ox.getset(obj, [key, val], callback, context) Ox.getset(obj, [{key: val, ...}], callback, context) sets obj.key to val, calls callback(key, val), returns context - */ + ***/ var obj_ = obj, args_ = {}, args = args || [], @@ -82,10 +87,9 @@ Ox.print = function() { Ox.uid = function() { /*** - Ox.uid - returns a unique id - >>> Ox.uid() == Ox.uid() - false + returns a unique id + >>> Ox.uid() != Ox.uid() + true ***/ var uid = 0; return function() { @@ -129,8 +133,7 @@ Array and Object functions Ox.avg = function(obj) { /*** - Ox.avg(obj) - returns the average of an array's values, or an object's properties + returns the average of an array's values, or an object's properties >>> Ox.avg([-1, 0, 1]) 0 >>> Ox.avg({"a": 1, "b": 2, "c": 3}) @@ -139,6 +142,10 @@ Ox.avg = function(obj) { return Ox.sum(obj) / Ox.length(obj); }; +Ox.clone = function(obj) { + return Ox.isArray(obj) ? obj.slice() : Ox.extend({}, obj); +}; + Ox.each = function(obj, fn) { /* Ox.each() works for arrays, objects and strings, @@ -152,6 +159,7 @@ Ox.each = function(obj, fn) { */ var i; for (i in obj) { + // fixme: should be (v, k), like [].forEach() if (fn(i, obj[i]) === false) { break; } @@ -174,6 +182,8 @@ Ox.equals = function(obj0, obj1) { ret = Ox.equals(v, obj1[i]); return ret; }); + } else if (Ox.isDate(obj0)) { + ret = obj0.getTime() == obj1.getTime(); } else if (Ox.isObject(obj0)) { ret = Ox.equals(Ox.keys(obj0), Ox.keys(obj1)) && Ox.equals(Ox.values(obj0), Ox.values(obj1)); @@ -202,6 +212,15 @@ Ox.every = function(obj, fn) { }).length == Ox.length(obj); }; +Ox.extend = function(obj) { + Ox.each(Array.prototype.slice.call(arguments, 1), function(i, arg) { + Ox.each(arg, function(key, val) { + obj0[key] = val; + }); + }); + return obj0; +}; + Ox.filter = function(arr, fn) { /* Ox.filter works for arrays and strings, like $.grep(), unlike [].filter() @@ -239,6 +258,10 @@ Ox.flatten = function(arr) { Ox.find = function(arr, str) { /* + returns an array with two arrays as elements: + an array of elements of arr that begin with str, + and an array of elements of arr that contain, + but do not begin with str >>> Ox.find(["foo", "bar", "foobar", "barfoo"], "foo") [["foo", "foobar"], ["barfoo"]] */ @@ -254,6 +277,10 @@ Ox.find = function(arr, str) { } Ox.getObjectById = function(arr, id) { + /*** + >>> Ox.getObjectById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo").title + "Foo" + ***/ var ret = null; Ox.each(arr, function(i, v) { if (v.id == id) { @@ -265,6 +292,10 @@ Ox.getObjectById = function(arr, id) { }; Ox.getPositionById = function(arr, id) { + /*** + >>> Ox.getPositionById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "bar") + 1 + ***/ var ret = -1; Ox.each(arr, function(i, v) { if (v.id == id) { @@ -353,6 +384,7 @@ Ox.map = function(arr, fn) { >>> Ox.map(new Array(3), function(v, i) { return i; }) [0, 1, 2] */ + // fixme: why not [].map.call(str, fn)? var i, len = arr.length, val, ret = []; for (i = 0; i < len; i++) { if ((val = fn(arr[i], i)) !== null) { @@ -382,6 +414,15 @@ Ox.min = function(obj) { return Math.min.apply(Math, Ox.values(obj)); }; +Ox.merge = function(arr) { + Ox.each(Array.prototype.slice.call(arguments, 1), function(i, arg) { + Ox.each(arg, function(i, val) { + arr.push(val); + }); + }); + return arr; +}; + Ox.range = function(start, stop, step) { /* >>> Ox.range(3) @@ -1696,7 +1737,7 @@ Ox.repeat = function(str, num) { >>> Ox.repeat("foo", 3) "foofoofoo" */ - return num >= 1 ? new Array(num + 1).join(str.toString()) : ""; + return num >= 1 ? new Array(num + 1).join(str.toString()) : ''; }; Ox.reverse = function(str) { @@ -1909,6 +1950,10 @@ Ox.isDate = function(val) { return val instanceof Date; }; +Ox.isElement = function(val) { + return !!(val && val.nodeType == 1); +} + Ox.isFunction = function(val) { // is in jQuery /* >>> Ox.isFunction(function() {}) @@ -1919,6 +1964,14 @@ Ox.isFunction = function(val) { // is in jQuery return typeof val == 'function' && !Ox.isRegExp(val); }; +Ox.isNaN = function(val) { + /* + >>> Ox.isNaN(NaN) + true + */ + return val !== val; +} + Ox.isNull = function(val) { /* >>> Ox.isNull(null) diff --git a/build/js/ox.ui.js b/build/js/ox.ui.js index 5705e018..92fe8cf1 100644 --- a/build/js/ox.ui.js +++ b/build/js/ox.ui.js @@ -134,9 +134,8 @@ requires self.options = $.extend({ apiTimeout: 60000, apiType: 'POST', - apiURL: '', - config: '', - init: '' + apiURL: '/api/', + init: 'init' }, options); function getUserAgent() { @@ -241,8 +240,8 @@ requires }; }); that.api[self.options.init](getUserData(), function(data) { - var user = data.data.user, - config = data.data.config; + var config = data.data.config, + user = data.data.user; document.title = config.site.name; $(function() { var $div = $body.find('div'); @@ -667,7 +666,7 @@ requires requests = {}, self = { options: { - timeout: 15000, + timeout: 60000, type: 'POST', url: 'api' } @@ -2090,13 +2089,230 @@ requires */ Ox.Filter = function(options, self) { - var self = self || {} - that = new Ox.Element() + var self = self || {}, + that = new Ox.Element('div', self) .defaults({ - + keys: [], + query: { + conditions: [], + operator: '&' + } }) .options(options || {}); + Ox.print('s.o', self.options) + + if (!self.options.query.conditions.length) { + self.options.query.conditions = [{ + key: self.options.keys[0].id, + value: '' + }]; + } + + $.extend(self, { + operators: [ + {title: 'all', operator: '&'}, + {title: 'any', operator: '|'} + ], + relations: { + date: [ + {title: 'is', operator: '='}, + {title: 'is not', operator: '!'}, + {title: 'is before', operator: '<'}, + {title: 'is after', operator: '>'}, + {title: 'is between', operator: '>&<'}, + {title: 'is not between', operator: '<|>'} + ], + list: [ + {title: 'is', operator: '='}, + {title: 'is not', operator: '!'} + ], + number: [ + {title: 'is', operator: '='}, + {title: 'is not', operator: '!'}, + {title: 'is less than', operator: '<'}, + {title: 'is greater than', operator: '>'}, + {title: 'is between', operator: '>&<'}, + {title: 'is not between', operator: '<|>'} + ], + string: [ + {title: 'is', operator: '='}, + {title: 'is not', operator: '!='}, + {title: 'begins with', operator: '^'}, + {title: 'ends with', operator: '$'}, + {title: 'contains', operator: ''}, + {title: 'does not contain', operator: '!'} + ], + text: [ + {title: 'contains', operator: ''}, + {title: 'does not contain', operator: '!'} + ] + } + }); + + self.$operator = new Ox.FormElementGroup({ + elements: [ + new Ox.Label({ + title: 'Match', + overlap: 'right', + width: 48 + }), + new Ox.Select({ + items: [ + {id: 'all', title: 'all'}, + {id: 'any', title: 'any'}, + ], + overlap: 'right', + width: 48 + }), + new Ox.Label({ + title: 'of the following conditions', + width: 160 + }), + ], + 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: $.map(self.options.keys, function(key) { + return {id: key.id, title: key.title}; + }), + */ + items: [ + {id: "title", title: "Title"}, + {id: "director", title: "Director"} + ], + width: 128 + }) + ], + separators: [ + {title: 'Limit to', width: 56}, + {title: 'sorted by', width: 64} + ] + }); + + self.$items = $.merge($.merge([self.$operator], self.$conditions), [self.$limit]); + + that = new Ox.Form({ + items: self.$items + }, $.extend({}, self)); + + function addCondition(pos) { + var key = self.options.keys[0]; + self.options.query.conditions.splice(pos, 0, { + key: key.id, + value: '', + operator: self.relations[key.type][0].operator + }); + self.$conditions.splice(pos, 0, constructCondition({}, pos)); + updateConditions(); + that.addItem(pos + 1, self.$conditions[pos]); + } + + function constructCondition(condition, pos) { + var $condition; + return $condition = new Ox.FormElementGroup({ + elements: [ + new Ox.Select({ + items: $.extend({}, self.options.keys), // fixme: Ox.Menu messes with keys + overlap: 'right', + width: 128 + }), + new Ox.Select({ + items: $.map(self.relations[self.options.keys[0].type], function(relation) { + return {id: relation.title, title: relation.title} + }), + overlap: 'right', + width: 128 + }), + new Ox.Input({ + width: 256 + }), + new Ox.Button({ + disabled: self.options.query.conditions.length == 1, + id: 'remove', + title: 'remove', + tooltip: 'Remove Condition', + type: 'image' + }) + .css({margin: '0 4px 0 8px'}) + .bindEvent({ + click: function() { + removeCondition($condition.data('position')); + } + }), + new Ox.Button({ + id: 'add', + title: 'add', + tooltip: 'Add Condition', + type: 'image' + }) + .css({margin: '0 4px 0 4px'}) + .bindEvent({ + click: function() { + addCondition($condition.data('position') + 1) + } + }), + new Ox.Button({ + id: 'more', + title: 'more', + tooltip: 'Add Group of Conditions', + type: 'image' + }) + .css({margin: '0 0 0 4px'}), + ] + }) + .data({position: pos}); + } + + function removeCondition(pos) { + self.options.query.conditions.splice(pos, 1); + self.$conditions.splice(pos, 1); + updateConditions(); + that.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; }; @@ -2127,8 +2343,7 @@ requires $.each(self.options.items, function(i, item) { self.itemIds[i] = item.options('id') || item.id; self.itemIsValid[i] = !!item.value().length; - that.append(self.$items[i] = new Ox.FormItem({element: item})) - .append(self.$messages[i] = new Ox.Element().addClass('OxFormMessage')); + that.append(self.$items[i] = new Ox.FormItem({element: item})); item.bindEvent({ /* blur: function(event, data) { @@ -2143,22 +2358,14 @@ requires autovalidate: function(event, data) { data.valid = !!data.value.length; validate(i, data.valid); - if (data.valid) { - self.$messages[i].html('').hide(); - } else { - //self.$messages[i].html(data.message).show(); - } + data.valid && self.$items[i].setMessage(''); }, submit: function(event, data) { self.formIsValid && that.submit(); }, validate: function(event, data) { validate(i, data.valid); - if (data.valid) { - self.$messages[i].html('').hide(); - } else { - self.$messages[i].html(data.message).show(); - } + self.$items[i].setMessage(data.valid ? '' : data.message); } }); }); @@ -2167,13 +2374,9 @@ requires return self.itemIds.indexOf(id); } - function setMessage(id, message) { - self.$messages[getItemPositionById(id)].html(message)[message !== '' ? 'show' : 'hide'](); - } - function submitCallback(data) { $.each(data, function(i, v) { - setMessage(v.id, v.message); + self.$items[i].setMessage(v.message); }); } @@ -2188,6 +2391,22 @@ requires } } + that.addItem = function(pos, item) { + Ox.print('addItem', pos) + self.options.items.splice(pos, 0, item); + self.$items.splice(pos, 0, new Ox.FormItem({element: item})); + pos == 0 ? + self.$items[pos].insertBefore(self.$items[0]) : + self.$items[pos].insertAfter(self.$items[pos - 1]); + } + + that.removeItem = function(pos) { + Ox.print('removeItem', pos); + self.$items[pos].remove(); + self.options.items.splice(pos, 1); + self.$items.splice(pos, 1); + } + that.submit = function() { //Ox.print('---- that.values()', that.values()) self.options.submit(that.values(), submitCallback); @@ -2232,6 +2451,14 @@ requires .addClass('OxFormItem') .append(self.options.element); + self.$message = new Ox.Element() + .addClass('OxFormMessage') + .appendTo(that); + + that.setMessage = function(message) { + self.$message.html(message)[message !== '' ? 'show' : 'hide'](); + } + that.value = function() { return self.options.element.value(); }; @@ -2504,11 +2731,15 @@ requires id: '', group: false, checked: false, + overlap: 'none', title: '', width: 'auto' }) .options(options || {}) - .addClass('OxCheckbox') + .addClass('OxCheckbox' + + (self.options.overlap == 'none' ? '' : ' OxOverlap' + + Ox.toTitleCase(self.options.overlap)) + ) .attr(self.options.disabled ? { disabled: 'disabled' } : {}); @@ -2872,6 +3103,9 @@ requires function autocomplete(oldValue, oldCursor) { + oldValue = Ox.isUndefined(oldValue) ? self.options.value : oldValue; + oldCursor = Ox.isUndefined(oldCursor) ? cursor : oldCursor; + Ox.print('autocomplete', oldValue, oldCursor) if (self.options.value || self.options.autocompleteReplaceCorrect) { @@ -3405,6 +3639,7 @@ requires }); $.each(self.options.separators, function(i, v) { + self.options.id == 'debug' && Ox.print('separator #' + i + ' ' + self.options.inputs[i].options('id') + ' ' + self.options.inputs[i].options('width')) self.$separator[i] = new Ox.Label({ textAlign: 'center', title: v.title, @@ -3453,7 +3688,7 @@ requires return v.options('width'); })) + Ox.sum($.map(self.options.separators, function(v, i) { return v.width; - })); + })) + 2; // fixme: why + 2? } function setWidths() { @@ -4438,6 +4673,8 @@ requires key_down: showMenu }); + Ox.print('Ox.Select', self.options) + $.extend(self, { buttonId: self.options.id + 'Button', groupId: self.options.id + 'Group', @@ -9015,6 +9252,7 @@ requires select_menuId {id, value} item was selected */ Ox.Menu = function(options, self) { + var self = self || {}, that = new Ox.Element({}, self) .defaults({ diff --git a/build/json/ox.ui.images.json b/build/json/ox.ui.images.json index 55461ddc..ee56a969 100644 --- a/build/json/ox.ui.images.json +++ b/build/json/ox.ui.images.json @@ -46,6 +46,7 @@ "png/ox.ui.classic/symbolList.png", "png/ox.ui.classic/symbolLocation.png", "png/ox.ui.classic/symbolLock.png", + "png/ox.ui.classic/symbolMore.png", "png/ox.ui.classic/symbolMute.png", "png/ox.ui.classic/symbolNext.png", "png/ox.ui.classic/symbolNone.png", @@ -103,6 +104,7 @@ "png/ox.ui.modern/symbolList.png", "png/ox.ui.modern/symbolLocation.png", "png/ox.ui.modern/symbolLock.png", + "png/ox.ui.modern/symbolMore.png", "png/ox.ui.modern/symbolMute.png", "png/ox.ui.modern/symbolNext.png", "png/ox.ui.modern/symbolNone.png", diff --git a/demos/form2/js/form.js b/demos/form2/js/form.js index 0e512f92..35b89454 100644 --- a/demos/form2/js/form.js +++ b/demos/form2/js/form.js @@ -418,6 +418,31 @@ $(function() { }, title: "FormElementGroup (Input and Select)" }, + { + options: { + elements: [ + new Ox.Label({ + title: "Match", + overlap: "right", + }), + new Ox.Select({ + id: "select_", + items: [ + {id: "all", title: "all"}, + {id: "any", title: "any"}, + ], + overlap: "right", + width: 48 + }), + new Ox.Label({ + title: "of the following conditions", + }), + ], + float: "left", + id: "formElementGroupLabels" + }, + title: "foo" + }, { options: { elements: [ @@ -717,6 +742,52 @@ $(function() { {title: "", width: 0} ] } + }, + { + options: { + id: 'debug', + inputs: [ + new Ox.Checkbox({ + id: 'tttt', + width: 16 + }), + new Ox.FormElementGroup({ + elements: [ + new Ox.Input({ + //overlap: "right", + width: 64 + }), + 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: 128 + }), + new Ox.Select({ + items: [ + {id: "title", title: "Title"}, + {id: "director", title: "Director"}, + ], + width: 128 + }) + ], + separators: [ + {title: 'Limit to', width: 64}, + {title: 'sorted by', width: 64} + ], + //width: 480 + }, + title: "bar" } ], "Label": [