962 lines
34 KiB
JavaScript
962 lines
34 KiB
JavaScript
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
|
|
|
/*@
|
|
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> ...
|
|
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 <a|[]>
|
|
sortable <b|false> If true, elements can be re-ordered
|
|
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, // function() {} {sort, range, keys, callback} or array
|
|
keys: [],
|
|
max: -1,
|
|
min: 0,
|
|
pageLength: 100,
|
|
scrollbarVisible: false,
|
|
selected: [],
|
|
sort: [],
|
|
sortable: false
|
|
})
|
|
.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.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;
|
|
}
|
|
});
|
|
|
|
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 {
|
|
checked: column.visible,
|
|
disabled: column.removable === false,
|
|
id: column.id,
|
|
title: column.title
|
|
};
|
|
}),
|
|
max: -1,
|
|
min: 1,
|
|
type: 'image'
|
|
})
|
|
.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,
|
|
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.print('cancel edit', data);
|
|
},
|
|
edit: function(data) {
|
|
that.editCell(data.id, data.key);
|
|
},
|
|
init: function(data) {
|
|
// fixme: why does this never reach?
|
|
//Ox.print('INIT????')
|
|
//that.triggerEvent('init', data);
|
|
},
|
|
select: function() {
|
|
self.options.selected = that.$body.options('selected');
|
|
}
|
|
})
|
|
.appendTo(that);
|
|
|
|
that.$body.$content.css({
|
|
width: getItemWidth() + 'px'
|
|
});
|
|
|
|
//Ox.print('s.vC', self.visibleColumns)
|
|
|
|
function addColumn(id) {
|
|
//Ox.print('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.selected, function(column) {
|
|
var index = getColumnIndexById(column.id);
|
|
if (!self.options.columns[index].visible) {
|
|
addColumn(column.id);
|
|
add = true;
|
|
return false;
|
|
}
|
|
ids.push(column.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.print('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
|
|
})
|
|
.html(column.title)
|
|
.appendTo(self.$heads[i]);
|
|
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) {
|
|
$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.print('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.print('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;
|
|
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('getCell', id, key)
|
|
var $item = getItem(id);
|
|
key = key || '';
|
|
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.getPositionById(self.options.columns, id);
|
|
}
|
|
|
|
function getColumnPositionById(id) {
|
|
return Ox.getPositionById(self.visibleColumns, id);
|
|
}
|
|
|
|
function getItem(id) {
|
|
//Ox.print('getItem', id)
|
|
var $item = null;
|
|
that.find('.OxItem').each(function() {
|
|
$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.print('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.print(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.$element.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.print('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.$element.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.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.print('---------------------------- 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.closePreview = function() {
|
|
that.$body.closePreview();
|
|
return that;
|
|
};
|
|
|
|
that.addItem = function(item) {
|
|
/*
|
|
self.options.items.push(item);
|
|
that.$body.options({items: self.options.items});
|
|
//that.$body.options({selected: [item.id]});
|
|
*/
|
|
}
|
|
|
|
that.editCell = function(id, key) {
|
|
Ox.print('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: html,
|
|
width: width
|
|
})
|
|
.bind({
|
|
blur: submit,
|
|
mousedown: function(e) {
|
|
// keep mousedown from reaching list
|
|
e.stopPropagation();
|
|
}
|
|
})
|
|
.bindEvent({
|
|
blur: submit,
|
|
})
|
|
.appendTo($cell);
|
|
//.focusInput();
|
|
setTimeout($input.focusInput, 0); // fixme: strange
|
|
function submit() {
|
|
var value = $input.value();
|
|
//$input.loseFocus().remove();
|
|
// fixme: leaky, inputs remain in focus stack
|
|
$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.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.print('SIZE FUNCTION CALLED')
|
|
setWidth();
|
|
that.$body.size();
|
|
}
|
|
|
|
// fixme: deprecated
|
|
that.sortList = function(key, operator) {
|
|
Ox.print('$$$$ 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.print('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;
|
|
|
|
};
|