// vim: et:ts=4:sw=4:sts=4:ft=javascript 'use strict'; /*@ Ox.TextList <f:Ox.Element> TextList Object () -> <f> TextList Object (options) -> <f> TextList Object (options, self) -> <f> TextList Object options <o> Options object columns <[o]|[]> Columns # Fixme: There's probably more... addable <b> ... editable <b> ... format <f> ... id <s> ... removable <b> ... map <f> function that maps values to sort values operator <s> default sort operator title <s> ... titleImage <s> ... unformat <f> Applied before editing unique <b> If true, this column acts as unique id visible <b> ... width <n> ... columnsMovable <b|false> If true, columns can be re-ordered columnsRemovable <b|false> If true, columns are removable columnsResizable <b|false> If true, columns are resizable columnsVisible <b|false> If true, columns are visible columnWidth <[n]|[40, 800]> Minimum and maximum column width draggable <b|false> If true, items can be dragged id <s|''> items <f|null> function() {} {sort, range, keys, callback} or array keys <[s]|[]> Additional keys (apart from keys of visible columns) max <n|-1> Maximum number of items that can be selected (-1 for all) min <n|0> Minimum number of items that must be selected pageLength <n|100> Number of items per page scrollbarVisible <b|false> If true, the scrollbar is always visible selected <a|[]> sort <[]|[]> sortable <b|false> If true, elements can be re-ordered sums <[]|[]> Sums to be included in totals self <o> shared private variable @*/ // fixme: options.columnsMovable, but options.sortable ... pick one. Ox.TextList = function(options, self) { // fixme: rename to TableList // fixme: in columns, "operator" should be "sortOperator" self = self || {}; var that = Ox.Element({}, self) .defaults({ columns: [], columnsMovable: false, columnsRemovable: false, columnsResizable: false, columnsVisible: false, columnWidth: [40, 800], draggable: false, id: '', items: null, keys: [], max: -1, min: 0, pageLength: 100, scrollbarVisible: false, selected: [], sort: [], sortable: false, sums: [] }) .options(options || {}) .addClass('OxTextList') .bindEvent({ key_left: function() { var $element = that.$body.$element, scrollLeft = $element[0].scrollLeft - $element.width(); $element.animate({scrollLeft: scrollLeft}, 250); }, key_right: function() { var $element = that.$body.$element, scrollLeft = $element[0].scrollLeft + $element.width(); $element.animate({scrollLeft: scrollLeft}, 250); }, keys: find }); self.options.sort = self.options.sort.map(function(sort) { return Ox.isString(sort) ? { key: sort.replace(/^[\+\-]/, ''), operator: sort[0] == '-' ? '-' : '+' } : sort; }); self.options.columns.forEach(function(column) { // fixme: can this go into a generic ox.js function? // fixme: and can't these just remain undefined? if (Ox.isUndefined(column.align)) { column.align = 'left'; } if (Ox.isUndefined(column.clickable)) { column.clickable = false; } if (Ox.isUndefined(column.editable)) { column.editable = false; } if (Ox.isUndefined(column.unique)) { column.unique = false; } if (Ox.isUndefined(column.visible)) { column.visible = false; } if (column.unique) { self.unique = column.id; } }); if (Ox.isEmpty(self.options.sort)) { self.options.sort = [{ key: self.unique, operator: Ox.getObjectById(self.options.columns, self.unique).operator }]; } Ox.extend(self, { columnPositions: [], defaultColumnWidths: self.options.columns.map(function(column) { return column.defaultWidth || column.width; }), itemHeight: 16, page: 0, pageLength: 100, scrollLeft: 0, selectedColumn: getColumnIndexById(self.options.sort[0].key), visibleColumns: self.options.columns.filter(function(column) { return column.visible; }) }); // fixme: there might be a better way than passing both visible and position self.options.columns.forEach(function(column) { if (!Ox.isUndefined(column.position)) { self.visibleColumns[column.position] = column; } }); Ox.extend(self, { columnWidths: self.visibleColumns.map(function(column) { return column.width; }), pageHeight: self.options.pageLength * self.itemHeight }); self.format = {}; self.options.columns.forEach(function(column) { if (column.format) { self.format[column.id] = column.format; } }); // Head if (self.options.columnsVisible) { that.$bar = Ox.Bar({ orientation: 'horizontal', size: 16 }).appendTo(that); that.$head = Ox.Container() .addClass('OxHead') .css({ right: self.options.scrollbarVisible ? Ox.UI.SCROLLBAR_SIZE + 'px' : 0 }) .appendTo(that.$bar); that.$head.$content.addClass('OxTitles'); constructHead(); if (self.options.columnsRemovable) { that.$select = Ox.Select({ id: self.options.id + 'SelectColumns', items: self.options.columns.map(function(column) { return { disabled: column.removable === false, id: column.id, title: column.title }; }), max: -1, min: 1, type: 'image', value: Ox.map(self.options.columns, function(column) { return column.visible ? column.id : null; }) }) .bindEvent('change', changeColumns) .appendTo(that.$bar.$element); } } // Body that.$body = Ox.List({ construct: constructItem, draggable: self.options.draggable, id: self.options.id, itemHeight: 16, items: self.options.items, itemWidth: getItemWidth(), format: self.format, // fixme: not needed, happens in TextList keys: Ox.merge(self.visibleColumns.map(function(column) { return column.id; }), self.options.keys), max: self.options.max, min: self.options.min, pageLength: self.options.pageLength, paste: self.options.paste, orientation: 'vertical', selected: self.options.selected, sort: self.options.sort, sortable: self.options.sortable, sums: self.options.sums, type: 'text', unique: self.unique }, Ox.clone(self)) // pass event handler .addClass('OxBody') .css({ top: (self.options.columnsVisible ? 16 : 0) + 'px', overflowY: (self.options.scrollbarVisible ? 'scroll' : 'hidden') }) .scroll(function() { var scrollLeft = $(this).scrollLeft(); if (scrollLeft != self.scrollLeft) { self.scrollLeft = scrollLeft; that.$head && that.$head.scrollLeft(scrollLeft); } }) .bindEvent({ cancel: function(data) { Ox.Log('List', 'cancel edit', data); }, edit: function(data) { that.editCell(data.id, data.key); }, select: function() { self.options.selected = that.$body.options('selected'); } }) .appendTo(that); that.$body.$content.css({ width: getItemWidth() + 'px' }); //Ox.Log('List', 's.vC', self.visibleColumns) function addColumn(id) { //Ox.Log('List', 'addColumn', id); var column, ids, index = 0; Ox.forEach(self.options.columns, function(v) { if (v.visible) { index++; } else if (v.id == id) { column = v; return false; } }); column.visible = true; self.visibleColumns.splice(index, 0, column); self.columnWidths.splice(index, 0, column.width); that.$head.$content.empty(); constructHead(); that.$body.options({ keys: Ox.merge(self.visibleColumns.map(function(column) { return column.id; }), self.options.keys) }); that.$body.reloadPages(); } function changeColumns(data) { var add, ids = []; Ox.forEach(data.value, function(id) { var index = getColumnIndexById(id); if (!self.options.columns[index].visible) { addColumn(id); add = true; return false; } ids.push(id); }); if (!add) { Ox.forEach(self.visibleColumns, function(column) { if (ids.indexOf(column.id) == -1) { removeColumn(column.id); return false; } }); } triggerColumnChangeEvent(); } function clickColumn(id) { Ox.Log('List', 'clickColumn', id); var i = getColumnIndexById(id), isSelected = self.options.sort[0].key == self.options.columns[i].id; self.options.sort = [{ key: self.options.columns[i].id, operator: isSelected ? (self.options.sort[0].operator == '+' ? '-' : '+') : self.options.columns[i].operator, map: self.options.columns[i].map }] updateColumn(); // fixme: strangely, sorting the list blocks updating the column, // so we use a timeout for now setTimeout(function() { that.$body.options({sort: self.options.sort}); }, 10); that.gainFocus().triggerEvent('sort', { key: self.options.sort[0].key, operator: self.options.sort[0].operator }); } function constructHead() { self.$heads = []; self.$titles = []; self.$orderButtons = []; self.visibleColumns.forEach(function(column, i) { var $resize; self.$heads[i] = Ox.Element() .addClass('OxHeadCell OxColumn' + Ox.toTitleCase(column.id)) .css({width: self.columnWidths[i] - 5 + 'px'}) .appendTo(that.$head.$content.$element); // if sort operator is set, bind click event if (column.operator) { self.$heads[i].bindEvent({ anyclick: function() { clickColumn(column.id); } }); } // if columns are movable, bind drag events if (self.options.columnsMovable) { self.$heads[i].bindEvent({ dragstart: function(data) { dragstartColumn(column.id, data); }, drag: function(data) { dragColumn(column.id, data); }, dragpause: function(data) { dragpauseColumn(column.id, data); }, dragend: function(data) { dragendColumn(column.id, data); } }) } self.$titles[i] = Ox.Element() .addClass('OxTitle') .css({ width: self.columnWidths[i] - 9 + 'px', textAlign: column.align }) .appendTo(self.$heads[i]); if (column.titleImage) { self.$titles[i].append( $('<img>').attr({ src: Ox.UI.getImageURL('symbol' + Ox.toTitleCase(column.titleImage)) }) ) } else { self.$titles[i].html(column.title); } if (column.operator) { self.$orderButtons[i] = Ox.Button({ style: 'symbol', title: column.operator == '+' ? 'up' : 'down', type: 'image' }) .addClass('OxOrder') .css({marginTop: (column.operator == '+' ? 1 : -1) + 'px'}) .click(function() { $(this).parent().trigger('click'); }) .appendTo(self.$heads[i]); } $resize = Ox.Element() .addClass('OxResize') .appendTo(that.$head.$content.$element); $('<div>').appendTo($resize); $('<div>').addClass('OxCenter').appendTo($resize); $('<div>').appendTo($resize); // if columns are resizable, bind click and drag events if (self.options.columnsResizable && column.resizable !== false) { $resize.addClass('OxResizable') .bindEvent({ doubleclick: function(data) { resetColumn(column.id, data); }, dragstart: function(data) { dragstartResize(column.id, data); }, drag: function(data) { dragResize(column.id, data); }, dragend: function(data) { dragendResize(column.id, data); } }); } }); that.$head.$content.css({ width: (Ox.sum(self.columnWidths) + 2) + 'px' }); if (getColumnPositionById(self.options.columns[self.selectedColumn].id) > -1) { // fixme: save in var toggleSelected(self.options.columns[self.selectedColumn].id); self.$titles[getColumnPositionById(self.options.columns[self.selectedColumn].id)].css({ width: (self.options.columns[self.selectedColumn].width - 25) + 'px' }); } } function constructItem(data) { var $item = $('<div>') .addClass('OxTarget') .css({ width: getItemWidth(true) + 'px' }); self.visibleColumns.forEach(function(v, i) { var clickable = Ox.isBoolean(v.clickable) ? v.clickable : v.clickable(data), editable = Ox.isBoolean(v.editable) ? v.editable : v.editable(data), $cell; if (v.tooltip) { $cell = Ox.Element({ tooltip: function() { return self.options.selected.indexOf(data[self.unique]) > -1 ? (Ox.isString(v.tooltip) ? v.tooltip : v.tooltip(data)) : ''; } }); } else { // this is faster $cell = $('<div>'); } $cell.addClass( 'OxCell OxColumn' + Ox.toTitleCase(v.id) + (clickable ? ' OxClickable' : '') + (editable ? ' OxEditable' : '') ) .css({ width: (self.columnWidths[i] - (self.options.columnsVisible ? 9 : 8)) + 'px', borderRightWidth: (self.options.columnsVisible ? 1 : 0) + 'px', textAlign: v.align }) // if the column id is not in data, we're constructing an empty cell .html(v.id in data ? formatValue(v.id, data[v.id], data) : '') .appendTo($item); }); return $item; } function dragstartColumn(id, e) { self.drag = { columnOffsets: getColumnOffsets(), listOffset: that.$element.offset().left - that.$body.scrollLeft(), startPos: getColumnPositionById(id) } self.drag.stopPos = self.drag.startPos; $('.OxColumn' + Ox.toTitleCase(id)).css({opacity: 0.25}); self.drag.startPos > 0 && self.$heads[self.drag.startPos].prev().children().eq(2).css({opacity: 0.25}); self.$heads[self.drag.startPos].next().children().eq(0).css({opacity: 0.25}); self.$heads[self.drag.startPos].addClass('OxDrag').css({ // fixme: why does the class not work? cursor: 'move' }); } function dragColumn(id, e) { var listLeft = that.$element.offset().left, listRight = listLeft + that.$element.width(), pos = self.drag.stopPos; Ox.forEach(self.drag.columnOffsets, function(offset, i) { var x = self.drag.listOffset + offset + self.columnWidths[i] / 2; if (i < self.drag.startPos && e.clientX < x) { self.drag.stopPos = i; return false; } else if (i > self.drag.startPos && e.clientX > x) { self.drag.stopPos = i; } }); if (self.drag.stopPos != pos) { moveColumn(id, self.drag.stopPos); self.drag.columnOffsets = getColumnOffsets(); self.drag.startPos = self.drag.stopPos; ///* var left = self.drag.columnOffsets[self.drag.startPos], right = left + self.columnWidths[self.drag.startPos]; if (left < that.$body.scrollLeft() || right > that.$element.width()) { that.$body.scrollLeft( left < that.$body.scrollLeft() ? left : right - that.$element.width() ); self.drag.listOffset = that.$element.offset().left - that.$body.scrollLeft(); } //*/ } if (e.clientX < listLeft + 16 || e.clientX > listRight - 16) { if (!self.scrollInterval) { self.scrollInterval = setInterval(function() { that.$body.scrollLeft( that.$body.scrollLeft() + (e.clientX < listLeft + 16 ? -16 : 16) ); self.drag.listOffset = that.$element.offset().left - that.$body.scrollLeft(); }, 100); } } else if (self.scrollInterval) { clearInterval(self.scrollInterval); self.scrollInterval = 0; } } function dragpauseColumn(id, e) { } function dragendColumn(id, e) { var column = self.visibleColumns.splice(self.drag.stopPos, 1)[0], width = self.columnWidths.splice(self.drag.stopPos, 1)[0]; self.visibleColumns.splice(self.drag.stopPos, 0, column); self.columnWidths.splice(self.drag.stopPos, 0, width); that.$head.$content.empty(); constructHead(); $('.OxColumn' + Ox.toTitleCase(id)).css({opacity: 1}); self.$heads[self.drag.stopPos].removeClass('OxDrag').css({ cursor: 'pointer' }); that.$body.clearCache(); triggerColumnChangeEvent(); } function dragstartResize(id, e) { var pos = getColumnPositionById(id); self.drag = { startWidth: self.columnWidths[pos] }; } function dragResize(id, e) { var width = Ox.limit( self.drag.startWidth + e.clientDX, self.options.columnWidth[0], self.options.columnWidth[1] ); resizeColumn(id, width); } function dragendResize(id, e) { var pos = getColumnPositionById(id); // fixme: shouldn't this be resizecolumn? that.triggerEvent('columnresize', { id: id, width: self.columnWidths[pos] }); } function find(data) { // fixme: works only if items are an array var query = data.keys, sort = self.options.sort[0]; Ox.Log('List', 'QUERY', query) Ox.forEach(self.options.items, function(item, i) { var value = ( sort.map ? sort.map(item[sort.key]) : item[sort.key] ).toString().toLowerCase(); if (Ox.startsWith(value, query)) { that.$body.options({selected: [item[self.unique]]}); Ox.Log('List', 'QUERY', query, 'VALUE', value) return false; } }); } function formatValue(key, value, data) { // fixme: this may be obscure... // since the format of a value may depend on another value, // we pass all data as a second parameter to the supplied format function var format = self.format[key], formatFunction; // FIXME: this keeps null from ever reaching a format function! if (value === null) { value = ''; } else if (format) { if (Ox.isObject(format)) { value = ( /^color/.test(format.type.toLowerCase()) ? Ox.Theme : Ox )['format' + Ox.toTitleCase(format.type)].apply( this, Ox.merge([value], format.args || []) ); } else { value = format(value, data); } } else if (Ox.isArray(value)) { value = value.join(', '); } return value; } function getCell(id, key) { Ox.print('List', 'getCell', id, key) var $item = getItem(id); key = key || ''; // fixme: what is this? return $($item.find('.OxCell.OxColumn' + Ox.toTitleCase(key))[0]); } function getColumnOffsets() { return self.visibleColumns.map(function(column, i) { return Ox.sum(self.visibleColumns.map(function(column_, i_) { return i_ < i ? self.columnWidths[i_] : 0; })); }); } function getColumnIndexById(id) { return Ox.getIndexById(self.options.columns, id); } function getColumnPositionById(id) { return Ox.getIndexById(self.visibleColumns, id); } function getItem(id) { //Ox.Log('List', 'getItem', id) var $item = null; that.find('.OxItem').each(function() { var $this = $(this); if ($this.data('id') == id) { $item = $this; return false; } }); return $item; } function getItemWidth(cached) { // fixme: this gets called for every constructItem and is slooow // the proper way to fix this would be to find out how and when // that.$element.width() might change... which would probably // mean binding to every SplitPanel and window resize... // for now, use a cached value if (!cached) { self.cachedWidth = that.$element.width(); } else if (!self.cachedWidth || self.cachedWidthTime < +new Date() - 5000) { self.cachedWidth = that.$element.width(); self.cachedWidthTime = +new Date(); } return Math.max( Ox.sum(self.columnWidths), self.cachedWidth - (self.options.scrollbarVisible ? Ox.UI.SCROLLBAR_SIZE : 0) ); } function moveColumn(id, pos) { //Ox.Log('List', 'moveColumn', id, pos) var startPos = getColumnPositionById(id), stopPos = pos, startSelector = '.OxColumn' + Ox.toTitleCase(id), stopSelector = '.OxColumn' + Ox.toTitleCase(self.visibleColumns[stopPos].id), insert = startPos < stopPos ? 'insertAfter' : 'insertBefore', $column = $('.OxHeadCell' + startSelector), $resize = $column.next(); //Ox.Log('List', startSelector, insert, stopSelector) $column.detach()[insert](insert == 'insertAfter' ? $('.OxHeadCell' + stopSelector).next() : $('.OxHeadCell' + stopSelector)); $resize.detach().insertAfter($column); that.$body.find('.OxItem').each(function() { var $this = $(this); $this.children(startSelector).detach()[insert]( $this.children(stopSelector) ); }); var $head = self.$heads.splice(startPos, 1)[0], columnWidth = self.columnWidths.splice(startPos, 1)[0], visibleColumn = self.visibleColumns.splice(startPos, 1)[0]; self.$heads.splice(stopPos, 0, $head); self.columnWidths.splice(stopPos, 0, columnWidth); self.visibleColumns.splice(stopPos, 0, visibleColumn); var pos = getColumnPositionById(self.options.columns[self.selectedColumn].id); if (pos > -1) { that.find('.OxResize .OxSelected').removeClass('OxSelected'); pos > 0 && self.$heads[pos].prev().children().eq(2).addClass('OxSelected'); self.$heads[pos].next().children().eq(0).addClass('OxSelected'); if (pos == stopPos) { pos > 0 && self.$heads[pos].prev().children().eq(2).css({opacity: 0.25}); self.$heads[pos].next().children().eq(0).css({opacity: 0.25}); } } } function removeColumn(id) { //Ox.Log('List', 'removeColumn', id); var index = getColumnIndexById(id), itemWidth, position = getColumnPositionById(id), selector = '.OxColumn' + Ox.toTitleCase(id), $column = $('.OxHeadCell ' + selector), $order = $column.next(), $resize = $order.next(); self.options.columns[index].visible = false; self.visibleColumns.splice(position, 1); self.columnWidths.splice(position, 1); that.$head.$content.empty(); constructHead(); itemWidth = getItemWidth(); that.$body.find('.OxItem').each(function() { var $this = $(this); $this.children(selector).remove(); $this.css({width: itemWidth + 'px'}); }); that.$body.$content.css({ width: itemWidth + 'px' }); that.$body.options({ keys: Ox.merge(self.visibleColumns.map(function(column) { return column.id; }), self.options.keys) }); //that.$body.clearCache(); } function resetColumn(id) { var width = self.defaultColumnWidths[getColumnIndexById(id)]; resizeColumn(id, width); that.triggerEvent('columnresize', { id: id, width: width }); } function resizeColumn(id, width) { var i = getColumnIndexById(id), pos = getColumnPositionById(id); self.options.columns[i].width = width; self.columnWidths[pos] = width; if (self.options.columnsVisible) { that.$head.$content.css({ width: (Ox.sum(self.columnWidths) + 2) + 'px' }); self.$heads[pos].css({ width: width - 5 + 'px' }); self.$titles[pos].css({ width: width - 9 - (i == self.selectedColumn ? 16 : 0) + 'px' }); } that.find('.OxCell.OxColumn' + Ox.toTitleCase(self.options.columns[i].id)).css({ width: width - (self.options.columnsVisible ? 9 : 8) + 'px' }); setWidth(); } function setWidth() { var width = getItemWidth(); that.$body.$content.$element.find('.OxItem').css({ // fixme: can we avoid this lookup? width: width + 'px' }); that.$body.$content.css({ width: width + 'px' // fixme: check if scrollbar visible, and listen to resize/toggle event }); } function toggleSelected(id) { var pos = getColumnPositionById(id); if (pos > -1) { updateOrder(id); pos > 0 && self.$heads[pos].prev().children().eq(2).toggleClass('OxSelected'); self.$heads[pos].toggleClass('OxSelected'); self.$heads[pos].next().children().eq(0).toggleClass('OxSelected'); self.$titles[pos].css({ width: self.$titles[pos].width() + (self.$heads[pos].hasClass('OxSelected') ? -16 : 16) + 'px' }); } } function triggerColumnChangeEvent() { that.triggerEvent('columnchange', { ids: self.visibleColumns.map(function(column) { return column.id; }) }); } function updateColumn() { var columnId = self.options.columns[self.selectedColumn].id, isSelected = columnId == self.options.sort[0].key; if (self.options.columnsVisible) { if (isSelected) { updateOrder(columnId); } else { toggleSelected(columnId); self.selectedColumn = getColumnIndexById(self.options.sort[0].key); toggleSelected(self.options.columns[self.selectedColumn].id); } } } function updateOrder(id) { var operator = self.options.sort[0].operator, pos = getColumnPositionById(id); if (pos > -1) { self.$orderButtons[pos].options({ title: operator == '+' ? 'up' : 'down' }).css({ marginTop: (operator == '+' ? 1 : -1) + 'px' }); } } self.setOption = function(key, value) { //Ox.Log('List', '---------------------------- TextList setOption', key, value) if (key == 'items') { that.$body.options(key, value); } else if (key == 'paste') { that.$body.options(key, value); } else if (key == 'selected') { that.$body.options(key, value); } else if (key == 'sort') { updateColumn(); that.$body.options(key, value); } }; that.addItem = function(item) { /* self.options.items.push(item); that.$body.options({items: self.options.items}); //that.$body.options({selected: [item.id]}); */ } that.closePreview = function() { that.$body.closePreview(); return that; }; that.editCell = function(id, key, select) { Ox.Log('List', 'editCell', id, key) var $item = getItem(id), $cell = getCell(id, key), $input, html = $cell.html(), index = getColumnIndexById(key), column = self.options.columns[index], width = column.width - self.options.columnsVisible; $cell.empty() .addClass('OxEdit') .css({width: width + 'px'}); $input = Ox.Input({ autovalidate: column.input ? column.input.autovalidate : null, style: 'square', value: column.unformat ? column.unformat(html) : html, width: width }) .bind({ mousedown: function(e) { // keep mousedown from reaching list e.stopPropagation(); }, }) .bindEvent({ blur: submit, cancel: submit, submit: submit }) .appendTo($cell); // fixme: why do we need a timeout? setTimeout(function() { $input.focusInput(select); }, 0); function submit() { var value = $input.value(); $input.remove(); $cell.removeClass('OxEdit') .css({ // account for padding width: (width - 8) + 'px' }) .html(value); that.triggerEvent('submit', { id: id, key: key, value: value }); } } that.gainFocus = function() { that.$body.gainFocus(); return that; }; that.loseFocus = function() { that.$body.loseFocus(); return that; }; that.openPreview = function() { that.$body.openPreview(); return that; }; that.paste = function(data) { that.$body.paste(); return that; }; that.reloadList = function(stayAtPosition) { that.$body.reloadList(stayAtPosition); return that; }; that.resizeColumn = function(id, width) { resizeColumn(id, width); return that; } that.size = function() { Ox.Log('List', 'SIZE FUNCTION CALLED') setWidth(); that.$body.size(); } // fixme: deprecated that.sortList = function(key, operator) { Ox.Log('List', '$$$$ DEPRECATED $$$$') var isSelected = key == self.options.sort[0].key; self.options.sort = [{ key: key, operator: operator, map: self.options.columns[self.selectedColumn].sort }]; if (self.options.columnsVisible) { if (isSelected) { updateOrder(self.options.columns[self.selectedColumn].id); } else { toggleSelected(self.options.columns[self.selectedColumn].id); self.selectedColumn = getColumnIndexById(key); toggleSelected(self.options.columns[self.selectedColumn].id); } } // fixme: strangely, sorting the list blocks toggling the selection, // so we use a timeout for now setTimeout(function() { that.$body.options({sort: self.options.sort}); /* that.$body.sortList( self.options.sort[0].key, self.options.sort[0].operator, self.options.sort[0].map ); */ }, 10); return that; }; that.value = function(id, key, value) { // fixme: make this accept id, {k: v, ...} //Ox.Log('List', 'value', id, key, value) var $cell, $item = getItem(id); //column = self.options.columns[getColumnIndexById(key)]; if (arguments.length == 1) { return that.$body.value(id); } else if (arguments.length == 2) { return that.$body.value(id, key); } else { that.$body.value(id, key, value); if (key == self.unique) { // unique id has changed self.options.selected = self.options.selected.map(function(id_) { return id_ == id ? value : id_ }); id = value; } $cell = getCell(id, key); $cell && $cell.html(formatValue(key, value, that.$body.value(id))); if (key == self.options.sort[0].key) { // sort key has changed that.$body.sort(); } return that; } } return that; };