oxjs/source/Ox.UI/js/List/Ox.TextList.js

1039 lines
36 KiB
JavaScript
Raw Normal View History

2011-11-05 16:46:53 +00:00
'use strict';
2011-05-16 08:24:46 +00:00
/*@
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
2011-09-04 12:19:36 +00:00
# Fixme: There's probably more...
addable <b> ...
editable <b> ...
format <f> ...
id <s> ...
removable <b> ...
2011-05-24 12:50:16 +00:00
map <f> function that maps values to sort values
operator <s> default sort operator
title <s> ...
titleImage <s> ...
unformat <f> Applied before editing
2011-05-22 17:12:21 +00:00
unique <b> If true, this column acts as unique id
visible <b> ...
width <n> ...
2011-09-04 12:19:36 +00:00
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
2011-05-16 10:49:48 +00:00
id <s|''>
items <f|null> function() {} {sort, range, keys, callback} or array
keys <[s]|[]> Additional keys (apart from keys of visible columns)
2011-09-03 23:04:18 +00:00
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
2011-09-04 12:19:36 +00:00
pageLength <n|100> Number of items per page
scrollbarVisible <b|false> If true, the scrollbar is always visible
2011-05-16 10:49:48 +00:00
selected <a|[]>
2012-04-04 07:06:55 +00:00
sort <[]|[]>
2011-09-03 23:04:18 +00:00
sortable <b|false> If true, elements can be re-ordered
2012-04-04 07:06:55 +00:00
sums <[]|[]> Sums to be included in totals
2011-05-16 08:24:46 +00:00
self <o> shared private variable
@*/
2011-09-04 12:19:36 +00:00
// fixme: options.columnsMovable, but options.sortable ... pick one.
2011-04-22 22:03:10 +00:00
Ox.TextList = function(options, self) {
// fixme: rename to TableList
2011-10-09 21:13:16 +00:00
// fixme: in columns, "operator" should be "sortOperator"
2011-04-22 22:03:10 +00:00
self = self || {};
var that = Ox.Element({}, self)
2011-04-22 22:03:10 +00:00
.defaults({
columns: [],
columnsMovable: false,
columnsRemovable: false,
columnsResizable: false,
columnsVisible: false,
columnWidth: [40, 800],
draggable: false,
2011-04-22 22:03:10 +00:00
id: '',
2011-11-30 14:56:01 +00:00
items: null,
keys: [],
2011-04-22 22:03:10 +00:00
max: -1,
min: 0,
pageLength: 100,
scrollbarVisible: false,
selected: [],
2011-09-03 23:04:18 +00:00
sort: [],
2012-04-04 07:06:55 +00:00
sortable: false,
sums: []
2011-04-22 22:03:10 +00:00
})
.options(options || {})
.addClass('OxTextList')
.bindEvent({
key_left: function() {
2011-05-24 20:22:18 +00:00
var $element = that.$body.$element,
scrollLeft = $element[0].scrollLeft - $element.width();
$element.animate({scrollLeft: scrollLeft}, 250);
},
key_right: function() {
2011-05-24 20:22:18 +00:00
var $element = that.$body.$element,
scrollLeft = $element[0].scrollLeft + $element.width();
$element.animate({scrollLeft: scrollLeft}, 250);
},
keys: find
});
2011-04-22 22:03:10 +00:00
2012-04-04 07:06:55 +00:00
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?
2011-04-22 22:03:10 +00:00
// fixme: and can't these just remain undefined?
if (Ox.isUndefined(column.align)) {
column.align = 'left';
2011-04-22 22:03:10 +00:00
}
if (Ox.isUndefined(column.clickable)) {
column.clickable = false;
2011-04-22 22:03:10 +00:00
}
if (Ox.isUndefined(column.editable)) {
column.editable = false;
2011-04-22 22:03:10 +00:00
}
if (Ox.isUndefined(column.unique)) {
column.unique = false;
2011-04-22 22:03:10 +00:00
}
if (Ox.isUndefined(column.visible)) {
column.visible = false;
2011-04-22 22:03:10 +00:00
}
if (column.unique) {
self.unique = column.id;
2011-04-22 22:03:10 +00:00
}
});
if (Ox.isEmpty(self.options.sort)) {
self.options.sort = [{
key: self.unique,
operator: Ox.getObjectById(self.options.columns, self.unique).operator
}];
}
Ox.extend(self, {
2011-04-22 22:03:10 +00:00
columnPositions: [],
defaultColumnWidths: self.options.columns.map(function(column) {
return column.defaultWidth || column.width;
2011-04-22 22:03:10 +00:00
}),
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;
2011-04-22 22:03:10 +00:00
})
});
// 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;
2011-04-22 22:03:10 +00:00
}
});
Ox.extend(self, {
columnWidths: self.visibleColumns.map(function(column) {
return column.width;
2011-04-22 22:03:10 +00:00
}),
pageHeight: self.options.pageLength * self.itemHeight
});
self.format = {};
self.options.columns.forEach(function(column) {
if (column.format) {
self.format[column.id] = column.format;
2011-04-22 22:03:10 +00:00
}
});
// Head
if (self.options.columnsVisible) {
that.$bar = Ox.Bar({
2011-04-22 22:03:10 +00:00
orientation: 'horizontal',
size: 16
}).appendTo(that);
that.$head = Ox.Container()
2011-04-22 22:03:10 +00:00
.addClass('OxHead')
.css({
right: self.options.scrollbarVisible
? Ox.UI.SCROLLBAR_SIZE + 'px' : 0
2011-04-22 22:03:10 +00:00
})
.appendTo(that.$bar);
that.$head.$content.addClass('OxTitles');
constructHead();
if (self.options.columnsRemovable) {
that.$select = Ox.Select({
2011-04-22 22:03:10 +00:00
id: self.options.id + 'SelectColumns',
items: self.options.columns.map(function(column) {
2011-04-22 22:03:10 +00:00
return {
disabled: column.removable === false,
id: column.id,
title: column.title
};
2011-04-22 22:03:10 +00:00
}),
max: -1,
min: 1,
2011-12-23 09:54:20 +00:00
type: 'image',
value: Ox.map(self.options.columns, function(column) {
return column.visible ? column.id : null;
})
2011-04-22 22:03:10 +00:00
})
.bindEvent('change', changeColumns)
.appendTo(that.$bar.$element);
}
}
// Body
that.$body = Ox.List({
2011-04-22 22:03:10 +00:00
construct: constructItem,
draggable: self.options.draggable,
2011-04-22 22:03:10 +00:00
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),
2011-04-22 22:03:10 +00:00
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,
2012-04-04 07:06:55 +00:00
sums: self.options.sums,
2011-04-22 22:03:10 +00:00
type: 'text',
unique: self.unique
2011-09-28 17:31:35 +00:00
}, Ox.clone(self)) // pass event handler
2011-04-22 22:03:10 +00:00
.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({
2011-06-15 14:36:01 +00:00
cancel: function(data) {
2011-11-04 15:54:28 +00:00
Ox.Log('List', 'cancel edit', data);
2011-06-15 14:36:01 +00:00
},
2011-09-04 12:19:36 +00:00
edit: function(data) {
that.editCell(data.id, data.key);
},
2011-08-28 19:50:06 +00:00
select: function() {
self.options.selected = that.$body.options('selected');
2011-04-22 22:03:10 +00:00
}
})
.appendTo(that);
2011-09-04 12:19:36 +00:00
2011-04-22 22:03:10 +00:00
that.$body.$content.css({
width: getItemWidth() + 'px'
});
2011-11-04 15:54:28 +00:00
//Ox.Log('List', 's.vC', self.visibleColumns)
2011-04-22 22:03:10 +00:00
function addColumn(id) {
2011-11-04 15:54:28 +00:00
//Ox.Log('List', 'addColumn', id);
2011-04-22 22:03:10 +00:00
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)
2011-04-22 22:03:10 +00:00
});
that.$body.reloadPages();
}
function changeColumns(data) {
2011-04-22 22:03:10 +00:00
var add,
ids = [];
2011-12-23 09:54:20 +00:00
Ox.forEach(data.value, function(id) {
var index = getColumnIndexById(id);
2011-04-22 22:03:10 +00:00
if (!self.options.columns[index].visible) {
2011-12-23 09:54:20 +00:00
addColumn(id);
2011-04-22 22:03:10 +00:00
add = true;
return false;
}
2011-12-23 09:54:20 +00:00
ids.push(id);
2011-04-22 22:03:10 +00:00
});
if (!add) {
Ox.forEach(self.visibleColumns, function(column) {
if (ids.indexOf(column.id) == -1) {
removeColumn(column.id);
return false;
}
});
}
triggerColumnChangeEvent();
}
function clickColumn(id) {
2011-11-04 15:54:28 +00:00
Ox.Log('List', 'clickColumn', id);
2011-04-22 22:03:10 +00:00
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});
2011-08-18 07:55:39 +00:00
}, 10);
that.gainFocus().triggerEvent('sort', {
2011-08-18 07:55:39 +00:00
key: self.options.sort[0].key,
operator: self.options.sort[0].operator
});
2011-04-22 22:03:10 +00:00
}
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'})
2011-05-22 17:12:21 +00:00
.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);
2011-05-22 17:12:21 +00:00
}
});
}
// if columns are movable, bind drag events
if (self.options.columnsMovable) {
self.$heads[i].bindEvent({
2011-09-17 11:49:29 +00:00
dragstart: function(data) {
dragstartColumn(column.id, data);
2011-04-22 22:03:10 +00:00
},
2011-09-17 11:49:29 +00:00
drag: function(data) {
dragColumn(column.id, data);
2011-04-22 22:03:10 +00:00
},
2011-09-17 11:49:29 +00:00
dragpause: function(data) {
dragpauseColumn(column.id, data);
},
2011-09-17 11:49:29 +00:00
dragend: function(data) {
dragendColumn(column.id, data);
2011-04-22 22:03:10 +00:00
}
})
2011-05-22 17:12:21 +00:00
}
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()
2011-04-22 22:03:10 +00:00
.addClass('OxResize')
.appendTo(that.$head.$content.$element);
$('<div>').appendTo($resize);
$('<div>').addClass('OxCenter').appendTo($resize);
$('<div>').appendTo($resize);
2011-05-22 17:12:21 +00:00
// if columns are resizable, bind click and drag events
if (self.options.columnsResizable && column.resizable !== false) {
2011-04-22 22:03:10 +00:00
$resize.addClass('OxResizable')
.bindEvent({
2011-09-17 11:49:29 +00:00
doubleclick: function(data) {
resetColumn(column.id, data);
2011-04-22 22:03:10 +00:00
},
2011-09-17 11:49:29 +00:00
dragstart: function(data) {
dragstartResize(column.id, data);
2011-04-22 22:03:10 +00:00
},
2011-09-17 11:49:29 +00:00
drag: function(data) {
dragResize(column.id, data);
2011-04-22 22:03:10 +00:00
},
2011-09-17 11:49:29 +00:00
dragend: function(data) {
dragendResize(column.id, data);
2011-04-22 22:03:10 +00:00
}
});
}
});
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({
2011-04-22 22:03:10 +00:00
width: (self.options.columns[self.selectedColumn].width - 25) + 'px'
});
}
}
function constructItem(data) {
var $item = $('<div>')
.addClass('OxTarget')
2011-04-22 22:03:10 +00:00
.css({
width: getItemWidth(true) + 'px'
2011-04-22 22:03:10 +00:00
});
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) {
2011-08-28 19:50:06 +00:00
$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);
2011-04-22 22:03:10 +00:00
});
return $item;
}
function dragstartColumn(id, e) {
self.drag = {
columnOffsets: getColumnOffsets(),
listOffset: that.$element.offset().left - that.$body.scrollLeft(),
2011-04-22 22:03:10 +00:00
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?
2011-04-22 22:03:10 +00:00
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) {
2011-04-22 22:03:10 +00:00
self.drag.stopPos = i;
return false;
} else if (i > self.drag.startPos && e.clientX > x) {
2011-04-22 22:03:10 +00:00
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;
2011-04-22 22:03:10 +00:00
}
}
function dragpauseColumn(id, e) {
}
2011-04-22 22:03:10 +00:00
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({
2011-04-22 22:03:10 +00:00
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,
2011-04-22 22:03:10 +00:00
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?
2011-04-22 22:03:10 +00:00
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];
2011-11-04 15:54:28 +00:00
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]
2011-05-24 14:51:40 +00:00
).toString().toLowerCase();
if (Ox.startsWith(value, query)) {
that.$body.options({selected: [item[self.unique]]});
2011-11-04 15:54:28 +00:00
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
2011-10-26 14:52:03 +00:00
var format = self.format[key], formatFunction;
2012-03-27 09:18:01 +00:00
// FIXME: this keeps null from ever reaching a format function!
if (value === null) {
value = '';
} else if (format) {
2011-10-26 14:52:03 +00:00
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;
}
2011-04-22 22:03:10 +00:00
function getCell(id, key) {
var $item = getItem(id);
key = key || ''; // fixme: what is this?
2011-04-22 22:03:10 +00:00
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;
}));
});
}
2011-04-22 22:03:10 +00:00
function getColumnIndexById(id) {
return Ox.getIndexById(self.options.columns, id);
2011-04-22 22:03:10 +00:00
}
function getColumnPositionById(id) {
return Ox.getIndexById(self.visibleColumns, id);
2011-04-22 22:03:10 +00:00
}
function getItem(id) {
2011-11-04 15:54:28 +00:00
//Ox.Log('List', 'getItem', id)
2011-04-22 22:03:10 +00:00
var $item = null;
that.find('.OxItem').each(function() {
2011-11-05 17:27:11 +00:00
var $this = $(this);
if ($this.data('id') == id) {
$item = $this;
2011-04-22 22:03:10 +00:00
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();
}
2011-04-22 22:03:10 +00:00
return Math.max(
Ox.sum(self.columnWidths),
self.cachedWidth -
2011-04-22 22:03:10 +00:00
(self.options.scrollbarVisible ? Ox.UI.SCROLLBAR_SIZE : 0)
);
}
function moveColumn(id, pos) {
2011-11-04 15:54:28 +00:00
//Ox.Log('List', 'moveColumn', id, pos)
2011-04-22 22:03:10 +00:00
var startPos = getColumnPositionById(id),
stopPos = pos,
startSelector = '.OxColumn' + Ox.toTitleCase(id),
stopSelector = '.OxColumn' + Ox.toTitleCase(self.visibleColumns[stopPos].id),
2011-11-06 00:19:27 +00:00
insert = startPos < stopPos ? 'insertAfter' : 'insertBefore',
$column = $('.OxHeadCell' + startSelector),
$resize = $column.next();
2011-11-04 15:54:28 +00:00
//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)
);
2011-04-22 22:03:10 +00:00
});
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});
}
}
2011-04-22 22:03:10 +00:00
}
function removeColumn(id) {
2011-11-04 15:54:28 +00:00
//Ox.Log('List', 'removeColumn', id);
var index = getColumnIndexById(id),
2011-04-22 22:03:10 +00:00
itemWidth,
position = getColumnPositionById(id),
selector = '.OxColumn' + Ox.toTitleCase(id),
$column = $('.OxHeadCell ' + selector),
2011-04-22 22:03:10 +00:00
$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'});
2011-04-22 22:03:10 +00:00
});
that.$body.$content.css({
width: itemWidth + 'px'
});
that.$body.options({
keys: Ox.merge(self.visibleColumns.map(function(column) {
return column.id;
}), self.options.keys)
2011-04-22 22:03:10 +00:00
});
//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;
2011-11-03 15:42:41 +00:00
if (self.options.columnsVisible) {
2011-04-22 22:03:10 +00:00
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'
2011-04-22 22:03:10 +00:00
});
}
that.find('.OxCell.OxColumn' + Ox.toTitleCase(self.options.columns[i].id)).css({
width: width - (self.options.columnsVisible ? 9 : 8) + 'px'
2011-04-22 22:03:10 +00:00
});
setWidth();
}
function setWidth() {
var width = getItemWidth();
that.$body.$content.$element.find('.OxItem').css({ // fixme: can we avoid this lookup?
2011-04-22 22:03:10 +00:00
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'
2011-04-22 22:03:10 +00:00
});
}
}
function triggerColumnChangeEvent() {
that.triggerEvent('columnchange', {
ids: self.visibleColumns.map(function(column) {
return column.id;
2011-04-22 22:03:10 +00:00
})
});
}
function updateColumn() {
2011-11-05 18:05:14 +00:00
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);
}
2011-11-03 15:42:41 +00:00
}
}
2011-04-22 22:03:10 +00:00
function updateOrder(id) {
var operator = self.options.sort[0].operator,
pos = getColumnPositionById(id);
2011-08-18 07:55:39 +00:00
if (pos > -1) {
self.$orderButtons[pos].options({
title: operator == '+' ? 'up' : 'down'
}).css({
marginTop: (operator == '+' ? 1 : -1) + 'px'
});
2011-08-18 07:55:39 +00:00
}
2011-04-22 22:03:10 +00:00
}
2011-04-29 12:40:51 +00:00
self.setOption = function(key, value) {
2011-11-04 15:54:28 +00:00
//Ox.Log('List', '---------------------------- TextList setOption', key, value)
2011-04-22 22:03:10 +00:00
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);
2011-04-22 22:03:10 +00:00
}
};
2011-05-24 10:44:34 +00:00
that.addItem = function(item) {
/*
self.options.items.push(item);
that.$body.options({items: self.options.items});
//that.$body.options({selected: [item.id]});
*/
}
2012-05-21 10:38:18 +00:00
/*@
closePreivew <f> closePreview
@*/
that.closePreview = function() {
that.$body.closePreview();
return that;
};
2012-05-21 10:38:18 +00:00
/*@
editCell <f> editCell
(id, key, select) -> <u> edit cell
@*/
that.editCell = function(id, key, select) {
2011-11-04 15:54:28 +00:00
Ox.Log('List', 'editCell', id, key)
2011-04-22 22:03:10 +00:00
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({
2011-04-22 22:03:10 +00:00
autovalidate: column.input ? column.input.autovalidate : null,
style: 'square',
value: column.unformat ? column.unformat(html) : html,
2011-04-22 22:03:10 +00:00
width: width
})
.bind({
mousedown: function(e) {
// keep mousedown from reaching list
e.stopPropagation();
2012-01-27 18:28:44 +00:00
},
2011-04-22 22:03:10 +00:00
})
.bindEvent({
blur: submit,
2012-01-27 18:28:44 +00:00
cancel: submit,
submit: submit
2011-04-22 22:03:10 +00:00
})
.appendTo($cell);
// fixme: why do we need a timeout?
setTimeout(function() {
$input.focusInput(select);
}, 0);
2011-04-22 22:03:10 +00:00
function submit() {
var value = $input.value();
2012-01-27 18:28:44 +00:00
$input.remove();
2011-04-22 22:03:10 +00:00
$cell.removeClass('OxEdit')
.css({
// account for padding
2011-04-22 22:03:10 +00:00
width: (width - 8) + 'px'
})
.html(value);
2011-04-22 22:03:10 +00:00
that.triggerEvent('submit', {
id: id,
key: key,
value: value
});
}
}
2012-05-21 10:38:18 +00:00
/*@
gainFocus <f> gainFocus
@*/
2011-04-22 22:03:10 +00:00
that.gainFocus = function() {
that.$body.gainFocus();
return that;
};
2012-05-21 10:38:18 +00:00
/*@
hasFocus <f> hasFocus
@*/
that.hasFocus = function() {
return that.$body.hasFocus();
};
2012-05-21 10:38:18 +00:00
/*@
loseFocus <f> loseFocus
@*/
2011-04-22 22:03:10 +00:00
that.loseFocus = function() {
that.$body.loseFocus();
return that;
};
2012-05-21 10:38:18 +00:00
/*@
openPreview <f> openPreview
@*/
that.openPreview = function() {
that.$body.openPreview();
return that;
};
2011-04-22 22:03:10 +00:00
2012-05-21 10:38:18 +00:00
/*@
paste <f> paste
(data) -> <o> paste data
@*/
2011-04-22 22:03:10 +00:00
that.paste = function(data) {
that.$body.paste();
return that;
};
2012-05-21 10:38:18 +00:00
/*@
reloadList <f> reloadList
(stayAtPosition) -> <o> reload list
@*/
that.reloadList = function(stayAtPosition) {
that.$body.reloadList(stayAtPosition);
2011-04-22 22:03:10 +00:00
return that;
};
2012-05-21 10:38:18 +00:00
/*@
resizeColumn <f> resizeColumn
(id, width) -> <o> resize column id to width
@*/
2011-04-22 22:03:10 +00:00
that.resizeColumn = function(id, width) {
resizeColumn(id, width);
return that;
}
2012-05-21 10:38:18 +00:00
/*@
size <f> size
@*/
2011-04-22 22:03:10 +00:00
that.size = function() {
setWidth();
that.$body.size();
}
// fixme: deprecated
2011-04-22 22:03:10 +00:00
that.sortList = function(key, operator) {
2011-11-04 15:54:28 +00:00
Ox.Log('List', '$$$$ DEPRECATED $$$$')
2011-04-22 22:03:10 +00:00
var isSelected = key == self.options.sort[0].key;
2011-05-24 12:50:16 +00:00
self.options.sort = [{
key: key,
operator: operator,
map: self.options.columns[self.selectedColumn].sort
}];
2011-04-22 22:03:10 +00:00
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);
}
}
2011-05-22 17:12:21 +00:00
// 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});
/*
2011-05-22 17:12:21 +00:00
that.$body.sortList(
self.options.sort[0].key,
self.options.sort[0].operator,
2011-05-24 12:50:16 +00:00
self.options.sort[0].map
2011-05-22 17:12:21 +00:00
);
*/
2011-05-22 17:12:21 +00:00
}, 10);
2011-04-22 22:03:10 +00:00
return that;
};
2012-05-21 10:38:18 +00:00
/*@
value <f> value
(id) -> get values of row id
(id, key) -> get value of cell id, key
(id, key, value) -> set id, key to value
@*/
2011-04-22 22:03:10 +00:00
that.value = function(id, key, value) {
// fixme: make this accept id, {k: v, ...}
2011-11-04 15:54:28 +00:00
//Ox.Log('List', 'value', id, key, value)
2011-08-28 19:50:06 +00:00
var $cell,
$item = getItem(id);
//column = self.options.columns[getColumnIndexById(key)];
2011-04-22 22:03:10 +00:00
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);
2011-09-28 17:31:35 +00:00
if (key == self.unique) {
// unique id has changed
self.options.selected = self.options.selected.map(function(id_) {
return id_ == id ? value : id_
});
id = value;
}
2011-08-28 19:50:06 +00:00
$cell = getCell(id, key);
2011-11-03 15:42:41 +00:00
$cell && $cell.html(formatValue(key, value, that.$body.value(id)));
2011-05-24 14:51:40 +00:00
if (key == self.options.sort[0].key) {
2011-09-28 17:31:35 +00:00
// sort key has changed
2011-05-24 14:51:40 +00:00
that.$body.sort();
}
2011-04-22 22:03:10 +00:00
return that;
}
}
return that;
};