From 6a33b9cb975bc28c46a8b4fb5eac9b7515b8b9bb Mon Sep 17 00:00:00 2001 From: rolux Date: Sat, 21 May 2011 19:56:15 +0200 Subject: [PATCH] add Ox.ArrayInput, more Ox.ListMap UI --- demos/form2/js/form.js | 11 +- demos/listmap2/js/listmap.js | 19 ++-- source/Ox.UI/css/Ox.UI.css | 3 + source/Ox.UI/js/Form/Ox.ArrayInput.js | 151 ++++++++++++++++++++++++++ source/Ox.UI/js/Form/Ox.Input.js | 135 +++++++++++++---------- source/Ox.UI/js/Form/Ox.Label.js | 2 + source/Ox.UI/js/Map/Ox.ListMap.js | 144 ++++++++++++++++++++---- source/Ox.js | 17 ++- 8 files changed, 381 insertions(+), 101 deletions(-) create mode 100644 source/Ox.UI/js/Form/Ox.ArrayInput.js diff --git a/demos/form2/js/form.js b/demos/form2/js/form.js index 7c31bf90..e4afca30 100644 --- a/demos/form2/js/form.js +++ b/demos/form2/js/form.js @@ -581,14 +581,17 @@ Ox.load('Geo', function() { }, { options: { + min: 0, + max: 100, type: "float" }, title: "Float Input (0.0 - 100.0)" }, { options: { + min: 0, max: 255, - type: "integer" + type: "int" }, title: "Integer Input (0 - 255)" }, @@ -596,8 +599,7 @@ Ox.load('Geo', function() { options: { clear: true, id: "integerClear", - max: 255, - type: "integer" + type: "int" }, title: "Integer Input with Clear Button" }, @@ -605,8 +607,7 @@ Ox.load('Geo', function() { options: { arrows: true, id: "integerArrows", - max: 255, - type: "integer" + type: "int" }, title: "Integer Input with Arrows" }, diff --git a/demos/listmap2/js/listmap.js b/demos/listmap2/js/listmap.js index 412feffc..2c268e9f 100644 --- a/demos/listmap2/js/listmap.js +++ b/demos/listmap2/js/listmap.js @@ -1,16 +1,17 @@ Ox.load('UI', { debug: true, - hideScreen: true, - showScreen: true, theme: 'modern' }, function() { - Ox.getJSON('json/cities100000.json', function(data) { +Ox.load('Geo', function() { + + Ox.getJSON('json/cities1000000.json', function(data) { var listmap = new Ox.ListMap({ height: window.innerHeight, places: data.map(function(city) { - var marker = city.population > 20000000 ? {size: 24, color: [255, 0, 0]} : + var countryCode = city.country_code == 'XK' ? 'RS-KO' : city.country_code, + marker = city.population > 20000000 ? {size: 24, color: [255, 0, 0]} : city.population > 10000000 ? {size: 22, color: [255, 32, 0]} : city.population > 5000000 ? {size: 20, color: [255, 64, 0]} : city.population > 2000000 ? {size: 18, color: [255, 96, 0]} : @@ -23,10 +24,10 @@ Ox.load('UI', { latSize = size / Ox.EARTH_CIRCUMFERENCE * 360, lngSize = size * Ox.getDegreesPerMeter(city.latitude); return { - countryCode: city.country_code == 'XK' ? 'RS-KO' : city.country_code, + countryCode: countryCode, editable: true, - flag: city.country_code, - geoname: city.name, + flag: countryCode, + geoname: city.name + ', ' + Ox.getCountryByCode(countryCode).name, markerColor: marker.color, markerSize: marker.size, name: city.name, @@ -49,6 +50,7 @@ Ox.load('UI', { } }) .appendTo(Ox.UI.$body); + $(window).resize(function() { Ox.print('RESIZE', window.innerHeight) listmap.options({ @@ -58,5 +60,8 @@ Ox.load('UI', { }); window.listmap = listmap; + }); + +}); }); \ No newline at end of file diff --git a/source/Ox.UI/css/Ox.UI.css b/source/Ox.UI/css/Ox.UI.css index 5e458ea8..10d7c7d2 100644 --- a/source/Ox.UI/css/Ox.UI.css +++ b/source/Ox.UI/css/Ox.UI.css @@ -1088,6 +1088,9 @@ Maps position: absolute; } +.OxMap .OxRange .OxArrow { + border-radius: 0; +} .OxMapButton { position: absolute; diff --git a/source/Ox.UI/js/Form/Ox.ArrayInput.js b/source/Ox.UI/js/Form/Ox.ArrayInput.js new file mode 100644 index 00000000..cf2a481d --- /dev/null +++ b/source/Ox.UI/js/Form/Ox.ArrayInput.js @@ -0,0 +1,151 @@ +Ox.ArrayInput = function(options, self) { + + self = self || {}; + var that = Ox.Element({}, self) + .defaults({ + label: '', + max: 0, + sort: false, + value: [], + width: 256 + }) + .options(options || {}); + + if (self.options.value.length == 0) { + self.options.value = ['']; + } + + if (self.options.label) { + self.$label = Ox.Label({ + title: self.options.label, + width: self.options.width + }) + .appendTo(that); + } + + self.$element = []; + self.$input = []; + self.$removeButton = []; + self.$addButton = []; + self.options.value.forEach(function(value, i) { + addInput(i, value); + }); + + function addInput(i, value, focus) { + Ox.print('add', i) + self.$element.splice(i, 0, Ox.Element() + .css({height: '16px', marginTop: self.options.label || i > 0 ? '8px' : 0}) + .data({index: i})); + if (i == 0) { + self.$element[i].appendTo(that); + } else { + Ox.print(i, self.$element) + self.$element[i].insertAfter(self.$element[i - 1]); + } + self.$input.splice(i, 0, Ox.Input({ + value: value, + width: self.options.width - 48 + }) + .css({float: 'left'}) + .bindEvent({ + change: function(data) { + self.options.sort && data.value !== '' && sortInputs(); + } + }) + .appendTo(self.$element[i])); + focus && self.$input[i].focusInput(); + self.$removeButton.splice(i, 0, Ox.Button({ + disabled: self.$input.length == 1, + title: 'remove', + type: 'image' + }) + .css({float: 'left', marginLeft: '8px'}) + .bind({ + click: function() { + removeInput($(this).parent().data('index')); + } + }) + .appendTo(self.$element[i])); + self.$addButton.splice(i, 0, Ox.Button({ + disabled: i == self.options.max - 1, + title: 'add', + type: 'image' + }) + .css({float: 'left', marginLeft: '8px'}) + .bind({ + click: function() { + addInput($(this).parent().data('index') + 1, '', true); + } + }) + .appendTo(self.$element[i])); + self.$input.length > 1 && self.$removeButton[0].options({disabled: false}); + self.$input.length == self.options.max && self.$addButton.forEach(function($button) { + $button.options({disabled: true}); + }); + updateIndices(); + } + + function removeInput(i) { + Ox.print('remove', i); + ['input', 'removeButton', 'addButton', 'element'].forEach(function(element) { + var key = '$' + element; + self[key][i].removeElement(); + self[key].splice(i, 1); + }); + self.$input.length == 1 && self.$removeButton[0].options({disabled: true}); + self.$input.length == self.options.max - 1 && self.$addButton.forEach(function($button) { + $button.options({disabled: false}); + }); + updateIndices(); + } + + function setWidths() { + self.$label && self.$label.options({width: self.options.width}) + self.$element.forEach(function($element, i) { + $element.css({width: self.options.width + 'px'}); + self.$input[i].options({width: self.options.width - 48}); + }); + } + + function sortInputs() { + Ox.sort(self.$element, function($element) { + return self.$input[$element.data('index')].value(); + }).forEach(function($element) { + $element.detach(); + }); + self.$element.forEach(function($element, i) { + $element.data({index: i}).appendTo(that); + }); + } + + function updateIndices() { + self.$element.forEach(function($element, i) { + Ox.print(i, $element) + $element.data({index: i}); + }); + /* + self.$input.forEach(function($input, i) { + $input.options({value: i}); + }); + */ + } + + self.setOption = function(key, value) { + if (key == 'width') { + setWidths(); + } + } + + // fixme: can't we generally use options.value for this? + that.value = function() { + if (arguments.length == 0) { + return Ox.map(self.$input, function($input) { + var value = $input.value(); + return value === '' ? null : value; + }); + } + }; + + return that; + +}; \ No newline at end of file diff --git a/source/Ox.UI/js/Form/Ox.Input.js b/source/Ox.UI/js/Form/Ox.Input.js index e737dbe9..23ff73b0 100644 --- a/source/Ox.UI/js/Form/Ox.Input.js +++ b/source/Ox.UI/js/Form/Ox.Input.js @@ -5,7 +5,7 @@ Ox.Input Input Element (options) -> Input Element (options, self) -> Input Element options Options object - arrows if true, and type is 'float' or 'integer', display arrows + arrows if true, and type is 'float' or 'int', display arrows arrowStep step when clicking arrows autocomplete array of possible values, or function(key, value, callback), returns one or more values @@ -14,7 +14,7 @@ Ox.Input Input Element autocompleteSelect if true, menu is displayed autocompleteSelectHighlight if true, value in menu is highlighted autocompleteSelectSubmit if true, submit input on menu selection - autocorrect ('email', 'float', 'integer', 'phone', 'url'), or + autocorrect ('email', 'float', 'int', 'phone', 'url'), or regexp(value), or function(key, value, blur, callback), returns value autovalidate --remote validation-- @@ -24,8 +24,8 @@ Ox.Input Input Element height px (for type='textarea' and type='range' with orientation='horizontal') id element id key to be passed to autocomplete and autovalidate functions - max max value if type is 'integer' or 'float' - min min value if type is 'integer' or 'float' + max max value if type is 'int' or 'float' + min min value if type is 'int' or 'float' name will be displayed by autovalidate function ('invalid ' + name) overlap '', 'left' or 'right', will cause padding and negative margin picker picker object @@ -48,7 +48,7 @@ Ox.Input Input Element trackValues boolean serialize function used to serialize value in submit textAlign 'left', 'center' or 'right' - type 'float', 'integer', 'password', 'text', 'textarea' + type 'float', 'int', 'password', 'text', 'textarea' value string validate remote validation width px @@ -74,10 +74,11 @@ Ox.Input = function(options, self) { autovalidate: null, changeOnKeypress: false, clear: false, + decimals: 0, disabled: false, key: '', - min: 0, - max: 100, + min: -Infinity, + max: Infinity, label: '', labelWidth: 64, overlap: 'none', @@ -97,6 +98,7 @@ Ox.Input = function(options, self) { ' OxOverlap' + Ox.toTitleCase(self.options.overlap) : '' )*/ ) + .css({width: self.options.width + 'px'}) .bindEvent($.extend(self.options.type == 'textarea' ? {} : { key_enter: submit }, { @@ -114,15 +116,17 @@ Ox.Input = function(options, self) { } // fixme: set to min, not 0 + // fixme: validate self.options.value ! if (self.options.type == 'float') { + self.decimals = Ox.repeat('0', self.options.decimals || 1) $.extend(self.options, { autovalidate: 'float', textAlign: 'right', - value: self.options.value || '0.0' + value: self.options.value || '0.' + self.decimals }); - } else if (self.options.type == 'integer') { + } else if (self.options.type == 'int') { $.extend(self.options, { - autovalidate: 'integer', + autovalidate: 'int', textAlign: 'right', value: self.options.value || '0' }); @@ -176,7 +180,8 @@ Ox.Input = function(options, self) { } $.extend(self, { - bindKeyboard: self.options.autocomplete || self.options.autovalidate || self.options.changeOnKeypress, + bindKeyboard: self.options.autocomplete || self.options.autovalidate || + self.options.changeOnKeypress, hasPasswordPlaceholder: self.options.type == 'password' && self.options.placeholder, inputWidth: getInputWidth() }); @@ -449,48 +454,57 @@ Ox.Input = function(options, self) { function autovalidateTypeFunction(type, value) { // fixme: remove trailing zeroes on blur + // /(^\-?\d+\.?\d{0,8}$)/('-13000.12345678') var cursor, - regexp = type == 'float' ? /[\d\.]/ : /\d/; + length, + regexp = type == 'float' ? new RegExp( + '(^' + (self.options.min < 0 ? '\\-?' : '') + '\\d+\\.?\\d' + + (self.options.decimals ? '{0,' + self.options.decimals + '}' : '*') + + '$)' + ) : new RegExp('(^' + (self.options.min < 0 ? '\\-?' : '') + '\\d+)'); if (type == 'float') { - if (value.indexOf('.') != value.lastIndexOf('.')) { - value = oldValue; - } else { - if (self.autovalidateFloatFlag) { - if (Ox.endsWith(value, '.')) { - value = value.substr(0, value.length - 1); - } - self.autovalidateFloatFlag = false; - } - while (value[0] == '0' && value[1] != '.') { - value = value.substr(1); - } - while (Ox.startsWith(value, '.')) { - if (Ox.startsWith(value, '..')) { - value = value.substr(1); - } else { - value = '0' + value; - } - } - if (Ox.endsWith(value, '.')) { - value += '0'; - cursor = [value.length - 1, value.length]; - self.autovalidateFloatFlag = true; + //Ox.print('--float--', value) + if (value === '') { + value = '0.' + self.decimals; + cursor = [0, value.length]; + } else if (value == '-') { + value = '-0.' + self.decimals; + cursor = [1, value.length]; + } else if (value == '.') { + value = '0.' + self.decimals; + cursor = [2, value.length]; + } else if (!/\./(value)) { + value += '.' + self.decimals + cursor = [value.indexOf('.'), value.length]; + } else if (/^\./(value)) { + value = '0' + value; + cursor = [2, value.length]; + } else if (/\.$/(value)) { + //Ox.print('$$$$$$$$$$$') + value += self.decimals; + cursor = [value.indexOf('.') + 1, value.length]; + } else if (/\./(value) && self.options.decimals) { + length = value.split('.')[1].length; + if (length > self.options.decimals) { + value = value.substr(0, value.indexOf('.') + 1 + self.options.decimals); + cursor = [oldCursor[0] + 1, oldCursor[1] + 1]; + } else if (length < self.options.decimals) { + value += Ox.repeat('0', self.options.decimals - length); + cursor = [value.indexOf('.') + 1 + length, value.length]; } } - } - value = $.map(value.split(''), function(v) { - return regexp(v) ? v : null; - }).join(''); - if (type == 'integer') { - while (value.length > 1 && Ox.startsWith(value, '0')) { - value = value.substr(1); + } else { + if (value === '') { + value = '0'; + cursor = [0, 1]; } } - if (value === '') { - value = type == 'float' ? '0.0' : '0'; - cursor = [0, value.length]; - } else if (value > self.options.max) { + while (/^0\d/(value)) { + value = value.substr(1, value.length); + } + if (!regexp.test(value) || value < self.options.min || value > self.options.max) { value = oldValue; + cursor = oldCursor; } autovalidateCallback(value, cursor); } @@ -549,8 +563,8 @@ Ox.Input = function(options, self) { self.options.placeholder && setPlaceholder(); self.options.validate && validate(); if (self.bindKeyboard) { - Ox.UI.$document.unbind('keydown', keypress); - Ox.UI.$document.unbind('keypress', keypress); + Ox.UI.$document.unbind('keydown', keydown); + //Ox.UI.$document.unbind('keypress', keypress); } that.triggerEvent('blur'); } @@ -572,7 +586,7 @@ Ox.Input = function(options, self) { var value = ''; if (self.options.type == 'float') { value = '0.0'; - } else if (self.options.type == 'integer') { + } else if (self.options.type == 'int') { value = '0' } self.$input.val(value); @@ -615,7 +629,7 @@ Ox.Input = function(options, self) { function deselectMenu() { return; - Ox.print('deselectMenu') + //Ox.print('deselectMenu') self.options.value = self.oldValue; self.$input.val(self.options.value); cursor(self.oldCursor); @@ -636,8 +650,8 @@ Ox.Input = function(options, self) { if (self.bindKeyboard) { //Ox.print('binding...') // fixme: different in webkit and firefox (?), see keyboard handler, need generic function - Ox.UI.$document.keydown(keypress); - Ox.UI.$document.keypress(keypress); + Ox.UI.$document.keydown(keydown); + //Ox.UI.$document.keypress(keypress); self.options.autocompleteSelect && setTimeout(autocomplete, 0); // fixme: why is the timeout needed? } that.triggerEvent('focus'); @@ -651,7 +665,7 @@ Ox.Input = function(options, self) { (self.options.style == 'rounded' ? 14 : 6); } - function keypress(event) { + function keydown(event) { var oldCursor = cursor(), oldValue = self.options.value, newValue = oldValue.substr(0, oldCursor[0] - 1), @@ -664,7 +678,8 @@ Ox.Input = function(options, self) { ) { // fixme: can't 13 and 27 return false? setTimeout(function() { // wait for val to be set var value = self.$input.val(); - if (self.options.autocompleteReplace && hasDeletedSelectedEnd) { + if ((self.options.autocompleteReplace || self.options.decimals) && hasDeletedSelectedEnd) { + //Ox.print('HAS DELETED SELECTED END', event.keyCode) value = newValue; self.$input.val(value); } @@ -697,7 +712,7 @@ Ox.Input = function(options, self) { function selectMenu(event, data) { var pos = cursor(); //if (self.options.value) { - Ox.print('selectMenu', pos, data.title) + //Ox.print('selectMenu', pos, data.title) self.options.value = data.title self.$input.val(self.options.value); cursor(pos[0], self.options.value.length); @@ -778,6 +793,7 @@ Ox.Input = function(options, self) { self.$input.val(value); setPlaceholder(); } else if (key == 'width') { + that.css({width: self.options.width + 'px'}); inputWidth = getInputWidth(); self.$input.css({ width: inputWidth + 'px' @@ -1535,8 +1551,8 @@ Ox.InputElement_ = function(options, self) { } } if (self.bindKeyboard) { - Ox.UI.$document.unbind('keydown', keypress); - Ox.UI.$document.unbind('keypress', keypress); + Ox.UI.$document.unbind('keydown', keydown); + //Ox.UI.$document.unbind('keypress', keypress); } } @@ -1585,14 +1601,13 @@ Ox.InputElement_ = function(options, self) { } if (self.bindKeyboard) { // fixme: different in webkit and firefox (?), see keyboard handler, need generic function - Ox.UI.$document.keydown(keypress); - Ox.UI.$document.keypress(keypress); + Ox.UI.$document.keydown(keydown); //Ox.print('calling autosuggest...') self.options.autosuggest && setTimeout(autosuggestCall, 0); // fixme: why is the timeout needed? } } - function keypress(event) { + function keydown(event) { //Ox.print('keyCode', event.keyCode) if (event.keyCode != 9 && event.keyCode != 13 && event.keyCode != 27) { // fixme: can't 13 and 27 return false? setTimeout(function() { // fixme: document what this timeout is for diff --git a/source/Ox.UI/js/Form/Ox.Label.js b/source/Ox.UI/js/Form/Ox.Label.js index 1c2b9d33..9d198582 100644 --- a/source/Ox.UI/js/Form/Ox.Label.js +++ b/source/Ox.UI/js/Form/Ox.Label.js @@ -35,6 +35,8 @@ Ox.Label = function(options, self) { self.setOption = function(key, value) { if (key == 'title') { that.html(value); + } else if (key == 'width') { + that.css({width: self.options.width - 14 + 'px'}) } } diff --git a/source/Ox.UI/js/Map/Ox.ListMap.js b/source/Ox.UI/js/Map/Ox.ListMap.js index ac3fb627..312dec03 100644 --- a/source/Ox.UI/js/Map/Ox.ListMap.js +++ b/source/Ox.UI/js/Map/Ox.ListMap.js @@ -18,7 +18,7 @@ Ox.ListMap ListMap Object Ox.ListMap = function(options, self) { var self = self || {}, - that = new Ox.Element({}, self) + that = Ox.Element({}, self) .defaults({ addPlace: null, height: 256, @@ -187,13 +187,13 @@ Ox.ListMap = function(options, self) { } ]; - self.$toolbar = new Ox.Bar({ + self.$listToolbar = Ox.Bar({ size: 24 }); - self.$findElement = new Ox.FormElementGroup({ + self.$findElement = Ox.FormElementGroup({ elements: [ - self.$findSelect = new Ox.Select({ + self.$findSelect = Ox.Select({ items: [ {id: 'all', title: 'Find: All'}, {id: 'name', title: 'Find: Name'}, @@ -203,16 +203,16 @@ Ox.ListMap = function(options, self) { overlap: 'right', width: 128 }), - self.$findInput = new Ox.Input({ + self.$findInput = Ox.Input({ clear: true, width: 192 }) ] }) .css({float: 'right', margin: '4px'}) - .appendTo(self.$toolbar) + .appendTo(self.$listToolbar) - self.$list = new Ox.TextList({ + self.$list = Ox.TextList({ columns: self.columns, columnsRemovable: true, columnsVisible: true, @@ -233,19 +233,95 @@ Ox.ListMap = function(options, self) { select: selectItem }); - self.$statusbar = new Ox.Bar({ - size: 24 + self.$listStatusbar = Ox.Bar({ + size: 16 + }); + + self.$status = Ox.Element() + .css({paddingTop: '2px', margin: 'auto', fontSize: '9px', textAlign: 'center'}) + .html(self.options.places.length + ' Place' + (self.options.places.length == 1 ? '' : 's')) + .appendTo(self.$listStatusbar); + + self.$placeToolbar = Ox.Bar({ + size: 24 + }); + + self.$newPlaceButton = Ox.Button({ + id: 'newPlace', + title: 'New Place', + width: 80 + }) + .css({float: 'left', margin: '4px'}) + .appendTo(self.$placeToolbar); + + self.$placeFormItems = Ox.merge([ + Ox.Input({ + id: 'name', + label: 'Name', + labelWidth: 64, + width: 240 + }), + Ox.Input({ + id: 'geoname', + label: 'Geoname', + labelWidth: 64, + width: 240 + }), + Ox.ArrayInput({ + id: 'alternativeNames', + label: 'Alternative Names', + max: 10, + //sort: true, + values: [], + width: 240 + }), + ], ['Latitude', 'Longitude', 'South', 'West', 'North', 'East'].map(function(v) { + var key = ( + v == 'Latitude' ? 'lat' : v == 'Longitude' ? 'lng' : v + ).toLowerCase(), + max = ['Latitude', 'South', 'North'].indexOf(v) > -1 ? Ox.MAX_LATITUDE : 180; + return Ox.Input({ + decimals: 8, + label: v, + labelWidth: 80, + min: -max, + max: max, + type: 'float', + value: self.options.places[0][key].toFixed(8), + width: 240 }); + })); + + self.$placeForm = Ox.Form({ + items: self.$placeFormItems, + width: 240 + }) + .css({margin: '8px'}); - self.$status = new Ox.Element() - .css({paddingTop: '4px', margin: 'auto', textAlign: 'center'}) - .appendTo(self.$statusbar); + self.$placeStatusbar = Ox.Bar({ + size: 16 + }) + .addClass('OxVideoPlayer'); // fixme! + self.$savePlaceButton = Ox.Button({ + disabled: true, + id: 'savePlace', + title: 'check', + style: 'symbol', + tooltip: 'Save Place', + type: 'image', + //width: 80 + }) + .css({float: 'right'}) + .appendTo(self.$placeStatusbar); + + /* self.mapResize = [ Math.round(self.options.width * 0.25), Math.round(self.options.width * 0.5), Math.round(self.options.width * 0.75) ]; + */ if (Ox.isArray(self.options.places)) { init(self.options.places) @@ -266,15 +342,15 @@ Ox.ListMap = function(options, self) { function init(places) { //Ox.print('PLACES', places) - self.$map = new Ox.Map({ + self.$map = Ox.Map({ clickable: true, editable: true, findPlaceholder: 'Find on Map', height: self.options.height, places: places, - statusbar: true, + //statusbar: true, toolbar: true, - width: self.mapResize[1], + width: self.options.width - 514,//self.mapResize[1], zoombar: true }) .bindEvent({ @@ -290,31 +366,51 @@ Ox.ListMap = function(options, self) { selectplace: selectPlace }); that.$element.replaceWith( - that.$element = new Ox.SplitPanel({ + that.$element = Ox.SplitPanel({ elements: [ { - element: new Ox.SplitPanel({ + element: Ox.SplitPanel({ elements: [ { - element: self.$toolbar, + element: self.$listToolbar, size: 24 }, { element: self.$list }, { - element: self.$statusbar, - size: 24 + element: self.$listStatusbar, + size: 16 } ], orientation: 'vertical' - }) + }), + resizable: true, + resize: [256, 384, 512], + size: 256 }, { element: self.$map, - resizable: true, - resize: self.mapResize, - size: self.mapResize[1] + }, + { + collapsible: true, + element: Ox.SplitPanel({ + elements: [ + { + element: self.$placeToolbar, + size: 24 + }, + { + element: self.$placeForm + }, + { + element: self.$placeStatusbar, + size: 16 + } + ], + orientation: 'vertical' + }), + size: 256 } ], orientation: 'horizontal' diff --git a/source/Ox.js b/source/Ox.js index 1ed8ef24..c8d7d257 100644 --- a/source/Ox.js +++ b/source/Ox.js @@ -1,6 +1,6 @@ // vim: et:ts=4:sw=4:sts=4:ft=js -// OxJS (c) 2011 Ox2620, dual-licensed GPL/MIT, see http://oxjs.org for details +// OxJS (c) 2011 0x2620, dual-licensed GPL/MIT, see http://oxjs.org for details /* Some conventions: @@ -194,13 +194,18 @@ Ox.merge = function(arr) { /*@ Ox.sort Sorts an array, handling leading digits and ignoring capitalization + arr Array + fn Optional map function that returns the value for the array element > Ox.sort(['10', '9', 'B', 'a']) ['9', '10', 'a', 'B'] + > Ox.sort([{id: 0, name: '80 Days'}, {id: 1, name: '8 Women'}], function(v) {return v.name}); + [{id: 1, name: '8 Women'}, {id: 0, name: '80 Days'}] @*/ -Ox.sort = function(arr) { - var len, matches = {}, sort = {}; +Ox.sort = function(arr, fn) { + var len, matches = {}, sort = {}, + values = fn ? arr.map(fn) : arr; // find leading numbers - arr.forEach(function(val, i) { + values.forEach(function(val, i) { var match = /^\d+/(val); matches[val] = match ? match[0] : ''; }); @@ -209,7 +214,7 @@ Ox.sort = function(arr) { return val.length; })); // pad leading numbers, and make lower case - arr.forEach(function(val) { + values.forEach(function(val) { sort[val] = ( matches[val] ? Ox.pad(matches[val], len) + val.toString().substr(matches[val].length) : @@ -217,6 +222,8 @@ Ox.sort = function(arr) { ).toLowerCase(); }); return arr.sort(function(a, b) { + a = fn ? fn(a) : a; + b = fn ? fn(b) : b; var ret = 0; if (sort[a] < sort[b]) { ret = -1;