remove unneeded Ox. prefix from path and file names
This commit is contained in:
parent
4138e4e558
commit
51696562f1
1365 changed files with 43 additions and 43 deletions
285
source/UI/js/List/Chart.js
vendored
Normal file
285
source/UI/js/List/Chart.js
vendored
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Chart <f> Bar Chart
|
||||
options <o> Options
|
||||
color <[n]|[[n]]|[128, 128, 128]> Bar color
|
||||
data <o> {k: v, ...} or {k: {k: v, ...}, ...}
|
||||
formatKey <f|null> Format function for keys
|
||||
keyAlign <s|'right'> Alignment of keys
|
||||
keyWidth <n|128> Width of keys
|
||||
limit <n|0> Number of items, or 0 for all
|
||||
rows <n|1> undocumented
|
||||
sort <o|{key: 'value', operator: '-'}> Sort
|
||||
title <s|''> Chart title
|
||||
width <n|512> Chart width
|
||||
self <o> shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Chart object
|
||||
@*/
|
||||
|
||||
Ox.Chart = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
color: [128, 128, 128],
|
||||
data: {},
|
||||
formatKey: null,
|
||||
keyAlign: 'right',
|
||||
keyWidth: 128,
|
||||
limit: 0,
|
||||
rows: 1,
|
||||
sort: {key: 'value', operator: '-'},
|
||||
sortKey: null,
|
||||
title: '',
|
||||
width: 512
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
width: function() {
|
||||
self.$chart.empty();
|
||||
renderChart();
|
||||
}
|
||||
})
|
||||
.addClass('OxChart');
|
||||
|
||||
self.valueWidth = self.options.width - self.options.keyWidth;
|
||||
|
||||
self.keys = Object.keys(self.options.data);
|
||||
if (Ox.isObject(self.options.data[self.keys[0]])) {
|
||||
if (Ox.isUndefined(options.color)) {
|
||||
self.options.color = [
|
||||
[192, 64, 64], [ 64, 192, 64], [ 64, 64, 192],
|
||||
[192, 192, 64], [ 64, 192, 192], [192, 64, 192],
|
||||
[192, 128, 64], [ 64, 192, 128], [128, 64, 192],
|
||||
[192, 64, 128], [128, 192, 64], [ 64, 128, 192]
|
||||
];
|
||||
}
|
||||
self.subData = {};
|
||||
}
|
||||
|
||||
self.sort = {};
|
||||
self.totals = {};
|
||||
Ox.forEach(self.options.data, function(value, key) {
|
||||
self.totals[key] = self.subData ? Ox.sum(value) : value;
|
||||
if (self.subData) {
|
||||
Object.keys(value).forEach(function(subKey) {
|
||||
self.subData[subKey] = (self.subData[subKey] || 0) + value[subKey];
|
||||
});
|
||||
}
|
||||
self.sort[key] = key.replace(/(\d+)/g, function(number) {
|
||||
return Ox.pad(parseInt(number, 10), 16);
|
||||
});
|
||||
});
|
||||
self.max = Ox.max(self.totals);
|
||||
self.sum = Ox.sum(self.totals);
|
||||
|
||||
if (self.subData) {
|
||||
Ox.forEach(self.subData, function(subValue, subKey) {
|
||||
self.sort[subKey] = subKey.replace(/(\d+)/g, function(number) {
|
||||
return Ox.pad(parseInt(number, 10), 16);
|
||||
});
|
||||
});
|
||||
self.subKeys = Object.keys(self.subData).sort(function(a, b) {
|
||||
var aValue = self.subData[a],
|
||||
bValue = self.subData[b];
|
||||
return a === '' ? 1
|
||||
: b === '' ? -1
|
||||
: self.sort[a] < self.sort[b] ? -1
|
||||
: self.sort[a] > self.sort[b] ? 1
|
||||
: 0;
|
||||
});
|
||||
}
|
||||
|
||||
self.items = self.keys.map(function(key) {
|
||||
return {
|
||||
key: key,
|
||||
keySort: self.sort[key],
|
||||
value: self.options.data[key],
|
||||
valueSort: self.subData ? self.totals[key] : self.options.data[key]
|
||||
};
|
||||
});
|
||||
self.sortBy = self.options.sort.key == 'key'
|
||||
? [
|
||||
{key: 'keySort', operator: self.options.sort.operator}
|
||||
]
|
||||
: [
|
||||
{key: 'valueSort', operator: self.options.sort.operator},
|
||||
{key: 'keySort', operator: '+'}
|
||||
]
|
||||
if (self.options.limit) {
|
||||
self.items = Ox.sortBy(
|
||||
self.items, self.sortBy
|
||||
).slice(0, self.options.limit);
|
||||
self.max = Ox.max(self.items.map(function(item) {
|
||||
return self.subData ? Ox.sum(item.value) : item.value;
|
||||
}));
|
||||
}
|
||||
self.max = self.max || 1;
|
||||
|
||||
if (self.options.rows == 2) {
|
||||
self.row = 0;
|
||||
}
|
||||
|
||||
self.$title = Ox.Bar({size: 16})
|
||||
.append(
|
||||
$('<div>')
|
||||
.css({margin: '1px 0 0 4px'})
|
||||
.html(self.options.title)
|
||||
)
|
||||
.appendTo(that);
|
||||
|
||||
self.$chart = $('<div>')
|
||||
.css({position: 'absolute', top: '16px'})
|
||||
.append(renderChart())
|
||||
.appendTo(that);
|
||||
|
||||
function getColumns() {
|
||||
return [
|
||||
{
|
||||
align: self.options.keyAlign,
|
||||
format: self.options.formatKey,
|
||||
id: 'key',
|
||||
width: self.options.keyWidth,
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
format: renderValue,
|
||||
id: 'value',
|
||||
width: self.valueWidth,
|
||||
visible: true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
function getWidths(values) {
|
||||
var max, maxKeys,
|
||||
total = Ox.sum(values),
|
||||
totalWidth = Math.ceil(total / self.max * self.valueWidth),
|
||||
widths = {};
|
||||
Ox.forEach(values, function(value, key) {
|
||||
widths[key] = Math.round(value / total * totalWidth);
|
||||
});
|
||||
while (Ox.sum(widths) != totalWidth) {
|
||||
max = Ox.max(widths);
|
||||
maxKeys = Object.keys(widths).filter(function(key) {
|
||||
return widths[key] == max;
|
||||
});
|
||||
widths[maxKeys[0]] += Ox.sum(widths) < totalWidth ? 1 : -1;
|
||||
}
|
||||
return widths;
|
||||
}
|
||||
|
||||
function renderChart() {
|
||||
that.css({
|
||||
width: self.options.width + 'px',
|
||||
height: 16 + self.items.length * 16 + 'px',
|
||||
overflowY: 'hidden'
|
||||
});
|
||||
return Ox.TableList({
|
||||
columns: getColumns(),
|
||||
items: self.items,
|
||||
max: 0,
|
||||
min: 0,
|
||||
pageLength: self.items.length,
|
||||
sort: self.sortBy,
|
||||
width: self.options.width,
|
||||
unique: 'key'
|
||||
})
|
||||
.css({
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: self.options.width + 'px',
|
||||
height: self.items.length * 16 + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
function renderValue(value, data) {
|
||||
var $bars = [],
|
||||
$element,
|
||||
colors = [], len, widths;
|
||||
if (!self.subData) {
|
||||
$element = $bars[0] = Ox.Element({
|
||||
element: '<div>',
|
||||
tooltip: Ox.formatNumber(value)
|
||||
+ ' (' + Ox.formatPercent(value * self.options.rows, self.sum, 2) + ')'
|
||||
})
|
||||
.css({
|
||||
width: Math.ceil(value / self.max * self.valueWidth) + 'px',
|
||||
height: '14px',
|
||||
borderRadius: '4px',
|
||||
marginLeft: '-4px'
|
||||
});
|
||||
colors[0] = Ox.isFunction(self.options.color)
|
||||
? self.options.color(data.key) : self.options.color;
|
||||
} else {
|
||||
$element = $('<div>')
|
||||
.css({
|
||||
width: Math.ceil(self.totals[data.key] / self.max * self.valueWidth) + 'px',
|
||||
height: '14px',
|
||||
marginLeft: '-4px'
|
||||
});
|
||||
len = Ox.len(value);
|
||||
widths = getWidths(value);
|
||||
self.subKeys.forEach(function(subKey, subKeyIndex) {
|
||||
var i = $bars.length,
|
||||
subValue = value[subKey];
|
||||
if (subValue) {
|
||||
$bars[i] = Ox.Element({
|
||||
element: '<div>',
|
||||
/*
|
||||
tooltip: Ox.formatNumber(self.totals[data.key])
|
||||
+ ' (' + Ox.formatPercent(self.totals[data.key] * self.options.rows, self.sum, 2) + ')'
|
||||
+ '<br>' + subKey + ': ' + Ox.formatNumber(subValue)
|
||||
+ ' (' + Ox.formatPercent(subValue, self.totals[data.key], 2) + ')'
|
||||
*/
|
||||
tooltip: Ox.formatNumber(self.totals[data.key])
|
||||
+ ' (' + Ox.formatPercent(self.totals[data.key] * self.options.rows, self.sum, 2) + ')'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
width: widths[subKey] + 'px',
|
||||
height: '14px',
|
||||
borderTopLeftRadius: i == 0 ? '4px' : 0,
|
||||
borderBottomLeftRadius: i == 0 ? '4px' : 0,
|
||||
borderTopRightRadius: i == len - 1 ? '4px' : 0,
|
||||
borderBottomRightRadius: i == len - 1 ? '4px' : 0
|
||||
})
|
||||
.appendTo($element);
|
||||
colors[i] = subKey == '' ? [128, 128, 128]
|
||||
: Ox.isArray(self.options.color)
|
||||
? self.options.color[subKeyIndex % self.options.color.length]
|
||||
: Ox.isObject(self.options.color)
|
||||
? self.options.color[subKey]
|
||||
: self.options.color(subKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
$bars.forEach(function($bar, i) {
|
||||
/*
|
||||
if (self.options.rows == 2) {
|
||||
colors[i] = colors[i].map(function(v) {
|
||||
return v + (self.row % 2 == 0 ? 16 : -16);
|
||||
});
|
||||
}
|
||||
*/
|
||||
['moz', 'o', 'webkit'].forEach(function(browser) {
|
||||
$bar.css({
|
||||
backgroundImage: '-' + browser
|
||||
+ '-linear-gradient(top, rgb(' + colors[i].map(function(v) {
|
||||
return Ox.limit(v + 16, 0, 255);
|
||||
}).join(', ') + '), rgb(' + colors[i].map(function(v) {
|
||||
return Ox.limit(v - 16, 0, 255);
|
||||
}) + '))'
|
||||
});
|
||||
});
|
||||
});
|
||||
if (self.options.rows == 2) {
|
||||
self.row++;
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
212
source/UI/js/List/ColumnList.js
Normal file
212
source/UI/js/List/ColumnList.js
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.ColumnList <f> Column List Widget
|
||||
experimental
|
||||
@*/
|
||||
|
||||
Ox.ColumnList = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
columns: [],
|
||||
custom: {},
|
||||
items: [],
|
||||
list: 'table',
|
||||
query: {conditions: [], operator: '&'},
|
||||
resize: null,
|
||||
width: 768
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
query: updateQuery,
|
||||
width: function() {
|
||||
self.columnWidths = getColumnWidths();
|
||||
self.$panel.size(0, self.columnWidths[0]);
|
||||
self.$panel.size(2, self.columnWidths[2]);
|
||||
self.$lists.forEach(function($list, i) {
|
||||
$list.options({itemWidth: self.columnWidths[i]});
|
||||
});
|
||||
}
|
||||
})
|
||||
.addClass('OxColumnList');
|
||||
|
||||
self.numberOfColumns = self.options.columns.length;
|
||||
self.columnWidths = getColumnWidths();
|
||||
|
||||
self.flatItems = [];
|
||||
self.ids = [{}, {}, {}];
|
||||
self.options.items.forEach(function(item0) {
|
||||
item0.items.forEach(function(item1) {
|
||||
self.ids[1][item1.id] = [item0.id];
|
||||
item1.items.forEach(function(item2) {
|
||||
self.ids[2][item2.id] = [item0.id, item1.id];
|
||||
self.flatItems.push(item2);
|
||||
});
|
||||
});
|
||||
});
|
||||
self.api = Ox.api(self.flatItems);
|
||||
|
||||
self.$lists = self.options.columns.map(function(column, i) {
|
||||
return Ox.CustomList({
|
||||
item: self.options.columns[i].item,
|
||||
itemHeight: self.options.columns[i].itemHeight,
|
||||
items: getItems(i),
|
||||
itemWidth: self.columnWidths[i],
|
||||
keys: self.options.columns[i].keys,
|
||||
// FIXME: undefined max will overwrite CustomList default
|
||||
max: self.options.columns[i].max,
|
||||
resize: self.options.resize,
|
||||
scrollbarVisible: true,
|
||||
selected: self.options.columns[i].selected,
|
||||
sort: self.options.columns[i].sort,
|
||||
unique: 'id'
|
||||
})
|
||||
.bindEvent({
|
||||
key_left: function() {
|
||||
if (i > 0) {
|
||||
self.$lists[i - 1].gainFocus();
|
||||
that.triggerEvent('select', {
|
||||
id: self.options.columns[i - 1].id,
|
||||
ids: self.$lists[i - 1].options('selected')
|
||||
});
|
||||
}
|
||||
},
|
||||
key_right: function() {
|
||||
var index, object, selected = self.$lists[i].options('selected');
|
||||
if (selected.length) {
|
||||
if (i < self.numberOfColumns - 1) {
|
||||
if (self.$lists[i + 1].options('selected').length == 0) {
|
||||
self.$lists[i + 1].selectPosition(0);
|
||||
}
|
||||
self.$lists[i + 1].gainFocus();
|
||||
that.triggerEvent('select', {
|
||||
id: self.options.columns[i + 1].id,
|
||||
ids: self.$lists[i + 1].options('selected')
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
open: function(data) {
|
||||
that.triggerEvent('open', {
|
||||
id: column.id,
|
||||
ids: data.ids
|
||||
});
|
||||
},
|
||||
select: function(data) {
|
||||
self.options.columns[i].selected = data.ids;
|
||||
if (i < self.numberOfColumns - 1) {
|
||||
self.$lists[i + 1].options({items: getItems(i + 1)});
|
||||
}
|
||||
if (i == 0 || data.ids.length) {
|
||||
that.triggerEvent('select', {
|
||||
id: column.id,
|
||||
ids: data.ids
|
||||
});
|
||||
} else {
|
||||
self.$lists[i - 1].gainFocus();
|
||||
that.triggerEvent('select', {
|
||||
id: self.options.columns[i - 1].id,
|
||||
ids: self.$lists[i - 1].options('selected')
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
self.$panel = Ox.SplitPanel({
|
||||
elements: self.$lists.map(function($list, index) {
|
||||
return Ox.extend(
|
||||
{element: $list},
|
||||
index == 1 ? {} : {size: self.columnWidths[index]}
|
||||
);
|
||||
}),
|
||||
orientation: 'horizontal'
|
||||
});
|
||||
|
||||
that.setElement(self.$panel);
|
||||
|
||||
function getColumnWidths() {
|
||||
return Ox.splitInt(self.options.width, self.numberOfColumns);
|
||||
}
|
||||
|
||||
function getItems(i) {
|
||||
var items;
|
||||
if (i == 0) {
|
||||
items = self.options.items;
|
||||
} else {
|
||||
items = [];
|
||||
self.options.columns[i - 1].selected.forEach(function(id) {
|
||||
var index;
|
||||
if (i == 1) {
|
||||
index = Ox.getIndexById(self.options.items, id);
|
||||
items = items.concat(
|
||||
self.options.items[index].items
|
||||
);
|
||||
} else if (i == 2) {
|
||||
index = [];
|
||||
Ox.forEach(self.options.columns[0].selected, function(id_) {
|
||||
index[0] = Ox.getIndexById(
|
||||
self.options.items, id_
|
||||
);
|
||||
index[1] = Ox.getIndexById(
|
||||
self.options.items[index[0]].items, id
|
||||
);
|
||||
if (index[1] > -1) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
items = items.concat(
|
||||
self.options.items[index[0]].items[index[1]].items
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
function updateQuery() {
|
||||
if (self.options.query.conditions.length == 0) {
|
||||
self.items = self.options.items;
|
||||
} else {
|
||||
self.api({
|
||||
keys: ['id', '_ids'],
|
||||
query: self.options.query
|
||||
}, function(result) {
|
||||
var ids = [[], [], []];
|
||||
result.data.items.forEach(function(item) {
|
||||
ids[0].push(self.ids[2][item.id][0]);
|
||||
ids[1].push(self.ids[2][item.id][1]);
|
||||
ids[2].push(item.id);
|
||||
});
|
||||
self.items = self.options.items.filter(function(item0) {
|
||||
return Ox.contains(ids[0], item0.id);
|
||||
});
|
||||
self.items.forEach(function(item0) {
|
||||
item0.items = item0.items.filter(function(item1) {
|
||||
return Ox.contains(ids[1], item1.id);
|
||||
});
|
||||
item0.items.forEach(function(item1) {
|
||||
item1.items = item1.items.filter(function(item2) {
|
||||
return Ox.contains(ids[2], item2.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
self.$lists.forEach(function($list, i) {
|
||||
$list.options({items: i == 0 ? self.items : []});
|
||||
});
|
||||
}
|
||||
|
||||
that.sort = function(id, sort) {
|
||||
var index = Ox.isNumber(id) ? id
|
||||
: Ox.getIndexById(self.options.columns, id);
|
||||
self.$lists[index].options({sort: sort});
|
||||
self.options.columns[index].sort = sort;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
156
source/UI/js/List/CustomList.js
Normal file
156
source/UI/js/List/CustomList.js
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.CustomList <f> Custom List Widget
|
||||
experimental
|
||||
@*/
|
||||
|
||||
Ox.CustomList = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
draggable: false,
|
||||
item: null,
|
||||
itemHeight: 32,
|
||||
items: null,
|
||||
itemWidth: 256,
|
||||
keys: [],
|
||||
max: -1,
|
||||
min: 0,
|
||||
pageLength: 100,
|
||||
query: {conditions: [], operator: '&'},
|
||||
resize: null,
|
||||
scrollbarVisible: false,
|
||||
selected: [],
|
||||
sort: [],
|
||||
sortable: false,
|
||||
sums: [],
|
||||
unique: ''
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
items: function() {
|
||||
self.$list.options({items: self.options.items});
|
||||
},
|
||||
itemWidth: function() {
|
||||
var width = self.options.itemWidth - Ox.UI.SCROLLBAR_SIZE;
|
||||
if (self.options.resize) {
|
||||
that.find('.OxItem').each(function(element) {
|
||||
self.options.resize($(this), width);
|
||||
});
|
||||
}
|
||||
},
|
||||
query: function() {
|
||||
self.$list.options({query: self.options.query});
|
||||
},
|
||||
selected: function() {
|
||||
self.$list.options({selected: self.options.selected});
|
||||
// FIXME: TableList doesn't trigger event here
|
||||
that.triggerEvent('select', {ids: self.options.selected});
|
||||
},
|
||||
sort: function() {
|
||||
self.$list.options({sort: self.options.sort});
|
||||
}
|
||||
})
|
||||
.addClass('OxCustomList');
|
||||
|
||||
self.$list = Ox.List({
|
||||
construct: function(data) {
|
||||
return self.options.item(
|
||||
data, self.options.itemWidth - Ox.UI.SCROLLBAR_SIZE
|
||||
).addClass('OxTarget');
|
||||
},
|
||||
draggable: self.options.draggable,
|
||||
itemHeight: self.options.itemHeight,
|
||||
itemWidth: self.options.itemWidth
|
||||
- self.options.scrollbarVisible * Ox.UI.SCROLLBAR_SIZE,
|
||||
items: self.options.items,
|
||||
keys: self.options.keys.concat(self.options.unique),
|
||||
max: self.options.max,
|
||||
min: self.options.min,
|
||||
orientation: 'vertical',
|
||||
pageLength: self.options.pageLength,
|
||||
query: Ox.clone(self.options.query, true),
|
||||
selected: self.options.selected,
|
||||
sort: Ox.clone(self.options.sort, true),
|
||||
sortable: self.options.sortable,
|
||||
sums: self.options.sums,
|
||||
type: 'text',
|
||||
unique: self.options.unique
|
||||
})
|
||||
.css({
|
||||
top: 0,
|
||||
overflowY: (self.options.scrollbarVisible ? 'scroll' : 'hidden')
|
||||
})
|
||||
.bindEvent(function(data, event) {
|
||||
if (event == 'select') {
|
||||
self.options.selected = data.ids;
|
||||
}
|
||||
that.triggerEvent(event, data);
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
that.api = self.$list.options('items');
|
||||
|
||||
/*@
|
||||
gainFocus <f> gain Focus
|
||||
@*/
|
||||
that.gainFocus = function() {
|
||||
self.$list.gainFocus();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.getPasteIndex = function() {
|
||||
return self.$list.getPasteIndex();
|
||||
};
|
||||
|
||||
/*@
|
||||
hasFocus <f> has Focus
|
||||
@*/
|
||||
that.hasFocus = function() {
|
||||
return self.$list.hasFocus();
|
||||
};
|
||||
|
||||
that.invertSelection = function() {
|
||||
self.$list.invertSelection();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
loseFocus <f> lose Focus
|
||||
@*/
|
||||
that.loseFocus = function() {
|
||||
self.$list.loseFocus();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.selectAll = function() {
|
||||
self.$list.selectAll();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
selectPosition <f> select position
|
||||
@*/
|
||||
that.selectPosition = function(pos) {
|
||||
self.$list.selectPosition(pos);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.selectSelected = function(offset) {
|
||||
that.$list.selectSelected(offset);
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
size <f> size
|
||||
@*/
|
||||
that.size = function() {
|
||||
self.$list.size();
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
226
source/UI/js/List/IconItem.js
Normal file
226
source/UI/js/List/IconItem.js
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.IconItem <f> IconItem Object
|
||||
options <o> Options object
|
||||
borderRadius <n|0> Border radius for icon images
|
||||
find <s|''> String to be highlighted
|
||||
iconHeight <n|128> Icon height
|
||||
iconWidth <n|128> Icon width
|
||||
imageHeight <n|128> Icon image height
|
||||
imageWidth <n|128> Icon image width
|
||||
id <s> Element id
|
||||
info <s> Icon info
|
||||
size <n|128> Icon size
|
||||
title <s> Title
|
||||
url <s> Icon url
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> IconItem Object
|
||||
@*/
|
||||
|
||||
Ox.IconItem = function(options, self) {
|
||||
|
||||
//Ox.Log('List', 'IconItem', options, self)
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
borderRadius: 0,
|
||||
extra: null,
|
||||
find: '',
|
||||
iconHeight: 128,
|
||||
iconWidth: 128,
|
||||
imageHeight: 128,
|
||||
imageWidth: 128,
|
||||
itemHeight: 192,
|
||||
itemWidth: 128,
|
||||
id: '',
|
||||
info: '',
|
||||
title: '',
|
||||
url: ''
|
||||
})
|
||||
.options(options || {});
|
||||
|
||||
Ox.extend(self, {
|
||||
fontSize: self.options.itemWidth == 64 ? 6 : 9,
|
||||
infoIsObject: Ox.isObject(self.options.info),
|
||||
lineLength: self.options.itemWidth == 64 ? 15 : 23,
|
||||
lines: self.options.itemWidth == 64 ? 4 : 5,
|
||||
url: Ox.UI.PATH + 'png/transparent.png'
|
||||
});
|
||||
|
||||
self.title = formatText(self.options.title, self.lines - 1 - self.infoIsObject, self.lineLength);
|
||||
if (!self.infoIsObject) {
|
||||
self.info = formatText(self.options.info, 5 - self.title.split('<br/>').length, self.lineLength);
|
||||
} else {
|
||||
self.title = $('<div>').css({fontSize: self.fontSize + 'px'}).html(self.title);
|
||||
self.info = $('<div>').append(
|
||||
self.options.info.css(Ox.extend(self.options.info.css('width') == '0px' ? {
|
||||
width: Math.round(self.options.itemWidth / 2) + 'px'
|
||||
} : {}, {
|
||||
padding: 0,
|
||||
margin: '1px auto',
|
||||
fontSize: self.fontSize + 'px',
|
||||
textShadow: 'none'
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
that.css({
|
||||
// 2 * 2 px margin (.css), 2 * 2 px border (here)
|
||||
width: self.options.itemWidth + 4 + 'px',
|
||||
height: self.options.itemHeight + + 4 + 'px'
|
||||
});
|
||||
that.$icon = $('<div>')
|
||||
.addClass('OxIcon')
|
||||
.css({
|
||||
top: (self.options.iconWidth == 64 ? -64 : -122) + 'px',
|
||||
width: self.options.iconWidth + 4 + 'px',
|
||||
height: self.options.iconHeight + 4 + 'px'
|
||||
});
|
||||
that.$iconImage = $('<img>')
|
||||
.addClass('OxLoading OxTarget')
|
||||
.attr({
|
||||
src: self.url
|
||||
})
|
||||
.css({
|
||||
width: self.options.imageWidth + 'px',
|
||||
height: self.options.imageHeight + 'px',
|
||||
borderRadius: self.options.borderRadius + 4 + 'px'
|
||||
})
|
||||
.mousedown(mousedown)
|
||||
.mouseenter(mouseenter)
|
||||
.mouseleave(mouseleave);
|
||||
self.options.url && that.$iconImage.one('load', load);
|
||||
that.$textBox = $('<div>')
|
||||
.addClass('OxText')
|
||||
.css({
|
||||
top: self.options.iconHeight - (self.options.itemWidth == 64 ? 32 : 62) + 'px',
|
||||
width: self.options.itemWidth + 4 + 'px',
|
||||
height: (self.options.itemWidth == 64 ? 30 : 58) + 'px'
|
||||
});
|
||||
that.$text = $('<div>')
|
||||
.addClass('OxTarget')
|
||||
.css({
|
||||
fontSize: self.fontSize + 'px'
|
||||
})
|
||||
.mouseenter(mouseenter)
|
||||
.mouseleave(mouseleave);
|
||||
if (!self.infoIsObject) {
|
||||
that.$text.html(
|
||||
(self.title ? self.title + '<br/>' : '')
|
||||
+ '<span class="OxInfo">' + self.info + '</span>'
|
||||
);
|
||||
} else {
|
||||
that.$text.append(self.title).append(self.info);
|
||||
}
|
||||
that.$reflection = $('<div>')
|
||||
.addClass('OxReflection')
|
||||
.css({
|
||||
top: self.options.iconHeight + (self.options.itemWidth == 64 ? 0 : 2) + 'px',
|
||||
width: self.options.itemWidth + 4 + 'px',
|
||||
height: self.options.itemHeight - self.options.iconHeight + 'px'
|
||||
});
|
||||
that.$reflectionImage = $('<img>')
|
||||
.addClass('OxLoading')
|
||||
.attr({
|
||||
src: self.url
|
||||
})
|
||||
.css({
|
||||
width: self.options.imageWidth + 'px',
|
||||
height: self.options.imageHeight + 'px',
|
||||
// firefox is 1px off when centering images with odd width and scaleY(-1)
|
||||
paddingLeft: ($.browser.mozilla && self.options.imageWidth % 2 ? 1 : 0) + 'px',
|
||||
borderRadius: self.options.borderRadius + 'px'
|
||||
});
|
||||
that.$gradient = $('<div>')
|
||||
.css({
|
||||
// `+2` is a temporary fix for https://code.google.com/p/chromium/issues/detail?id=167198
|
||||
width: self.options.itemWidth + 2 + 'px',
|
||||
height: self.options.itemWidth / 2 + 'px'
|
||||
});
|
||||
|
||||
that.append(
|
||||
that.$reflection.append(
|
||||
that.$reflectionImage
|
||||
).append(
|
||||
that.$gradient
|
||||
)
|
||||
).append(
|
||||
that.$textBox.append(
|
||||
that.$text
|
||||
)
|
||||
).append(
|
||||
that.$icon.append(
|
||||
that.$iconImage
|
||||
)
|
||||
);
|
||||
|
||||
if (self.options.extra) {
|
||||
that.$extra = $('<div>')
|
||||
.addClass('OxTarget')
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
width: self.options.imageWidth + 'px',
|
||||
height: self.options.imageHeight + 'px',
|
||||
border: '2px solid transparent',
|
||||
margin: 'auto',
|
||||
cursor: 'pointer',
|
||||
overflow: 'hidden'
|
||||
})
|
||||
that.$icon.append(
|
||||
that.$extra.append(
|
||||
self.options.extra
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function formatText(text, maxLines, maxLength) {
|
||||
text = Ox.isArray(text) ? text.join(', ') : text;
|
||||
var lines = Ox.wordwrap(text, maxLength, true).split('\n');
|
||||
// if the text has too many lines, replace the last line with the
|
||||
// truncated rest (including the last line) and discard all extra lines
|
||||
if (lines.length > maxLines) {
|
||||
lines[maxLines - 1] = Ox.truncate(
|
||||
lines.slice(maxLines - 1).join(''), 'center', maxLength
|
||||
).replace('…', '<span class="OxLight">…</span>');
|
||||
lines = lines.slice(0, maxLines);
|
||||
}
|
||||
return Ox.highlight(
|
||||
lines.join('<br/>'), self.options.find, 'OxHighlight', true
|
||||
);
|
||||
}
|
||||
|
||||
function load() {
|
||||
that.$iconImage.attr({
|
||||
src: self.options.url
|
||||
})
|
||||
.one('load', function() {
|
||||
that.$iconImage.removeClass('OxLoading');
|
||||
that.$reflectionImage
|
||||
.attr({
|
||||
src: self.options.url
|
||||
})
|
||||
.removeClass('OxLoading');
|
||||
});
|
||||
}
|
||||
|
||||
function mousedown(e) {
|
||||
// fixme: preventDefault keeps image from being draggable in safari - but also keeps the list from getting focus
|
||||
// e.preventDefault();
|
||||
}
|
||||
|
||||
function mouseenter() {
|
||||
that.addClass('OxHover');
|
||||
}
|
||||
|
||||
function mouseleave() {
|
||||
that.removeClass('OxHover');
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
308
source/UI/js/List/IconList.js
Normal file
308
source/UI/js/List/IconList.js
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
'use strict';
|
||||
/*@
|
||||
Ox.IconList <f> IconList Object
|
||||
options <o> Options object
|
||||
borderRadius <n|0> border radius for icon images
|
||||
centered <b|false> scroll list so selection is always centered
|
||||
defaultRatio <n|1> aspect ratio of icon placeholders
|
||||
draggable <b|false> If true, items can be dragged
|
||||
fixedRatio <b|n|false> if set to a number, icons have a fixed ratio
|
||||
id <s|''> element id
|
||||
item <f|null> called with data, sort, size,
|
||||
extends data with information needed for constructor
|
||||
itemConstructor <f|Ox.IconItem> contructor used to create item
|
||||
items <f|null> items array or callback function
|
||||
keys <a|[]> available item keys
|
||||
max <n|-1> maximum selected selected items
|
||||
min <n|0> minimum of selcted items
|
||||
orientation <s|both> orientation ("horizontal", "vertical" or "both")
|
||||
pageLength <n|100> Number of items per page (if orientation != "both")
|
||||
query <o> Query
|
||||
selectAsYouType <s|''> If set to a key, enables select-as-you-type
|
||||
selected <a|[]> array of selected items
|
||||
size <n|128> list size
|
||||
sort <a|[]> sort keys
|
||||
sums <[s]|[]> Sums to be included in totals
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.List> IconList Object
|
||||
@*/
|
||||
|
||||
Ox.IconList = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
borderRadius: 0,
|
||||
centered: false,
|
||||
defaultRatio: 1,
|
||||
draggable: false,
|
||||
find: '',
|
||||
fixedRatio: false,
|
||||
id: '',
|
||||
item: null,
|
||||
itemConstructor: Ox.IconItem,
|
||||
items: null,
|
||||
keys: [],
|
||||
max: -1,
|
||||
min: 0,
|
||||
orientation: 'both',
|
||||
pageLength: 100,
|
||||
query: {conditions: [], operator: '&'},
|
||||
selectAsYouType: '',
|
||||
selected: [],
|
||||
size: 128,
|
||||
sort: [],
|
||||
sums: [],
|
||||
unique: ''
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
items: function() {
|
||||
self.$list.options({items: self.options.items});
|
||||
},
|
||||
query: function() {
|
||||
self.$list.options({query: self.options.query});
|
||||
},
|
||||
selected: function() {
|
||||
self.$list.options({selected: self.options.selected});
|
||||
},
|
||||
sort: function() {
|
||||
updateKeys();
|
||||
self.$list.options({sort: self.options.sort});
|
||||
}
|
||||
});
|
||||
|
||||
if (self.options.fixedRatio) {
|
||||
self.options.defaultRatio = self.options.fixedRatio;
|
||||
}
|
||||
|
||||
if (Ox.isArray(self.options.items)) {
|
||||
self.options.keys = Ox.unique(Ox.flatten(
|
||||
self.options.items.map(function(item) {
|
||||
return Object.keys(item);
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
self.iconWidth = self.options.size;
|
||||
self.iconHeight = self.options.fixedRatio > 1
|
||||
? Math.round(self.options.size / self.options.fixedRatio)
|
||||
: self.options.size;
|
||||
self.itemWidth = self.options.size;
|
||||
self.itemHeight = self.iconHeight + self.options.size * 0.5;
|
||||
|
||||
self.$list = Ox.List({
|
||||
centered: self.options.centered,
|
||||
// fixme: change all occurences of construct to render
|
||||
construct: constructItem,
|
||||
draggable: self.options.draggable,
|
||||
id: self.options.id,
|
||||
itemHeight: self.itemHeight,
|
||||
items: self.options.items,
|
||||
itemWidth: self.itemWidth,
|
||||
keys: self.options.keys,
|
||||
max: self.options.max,
|
||||
min: self.options.min,
|
||||
orientation: self.options.orientation,
|
||||
pageLength: self.options.pageLength,
|
||||
query: self.options.query,
|
||||
selectAsYouType: self.options.selectAsYouType,
|
||||
selected: self.options.selected,
|
||||
sort: self.options.sort,
|
||||
sums: self.options.sums,
|
||||
type: 'icon',
|
||||
unique: self.options.unique
|
||||
})
|
||||
.addClass('OxIconList Ox' + Ox.toTitleCase(self.options.orientation))
|
||||
.bindEvent(function(data, event) {
|
||||
if (event == 'select') {
|
||||
self.options.selected = data.ids;
|
||||
}
|
||||
that.triggerEvent(event, data);
|
||||
});
|
||||
|
||||
that.setElement(self.$list);
|
||||
|
||||
updateKeys();
|
||||
|
||||
function constructItem(data) {
|
||||
var isEmpty = Ox.isEmpty(data);
|
||||
data = !isEmpty
|
||||
? self.options.item(data, self.options.sort, self.options.size)
|
||||
: {
|
||||
width: Math.round(self.options.size * (
|
||||
self.options.defaultRatio >= 1 ? 1 : self.options.defaultRatio
|
||||
)),
|
||||
height: Math.round(self.options.size / (
|
||||
self.options.defaultRatio <= 1 ? 1 : self.options.defaultRatio
|
||||
))
|
||||
};
|
||||
return self.options.itemConstructor(Ox.extend(data, {
|
||||
borderRadius: self.options.borderRadius,
|
||||
find: self.options.find,
|
||||
iconHeight: self.iconHeight,
|
||||
iconWidth: self.iconWidth,
|
||||
imageHeight: data.height,
|
||||
imageWidth: data.width,
|
||||
itemHeight: self.itemHeight,
|
||||
itemWidth: self.itemWidth
|
||||
//height: Math.round(self.options.size / (ratio <= 1 ? 1 : ratio)),
|
||||
//size: self.options.size,
|
||||
//width: Math.round(self.options.size * (ratio >= 1 ? 1 : ratio))
|
||||
}));
|
||||
}
|
||||
|
||||
function updateKeys() {
|
||||
self.$list.options({
|
||||
keys: Ox.unique(
|
||||
[self.options.sort[0].key].concat(self.options.keys)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
/*@
|
||||
closePreview <f> close preview
|
||||
() -> <o> the list
|
||||
@*/
|
||||
that.closePreview = function() {
|
||||
self.$list.closePreview();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
gainFocus <f> gainFocus
|
||||
@*/
|
||||
that.gainFocus = function() {
|
||||
self.$list.gainFocus();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.getPasteIndex = function() {
|
||||
return self.$list.getPasteIndex();
|
||||
};
|
||||
|
||||
/*@
|
||||
hasFocus <f> hasFocus
|
||||
@*/
|
||||
that.hasFocus = function() {
|
||||
return self.$list.hasFocus();
|
||||
};
|
||||
|
||||
that.invertSelection = function() {
|
||||
self.$list.invertSelection();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
loseFocus <f> loseFocus
|
||||
@*/
|
||||
that.loseFocus = function() {
|
||||
self.$list.loseFocus();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
openPreview <f> openPreview
|
||||
@*/
|
||||
that.openPreview = function() {
|
||||
self.$list.openPreview();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
reloadList <f> reload list
|
||||
() -> <o> the list
|
||||
@*/
|
||||
that.reloadList = function() {
|
||||
self.$list.reloadList();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
scrollToSelection <f> scroll list to selection
|
||||
() -> <o> the list
|
||||
@*/
|
||||
that.scrollToSelection = function() {
|
||||
self.$list.scrollToSelection();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.selectAll = function() {
|
||||
self.$list.selectAll();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.selectPosition = function(pos) {
|
||||
self.$list.selectPosition(pos);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.selectSelected = function(offset) {
|
||||
self.$list.selectSelected(offset);
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
size <f> get size of list
|
||||
() -> <n> size
|
||||
@*/
|
||||
that.size = function() {
|
||||
self.$list.size();
|
||||
return that;
|
||||
};
|
||||
|
||||
// fixme: deprecate, use options()
|
||||
/*@
|
||||
sortList <f> sort list
|
||||
(key, operator) -> <o> the list
|
||||
key <s> sort key
|
||||
operator <s> sort operator ("+" or "-")
|
||||
@*/
|
||||
that.sortList = function(key, operator) {
|
||||
self.options.sort = [{
|
||||
key: key,
|
||||
operator: operator
|
||||
}];
|
||||
updateKeys();
|
||||
self.$list.sortList(key, operator);
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
value <f> get/set value of item in list
|
||||
(id) -> <o> get all data for item
|
||||
(id, key) -> <s> get value of key of item with id
|
||||
(id, key, value) -> <f> set value, returns IconList
|
||||
(id, {key: value, ...}) -> <f> set values, returns IconList
|
||||
@*/
|
||||
that.value = function() {
|
||||
var args = Ox.slice(arguments),
|
||||
id = args.shift(),
|
||||
sort = false;
|
||||
if (arguments.length == 1) {
|
||||
return self.$list.value(id);
|
||||
} else if (arguments.length == 2 && Ox.isString(arguments[1])) {
|
||||
return self.$list.value(id, arguments[1]);
|
||||
} else {
|
||||
Ox.forEach(Ox.makeObject(args), function(value, key) {
|
||||
self.$list.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;
|
||||
}
|
||||
if (key == self.options.sort[0].key) {
|
||||
// sort key has changed
|
||||
sort = true;
|
||||
}
|
||||
});
|
||||
sort && self.$list.sort();
|
||||
return that;
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
303
source/UI/js/List/InfoList.js
Normal file
303
source/UI/js/List/InfoList.js
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.InfoList <f> Info List
|
||||
options <o> Options
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.List> Info List
|
||||
@*/
|
||||
Ox.InfoList = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
borderRadius: 0,
|
||||
defaultRatio: 1,
|
||||
draggable: false,
|
||||
id: '',
|
||||
item: null,
|
||||
items: null,
|
||||
keys: [],
|
||||
max: -1,
|
||||
min: 0,
|
||||
query: {conditions: [], operator: '&'},
|
||||
selectAsYouType: '',
|
||||
selected: [],
|
||||
size: 192,
|
||||
sort: [],
|
||||
sums: [],
|
||||
unique: ''
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
items: function() {
|
||||
self.$list.options({items: self.options.items});
|
||||
},
|
||||
query: function() {
|
||||
self.$list.options({query: self.options.query});
|
||||
},
|
||||
selected: function() {
|
||||
self.$list.options({selected: self.options.selected});
|
||||
},
|
||||
sort: function() {
|
||||
updateKeys();
|
||||
self.$list.options({sort: self.options.sort});
|
||||
},
|
||||
width: function() {
|
||||
// FIXME: don't use classname for this lookup
|
||||
var width = getItemWidth();
|
||||
$('.OxInfoElement').each(function() {
|
||||
var $parent = $(this).parent(),
|
||||
id = parseInt(/OxId(.*?)$/.exec(this.className)[1], 10);
|
||||
$parent.css({width: width - 144});
|
||||
$parent.parent().css({width: width - 144});
|
||||
$parent.parent().parent().css({width: width - 8});
|
||||
Ox.$elements[id].options({width: width - 152});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
self.iconSize = Math.round(self.options.size * 2/3);
|
||||
self.itemHeight = self.options.size;
|
||||
|
||||
that.setElement(
|
||||
self.$list = Ox.List({
|
||||
construct: constructItem,
|
||||
draggable: self.options.draggable,
|
||||
id: self.options.id,
|
||||
itemHeight: self.itemHeight,
|
||||
items: self.options.items,
|
||||
itemWidth: getItemWidth(),
|
||||
keys: self.options.keys,
|
||||
max: self.options.max,
|
||||
min: self.options.min,
|
||||
orientation: 'vertical',
|
||||
pageLength: 10,
|
||||
query: self.options.query,
|
||||
selectAsYouType: self.options.selectAsYouType,
|
||||
selected: self.options.selected,
|
||||
sort: self.options.sort,
|
||||
sums: self.options.sums,
|
||||
type: 'info',
|
||||
unique: self.options.unique
|
||||
})
|
||||
.addClass('OxInfoList')
|
||||
.bindEvent(function(data, event) {
|
||||
if (event == 'select') {
|
||||
self.options.selected = data.ids;
|
||||
}
|
||||
that.triggerEvent(event, data);
|
||||
})
|
||||
);
|
||||
|
||||
updateKeys();
|
||||
|
||||
function constructItem(data) {
|
||||
var isEmpty = Ox.isEmpty(data),
|
||||
data = !isEmpty
|
||||
? self.options.item(data, self.options.sort, self.options.size)
|
||||
: {
|
||||
icon: {
|
||||
width: Math.round(self.iconSize * (
|
||||
self.options.defaultRatio >= 1 ? 1 : self.options.defaultRatio
|
||||
)),
|
||||
height: Math.round(self.iconSize / (
|
||||
self.options.defaultRatio <= 1 ? 1 : self.options.defaultRatio
|
||||
))
|
||||
},
|
||||
info: {}
|
||||
},
|
||||
$icon = Ox.Element()
|
||||
.css({
|
||||
float: 'left',
|
||||
width: '132px',
|
||||
height: '192px',
|
||||
margin: '2px'
|
||||
})
|
||||
.append(
|
||||
Ox.IconItem(Ox.extend(data.icon, {
|
||||
borderRadius: self.options.borderRadius,
|
||||
iconHeight: self.iconSize,
|
||||
iconWidth: self.iconSize,
|
||||
imageHeight: data.icon.height,
|
||||
imageWidth: data.icon.width,
|
||||
itemHeight: self.itemHeight,
|
||||
itemWidth: 128
|
||||
}))
|
||||
.addClass('OxInfoIcon')
|
||||
.css({
|
||||
position: 'absolute'
|
||||
})
|
||||
),
|
||||
$info = Ox.Element()
|
||||
.css({
|
||||
float: 'left',
|
||||
width: getItemWidth() - 144 + 'px',
|
||||
height: 196 + 'px'
|
||||
}),
|
||||
$infobox = Ox.Element()
|
||||
.css({
|
||||
position: 'absolute',
|
||||
width: getItemWidth() - 144 + 'px',
|
||||
height: 196 + 'px',
|
||||
marginTop: '-2px',
|
||||
overflow: 'hidden'
|
||||
})
|
||||
.appendTo($info),
|
||||
$item = Ox.Element()
|
||||
.css({
|
||||
width: getItemWidth() - 8 + 'px',
|
||||
height: 196 + 'px',
|
||||
margin: '4px'
|
||||
})
|
||||
.append($icon)
|
||||
.append($info);
|
||||
if (!isEmpty) {
|
||||
var $element = data.info.element(Ox.extend(data.info.options, {
|
||||
width: getItemWidth() - 152
|
||||
}))
|
||||
.addClass('OxInfoElement');
|
||||
data.info.css && $element.css(data.info.css);
|
||||
data.info.events && $element.bindEvent(data.info.events);
|
||||
$element.addClass('OxId' + $element.oxid); // fixme: needed?
|
||||
$infobox.append($element);
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
function getItemWidth(cached) {
|
||||
if (!cached) {
|
||||
self.cachedWidth = that.width() - Ox.UI.SCROLLBAR_SIZE;
|
||||
} else if (!self.cachedWidth || self.cachedWidthTime < +new Date() - 5000) {
|
||||
self.cachedWidth = that.width() - Ox.UI.SCROLLBAR_SIZE;
|
||||
self.cachedWidthTime = +new Date();
|
||||
}
|
||||
return self.cachedWidth;
|
||||
}
|
||||
|
||||
function updateKeys() {
|
||||
self.$list.options({
|
||||
keys: Ox.unique(
|
||||
[self.options.sort[0].key].concat(self.options.keys)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
/*@
|
||||
closePreview <f> closePreview
|
||||
@*/
|
||||
that.closePreview = function() {
|
||||
self.$list.closePreview();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
gainFocus <f> gainFocus
|
||||
@*/
|
||||
that.gainFocus = function() {
|
||||
self.$list.gainFocus();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.getPasteIndex = function() {
|
||||
return self.$list.getPasteIndex();
|
||||
};
|
||||
|
||||
/*@
|
||||
hasFocus <f> hasFocus
|
||||
@*/
|
||||
that.hasFocus = function() {
|
||||
return self.$list.hasFocus();
|
||||
};
|
||||
|
||||
that.invertSelection = function() {
|
||||
self.$list.invertSelection();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
loseFocus <f> loseFocus
|
||||
@*/
|
||||
that.loseFocus = function() {
|
||||
self.$list.loseFocus();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
reloadList <f> reloadList
|
||||
@*/
|
||||
that.reloadList = function(stayAtPosition) {
|
||||
self.$list.reloadList(stayAtPosition);
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
scrollToSelection <f> scrollToSelection
|
||||
@*/
|
||||
that.scrollToSelection = function() {
|
||||
self.$list.scrollToSelection();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.selectAll = function() {
|
||||
self.$list.selectAll();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.selectPosition = function(pos) {
|
||||
self.$list.selectPosition(pos);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.selectSelected = function(offset) {
|
||||
self.$list.selectSelected(offset);
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
size <f> size
|
||||
@*/
|
||||
that.size = function() {
|
||||
self.$list.size();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
sortList <f> sortList
|
||||
(key, operator) -> <o>
|
||||
@*/
|
||||
that.sortList = function(key, operator) {
|
||||
self.options.sort = [{
|
||||
key: key,
|
||||
operator: operator
|
||||
}];
|
||||
updateKeys();
|
||||
self.$list.sortList(key, operator);
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
value <f> value
|
||||
(id) -> values
|
||||
(id, key) -> value
|
||||
(id, key, value) -> <o>
|
||||
(id, {key: value}) -> <o>
|
||||
@*/
|
||||
that.value = function() {
|
||||
var args = Ox.slice(arguments),
|
||||
id = args.shift();
|
||||
if (arguments.length == 1) {
|
||||
return self.$list.value(id);
|
||||
} else if (arguments.length == 2 && Ox.isString(arguments[1])) {
|
||||
return self.$list.value(id, arguments[1]);
|
||||
} else {
|
||||
Ox.forEach(Ox.makeObject(args), function(value, key) {
|
||||
self.$list.value(id, key, value);
|
||||
});
|
||||
return that;
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
1863
source/UI/js/List/List.js
Normal file
1863
source/UI/js/List/List.js
Normal file
File diff suppressed because it is too large
Load diff
55
source/UI/js/List/ListItem.js
Normal file
55
source/UI/js/List/ListItem.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.ListItem <f> ListItem Object
|
||||
options <o> Options object
|
||||
construct <f> construct function
|
||||
data <o|{}> item data
|
||||
draggable <b|false> can be dragged
|
||||
position <n|0> item position
|
||||
unique <s|''> unique key
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> ListItem Object
|
||||
cancel <!> triggered if cancel button is pressed
|
||||
save <!> triggered if save button is pressed
|
||||
@*/
|
||||
|
||||
Ox.ListItem = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
construct: null,
|
||||
data: {},
|
||||
draggable: false,
|
||||
position: 0,
|
||||
unique: ''
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
data: function() {
|
||||
constructItem(true);
|
||||
},
|
||||
position: function() {
|
||||
that.data({position: self.options.position});
|
||||
}
|
||||
});
|
||||
|
||||
constructItem();
|
||||
|
||||
function constructItem(update) {
|
||||
var $element = self.options.construct(self.options.data)
|
||||
.addClass('OxItem')
|
||||
.data({
|
||||
id: self.options.data[self.options.unique],
|
||||
position: self.options.position
|
||||
});
|
||||
if (update) {
|
||||
that.hasClass('OxSelected') && $element.addClass('OxSelected');
|
||||
}
|
||||
that.setElement($element);
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
64
source/UI/js/List/SortList.js
Normal file
64
source/UI/js/List/SortList.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.SortList <f> Sortable List
|
||||
options <o> Options object
|
||||
items <[o]|[]> Items ({id: ..., title: ...})
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> SortList Object
|
||||
@*/
|
||||
|
||||
Ox.SortList = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
items: [],
|
||||
scrollbarVisible: false
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
items: function() {
|
||||
self.items = getItems();
|
||||
self.$list.reloadList();
|
||||
that.triggerEvent('sort', {
|
||||
ids: self.items.map(function(item) {
|
||||
return item.id;
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
self.items = getItems();
|
||||
|
||||
self.$list = Ox.TableList({
|
||||
columns: [
|
||||
{id: 'title', visible: true}
|
||||
],
|
||||
items: self.items,
|
||||
max: 1,
|
||||
scrollbarVisible: self.options.scrollbarVisible,
|
||||
sort: [{key: 'position', operator: '+'}],
|
||||
sortable: true,
|
||||
unique: 'id'
|
||||
})
|
||||
.bindEvent({
|
||||
move: function(data) {
|
||||
self.options.items.sort(function(a, b) {
|
||||
return data.ids.indexOf(a.id) - data.ids.indexOf(b.id);
|
||||
});
|
||||
that.triggerEvent('sort', data);
|
||||
}
|
||||
});
|
||||
|
||||
function getItems() {
|
||||
return self.options.items.map(function(item, position) {
|
||||
return {id: item.id, title: item.title, position: position};
|
||||
});
|
||||
}
|
||||
|
||||
that.setElement(self.$list);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
1264
source/UI/js/List/TableList.js
Normal file
1264
source/UI/js/List/TableList.js
Normal file
File diff suppressed because it is too large
Load diff
305
source/UI/js/List/TreeList.js
Normal file
305
source/UI/js/List/TreeList.js
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.TreeList <f> Tree List
|
||||
options <o> Options object
|
||||
data <f|null> Data to be parsed as items (needs documentation)
|
||||
expanded <b|false> If true, and data is not null, all items are expanded
|
||||
icon <o|f|null> Image URL, or function that returns an image object
|
||||
items <a|[]> Array of items
|
||||
max <n|-1> Maximum number of items that can be selected, -1 unlimited
|
||||
min <n|0> Minimum number of items that have to be selected
|
||||
selected <a|[]> Selected ids
|
||||
width <n|256> List width
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.List> Tree List Object
|
||||
@*/
|
||||
|
||||
Ox.TreeList = function(options, self) {
|
||||
|
||||
// fixme: expanding the last item should cause some scroll
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
data: null,
|
||||
expanded: false,
|
||||
icon: null,
|
||||
items: [],
|
||||
max: 1,
|
||||
min: 0,
|
||||
selected: [],
|
||||
width: 'auto'
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
data: function() {
|
||||
self.options.items = getItems();
|
||||
self.$list.options({items: parseItems()});
|
||||
},
|
||||
selected: function() {
|
||||
//self.$list.options({selected: self.options.selected});
|
||||
selectItem({ids: self.options.selected});
|
||||
self.$list.scrollToSelection();
|
||||
},
|
||||
width: function() {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
|
||||
if (self.options.data) {
|
||||
self.options.items = getItems();
|
||||
}
|
||||
|
||||
that.setElement(
|
||||
self.$list = Ox.List({
|
||||
_tree: true,
|
||||
construct: constructItem,
|
||||
itemHeight: 16,
|
||||
items: parseItems(),
|
||||
itemWidth: self.options.width,
|
||||
keys: ['expanded', 'id', 'items', 'level', 'title'],
|
||||
max: self.options.max,
|
||||
min: self.options.min,
|
||||
unique: 'id'
|
||||
})
|
||||
.addClass('OxTableList OxTreeList')
|
||||
.css({
|
||||
width: self.options.width + 'px',
|
||||
overflowY: 'scroll'
|
||||
})
|
||||
.bindEvent(function(data, event) {
|
||||
if (event == 'anyclick') {
|
||||
clickItem(data);
|
||||
} else if (event == 'toggle') {
|
||||
toggleItems(data);
|
||||
}
|
||||
that.triggerEvent(event, data);
|
||||
})
|
||||
);
|
||||
|
||||
self.options.selected.length && selectItem({ids: self.options.selected});
|
||||
|
||||
function clickItem(e) {
|
||||
var $target = $(e.target),
|
||||
$item, id, item;
|
||||
if ($target.is('.OxToggle')) {
|
||||
$item = $target.parent().parent();
|
||||
id = $item.data('id');
|
||||
item = getItemById(id);
|
||||
toggleItem(item, !item.expanded);
|
||||
}
|
||||
}
|
||||
|
||||
function constructItem(data) {
|
||||
var $item = $('<div>').css({
|
||||
width: self.options.width == 'auto' ? '100%'
|
||||
: self.options.width - Ox.UI.SCROLLBAR_SIZE + 'px'
|
||||
}),
|
||||
$cell = $('<div>').addClass('OxCell').css({width: '8px'}),
|
||||
$icon = data.id ? getIcon(data.id, data.expanded || (
|
||||
data.items ? false : null
|
||||
)) : null,
|
||||
padding = data.level * 16 - 8;
|
||||
if (data.level) {
|
||||
$('<div>')
|
||||
.addClass('OxCell OxTarget')
|
||||
.css({width: padding + 'px'})
|
||||
.appendTo($item);
|
||||
}
|
||||
$cell.appendTo($item);
|
||||
$icon && $icon.addClass(data.items ? 'OxToggle' : 'OxTarget').appendTo($cell);
|
||||
$('<div>')
|
||||
.addClass('OxCell OxTarget')
|
||||
.css({
|
||||
width: self.options.width - padding - 32 - Ox.UI.SCROLLBAR_SIZE + 'px'
|
||||
})
|
||||
.html(data.title || '')
|
||||
.appendTo($item);
|
||||
return $item;
|
||||
}
|
||||
|
||||
function getIcon(id, expanded) {
|
||||
var isFunction = Ox.isFunction(self.options.icon),
|
||||
$icon = isFunction ? self.options.icon(id, expanded) : null;
|
||||
if (!$icon) {
|
||||
if (expanded === null) {
|
||||
if (!$icon && self.options.icon && !isFunction) {
|
||||
$icon = $('<img>').attr({src: self.options.icon});
|
||||
}
|
||||
} else {
|
||||
$icon = $('<img>').attr({src: Ox.UI.getImageURL(
|
||||
'symbol' + (expanded ? 'Down' : 'Right')
|
||||
)});
|
||||
}
|
||||
}
|
||||
return $icon;
|
||||
}
|
||||
|
||||
function getItemById(id, items, level) {
|
||||
var ret = null;
|
||||
items = items || self.options.items;
|
||||
level = level || 0;
|
||||
Ox.forEach(items, function(item) {
|
||||
if (item.id == id) {
|
||||
ret = Ox.extend(item, {
|
||||
level: level
|
||||
});
|
||||
return false; // break
|
||||
}
|
||||
if (item.items) {
|
||||
ret = getItemById(id, item.items, level + 1);
|
||||
if (ret) {
|
||||
return false; // break
|
||||
}
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getItems() {
|
||||
var items = [];
|
||||
Ox.sort(Object.keys(self.options.data)).forEach(function(key) {
|
||||
items.push(parseData(key, self.options.data[key]));
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
function getParent(id, items) {
|
||||
var ret;
|
||||
Ox.forEach(items, function(item) {
|
||||
if (item.items) {
|
||||
if (Ox.getObjectById(item.items, id)) {
|
||||
ret = item.id;
|
||||
} else {
|
||||
ret = getParent(id, item.items);
|
||||
}
|
||||
if (ret) {
|
||||
return false; // break
|
||||
}
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
function parseData(key, value) {
|
||||
var ret = {
|
||||
id: Ox.uid().toString(),
|
||||
title: Ox.encodeHTMLEntities(key.toString()) + ': '
|
||||
},
|
||||
type = Ox.typeOf(value);
|
||||
if (type == 'array' || type == 'object') {
|
||||
ret.expanded = self.options.expanded;
|
||||
ret.title += Ox.toTitleCase(type)
|
||||
+ ' <span class="OxLight">[' + Ox.len(value) + ']</span>';
|
||||
ret.items = type == 'array' ? value.map(function(v, i) {
|
||||
return parseData(i, v);
|
||||
}) : Ox.sort(Object.keys(value)).map(function(k) {
|
||||
return parseData(k, value[k]);
|
||||
});
|
||||
} else {
|
||||
ret.title += (
|
||||
type == 'function'
|
||||
? value.toString().split('{')[0]
|
||||
: Ox.encodeHTMLEntities(JSON.stringify(value))
|
||||
.replace(/(^"|"$)/g, '<span class="OxLight">"</span>')
|
||||
);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function parseItems(items, level) {
|
||||
var ret = [];
|
||||
items = items || self.options.items;
|
||||
level = level || 0;
|
||||
items.forEach(function(item, i) {
|
||||
if (item.items && self.options.expanded) {
|
||||
item.expanded = true;
|
||||
}
|
||||
var item_ = Ox.extend({level: level}, item, item.items ? {
|
||||
items: item.expanded
|
||||
? parseItems(item.items, level + 1)
|
||||
: []
|
||||
} : {});
|
||||
ret.push(item_);
|
||||
if (item.items) {
|
||||
ret = ret.concat(item_.items);
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
function selectItem(data) {
|
||||
var id = data.ids[0], parent = id, parents = [];
|
||||
while (parent = getParent(parent, self.options.items)) {
|
||||
parents.push(parent);
|
||||
}
|
||||
parents = parents.reverse();
|
||||
toggleItems({
|
||||
expanded: true,
|
||||
ids: parents
|
||||
});
|
||||
self.$list.options({selected: data.ids})
|
||||
}
|
||||
|
||||
function toggleItem(item, expanded) {
|
||||
var $img, pos;
|
||||
item.expanded = expanded;
|
||||
that.find('.OxItem').each(function() {
|
||||
var $item = $(this);
|
||||
if ($item.data('id') == item.id) {
|
||||
$img = $item.find('.OxToggle');
|
||||
pos = $item.data('position');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
//self.$list.value(item.id, 'expanded', expanded);
|
||||
$img.attr({
|
||||
src: getIcon(item.id, expanded).attr('src')
|
||||
});
|
||||
if (expanded) {
|
||||
self.$list.addItems(
|
||||
pos + 1, parseItems(item.items, item.level + 1)
|
||||
);
|
||||
} else {
|
||||
self.$list.removeItems(
|
||||
pos + 1, parseItems(item.items, item.level + 1).length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleItems(data) {
|
||||
data.ids.forEach(function(id, i) {
|
||||
var item = getItemById(id);
|
||||
if (item.items && data.expanded != !!item.expanded) {
|
||||
toggleItem(item, data.expanded);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*@
|
||||
gainFocus <f> gainFocus
|
||||
@*/
|
||||
that.gainFocus = function() {
|
||||
self.$list.gainFocus();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
hasFocus <f> hasFocus
|
||||
@*/
|
||||
that.hasFocus = function() {
|
||||
return self.$list.hasFocus();
|
||||
};
|
||||
|
||||
/*@
|
||||
loseFocus <f> loseFocus
|
||||
@*/
|
||||
that.loseFocus = function() {
|
||||
self.$list.loseFocus();
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue