forked from 0x2620/oxjs
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
437
source/UI/js/Form/ArrayEditable.js
Normal file
437
source/UI/js/Form/ArrayEditable.js
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.ArrayEditable <f> Array Editable
|
||||
options <o> Options object
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Array Editable
|
||||
add <!> add
|
||||
blur <!> blur
|
||||
change <!> change
|
||||
delete <!> delete
|
||||
edit <!> edit
|
||||
insert <!> insert
|
||||
open <!> open
|
||||
selectnext <!> selectnext
|
||||
selectprevious <!> selectprevious
|
||||
selectnone <!> selectnone
|
||||
select <!> select
|
||||
submit <!> submit
|
||||
@*/
|
||||
|
||||
Ox.ArrayEditable = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
clickLink: null,
|
||||
editable: true,
|
||||
getSortValue: null,
|
||||
globalAttributes: [],
|
||||
highlight: '',
|
||||
itemName: 'item',
|
||||
items: [],
|
||||
maxHeight: void 0,
|
||||
placeholder: '',
|
||||
position: -1,
|
||||
selected: '',
|
||||
separator: ',',
|
||||
sort: [],
|
||||
submitOnBlur: true,
|
||||
tooltipText: '',
|
||||
type: 'input',
|
||||
width: 256
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
highlight: function() {
|
||||
self.$items.forEach(function($item) {
|
||||
$item.options({highlight: self.options.highlight})
|
||||
});
|
||||
},
|
||||
items: function() {
|
||||
if (self.options.selected && getSelectedPosition() == -1) {
|
||||
selectNone();
|
||||
}
|
||||
renderItems(true);
|
||||
},
|
||||
placeholder: function() {
|
||||
if (self.options.items.length == 0) {
|
||||
self.$items[0].options({
|
||||
placeholder: self.options.placeholder,
|
||||
value: ''
|
||||
});
|
||||
}
|
||||
},
|
||||
selected: function() {
|
||||
selectItem(self.options.selected);
|
||||
},
|
||||
sort: renderItems,
|
||||
width: function() {
|
||||
var width = self.options.width;
|
||||
that.css({width: width - 8 + 'px'}); // 2 x 4 px padding
|
||||
self.options.type == 'textarea' && self.$items.forEach(function($item) {
|
||||
$item.options({width: width})
|
||||
});
|
||||
}
|
||||
})
|
||||
.addClass('OxArrayEditable OxArrayEditable' + Ox.toTitleCase(self.options.type))
|
||||
.css({width: self.options.width - (self.options.type == 'input' ? 8 : 0) + 'px'}) // 2 x 4 px padding
|
||||
.bindEvent({
|
||||
key_delete: deleteItem,
|
||||
key_enter: function() {
|
||||
// make sure the newline does
|
||||
// not end up in the textarea
|
||||
setTimeout(function() {
|
||||
that.editItem();
|
||||
}, 0);
|
||||
},
|
||||
key_escape: selectNone,
|
||||
key_down: self.options.type == 'input' ? selectLast : selectNext,
|
||||
key_left: self.options.type == 'input' ? selectPrevious : selectFirst,
|
||||
key_right: self.options.type == 'input' ? selectNext : selectLast,
|
||||
key_up: self.options.type == 'input' ? selectFirst : selectPrevious,
|
||||
singleclick: singleclick
|
||||
});
|
||||
|
||||
self.$items = [];
|
||||
self.editing = false;
|
||||
|
||||
renderItems();
|
||||
|
||||
self.selected = getSelectedPosition();
|
||||
|
||||
function deleteItem() {
|
||||
if (self.options.editable) {
|
||||
self.options.items.splice(self.selected, 1);
|
||||
renderItems();
|
||||
that.triggerEvent('delete', {
|
||||
id: self.options.selected
|
||||
});
|
||||
self.editing = false;
|
||||
self.selected = -1;
|
||||
self.options.selected = '';
|
||||
}
|
||||
}
|
||||
|
||||
function doubleclick(e) {
|
||||
// fixme: unused
|
||||
var $target = $(e.target),
|
||||
$parent = $target.parent();
|
||||
if ($parent.is('.OxEditableElement')) {
|
||||
that.editItem();
|
||||
} else if (!$target.is('.OxInput')) {
|
||||
that.triggerEvent('add');
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedId() {
|
||||
return self.selected > -1 ? self.options.items[self.selected].id : '';
|
||||
}
|
||||
|
||||
function getSelectedPosition() {
|
||||
return Ox.getIndexById(self.options.items, self.options.selected);
|
||||
}
|
||||
|
||||
function renderItems(blur) {
|
||||
if (self.editing) {
|
||||
self.options.items[getSelectedPosition()].value = that.find(self.options.type + ':visible').val();
|
||||
}
|
||||
that.empty();
|
||||
if (self.options.items.length == 0) {
|
||||
self.$items[0] = Ox.Editable({
|
||||
editable: false,
|
||||
placeholder: self.options.placeholder,
|
||||
type: self.options.type,
|
||||
value: ''
|
||||
})
|
||||
.appendTo(that);
|
||||
} else {
|
||||
sortItems();
|
||||
self.options.items.forEach(function(item, i) {
|
||||
if (i && self.options.type == 'input') {
|
||||
$('<span>')
|
||||
.addClass('OxSeparator')
|
||||
.html(self.options.separator + ' ')
|
||||
.appendTo(that);
|
||||
}
|
||||
self.$items[i] = Ox.Editable({
|
||||
blurred: self.editing && i == self.selected ? blur : false,
|
||||
clickLink: self.options.clickLink,
|
||||
editable: self.options.editable && item.editable,
|
||||
editing: self.editing && i == self.selected,
|
||||
globalAttributes: self.options.globalAttributes,
|
||||
highlight: self.options.highlight,
|
||||
maxHeight: self.options.maxHeight,
|
||||
submitOnBlur: self.options.submitOnBlur,
|
||||
tooltip: (
|
||||
self.options.tooltipText
|
||||
? self.options.tooltipText(item) + '<br>'
|
||||
: ''
|
||||
) + (
|
||||
self.options.editable
|
||||
? Ox._('Click to select') + (
|
||||
item.editable ? Ox._(', doubleclick to edit') : ''
|
||||
)
|
||||
: ''
|
||||
),
|
||||
type: self.options.type,
|
||||
value: item.value,
|
||||
width: self.options.type == 'input' ? 0 : self.options.width - 9
|
||||
})
|
||||
.addClass(item.id == self.options.selected ? 'OxSelected' : '')
|
||||
.data({id: item.id, position: i})
|
||||
.bindEvent({
|
||||
blur: function(data) {
|
||||
// fixme: remove data
|
||||
that.gainFocus();
|
||||
that.triggerEvent('blur', {
|
||||
id: item.id,
|
||||
value: data.value
|
||||
});
|
||||
self.blurred = true;
|
||||
setTimeout(function() {
|
||||
self.blurred = false;
|
||||
}, 250);
|
||||
},
|
||||
cancel: function(data) {
|
||||
self.editing = false;
|
||||
that.gainFocus();
|
||||
data.value === ''
|
||||
? submitItem(i, '')
|
||||
: that.triggerEvent('blur', data);
|
||||
},
|
||||
change: function(data) {
|
||||
that.triggerEvent('change', {
|
||||
id: item.id,
|
||||
value: data.value
|
||||
});
|
||||
},
|
||||
edit: function() {
|
||||
if (item.id != self.options.selected) {
|
||||
selectItem(item.id);
|
||||
}
|
||||
self.editing = true;
|
||||
that.triggerEvent('edit');
|
||||
},
|
||||
insert: function(data) {
|
||||
that.triggerEvent('insert', data);
|
||||
},
|
||||
open: function(data) {
|
||||
that.triggerEvent('open');
|
||||
},
|
||||
submit: function(data) {
|
||||
self.editing = false;
|
||||
that.gainFocus();
|
||||
submitItem(i, data.value);
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
});
|
||||
}
|
||||
//self.editing && that.editItem(blur);
|
||||
}
|
||||
|
||||
function selectFirst() {
|
||||
if (self.selected > -1) {
|
||||
self.selected > 0
|
||||
? selectItem(0)
|
||||
: that.triggerEvent('selectprevious');
|
||||
}
|
||||
}
|
||||
|
||||
function selectItem(idOrPosition) {
|
||||
if (Ox.isString(idOrPosition)) {
|
||||
self.options.selected = idOrPosition;
|
||||
self.selected = getSelectedPosition();
|
||||
} else {
|
||||
self.selected = idOrPosition;
|
||||
self.options.selected = getSelectedId();
|
||||
}
|
||||
if (/*self.options.selected == '' && */self.editing) {
|
||||
self.editing = false;
|
||||
that.blurItem();
|
||||
}
|
||||
that.find('.OxSelected').removeClass('OxSelected');
|
||||
self.selected > -1 && self.$items[self.selected].addClass('OxSelected');
|
||||
triggerSelectEvent();
|
||||
}
|
||||
|
||||
function selectLast() {
|
||||
if (self.selected > -1) {
|
||||
self.selected < self.options.items.length - 1
|
||||
? selectItem(self.options.items.length - 1)
|
||||
: that.triggerEvent('selectnext');
|
||||
}
|
||||
}
|
||||
|
||||
function selectNext() {
|
||||
if (self.selected > -1) {
|
||||
self.selected < self.options.items.length - 1
|
||||
? selectItem(self.selected + 1)
|
||||
: that.triggerEvent('selectnext');
|
||||
}
|
||||
}
|
||||
|
||||
function selectNone() {
|
||||
selectItem(-1);
|
||||
}
|
||||
|
||||
function selectPrevious() {
|
||||
if (self.selected > -1) {
|
||||
self.selected > 0
|
||||
? selectItem(self.selected - 1)
|
||||
: that.triggerEvent('selectprevious');
|
||||
}
|
||||
}
|
||||
|
||||
function singleclick(e) {
|
||||
var $target = $(e.target),
|
||||
$element = $target.is('.OxEditableElement')
|
||||
? $target : $target.parents('.OxEditableElement'),
|
||||
position;
|
||||
if (!$target.is('.OxInput')) {
|
||||
if ($element.length) {
|
||||
position = $element.data('position');
|
||||
// if clicked on an element
|
||||
if (position != self.selected) {
|
||||
// select another item
|
||||
selectItem(position);
|
||||
} else if (e.metaKey) {
|
||||
// or deselect current item
|
||||
selectNone();
|
||||
}
|
||||
} else if (!self.blurred) {
|
||||
// otherwise, if there wasn't an active input element
|
||||
if (self.editing) {
|
||||
// blur if still in editing mode
|
||||
that.blurItem();
|
||||
} else {
|
||||
// otherwise
|
||||
if (self.selected > -1) {
|
||||
// deselect selected
|
||||
selectNone();
|
||||
} else {
|
||||
// or trigger event
|
||||
that.triggerEvent('selectnone');
|
||||
}
|
||||
}
|
||||
}
|
||||
that.gainFocus();
|
||||
}
|
||||
}
|
||||
|
||||
function sortItems() {
|
||||
if (!Ox.isEmpty(self.options.sort)) {
|
||||
self.options.items = Ox.sortBy(
|
||||
self.options.items,
|
||||
self.options.sort,
|
||||
self.options.getSortValue
|
||||
? {value: self.options.getSortValue}
|
||||
: {}
|
||||
);
|
||||
self.selected = getSelectedPosition();
|
||||
}
|
||||
}
|
||||
|
||||
function submitItem(position, value) {
|
||||
var item = self.options.items[position];
|
||||
if (value === '') {
|
||||
deleteItem();
|
||||
} else {
|
||||
that.triggerEvent(item.value === value ? 'blur' : 'submit', {
|
||||
id: item.id,
|
||||
value: value
|
||||
});
|
||||
item.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
var triggerSelectEvent = Ox.debounce(function() {
|
||||
that.triggerEvent('select', {
|
||||
id: self.options.selected
|
||||
});
|
||||
}, true);
|
||||
|
||||
/*@
|
||||
addItem <f> addItem
|
||||
(position, item) -> <o> add item at position
|
||||
@*/
|
||||
that.addItem = function(position, item) {
|
||||
if (self.options.editable) {
|
||||
self.options.items.splice(position, 0, item);
|
||||
renderItems();
|
||||
}
|
||||
return that;
|
||||
//that.triggerEvent('add');
|
||||
/*
|
||||
self.values = Ox.filter(values, function(value) {
|
||||
return value;
|
||||
});
|
||||
self.values.push('');
|
||||
renderItems();
|
||||
Ox.last(self.$items).triggerEvent('doubleclick');
|
||||
*/
|
||||
};
|
||||
|
||||
/*@
|
||||
blurItem <f> blurItem
|
||||
@*/
|
||||
that.blurItem = function() {
|
||||
/*
|
||||
if (self.options.selected) {
|
||||
self.$items[self.selected].options({editing: false});
|
||||
} else {
|
||||
*/
|
||||
self.editing = false;
|
||||
self.$items.forEach(function($item) {
|
||||
$item.options({editing: false});
|
||||
});
|
||||
//}
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
editItem <f> editItem
|
||||
@*/
|
||||
that.editItem = function() {
|
||||
Ox.Log('AE', 'EDIT ITEM', self.options.editable, self.options.selected);
|
||||
if (self.options.editable && self.options.selected) {
|
||||
self.editing = true;
|
||||
self.$items[self.selected].options({editing: true});
|
||||
} else if (!self.options.editable) {
|
||||
that.triggerEvent('open');
|
||||
}
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
reloadItems <f> reloadItems
|
||||
@*/
|
||||
that.reloadItems = function() {
|
||||
renderItems();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
removeItem <f> removeItem
|
||||
@*/
|
||||
that.removeItem = function() {
|
||||
if (self.options.editable && self.options.selected) {
|
||||
deleteItem();
|
||||
}
|
||||
return that;
|
||||
};
|
||||
|
||||
/*
|
||||
that.submitItem = function() {
|
||||
if (self.editing) {
|
||||
self.editing = false;
|
||||
self.$items[self.selected].options({editing: false});
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
198
source/UI/js/Form/ArrayInput.js
Normal file
198
source/UI/js/Form/ArrayInput.js
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.ArrayInput <f> Array input
|
||||
options <o> Options object
|
||||
label <s> string, ''
|
||||
max <n> integer, maximum number of items, 0 for all
|
||||
sort <b> fixme: this should probably be removed
|
||||
value <[]> value
|
||||
width <n|256> width
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Array input
|
||||
change <!> change
|
||||
@*/
|
||||
|
||||
Ox.ArrayInput = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
label: '',
|
||||
max: 0,
|
||||
sort: false, // fixme: this should probably be removed
|
||||
value: [],
|
||||
width: 256
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
value: setValue,
|
||||
width: setWidths
|
||||
});
|
||||
|
||||
if (self.options.label) {
|
||||
self.$label = Ox.Label({
|
||||
title: self.options.label,
|
||||
width: self.options.width
|
||||
})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
self.$element = [];
|
||||
self.$input = [];
|
||||
self.$removeButton = [];
|
||||
self.$addButton = [];
|
||||
|
||||
(
|
||||
self.options.value.length ? self.options.value : ['']
|
||||
).forEach(function(value, i) {
|
||||
addInput(i, value);
|
||||
});
|
||||
|
||||
self.options.value = getValue();
|
||||
|
||||
function addInput(index, value, focus) {
|
||||
Ox.Log('Form', 'add', index)
|
||||
self.$element.splice(index, 0, Ox.Element()
|
||||
.css({
|
||||
height: '16px',
|
||||
marginTop: self.options.label || index > 0 ? '8px' : 0
|
||||
})
|
||||
.data({index: index}));
|
||||
if (index == 0) {
|
||||
self.$element[index].appendTo(that);
|
||||
} else {
|
||||
self.$element[index].insertAfter(self.$element[index - 1]);
|
||||
}
|
||||
self.$input.splice(index, 0, Ox.Input({
|
||||
value: value,
|
||||
width: self.options.width - 48
|
||||
})
|
||||
.css({float: 'left'})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
self.options.sort && data.value !== '' && sortInputs();
|
||||
self.options.value = getValue();
|
||||
that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
})
|
||||
.appendTo(self.$element[index]));
|
||||
focus && self.$input[index].focusInput(true);
|
||||
self.$removeButton.splice(index, 0, Ox.Button({
|
||||
title: self.$input.length == 1 ? 'close' : 'remove',
|
||||
type: 'image'
|
||||
})
|
||||
.css({float: 'left', marginLeft: '8px'})
|
||||
.on({
|
||||
click: function() {
|
||||
var index = $(this).parent().data('index');
|
||||
if (self.$input[index].value() !== '') {
|
||||
self.$input[index].value('');
|
||||
self.options.value = getValue();
|
||||
that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
if (self.$input.length == 1) {
|
||||
self.$input[0].focusInput(true);
|
||||
} else {
|
||||
removeInput(index);
|
||||
}
|
||||
}
|
||||
})
|
||||
.appendTo(self.$element[index]));
|
||||
self.$addButton.splice(index, 0, Ox.Button({
|
||||
disabled: index == self.options.max - 1,
|
||||
title: 'add',
|
||||
type: 'image'
|
||||
})
|
||||
.css({float: 'left', marginLeft: '8px'})
|
||||
.on({
|
||||
click: function() {
|
||||
var index = $(this).parent().data('index');
|
||||
addInput(index + 1, '', true);
|
||||
}
|
||||
})
|
||||
.appendTo(self.$element[index]));
|
||||
updateInputs();
|
||||
}
|
||||
|
||||
function getValue() {
|
||||
return Ox.map(self.$input, function($input) {
|
||||
return $input.value();
|
||||
}).filter(function(value) {
|
||||
return value !== '';
|
||||
});
|
||||
};
|
||||
|
||||
function removeInput(index) {
|
||||
Ox.Log('Form', 'remove', index);
|
||||
[
|
||||
'input', 'removeButton', 'addButton', 'element'
|
||||
].forEach(function(element) {
|
||||
var key = '$' + element;
|
||||
self[key][index].remove();
|
||||
self[key].splice(index, 1);
|
||||
});
|
||||
updateInputs();
|
||||
}
|
||||
|
||||
function setValue() {
|
||||
while (self.$input.length) {
|
||||
removeInput(0);
|
||||
}
|
||||
(
|
||||
self.options.value.length ? self.options.value : ['']
|
||||
).forEach(function(value, i) {
|
||||
addInput(i, value);
|
||||
});
|
||||
}
|
||||
|
||||
function setWidths() {
|
||||
self.$label && self.$label.options({width: self.options.width})
|
||||
self.$element.forEach(function($element, i) {
|
||||
$element.css({width: self.options.width + 'px'});
|
||||
self.$input[i].options({width: self.options.width - 48});
|
||||
});
|
||||
}
|
||||
|
||||
function sortInputs() {
|
||||
Ox.sort(self.$element, function($element) {
|
||||
return self.$input[$element.data('index')].value();
|
||||
}).forEach(function($element) {
|
||||
$element.detach();
|
||||
});
|
||||
self.$element.forEach(function($element, i) {
|
||||
$element.data({index: i}).appendTo(that);
|
||||
});
|
||||
}
|
||||
|
||||
function updateInputs() {
|
||||
self.$element.forEach(function($element, i) {
|
||||
$element.data({index: i});
|
||||
self.$removeButton[i].options({
|
||||
title: self.$element.length == 1 ? 'close' : 'remove'
|
||||
});
|
||||
self.$addButton[i].options({
|
||||
disabled: self.$element.length == self.options.max
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*@
|
||||
setErrors <f> setErrors
|
||||
(values) -> <u> set errors
|
||||
@*/
|
||||
that.setErrors = function(values) {
|
||||
self.$input.forEach(function($input) {
|
||||
$input[
|
||||
values.indexOf($input.value()) > -1 ? 'addClass' : 'removeClass'
|
||||
]('OxError');
|
||||
});
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
198
source/UI/js/Form/Button.js
Normal file
198
source/UI/js/Form/Button.js
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Button <f> Button Object
|
||||
options <o> Options object
|
||||
If a button is both selectable and has two values, its value is the
|
||||
selected id, and the second value corresponds to the selected state
|
||||
disabled <b|false> If true, button is disabled
|
||||
group <b|false> If true, button is part of group
|
||||
id <s|''> Element id
|
||||
overlap <s|'none'> 'none', 'left' or 'right'
|
||||
selectable <b|false> If true, button is selectable
|
||||
style <s|'default'> 'default', 'checkbox', 'symbol', 'tab' or 'video'
|
||||
title <s|''> Button title
|
||||
tooltip <s|[s]|''> Tooltip
|
||||
type <s|text> 'text' or 'image'
|
||||
value <b|s|undefined> True for selected, or current value id
|
||||
values <[o]|[]> [{id, title}, {id, title}]
|
||||
width <s|'auto'> Button width
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Button Object
|
||||
click <!> non-selectable button was clicked
|
||||
change <!> selectable button was clicked
|
||||
@*/
|
||||
|
||||
Ox.Button = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element('<input>', self)
|
||||
.defaults({
|
||||
disabled: false,
|
||||
group: false,
|
||||
id: '',
|
||||
overlap: 'none',
|
||||
selectable: false,
|
||||
size: 'medium',
|
||||
// fixme: 'default' or ''?
|
||||
style: 'default',
|
||||
title: '',
|
||||
tooltip: '',
|
||||
type: 'text',
|
||||
value: void 0,
|
||||
values: [],
|
||||
width: 'auto'
|
||||
})
|
||||
.options(Ox.isArray(options.tooltip) ? Ox.extend(Ox.clone(options), {
|
||||
tooltip: options.tooltip[0]
|
||||
}) : options || {})
|
||||
.update({
|
||||
disabled: setDisabled,
|
||||
tooltip: function() {
|
||||
if (Ox.isArray(self.options.tooltip) && that.$tooltip) {
|
||||
that.$tooltip.options({
|
||||
title: self.options.tooltip[self.value]
|
||||
});
|
||||
}
|
||||
},
|
||||
title: setTitle,
|
||||
value: function() {
|
||||
if (self.options.values.length) {
|
||||
self.options.title = Ox.getObjectById(
|
||||
self.options.values, self.options.value
|
||||
).title;
|
||||
setTitle();
|
||||
}
|
||||
self.options.selectable && setSelected();
|
||||
},
|
||||
width: function() {
|
||||
that.css({width: (self.options.width - 14) + 'px'});
|
||||
}
|
||||
})
|
||||
.addClass(
|
||||
'OxButton Ox' + Ox.toTitleCase(self.options.size)
|
||||
+ (self.options.disabled ? ' OxDisabled': '')
|
||||
+ (self.options.selectable && self.options.value ? ' OxSelected' : '')
|
||||
+ (self.options.style != 'default' ? ' Ox' + Ox.toTitleCase(self.options.style) : '')
|
||||
+ (self.options.overlap != 'none' ? ' OxOverlap' + Ox.toTitleCase(self.options.overlap) : '')
|
||||
)
|
||||
.attr({
|
||||
disabled: self.options.disabled,
|
||||
type: self.options.type == 'text' ? 'button' : 'image'
|
||||
})
|
||||
.css(self.options.width == 'auto' ? {} : {
|
||||
width: (self.options.width - 14) + 'px'
|
||||
})
|
||||
.on({
|
||||
click: click,
|
||||
mousedown: mousedown
|
||||
});
|
||||
|
||||
if (self.options.values.length) {
|
||||
self.options.values = self.options.values.map(function(value) {
|
||||
return {
|
||||
id: value.id || value,
|
||||
title: value.title || value
|
||||
};
|
||||
});
|
||||
self.value = Ox.getIndexById(self.options.values, self.options.value);
|
||||
if (self.value == -1) {
|
||||
self.value = 0;
|
||||
self.options.value = self.options.values[0].id;
|
||||
}
|
||||
self.options.title = self.options.values[self.value].title;
|
||||
} else if (self.options.selectable) {
|
||||
self.options.value = self.options.value || false;
|
||||
}
|
||||
|
||||
setTitle();
|
||||
|
||||
if (Ox.isArray(options.tooltip)) {
|
||||
self.options.tooltip = options.tooltip;
|
||||
// Ox.Element creates tooltip only on mouse over.
|
||||
// create here to overwrite tooltip title
|
||||
if (!that.$tooltip) {
|
||||
that.$tooltip = Ox.Tooltip();
|
||||
}
|
||||
that.$tooltip.options({
|
||||
title: self.options.tooltip[self.value]
|
||||
});
|
||||
}
|
||||
|
||||
function click() {
|
||||
if (!self.options.disabled) {
|
||||
that.$tooltip && that.$tooltip.hide();
|
||||
that.triggerEvent('click');
|
||||
if (self.options.values.length || self.options.selectable) {
|
||||
that.toggle();
|
||||
that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mousedown(e) {
|
||||
if (self.options.type == 'image' && $.browser.safari) {
|
||||
// keep image from being draggable
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function setDisabled() {
|
||||
that.attr({disabled: self.options.disabled});
|
||||
that[self.options.disabled ? 'addClass' : 'removeClass']('OxDisabled');
|
||||
self.options.disabled && that.$tooltip && that.$tooltip.hide();
|
||||
self.options.type == 'image' && setTitle();
|
||||
}
|
||||
|
||||
function setSelected() {
|
||||
that[self.options.value ? 'addClass' : 'removeClass']('OxSelected');
|
||||
self.options.type == 'image' && setTitle();
|
||||
}
|
||||
|
||||
function setTitle() {
|
||||
if (self.options.type == 'image') {
|
||||
that.attr({
|
||||
src: Ox.UI.getImageURL(
|
||||
'symbol' + self.options.title[0].toUpperCase()
|
||||
+ self.options.title.slice(1),
|
||||
self.options.style == 'overlay' ? 'overlay' + (
|
||||
self.options.disabled ? 'Disabled'
|
||||
: self.options.selectable && self.options.value ? 'Selected'
|
||||
: ''
|
||||
)
|
||||
: self.options.style == 'video' ? 'video'
|
||||
: self.options.disabled ? 'disabled'
|
||||
: self.options.selectable && self.options.value ? 'selected'
|
||||
: ''
|
||||
)
|
||||
});
|
||||
} else {
|
||||
that.val(self.options.title);
|
||||
}
|
||||
}
|
||||
|
||||
/*@
|
||||
toggle <f> toggle
|
||||
() -> <o> toggle button
|
||||
@*/
|
||||
that.toggle = function() {
|
||||
if (self.options.values.length) {
|
||||
self.value = 1 - Ox.getIndexById(self.options.values, self.options.value);
|
||||
self.options.title = self.options.values[self.value].title;
|
||||
self.options.value = self.options.values[self.value].id;
|
||||
setTitle();
|
||||
// fixme: if the tooltip is visible
|
||||
// we also need to call show()
|
||||
that.$tooltip && that.$tooltip.options({
|
||||
title: self.options.tooltip[self.value]
|
||||
});
|
||||
} else {
|
||||
self.options.value = !self.options.value;
|
||||
}
|
||||
self.options.selectable && setSelected();
|
||||
return that;
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
157
source/UI/js/Form/ButtonGroup.js
Normal file
157
source/UI/js/Form/ButtonGroup.js
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.ButtonGroup <f> ButtonGroup Object
|
||||
options <o> Options object
|
||||
buttons <[o]> array of button options
|
||||
max <n> integer, maximum number of selected buttons, 0 for all
|
||||
min <n> integer, minimum number of selected buttons, 0 for none
|
||||
selectable <b> if true, buttons are selectable
|
||||
type <s> string, 'image' or 'text'
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> ButtonGroup Object
|
||||
change <!> {id, value} selection within a group changed
|
||||
@*/
|
||||
|
||||
Ox.ButtonGroup = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
buttons: [],
|
||||
max: 1,
|
||||
min: 1,
|
||||
overlap: 'none',
|
||||
selectable: false,
|
||||
size: 'medium',
|
||||
style: 'default',
|
||||
type: 'text',
|
||||
value: options.max != 1 ? [] : ''
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
value: function() {
|
||||
// fixme: this doesn't work in cases where
|
||||
// multiple buttons can be selected
|
||||
var position = Ox.getIndexById(
|
||||
self.options.buttons, self.options.value
|
||||
);
|
||||
if (position > -1) {
|
||||
self.$buttons[position].trigger('click');
|
||||
} else if (self.options.min == 0) {
|
||||
self.$buttons.forEach(function($button, i) {
|
||||
$button.options('value') && $button.trigger('click');
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.addClass(
|
||||
'OxButtonGroup'
|
||||
+ (self.options.style != 'default' ? ' Ox' + Ox.toTitleCase(self.options.style) : '')
|
||||
+ (self.options.overlap != 'none' ? ' OxOverlap' + Ox.toTitleCase(self.options.overlap) : '')
|
||||
);
|
||||
|
||||
self.options.buttons = self.options.buttons.map(function(button, i) {
|
||||
return Ox.extend({
|
||||
disabled: button.disabled,
|
||||
id: button.id || button,
|
||||
overlap: self.options.overlap == 'left' && i == 0 ? 'left'
|
||||
: self.options.overlap == 'right' && i == self.options.buttons.length - 1 ? 'right'
|
||||
: 'none',
|
||||
title: button.title || button,
|
||||
tooltip: button.tooltip,
|
||||
width: button.width
|
||||
}, self.options.selectable ? {
|
||||
selected: Ox.makeArray(self.options.value).indexOf(button.id || button) > -1
|
||||
} : {});
|
||||
});
|
||||
|
||||
if (self.options.selectable) {
|
||||
self.optionGroup = new Ox.OptionGroup(
|
||||
self.options.buttons,
|
||||
self.options.min,
|
||||
self.options.max,
|
||||
'selected'
|
||||
);
|
||||
self.options.buttons = self.optionGroup.init();
|
||||
self.options.value = self.optionGroup.value();
|
||||
}
|
||||
|
||||
self.$buttons = [];
|
||||
self.options.buttons.forEach(function(button, pos) {
|
||||
self.$buttons[pos] = Ox.Button({
|
||||
disabled: button.disabled || false, // FIXME: getset should handle undefined
|
||||
group: true,
|
||||
id: button.id,
|
||||
overlap: button.overlap,
|
||||
selectable: self.options.selectable,
|
||||
size: self.options.size,
|
||||
style: self.options.style == 'squared' ? 'default' : self.options.style, // FIXME: ugly
|
||||
title: button.title,
|
||||
tooltip: button.tooltip,
|
||||
type: self.options.type,
|
||||
value: button.selected || false, // FIXME: getset should handle undefined
|
||||
width: button.width
|
||||
})
|
||||
.bindEvent(self.options.selectable ? {
|
||||
change: function() {
|
||||
toggleButton(pos);
|
||||
}
|
||||
} : {
|
||||
click: function() {
|
||||
that.triggerEvent('click', {id: button.id});
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
});
|
||||
|
||||
function getButtonById(id) {
|
||||
return self.$buttons[Ox.getIndexById(self.options.buttons, id)];
|
||||
}
|
||||
|
||||
function toggleButton(pos) {
|
||||
var toggled = self.optionGroup.toggle(pos);
|
||||
if (!toggled.length) {
|
||||
// FIXME: fix and use that.toggleOption()
|
||||
self.$buttons[pos].value(!self.$buttons[pos].value());
|
||||
} else {
|
||||
toggled.forEach(function(i) {
|
||||
i != pos && self.$buttons[i].value(!self.$buttons[i].value());
|
||||
});
|
||||
self.options.value = self.optionGroup.value();
|
||||
that.triggerEvent('change', {
|
||||
title: self.options.value === '' ? ''
|
||||
: Ox.isString(self.options.value)
|
||||
? Ox.getObjectById(self.options.buttons, self.options.value).title
|
||||
: self.options.value.map(function(value) {
|
||||
return Ox.getObjectById(self.options.buttons, value).title;
|
||||
}),
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*@
|
||||
disableButton <f> Disable button
|
||||
@*/
|
||||
that.disableButton = function(id) {
|
||||
getButtonById(id).options({disabled: true});
|
||||
};
|
||||
|
||||
/*@
|
||||
enableButton <f> Enable button
|
||||
@*/
|
||||
that.enableButton = function(id) {
|
||||
getButtonById(id).options({disabled: false});
|
||||
};
|
||||
|
||||
/*
|
||||
buttonOptions <f> Get or set button options
|
||||
*/
|
||||
that.buttonOptions = function(id, options) {
|
||||
return getButtonById(id).options(options);
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
123
source/UI/js/Form/Checkbox.js
Normal file
123
source/UI/js/Form/Checkbox.js
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Checkbox <f> Checkbox Element
|
||||
options <o> Options object
|
||||
disabled <b> if true, checkbox is disabled
|
||||
group <b> if true, checkbox is part of a group
|
||||
label <s> Label (on the left side)
|
||||
labelWidth <n|64> Label width
|
||||
title <s> Title (on the right side)
|
||||
value <b> if true, checkbox is checked
|
||||
width <n> width in px
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Checkbox Element
|
||||
change <!> triggered when value changes
|
||||
@*/
|
||||
|
||||
Ox.Checkbox = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
disabled: false,
|
||||
group: false,
|
||||
label: '',
|
||||
labelWidth: 64,
|
||||
overlap: 'none',
|
||||
title: '',
|
||||
value: false,
|
||||
width: options && (options.label || options.title) ? 'auto' : 16
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
disabled: function() {
|
||||
var disabled = self.options.disabled;
|
||||
that.attr({disabled: disabled});
|
||||
self.$button.options({disabled: disabled});
|
||||
self.$title && self.$title.options({disabled: disabled});
|
||||
},
|
||||
label: function() {
|
||||
self.$label.options({title: self.options.label});
|
||||
},
|
||||
title: function() {
|
||||
self.$title.options({title: self.options.title});
|
||||
},
|
||||
value: function() {
|
||||
self.$button.toggle();
|
||||
},
|
||||
width: function() {
|
||||
that.css({width: self.options.width + 'px'});
|
||||
self.$title && self.$title.options({width: getTitleWidth()});
|
||||
}
|
||||
})
|
||||
.addClass('OxCheckbox' + (
|
||||
self.options.overlap == 'none'
|
||||
? '' : ' OxOverlap' + Ox.toTitleCase(self.options.overlap)
|
||||
))
|
||||
.attr({
|
||||
disabled: self.options.disabled
|
||||
})
|
||||
.css(self.options.width != 'auto' ? {
|
||||
width: self.options.width
|
||||
} : {});
|
||||
|
||||
if (self.options.title) {
|
||||
self.options.width != 'auto' && that.css({
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
self.$title = Ox.Label({
|
||||
disabled: self.options.disabled,
|
||||
id: self.options.id + 'Label',
|
||||
overlap: 'left',
|
||||
title: self.options.title,
|
||||
width: getTitleWidth()
|
||||
})
|
||||
.css({float: 'right'})
|
||||
.on({click: clickTitle})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
if (self.options.label) {
|
||||
self.$label = Ox.Label({
|
||||
overlap: 'right',
|
||||
textAlign: 'right',
|
||||
title: self.options.label,
|
||||
width: self.options.labelWidth
|
||||
})
|
||||
.css({float: 'left'})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
self.$button = Ox.Button({
|
||||
disabled: self.options.disabled,
|
||||
id: self.options.id + 'Button',
|
||||
type: 'image',
|
||||
value: self.options.value ? 'check' : 'none',
|
||||
values: ['none', 'check']
|
||||
})
|
||||
.addClass('OxCheckbox')
|
||||
.bindEvent({
|
||||
change: clickButton
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
function clickButton() {
|
||||
self.options.value = !self.options.value;
|
||||
that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
|
||||
function clickTitle() {
|
||||
!self.options.disabled && self.$button.trigger('click');
|
||||
}
|
||||
|
||||
function getTitleWidth() {
|
||||
return self.options.width - 16
|
||||
- !!self.options.label * self.options.labelWidth;
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
119
source/UI/js/Form/CheckboxGroup.js
Normal file
119
source/UI/js/Form/CheckboxGroup.js
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.CheckboxGroup <f> CheckboxGroup Object
|
||||
options <o> Options object
|
||||
checkboxes <a|[]> array of checkboxes
|
||||
max <n|1> max selected
|
||||
min <n|1> min selected
|
||||
type <s|"group"> type ("group" or "list")
|
||||
width <n> width in px
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> CheckboxGroup Object
|
||||
change <!> triggered when checked property changes
|
||||
passes {id, title, value}
|
||||
@*/
|
||||
|
||||
Ox.CheckboxGroup = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
// fixme: 'checkboxes' should be 'items'?
|
||||
checkboxes: [],
|
||||
max: 1,
|
||||
min: 1,
|
||||
type: 'group',
|
||||
value: options.max != 1 ? [] : '',
|
||||
width: 256
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
value: function() {
|
||||
var value = Ox.clone(self.options.value);
|
||||
self.$checkboxes.forEach(function($checkbox, index) {
|
||||
var checked = Ox.contains(value, $checkbox.options('id'));
|
||||
if (checked != $checkbox.value()) {
|
||||
$checkbox.value(!$checkbox.value());
|
||||
toggleCheckbox(index);
|
||||
}
|
||||
});
|
||||
},
|
||||
width: function() {
|
||||
self.$checkboxes.forEach(function($checkbox) {
|
||||
$checkbox.options({width: self.options.width});
|
||||
});
|
||||
}
|
||||
})
|
||||
.addClass('OxCheckboxGroup Ox' + Ox.toTitleCase(self.options.type));
|
||||
|
||||
self.options.checkboxes = self.options.checkboxes.map(function(checkbox) {
|
||||
return {
|
||||
checked: Ox.makeArray(self.options.value).indexOf(checkbox.id || checkbox) > -1,
|
||||
id: checkbox.id || checkbox,
|
||||
title: checkbox.title || checkbox
|
||||
};
|
||||
});
|
||||
|
||||
self.optionGroup = new Ox.OptionGroup(
|
||||
self.options.checkboxes,
|
||||
self.options.min,
|
||||
self.options.max,
|
||||
'checked'
|
||||
);
|
||||
self.options.checkboxes = self.optionGroup.init();
|
||||
self.options.value = self.optionGroup.value();
|
||||
|
||||
self.$checkboxes = [];
|
||||
if (self.options.type == 'group') {
|
||||
self.checkboxWidth = Ox.splitInt(
|
||||
self.options.width + (self.options.checkboxes.length - 1) * 6,
|
||||
self.options.checkboxes.length
|
||||
).map(function(v, i) {
|
||||
return v + (i < self.options.checkboxes.length - 1 ? 10 : 0);
|
||||
});
|
||||
};
|
||||
|
||||
self.options.checkboxes.forEach(function(checkbox, pos) {
|
||||
self.$checkboxes[pos] = Ox.Checkbox(Ox.extend(checkbox, {
|
||||
group: true,
|
||||
id: checkbox.id,
|
||||
width: self.options.type == 'group'
|
||||
? self.checkboxWidth[pos] : self.options.width,
|
||||
value: checkbox.checked
|
||||
}))
|
||||
.bindEvent('change', function() {
|
||||
toggleCheckbox(pos);
|
||||
})
|
||||
.appendTo(that);
|
||||
});
|
||||
|
||||
function toggleCheckbox(pos) {
|
||||
var toggled = self.optionGroup.toggle(pos);
|
||||
Ox.Log('Form', 'change', pos, 'toggled', toggled)
|
||||
if (!toggled.length) {
|
||||
// FIXME: fix and use that.toggleOption()
|
||||
self.$checkboxes[pos].value(!self.$checkboxes[pos].value());
|
||||
} else {
|
||||
toggled.forEach(function(i) {
|
||||
i != pos && self.$checkboxes[i].value(!self.$checkboxes[i].value());
|
||||
});
|
||||
self.options.value = self.optionGroup.value();
|
||||
that.triggerEvent('change', {
|
||||
title: Ox.isString(self.options.value)
|
||||
? (
|
||||
self.options.value
|
||||
? Ox.getObjectById(self.options.checkboxes, self.options.value).title
|
||||
: ''
|
||||
)
|
||||
: self.options.value.map(function(value) {
|
||||
return Ox.getObjectById(self.options.checkboxes, value).title;
|
||||
}),
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
88
source/UI/js/Form/ColorInput.js
Normal file
88
source/UI/js/Form/ColorInput.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.ColorInput <f> ColorInput Element
|
||||
options <o> Options object
|
||||
mode <s|'rgb'> Mode ('rgb' or 'hsl')
|
||||
value <[n]|[0, 0, 0]> Value
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.InputGroup> ColorInput Element
|
||||
change <!> change
|
||||
@*/
|
||||
Ox.ColorInput = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
mode: 'rgb',
|
||||
value: options.mode == 'hsl' ? [0, 1, 0.5] : [0, 0, 0]
|
||||
})
|
||||
.options(options || {});
|
||||
|
||||
self.$inputs = [];
|
||||
Ox.loop(3, function(i) {
|
||||
self.$inputs[i] = Ox.Input({
|
||||
max: self.options.mode == 'rgb' ? 255 : i == 0 ? 360 : 1,
|
||||
type: self.options.mode == 'rgb' || i == 0 ? 'int' : 'float',
|
||||
value: self.options.value[i],
|
||||
width: 40
|
||||
})
|
||||
.bindEvent('autovalidate', change);
|
||||
});
|
||||
self.$inputs[3] = Ox.Label({
|
||||
width: 40
|
||||
})
|
||||
.css({
|
||||
background: 'rgb(' + getRGB().join(', ') + ')'
|
||||
});
|
||||
self.$inputs[4] = Ox.ColorPicker({
|
||||
mode: self.options.mode,
|
||||
width: 16
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
self.options.value = data.value;
|
||||
Ox.loop(3, function(i) {
|
||||
self.$inputs[i].options({value: self.options.value[i]});
|
||||
});
|
||||
self.$inputs[3].css({
|
||||
background: 'rgb(' + getRGB().join(', ') + ')'
|
||||
});
|
||||
}
|
||||
})
|
||||
.options({
|
||||
width: 16 // this is just a hack to make the InputGroup layout work
|
||||
});
|
||||
|
||||
that.setElement(Ox.InputGroup({
|
||||
inputs: self.$inputs,
|
||||
separators: [
|
||||
{title: ',', width: 8},
|
||||
{title: ',', width: 8},
|
||||
{title: '', width: 8},
|
||||
{title: '', width: 8}
|
||||
],
|
||||
value: Ox.clone(self.options.value)
|
||||
})
|
||||
.bindEvent('change', change)
|
||||
);
|
||||
|
||||
function change() {
|
||||
self.options.value = Ox.range(3).map(function(i) {
|
||||
return self.$inputs[i].options('value');
|
||||
})
|
||||
self.$inputs[3].css({
|
||||
background: 'rgb(' + getRGB().join(', ') + ')'
|
||||
});
|
||||
that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
|
||||
function getRGB() {
|
||||
return self.options.mode == 'rgb'
|
||||
? self.options.value
|
||||
: Ox.rgb(self.options.value);
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
117
source/UI/js/Form/ColorPicker.js
Normal file
117
source/UI/js/Form/ColorPicker.js
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.ColorPicker <f> ColorPicker Element
|
||||
options <o> Options object
|
||||
mode <s|'rgb'> Mode ('rgb' or 'hsl')
|
||||
value <[n]|[0, 0, 0]> Value
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Picker> ColorPicker Element
|
||||
change <!> triggered on change of value
|
||||
@*/
|
||||
|
||||
Ox.ColorPicker = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
mode: 'rgb',
|
||||
value: options && options.mode == 'hsl' ? [0, 1, 0.5] : [0, 0, 0]
|
||||
})
|
||||
.options(options || {});
|
||||
|
||||
//Ox.Log('Form', self)
|
||||
self.$ranges = [];
|
||||
|
||||
Ox.loop(3, function(i) {
|
||||
self.$ranges[i] = Ox.Range({
|
||||
arrows: true,
|
||||
changeOnDrag: true,
|
||||
id: self.options.id + i,
|
||||
max: self.options.mode == 'rgb' ? 255 : i == 0 ? 359 : 1,
|
||||
size: self.options.mode == 'rgb' ? 328 : 432, // 256|360 + 16 + 40 + 16
|
||||
step: self.options.mode == 'rgb' || i == 0 ? 1 : 0.01,
|
||||
thumbSize: 40,
|
||||
thumbValue: true,
|
||||
trackColors: getColors(i),
|
||||
trackGradient: true,
|
||||
value: self.options.value[i]
|
||||
})
|
||||
.css({
|
||||
position: 'absolute',
|
||||
top: (i * 15) + 'px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
change(i, data.value);
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
if (i == 0) {
|
||||
// fixme: this should go into Ox.UI.css
|
||||
self.$ranges[i].children('input.OxOverlapRight').css({
|
||||
borderRadius: 0
|
||||
});
|
||||
self.$ranges[i].children('input.OxOverlapLeft').css({
|
||||
borderRadius: '0 8px 0 0'
|
||||
});
|
||||
} else {
|
||||
self.$ranges[i].children('input').css({
|
||||
borderRadius: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
that = Ox.Picker({
|
||||
element: that,
|
||||
elementHeight: 46,
|
||||
elementWidth: self.options.mode == 'rgb' ? 328 : 432
|
||||
});
|
||||
|
||||
function change(index, value) {
|
||||
self.options.value[index] = value;
|
||||
that.$label.css({
|
||||
background: 'rgb(' + getRGB(self.options.value).join(', ') + ')'
|
||||
});
|
||||
Ox.loop(3, function(i) {
|
||||
if (i != index) {
|
||||
self.$ranges[i].options({
|
||||
trackColors: getColors(i)
|
||||
});
|
||||
}
|
||||
});
|
||||
that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
|
||||
function getColors(index) {
|
||||
return (
|
||||
self.options.mode == 'rgb' ? [
|
||||
Ox.range(3).map(function(i) {
|
||||
return i == index ? 0 : self.options.value[i];
|
||||
}),
|
||||
Ox.range(3).map(function(i) {
|
||||
return i == index ? 255 : self.options.value[i];
|
||||
})
|
||||
]
|
||||
: index == 0 ? Ox.range(7).map(function(i) {
|
||||
return [i * 60, self.options.value[1], self.options.value[2]];
|
||||
})
|
||||
: Ox.range(3).map(function(i) {
|
||||
return [
|
||||
self.options.value[0],
|
||||
index == 1 ? i / 2 : self.options.value[1],
|
||||
index == 2 ? i / 2 : self.options.value[2]
|
||||
];
|
||||
})
|
||||
).map(function(values) {
|
||||
return 'rgb(' + getRGB(values).join(', ') + ')';
|
||||
});
|
||||
}
|
||||
|
||||
function getRGB(values) {
|
||||
return self.options.mode == 'rgb' ? values : Ox.rgb(values);
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
216
source/UI/js/Form/DateInput.js
Normal file
216
source/UI/js/Form/DateInput.js
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.DateInput <f> DateInput Element
|
||||
options <o> Options object
|
||||
format <s|short> format can be short, medium, long
|
||||
value <d> date value, defaults to current date
|
||||
weekday <b|false> weekday
|
||||
width <o> width of individual input elements, in px
|
||||
day <n> width of day input element
|
||||
month <n> width of month input element
|
||||
weekday <n> width of weekday input element
|
||||
year <n> width of year input element
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.InputGroup> DateInput Element
|
||||
change <!> triggered on change of value
|
||||
@*/
|
||||
Ox.DateInput = function(options, self) {
|
||||
|
||||
var that;
|
||||
self = Ox.extend(self || {}, {
|
||||
options: Ox.extend({
|
||||
format: 'short',
|
||||
value: Ox.formatDate(new Date(), '%F'),
|
||||
weekday: false,
|
||||
width: {
|
||||
day: 32,
|
||||
month: options && options.format == 'long' ? 80
|
||||
: options && options.format == 'medium' ? 40 : 32,
|
||||
weekday: options && options.format == 'long' ? 80 : 40,
|
||||
year: 48
|
||||
}
|
||||
}, options || {})
|
||||
});
|
||||
|
||||
self.formats = {
|
||||
day: '%d',
|
||||
month: self.options.format == 'short' ? '%m' :
|
||||
(self.options.format == 'medium' ? '%b' : '%B'),
|
||||
weekday: self.options.format == 'long' ? '%A' : '%a',
|
||||
year: '%Y'
|
||||
};
|
||||
self.months = self.options.format == 'long' ? Ox.MONTHS : Ox.SHORT_MONTHS;
|
||||
self.weekdays = self.options.format == 'long' ? Ox.WEEKDAYS : Ox.SHORT_WEEKDAYS;
|
||||
|
||||
self.$input = Ox.extend(self.options.weekday ? {
|
||||
weekday: Ox.Input({
|
||||
autocomplete: self.weekdays,
|
||||
autocompleteReplace: true,
|
||||
autocompleteReplaceCorrect: true,
|
||||
id: 'weekday',
|
||||
width: self.options.width.weekday
|
||||
})
|
||||
.bindEvent('autocomplete', changeWeekday)
|
||||
} : {}, {
|
||||
day: Ox.Input({
|
||||
autocomplete: Ox.range(1, Ox.getDaysInMonth(
|
||||
parseInt(Ox.formatDate(self.date, '%Y'), 10),
|
||||
parseInt(Ox.formatDate(self.date, '%m'), 10)
|
||||
) + 1).map(function(i) {
|
||||
return self.options.format == 'short' ? Ox.pad(i, 2) : i.toString();
|
||||
}),
|
||||
autocompleteReplace: true,
|
||||
autocompleteReplaceCorrect: true,
|
||||
id: 'day',
|
||||
textAlign: 'right',
|
||||
width: self.options.width.day
|
||||
})
|
||||
.bindEvent('autocomplete', changeDay),
|
||||
month: Ox.Input({
|
||||
autocomplete: self.options.format == 'short' ? Ox.range(1, 13).map(function(i) {
|
||||
return Ox.pad(i, 2);
|
||||
}) : self.months,
|
||||
autocompleteReplace: true,
|
||||
autocompleteReplaceCorrect: true,
|
||||
id: 'month',
|
||||
textAlign: self.options.format == 'short' ? 'right' : 'left',
|
||||
width: self.options.width.month
|
||||
})
|
||||
.bindEvent('autocomplete', changeMonthOrYear),
|
||||
year: Ox.Input({
|
||||
autocomplete: Ox.range(1900, 3000).concat(Ox.range(1000, 1900)).map(function(i) {
|
||||
return i.toString();
|
||||
}),
|
||||
autocompleteReplace: true,
|
||||
autocompleteReplaceCorrect: true,
|
||||
id: 'year',
|
||||
textAlign: 'right',
|
||||
width: self.options.width.year
|
||||
})
|
||||
.bindEvent('autocomplete', changeMonthOrYear)
|
||||
});
|
||||
|
||||
that = Ox.InputGroup(Ox.extend(self.options, {
|
||||
id: self.options.id,
|
||||
inputs: [].concat(self.options.weekday ? [
|
||||
self.$input.weekday
|
||||
] : [], self.options.format == 'short' ? [
|
||||
self.$input.year, self.$input.month, self.$input.day
|
||||
] : [
|
||||
self.$input.month, self.$input.day, self.$input.year
|
||||
]),
|
||||
join: join,
|
||||
separators: [].concat(self.options.weekday ? [
|
||||
{title: self.options.format == 'short' ? '' : ',', width: 8},
|
||||
] : [], self.options.format == 'short' ? [
|
||||
{title: '-', width: 8}, {title: '-', width: 8}
|
||||
] : [
|
||||
{title: '', width: 8}, {title: ',', width: 8}
|
||||
]),
|
||||
split: split,
|
||||
width: 0
|
||||
}), self);
|
||||
|
||||
function changeDay() {
|
||||
self.options.weekday && self.$input.weekday.value(
|
||||
Ox.formatDate(new Date([
|
||||
self.$input.month.value(),
|
||||
self.$input.day.value(),
|
||||
self.$input.year.value()
|
||||
].join(' ')), self.formats.weekday)
|
||||
);
|
||||
self.options.value = join();
|
||||
}
|
||||
|
||||
function changeMonthOrYear() {
|
||||
var day = self.$input.day.value(),
|
||||
month = self.$input.month.value(),
|
||||
year = self.$input.year.value(),
|
||||
days = Ox.getDaysInMonth(year, self.options.format == 'short' ? parseInt(month, 10) : month);
|
||||
day = day <= days ? day : days;
|
||||
//Ox.Log('Form', year, month, 'day days', day, days)
|
||||
self.options.weekday && self.$input.weekday.value(
|
||||
Ox.formatDate(
|
||||
new Date([month, day, year].join(' ')),
|
||||
self.formats.weekday
|
||||
)
|
||||
);
|
||||
self.$input.day.options({
|
||||
autocomplete: Ox.range(1, days + 1).map(function(i) {
|
||||
return self.options.format == 'short' ? Ox.pad(i, 2) : i.toString();
|
||||
}),
|
||||
value: self.options.format == 'short' ? Ox.pad(parseInt(day, 10), 2) : day.toString()
|
||||
});
|
||||
self.options.value = join();
|
||||
}
|
||||
|
||||
function changeWeekday() {
|
||||
var date = getDateInWeek(
|
||||
self.$input.weekday.value(),
|
||||
self.$input.month.value(),
|
||||
self.$input.day.value(),
|
||||
self.$input.year.value()
|
||||
);
|
||||
self.$input.month.value(date.month);
|
||||
self.$input.day.options({
|
||||
autocomplete: Ox.range(1, Ox.getDaysInMonth(date.year, date.month) + 1).map(function(i) {
|
||||
return self.options.format == 'short' ? Ox.pad(i, 2) : i.toString();
|
||||
}),
|
||||
value: date.day
|
||||
});
|
||||
self.$input.year.value(date.year);
|
||||
self.options.value = join();
|
||||
}
|
||||
|
||||
function getDate(value) {
|
||||
return new Date(self.options.value.replace(/-/g, '/'));
|
||||
}
|
||||
|
||||
function getDateInWeek(weekday, month, day, year) {
|
||||
//Ox.Log('Form', [month, day, year].join(' '))
|
||||
var date = new Date([month, day, year].join(' '));
|
||||
date = Ox.getDateInWeek(date, weekday);
|
||||
return {
|
||||
day: Ox.formatDate(date, self.formats.day),
|
||||
month: Ox.formatDate(date, self.formats.month),
|
||||
year: Ox.formatDate(date, self.formats.year)
|
||||
};
|
||||
}
|
||||
|
||||
function getValues() {
|
||||
var date = getDate();
|
||||
return {
|
||||
day: Ox.formatDate(date, self.formats.day),
|
||||
month: Ox.formatDate(date, self.formats.month),
|
||||
weekday: Ox.formatDate(date, self.formats.weekday),
|
||||
year: Ox.formatDate(date, self.formats.year)
|
||||
};
|
||||
}
|
||||
|
||||
function join() {
|
||||
return Ox.formatDate(new Date(self.options.format == 'short' ? [
|
||||
self.$input.year.value(),
|
||||
self.$input.month.value(),
|
||||
self.$input.day.value()
|
||||
].join('/') : [
|
||||
self.$input.month.value(),
|
||||
self.$input.day.value(),
|
||||
self.$input.year.value()
|
||||
].join(' ')), '%F');
|
||||
}
|
||||
|
||||
function split() {
|
||||
var values = getValues();
|
||||
return [].concat(self.options.weekday ? [
|
||||
values.weekday
|
||||
] : [], self.options.format == 'short' ? [
|
||||
values.year, values.month, values.day
|
||||
] : [
|
||||
values.month, values.day, values.year
|
||||
]);
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
70
source/UI/js/Form/DateTimeInput.js
Normal file
70
source/UI/js/Form/DateTimeInput.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.DateTimeInput <f> DateTimeInput Element
|
||||
options <o> Options object
|
||||
ampm <b|false> false is 24h true is am/pm
|
||||
format <s|short> options are short, medium, long
|
||||
seconds <b|false> show seconds
|
||||
value <d> defautls to now
|
||||
weekday <b|false> weekday
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.InputGroup> DateTimeInput Element
|
||||
change <!> triggered on change of value
|
||||
@*/
|
||||
|
||||
Ox.DateTimeInput = function(options, self) {
|
||||
|
||||
var that;
|
||||
self = Ox.extend(self || {}, {
|
||||
options: Ox.extend({
|
||||
ampm: false,
|
||||
format: 'short',
|
||||
milliseconds: false,
|
||||
seconds: false,
|
||||
value: (function() {
|
||||
var date = new Date();
|
||||
return Ox.formatDate(
|
||||
date,
|
||||
'%F ' + (options && (options.seconds || options.milliseconds) ? '%T' : '%H:%M')
|
||||
) + (options && options.milliseconds ? '.' + Ox.pad(date % 1000, 3) : '');
|
||||
}()),
|
||||
weekday: false
|
||||
}, options || {})
|
||||
});
|
||||
|
||||
self.options.seconds = self.options.seconds || self.options.milliseconds;
|
||||
|
||||
that = Ox.InputGroup(Ox.extend(self.options, {
|
||||
inputs: [
|
||||
Ox.DateInput({
|
||||
format: self.options.format,
|
||||
id: 'date',
|
||||
weekday: self.options.weekday
|
||||
}),
|
||||
Ox.TimeInput({
|
||||
ampm: self.options.ampm,
|
||||
id: 'time',
|
||||
seconds: self.options.seconds
|
||||
})
|
||||
],
|
||||
join: join,
|
||||
separators: [
|
||||
{title: '', width: 8}
|
||||
],
|
||||
split: split
|
||||
}), self);
|
||||
|
||||
function join() {
|
||||
return that.options('inputs').map(function($input) {
|
||||
return $input.value();
|
||||
}).join(' ');
|
||||
}
|
||||
|
||||
function split() {
|
||||
return self.options.value.split(' ');
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
301
source/UI/js/Form/Editable.js
Normal file
301
source/UI/js/Form/Editable.js
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Editable <f> Editable element
|
||||
options <o> Options object
|
||||
editing <b|false> If true, loads in editing state
|
||||
format <f|null> Format function
|
||||
(value) -> <s> Formatted value
|
||||
value <s> Input value
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Input Element
|
||||
blur <!> blur
|
||||
cancel <!> cancel
|
||||
edit <!> edit
|
||||
open <!> open
|
||||
submit <!> submit
|
||||
@*/
|
||||
Ox.Editable = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({
|
||||
element: options.type == 'textarea' ? '<div>' : '<span>',
|
||||
tooltip: options.tooltip
|
||||
}, self)
|
||||
.defaults({
|
||||
blurred: false,
|
||||
clickLink: null,
|
||||
editable: true,
|
||||
editing: false,
|
||||
format: null,
|
||||
globalAttributes: [],
|
||||
height: 0,
|
||||
highlight: null,
|
||||
maxHeight: void 0,
|
||||
placeholder: '',
|
||||
submitOnBlur: true,
|
||||
tags: null,
|
||||
tooltip: '',
|
||||
type: 'input',
|
||||
value: '',
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
editing: function() {
|
||||
if (self.options.editing) {
|
||||
// edit will toggle self.options.editing
|
||||
self.options.editing = false;
|
||||
edit();
|
||||
} else {
|
||||
submit();
|
||||
}
|
||||
},
|
||||
height: function() {
|
||||
setCSS({height: self.options.height + 'px'});
|
||||
},
|
||||
width: function() {
|
||||
setCSS({width: self.options.width + 'px'});
|
||||
},
|
||||
highlight: function() {
|
||||
self.$value.html(formatValue());
|
||||
},
|
||||
placeholder: function() {
|
||||
self.$value.html(formatValue());
|
||||
},
|
||||
value: function() {
|
||||
self.$value.html(formatValue());
|
||||
}
|
||||
})
|
||||
.addClass(
|
||||
'OxEditableElement OxKeyboardFocus'
|
||||
+ (self.options.editable ? ' OxEditable' : '')
|
||||
)
|
||||
.on({
|
||||
click: function(e) {
|
||||
var $target = $(e.target);
|
||||
if (!e.shiftKey && ($target.is('a') || ($target = $target.parents('a')).length)) {
|
||||
e.preventDefault();
|
||||
if (self.options.clickLink) {
|
||||
e.target = $target[0];
|
||||
self.options.clickLink(e);
|
||||
} else {
|
||||
document.location.href = $target.attr('href');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.bindEvent({
|
||||
doubleclick: edit,
|
||||
singleclick: function(e) {
|
||||
}
|
||||
});
|
||||
|
||||
self.options.value = self.options.value.toString();
|
||||
|
||||
self.css = {};
|
||||
self.$value = Ox.Element(self.options.type == 'input' ? '<span>' : '<div>')
|
||||
.addClass('OxValue')
|
||||
.html(formatValue())
|
||||
.appendTo(that);
|
||||
|
||||
if (self.options.editing) {
|
||||
// need timeout so that when determining height
|
||||
// the element is actually in the DOM
|
||||
setTimeout(function() {
|
||||
// edit will toggle self.options.editing
|
||||
self.options.editing = false;
|
||||
edit();
|
||||
});
|
||||
}
|
||||
|
||||
function blur(data) {
|
||||
self.options.value = parseValue();
|
||||
if (self.options.value !== self.originalValue) {
|
||||
self.originalValue = self.options.value;
|
||||
that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
that.triggerEvent('blur', data);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
self.options.editing = false;
|
||||
that.removeClass('OxEditing');
|
||||
self.options.value = self.originalValue;
|
||||
self.$input.value(formatInputValue()).hide();
|
||||
self.$test.html(formatTestValue());
|
||||
self.$value.html(formatValue()).show();
|
||||
that.triggerEvent('cancel', {value: self.options.value});
|
||||
}
|
||||
|
||||
function change(data) {
|
||||
self.options.value = parseValue(data.value);
|
||||
self.$value.html(formatValue());
|
||||
self.$test.html(formatTestValue());
|
||||
setSizes();
|
||||
}
|
||||
|
||||
function edit() {
|
||||
var height, width;
|
||||
if (self.options.editable && !self.options.editing) {
|
||||
self.options.editing = true;
|
||||
that.addClass('OxEditing');
|
||||
self.originalValue = self.options.value;
|
||||
if (!self.$input) {
|
||||
self.$input = Ox.Input({
|
||||
changeOnKeypress: true,
|
||||
element: self.options.type == 'input' ? '<span>' : '<div>',
|
||||
style: 'square',
|
||||
type: self.options.type,
|
||||
value: formatInputValue()
|
||||
})
|
||||
.css(self.css)
|
||||
.bindEvent({
|
||||
blur: self.options.submitOnBlur ? submit : blur,
|
||||
cancel: cancel,
|
||||
change: change,
|
||||
insert: function(data) {
|
||||
that.triggerEvent('insert', data);
|
||||
},
|
||||
submit: submit
|
||||
})
|
||||
.appendTo(that);
|
||||
self.$input.find('input').css(self.css);
|
||||
self.$test = self.$value.clone()
|
||||
.css(Ox.extend({display: 'inline-block'}, self.css))
|
||||
.html(formatTestValue())
|
||||
.css({background: 'rgb(192, 192, 192)'})
|
||||
.appendTo(that);
|
||||
}
|
||||
self.minWidth = 8;
|
||||
self.maxWidth = that.parent().width();
|
||||
self.minHeight = 13;
|
||||
self.maxHeight = self.options.type == 'input'
|
||||
? self.minHeight
|
||||
: self.options.maxHeight || that.parent().height();
|
||||
setSizes();
|
||||
self.$value.hide();
|
||||
self.$input.show();
|
||||
if (!self.options.blurred) {
|
||||
setTimeout(function() {
|
||||
self.$input.focusInput(self.options.type == 'input');
|
||||
}, 0);
|
||||
that.$tooltip && that.$tooltip.options({title: ''});
|
||||
that.triggerEvent('edit');
|
||||
}
|
||||
} else if (!self.options.editable) {
|
||||
that.triggerEvent('open');
|
||||
}
|
||||
self.options.blurred = false;
|
||||
}
|
||||
|
||||
function formatInputValue() {
|
||||
return self.options.type == 'input'
|
||||
? self.options.value
|
||||
: self.options.value.replace(/<br\/?><br\/?>/g, '\n\n');
|
||||
}
|
||||
|
||||
function formatTestValue() {
|
||||
var value = Ox.encodeHTMLEntities(self.$input.options('value'));
|
||||
return !value ? ' '
|
||||
: self.options.type == 'input'
|
||||
? value.replace(/ /g, ' ')
|
||||
: value.replace(/\n$/, '\n ')
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/(^ | $)/, ' ')
|
||||
.replace(/\n/g, '<br/>')
|
||||
}
|
||||
|
||||
function formatValue() {
|
||||
var value = self.options.value;
|
||||
that.removeClass('OxPlaceholder');
|
||||
if (self.options.value === '' && self.options.placeholder) {
|
||||
value = self.options.placeholder;
|
||||
that.addClass('OxPlaceholder');
|
||||
} else if (self.options.format) {
|
||||
value = self.options.format(self.options.value);
|
||||
}
|
||||
if (self.options.highlight) {
|
||||
value = Ox.highlight(
|
||||
value, self.options.highlight, 'OxHighlight', true
|
||||
);
|
||||
}
|
||||
// timeout needed since formatValue is used when assinging self.$value
|
||||
setTimeout(function() {
|
||||
self.$value[
|
||||
self.options.value === '' ? 'removeClass' : 'addClass'
|
||||
]('OxSelectable');
|
||||
})
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseValue() {
|
||||
var value = Ox.clean(
|
||||
self.$input.value().replace(/\n\n+/g, '\0')
|
||||
).replace(/\0/g, '\n\n').trim();
|
||||
return (
|
||||
self.options.type == 'input'
|
||||
? Ox.encodeHTMLEntities(value)
|
||||
: Ox.sanitizeHTML(value, self.options.tags, self.options.globalAttributes)
|
||||
);
|
||||
}
|
||||
|
||||
function setCSS(css) {
|
||||
self.$test && self.$test.css(css);
|
||||
self.$input && self.$input.css(css);
|
||||
self.$input && self.$input.find(self.options.type).css(css);
|
||||
}
|
||||
|
||||
function setSizes() {
|
||||
var height, width;
|
||||
self.$test.css({display: 'inline-block'});
|
||||
height = self.options.height || Ox.limit(self.$test.height(), self.minHeight, self.maxHeight);
|
||||
width = self.$test.width();
|
||||
// +Ox.UI.SCROLLBAR_SIZE to prevent scrollbar from showing up
|
||||
if (self.options.type == 'textarea') {
|
||||
width += Ox.UI.SCROLLBAR_SIZE;
|
||||
}
|
||||
width = self.options.width || Ox.limit(width, self.minWidth, self.maxWidth);
|
||||
self.$test.css({display: 'none'});
|
||||
/*
|
||||
that.css({
|
||||
width: width + 'px',
|
||||
height: height + 'px'
|
||||
});
|
||||
*/
|
||||
self.$input.options({
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
self.$input.find(self.options.type).css({
|
||||
width: width + 'px',
|
||||
height: height + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
function submit() {
|
||||
self.options.editing = false;
|
||||
that.removeClass('OxEditing');
|
||||
self.$input.value(formatInputValue()).hide();
|
||||
self.$test.html(formatTestValue());
|
||||
self.$value.html(formatValue()).show();
|
||||
that.$tooltip && that.$tooltip.options({title: self.options.tooltip});
|
||||
that.triggerEvent('submit', {value: self.options.value});
|
||||
}
|
||||
|
||||
/*@
|
||||
css <f> css
|
||||
@*/
|
||||
that.css = function(css) {
|
||||
self.css = css;
|
||||
that.$element.css(css);
|
||||
self.$value.css(css);
|
||||
self.$test && self.$test.css(css);
|
||||
self.$input && self.$input.css(css);
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
229
source/UI/js/Form/EditableContent.js
Normal file
229
source/UI/js/Form/EditableContent.js
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
Ox.EditableContent = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
if (options.tooltip) {
|
||||
self.tooltip = options.tooltip;
|
||||
options.tooltip = function(e) {
|
||||
return that.hasClass('OxEditing') ? ''
|
||||
: Ox.isString(self.tooltip) ? self.tooltip
|
||||
: self.tooltip(e);
|
||||
}
|
||||
}
|
||||
var that = Ox.Element(options.type == 'textarea' ? '<div>' : '<span>', self)
|
||||
.defaults({
|
||||
clickLink: null,
|
||||
collapseToEnd: true,
|
||||
editable: true,
|
||||
editing: false,
|
||||
format: null,
|
||||
globalAttributes: [],
|
||||
highlight: null,
|
||||
placeholder: '',
|
||||
submitOnBlur: true,
|
||||
tags: null,
|
||||
tooltip: '',
|
||||
type: 'input',
|
||||
value: ''
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
editing: function() {
|
||||
if (self.options.editing) {
|
||||
// edit will toggle self.options.editing
|
||||
self.options.editing = false;
|
||||
edit();
|
||||
} else {
|
||||
submit();
|
||||
}
|
||||
},
|
||||
highlight: function() {
|
||||
!self.options.editing && that.html(formatValue());
|
||||
},
|
||||
value: function() {
|
||||
!self.options.editing && that.html(formatValue());
|
||||
}
|
||||
})
|
||||
.addClass('OxEditableContent OxKeyboardFocus')
|
||||
.on({
|
||||
blur: self.options.submitOnBlur ? submit : blur,
|
||||
click: function(e) {
|
||||
var $target = $(e.target);
|
||||
if (!e.shiftKey && ($target.is('a') || ($target = $target.parents('a')).length)) {
|
||||
e.preventDefault();
|
||||
if (self.options.clickLink) {
|
||||
e.target = $target[0];
|
||||
self.options.clickLink(e);
|
||||
} else {
|
||||
document.location.href = $target.attr('href');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
keydown: function(e) {
|
||||
if (e.keyCode == 13) {
|
||||
if (e.shiftKey || self.options.type == 'input') {
|
||||
submit();
|
||||
} else {
|
||||
var selection = window.getSelection(),
|
||||
node = selection.anchorNode,
|
||||
offset = selection.anchorOffset,
|
||||
range = document.createRange(),
|
||||
text = node.textContent;
|
||||
e.preventDefault();
|
||||
node.textContent = text.substr(0, offset)
|
||||
+ '\n' + (text.substr(offset) || ' ');
|
||||
range.setStart(node, offset + 1);
|
||||
range.setEnd(node, offset + 1);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
return false;
|
||||
} else if (e.keyCode == 27) {
|
||||
cancel();
|
||||
return false;
|
||||
}
|
||||
setTimeout(function() {
|
||||
that.css({padding: that.text() ? 0 : '0 2px'});
|
||||
});
|
||||
},
|
||||
paste: function(e) {
|
||||
//Ox.print('PASTE', e);
|
||||
if (e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData) {
|
||||
//Ox.print('TYPES', e.originalEvent.clipboardData.types);
|
||||
var value = e.originalEvent.clipboardData.getData('text/plain');
|
||||
value = Ox.encodeHTMLEntities(value).replace(/\n\n\n/g, '<br/><br/>\n');
|
||||
document.execCommand('insertHTML', false, value);
|
||||
e.originalEvent.stopPropagation();
|
||||
e.originalEvent.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
})
|
||||
.bindEvent({
|
||||
doubleclick: edit
|
||||
});
|
||||
|
||||
self.options.value = self.options.value.toString();
|
||||
|
||||
that.html(formatValue());
|
||||
|
||||
if (self.options.editing) {
|
||||
// wait for the element to be in the DOM
|
||||
setTimeout(function() {
|
||||
// edit will toggle self.options.editing
|
||||
self.options.editing = false;
|
||||
edit();
|
||||
});
|
||||
}
|
||||
|
||||
function blur() {
|
||||
// ...
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
if (self.options.editing) {
|
||||
that.loseFocus();
|
||||
self.options.editing = false;
|
||||
that.removeClass('OxEditing')
|
||||
.attr({contenteditable: false})
|
||||
.html(formatValue());
|
||||
if (self.options.type == 'input') {
|
||||
that.css({padding: 0});
|
||||
}
|
||||
that.triggerEvent('cancel', {value: self.options.value});
|
||||
}
|
||||
}
|
||||
|
||||
function edit() {
|
||||
if (self.options.editable && !self.options.editing) {
|
||||
var value = formatInputValue();
|
||||
that.$tooltip && that.$tooltip.remove();
|
||||
that.addClass('OxEditing')
|
||||
.removeClass('OxPlaceholder')
|
||||
.attr({contenteditable: true});
|
||||
if (value) {
|
||||
that.text(value);
|
||||
} else {
|
||||
that.text('');
|
||||
if (self.options.type == 'input') {
|
||||
that.css({padding: '0 2px'});
|
||||
}
|
||||
}
|
||||
self.options.editing = true;
|
||||
that.gainFocus();
|
||||
setTimeout(updateSelection);
|
||||
that.triggerEvent('edit');
|
||||
} else if (!self.options.editable) {
|
||||
that.triggerEvent('open');
|
||||
}
|
||||
}
|
||||
|
||||
function formatInputValue() {
|
||||
return self.options.type == 'input'
|
||||
? Ox.decodeHTMLEntities(self.options.value)
|
||||
: self.options.value.replace(/<br\/?><br\/?>/g, '\n\n');
|
||||
}
|
||||
|
||||
function formatValue() {
|
||||
var value = self.options.value;
|
||||
that.removeClass('OxPlaceholder');
|
||||
if (self.options.value === '' && self.options.placeholder) {
|
||||
value = self.options.placeholder;
|
||||
that.addClass('OxPlaceholder');
|
||||
} else if (self.options.format) {
|
||||
value = self.options.format(self.options.value);
|
||||
}
|
||||
if (self.options.highlight) {
|
||||
value = Ox.highlight(
|
||||
value, self.options.highlight, 'OxHighlight', true
|
||||
);
|
||||
}
|
||||
that[
|
||||
self.options.value === '' ? 'removeClass' : 'addClass'
|
||||
]('OxSelectable');
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseValue() {
|
||||
var value = Ox.clean(
|
||||
that.text().replace(/\n\n+/g, '\0')
|
||||
).replace(/\0/g, '\n\n').trim();
|
||||
return (
|
||||
self.options.type == 'input'
|
||||
? Ox.encodeHTMLEntities(value)
|
||||
: Ox.sanitizeHTML(value, self.options.tags, self.options.globalAttributes)
|
||||
);
|
||||
}
|
||||
|
||||
function submit() {
|
||||
if (self.options.editing) {
|
||||
that.loseFocus();
|
||||
self.options.editing = false;
|
||||
self.options.value = parseValue();
|
||||
that.removeClass('OxEditing')
|
||||
.attr({contenteditable: false})
|
||||
.html(formatValue());
|
||||
if (self.options.type == 'input') {
|
||||
that.css({padding: 0});
|
||||
}
|
||||
that.triggerEvent('submit', {value: self.options.value});
|
||||
}
|
||||
}
|
||||
|
||||
function updateSelection() {
|
||||
var range = document.createRange(),
|
||||
selection = window.getSelection();
|
||||
that[0].focus();
|
||||
if (self.options.collapseToEnd) {
|
||||
selection.removeAllRanges();
|
||||
range.selectNodeContents(that[0]);
|
||||
selection.addRange(range);
|
||||
}
|
||||
setTimeout(function() {
|
||||
selection.collapseToEnd();
|
||||
});
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
140
source/UI/js/Form/FileButton.js
Normal file
140
source/UI/js/Form/FileButton.js
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.FileButton <f> File Button
|
||||
options <o> Options
|
||||
disabled <b|false> If true, the button is disabled
|
||||
image <s|'file'> Symbol name (if type is 'image')
|
||||
The default value will be 'files' if maxFiles is not 1
|
||||
maxFiles <n|-1> Maximum number of files (or -1 for unlimited)
|
||||
maxSize <n|-1> Maximum total file size in bytes (or -1 for unlimited)
|
||||
title <s|''> Title of the button (and its tooltip)
|
||||
type <s|'text'> Type of the button ('text' or 'image')
|
||||
width <n|256> Width of the button in px
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> File Button
|
||||
click <!> click
|
||||
@*/
|
||||
Ox.FileButton = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
disabled: false,
|
||||
image: options && options.maxFiles == 1 ? 'file' : 'files',
|
||||
maxFiles: -1,
|
||||
maxSize: -1,
|
||||
style: 'default',
|
||||
title: '',
|
||||
type: 'text',
|
||||
width: options.type == 'image' ? 16 : 256
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
disabled: function() {
|
||||
self.$button.options({disabled: self.options.disabled});
|
||||
self.$input[self.options.disabled ? 'hide' : 'show']();
|
||||
},
|
||||
title: function() {
|
||||
self.$button.options({title: self.options.title});
|
||||
}
|
||||
})
|
||||
.addClass('OxFileButton')
|
||||
.css({overflow: 'hidden'});
|
||||
|
||||
self.files = [];
|
||||
self.multiple = self.options.maxFiles != 1;
|
||||
|
||||
self.$button = Ox.Button({
|
||||
disabled: self.options.disabled,
|
||||
style: self.options.style,
|
||||
title: self.options.type == 'image'
|
||||
? self.options.image
|
||||
: self.options.title,
|
||||
type: self.options.type,
|
||||
width: self.options.type == 'image'
|
||||
? 'auto'
|
||||
: self.options.width
|
||||
})
|
||||
.css({
|
||||
float: 'left'
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
self.$input = renderInput();
|
||||
self.options.disabled && self.$input.hide();
|
||||
|
||||
function selectFiles(e) {
|
||||
var filelist = e.target.files,
|
||||
files = [];
|
||||
self.files = [];
|
||||
Ox.loop(filelist.length, function(i) {
|
||||
files.push(filelist.item(i));
|
||||
});
|
||||
files.sort(self.options.maxSize == -1 ? function(a, b) {
|
||||
a = a.name.toLowerCase();
|
||||
b = b.name.toLowerCase();
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
} : function(a, b) {
|
||||
// if there's a max size,
|
||||
// try to add small files first
|
||||
return a.size - b.size;
|
||||
}).forEach(function(file) {
|
||||
if ((
|
||||
self.options.maxFiles == -1
|
||||
|| self.files.length < self.options.maxFiles
|
||||
) && (
|
||||
self.options.maxSize == -1
|
||||
|| self.size + file.size < self.options.maxSize
|
||||
)) {
|
||||
self.files.push(file);
|
||||
self.size += file.size;
|
||||
}
|
||||
});
|
||||
self.$input = renderInput();
|
||||
if (self.files.length) {
|
||||
that.triggerEvent('click', {files: self.files});
|
||||
}
|
||||
}
|
||||
|
||||
function renderInput() {
|
||||
self.$input && self.$input.remove();
|
||||
return $('<input>')
|
||||
.attr(
|
||||
Ox.extend({
|
||||
title: self.options.title,
|
||||
type: 'file'
|
||||
}, self.multiple ? {
|
||||
multiple: true
|
||||
} : {})
|
||||
)
|
||||
.css({
|
||||
float: 'left',
|
||||
width: self.options.width + 'px',
|
||||
height: '16px',
|
||||
marginLeft: -self.options.width + 'px',
|
||||
opacity: 0
|
||||
})
|
||||
.on({
|
||||
change: selectFiles
|
||||
})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
/*@
|
||||
blurButton <f> blurButton
|
||||
@*/
|
||||
that.blurButton = function() {
|
||||
self.$input.blur();
|
||||
};
|
||||
|
||||
/*@
|
||||
focusButton <f> focusButton
|
||||
@*/
|
||||
that.focusButton = function() {
|
||||
self.$input.focus();
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
}
|
||||
337
source/UI/js/Form/FileInput.js
Normal file
337
source/UI/js/Form/FileInput.js
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.FileInput <f> File Input
|
||||
options <o> Options
|
||||
disabled <b|false> If true, the element is disabled
|
||||
maxFiles <n|-1> Maximum number of files (or -1 for unlimited)
|
||||
maxLines <n|-1> Maximum number of lines to display (or -1 for unlimited)
|
||||
maxSize <n|-1> Maximum total file size in bytes (or -1 for unlimited)
|
||||
value <a|[]> Value (array of file objects)
|
||||
width <w|256> Width in px
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> File Input
|
||||
change <!> change
|
||||
@*/
|
||||
|
||||
Ox.FileInput = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
disabled: false,
|
||||
label: '',
|
||||
labelWidth: 128,
|
||||
maxFiles: -1,
|
||||
maxLines: -1,
|
||||
maxSize: -1,
|
||||
value: [],
|
||||
width: 256
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
disabled: function() {
|
||||
that[self.options.disabled ? 'addClass' : 'removeClass']('OxDisabled');
|
||||
self.$button.options({disabled: self.options.disabled});
|
||||
self.$input && self.$input[self.options.disabled ? 'hide' : 'show']();
|
||||
},
|
||||
label: function() {
|
||||
self.$label && self.$label.options({title: self.options.label});
|
||||
}
|
||||
})
|
||||
.addClass('OxFileInput' + (self.options.disabled ? ' OxDisabled' : ''))
|
||||
.css({width: self.options.width + 'px'});
|
||||
|
||||
self.multiple = self.options.maxFiles != 1;
|
||||
self.size = getSize();
|
||||
|
||||
if (self.options.label) {
|
||||
self.$label = Ox.Label({
|
||||
overlap: 'right',
|
||||
textAlign: 'right',
|
||||
title: self.options.label,
|
||||
width: self.options.labelWidth
|
||||
})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
self.$bar = Ox.Bar({size: 14})
|
||||
.css(
|
||||
Ox.extend({
|
||||
width: getWidth() - 2 + 'px'
|
||||
}, self.multiple && self.options.value.length ? {
|
||||
borderBottomLeftRadius: 0,
|
||||
borderBottomRightRadius: 0
|
||||
} : {})
|
||||
)
|
||||
.appendTo(that);
|
||||
|
||||
self.$title = $('<div>')
|
||||
.css({
|
||||
float: 'left',
|
||||
width: getWidth() - 104 + 'px',
|
||||
paddingLeft: '6px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis'
|
||||
})
|
||||
.html(getTitleText())
|
||||
.appendTo(self.$bar);
|
||||
|
||||
self.$size = $('<div>')
|
||||
.css({
|
||||
float: 'left',
|
||||
width: '64px',
|
||||
height: '14px',
|
||||
paddingRight: '16px',
|
||||
textAlign: 'right'
|
||||
})
|
||||
.html(getSizeText())
|
||||
.appendTo(self.$bar);
|
||||
|
||||
self.$button = Ox.Button({
|
||||
disabled: self.options.disabled,
|
||||
style: 'symbol',
|
||||
title: self.multiple || self.options.value.length == 0
|
||||
? 'add' : 'close',
|
||||
type: 'image'
|
||||
})
|
||||
.attr({
|
||||
title: self.multiple || self.options.value.length == 0
|
||||
? '' : 'Clear'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
marginTop: '-1px'
|
||||
})
|
||||
.bindEvent({
|
||||
click: clearFile
|
||||
})
|
||||
.appendTo(self.$bar);
|
||||
|
||||
if (self.multiple || self.options.value.length == 0) {
|
||||
self.$input = renderInput();
|
||||
self.options.disabled && self.$input.hide();
|
||||
}
|
||||
|
||||
if (self.multiple) {
|
||||
self.$files = $('<div>')
|
||||
.addClass('OxFiles')
|
||||
.css({
|
||||
width: getWidth() - 2 + 'px',
|
||||
height: getHeight()
|
||||
})
|
||||
.appendTo(that);
|
||||
self.options.value.length == 0 && self.$files.hide();
|
||||
self.$list = Ox.TableList({
|
||||
columns: [
|
||||
{
|
||||
id: 'name',
|
||||
visible: true,
|
||||
width: getWidth() - 94
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: function(value) {
|
||||
return Ox.formatValue(value, 'B');
|
||||
},
|
||||
id: 'size',
|
||||
visible: true,
|
||||
width: 64
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: function(value, data) {
|
||||
return Ox.Button({
|
||||
style: 'symbol',
|
||||
title: 'close',
|
||||
type: 'image'
|
||||
})
|
||||
.attr({title: Ox._('Remove File')})
|
||||
.css({margin: '-1px -4px 0 0'})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
self.$list.options({selected: [value]});
|
||||
removeFiles([value]);
|
||||
}
|
||||
});
|
||||
},
|
||||
id: 'id',
|
||||
visible: true,
|
||||
width: 28
|
||||
}
|
||||
],
|
||||
items: getItems(),
|
||||
sort: [{key: 'name', operator: '+'}],
|
||||
unique: 'id'
|
||||
})
|
||||
.css({
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: getWidth() - 2 + 'px',
|
||||
height: '64px'
|
||||
})
|
||||
.bindEvent({
|
||||
'delete': function(data) {
|
||||
removeFiles(data.ids);
|
||||
}
|
||||
})
|
||||
.appendTo(self.$files);
|
||||
}
|
||||
|
||||
function addFiles(e) {
|
||||
var filelist = e.target.files,
|
||||
files = [];
|
||||
Ox.loop(filelist.length, function(i) {
|
||||
files.push(filelist.item(i));
|
||||
});
|
||||
files.sort(self.options.maxSize == -1 ? function(a, b) {
|
||||
a = a.name.toLowerCase();
|
||||
b = b.name.toLowerCase();
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
} : function(a, b) {
|
||||
// if there is a max size,
|
||||
// try to add small files first
|
||||
return a.size - b.size;
|
||||
}).forEach(function(file) {
|
||||
if (!exists(file) && (
|
||||
self.options.maxFiles == -1
|
||||
|| self.options.value.length < self.options.maxFiles
|
||||
) && (
|
||||
self.options.maxSize == -1
|
||||
|| self.size + file.size < self.options.maxSize
|
||||
)) {
|
||||
self.options.value.push(file);
|
||||
self.size += file.size;
|
||||
}
|
||||
});
|
||||
self.$title.html(getTitleText());
|
||||
self.$size.html(getSizeText());
|
||||
if (self.multiple) {
|
||||
self.$bar.css({
|
||||
borderBottomLeftRadius: 0,
|
||||
borderBottomRightRadius: 1
|
||||
});
|
||||
self.$files.css({height: getHeight()}).show();
|
||||
self.$list.options({items: getItems()});
|
||||
if (
|
||||
self.options.value.length == self.options.maxFiles
|
||||
|| self.size == self.options.maxSize
|
||||
) {
|
||||
self.$button.options({disabled: true});
|
||||
}
|
||||
self.$input = renderInput();
|
||||
} else {
|
||||
self.$button.options({title: 'close'}).attr({title: Ox._('Clear')});
|
||||
self.$input.remove();
|
||||
}
|
||||
that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
|
||||
function clearFile() {
|
||||
self.options.value = [];
|
||||
self.size = 0;
|
||||
self.$title.html(getTitleText());
|
||||
self.$size.html(getSizeText());
|
||||
self.$button.options({title: 'add'}).attr({title: ''});
|
||||
self.$input = renderInput();
|
||||
that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
|
||||
function exists(file) {
|
||||
return self.options.value.some(function(f) {
|
||||
return f.name == file.name
|
||||
&& f.size == file.size
|
||||
&& Ox.isEqual(f.lastModifiedDate, file.lastModifiedDate);
|
||||
});
|
||||
}
|
||||
|
||||
function getHeight() {
|
||||
return (
|
||||
self.options.maxLines == -1
|
||||
? self.options.value.length
|
||||
: Math.min(self.options.value.length, self.options.maxLines)
|
||||
) * 16 + 'px';
|
||||
}
|
||||
|
||||
function getItems() {
|
||||
return self.options.value.map(function(file, i) {
|
||||
return {name: file.name, size: file.size, id: i};
|
||||
});
|
||||
}
|
||||
|
||||
function getSize() {
|
||||
return self.options.value.reduce(function(prev, curr) {
|
||||
return prev + curr.size;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function getSizeText() {
|
||||
return self.size ? Ox.formatValue(self.size, 'B') : '';
|
||||
}
|
||||
|
||||
function getTitleText() {
|
||||
var length = self.options.value.length
|
||||
return length == 0
|
||||
? Ox._('No file' + (self.multiple ? 's' : '') + ' selected')
|
||||
: self.multiple
|
||||
? Ox.formatCount(length, 'file')
|
||||
: self.options.value[0].name;
|
||||
}
|
||||
|
||||
function getWidth() {
|
||||
return self.options.width - (
|
||||
self.options.label ? self.options.labelWidth : 0
|
||||
);
|
||||
}
|
||||
|
||||
function removeFiles(ids) {
|
||||
self.options.value = self.options.value.filter(function(v, i) {
|
||||
return ids.indexOf(i) == -1;
|
||||
});
|
||||
self.size = getSize();
|
||||
if (self.options.value.length == 0) {
|
||||
self.$bar.css({
|
||||
borderBottomLeftRadius: '8px',
|
||||
borderBottomRightRadius: '8px'
|
||||
});
|
||||
self.$files.hide();
|
||||
}
|
||||
self.$title.html(getTitleText());
|
||||
self.$size.html(getSizeText());
|
||||
self.$list.options({items: getItems(), selected: []});
|
||||
self.$files.css({height: getHeight()});
|
||||
that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
|
||||
function renderInput() {
|
||||
self.$input && self.$input.remove();
|
||||
return $('<input>')
|
||||
.attr(
|
||||
Ox.extend({
|
||||
title: self.multiple ? Ox._('Add Files') : Ox._('Select File'),
|
||||
type: 'file'
|
||||
}, self.multiple ? {
|
||||
multiple: true
|
||||
} : {})
|
||||
)
|
||||
.css({
|
||||
float: 'left',
|
||||
width: '16px',
|
||||
height: '14px',
|
||||
margin: '-1px -7px 0 -16px',
|
||||
opacity: 0
|
||||
})
|
||||
.on({
|
||||
change: addFiles
|
||||
})
|
||||
.appendTo(self.$bar);
|
||||
}
|
||||
|
||||
that.clear = function() {
|
||||
clearFile();
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
907
source/UI/js/Form/Filter.js
Normal file
907
source/UI/js/Form/Filter.js
Normal file
|
|
@ -0,0 +1,907 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Filter <f> Filter Object
|
||||
options <o> Options object
|
||||
findKeys <[]|[]> keys
|
||||
list <o> list object
|
||||
sort <s> List sort
|
||||
view <s> List view
|
||||
sortKeys <a|[]> keys to sort by
|
||||
value <o> query object
|
||||
conditions <[o]> Conditions (array of {key, value, operator})
|
||||
operator <s> Operator ('&' or '|')
|
||||
limit <o> Limit
|
||||
key <s> Limit key
|
||||
sort <s> Limit sort
|
||||
value <n> Limit value
|
||||
viewKeys <a|[]> visible keys
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Filter Object
|
||||
change <!> change
|
||||
@*/
|
||||
|
||||
Ox.Filter = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
findKeys: [],
|
||||
list: null,
|
||||
sortKeys: [],
|
||||
value: {
|
||||
conditions: [],
|
||||
operator: '&'
|
||||
},
|
||||
viewKeys: []
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
value: function() {
|
||||
setValue();
|
||||
renderConditions();
|
||||
}
|
||||
});
|
||||
|
||||
self.conditionOperators = {
|
||||
boolean: [
|
||||
{id: '=', title: Ox._('is')},
|
||||
{id: '!=', title: Ox._('is not')}
|
||||
],
|
||||
date: [
|
||||
{id: '=', title: Ox._('is')},
|
||||
{id: '!=', title: Ox._('is not')},
|
||||
{id: '<', title: Ox._('is before')},
|
||||
{id: '!<', title: Ox._('is not before')},
|
||||
{id: '>', title: Ox._('is after')},
|
||||
{id: '!>', title: Ox._('is not after')},
|
||||
{id: '=,', title: Ox._('is between')},
|
||||
{id: '!=,', title: Ox._('is not between')}
|
||||
],
|
||||
'enum': [
|
||||
{id: '=', title: Ox._('is')},
|
||||
{id: '!=', title: Ox._('is not')},
|
||||
{id: '<', title: Ox._('is less than')},
|
||||
{id: '!<', title: Ox._('is not less than')},
|
||||
{id: '>', title: Ox._('is greater than')},
|
||||
{id: '!>', title: Ox._('is not greater than')},
|
||||
{id: '=,', title: Ox._('is between')},
|
||||
{id: '!=,', title: Ox._('is not between')}
|
||||
],
|
||||
item: [
|
||||
{id: '==', title: Ox._('is')},
|
||||
{id: '!==', title: Ox._('is not')}
|
||||
],
|
||||
list: [
|
||||
{id: '==', title: Ox._('is')},
|
||||
{id: '!==', title: Ox._('is not')}
|
||||
],
|
||||
number: [
|
||||
{id: '=', title: Ox._('is')},
|
||||
{id: '!=', title: Ox._('is not')},
|
||||
{id: '<', title: Ox._('is less than')},
|
||||
{id: '!<', title: Ox._('is not less than')},
|
||||
{id: '>', title: Ox._('is greater than')},
|
||||
{id: '!>', title: Ox._('is not greater than')},
|
||||
{id: '=,', title: Ox._('is between')},
|
||||
{id: '!=,', title: Ox._('is not between')}
|
||||
],
|
||||
string: [
|
||||
{id: '==', title: Ox._('is')},
|
||||
{id: '!==', title: Ox._('is not')},
|
||||
{id: '=', title: Ox._('contains')},
|
||||
{id: '!=', title: Ox._('does not contain')},
|
||||
{id: '^', title: Ox._('starts with')},
|
||||
{id: '!^', title: Ox._('does not start with')},
|
||||
{id: '$', title: Ox._('ends with')},
|
||||
{id: '!$', title: Ox._('does not end with')}
|
||||
],
|
||||
text: [
|
||||
{id: '=', title: Ox._('contains')},
|
||||
{id: '!=', title: Ox._('does not contain')}
|
||||
],
|
||||
year: [
|
||||
{id: '==', title: Ox._('is')},
|
||||
{id: '!==', title: Ox._('is not')},
|
||||
{id: '<', title: Ox._('is before')},
|
||||
{id: '!<', title: Ox._('is not before')},
|
||||
{id: '>', title: Ox._('is after')},
|
||||
{id: '!>', title: Ox._('is not after')},
|
||||
{id: '=,', title: Ox._('is between')},
|
||||
{id: '!=,', title: Ox._('is not between')}
|
||||
]
|
||||
};
|
||||
self.defaultValue = {
|
||||
boolean: 'true',
|
||||
date: Ox.formatDate(new Date(), '%F'),
|
||||
'enum': 0,
|
||||
float: 0,
|
||||
hue: 0,
|
||||
integer: 0,
|
||||
item: void 0,
|
||||
list: '',
|
||||
string: '',
|
||||
text: '',
|
||||
time: '00:00:00',
|
||||
year: new Date().getFullYear().toString()
|
||||
};
|
||||
self.operators = [
|
||||
{id: '&', title: Ox._('all')},
|
||||
{id: '|', title: Ox._('any')}
|
||||
];
|
||||
|
||||
setValue();
|
||||
|
||||
self.$operator = Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Label({
|
||||
title: Ox._('Match'),
|
||||
overlap: 'right',
|
||||
width: 48
|
||||
}),
|
||||
Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Select({
|
||||
items: self.operators,
|
||||
value: self.options.value.operator,
|
||||
width: 48
|
||||
})
|
||||
.bindEvent({
|
||||
change: changeOperator
|
||||
}),
|
||||
Ox.Label({
|
||||
overlap: 'left',
|
||||
title: Ox._('of the following conditions'),
|
||||
width: 160
|
||||
})
|
||||
],
|
||||
float: 'right',
|
||||
width: 208
|
||||
})
|
||||
],
|
||||
float: 'left'
|
||||
});
|
||||
|
||||
self.$save = Ox.InputGroup({
|
||||
inputs: [
|
||||
Ox.Checkbox({
|
||||
width: 16
|
||||
}),
|
||||
Ox.Input({
|
||||
id: 'list',
|
||||
placeholder: Ox._('Untitled'),
|
||||
width: 128
|
||||
})
|
||||
],
|
||||
separators: [
|
||||
{title: Ox._('Save as Smart List'), width: 112}
|
||||
]
|
||||
});
|
||||
|
||||
self.$limit = Ox.InputGroup({
|
||||
inputs: [
|
||||
Ox.Checkbox({
|
||||
width: 16
|
||||
}),
|
||||
Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Input({
|
||||
type: 'int',
|
||||
width: 56
|
||||
}),
|
||||
Ox.Select({
|
||||
items: [
|
||||
{id: 'items', title: Ox._('items')},
|
||||
{},
|
||||
{id: 'hours', title: Ox._('hours')},
|
||||
{id: 'days', title: Ox._('days')},
|
||||
{},
|
||||
{id: 'GB', title: 'GB'}
|
||||
],
|
||||
overlap: 'left',
|
||||
width: 64
|
||||
})
|
||||
],
|
||||
float: 'right',
|
||||
width: 120
|
||||
}),
|
||||
Ox.Select({
|
||||
items: self.options.sortKeys,
|
||||
width: 128
|
||||
}),
|
||||
Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Select({
|
||||
items: [
|
||||
{id: 'ascending', title: Ox._('ascending')},
|
||||
{id: 'descending', title: Ox._('descending')}
|
||||
],
|
||||
width: 128
|
||||
}),
|
||||
Ox.Label({
|
||||
overlap: 'left',
|
||||
title: Ox._('order'),
|
||||
width: 72
|
||||
})
|
||||
],
|
||||
float: 'right',
|
||||
width: 200
|
||||
})
|
||||
],
|
||||
separators: [
|
||||
{title: Ox._('Limit to'), width: 56},
|
||||
{title: Ox._('sorted by'), width: 60}, // fixme: this is odd, should be 64
|
||||
{title: Ox._('in'), width: 32}
|
||||
]
|
||||
});
|
||||
|
||||
self.$view = Ox.InputGroup({
|
||||
inputs: [
|
||||
Ox.Checkbox({
|
||||
width: 16
|
||||
}),
|
||||
Ox.Select({
|
||||
items: self.options.viewKeys,
|
||||
width: 128
|
||||
}),
|
||||
Ox.Select({
|
||||
items: self.options.sortKeys,
|
||||
width: 128
|
||||
}),
|
||||
Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Select({
|
||||
items: [
|
||||
{id: 'ascending', title: Ox._('ascending')},
|
||||
{id: 'descending', title: Ox._('descending')}
|
||||
],
|
||||
width: 128
|
||||
}),
|
||||
Ox.Label({
|
||||
overlap: 'left',
|
||||
title: Ox._('order'),
|
||||
width: 72
|
||||
})
|
||||
],
|
||||
float: 'right',
|
||||
width: 200
|
||||
})
|
||||
],
|
||||
separators: [
|
||||
{title: Ox._('View'), width: 48},
|
||||
{title: Ox._('sorted by'), width: 60},
|
||||
{title: Ox._('in'), width: 32}
|
||||
]
|
||||
});
|
||||
|
||||
// limit and view temporarily disabled
|
||||
self.$items = self.options.list
|
||||
? [self.$operator, self.$save/*, self.$limit, self.$view*/]
|
||||
: [self.$operator/*, self.$limit, self.$view*/];
|
||||
|
||||
self.numberOfAdditionalFormItems = self.$items.length;
|
||||
|
||||
self.$form = Ox.Form({
|
||||
items: self.$items
|
||||
}).appendTo(that);
|
||||
|
||||
renderConditions();
|
||||
|
||||
function addCondition(pos, subpos, isGroup) {
|
||||
subpos = Ox.isUndefined(subpos) ? -1 : subpos;
|
||||
var condition, key;
|
||||
if (subpos == -1) {
|
||||
condition = self.options.value.conditions[pos - 1]
|
||||
} else {
|
||||
condition = self.options.value.conditions[pos].conditions[subpos - 1];
|
||||
}
|
||||
key = Ox.getObjectById(self.options.findKeys, condition.key);
|
||||
condition = {
|
||||
key: key.id,
|
||||
operator: condition.operator,
|
||||
value: ''
|
||||
};
|
||||
if (isGroup) {
|
||||
Ox.Log('Form', 'isGroup', self.options.value.operator)
|
||||
condition = {
|
||||
conditions: [condition],
|
||||
operator: self.options.value.operator == '&' ? '|' : '&'
|
||||
};
|
||||
}
|
||||
if (subpos == -1) {
|
||||
self.options.value.conditions.splice(pos, 0, condition);
|
||||
} else {
|
||||
self.options.value.conditions[pos].conditions.splice(subpos, 0, condition);
|
||||
}
|
||||
renderConditions(pos, subpos);
|
||||
if (!isUselessCondition(pos, subpos)) {
|
||||
triggerChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
function changeConditionKey(pos, subpos, key) {
|
||||
subpos = Ox.isUndefined(subpos) ? -1 : subpos;
|
||||
Ox.Log('Form', 'changeConditionKey', pos, subpos, key);
|
||||
var condition = subpos == -1
|
||||
? self.options.value.conditions[pos]
|
||||
: self.options.value.conditions[pos].conditions[subpos],
|
||||
oldFindKey = Ox.getObjectById(self.options.findKeys, condition.key),
|
||||
newFindKey = Ox.getObjectById(self.options.findKeys, key),
|
||||
oldConditionType = getConditionType(oldFindKey.type),
|
||||
newConditionType = getConditionType(newFindKey.type),
|
||||
changeConditionType = oldConditionType != newConditionType,
|
||||
changeConditionFormat = !Ox.isEqual(oldFindKey.format, newFindKey.format),
|
||||
wasUselessCondition = isUselessCondition(pos, subpos);
|
||||
Ox.Log('Form', 'old new', oldConditionType, newConditionType)
|
||||
condition.key = key;
|
||||
if (changeConditionType || changeConditionFormat) {
|
||||
if (Ox.getIndexById(self.conditionOperators[newConditionType], condition.operator) == -1) {
|
||||
condition.operator = self.conditionOperators[newConditionType][0].id;
|
||||
}
|
||||
if (
|
||||
['string', 'text'].indexOf(oldConditionType) == -1
|
||||
|| ['string', 'text'].indexOf(newConditionType) == -1
|
||||
) {
|
||||
condition.value = newFindKey.type == 'item'
|
||||
? newFindKey.values[0].id
|
||||
: self.defaultValue[newFindKey.type];
|
||||
}
|
||||
renderConditions();
|
||||
}
|
||||
if (!(wasUselessCondition && isUselessCondition(pos, subpos))) {
|
||||
triggerChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
function changeConditionOperator(pos, subpos, operator) {
|
||||
subpos = Ox.isUndefined(subpos) ? -1 : subpos;
|
||||
Ox.Log('FILTER', 'chCoOp', 'query', self.options.value)
|
||||
var condition = subpos == -1
|
||||
? self.options.value.conditions[pos]
|
||||
: self.options.value.conditions[pos].conditions[subpos],
|
||||
isBetween = operator.indexOf(',') > -1,
|
||||
wasBetween = Ox.isArray(condition.value),
|
||||
wasUselessCondition = isUselessCondition(pos, subpos);
|
||||
Ox.Log('FILTER', 'chCoOp', 'iB/wB', isBetween, wasBetween)
|
||||
condition.operator = operator;
|
||||
if (isBetween && !wasBetween) {
|
||||
condition.operator = condition.operator.replace(',', '');
|
||||
condition.value = [condition.value, condition.value]
|
||||
renderConditions();
|
||||
} else if (!isBetween && wasBetween) {
|
||||
condition.value = condition.value[0]
|
||||
renderConditions();
|
||||
}
|
||||
if (!(wasUselessCondition && isUselessCondition(pos, subpos))) {
|
||||
triggerChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
function changeConditionValue(pos, subpos, value) {
|
||||
Ox.Log('FILTER', 'cCV', pos, subpos, value);
|
||||
var condition = subpos == -1
|
||||
? self.options.value.conditions[pos]
|
||||
: self.options.value.conditions[pos].conditions[subpos];
|
||||
condition.value = value;
|
||||
triggerChangeEvent();
|
||||
}
|
||||
|
||||
function changeGroupOperator(pos, value) {
|
||||
self.options.value.conditions[pos].operator = value;
|
||||
triggerChangeEvent();
|
||||
}
|
||||
|
||||
function changeOperator(data) {
|
||||
var hasGroups = false;
|
||||
self.options.value.operator = data.value;
|
||||
Ox.forEach(self.options.value.conditions, function(condition) {
|
||||
if (condition.conditions) {
|
||||
hasGroups = true;
|
||||
return false; // break
|
||||
}
|
||||
});
|
||||
hasGroups && renderConditions();
|
||||
self.options.value.conditions.length > 1 && triggerChangeEvent();
|
||||
}
|
||||
|
||||
function getConditionType(type) {
|
||||
type = Ox.isArray(type) ? type[0] : type;
|
||||
if (['float', 'hue', 'integer', 'time'].indexOf(type) > -1) {
|
||||
type = 'number';
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function isUselessCondition(pos, subpos) {
|
||||
subpos = Ox.isUndefined(subpos) ? -1 : subpos;
|
||||
var conditions = subpos == -1
|
||||
? self.options.value.conditions[pos].conditions
|
||||
|| [self.options.value.conditions[pos]]
|
||||
: [self.options.value.conditions[pos].conditions[subpos]],
|
||||
isUseless = false;
|
||||
Ox.forEach(conditions, function(condition) {
|
||||
isUseless = ['string', 'text'].indexOf(getConditionType(
|
||||
Ox.getObjectById(self.options.findKeys, condition.key).type
|
||||
)) > -1
|
||||
&& (
|
||||
self.options.value.operator == '&' ? ['', '^', '$'] : ['!', '!^', '!$']
|
||||
).indexOf(condition.operator) > -1
|
||||
&& condition.value === '';
|
||||
if (!isUseless) {
|
||||
return false; // break if one of the conditions is not useless
|
||||
}
|
||||
});
|
||||
return isUseless;
|
||||
}
|
||||
|
||||
function removeCondition(pos, subpos) {
|
||||
subpos = Ox.isUndefined(subpos) ? -1 : subpos;
|
||||
var wasUselessCondition = isUselessCondition(pos, subpos);
|
||||
if (subpos == -1 || self.options.value.conditions[pos].conditions.length == 1) {
|
||||
self.options.value.conditions.splice(pos, 1);
|
||||
} else {
|
||||
self.options.value.conditions[pos].conditions.splice(subpos, 1);
|
||||
}
|
||||
renderConditions();
|
||||
if (!wasUselessCondition) {
|
||||
triggerChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
function renderButtons(pos, subpos) {
|
||||
subpos = Ox.isUndefined(subpos) ? -1 : subpos;
|
||||
var isGroup = subpos == -1 && self.options.value.conditions[pos].conditions;
|
||||
return [].concat([
|
||||
Ox.Button({
|
||||
id: 'remove',
|
||||
title: self.options.value.conditions.length == 1 ? 'close' : 'remove',
|
||||
tooltip: self.options.value.conditions.length == 1 ? Ox._('Reset this condition')
|
||||
: isGroup ? Ox._('Remove this group of conditions')
|
||||
: Ox._('Remove this condition'),
|
||||
type: 'image'
|
||||
})
|
||||
.css({margin: '0 4px 0 ' + (isGroup ? '292px' : '8px')}) // fixme: 296 is probably correct, but labels seem to be too wide
|
||||
.bindEvent({
|
||||
click: function(data) {
|
||||
var key, that = this;
|
||||
that.focus(); // make input trigger change
|
||||
setTimeout(function() {
|
||||
Ox.print(self.options.value.conditions.length, that.parent().data('subposition') == -1);
|
||||
if (self.options.value.conditions.length == 1) {
|
||||
key = self.options.findKeys[0];
|
||||
self.options.value.conditions = [{
|
||||
key: key.id,
|
||||
value: '',
|
||||
operator: self.conditionOperators[key.type][0].id
|
||||
}];
|
||||
renderConditions();
|
||||
triggerChangeEvent();
|
||||
} else if (that.parent().data('subposition') == -1) {
|
||||
removeCondition(that.parent().data('position'));
|
||||
} else {
|
||||
removeCondition(
|
||||
that.parent().data('position'),
|
||||
that.parent().data('subposition')
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
Ox.Button({
|
||||
id: 'add',
|
||||
title: 'add',
|
||||
tooltip: Ox._('Add a condition'),
|
||||
type: 'image'
|
||||
})
|
||||
.css({margin: '0 ' + (subpos == -1 ? '4px' : '0') + ' 0 4px'})
|
||||
.bindEvent({
|
||||
click: function(data) {
|
||||
var that = this;
|
||||
that.focus(); // make input trigger change
|
||||
setTimeout(function() {
|
||||
if (that.parent().data('subposition') == -1) {
|
||||
addCondition(that.parent().data('position') + 1);
|
||||
} else {
|
||||
addCondition(
|
||||
that.parent().data('position'),
|
||||
that.parent().data('subposition') + 1
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
], subpos == -1 ? [
|
||||
Ox.Button({
|
||||
id: 'addgroup',
|
||||
title: 'bracket',
|
||||
tooltip: Ox._('Add a group of conditions'),
|
||||
type: 'image'
|
||||
})
|
||||
.css({margin: '0 0 0 4px'})
|
||||
.bindEvent({
|
||||
click: function(data) {
|
||||
var that = this;
|
||||
that.focus(); // make input trigger change
|
||||
setTimeout(function() {
|
||||
addCondition(that.parent().data('position') + 1, -1, true);
|
||||
});
|
||||
}
|
||||
})
|
||||
] : []);
|
||||
}
|
||||
|
||||
function renderCondition(condition, pos, subpos) {
|
||||
subpos = Ox.isUndefined(subpos) ? -1 : subpos;
|
||||
var condition = subpos == -1
|
||||
? self.options.value.conditions[pos]
|
||||
: self.options.value.conditions[pos].conditions[subpos];
|
||||
Ox.Log('Form', 'renderCondition', condition, pos, subpos)
|
||||
return Ox.FormElementGroup({
|
||||
elements: [
|
||||
renderConditionKey(condition),
|
||||
renderConditionOperator(condition),
|
||||
renderConditionValue(condition)
|
||||
].concat(renderButtons(pos, subpos))
|
||||
})
|
||||
.css({marginLeft: subpos == -1 ? 0 : '24px'})
|
||||
.data({position: pos, subposition: subpos});
|
||||
}
|
||||
|
||||
function renderConditionKey(condition) {
|
||||
return Ox.Select({
|
||||
items: self.options.findKeys,
|
||||
//items: Ox.extend({}, self.options.findKeys), // fixme: Ox.Menu messes with keys
|
||||
overlap: 'right',
|
||||
value: condition.key,
|
||||
width: 128
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
var $element = this.parent();
|
||||
changeConditionKey(
|
||||
$element.data('position'),
|
||||
$element.data('subposition'),
|
||||
data.value
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderConditionOperator(condition) {
|
||||
Ox.Log('FILTER', 'rCO', condition, self.conditionOperators[getConditionType(
|
||||
Ox.getObjectById(self.options.findKeys, condition.key).type
|
||||
)])
|
||||
return Ox.Select({
|
||||
items: self.conditionOperators[getConditionType(
|
||||
Ox.getObjectById(self.options.findKeys, condition.key).type
|
||||
)],
|
||||
overlap: 'right',
|
||||
value: condition.operator + (Ox.isArray(condition.value) ? ',' : ''),
|
||||
width: 128
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
var $element = this.parent();
|
||||
changeConditionOperator(
|
||||
$element.data('position'),
|
||||
$element.data('subposition'),
|
||||
data.value
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderConditionValue(condition) {
|
||||
return (!Ox.isArray(condition.value)
|
||||
? renderInput(condition)
|
||||
: Ox.InputGroup({
|
||||
inputs: [
|
||||
renderInput(condition, 0).options({id: 'start'}),
|
||||
renderInput(condition, 1).options({id: 'end'})
|
||||
],
|
||||
separators: [
|
||||
{title: Ox._('and'), width: 32}
|
||||
]
|
||||
})
|
||||
).bindEvent({
|
||||
change: change,
|
||||
submit: change
|
||||
});
|
||||
function change(data) {
|
||||
var $element = this.parent();
|
||||
changeConditionValue(
|
||||
$element.data('position'),
|
||||
$element.data('subposition'),
|
||||
data.value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function renderConditions(focusPos, focusSubpos) {
|
||||
Ox.Log('Form', 'renderConditions', self.options.value)
|
||||
var $conditions = [], focusIndex;
|
||||
while (self.$form.options('items').length > self.numberOfAdditionalFormItems) {
|
||||
self.$form.removeItem(1);
|
||||
}
|
||||
self.options.value.conditions.forEach(function(condition, pos) {
|
||||
if (!condition.conditions) {
|
||||
$conditions.push(renderCondition(condition, pos));
|
||||
if (pos == focusPos && focusSubpos == -1) {
|
||||
focusIndex = $conditions.length - 1;
|
||||
}
|
||||
} else {
|
||||
$conditions.push(renderGroup(condition, pos));
|
||||
condition.conditions.forEach(function(subcondition, subpos) {
|
||||
$conditions.push(renderCondition(subcondition, pos, subpos));
|
||||
if (pos == focusPos && subpos == focusSubpos) {
|
||||
focusIndex = $conditions.length - 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
$conditions.forEach(function($condition, pos) {
|
||||
var $input;
|
||||
self.$form.addItem(1 + pos, $condition);
|
||||
if (focusIndex == pos) {
|
||||
$input = $condition.options('elements')[2];
|
||||
if ($input.focusInput) {
|
||||
$input.focusInput();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderGroup(condition, pos) {
|
||||
var subpos = -1;
|
||||
var $condition = Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Label({
|
||||
title: self.options.value.operator == '&'
|
||||
? (pos == 0 ? 'Both' : 'and')
|
||||
: (pos == 0 ? 'Either': 'or'),
|
||||
overlap: 'right',
|
||||
width: 48
|
||||
}).addClass('OxGroupLabel'),
|
||||
Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Select({
|
||||
items: self.operators,
|
||||
value: self.options.value.operator == '&' ? '|' : '&',
|
||||
width: 48
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
var $element = this.parent().parent();
|
||||
changeGroupOperator(
|
||||
$element.data('position'),
|
||||
data.value
|
||||
);
|
||||
}
|
||||
}),
|
||||
Ox.Label({
|
||||
overlap: 'left',
|
||||
title: Ox._('of the following conditions'),
|
||||
width: 160
|
||||
})
|
||||
],
|
||||
float: 'right',
|
||||
width: 208
|
||||
}),
|
||||
].concat(renderButtons(pos, subpos, true)),
|
||||
float: 'left'
|
||||
})
|
||||
.data({position: pos});
|
||||
return $condition;
|
||||
}
|
||||
|
||||
function renderInput(condition, index) {
|
||||
Ox.Log('Form', 'renderInput', condition)
|
||||
var $input,
|
||||
findKey = Ox.getObjectById(self.options.findKeys, condition.key),
|
||||
isArray = Ox.isArray(condition.value),
|
||||
isHue,
|
||||
// FIXME: always use 'int'
|
||||
type = findKey.type == 'integer' ? 'int' : findKey.type,
|
||||
value = !isArray ? condition.value : condition.value[index],
|
||||
formatArgs, formatType, title;
|
||||
if (type == 'boolean') {
|
||||
$input = Ox.Select({
|
||||
items: ['true', 'false'],
|
||||
max: 1,
|
||||
min: 1,
|
||||
value: value ? 'true' : 'false',
|
||||
width: 288
|
||||
});
|
||||
} else if (type == 'enum') {
|
||||
Ox.Log('FILTER', findKey, condition)
|
||||
$input = Ox.Select({
|
||||
items: findKey.values.map(function(v, i) {
|
||||
return {id: i, title: v}
|
||||
}),
|
||||
value: value,
|
||||
width: !isArray ? 288 : 128
|
||||
});
|
||||
} else if (type == 'item') {
|
||||
$input = Ox.Select({
|
||||
items: findKey.values,
|
||||
max: 1,
|
||||
min: 1,
|
||||
value: value,
|
||||
width: 288
|
||||
});
|
||||
} else if (type == 'list') {
|
||||
Ox.Log('FILTER', findKey)
|
||||
$input = Ox.Input({
|
||||
autocomplete: findKey.values,
|
||||
autocompleteSelect: true,
|
||||
autocompleteSelectSubmit: true,
|
||||
value: value,
|
||||
width: 288
|
||||
});
|
||||
} else if (findKey.format) {
|
||||
formatArgs = findKey.format.args
|
||||
formatType = findKey.format.type;
|
||||
if (formatType == 'color') {
|
||||
isHue = formatArgs[0] == 'hue';
|
||||
$input = Ox.Range({
|
||||
max: isHue ? 360 : 1,
|
||||
min: 0,
|
||||
size: !isArray ? 288 : 128, // fixme: should be width!
|
||||
width: !isArray ? 288 : 128, // have to set this too, for formatting when tuple
|
||||
step: isHue ? 1 : 0.01,
|
||||
thumbSize: 48,
|
||||
thumbValue: true,
|
||||
trackColors: isHue ? [
|
||||
'rgb(255, 0, 0)', 'rgb(255, 255, 0)',
|
||||
'rgb(0, 255, 0)', 'rgb(0, 255, 255)',
|
||||
'rgb(0, 0, 255)', 'rgb(255, 0, 255)',
|
||||
'rgb(255, 0, 0)'
|
||||
] : ['rgb(0, 0, 0)', 'rgb(255, 255, 255)'],
|
||||
value: value
|
||||
});
|
||||
} else if (formatType == 'date') {
|
||||
$input = Ox.DateInput(!isArray ? {
|
||||
value: value,
|
||||
width: {day: 66, month: 66, year: 140}
|
||||
} : {
|
||||
value: value,
|
||||
width: {day: 32, month: 32, year: 48}
|
||||
});
|
||||
} else if (formatType == 'duration') {
|
||||
$input = Ox.TimeInput(!isArray ? {
|
||||
seconds: true,
|
||||
value: value,
|
||||
width: {hours: 91, minutes: 91, seconds: 90}
|
||||
} : {
|
||||
seconds: true,
|
||||
value: value,
|
||||
width: {hours: 38, minutes: 37, seconds: 37}
|
||||
});
|
||||
} else if (formatType == 'number') {
|
||||
$input = Ox.Input({
|
||||
type: type,
|
||||
value: value,
|
||||
width: 288
|
||||
});
|
||||
} else if (formatType == 'resolution') {
|
||||
$input = Ox.InputGroup({
|
||||
inputs: [
|
||||
Ox.Input({
|
||||
id: 'width',
|
||||
type: 'int',
|
||||
value: value
|
||||
}),
|
||||
Ox.Input({
|
||||
id: 'height',
|
||||
type: 'int',
|
||||
value: value
|
||||
})
|
||||
],
|
||||
separators: [{title: 'x', width: 16}],
|
||||
width: !isArray ? 288 : 128
|
||||
})
|
||||
} else if ([
|
||||
'currency', 'percent', 'unit', 'value'
|
||||
].indexOf(formatType) > -1) {
|
||||
title = formatType == 'percent' ? '%' : formatArgs[0];
|
||||
$input = Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Input({
|
||||
type: type,
|
||||
value: value,
|
||||
width: !isArray ? 242 : 80
|
||||
}),
|
||||
formatType == 'value' ? Ox.Select({
|
||||
overlap: 'left',
|
||||
items: ['K', 'M', 'G', 'T'].map(function(prefix, i) {
|
||||
return {id: Math.pow(1000, i + 1), title: prefix + title};
|
||||
}),
|
||||
width: 48
|
||||
}) : Ox.Label({
|
||||
overlap: 'left',
|
||||
textAlign: 'center',
|
||||
title: title,
|
||||
width: 48
|
||||
})
|
||||
],
|
||||
float: 'right',
|
||||
join: function(value) {
|
||||
return formatType == 'value'
|
||||
? value[0] * value[1]
|
||||
: value[0];
|
||||
},
|
||||
split: function(value) {
|
||||
|
||||
},
|
||||
width: !isArray ? 288 : 128
|
||||
})
|
||||
}
|
||||
} else {
|
||||
$input = Ox.Input({
|
||||
type: type,
|
||||
value: value,
|
||||
width: !isArray ? 288 : 128
|
||||
});
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
function setValue() {
|
||||
// fixme: this should not happen, but some lists
|
||||
// have their query set to {} or their query operator set to ''
|
||||
if (Ox.isEmpty(self.options.value)) {
|
||||
self.options.value = {conditions: [], operator: '&'};
|
||||
} else if (self.options.value.operator == '') {
|
||||
self.options.value.operator = '&';
|
||||
}
|
||||
Ox.Log('Form', 'Ox.Filter self.options', self.options);
|
||||
// end fixme
|
||||
if (!self.options.value.conditions.length) {
|
||||
self.options.value.conditions = [{
|
||||
key: self.options.findKeys[0].id,
|
||||
value: '',
|
||||
operator: self.conditionOperators[
|
||||
getConditionType(self.options.findKeys[0].type)
|
||||
][0].id
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
function triggerChangeEvent() {
|
||||
var value = Ox.clone(self.options.value, true);
|
||||
/*
|
||||
// FIXME: doesn't work for nested conditions
|
||||
value.conditions.forEach(function(condition) {
|
||||
// Ox.print('CO', condition.operator)
|
||||
condition.operator = condition.operator.replace(':', '');
|
||||
});
|
||||
*/
|
||||
that.triggerEvent('change', {value: value});
|
||||
}
|
||||
|
||||
/*@
|
||||
getList <f> getList
|
||||
@*/
|
||||
// fixme: is this the best way/name?
|
||||
that.getList = function() {
|
||||
if (self.$save) {
|
||||
var value = self.$save.value();
|
||||
return {
|
||||
save: value[0],
|
||||
name: value[1],
|
||||
query: self.options.value
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
that.value = function() {
|
||||
if (arguments.length == 0) {
|
||||
return self.options.value;
|
||||
} else {
|
||||
return that.options({value: arguments[0]});
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
184
source/UI/js/Form/Form.js
Normal file
184
source/UI/js/Form/Form.js
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Form <f> Form Object
|
||||
options <o> Options object
|
||||
error <s> error
|
||||
id <s> id
|
||||
items <a|[]> []
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Form Object
|
||||
change <!> change
|
||||
validate <!> validate
|
||||
submit <!> submit
|
||||
@*/
|
||||
|
||||
Ox.Form = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
error: '',
|
||||
id: '',
|
||||
items: [],
|
||||
validate: function(valid) {
|
||||
return Ox.every(valid);
|
||||
}
|
||||
})
|
||||
.options(options || {})
|
||||
.addClass('OxForm');
|
||||
|
||||
Ox.extend(self, {
|
||||
$items: [],
|
||||
$messages: [],
|
||||
itemIds: [],
|
||||
itemIsValid: []
|
||||
});
|
||||
|
||||
self.options.items.forEach(function(item, i) {
|
||||
validateItem(i, function(valid) {
|
||||
self.itemIsValid[i] = valid;
|
||||
});
|
||||
self.itemIds[i] = item.options('id') || item.id;
|
||||
self.$items[i] = Ox.FormItem({element: item}).appendTo(that);
|
||||
item.bindEvent({
|
||||
autovalidate: function(data) {
|
||||
validateForm(i, data.valid);
|
||||
data.valid && self.$items[i].setMessage('');
|
||||
},
|
||||
/*
|
||||
// fixme: should't inputs also trigger a change event?
|
||||
blur: function(data) {
|
||||
that.triggerEvent('change', {
|
||||
id: self.itemIds[i],
|
||||
data: data
|
||||
});
|
||||
},
|
||||
*/
|
||||
change: function(data) {
|
||||
// fixme: shouldn't this be key/value instead of id/data?
|
||||
that.triggerEvent('change', {
|
||||
id: self.itemIds[i],
|
||||
data: data
|
||||
});
|
||||
validateItem(i, function(valid) {
|
||||
validateForm(i, valid);
|
||||
});
|
||||
},
|
||||
submit: function(data) {
|
||||
self.formIsValid && that.submit();
|
||||
},
|
||||
validate: function(data) {
|
||||
validateForm(i, data.valid);
|
||||
// timeout needed for cases where the form is removed
|
||||
// from the DOM, triggering blur of an empty item -
|
||||
// in this case, we don't want the message to appear
|
||||
setTimeout(function() {
|
||||
self.$items[i].setMessage(data.valid ? '' : data.message);
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
self.formIsValid = self.options.validate(self.itemIsValid);
|
||||
|
||||
function getItemIndexById(id) {
|
||||
return self.itemIds.indexOf(id);
|
||||
}
|
||||
|
||||
function validateForm(pos, valid) {
|
||||
self.itemIsValid[pos] = valid;
|
||||
if (self.options.validate(self.itemIsValid) != self.formIsValid) {
|
||||
self.formIsValid = !self.formIsValid;
|
||||
that.triggerEvent('validate', {
|
||||
valid: self.formIsValid
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function validateItem(pos, callback) {
|
||||
var item = self.options.items[pos],
|
||||
validate = item.options('validate');
|
||||
if (validate) {
|
||||
validate(item.value(), function(data) {
|
||||
callback(data.valid);
|
||||
});
|
||||
} else {
|
||||
callback(item.value && !Ox.isEmpty(item.value));
|
||||
}
|
||||
}
|
||||
|
||||
/*@
|
||||
addItem <f> addItem
|
||||
(pos, item) -> <u> add item at position
|
||||
@*/
|
||||
that.addItem = function(pos, item) {
|
||||
Ox.Log('Form', 'addItem', pos)
|
||||
self.options.items.splice(pos, 0, item);
|
||||
self.$items.splice(pos, 0, Ox.FormItem({element: item}));
|
||||
pos == 0 ?
|
||||
self.$items[pos].insertBefore(self.$items[0]) :
|
||||
self.$items[pos].insertAfter(self.$items[pos - 1]);
|
||||
}
|
||||
|
||||
/*@
|
||||
removeItem <f> removeItem
|
||||
(pos) -> <u> remove item from position
|
||||
@*/
|
||||
that.removeItem = function(pos) {
|
||||
Ox.Log('Form', 'removeItem', pos);
|
||||
self.$items[pos].remove();
|
||||
self.options.items.splice(pos, 1);
|
||||
self.$items.splice(pos, 1);
|
||||
}
|
||||
|
||||
that.setMessages = function(messages) {
|
||||
Ox.forEach(messages, function(v) {
|
||||
self.$items[getItemIndexById(v.id)].setMessage(v.message);
|
||||
});
|
||||
};
|
||||
|
||||
/*@
|
||||
submit <f> submit
|
||||
@*/
|
||||
that.submit = function() {
|
||||
that.triggerEvent('submit', {values: that.values()});
|
||||
};
|
||||
|
||||
/*@
|
||||
valid <f> valid
|
||||
@*/
|
||||
that.valid = function() {
|
||||
return self.formIsValid;
|
||||
};
|
||||
|
||||
/*@
|
||||
values <f> values
|
||||
@*/
|
||||
that.values = function() {
|
||||
// FIXME: this should accept a single string argument to get a single value
|
||||
/*
|
||||
get/set form values
|
||||
call without arguments to get current form values
|
||||
pass values as array to set values (not implemented)
|
||||
*/
|
||||
var values = {};
|
||||
if (arguments.length == 0) {
|
||||
self.$items.forEach(function($item, i) {
|
||||
values[self.itemIds[i]] = self.$items[i].value();
|
||||
});
|
||||
//Ox.Log('Form', 'VALUES', values)
|
||||
return values;
|
||||
} else {
|
||||
Ox.Log('Form', 'SET FORM VALUES', arguments[0])
|
||||
Ox.forEach(arguments[0], function(value, key) {
|
||||
var index = getItemIndexById(key);
|
||||
index > -1 && self.options.items[index].value(value);
|
||||
});
|
||||
return that;
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
128
source/UI/js/Form/FormElementGroup.js
Normal file
128
source/UI/js/Form/FormElementGroup.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.FormElementGroup <f> FormElementGroup Element
|
||||
options <o> Options object
|
||||
id <s> element id
|
||||
elements <[o:Ox.Element]|[]> elements in group
|
||||
float <s|left> alignment
|
||||
separators <a|[]> separators (not implemented)
|
||||
width <n|0> group width
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> FormElementGroup Element
|
||||
autovalidate <!> autovalidate
|
||||
change <!> change
|
||||
validate <!> validate
|
||||
@*/
|
||||
|
||||
Ox.FormElementGroup = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
id: '',
|
||||
elements: [],
|
||||
float: 'left',
|
||||
join: null,
|
||||
separators: [],
|
||||
split: null,
|
||||
value: options.split ? '' : [],
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
value: setValue
|
||||
})
|
||||
.addClass('OxInputGroup');
|
||||
|
||||
if (Ox.isEmpty(self.options.value)) {
|
||||
self.options.value = getValue();
|
||||
} else {
|
||||
setValue();
|
||||
}
|
||||
|
||||
(
|
||||
self.options.float == 'left' ?
|
||||
self.options.elements : Ox.clone(self.options.elements).reverse()
|
||||
).forEach(function($element) {
|
||||
$element.css({
|
||||
float: self.options.float // fixme: make this a class
|
||||
})
|
||||
.bindEvent({
|
||||
autovalidate: function(data) {
|
||||
that.triggerEvent({autovalidate: data});
|
||||
},
|
||||
change: change,
|
||||
//submit: change,
|
||||
validate: function(data) {
|
||||
that.triggerEvent({validate: data});
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
});
|
||||
|
||||
function change(data) {
|
||||
self.options.value = getValue();
|
||||
that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
|
||||
/*
|
||||
if (self.options.width) {
|
||||
setWidths();
|
||||
} else {
|
||||
self.options.width = getWidth();
|
||||
}
|
||||
that.css({
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
*/
|
||||
|
||||
function getValue() {
|
||||
var value = self.options.elements.map(function($element) {
|
||||
return $element.value ? $element.value() : void 0;
|
||||
});
|
||||
return self.options.join ? self.options.join(value) : value;
|
||||
}
|
||||
|
||||
function getWidth() {
|
||||
|
||||
}
|
||||
|
||||
function setValue() {
|
||||
var values = self.options.split
|
||||
? self.options.split(self.options.value)
|
||||
: self.options.value;
|
||||
values.forEach(function(value, i) {
|
||||
self.options.elements[i].value && self.options.elements[i].value(value);
|
||||
});
|
||||
}
|
||||
|
||||
function setWidth() {
|
||||
|
||||
}
|
||||
|
||||
/*@
|
||||
replaceElement <f> replaceElement
|
||||
(pos, element) -> <u> replcae element at position
|
||||
@*/
|
||||
that.replaceElement = function(pos, element) {
|
||||
Ox.Log('Form', 'Ox.FormElementGroup replaceElement', pos, element)
|
||||
self.options.elements[pos].replaceWith(element.$element);
|
||||
self.options.elements[pos] = element;
|
||||
};
|
||||
|
||||
/*@
|
||||
value <f> value
|
||||
@*/
|
||||
that.value = function() {
|
||||
var values = self.options.elements.map(function(element) {
|
||||
return element.value ? element.value() : void 0;
|
||||
});
|
||||
return self.options.joinValues
|
||||
? self.options.joinValues(values)
|
||||
: values;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
53
source/UI/js/Form/FormItem.js
Normal file
53
source/UI/js/Form/FormItem.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.FormItem <f> FormItem Element, wraps form element with an error message
|
||||
options <o> Options object
|
||||
element <o|null> element
|
||||
error <s> error message
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> FormItem Element
|
||||
@*/
|
||||
|
||||
Ox.FormItem = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
element: null,
|
||||
error: ''
|
||||
})
|
||||
.options(options || {})
|
||||
.addClass('OxFormItem');
|
||||
|
||||
self.description = self.options.element.options('description');
|
||||
if (self.description) {
|
||||
$('<div>')
|
||||
.addClass('OxFormDescription OxSelectable')
|
||||
.html(self.description)
|
||||
.appendTo(that);
|
||||
}
|
||||
that.append(self.options.element);
|
||||
self.$message = Ox.Element()
|
||||
.addClass('OxFormMessage OxSelectable')
|
||||
.appendTo(that);
|
||||
|
||||
/*@
|
||||
setMessage <f> set message
|
||||
(message) -> <u> set message
|
||||
@*/
|
||||
that.setMessage = function(message) {
|
||||
self.$message.html(message)[message !== '' ? 'show' : 'hide']();
|
||||
};
|
||||
|
||||
/*@
|
||||
value <f> get value
|
||||
() -> <s> get value of wrapped element
|
||||
@*/
|
||||
that.value = function() {
|
||||
return self.options.element.value();
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
203
source/UI/js/Form/FormPanel.js
Normal file
203
source/UI/js/Form/FormPanel.js
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.FormPanel <f> Form Panel
|
||||
options <o> Options
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Form Panel
|
||||
change <!> Fires when a value changed
|
||||
select <!> Fires when a section gets selected
|
||||
validate <!> Fires when the form becomes valid or invalid
|
||||
@*/
|
||||
Ox.FormPanel = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
form: [],
|
||||
listSize: 256
|
||||
})
|
||||
.options(options || {});
|
||||
|
||||
self.section = 0;
|
||||
self.sectionTitle = self.options.form[self.section].title;
|
||||
self.$list = Ox.TableList({
|
||||
columns: [
|
||||
{
|
||||
id: 'id',
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
format: function(value) {
|
||||
return $('<img>')
|
||||
.attr({
|
||||
src: Ox.UI.getImageURL('symbolCheck')
|
||||
})
|
||||
.css({
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
margin: '2px 2px 2px 0',
|
||||
opacity: value ? 1 : 0.1
|
||||
})
|
||||
},
|
||||
id: 'valid',
|
||||
title: Ox._('Valid'),
|
||||
visible: true,
|
||||
width: 16
|
||||
},
|
||||
{
|
||||
format: function(value) {
|
||||
return (Ox.indexOf(self.options.form, function(section) {
|
||||
return section.title == value;
|
||||
}) + 1) + '. ' + value;
|
||||
},
|
||||
id: 'title',
|
||||
title: Ox._('Title'),
|
||||
visible: true,
|
||||
width: self.options.listSize - 16
|
||||
}
|
||||
],
|
||||
items: self.options.form.map(function(section) {
|
||||
return {id: section.id, title: section.title, valid: false};
|
||||
}),
|
||||
max: 1,
|
||||
min: 1,
|
||||
selected: [self.options.form[0].id],
|
||||
sort: [{key: 'id', operator: '+'}],
|
||||
unique: 'id',
|
||||
width: self.options.listSize
|
||||
}).bindEvent({
|
||||
select: function(data) {
|
||||
self.$sections[self.section].hide();
|
||||
self.section = Ox.getIndexById(self.options.form, data.ids[0]);
|
||||
self.$sections[self.section].show();
|
||||
that.triggerEvent('select', {section: data.ids[0]});
|
||||
}
|
||||
});
|
||||
|
||||
self.$section = $('<div>')
|
||||
.css({overflowY: 'auto'});
|
||||
self.$forms = [];
|
||||
self.$sections = self.options.form.map(function(section, i) {
|
||||
return $('<div>')
|
||||
.css({
|
||||
width: (
|
||||
section.descriptionWidth || section.items[0].options('width')
|
||||
) + 'px',
|
||||
margin: '16px'
|
||||
})
|
||||
.append(
|
||||
$('<div>')
|
||||
.addClass('OxSelectable')
|
||||
.css({marginBottom: '8px', fontWeight: 'bold'})
|
||||
.html((i + 1) + '. ' + section.title)
|
||||
)
|
||||
.append(
|
||||
$('<div>')
|
||||
.addClass('OxSelectable')
|
||||
.css({marginBottom: '16px'})
|
||||
.html(section.description)
|
||||
)
|
||||
.append(
|
||||
self.$forms[i] = Ox.Form({
|
||||
items: section.items,
|
||||
validate: section.validate
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
self.$list.value(section.id, 'valid', self.$forms[i].valid());
|
||||
that.triggerEvent('change', {
|
||||
section: section.id,
|
||||
data: data
|
||||
});
|
||||
},
|
||||
validate: function(data) {
|
||||
self.$list.value(section.id, 'valid', data.valid);
|
||||
that.triggerEvent('validate', {
|
||||
section: section.id,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
})
|
||||
)
|
||||
.hide()
|
||||
.appendTo(self.$section);
|
||||
});
|
||||
|
||||
self.$list.bindEvent('load', function() {
|
||||
self.$forms.forEach(function($form, i) {
|
||||
self.$list.value(self.options.form[i].id, 'valid', $form.valid());
|
||||
});
|
||||
});
|
||||
|
||||
self.$sections[0].show();
|
||||
|
||||
that.setElement(Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: self.$list,
|
||||
size: self.options.listSize
|
||||
},
|
||||
{
|
||||
element: self.$section
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
}));
|
||||
|
||||
/*@
|
||||
renderPrintVersion <f> renderPrintVersion
|
||||
(title) -> <s>
|
||||
@*/
|
||||
that.renderPrintVersion = function(title) {
|
||||
var $printVersion = $('<div>').css({overflowY: 'auto'});
|
||||
$printVersion.append(
|
||||
$('<div>')
|
||||
.addClass('OxFormSectionTitle')
|
||||
.css({
|
||||
height: '16px',
|
||||
padding: '16px 16px 8px 16px',
|
||||
fontWeight: 'bold'
|
||||
})
|
||||
.html(title)
|
||||
);
|
||||
self.$sections.forEach(function($section, i) {
|
||||
// jQuery bug: textarea html/val does not survive cloning
|
||||
// http://bugs.jquery.com/ticket/3016
|
||||
var $clone = $section.clone(true),
|
||||
textareas = {
|
||||
section: $section.find('textarea'),
|
||||
clone: $clone.find('textarea')
|
||||
};
|
||||
textareas.section.each(function(i) {
|
||||
$(textareas.clone[i]).val($(this).val());
|
||||
});
|
||||
$printVersion
|
||||
.append(
|
||||
$('<div>').css({
|
||||
height: '1px',
|
||||
margin: '8px 0 8px 0',
|
||||
background: 'rgb(128, 128, 128)'
|
||||
})
|
||||
)
|
||||
.append(
|
||||
$clone.show()
|
||||
);
|
||||
});
|
||||
return $printVersion;
|
||||
};
|
||||
|
||||
/*@
|
||||
values <f> values
|
||||
@*/
|
||||
that.values = function() {
|
||||
var values = {};
|
||||
self.options.form.forEach(function(section, i) {
|
||||
values[section.id] = self.$forms[i].values();
|
||||
});
|
||||
return values;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
983
source/UI/js/Form/Input.js
Normal file
983
source/UI/js/Form/Input.js
Normal file
|
|
@ -0,0 +1,983 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Input <f> Input Element
|
||||
options <o> Options object
|
||||
arrows <b> if true, and type is 'float' or 'int', display arrows
|
||||
arrowStep <n> step when clicking arrows
|
||||
autocomplete <a> array of possible values, or
|
||||
<f> function(key, value, callback), returns one or more values
|
||||
autocompleteReplace <b> if true, value is replaced
|
||||
autocompleteReplaceCorrect <b> if true, only valid values can be entered
|
||||
autocompleteSelect <b> if true, menu is displayed
|
||||
autocompleteSelectHighlight <b> if true, value in menu is highlighted
|
||||
autocompleteSelectMaxWidth <n|0> Maximum width of autocomplete menu, or 0
|
||||
autocompleteSelectSubmit <b> if true, submit input on menu selection
|
||||
autocorrect <s|r|f|null> ('email', 'float', 'int', 'phone', 'url'), or
|
||||
<r> regexp(value), or
|
||||
<f> function(key, value, blur, callback), returns value
|
||||
autovalidate <f> --remote validation--
|
||||
clear <b> if true, has clear button
|
||||
clearTooltip <s|f|''> clear button tooltip
|
||||
changeOnKeypress <b> if true, fire change event while typing
|
||||
disabled <b> if true, is disabled
|
||||
height <n> px (for type='textarea' and type='range' with orientation='horizontal')
|
||||
id <s> element id
|
||||
key <s> to be passed to autocomplete and autovalidate functions
|
||||
label <s|''> Label
|
||||
labelWidth <n|64> Label width
|
||||
max <n> max value if type is 'int' or 'float'
|
||||
min <n> min value if type is 'int' or 'float'
|
||||
name <s> will be displayed by autovalidate function ('invalid ' + name)
|
||||
overlap <s> '', 'left' or 'right', will cause padding and negative margin
|
||||
picker <o> picker object
|
||||
rangeOptions <o> range options
|
||||
arrows <b>boolean, if true, display arrows
|
||||
//arrowStep <n> number, step when clicking arrows
|
||||
//arrowSymbols <a> array of two strings
|
||||
max <n> number, maximum value
|
||||
min <n> number, minimum value
|
||||
orientation <s> 'horizontal' or 'vertical'
|
||||
step <n> number, step
|
||||
thumbValue <b> boolean, if true, value is displayed on thumb, or
|
||||
<a> array of strings per value, or
|
||||
<f> function(value), returns string
|
||||
thumbSize <n> integer, px
|
||||
trackGradient <s> string, css gradient for track
|
||||
trackImage <s> string, image url, or
|
||||
<a> array of image urls
|
||||
//trackStep <n> number, 0 for 'scroll here', positive for step
|
||||
trackValues <b> boolean
|
||||
serialize <f> function used to serialize value in submit
|
||||
style <s> 'rounded' or 'square'
|
||||
textAlign <s> 'left', 'center' or 'right'
|
||||
type <s> 'float', 'int', 'password', 'text', 'textarea'
|
||||
value <s> string
|
||||
validate <f> remote validation
|
||||
width <n> px
|
||||
([options[, self]]) -> <o:Ox.Element> Input Element
|
||||
autocomplete <!> autocomplete
|
||||
autovalidate <!> autovalidate
|
||||
blur <!> blur
|
||||
cancel <!> cancel
|
||||
change <!> input changed event
|
||||
clear <!> clear
|
||||
focus <!> focus
|
||||
insert <!> insert
|
||||
submit <!> input submit event
|
||||
validate <!> validate
|
||||
@*/
|
||||
|
||||
Ox.Input = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({
|
||||
element: options.element || '<div>'
|
||||
}, self)
|
||||
.defaults({
|
||||
arrows: false,
|
||||
arrowStep: 1,
|
||||
autocomplete: null,
|
||||
autocompleteReplace: false,
|
||||
autocompleteReplaceCorrect: false,
|
||||
autocompleteSelect: false,
|
||||
autocompleteSelectHighlight: false,
|
||||
autocompleteSelectMax: 0,
|
||||
autocompleteSelectMaxWidth: 0,
|
||||
autocompleteSelectSubmit: false,
|
||||
autovalidate: null,
|
||||
changeOnKeypress: false,
|
||||
clear: false,
|
||||
clearTooltip: '',
|
||||
decimals: 0,
|
||||
disabled: false,
|
||||
height: 16,
|
||||
key: '',
|
||||
min: -Infinity,
|
||||
max: Infinity,
|
||||
label: '',
|
||||
labelWidth: 64,
|
||||
overlap: 'none',
|
||||
placeholder: '',
|
||||
serialize: null,
|
||||
style: 'rounded',
|
||||
textAlign: 'left',
|
||||
type: 'text',
|
||||
validate: null,
|
||||
value: '',
|
||||
width: 128
|
||||
})
|
||||
.options(options || {})
|
||||
.update(function(key, value) {
|
||||
var inputWidth;
|
||||
if ([
|
||||
'autocomplete', 'autocompleteReplace', 'autocompleteSelect', 'autovalidate'
|
||||
].indexOf(key) > -1) {
|
||||
if (self.options.autocomplete && self.options.autocompleteSelect) {
|
||||
self.$autocompleteMenu = constructAutocompleteMenu();
|
||||
}
|
||||
self.bindKeyboard = self.options.autocomplete || self.options.autovalidate;
|
||||
} else if (key == 'disabled') {
|
||||
self.$input.attr({disabled: value});
|
||||
} else if (key == 'height') {
|
||||
that.css({height: value + 'px'});
|
||||
self.$input.css({height: value - 6 + 'px'});
|
||||
} else if (key == 'label') {
|
||||
self.$label.options({title: value});
|
||||
} else if (key == 'labelWidth') {
|
||||
self.$label.options({width: value});
|
||||
inputWidth = getInputWidth();
|
||||
self.$input.css({
|
||||
width: inputWidth + 'px'
|
||||
});
|
||||
self.hasPasswordPlaceholder && self.$placeholder.css({
|
||||
width: inputWidth + 'px'
|
||||
});
|
||||
} else if (key == 'placeholder') {
|
||||
setPlaceholder();
|
||||
} else if (key == 'value') {
|
||||
if (self.options.type == 'float' && self.options.decimals) {
|
||||
self.options.value = self.options.value.toFixed(self.options.decimals);
|
||||
}
|
||||
self.$input.val(self.options.value);
|
||||
that.is('.OxError') && that.removeClass('OxError');
|
||||
setPlaceholder();
|
||||
} else if (key == 'width') {
|
||||
that.css({width: self.options.width + 'px'});
|
||||
inputWidth = getInputWidth();
|
||||
self.$input.css({
|
||||
width: inputWidth + 'px'
|
||||
});
|
||||
self.hasPasswordPlaceholder && self.$placeholder.css({
|
||||
width: inputWidth + 'px'
|
||||
});
|
||||
}
|
||||
})
|
||||
.addClass(
|
||||
'OxInput OxKeyboardFocus OxMedium Ox' + Ox.toTitleCase(self.options.style)
|
||||
+ (self.options.type == 'textarea' ? ' OxTextarea' : '') /*+ (
|
||||
self.options.overlap != 'none' ?
|
||||
' OxOverlap' + Ox.toTitleCase(self.options.overlap) : ''
|
||||
)*/
|
||||
)
|
||||
.css(
|
||||
Ox.extend({
|
||||
width: self.options.width + 'px'
|
||||
}, self.options.type == 'textarea' ? {
|
||||
height: self.options.height + 'px'
|
||||
} : {})
|
||||
)
|
||||
.bindEvent(Ox.extend(self.options.type != 'textarea' ? {
|
||||
key_enter: submit
|
||||
} : {}, {
|
||||
key_control_i: insert,
|
||||
key_escape: cancel,
|
||||
key_shift_enter: submit
|
||||
}));
|
||||
|
||||
if (
|
||||
Ox.isArray(self.options.autocomplete)
|
||||
&& self.options.autocompleteReplace
|
||||
&& self.options.autocompleteReplaceCorrect
|
||||
&& self.options.value === ''
|
||||
) {
|
||||
self.options.value = self.options.autocomplete[0]
|
||||
}
|
||||
|
||||
// fixme: set to min, not 0
|
||||
// fixme: validate self.options.value !
|
||||
if (self.options.type == 'float') {
|
||||
self.decimals = Ox.repeat('0', self.options.decimals || 1)
|
||||
Ox.extend(self.options, {
|
||||
autovalidate: 'float',
|
||||
textAlign: 'right',
|
||||
value: self.options.value || '0.' + self.decimals
|
||||
});
|
||||
} else if (self.options.type == 'int') {
|
||||
Ox.extend(self.options, {
|
||||
autovalidate: 'int',
|
||||
textAlign: 'right',
|
||||
value: self.options.value || '0'
|
||||
});
|
||||
}
|
||||
|
||||
if (self.options.label) {
|
||||
self.$label = Ox.Label({
|
||||
overlap: 'right',
|
||||
textAlign: 'right',
|
||||
title: self.options.label,
|
||||
width: self.options.labelWidth
|
||||
})
|
||||
.css({
|
||||
float: 'left' // fixme: use css rule
|
||||
})
|
||||
.on({
|
||||
click: function() {
|
||||
// fixme: ???
|
||||
// that.focus();
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
if (self.options.arrows) {
|
||||
self.arrows = [];
|
||||
self.arrows[0] = [
|
||||
Ox.Button({
|
||||
overlap: 'right',
|
||||
title: 'left',
|
||||
type: 'image'
|
||||
})
|
||||
.css({float: 'left'})
|
||||
.on({
|
||||
click: function() {
|
||||
clickArrow(0);
|
||||
}
|
||||
})
|
||||
.appendTo(that),
|
||||
Ox.Button({
|
||||
overlap: 'left',
|
||||
title: 'right',
|
||||
type: 'image'
|
||||
})
|
||||
.css({float: 'right'})
|
||||
.on({
|
||||
click: function() {
|
||||
clickArrow(0);
|
||||
}
|
||||
})
|
||||
.appendTo(that)
|
||||
]
|
||||
}
|
||||
|
||||
self.bindKeyboard = self.options.autocomplete
|
||||
|| self.options.autovalidate
|
||||
|| self.options.changeOnKeypress;
|
||||
self.hasPasswordPlaceholder = self.options.type == 'password'
|
||||
&& self.options.placeholder;
|
||||
self.inputWidth = getInputWidth();
|
||||
|
||||
if (self.options.clear) {
|
||||
self.$button = Ox.Button({
|
||||
overlap: 'left',
|
||||
// FIXME: should always be self.options.style, but there
|
||||
// is a CSS bug for rounded image buttons
|
||||
style: self.options.style == 'squared' ? 'squared' : '',
|
||||
title: 'close',
|
||||
tooltip: self.options.clearTooltip,
|
||||
type: 'image'
|
||||
})
|
||||
.css({
|
||||
float: 'right' // fixme: use css rule
|
||||
})
|
||||
.bindEvent({
|
||||
click: clear,
|
||||
doubleclick: submit
|
||||
})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
self.$input = $(self.options.type == 'textarea' ? '<textarea>' : '<input>')
|
||||
.addClass('OxInput OxKeyboardFocus OxMedium Ox' + Ox.toTitleCase(self.options.style))
|
||||
.attr({
|
||||
disabled: self.options.disabled,
|
||||
type: self.options.type == 'password' ? 'password' : 'text'
|
||||
})
|
||||
.css(
|
||||
Ox.extend({
|
||||
width: self.inputWidth + 'px',
|
||||
textAlign: self.options.textAlign
|
||||
}, self.options.type == 'textarea' ? {
|
||||
height: self.options.height - 6 + 'px'
|
||||
} : {})
|
||||
)
|
||||
.val(self.options.value)
|
||||
.on({
|
||||
blur: blur,
|
||||
change: change,
|
||||
focus: focus
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
if (self.bindKeyboard) {
|
||||
self.$input.on({
|
||||
paste: keydown
|
||||
});
|
||||
}
|
||||
|
||||
if (self.options.type == 'textarea') {
|
||||
Ox.Log('Form', 'TEXTAREA', self.options.width, self.options.height, '...', that.css('width'), that.css('height'), '...', self.$input.css('width'), self.$input.css('height'), '...', self.$input.css('border'))
|
||||
}
|
||||
|
||||
// fixme: is there a better way than this one?
|
||||
// should at least go into ox.ui.theme.foo.js
|
||||
// probably better: divs in the background
|
||||
/*
|
||||
if (self.options.type == 'textarea') {
|
||||
Ox.extend(self, {
|
||||
colors: Ox.Theme() == 'oxlight' ?
|
||||
[208, 232, 244] :
|
||||
//[0, 16, 32],
|
||||
[32, 48, 64],
|
||||
colorstops: [8 / self.options.height, self.options.height - 8 / self.options.height]
|
||||
});
|
||||
self.$input.css({
|
||||
background: '-moz-linear-gradient(top, rgb(' +
|
||||
[self.colors[0], self.colors[0], self.colors[0]].join(', ') + '), rgb(' +
|
||||
[self.colors[1], self.colors[1], self.colors[1]].join(', ') + ') ' +
|
||||
Math.round(self.colorstops[0] * 100) + '%, rgb(' +
|
||||
[self.colors[1], self.colors[1], self.colors[1]].join(', ') + ') ' +
|
||||
Math.round(self.colorstops[1] * 100) + '%, rgb(' +
|
||||
[self.colors[2], self.colors[2], self.colors[2]].join(', ') + '))'
|
||||
});
|
||||
self.$input.css({
|
||||
background: '-webkit-linear-gradient(top, rgb(' +
|
||||
[self.colors[0], self.colors[0], self.colors[0]].join(', ') + '), rgb(' +
|
||||
[self.colors[1], self.colors[1], self.colors[1]].join(', ') + ') ' +
|
||||
Math.round(self.colorstops[0] * 100) + '%, rgb(' +
|
||||
[self.colors[1], self.colors[1], self.colors[1]].join(', ') + ') ' +
|
||||
Math.round(self.colorstops[1] * 100) + '%, rgb(' +
|
||||
[self.colors[2], self.colors[2], self.colors[2]].join(', ') + '))'
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
if (self.hasPasswordPlaceholder) {
|
||||
self.$input.hide();
|
||||
self.$placeholder = $('<input>')
|
||||
.addClass('OxInput OxKeyboardFocus OxMedium Ox' +
|
||||
Ox.toTitleCase(self.options.style) +
|
||||
' OxPlaceholder')
|
||||
.attr({type: 'text'})
|
||||
.css({width: self.inputWidth + 'px'})
|
||||
.val(self.options.placeholder)
|
||||
.on({focus: focus})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
if (self.options.autocomplete && self.options.autocompleteSelect) {
|
||||
self.$autocompleteMenu = constructAutocompleteMenu();
|
||||
}
|
||||
|
||||
self.options.placeholder && setPlaceholder();
|
||||
|
||||
function autocomplete(oldValue, oldCursor) {
|
||||
|
||||
oldValue = Ox.isUndefined(oldValue) ? self.options.value : oldValue;
|
||||
oldCursor = Ox.isUndefined(oldCursor) ? cursor() : oldCursor;
|
||||
|
||||
Ox.Log('AUTO', 'autocomplete', oldValue, oldCursor)
|
||||
|
||||
if (self.options.value || self.options.autocompleteReplaceCorrect) {
|
||||
var id = Ox.uid();
|
||||
self.autocompleteId = id;
|
||||
if (Ox.isFunction(self.options.autocomplete)) {
|
||||
if (self.options.key) {
|
||||
self.options.autocomplete(
|
||||
self.options.key, self.options.value, autocompleteCallback
|
||||
);
|
||||
} else {
|
||||
self.options.autocomplete(
|
||||
self.options.value, autocompleteCallback
|
||||
);
|
||||
}
|
||||
} else {
|
||||
autocompleteCallback(autocompleteFunction());
|
||||
}
|
||||
}
|
||||
if (!self.options.value) {
|
||||
if (self.options.autocompleteSelect) {
|
||||
self.$autocompleteMenu
|
||||
.unbindEvent('select')
|
||||
.hideMenu();
|
||||
self.selectEventBound = false;
|
||||
}
|
||||
}
|
||||
|
||||
function autocompleteFunction() {
|
||||
return Ox.find(
|
||||
self.options.autocomplete,
|
||||
self.options.value,
|
||||
self.options.autocompleteReplace
|
||||
);
|
||||
}
|
||||
|
||||
function autocompleteCallback(values) {
|
||||
|
||||
if (self.autocompleteId != id) {
|
||||
return;
|
||||
}
|
||||
//Ox.Log('Form', 'autocompleteCallback', values[0], self.options.value, self.options.value.length, oldValue, oldCursor)
|
||||
|
||||
var length = self.options.value.length,
|
||||
newValue, newLength,
|
||||
pos = cursor(),
|
||||
selected = -1,
|
||||
selectEnd = length == 0 || (values[0] && values[0].length),
|
||||
value;
|
||||
|
||||
if (values[0]) {
|
||||
if (self.options.autocompleteReplace) {
|
||||
newValue = values[0];
|
||||
} else {
|
||||
newValue = self.options.value;
|
||||
}
|
||||
} else {
|
||||
if (self.options.autocompleteReplaceCorrect) {
|
||||
newValue = oldValue;
|
||||
} else {
|
||||
newValue = self.options.value
|
||||
}
|
||||
}
|
||||
newLength = newValue.length;
|
||||
|
||||
if (self.options.autocompleteReplace) {
|
||||
value = self.options.value;
|
||||
self.options.value = newValue;
|
||||
self.$input.val(self.options.value);
|
||||
if (selectEnd) {
|
||||
cursor(length, newLength);
|
||||
} else if (self.options.autocompleteReplaceCorrect) {
|
||||
cursor(oldCursor);
|
||||
} else {
|
||||
cursor(pos);
|
||||
}
|
||||
selected = 0;
|
||||
}
|
||||
|
||||
if (self.options.autocompleteSelect) {
|
||||
value = (
|
||||
self.options.autocompleteReplace
|
||||
? value : self.options.value
|
||||
).toLowerCase();
|
||||
if (values.length) {
|
||||
self.oldCursor = cursor();
|
||||
self.oldValue = self.options.value;
|
||||
self.$autocompleteMenu.options({
|
||||
items: Ox.filter(values, function(v, i) {
|
||||
var ret = false;
|
||||
if (
|
||||
!self.options.autocompleteSelectMax ||
|
||||
i < self.options.autocompleteSelectMax
|
||||
) {
|
||||
if (v.toLowerCase() === value) {
|
||||
selected = i;
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}).map(function(v) {
|
||||
return {
|
||||
id: v.toLowerCase().replace(/ /g, '_'), // fixme: need function to do lowercase, underscores etc?
|
||||
title: self.options.autocompleteSelectHighlight
|
||||
? Ox.highlight(v, value, 'OxHighlight') : v
|
||||
};
|
||||
})
|
||||
});
|
||||
if (!self.selectEventBound) {
|
||||
self.$autocompleteMenu.bindEvent({
|
||||
select: selectMenu
|
||||
});
|
||||
self.selectEventBound = true;
|
||||
}
|
||||
self.$autocompleteMenu.options({
|
||||
selected: selected
|
||||
}).showMenu();
|
||||
} else {
|
||||
self.$autocompleteMenu
|
||||
.unbindEvent('select')
|
||||
.hideMenu();
|
||||
self.selectEventBound = false;
|
||||
}
|
||||
}
|
||||
|
||||
that.triggerEvent('autocomplete', {
|
||||
value: newValue
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function autovalidate() {
|
||||
|
||||
var blur, oldCursor, oldValue;
|
||||
|
||||
if (arguments.length == 1) {
|
||||
blur = arguments[0];
|
||||
} else {
|
||||
blur = false;
|
||||
oldValue = arguments[0];
|
||||
oldCursor = arguments[1];
|
||||
}
|
||||
|
||||
if (Ox.isFunction(self.options.autovalidate)) {
|
||||
if (self.options.key) {
|
||||
self.options.autovalidate(
|
||||
self.options.key, self.options.value, blur, autovalidateCallback
|
||||
);
|
||||
} else {
|
||||
self.options.autovalidate(
|
||||
self.options.value, blur, autovalidateCallback
|
||||
);
|
||||
}
|
||||
} else if (Ox.isRegExp(self.options.autovalidate)) {
|
||||
autovalidateCallback(autovalidateFunction(self.options.value));
|
||||
} else {
|
||||
autovalidateTypeFunction(self.options.type, self.options.value);
|
||||
}
|
||||
|
||||
function autovalidateFunction(value) {
|
||||
value = value.split('').map(function(v) {
|
||||
return self.options.autovalidate.test(v) ? v : null;
|
||||
}).join('');
|
||||
return {
|
||||
valid: !!value.length,
|
||||
value: value
|
||||
};
|
||||
}
|
||||
|
||||
function autovalidateTypeFunction(type, value) {
|
||||
// fixme: remove trailing zeroes on blur
|
||||
// /(^\-?\d+\.?\d{0,8}$)/('-13000.12345678')
|
||||
var cursor,
|
||||
length,
|
||||
regexp = type == 'float' ? new RegExp(
|
||||
'(^' + (self.options.min < 0 ? '\\-?' : '') + '\\d+\\.?\\d'
|
||||
+ (self.options.decimals ? '{0,' + self.options.decimals + '}' : '*')
|
||||
+ '$)'
|
||||
) : new RegExp('(^' + (self.options.min < 0 ? '\\-?' : '') + '\\d+$)');
|
||||
if (type == 'float') {
|
||||
if (value === '') {
|
||||
value = '0.' + self.decimals;
|
||||
cursor = [0, value.length];
|
||||
} else if (value == '-') {
|
||||
value = '-0.' + self.decimals;
|
||||
cursor = [1, value.length];
|
||||
} else if (value == '.') {
|
||||
value = '0.' + self.decimals;
|
||||
cursor = [2, value.length];
|
||||
} else if (!/\./.test(value)) {
|
||||
value += '.' + self.decimals;
|
||||
cursor = [value.indexOf('.'), value.length];
|
||||
} else if (/^\./.test(value)) {
|
||||
value = '0' + value;
|
||||
cursor = [2, value.length];
|
||||
} else if (/\.$/.test(value)) {
|
||||
value += self.decimals;
|
||||
cursor = [value.indexOf('.') + 1, value.length];
|
||||
} else if (/\./.test(value) && self.options.decimals) {
|
||||
length = value.split('.')[1].length;
|
||||
if (length > self.options.decimals) {
|
||||
value = value.slice(0, value.indexOf('.') + 1 + self.options.decimals);
|
||||
cursor = [oldCursor[0] + 1, oldCursor[1] + 1];
|
||||
} else if (length < self.options.decimals) {
|
||||
value += Ox.repeat('0', self.options.decimals - length);
|
||||
cursor = [value.indexOf('.') + 1 + length, value.length];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value === '') {
|
||||
value = '0';
|
||||
cursor = [0, 1];
|
||||
}
|
||||
}
|
||||
while (/^0\d/.test(value)) {
|
||||
value = value.slice(1);
|
||||
}
|
||||
if (!regexp.test(value) || value < self.options.min || value > self.options.max) {
|
||||
value = oldValue;
|
||||
cursor = oldCursor;
|
||||
}
|
||||
autovalidateCallback({
|
||||
cursor: cursor,
|
||||
valid: true,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
function autovalidateCallback(data) {
|
||||
//Ox.Log('Form', 'autovalidateCallback', newValue, oldCursor)
|
||||
self.options.value = data.value;
|
||||
self.$input.val(self.options.value);
|
||||
!blur && cursor(
|
||||
data.cursor || (oldCursor[1] + data.value.length - oldValue.length)
|
||||
);
|
||||
that.triggerEvent('autovalidate', {
|
||||
valid: data.valid,
|
||||
value: data.value
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
function autovalidate(blur) {
|
||||
Ox.Log('Form', 'autovalidate', self.options.value, blur || false)
|
||||
self.autocorrectBlur = blur || false;
|
||||
self.autocorrectCursor = cursor();
|
||||
Ox.isFunction(self.options.autocorrect) ?
|
||||
(self.options.key ? self.options.autocorrect(
|
||||
self.options.key,
|
||||
self.options.value,
|
||||
self.autocorrectBlur,
|
||||
autocorrectCallback
|
||||
) : self.options.autocorrect(
|
||||
self.options.value,
|
||||
self.autocorrectBlur,
|
||||
autocorrectCallback
|
||||
)) : autocorrectCallback(autocorrect(self.options.value));
|
||||
}
|
||||
|
||||
function autovalidateFunction(value) {
|
||||
var length = value.length;
|
||||
return value.toLowerCase().split('').map(function(v) {
|
||||
if (new RegExp(self.options.autocorrect).test(v)) {
|
||||
return v;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}).join('');
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
function blur() {
|
||||
that.loseFocus();
|
||||
//that.removeClass('OxFocus');
|
||||
self.options.value = self.$input.val();
|
||||
self.options.autovalidate && autovalidate(true);
|
||||
self.options.placeholder && setPlaceholder();
|
||||
self.options.validate && validate();
|
||||
self.bindKeyboard && Ox.$document.off('keydown', keydown);
|
||||
if (!self.cancelled && !self.submitted) {
|
||||
that.triggerEvent('blur', {value: self.options.value});
|
||||
self.options.value !== self.originalValue && that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
self.cancelled = true;
|
||||
self.$input.val(self.originalValue).blur();
|
||||
self.cancelled = false;
|
||||
that.triggerEvent('cancel');
|
||||
}
|
||||
|
||||
function cancelAutocomplete() {
|
||||
self.autocompleteId = null;
|
||||
}
|
||||
|
||||
function change() {
|
||||
// change gets invoked before blur
|
||||
self.options.value = self.$input.val();
|
||||
self.originalValue = self.options.value;
|
||||
!self.options.changeOnKeypress && that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
|
||||
function clear() {
|
||||
// fixme: set to min, not zero
|
||||
// fixme: make this work for password
|
||||
if (!self.clearTimeout) {
|
||||
that.triggerEvent('clear');
|
||||
self.options.value = '';
|
||||
self.options.value = self.options.type == 'float' ? '0.0'
|
||||
: self.options.type == 'int' ? '0'
|
||||
: '';
|
||||
self.$input.val(self.options.value);
|
||||
cursor(0, self.options.value.length);
|
||||
self.options.changeOnKeypress && that.triggerEvent({
|
||||
change: {value: self.options.value}
|
||||
});
|
||||
self.clearTimeout = setTimeout(function() {
|
||||
self.clearTimeout = 0;
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
function clickArrow(i) {
|
||||
var originalValue = self.options.value;
|
||||
self.options.value = Ox.limit(
|
||||
parseFloat(self.options.value) + (i == 0 ? -1 : 1) * self.options.arrowStep,
|
||||
self.options.min,
|
||||
self.options.max
|
||||
).toString();
|
||||
if (self.options.value != originalValue) {
|
||||
self.$input.val(self.options.value);//.focus();
|
||||
that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
}
|
||||
|
||||
function clickMenu(data) {
|
||||
//Ox.Log('Form', 'clickMenu', data);
|
||||
self.options.value = data.title;
|
||||
self.$input.val(self.options.value).focus();
|
||||
that.gainFocus();
|
||||
self.options.autocompleteSelectSubmit && submit();
|
||||
}
|
||||
|
||||
function constructAutocompleteMenu() {
|
||||
return Ox.Menu({
|
||||
element: self.$input,
|
||||
id: self.options.id + 'Menu', // fixme: we do this in other places ... are we doing it the same way? var name?,
|
||||
maxWidth: self.options.autocompleteSelectMaxWidth,
|
||||
offset: {
|
||||
left: 4,
|
||||
top: 0
|
||||
}
|
||||
})
|
||||
.addClass('OxAutocompleteMenu OxKeyboardFocus')
|
||||
.bindEvent({
|
||||
click: clickMenu,
|
||||
key_enter: function() {
|
||||
if (self.$autocompleteMenu.is(':visible')) {
|
||||
self.$autocompleteMenu.hideMenu();
|
||||
submit();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cursor(start, end) {
|
||||
/*
|
||||
cursor() returns [start, end]
|
||||
cursor(start) sets start
|
||||
cursor([start, end]) sets start and end
|
||||
cursor(start, end) sets start and end
|
||||
*/
|
||||
var isArray = Ox.isArray(start);
|
||||
if (arguments.length == 0) {
|
||||
return [self.$input[0].selectionStart, self.$input[0].selectionEnd];
|
||||
} else {
|
||||
end = isArray ? start[1] : (end ? end : start);
|
||||
start = isArray ? start[0] : start;
|
||||
//IE8 does not have setSelectionRange
|
||||
self.$input[0].setSelectionRange && self.$input[0].setSelectionRange(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
function deselectMenu() {
|
||||
return;
|
||||
//Ox.Log('Form', 'deselectMenu')
|
||||
self.options.value = self.oldValue;
|
||||
self.$input.val(self.options.value);
|
||||
cursor(self.oldCursor);
|
||||
}
|
||||
|
||||
function focus() {
|
||||
if (
|
||||
// that.hasClass('OxFocus') || // fixme: this is just a workaround, since for some reason, focus() gets called twice on focus
|
||||
(self.$autocompleteMenu && self.$autocompleteMenu.is(':visible')) ||
|
||||
(self.hasPasswordPlaceholder && self.$input.is(':visible'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
self.originalValue = self.options.value;
|
||||
that.gainFocus();
|
||||
that.is('.OxError') && that.removeClass('OxError');
|
||||
self.options.placeholder && setPlaceholder();
|
||||
if (self.bindKeyboard) {
|
||||
// fixme: different in webkit and firefox (?), see keyboard handler, need generic function
|
||||
Ox.$document.keydown(keydown);
|
||||
//Ox.$document.keypress(keypress);
|
||||
// temporarily disabled autocomplete on focus
|
||||
//self.options.autocompleteSelect && setTimeout(autocomplete, 0); // fixme: why is the timeout needed?
|
||||
}
|
||||
that.triggerEvent('focus');
|
||||
}
|
||||
|
||||
function getInputWidth() {
|
||||
return self.options.width
|
||||
- (self.options.arrows ? 32 : 0)
|
||||
- (self.options.clear ? 16 : 0)
|
||||
- (self.options.label ? self.options.labelWidth : 0)
|
||||
- (Ox.contains(['rounded', 'squared'], self.options.style) ? 14 : 6);
|
||||
}
|
||||
|
||||
function insert() {
|
||||
var input = self.$input[0];
|
||||
that.triggerEvent('insert', {
|
||||
end: input.selectionEnd,
|
||||
id: that.oxid,
|
||||
selection: input.value.slice(input.selectionStart, input.selectionEnd),
|
||||
start: input.selectionStart,
|
||||
value: input.value
|
||||
});
|
||||
}
|
||||
|
||||
function keydown(event) {
|
||||
var oldCursor = cursor(),
|
||||
oldValue = self.options.value,
|
||||
newValue = oldValue.toString().slice(0, oldCursor[0] - 1),
|
||||
hasDeletedSelectedEnd = (event.keyCode == 8 || event.keyCode == 46)
|
||||
&& oldCursor[0] < oldCursor[1]
|
||||
&& oldCursor[1] == oldValue.length;
|
||||
if (
|
||||
event.keyCode != 9 // tab
|
||||
&& (self.options.type == 'textarea' || event.keyCode != 13) // enter
|
||||
&& event.keyCode != 27 // escape
|
||||
) { // fixme: can't 13 and 27 return false?
|
||||
setTimeout(function() { // wait for val to be set
|
||||
var value = self.$input.val();
|
||||
if ((self.options.autocompleteReplace || self.options.decimals) && hasDeletedSelectedEnd) {
|
||||
//Ox.Log('Form', 'HAS DELETED SELECTED END', event.keyCode)
|
||||
value = newValue;
|
||||
self.$input.val(value);
|
||||
}
|
||||
if (value != self.options.value) {
|
||||
self.options.value = value;
|
||||
Ox.Log('AUTO', 'call autocomplete from keydown')
|
||||
self.options.autocomplete && autocomplete(oldValue, oldCursor);
|
||||
self.options.autovalidate && autovalidate(oldValue, oldCursor);
|
||||
self.options.changeOnKeypress && that.triggerEvent({
|
||||
change: {value: self.options.value}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (
|
||||
(event.keyCode == 38 || event.keyCode == 40) // up/down
|
||||
&& self.options.autocompleteSelect
|
||||
&& self.$autocompleteMenu.is(':visible')
|
||||
) {
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
|
||||
function paste() {
|
||||
// fixme: unused
|
||||
var data = Ox.Clipboard.paste();
|
||||
data.text && self.$input.val(data.text);
|
||||
}
|
||||
|
||||
function selectMenu(data) {
|
||||
var pos = cursor();
|
||||
//if (self.options.value) {
|
||||
//Ox.Log('Form', 'selectMenu', pos, data.title)
|
||||
self.options.value = Ox.decodeHTMLEntities(data.title);
|
||||
self.$input.val(self.options.value);
|
||||
cursor(pos[0], self.options.value.length);
|
||||
self.options.changeOnKeypress && that.triggerEvent({
|
||||
change: {value: self.options.value}
|
||||
});
|
||||
//}
|
||||
}
|
||||
|
||||
function setPlaceholder() {
|
||||
if (self.options.placeholder) {
|
||||
if (that.hasClass('OxFocus')) {
|
||||
if (self.options.value === '') {
|
||||
if (self.options.type == 'password') {
|
||||
self.$placeholder.hide();
|
||||
self.$input.show().focus();
|
||||
} else {
|
||||
self.$input
|
||||
.removeClass('OxPlaceholder')
|
||||
.val('');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (self.options.value === '') {
|
||||
if (self.options.type == 'password') {
|
||||
self.$input.hide();
|
||||
self.$placeholder.show();
|
||||
} else {
|
||||
self.$input
|
||||
.addClass('OxPlaceholder')
|
||||
.val(self.options.placeholder)
|
||||
}
|
||||
} else {
|
||||
self.$input
|
||||
.removeClass('OxPlaceholder')
|
||||
.val(self.options.value)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.$input
|
||||
.removeClass('OxPlaceholder')
|
||||
.val(self.options.value);
|
||||
}
|
||||
}
|
||||
|
||||
function setWidth() {
|
||||
|
||||
}
|
||||
|
||||
function submit() {
|
||||
cancelAutocomplete();
|
||||
self.submitted = true;
|
||||
self.$input.blur();
|
||||
self.submitted = false;
|
||||
//self.options.type == 'textarea' && self.$input.blur();
|
||||
that.triggerEvent('submit', {value: self.options.value});
|
||||
}
|
||||
|
||||
function validate() {
|
||||
self.options.validate(self.options.value, function(data) {
|
||||
that.triggerEvent('validate', data);
|
||||
});
|
||||
}
|
||||
|
||||
/*@
|
||||
blurInput <f> blurInput
|
||||
@*/
|
||||
that.blurInput = function() {
|
||||
self.$input.blur();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
clearInput <f> clearInput
|
||||
@*/
|
||||
that.clearInput = function() {
|
||||
clear();
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
focusInput <f> Focus input element
|
||||
(select) -> <o> Input object
|
||||
(start, end) -> <o> Input object
|
||||
select <b|false> If true, select all, otherwise position cursor at the end
|
||||
start <n> Selection start (can be negative)
|
||||
end <n> Selection end (can be negative), or equal to start if omitted
|
||||
@*/
|
||||
that.focusInput = function() {
|
||||
var length = self.$input.val().length,
|
||||
start = Ox.isNumber(arguments[0])
|
||||
? (arguments[0] < 0 ? length + arguments[0] : arguments[0])
|
||||
: arguments[0] ? 0 : length,
|
||||
stop = Ox.isNumber(arguments[1])
|
||||
? (arguments[1] < 0 ? length + arguments[1] : arguments[1])
|
||||
: Ox.isNumber(arguments[0]) ? arguments[0]
|
||||
: arguments[0] ? length : 0;
|
||||
self.$input.focus();
|
||||
cursor(start, stop);
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
value <f> get/set value
|
||||
@*/
|
||||
// FIXME: deprecate, options are enough
|
||||
that.value = function() {
|
||||
if (arguments.length == 0) {
|
||||
var value = self.$input.hasClass('OxPlaceholder') ? '' : self.$input.val();
|
||||
if (self.options.type == 'float') {
|
||||
value = parseFloat(value);
|
||||
} else if (self.options.type == 'int') {
|
||||
value = parseInt(value); // cannot have leading zero
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
return that.options({value: arguments[0]});
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
161
source/UI/js/Form/InputGroup.js
Normal file
161
source/UI/js/Form/InputGroup.js
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.InputGroup <f> InputGroup Object
|
||||
options <o> Options object
|
||||
id <s|''> id
|
||||
inputs <a|[]> inputs
|
||||
separators <a|[]> seperators
|
||||
width <n|0> width
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> InputGroup Object
|
||||
change <!> change
|
||||
validate <!> validate
|
||||
@*/
|
||||
|
||||
Ox.InputGroup = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
id: '',
|
||||
inputs: [],
|
||||
join: null,
|
||||
separators: [],
|
||||
split: null,
|
||||
value: options.split ? '' : [],
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
value: setValue
|
||||
})
|
||||
.addClass('OxInputGroup')
|
||||
.on({click: click});
|
||||
|
||||
if (Ox.isEmpty(self.options.value)) {
|
||||
self.options.value = getValue();
|
||||
} else {
|
||||
setValue();
|
||||
}
|
||||
|
||||
if (self.options.width) {
|
||||
setWidths();
|
||||
} else {
|
||||
self.options.width = getWidth();
|
||||
}
|
||||
that.css({
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
|
||||
self.$separator = [];
|
||||
|
||||
self.options.separators.forEach(function(v, i) {
|
||||
self.$separator[i] = Ox.Label({
|
||||
textAlign: 'center',
|
||||
title: v.title,
|
||||
width: v.width + 32
|
||||
})
|
||||
.addClass('OxSeparator')
|
||||
.css({
|
||||
marginLeft: (self.options.inputs[i].options('width') - (i == 0 ? 16 : 32)) + 'px'
|
||||
})
|
||||
.appendTo(that);
|
||||
});
|
||||
|
||||
self.options.inputs.forEach(function($input, i) {
|
||||
$input.options({
|
||||
id: self.options.id + Ox.toTitleCase($input.options('id') || '')
|
||||
})
|
||||
.css({
|
||||
marginLeft: -Ox.sum(self.options.inputs.map(function($input, i_) {
|
||||
return i_ > i
|
||||
? self.options.inputs[i_ - 1].options('width')
|
||||
+ self.options.separators[i_ - 1].width
|
||||
: i_ == i ? 16
|
||||
: 0;
|
||||
})) + 'px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: change,
|
||||
submit: change,
|
||||
validate: validate
|
||||
})
|
||||
.appendTo(that);
|
||||
});
|
||||
|
||||
function change(data) {
|
||||
self.options.value = getValue();
|
||||
that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
|
||||
function click(event) {
|
||||
if ($(event.target).hasClass('OxSeparator')) {
|
||||
Ox.forEach(self.options.inputs, function($input) {
|
||||
if ($input.focusInput) {
|
||||
$input.focusInput(true);
|
||||
return false; // break
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getValue() {
|
||||
var value = self.options.inputs.map(function($input) {
|
||||
return $input.value();
|
||||
});
|
||||
return self.options.join ? self.options.join(value) : value;
|
||||
}
|
||||
|
||||
function getWidth() {
|
||||
return Ox.sum(self.options.inputs.map(function(v) {
|
||||
return v.options('width');
|
||||
})) + Ox.sum(self.options.separators.map(function(v) {
|
||||
return v.width;
|
||||
}));
|
||||
}
|
||||
|
||||
function setValue() {
|
||||
var values = self.options.split
|
||||
? self.options.split(self.options.value)
|
||||
: self.options.value;
|
||||
values.forEach(function(value, i) {
|
||||
self.options.inputs[i].value(value);
|
||||
});
|
||||
}
|
||||
|
||||
function setWidths() {
|
||||
var length = self.options.inputs.length,
|
||||
inputWidths = Ox.splitInt(
|
||||
self.options.width - Ox.sum(self.options.separators.map(function(v) {
|
||||
return v.width;
|
||||
})), length
|
||||
);
|
||||
self.options.inputs.forEach(function(v) {
|
||||
v.options({
|
||||
width: inputWidths[1] // fixme: 1?? i?
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function validate(data) {
|
||||
that.triggerEvent('validate', data);
|
||||
}
|
||||
|
||||
// fixme: is this used?
|
||||
that.getInputById = function(id) {
|
||||
var input = null;
|
||||
Ox.forEach(self.options.inputs, function(v, i) {
|
||||
if (v.options('id') == self.options.id + Ox.toTitleCase(id)) {
|
||||
input = v;
|
||||
return false; // break
|
||||
}
|
||||
});
|
||||
return input;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
222
source/UI/js/Form/InsertHTMLDialog.js
Normal file
222
source/UI/js/Form/InsertHTMLDialog.js
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.InsertHTMLDialog <f> Insert HTML Dialog
|
||||
options <o> Options
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Dialog> Insert HTML Dialog
|
||||
@*/
|
||||
Ox.InsertHTMLDialog = function(options, self) {
|
||||
|
||||
var that;
|
||||
|
||||
self = self || {};
|
||||
self.options = Ox.extend({
|
||||
callback: void 0,
|
||||
end: 0,
|
||||
selection: '',
|
||||
start: 0
|
||||
}, options || {});
|
||||
|
||||
self.type = self.options.selection.indexOf('\n') > -1
|
||||
? 'textarea' : 'input';
|
||||
|
||||
self.items = [
|
||||
{id: 'img', title: Ox._('Image')},
|
||||
{id: 'a', title: Ox._('Link')},
|
||||
{id: 'li', title: Ox._('List')},
|
||||
{},
|
||||
{id: 'blockquote', title: Ox._('Blockquote')},
|
||||
{id: 'h1', title: Ox._('Headline')},
|
||||
{id: 'p', title: Ox._('Paragraph')},
|
||||
{id: 'div', title: Ox._('Right-to-Left')},
|
||||
{},
|
||||
{id: 'b', title: Ox._('Bold')},
|
||||
{id: 'i', title: Ox._('Italic')},
|
||||
{id: 'code', title: Ox._('Monospace')},
|
||||
{id: 's', title: Ox._('Strike')},
|
||||
{id: 'sub', title: Ox._('Subscript')},
|
||||
{id: 'sup', title: Ox._('Superscript')},
|
||||
{id: 'u', title: Ox._('Underline')},
|
||||
{},
|
||||
{id: 'br', title: Ox._('Linebreak')}
|
||||
].map(function(item, i) {
|
||||
var form, format;
|
||||
if (item.id == 'img') {
|
||||
form = [
|
||||
Ox.Input({
|
||||
id: 'url',
|
||||
label: 'URL',
|
||||
labelWidth: 128,
|
||||
width: 384
|
||||
})
|
||||
];
|
||||
format = function(values) {
|
||||
return '<img src="' + values.url + '"/>';
|
||||
};
|
||||
} else if (item.id == 'a') {
|
||||
form = [
|
||||
Ox.Input({
|
||||
height: 104,
|
||||
id: 'text',
|
||||
label: 'Text',
|
||||
labelWidth: 128,
|
||||
type: self.type,
|
||||
width: 384,
|
||||
value: self.options.selection
|
||||
})
|
||||
.css({background: 'transparent'}),
|
||||
Ox.Input({
|
||||
id: 'url',
|
||||
label: 'URL',
|
||||
labelWidth: 128,
|
||||
width: 384
|
||||
})
|
||||
];
|
||||
format = function(values) {
|
||||
return '<a href="' + values.url + '">' + values.text + '</a>';
|
||||
};
|
||||
} else if (item.id == 'li') {
|
||||
form = [
|
||||
Ox.Select({
|
||||
id: 'style',
|
||||
items: [
|
||||
{id: 'ul', title: Ox._('Bullets')},
|
||||
{id: 'ol', title: Ox._('Numbers')}
|
||||
],
|
||||
label: 'Style',
|
||||
labelWidth: 128,
|
||||
width: 384
|
||||
}),
|
||||
Ox.ArrayInput({
|
||||
id: 'items',
|
||||
label: 'Items',
|
||||
max: 10,
|
||||
value: self.options.selection.split('\n'),
|
||||
width: 384
|
||||
})
|
||||
];
|
||||
format = function(values) {
|
||||
return '<' + values.style + '>\n' + values.items.map(function(value) {
|
||||
return '<li>' + value + '</li>\n';
|
||||
}).join('') + '</' + values.style + '>';
|
||||
};
|
||||
} else if (['p', 'blockquote', 'div'].indexOf(item.id) > -1) {
|
||||
form = [
|
||||
Ox.Input({
|
||||
height: 128,
|
||||
id: 'text',
|
||||
label: 'Text',
|
||||
labelWidth: 128,
|
||||
type: 'textarea',
|
||||
value: self.options.selection,
|
||||
width: 384
|
||||
})
|
||||
.css({background: 'transparent'})
|
||||
];
|
||||
format = function(values) {
|
||||
return '<' + item.id + (
|
||||
item.id == 'div' ? ' style="direction: rtl"' : ''
|
||||
) + '>' + values.text + '</' + item.id + '>';
|
||||
};
|
||||
} else if (['h1', 'b', 'i', 'code', 's', 'sub', 'sup', 'u'].indexOf(item.id) > -1) {
|
||||
form = [
|
||||
Ox.Input({
|
||||
height: 128,
|
||||
id: 'text',
|
||||
label: 'Text',
|
||||
labelWidth: 128,
|
||||
type: self.type,
|
||||
value: self.options.selection,
|
||||
width: 384
|
||||
})
|
||||
.css({background: 'transparent'})
|
||||
];
|
||||
format = function(values) {
|
||||
return '<' + item.id + '>' + values.text + '</' + item.id + '>';
|
||||
};
|
||||
} else if (item.id == 'br') {
|
||||
form = [];
|
||||
format = function() {
|
||||
return '<br/>';
|
||||
};
|
||||
}
|
||||
return item.id ? Ox.extend(item, {
|
||||
form: form,
|
||||
format: format
|
||||
}) : item;
|
||||
});
|
||||
|
||||
self.$content = $('<div>')
|
||||
.css({padding: '16px'});
|
||||
|
||||
self.$select = Ox.Select({
|
||||
items: self.items,
|
||||
label: Ox._('Insert'),
|
||||
labelWidth: 128,
|
||||
value: 'img',
|
||||
width: 384
|
||||
})
|
||||
.bindEvent({
|
||||
change: renderForm
|
||||
})
|
||||
.appendTo(self.$content);
|
||||
|
||||
renderForm();
|
||||
|
||||
that = Ox.Dialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
id: 'cancel',
|
||||
title: Ox._('Cancel'),
|
||||
width: 64
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
}
|
||||
}),
|
||||
Ox.Button({
|
||||
id: 'insert',
|
||||
title: Ox._('Insert'),
|
||||
width: 64
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
var item = Ox.getObjectById(self.items, self.$select.value()),
|
||||
value = item.format(
|
||||
item.form.length ? self.$form.values() : void 0
|
||||
);
|
||||
self.options.callback({
|
||||
position: self.options.start + value.length,
|
||||
value: self.options.value.slice(0, self.options.start)
|
||||
+ value
|
||||
+ self.options.value.slice(self.options.end)
|
||||
});
|
||||
that.close();
|
||||
}
|
||||
})
|
||||
],
|
||||
closeButton: true,
|
||||
content: self.$content,
|
||||
height: 184,
|
||||
keys: {enter: 'insert', escape: 'cancel'},
|
||||
title: Ox._('Insert HTML'),
|
||||
width: 416 + Ox.UI.SCROLLBAR_SIZE
|
||||
});
|
||||
|
||||
function renderForm() {
|
||||
var items = Ox.getObjectById(self.items, self.$select.value()).form;
|
||||
self.$form && self.$form.remove();
|
||||
if (items.length) {
|
||||
self.$form = Ox.Form({
|
||||
items: items
|
||||
})
|
||||
.css({paddingTop: '8px'})
|
||||
.appendTo(self.$content);
|
||||
}
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
54
source/UI/js/Form/Label.js
Normal file
54
source/UI/js/Form/Label.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Label <f> Label element
|
||||
options <o|u> Options object
|
||||
self <o|u> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Label element
|
||||
@*/
|
||||
Ox.Label = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
disabled: false,
|
||||
id: '',
|
||||
overlap: 'none',
|
||||
textAlign: 'left',
|
||||
style: 'rounded',
|
||||
title: '',
|
||||
width: 'auto'
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
title: function() {
|
||||
that.html(self.options.title);
|
||||
},
|
||||
width: function() {
|
||||
that.css({
|
||||
width: self.options.width - (
|
||||
self.options.style == 'rounded' ? 14 : 8
|
||||
) + 'px'
|
||||
});
|
||||
}
|
||||
})
|
||||
.addClass(
|
||||
'OxLabel Ox' + Ox.toTitleCase(self.options.style)
|
||||
+ (self.options.disabled ? ' OxDisabled' : '')
|
||||
+ (
|
||||
self.options.overlap != 'none'
|
||||
? ' OxOverlap' + Ox.toTitleCase(self.options.overlap) : ''
|
||||
)
|
||||
)
|
||||
.css(Ox.extend(self.options.width == 'auto' ? {} : {
|
||||
width: self.options.width - (
|
||||
self.options.style == 'rounded' ? 14 : 8
|
||||
) + 'px'
|
||||
}, {
|
||||
textAlign: self.options.textAlign
|
||||
}))
|
||||
.html(Ox.isUndefined(self.options.title) ? '' : self.options.title);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
172
source/UI/js/Form/ObjectArrayInput.js
Normal file
172
source/UI/js/Form/ObjectArrayInput.js
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.ObjectArrayInput <f> Object Array Input
|
||||
options <o> Options
|
||||
buttonTitles
|
||||
inputs
|
||||
labelWidth
|
||||
max
|
||||
value
|
||||
width
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Object Array Input
|
||||
change <!> change
|
||||
@*/
|
||||
Ox.ObjectArrayInput = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
buttonTitles: {add: Ox._('Add'), remove: Ox._('Remove')},
|
||||
inputs: [],
|
||||
labelWidth: 128,
|
||||
max: 0,
|
||||
value: [],
|
||||
width: 256
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
value: function() {
|
||||
setValue(self.options.value);
|
||||
}
|
||||
})
|
||||
.addClass('OxObjectArrayInput');
|
||||
|
||||
if (Ox.isEmpty(self.options.value)) {
|
||||
self.options.value = [getDefaultValue()];
|
||||
}
|
||||
|
||||
self.$element = [];
|
||||
self.$input = [];
|
||||
self.$removeButton = [];
|
||||
self.$addButton = [];
|
||||
self.buttonWidth = self.options.width / 2 - 4;
|
||||
|
||||
setValue(self.options.value);
|
||||
|
||||
function addInput(index, value) {
|
||||
self.$element.splice(index, 0, Ox.Element()
|
||||
.css({
|
||||
width: self.options.width + 'px',
|
||||
})
|
||||
);
|
||||
if (index == 0) {
|
||||
self.$element[index].appendTo(that);
|
||||
} else {
|
||||
self.$element[index].insertAfter(self.$element[index - 1]);
|
||||
}
|
||||
self.$input.splice(index, 0, Ox.ObjectInput({
|
||||
elements: self.options.inputs.map(function(input) {
|
||||
return Ox[input.element](input.options || {})
|
||||
.bindEvent(input.events || {});
|
||||
}),
|
||||
labelWidth: self.options.labelWidth,
|
||||
value: value,
|
||||
width: self.options.width
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
var index = $(this).parent().data('index');
|
||||
self.options.value[index] = self.$input[index].value();
|
||||
that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
})
|
||||
.appendTo(self.$element[index])
|
||||
);
|
||||
self.$removeButton.splice(index, 0, Ox.Button({
|
||||
disabled: self.$input.length == 1,
|
||||
title: self.options.buttonTitles.remove,
|
||||
width: self.buttonWidth
|
||||
})
|
||||
.css({margin: '8px 4px 0 0'})
|
||||
.on({
|
||||
click: function() {
|
||||
var index = $(this).parent().data('index');
|
||||
if (self.$input.length > 1) {
|
||||
removeInput(index);
|
||||
self.options.value = getValue();
|
||||
that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.appendTo(self.$element[index])
|
||||
);
|
||||
self.$addButton.splice(index, 0, Ox.Button({
|
||||
disabled: index == self.options.max - 1,
|
||||
title: self.options.buttonTitles.add,
|
||||
width: self.buttonWidth
|
||||
})
|
||||
.css({margin: '8px 0 0 4px'})
|
||||
.on({
|
||||
click: function() {
|
||||
var index = $(this).parent().data('index');
|
||||
addInput(index + 1, getDefaultValue());
|
||||
self.options.value = getValue();
|
||||
that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
})
|
||||
.appendTo(self.$element[index])
|
||||
);
|
||||
updateInputs();
|
||||
}
|
||||
|
||||
function getDefaultValue() {
|
||||
var value = {};
|
||||
self.options.inputs.forEach(function(input) {
|
||||
value[input.options.id] = '';
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
function getValue() {
|
||||
return self.$input.map(function($input) {
|
||||
return $input.value();
|
||||
});
|
||||
}
|
||||
|
||||
function removeInput(index) {
|
||||
[
|
||||
'input', 'removeButton', 'addButton', 'element'
|
||||
].forEach(function(element) {
|
||||
var key = '$' + element;
|
||||
self[key][index].remove();
|
||||
self[key].splice(index, 1);
|
||||
});
|
||||
updateInputs();
|
||||
}
|
||||
|
||||
function setValue(value) {
|
||||
while (self.$element.length) {
|
||||
removeInput(0);
|
||||
}
|
||||
value.forEach(function(value, i) {
|
||||
addInput(i, value);
|
||||
});
|
||||
}
|
||||
|
||||
function updateInputs() {
|
||||
var length = self.$element.length;
|
||||
self.$element.forEach(function($element, i) {
|
||||
$element
|
||||
[i == 0 ? 'addClass' : 'removeClass']('OxFirst')
|
||||
[i == length - 1 ? 'addClass' : 'removeClass']('OxLast')
|
||||
.data({index: i});
|
||||
self.$removeButton[i].options({
|
||||
disabled: length == 1
|
||||
});
|
||||
self.$addButton[i].options({
|
||||
disabled: length == self.options.max
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
69
source/UI/js/Form/ObjectInput.js
Normal file
69
source/UI/js/Form/ObjectInput.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.ObjectInput <f> Object Input
|
||||
options <o> Options
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Object Input
|
||||
change <!> change
|
||||
@*/
|
||||
Ox.ObjectInput = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
elements: [],
|
||||
labelWidth: 128,
|
||||
value: {},
|
||||
width: 256
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
value: function() {
|
||||
setValue(self.options.value);
|
||||
}
|
||||
})
|
||||
.addClass('OxObjectInput')
|
||||
.css({
|
||||
width: self.options.width + 'px',
|
||||
});
|
||||
|
||||
if (Ox.isEmpty(self.options.value)) {
|
||||
self.options.value = getValue();
|
||||
} else {
|
||||
setValue(self.options.value);
|
||||
}
|
||||
|
||||
self.options.elements.forEach(function($element) {
|
||||
$element.options({
|
||||
labelWidth: self.options.labelWidth,
|
||||
width: self.options.width
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
self.options.value = getValue();
|
||||
that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
});
|
||||
|
||||
function getValue() {
|
||||
var value = {};
|
||||
self.options.elements.forEach(function(element) {
|
||||
value[element.options('id')] = element.value();
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
function setValue(value) {
|
||||
self.options.elements.forEach(function(element) {
|
||||
element.value(value[element.options('id')]);
|
||||
});
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
124
source/UI/js/Form/OptionGroup.js
Normal file
124
source/UI/js/Form/OptionGroup.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.OptionGroup <f> OptionGroup
|
||||
Helper object, used by ButtonGroup, CheckboxGroup, Select and Menu
|
||||
(items, min, max, property) -> <f> OptionGroup
|
||||
items <a> array of items
|
||||
min <n> minimum number of selected items
|
||||
max <n> maximum number of selected items
|
||||
property <s|'checked'> property to check
|
||||
@*/
|
||||
|
||||
// FIXME: Should be moved to Ox.js
|
||||
|
||||
Ox.OptionGroup = function(items, min, max, property) {
|
||||
|
||||
var length = items.length;
|
||||
property = property || 'checked';
|
||||
max = max == -1 ? length : max;
|
||||
|
||||
function getLastBefore(pos) {
|
||||
// returns the position of the last checked item before position pos
|
||||
var last = -1;
|
||||
// fixme: why is length not == items.length here?
|
||||
Ox.forEach([].concat(
|
||||
pos > 0 ? Ox.range(pos - 1, -1, -1) : [],
|
||||
pos < items.length - 1 ? Ox.range(items.length - 1, pos, -1) : []
|
||||
), function(v) {
|
||||
if (items[v][property]) {
|
||||
last = v;
|
||||
return false; // break
|
||||
}
|
||||
});
|
||||
return last;
|
||||
}
|
||||
|
||||
function getNumber() {
|
||||
// returns the number of checked items
|
||||
return items.reduce(function(prev, curr) {
|
||||
return prev + !!curr[property];
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/*@
|
||||
[property] <f> returns an array with the positions of all checked items
|
||||
() -> <a> positions of checked items
|
||||
@*/
|
||||
// FIXME: isn't value more useful in all cases?
|
||||
this[property] = function() {
|
||||
return Ox.indicesOf(items, function(item) {
|
||||
return item[property];
|
||||
});
|
||||
};
|
||||
|
||||
/*@
|
||||
init <f> init group
|
||||
() -> <a> returns items
|
||||
@*/
|
||||
this.init = function() {
|
||||
var num = getNumber(),
|
||||
count = 0;
|
||||
//if (num < min || num > max) {
|
||||
items.forEach(function(item) {
|
||||
if (Ox.isUndefined(item[property])) {
|
||||
item[property] = false;
|
||||
}
|
||||
if (item[property]) {
|
||||
count++;
|
||||
if (count > max) {
|
||||
item[property] = false;
|
||||
}
|
||||
} else {
|
||||
if (num < min) {
|
||||
item[property] = true;
|
||||
num++;
|
||||
}
|
||||
}
|
||||
});
|
||||
//}
|
||||
return items;
|
||||
};
|
||||
|
||||
/*@
|
||||
toggle <f> toggle options
|
||||
(pos) -> <a> returns toggled state
|
||||
@*/
|
||||
this.toggle = function(pos) {
|
||||
var last,
|
||||
num = getNumber(),
|
||||
toggled = [];
|
||||
if (!items[pos][property]) { // check
|
||||
if (num >= max) {
|
||||
last = getLastBefore(pos);
|
||||
items[last][property] = false;
|
||||
toggled.push(last);
|
||||
}
|
||||
if (!items[pos][property]) {
|
||||
items[pos][property] = true;
|
||||
toggled.push(pos);
|
||||
}
|
||||
} else { // uncheck
|
||||
if (num > min) {
|
||||
items[pos][property] = false;
|
||||
toggled.push(pos);
|
||||
}
|
||||
}
|
||||
return toggled;
|
||||
};
|
||||
|
||||
/*@
|
||||
value <f> get value
|
||||
@*/
|
||||
this.value = function() {
|
||||
var value = items.filter(function(item) {
|
||||
return item[property];
|
||||
}).map(function(item) {
|
||||
return item.id;
|
||||
});
|
||||
return max == 1 ? (value.length ? value[0] : '') : value;
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
110
source/UI/js/Form/Picker.js
Normal file
110
source/UI/js/Form/Picker.js
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Picker <f> Picker Object
|
||||
options <o> Options object
|
||||
element <o|null> picker element
|
||||
elementHeight <n|128> height
|
||||
elemementWidth <n|256> width
|
||||
id <s> picker id
|
||||
overlap <s|none> select button overlap value
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Picker Object
|
||||
show <!> picker is shown
|
||||
hide <!> picker is hidden
|
||||
@*/
|
||||
|
||||
Ox.Picker = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
element: null,
|
||||
elementHeight: 128,
|
||||
elementWidth: 256,
|
||||
overlap: 'right', // 'none'
|
||||
})
|
||||
.options(options || {});
|
||||
|
||||
self.$selectButton = Ox.Button({
|
||||
overlap: self.options.overlap,
|
||||
title: 'select',
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: showMenu
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
self.$menu = Ox.Element()
|
||||
.addClass('OxPicker')
|
||||
.css({
|
||||
width: self.options.elementWidth + 'px',
|
||||
height: (self.options.elementHeight + 24) + 'px'
|
||||
});
|
||||
|
||||
self.options.element.css({
|
||||
width: self.options.elementWidth + 'px',
|
||||
height: self.options.elementHeight + 'px'
|
||||
})
|
||||
.appendTo(self.$menu);
|
||||
|
||||
self.$bar = Ox.Bar({
|
||||
orientation: 'horizontal',
|
||||
size: 24
|
||||
})
|
||||
.appendTo(self.$menu);
|
||||
|
||||
that.$label = Ox.Label({
|
||||
width: self.options.elementWidth - 60
|
||||
})
|
||||
.appendTo(self.$bar);
|
||||
|
||||
self.$doneButton = Ox.Button({
|
||||
title: Ox._('Done'),
|
||||
width: 48
|
||||
})
|
||||
.bindEvent({
|
||||
click: hideMenu
|
||||
})
|
||||
.appendTo(self.$bar);
|
||||
|
||||
self.$layer = Ox.$('<div>')
|
||||
.addClass('OxLayer')
|
||||
.on({
|
||||
click: hideMenu
|
||||
});
|
||||
|
||||
function hideMenu() {
|
||||
self.$menu.detach();
|
||||
self.$layer.detach();
|
||||
self.$selectButton
|
||||
.removeClass('OxSelected')
|
||||
.css({
|
||||
borderRadius: '8px'
|
||||
});
|
||||
that.triggerEvent('hide');
|
||||
}
|
||||
|
||||
function showMenu() {
|
||||
var offset = that.offset(),
|
||||
left = offset.left,
|
||||
top = offset.top + 15;
|
||||
self.$selectButton
|
||||
.addClass('OxSelected')
|
||||
.css({
|
||||
borderRadius: '8px 8px 0 0'
|
||||
});
|
||||
self.$layer.appendTo(Ox.$body);
|
||||
self.$menu
|
||||
.css({
|
||||
left: left + 'px',
|
||||
top: top + 'px'
|
||||
})
|
||||
.appendTo(Ox.$body);
|
||||
that.triggerEvent('show');
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
44
source/UI/js/Form/PlaceInput.js
Normal file
44
source/UI/js/Form/PlaceInput.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.PlaceInput <f> PlaceInput Object
|
||||
options <o> Options object
|
||||
id <s> element id
|
||||
value <s|United States> default value of place input
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.FormElementGroup> PlaceInput Object
|
||||
@*/
|
||||
|
||||
Ox.PlaceInput = function(options, self) {
|
||||
|
||||
var that;
|
||||
self = Ox.extend(self || {}, {
|
||||
options: Ox.extend({
|
||||
id: '',
|
||||
value: 'United States'
|
||||
}, options)
|
||||
});
|
||||
that = Ox.FormElementGroup({
|
||||
id: self.options.id,
|
||||
elements: [
|
||||
Ox.Input({
|
||||
id: 'input',
|
||||
value: self.options.value
|
||||
}),
|
||||
Ox.PlacePicker({
|
||||
id: 'picker',
|
||||
overlap: 'left',
|
||||
value: self.options.value
|
||||
})
|
||||
],
|
||||
float: 'right'
|
||||
}, self)
|
||||
.bindEvent('change', change);
|
||||
|
||||
function change() {
|
||||
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
155
source/UI/js/Form/PlacePicker.js
Normal file
155
source/UI/js/Form/PlacePicker.js
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.PlacePicker <f> PlacePicker Object
|
||||
options <o> Options object
|
||||
id <s> element id
|
||||
value <s|United States> default value of place input
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Picker> PlacePicker Object
|
||||
@*/
|
||||
|
||||
Ox.PlacePicker = function(options, self) {
|
||||
|
||||
var that;
|
||||
self = Ox.extend(self || {}, {
|
||||
options: Ox.extend({
|
||||
id: '',
|
||||
value: 'United States'
|
||||
}, options)
|
||||
});
|
||||
|
||||
self.$element = Ox.Element()
|
||||
.css({
|
||||
width: '256px',
|
||||
height: '192px'
|
||||
})
|
||||
.append(
|
||||
self.$topBar = Ox.Bar({
|
||||
size: 16
|
||||
})
|
||||
.css({
|
||||
MozBorderRadius: '0 8px 0 0',
|
||||
WebkitBorderRadius: '0 8px 0 0'
|
||||
})
|
||||
.append(
|
||||
self.$input = Ox.Input({
|
||||
clear: true,
|
||||
id: self.options.id + 'Input',
|
||||
placeholder: Ox._('Find'),
|
||||
width: 256
|
||||
})
|
||||
.bindEvent('submit', findPlace)
|
||||
)
|
||||
)
|
||||
.append(
|
||||
self.$container = Ox.Element()
|
||||
.css({
|
||||
width: '256px',
|
||||
height: '160px'
|
||||
})
|
||||
)
|
||||
.append(
|
||||
self.$bottomBar = Ox.Bar({
|
||||
size: 16
|
||||
})
|
||||
.append(
|
||||
self.$range = Ox.Range({
|
||||
arrows: true,
|
||||
changeOnDrag: true,
|
||||
id: self.options.id + 'Range',
|
||||
max: 22,
|
||||
size: 256,
|
||||
thumbSize: 32,
|
||||
thumbValue: true
|
||||
})
|
||||
.bindEvent('change', changeZoom)
|
||||
)
|
||||
);
|
||||
|
||||
self.$input.children('input[type=text]').css({
|
||||
width: '230px',
|
||||
paddingLeft: '2px',
|
||||
MozBorderRadius: '0 8px 8px 0',
|
||||
WebkitBorderRadius: '0 8px 8px 0'
|
||||
});
|
||||
self.$input.children('input[type=image]').css({
|
||||
MozBorderRadius: '0 8px 0 0',
|
||||
WebkitBorderRadius: '0 8px 0 0'
|
||||
});
|
||||
self.$range.children('input').css({
|
||||
MozBorderRadius: 0,
|
||||
WebkitBorderRadius: 0
|
||||
});
|
||||
|
||||
that = Ox.Picker({
|
||||
element: self.$element,
|
||||
elementHeight: 192,
|
||||
elementWidth: 256,
|
||||
id: self.options.id,
|
||||
overlap: self.options.overlap,
|
||||
value: self.options.value
|
||||
}, self)
|
||||
.bindEvent('show', showPicker);
|
||||
|
||||
that.$label.on('click', clickLabel);
|
||||
|
||||
self.map = false;
|
||||
|
||||
function changeZoom(data) {
|
||||
//Ox.Log('Form', 'changeZoom')
|
||||
self.$map.zoom(data.value);
|
||||
}
|
||||
|
||||
function clickLabel() {
|
||||
var name = that.$label.html();
|
||||
if (name) {
|
||||
self.$input
|
||||
.value(name)
|
||||
.triggerEvent('submit', {
|
||||
value: name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function findPlace(data) {
|
||||
//Ox.Log('Form', 'findPlace', data);
|
||||
self.$map.find(data.value, function(place) {
|
||||
place && that.$label.html(place.geoname);
|
||||
});
|
||||
}
|
||||
|
||||
function onSelect(data) {
|
||||
that.$label.html(data.geoname);
|
||||
}
|
||||
|
||||
function onZoom(data) {
|
||||
self.$range.value(data.value);
|
||||
}
|
||||
|
||||
function showPicker() {
|
||||
if (!self.map) {
|
||||
self.$map = Ox.Map({
|
||||
clickable: true,
|
||||
id: self.options.id + 'Map',
|
||||
// fixme: this is retarded, allow for map without places
|
||||
places: [{south: -85, west: -179, north: -85, east: 179}]
|
||||
//places: [self.options.value]
|
||||
})
|
||||
.css({
|
||||
top: '16px',
|
||||
width: '256px',
|
||||
height: '160px'
|
||||
})
|
||||
.bindEvent({
|
||||
select: onSelect,
|
||||
zoom: onZoom
|
||||
})
|
||||
.appendTo(self.$container);
|
||||
self.map = true;
|
||||
}
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
295
source/UI/js/Form/Range.js
Normal file
295
source/UI/js/Form/Range.js
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Range <f> Range Object
|
||||
options <o> Options object
|
||||
arrows <b> if true, show arrows
|
||||
arrowStep <n> step when clicking arrows
|
||||
arrowSymbols <[s]> arrow symbols, like ['minus', 'plus']
|
||||
arrowTooltips <[s]> arrow tooltips
|
||||
max <n> maximum value
|
||||
min <n> minimum value
|
||||
orientation <s> 'horizontal' or 'vertical'
|
||||
step <n> step between values
|
||||
size <n> width or height, in px
|
||||
thumbSize <n> minimum width or height of thumb, in px
|
||||
thumbStyle <s|'opaque'> Thumb style ('opaque' or 'transparent')
|
||||
thumbValue <b> if true, display value on thumb
|
||||
trackColors <[s]> CSS colors
|
||||
trackGradient <b|false> if true, display track colors as gradient
|
||||
trackImages <s|[s]> one or multiple track background image URLs
|
||||
trackStep <n> 0 (scroll here) or step when clicking track
|
||||
value <n> initial value
|
||||
values <[s]> values to display on thumb
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Range Object
|
||||
change <!> triggered on change of the range
|
||||
@*/
|
||||
|
||||
Ox.Range = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
arrows: false,
|
||||
arrowStep: 1,
|
||||
arrowSymbols: ['left', 'right'],
|
||||
arrowTooltips: ['', ''],
|
||||
changeOnDrag: false,
|
||||
max: 100,
|
||||
min: 0,
|
||||
orientation: 'horizontal',
|
||||
step: 1,
|
||||
size: 128, // fixme: shouldn't this be width?
|
||||
thumbSize: 16,
|
||||
thumbStyle: 'default',
|
||||
thumbValue: false,
|
||||
trackColors: [],
|
||||
trackGradient: false,
|
||||
trackImages: [],
|
||||
trackStep: 0,
|
||||
value: 0,
|
||||
values: []
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
size: setSizes,
|
||||
trackColors: setTrackColors,
|
||||
value: setThumb
|
||||
})
|
||||
.addClass('OxRange')
|
||||
.css({
|
||||
width: self.options.size + 'px'
|
||||
});
|
||||
|
||||
self.hasValues = !Ox.isEmpty(self.options.values);
|
||||
if (self.hasValues) {
|
||||
self.options.max = self.options.values.length - 1;
|
||||
self.options.min = 0;
|
||||
self.options.step = 1;
|
||||
self.options.thumbValue = true;
|
||||
self.options.value = Ox.isNumber(self.options.value)
|
||||
? self.options.values[self.options.value] : self.options.value;
|
||||
}
|
||||
self.options.arrowStep = options.arrowStep || self.options.step;
|
||||
self.options.trackImages = Ox.makeArray(self.options.trackImages);
|
||||
|
||||
self.trackColors = self.options.trackColors.length;
|
||||
self.trackImages = self.options.trackImages.length;
|
||||
self.values = (
|
||||
self.options.max - self.options.min + self.options.step
|
||||
) / self.options.step;
|
||||
|
||||
setSizes();
|
||||
|
||||
if (self.options.arrows) {
|
||||
self.$arrows = [];
|
||||
Ox.range(0, 2).forEach(function(i) {
|
||||
self.$arrows[i] = Ox.Button({
|
||||
overlap: i == 0 ? 'right' : 'left',
|
||||
title: self.options.arrowSymbols[i],
|
||||
tooltip: self.options.arrowTooltips[i],
|
||||
type: 'image'
|
||||
})
|
||||
.addClass('OxArrow')
|
||||
.bindEvent({
|
||||
mousedown: function(data) {
|
||||
clickArrow(data, i, true);
|
||||
},
|
||||
mouserepeat: function(data) {
|
||||
clickArrow(data, i, false);
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
});
|
||||
}
|
||||
|
||||
self.$track = Ox.Element()
|
||||
.addClass('OxTrack')
|
||||
.css(Ox.extend({
|
||||
width: (self.trackSize - 2) + 'px'
|
||||
}, self.trackImages == 1 ? {
|
||||
background: 'rgb(0, 0, 0)'
|
||||
} : {}))
|
||||
.bindEvent(Ox.extend({
|
||||
mousedown: clickTrack,
|
||||
drag: dragTrack
|
||||
}, self.options.changeOnDrag ? {} : {
|
||||
dragend: dragendTrack
|
||||
}))
|
||||
.appendTo(that);
|
||||
|
||||
self.trackColors && setTrackColors();
|
||||
|
||||
if (self.trackImages) {
|
||||
self.$trackImages = $('<div>')
|
||||
.css({
|
||||
width: self.trackSize + 'px',
|
||||
marginRight: (-self.trackSize - 1) + 'px'
|
||||
})
|
||||
.appendTo(self.$track.$element);
|
||||
self.options.trackImages.forEach(function(v, i) {
|
||||
$('<img>')
|
||||
.attr({
|
||||
src: v
|
||||
})
|
||||
.addClass(i == 0 ? 'OxFirstChild' : '')
|
||||
.addClass(i == self.trackImages - 1 ? 'OxLastChild' : '')
|
||||
.css({
|
||||
width: self.trackImageWidths[i] + 'px'
|
||||
})
|
||||
.on({
|
||||
mousedown: function(e) {
|
||||
e.preventDefault(); // prevent drag
|
||||
}
|
||||
})
|
||||
.appendTo(self.$trackImages);
|
||||
//left += self.trackImageWidths[i];
|
||||
});
|
||||
}
|
||||
|
||||
self.$thumb = Ox.Button({
|
||||
id: self.options.id + 'Thumb',
|
||||
width: self.thumbSize
|
||||
})
|
||||
.addClass('OxThumb' + (
|
||||
self.options.thumbStyle == 'transparent' ? ' OxTransparent' : ''
|
||||
))
|
||||
.appendTo(self.$track);
|
||||
|
||||
setThumb();
|
||||
|
||||
function clickArrow(data, i, animate) {
|
||||
// fixme: shift doesn't work, see menu scrolling
|
||||
setValue(
|
||||
self.options.value
|
||||
+ self.options.arrowStep * (i == 0 ? -1 : 1) * (data.shiftKey ? 2 : 1),
|
||||
animate,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
function clickTrack(data) {
|
||||
// fixme: thumb ends up a bit too far on the right
|
||||
var isThumb = $(data.target).hasClass('OxThumb');
|
||||
self.drag = {
|
||||
left: self.$track.offset().left,
|
||||
offset: isThumb ? data.clientX - self.$thumb.offset().left - 8 /*self.thumbSize / 2*/ : 0
|
||||
};
|
||||
setValue(getValue(data.clientX - self.drag.left - self.drag.offset), !isThumb, true);
|
||||
}
|
||||
|
||||
function dragTrack(data) {
|
||||
setValue(
|
||||
getValue(data.clientX - self.drag.left - self.drag.offset),
|
||||
false,
|
||||
self.options.changeOnDrag
|
||||
);
|
||||
}
|
||||
|
||||
function dragendTrack(data) {
|
||||
self.options.value = void 0;
|
||||
setValue(getValue(data.clientX - self.drag.left - self.drag.offset), false, true);
|
||||
}
|
||||
|
||||
function getPx(value) {
|
||||
var pxPerValue = (self.trackSize - self.thumbSize)
|
||||
/ (self.options.max - self.options.min);
|
||||
value = self.hasValues ? self.options.values.indexOf(value) : value;
|
||||
return Math.ceil((value - self.options.min) * pxPerValue);
|
||||
}
|
||||
|
||||
/*
|
||||
function getTime(oldValue, newValue) {
|
||||
return self.animationTime * Math.abs(oldValue - newValue) / (self.options.max - self.options.min);
|
||||
}
|
||||
*/
|
||||
|
||||
function getValue(px) {
|
||||
var px = self.trackSize / self.values >= 16 ? px : px - 8,
|
||||
valuePerPx = (self.options.max - self.options.min)
|
||||
/ (self.trackSize - self.thumbSize),
|
||||
value = Ox.limit(
|
||||
self.options.min
|
||||
+ Math.floor(px * valuePerPx / self.options.step) * self.options.step,
|
||||
self.options.min,
|
||||
self.options.max
|
||||
);
|
||||
return self.hasValues ? self.options.values[value] : value;
|
||||
}
|
||||
|
||||
function setSizes() {
|
||||
self.trackSize = self.options.size - self.options.arrows * 32;
|
||||
self.thumbSize = Math.max(self.trackSize / self.values, self.options.thumbSize);
|
||||
self.trackImageWidths = self.trackImages == 1
|
||||
? [self.trackSize - 16]
|
||||
: Ox.splitInt(self.trackSize - 2, self.trackImages);
|
||||
self.trackColorStart = self.options.trackGradient
|
||||
? self.thumbSize / 2 / self.options.size : 0;
|
||||
self.trackColorStep = self.options.trackGradient
|
||||
? (self.options.size - self.thumbSize) / (self.trackColors - 1) / self.options.size
|
||||
: 1 / self.trackColors;
|
||||
that.css({
|
||||
width: self.options.size + 'px'
|
||||
});
|
||||
self.$track && self.$track.css({
|
||||
width: (self.trackSize - 2) + 'px'
|
||||
});
|
||||
if (self.$thumb) {
|
||||
self.$thumb.options({width: self.thumbSize});
|
||||
setThumb();
|
||||
}
|
||||
}
|
||||
|
||||
function setThumb(animate) {
|
||||
self.$thumb.stop().animate({
|
||||
marginLeft: getPx(self.options.value) - 1 + 'px'
|
||||
//, width: self.thumbSize + 'px'
|
||||
}, animate ? 250 : 0, function() {
|
||||
self.options.thumbValue && self.$thumb.options({
|
||||
title: self.options.value
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setTrackColors() {
|
||||
['moz', 'o', 'webkit'].forEach(function(browser) {
|
||||
self.$track.css({
|
||||
background: '-' + browser + '-linear-gradient(left, '
|
||||
+ self.options.trackColors[0] + ' 0%, '
|
||||
+ self.options.trackColors.map(function(v, i) {
|
||||
var ret = v + ' ' + (
|
||||
self.trackColorStart + self.trackColorStep * i
|
||||
) * 100 + '%';
|
||||
if (!self.options.trackGradient) {
|
||||
ret += ', ' + v + ' ' + (
|
||||
self.trackColorStart + self.trackColorStep * (i + 1)
|
||||
) * 100 + '%';
|
||||
}
|
||||
return ret;
|
||||
}).join(', ') + ', '
|
||||
+ self.options.trackColors[self.trackColors - 1] + ' 100%)'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setValue(value, animate, trigger) {
|
||||
// fixme: toPrecision helps with imprecise results of divisions,
|
||||
// but won't work for very large values. And is 10 a good value?
|
||||
value = Ox.limit(
|
||||
self.hasValues ? self.options.values.indexOf(value) : value.toPrecision(10),
|
||||
self.options.min,
|
||||
self.options.max
|
||||
);
|
||||
value = self.hasValues ? self.options.values[value] : value;
|
||||
if (value != self.options.value) {
|
||||
//time = getTime(self.options.value, value);
|
||||
self.options.value = value;
|
||||
setThumb(animate);
|
||||
trigger && that.triggerEvent('change', {value: self.options.value});
|
||||
}
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
272
source/UI/js/Form/Select.js
Normal file
272
source/UI/js/Form/Select.js
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Select <f> Select Object
|
||||
options <o> Options object
|
||||
disabled <b|false> If true, select is disabled
|
||||
id <s> Element id
|
||||
items <a|[]> Items (array of {id, title} or strings)
|
||||
label <s|''> Label
|
||||
labelWidth <n|64> Label width
|
||||
max <n|1> Maximum number of selected items
|
||||
maxWidth <n|0> Maximum menu width
|
||||
min <n|1> Minimum number of selected items
|
||||
overlap <s|'none'> Can be 'none', 'left' or 'right'
|
||||
selectable <b|true> is selectable
|
||||
size <s|'medium'> Size, can be small, medium, large
|
||||
style <s|'rounded'> Style ('rounded' or 'square')
|
||||
title <s|''> Select title
|
||||
tooltip <s|f|''> Tooltip title, or function that returns one
|
||||
(e) -> <string> Tooltip title
|
||||
e <object> Mouse event
|
||||
type <s|'text'> Type ('text' or 'image')
|
||||
value <a|s> Selected id, or array of selected ids
|
||||
width <s|n|'auto'> Width in px, or 'auto'
|
||||
self <o> Shared private variable
|
||||
([options[, self]) -> <o:Ox.Element> Select Object
|
||||
click <!> Click event
|
||||
change <!> Change event
|
||||
@*/
|
||||
|
||||
Ox.Select = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({
|
||||
tooltip: options.tooltip || ''
|
||||
}, self)
|
||||
.defaults({
|
||||
id: '',
|
||||
items: [],
|
||||
label: '',
|
||||
labelWidth: 64,
|
||||
max: 1,
|
||||
maxWidth: 0,
|
||||
min: 1,
|
||||
overlap: 'none',
|
||||
size: 'medium',
|
||||
style: 'rounded',
|
||||
title: '',
|
||||
type: 'text',
|
||||
value: options.max != 1 ? [] : '',
|
||||
width: 'auto'
|
||||
})
|
||||
// fixme: make default selection restorable
|
||||
.options(options)
|
||||
.update({
|
||||
label: function() {
|
||||
self.$label.options({title: self.options.label});
|
||||
},
|
||||
labelWidth: function() {
|
||||
self.$label.options({width: self.options.labelWidth});
|
||||
self.$title.css({width: getTitleWidth() + 'px'});
|
||||
},
|
||||
title: function() {
|
||||
var title = self.options.title
|
||||
? self.options.title
|
||||
: getItem(self.options.value).title;
|
||||
if (self.options.type == 'text') {
|
||||
self.$title.html(title);
|
||||
} else {
|
||||
self.$button.options({title: title});
|
||||
}
|
||||
},
|
||||
width: function() {
|
||||
that.css({width: self.options.width- 2 + 'px'});
|
||||
self.$title.css({width: getTitleWidth() + 'px'});
|
||||
},
|
||||
value: function() {
|
||||
var value = self.options.value;
|
||||
if (self.options.type == 'text' && !self.options.title) {
|
||||
self.$title.html(getItem(value).title);
|
||||
}
|
||||
value !== '' && Ox.makeArray(value).forEach(function(value) {
|
||||
self.$menu.checkItem(value);
|
||||
});
|
||||
self.options.value = self.optionGroup.value();
|
||||
}
|
||||
})
|
||||
.addClass(
|
||||
'OxSelect Ox' + Ox.toTitleCase(self.options.size)
|
||||
+ ' Ox' + Ox.toTitleCase(self.options.style) + (
|
||||
self.options.overlap == 'none'
|
||||
? '' : ' OxOverlap' + Ox.toTitleCase(self.options.overlap)
|
||||
) + (self.options.label ? ' OxLabelSelect' : '')
|
||||
)
|
||||
.css(self.options.width == 'auto' ? {} : {
|
||||
width: self.options.width - 2 + 'px'
|
||||
})
|
||||
.bindEvent({
|
||||
anyclick: function(e) {
|
||||
showMenu($(e.target).is('.OxButton') ? 'button' : null);
|
||||
},
|
||||
key_escape: loseFocus,
|
||||
key_down: showMenu
|
||||
});
|
||||
|
||||
self.options.items = self.options.items.map(function(item) {
|
||||
var isObject = Ox.isObject(item);
|
||||
return Ox.isEmpty(item) ? item : {
|
||||
id: isObject ? item.id : item,
|
||||
title: isObject ? item.title : item,
|
||||
checked: Ox.makeArray(self.options.value).indexOf(
|
||||
isObject ? item.id : item
|
||||
) > -1,
|
||||
disabled: isObject ? item.disabled : false
|
||||
};
|
||||
});
|
||||
|
||||
self.optionGroup = new Ox.OptionGroup(
|
||||
self.options.items,
|
||||
self.options.min,
|
||||
self.options.max,
|
||||
'checked'
|
||||
);
|
||||
self.options.items = self.optionGroup.init();
|
||||
self.options.value = self.optionGroup.value();
|
||||
|
||||
if (self.options.label) {
|
||||
self.$label = Ox.Label({
|
||||
overlap: 'right',
|
||||
textAlign: 'right',
|
||||
title: self.options.label,
|
||||
width: self.options.labelWidth
|
||||
})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
if (self.options.type == 'text') {
|
||||
self.$title = $('<div>')
|
||||
.addClass('OxTitle')
|
||||
.css({
|
||||
width: getTitleWidth() + 'px'
|
||||
})
|
||||
.html(
|
||||
self.options.title || getItem(self.options.value).title
|
||||
)
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
self.$button = Ox.Button({
|
||||
id: self.options.id + 'Button',
|
||||
selectable: true,
|
||||
style: 'symbol',
|
||||
title: self.options.type == 'text' || !self.options.title
|
||||
? 'select' : self.options.title,
|
||||
type: 'image'
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
self.$menu = Ox.Menu({
|
||||
edge: 'bottom',
|
||||
element: self.$title || self.$button,
|
||||
id: self.options.id + 'Menu',
|
||||
items: [{
|
||||
group: self.options.id + 'Group',
|
||||
items: self.options.items,
|
||||
max: self.options.max,
|
||||
min: self.options.min
|
||||
}],
|
||||
maxWidth: self.options.maxWidth,
|
||||
size: self.options.size
|
||||
})
|
||||
.bindEvent({
|
||||
change: changeMenu,
|
||||
click: clickMenu,
|
||||
hide: hideMenu
|
||||
});
|
||||
|
||||
self.options.type == 'image' && self.$menu.addClass('OxRight');
|
||||
|
||||
function clickMenu(data) {
|
||||
that.triggerEvent('click', data);
|
||||
}
|
||||
|
||||
function changeMenu(data) {
|
||||
self.options.value = self.optionGroup.value();
|
||||
self.$title && self.$title.html(
|
||||
self.options.title || getItem(self.options.value).title
|
||||
);
|
||||
that.triggerEvent('change', {
|
||||
title: Ox.isEmpty(self.options.value) ? ''
|
||||
: Ox.isArray(self.options.value)
|
||||
? self.options.value.map(function(value) {
|
||||
return getItem(value).title;
|
||||
})
|
||||
: getItem(self.options.value).title,
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
|
||||
function getItem(id) {
|
||||
return Ox.getObjectById(self.options.items, id);
|
||||
}
|
||||
|
||||
function getTitleWidth() {
|
||||
// fixme: used to be 22. obscure
|
||||
return self.options.width - 24 - (
|
||||
self.options.label ? self.options.labelWidth : 0
|
||||
);
|
||||
}
|
||||
|
||||
function hideMenu() {
|
||||
that.loseFocus();
|
||||
that.removeClass('OxSelected');
|
||||
self.$button.options({value: false});
|
||||
}
|
||||
|
||||
function loseFocus() {
|
||||
that.loseFocus();
|
||||
}
|
||||
|
||||
function selectItem() {
|
||||
|
||||
}
|
||||
|
||||
function showMenu(from) {
|
||||
that.gainFocus();
|
||||
that.addClass('OxSelected');
|
||||
from != 'button' && self.$button.options({value: true});
|
||||
self.options.tooltip && that.$tooltip.hide();
|
||||
self.$menu.showMenu();
|
||||
}
|
||||
|
||||
/*@
|
||||
disableItem <f> disableItem
|
||||
@*/
|
||||
that.disableItem = function(id) {
|
||||
self.$menu.getItem(id).options({disabled: true});
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
enableItem <f> enableItem
|
||||
@*/
|
||||
that.enableItem = function(id) {
|
||||
self.$menu.getItem(id).options({disabled: false});
|
||||
return that;
|
||||
};
|
||||
|
||||
/*@
|
||||
removeElement <f> removeElement
|
||||
@*/
|
||||
that.removeElement = function() {
|
||||
self.$menu.remove();
|
||||
return Ox.Element.prototype.removeElement.apply(that, arguments);
|
||||
};
|
||||
|
||||
/*@
|
||||
selected <f> gets selected item
|
||||
() -> <o> returns array of selected items with id and title
|
||||
@*/
|
||||
that.selected = function() {
|
||||
return Ox.makeArray(self.optionGroup.value()).map(function(id) {
|
||||
return {
|
||||
id: id,
|
||||
title: getItem(id).title
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
149
source/UI/js/Form/SelectInput.js
Normal file
149
source/UI/js/Form/SelectInput.js
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
'use strict';
|
||||
//FIXME: does not work without options
|
||||
/*@
|
||||
Ox.SelectInput <f> Select Input
|
||||
options <o> Options
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.FormElementGroup> Select Input
|
||||
@*/
|
||||
Ox.SelectInput = function(options, self) {
|
||||
|
||||
var that;
|
||||
self = Ox.extend(self || {}, {
|
||||
options: Ox.extend({
|
||||
inputValue: '',
|
||||
inputWidth: 128,
|
||||
items: [],
|
||||
label: '',
|
||||
labelWidth: 128,
|
||||
max: 1,
|
||||
min: 0,
|
||||
placeholder: '',
|
||||
title: '',
|
||||
value: options.max == 1 || options.max == void 0 ? '' : [],
|
||||
width: 384
|
||||
}, options || {})
|
||||
});
|
||||
|
||||
self.other = self.options.items[self.options.items.length - 1].id;
|
||||
self.otherWidth = self.options.width - self.options.inputWidth;
|
||||
|
||||
self.$select = Ox.Select({
|
||||
items: self.options.items,
|
||||
label: self.options.label,
|
||||
labelWidth: self.options.labelWidth,
|
||||
max: self.options.max,
|
||||
min: self.options.min,
|
||||
title: getTitle(),
|
||||
value: self.options.value,
|
||||
width: self.options.width
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
self.options.value = getValue();
|
||||
setValue(self.isOther);
|
||||
}
|
||||
});
|
||||
|
||||
self.$input = Ox.Input({
|
||||
placeholder: self.options.placeholder,
|
||||
width: self.options.inputWidth,
|
||||
value: self.options.inputValue
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
self.options.value = data.value;
|
||||
}
|
||||
})
|
||||
.hide();
|
||||
|
||||
setValue();
|
||||
|
||||
that = Ox.FormElementGroup({
|
||||
elements: [
|
||||
self.$select,
|
||||
self.$input
|
||||
],
|
||||
id: self.options.id,
|
||||
join: function(value) {
|
||||
return value[value[0] == self.other ? 1 : 0]
|
||||
},
|
||||
split: function(value) {
|
||||
return Ox.filter(self.options.items, function(item, i) {
|
||||
return i < item.length - 1;
|
||||
}).map(function(item) {
|
||||
return item.id;
|
||||
}).indexOf(value) > -1 ? [value, ''] : [self.other, value];
|
||||
},
|
||||
width: self.options.width
|
||||
})
|
||||
.update({
|
||||
label: function() {
|
||||
self.$select.options({label: self.options.label});
|
||||
},
|
||||
value: function() {
|
||||
self.options.value = that.options('value');
|
||||
setValue();
|
||||
}
|
||||
});
|
||||
|
||||
function getTitle() {
|
||||
var value = self.$select ? self.$select.value() : self.options.value;
|
||||
return Ox.isEmpty(value)
|
||||
? self.options.title
|
||||
: Ox.getObjectById(self.options.items, value).title;
|
||||
}
|
||||
|
||||
function getValue() {
|
||||
self.isOther = self.$select.value() == self.other;
|
||||
return !self.isOther ? self.$select.value() : self.$input.value();
|
||||
}
|
||||
|
||||
function setValue(isOther) {
|
||||
if (
|
||||
(!self.options.value && isOther !== true)
|
||||
|| Ox.filter(self.options.items, function(item) {
|
||||
return item.id != self.other;
|
||||
}).map(function(item) {
|
||||
return item.id;
|
||||
}).indexOf(self.options.value) > -1
|
||||
) {
|
||||
self.$select.options({
|
||||
title: !self.options.value
|
||||
? self.options.title
|
||||
: Ox.getObjectById(self.options.items, self.options.value).title,
|
||||
value: self.options.value,
|
||||
width: self.options.width
|
||||
})
|
||||
.removeClass('OxOverlapRight');
|
||||
self.$input.hide();
|
||||
} else {
|
||||
self.$select.options({
|
||||
title: Ox.getObjectById(self.options.items, self.other).title,
|
||||
value: self.other,
|
||||
width: self.otherWidth
|
||||
})
|
||||
.addClass('OxOverlapRight');
|
||||
self.$input.show().focusInput(true);
|
||||
}
|
||||
self.$select.options({title: getTitle()});
|
||||
}
|
||||
|
||||
/*@
|
||||
value <f> get/set value
|
||||
() -> value get value
|
||||
(value) -> <o> set value
|
||||
@*/
|
||||
that.value = function() {
|
||||
if (arguments.length == 0) {
|
||||
return getValue();
|
||||
} else {
|
||||
self.options.value = arguments[0];
|
||||
setValue();
|
||||
return that;
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
251
source/UI/js/Form/Spreadsheet.js
Normal file
251
source/UI/js/Form/Spreadsheet.js
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.Spreadsheet <f> Spreadsheet
|
||||
options <o> Options
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.Element> Spreadsheet
|
||||
change <!> change
|
||||
@*/
|
||||
Ox.Spreadsheet = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
columnPlaceholder: '',
|
||||
columns: [],
|
||||
columnTitleType: 'str',
|
||||
columnWidth: 64,
|
||||
rowPlaceholder: '',
|
||||
rows: [],
|
||||
rowTitleType: 'str',
|
||||
rowTitleWidth: 128,
|
||||
title: '',
|
||||
value: {}
|
||||
})
|
||||
.options(options || {})
|
||||
.addClass('OxSpreadsheet');
|
||||
|
||||
if (Ox.isEmpty(self.options.value)) {
|
||||
self.options.value = {
|
||||
columns: [],
|
||||
rows: [],
|
||||
values: []
|
||||
}
|
||||
Ox.loop(4, function(i) {
|
||||
self.options.value.columns.push('');
|
||||
self.options.value.rows.push('');
|
||||
self.options.value.values.push([0, 0, 0, 0]);
|
||||
});
|
||||
} else {
|
||||
self.options.value.values = self.options.value.values || [];
|
||||
if (Ox.isEmpty(self.options.value.values)) {
|
||||
self.options.value.values = [];
|
||||
self.options.value.rows.forEach(function(row, r) {
|
||||
self.options.value.values.push([]);
|
||||
self.options.value.columns.forEach(function(column, c) {
|
||||
self.options.value.values[r].push(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderSpreadsheet();
|
||||
|
||||
function addColumn(index) {
|
||||
self.options.value.columns.splice(index, 0, '');
|
||||
self.options.value.values.forEach(function(columns) {
|
||||
columns.splice(index, 0, 0);
|
||||
});
|
||||
renderSpreadsheet();
|
||||
}
|
||||
|
||||
function addRow(index) {
|
||||
self.options.value.rows.splice(index, 0, '');
|
||||
self.options.value.values.splice(index, 0, Ox.repeat([0], self.columns));
|
||||
renderSpreadsheet();
|
||||
}
|
||||
|
||||
function getSums() {
|
||||
var sums = {
|
||||
column: Ox.repeat([0], self.columns),
|
||||
row: Ox.repeat([0], self.rows),
|
||||
sheet: 0
|
||||
};
|
||||
self.options.value.values.forEach(function(columns, r) {
|
||||
columns.forEach(function(value, c) {
|
||||
sums.column[c] += value;
|
||||
sums.row[r] += value;
|
||||
sums.sheet += value;
|
||||
});
|
||||
});
|
||||
return sums;
|
||||
}
|
||||
|
||||
function removeColumn(index) {
|
||||
self.options.value.columns.splice(index, 1);
|
||||
self.options.value.values.forEach(function(columns) {
|
||||
columns.splice(index, 1);
|
||||
});
|
||||
renderSpreadsheet();
|
||||
}
|
||||
|
||||
function removeRow(index) {
|
||||
self.options.value.rows.splice(index, 1);
|
||||
self.options.value.values.splice(index, 1);
|
||||
renderSpreadsheet();
|
||||
}
|
||||
|
||||
function renderSpreadsheet() {
|
||||
|
||||
self.columns = self.options.value.columns.length;
|
||||
self.rows = self.options.value.rows.length;
|
||||
self.sums = getSums();
|
||||
self.$input = {};
|
||||
|
||||
that.empty()
|
||||
.css({
|
||||
width: self.options.rowTitleWidth
|
||||
+ self.options.columnWidth * (self.columns + 1) + 'px',
|
||||
height: 16 * (self.rows + 2) + 'px'
|
||||
});
|
||||
|
||||
[self.options.title].concat(Ox.clone(self.options.value.rows), ['Total']).forEach(function(row, r) {
|
||||
r--;
|
||||
[''].concat(Ox.clone(self.options.value.columns), ['Total']).forEach(function(column, c) {
|
||||
c--;
|
||||
if (r == -1) {
|
||||
if (c == -1 || c == self.columns) {
|
||||
Ox.Label({
|
||||
style: 'square',
|
||||
textAlign: c == -1 ? 'left' : 'right',
|
||||
title: c == -1 ? self.options.title : 'Total',
|
||||
width: c == -1 ? self.options.rowTitleWidth : self.options.columnWidth
|
||||
})
|
||||
.appendTo(that);
|
||||
} else {
|
||||
Ox.MenuButton({
|
||||
style: 'square',
|
||||
type: 'image',
|
||||
items: [
|
||||
{id: 'before', title: Ox._('Add column before')},
|
||||
{id: 'after', title: Ox._('Add column after')},
|
||||
{id: 'remove', title: Ox._('Remove this column'), disabled: self.columns == 1}
|
||||
]
|
||||
})
|
||||
.bindEvent({
|
||||
click: function(data) {
|
||||
if (data.id == 'remove') {
|
||||
removeColumn(c);
|
||||
} else {
|
||||
addColumn(c + (data.id == 'after'));
|
||||
}
|
||||
triggerChangeEvent();
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
Ox.Input({
|
||||
placeholder: self.options.columnPlaceholder,
|
||||
style: 'square',
|
||||
type: self.options.columnTitleType,
|
||||
value: column,
|
||||
width: self.options.columnWidth - 16
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
self.options.value.columns[c] = data.value;
|
||||
triggerChangeEvent();
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
}
|
||||
} else {
|
||||
if (c == -1) {
|
||||
if (r < self.rows) {
|
||||
Ox.MenuButton({
|
||||
style: 'square',
|
||||
type: 'image',
|
||||
items: [
|
||||
{id: 'before', title: Ox._('Add row above')},
|
||||
{id: 'after', title: Ox._('Add row below')},
|
||||
{id: 'remove', title: Ox._('Remove this row'), disabled: self.rows == 1}
|
||||
]
|
||||
})
|
||||
.bindEvent({
|
||||
click: function(data) {
|
||||
if (data.id == 'remove') {
|
||||
removeRow(r);
|
||||
} else {
|
||||
addRow(r + (data.id == 'after'));
|
||||
}
|
||||
triggerChangeEvent();
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
Ox.Input({
|
||||
placeholder: self.options.rowPlaceholder,
|
||||
style: 'square',
|
||||
type: self.options.rowTitleType,
|
||||
value: row,
|
||||
width: self.options.rowTitleWidth - 16
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
self.options.value.rows[r] = data.value;
|
||||
triggerChangeEvent();
|
||||
}
|
||||
})
|
||||
.appendTo(that);
|
||||
} else {
|
||||
Ox.Label({
|
||||
style: 'square',
|
||||
textAlign: 'right',
|
||||
title: row,
|
||||
width: self.options.rowTitleWidth
|
||||
})
|
||||
.appendTo(that);
|
||||
}
|
||||
} else {
|
||||
var id = c + ',' + r,
|
||||
isColumnSum = r == self.rows,
|
||||
isRowSum = c == self.columns,
|
||||
isSheetSum = isColumnSum && isRowSum,
|
||||
isSum = isColumnSum || isRowSum;
|
||||
self.$input[id] = Ox.Input({
|
||||
//changeOnKeypress: true,
|
||||
disabled: isSum,
|
||||
style: 'square',
|
||||
type: 'int',
|
||||
value: isSheetSum ? self.sums.sheet
|
||||
: isColumnSum ? self.sums.column[c]
|
||||
: isRowSum ? self.sums.row[r]
|
||||
: self.options.value.values[r][c],
|
||||
width: self.options.columnWidth
|
||||
})
|
||||
.appendTo(that);
|
||||
!isSum && self.$input[id].bindEvent({
|
||||
change: function(data) {
|
||||
self.options.value.values[r][c] = parseInt(data.value, 10);
|
||||
self.sums = getSums();
|
||||
self.$input[c + ',' + self.rows].value(self.sums.column[c]);
|
||||
self.$input[self.columns + ',' + r].value(self.sums.row[r]);
|
||||
self.$input[self.columns + ',' + self.rows].value(self.sums.sheet);
|
||||
triggerChangeEvent();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function triggerChangeEvent() {
|
||||
that.triggerEvent('change', {
|
||||
value: self.options.value
|
||||
});
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
173
source/UI/js/Form/TimeInput.js
Normal file
173
source/UI/js/Form/TimeInput.js
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.TimeInput <f> TimeInput Object
|
||||
options <o> Options object
|
||||
ampm <b|false> 24h/ampm
|
||||
seconds <b|false> show seconds
|
||||
milliseconds <b|false> show milliseconds
|
||||
value <d> value, defaults to current time
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Ox.InputGroup> TimeInput Object
|
||||
@*/
|
||||
|
||||
Ox.TimeInput = function(options, self) {
|
||||
|
||||
// fixme: seconds get set even if options.seconds is false
|
||||
var that;
|
||||
self = Ox.extend(self || {}, {
|
||||
options: Ox.extend({
|
||||
ampm: false,
|
||||
seconds: false,
|
||||
milliseconds: false,
|
||||
value: (function() {
|
||||
var date = new Date();
|
||||
return Ox.formatDate(
|
||||
date,
|
||||
options && (options.seconds || options.milliseconds) ? '%T' : '%H:%M'
|
||||
) + (options && options.milliseconds ? '.' + Ox.pad(date % 1000, 3) : '');
|
||||
}()),
|
||||
width: {
|
||||
hours: 32,
|
||||
minutes: 32,
|
||||
seconds: 32,
|
||||
milliseconds: 40,
|
||||
ampm: 32
|
||||
}
|
||||
}, options || {})
|
||||
});
|
||||
|
||||
self.options.seconds = self.options.seconds || self.options.milliseconds;
|
||||
|
||||
self.$input = {
|
||||
hours: Ox.Input({
|
||||
autocomplete: (self.options.ampm ? Ox.range(1, 13) : Ox.range(0, 24)).map(function(i) {
|
||||
return Ox.pad(i, 2);
|
||||
}),
|
||||
autocompleteReplace: true,
|
||||
autocompleteReplaceCorrect: true,
|
||||
id: 'hours',
|
||||
textAlign: 'right',
|
||||
width: self.options.width.hours
|
||||
}),
|
||||
minutes: Ox.Input({
|
||||
autocomplete: Ox.range(0, 60).map(function(i) {
|
||||
return Ox.pad(i, 2);
|
||||
}),
|
||||
autocompleteReplace: true,
|
||||
autocompleteReplaceCorrect: true,
|
||||
id: 'minutes',
|
||||
textAlign: 'right',
|
||||
width: self.options.width.minutes
|
||||
}),
|
||||
seconds: Ox.Input({
|
||||
autocomplete: Ox.range(0, 60).map(function(i) {
|
||||
return Ox.pad(i, 2);
|
||||
}),
|
||||
autocompleteReplace: true,
|
||||
autocompleteReplaceCorrect: true,
|
||||
id: 'seconds',
|
||||
textAlign: 'right',
|
||||
width: self.options.width.seconds
|
||||
}),
|
||||
milliseconds: Ox.Input({
|
||||
autocomplete: Ox.range(0, 1000).map(function(i) {
|
||||
return Ox.pad(i, 3);
|
||||
}),
|
||||
autocompleteReplace: true,
|
||||
autocompleteReplaceCorrect: true,
|
||||
id: 'milliseconds',
|
||||
textAlign: 'right',
|
||||
width: self.options.width.milliseconds
|
||||
}),
|
||||
ampm: Ox.Input({
|
||||
autocomplete: ['AM', 'PM'],
|
||||
autocompleteReplace: true,
|
||||
autocompleteReplaceCorrect: true,
|
||||
id: 'ampm',
|
||||
width: self.options.width.ampm
|
||||
})
|
||||
};
|
||||
|
||||
that = Ox.InputGroup(Ox.extend(self.options, {
|
||||
id: self.options.id,
|
||||
inputs: [].concat([
|
||||
self.$input.hours,
|
||||
self.$input.minutes,
|
||||
], self.options.seconds ? [
|
||||
self.$input.seconds
|
||||
] : [], self.options.milliseconds ? [
|
||||
self.$input.milliseconds
|
||||
] : [], self.options.ampm ? [
|
||||
self.$input.ampm
|
||||
] : []),
|
||||
join: join,
|
||||
separators: [].concat([
|
||||
{title: ':', width: 8},
|
||||
], self.options.seconds ? [
|
||||
{title: ':', width: 8}
|
||||
] : [], self.options.milliseconds ? [
|
||||
{title: '.', width: 8}
|
||||
] : [], self.options.ampm ? [
|
||||
{title: '', width: 8}
|
||||
] : []),
|
||||
split: split,
|
||||
value: self.options.value,
|
||||
width: 0
|
||||
}), self);
|
||||
|
||||
function getDate() {
|
||||
return new Date('1970/01/01 ' + (
|
||||
self.options.milliseconds
|
||||
? self.options.value.slice(0, -4)
|
||||
: self.options.value
|
||||
));
|
||||
}
|
||||
|
||||
function getValues() {
|
||||
var date = getDate();
|
||||
return {
|
||||
ampm: Ox.formatDate(date, '%p'),
|
||||
hours: Ox.formatDate(date, self.options.ampm ? '%I' : '%H'),
|
||||
milliseconds: self.options.milliseconds ? self.options.value.slice(-3) : '000',
|
||||
minutes: Ox.formatDate(date, '%M'),
|
||||
seconds: Ox.formatDate(date, '%S')
|
||||
};
|
||||
}
|
||||
|
||||
function join() {
|
||||
return Ox.formatDate(
|
||||
new Date(
|
||||
'1970/01/01 ' + [
|
||||
self.$input.hours.value(),
|
||||
self.$input.minutes.value(),
|
||||
self.options.seconds ? self.$input.seconds.value() : '00'
|
||||
].join(':') + (
|
||||
self.options.ampm ? ' ' + self.$input.ampm.value() : ''
|
||||
)
|
||||
),
|
||||
(
|
||||
self.options.seconds ? '%T' : '%H:%M'
|
||||
) + (
|
||||
self.options.milliseconds ? '.' + self.$input.milliseconds.value() : ''
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function split(value) {
|
||||
var values = getValues();
|
||||
return [].concat([
|
||||
values.hours,
|
||||
values.minutes,
|
||||
], self.options.seconds ? [
|
||||
values.seconds
|
||||
] : [], self.options.milliseconds ? [
|
||||
values.milliseconds
|
||||
] : [], self.options.ampm ? [
|
||||
values.ampm
|
||||
] : []);
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue