oxjs/source/Ox.UI/js/Form/Ox.Input.js

1929 lines
68 KiB
JavaScript
Raw Normal View History

2011-07-29 18:48:43 +00:00
// vim: et:ts=4:sw=4:sts=4:ft=javascript
2011-11-05 16:46:53 +00:00
'use strict';
2011-05-16 08:24:46 +00:00
/*@
Ox.Input <f:Ox.Element> Input Element
() -> <f> Input Element
(options) -> <f> Input Element
(options, self) -> <f> Input Element
options <o> Options object
2011-05-21 17:56:15 +00:00
arrows <b> if true, and type is 'float' or 'int', display arrows
2011-05-16 08:24:46 +00:00
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
2011-11-02 18:21:49 +00:00
autocompleteSelectMaxWidth <n|0> Maximum width of autocomplete menu, or 0
2011-05-16 08:24:46 +00:00
autocompleteSelectSubmit <b> if true, submit input on menu selection
2011-05-21 17:56:15 +00:00
autocorrect <s|r|f|null> ('email', 'float', 'int', 'phone', 'url'), or
2011-05-16 08:24:46 +00:00
<r> regexp(value), or
<f> function(key, value, blur, callback), returns value
autovalidate <f> --remote validation--
clear <b> if true, has clear button
2011-05-17 19:08:25 +00:00
changeOnKeypress <b> if true, fire change event while typing
2011-05-16 08:24:46 +00:00
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
2011-05-21 17:56:15 +00:00
max <n> max value if type is 'int' or 'float'
min <n> min value if type is 'int' or 'float'
2011-05-16 08:24:46 +00:00
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
2011-05-16 08:24:46 +00:00
//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
2011-08-12 21:00:42 +00:00
style <s> 'rounded' or 'square'
2011-05-16 08:24:46 +00:00
textAlign <s> 'left', 'center' or 'right'
2011-05-21 17:56:15 +00:00
type <s> 'float', 'int', 'password', 'text', 'textarea'
2011-05-16 08:24:46 +00:00
value <s> string
validate <f> remote validation
width <n> px
change <!> input changed event
submit <!> input submit event
@*/
2011-04-22 22:03:10 +00:00
2011-05-16 08:24:46 +00:00
Ox.Input = function(options, self) {
2011-04-22 22:03:10 +00:00
self = self || {};
var that = Ox.Element({}, self)
2011-04-22 22:03:10 +00:00
.defaults({
arrows: false,
arrowStep: 1,
autocomplete: null,
autocompleteReplace: false,
autocompleteReplaceCorrect: false,
autocompleteSelect: false,
autocompleteSelectHighlight: false,
autocompleteSelectMax: 0,
2011-11-02 18:21:49 +00:00
autocompleteSelectMaxWidth: 0,
2011-04-22 22:03:10 +00:00
autocompleteSelectSubmit: false,
autovalidate: null,
2011-05-17 19:08:25 +00:00
changeOnKeypress: false,
2011-04-22 22:03:10 +00:00
clear: false,
2011-05-21 17:56:15 +00:00
decimals: 0,
2011-04-22 22:03:10 +00:00
disabled: false,
2011-08-12 21:00:42 +00:00
height: 16,
2011-04-22 22:03:10 +00:00
key: '',
2011-05-21 17:56:15 +00:00
min: -Infinity,
max: Infinity,
2011-04-22 22:03:10 +00:00
label: '',
labelWidth: 64,
overlap: 'none',
placeholder: '',
serialize: null,
style: 'rounded',
textAlign: 'left',
type: 'text',
validate: null,
value: '',
width: 128
})
.options(options || {})
2011-04-22 22:03:10 +00:00
.addClass(
'OxInput OxMedium Ox' + Ox.toTitleCase(self.options.style) /*+ (
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'
} : {})
)
2011-10-27 13:13:28 +00:00
.bindEvent(Ox.extend(self.options.type == 'textarea' ? {
key_shift_enter: submit
} : {
2011-04-22 22:03:10 +00:00
key_enter: submit
}, {
key_control_v: paste,
key_escape: cancel
}));
if (
Ox.isArray(self.options.autocomplete)
&& self.options.autocompleteReplace
&& self.options.autocompleteReplaceCorrect
&& self.options.value === ''
2011-04-22 22:03:10 +00:00
) {
self.options.value = self.options.autocomplete[0]
}
// fixme: set to min, not 0
2011-05-21 17:56:15 +00:00
// fixme: validate self.options.value !
2011-04-22 22:03:10 +00:00
if (self.options.type == 'float') {
2011-05-21 17:56:15 +00:00
self.decimals = Ox.repeat('0', self.options.decimals || 1)
Ox.extend(self.options, {
2011-04-22 22:03:10 +00:00
autovalidate: 'float',
textAlign: 'right',
2011-05-21 17:56:15 +00:00
value: self.options.value || '0.' + self.decimals
2011-04-22 22:03:10 +00:00
});
2011-05-21 17:56:15 +00:00
} else if (self.options.type == 'int') {
Ox.extend(self.options, {
2011-05-21 17:56:15 +00:00
autovalidate: 'int',
2011-04-22 22:03:10 +00:00
textAlign: 'right',
value: self.options.value || '0'
});
}
if (self.options.label) {
self.$label = Ox.Label({
2011-04-22 22:03:10 +00:00
overlap: 'right',
textAlign: 'right',
title: self.options.label,
width: self.options.labelWidth
})
.css({
float: 'left', // fixme: use css rule
})
.click(function() {
// fixme: ???
// that.focus();
})
.appendTo(that);
}
if (self.options.arrows) {
self.arrows = [];
self.arrows[0] = [
Ox.Button({
2011-04-22 22:03:10 +00:00
overlap: 'right',
title: 'left',
type: 'image'
})
.css({
float: 'left'
})
.click(function() {
clickArrow(0);
})
.appendTo(that),
Ox.Button({
2011-04-22 22:03:10 +00:00
overlap: 'left',
title: 'right',
type: 'image'
})
.css({
float: 'right'
})
.click(function() {
clickArrow(1);
})
.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();
2011-04-22 22:03:10 +00:00
if (self.options.clear) {
self.$button = Ox.Button({
2011-04-22 22:03:10 +00:00
overlap: 'left',
title: 'close',
type: 'image'
})
.css({
float: 'right' // fixme: use css rule
})
.click(clear)
.appendTo(that);
}
self.$input = $(self.options.type == 'textarea' ? '<textarea>' : '<input>')
.addClass('OxInput OxMedium Ox' + Ox.toTitleCase(self.options.style))
.attr({
disabled: self.options.disabled,
2011-04-22 22:03:10 +00:00
type: self.options.type == 'password' ? 'password' : 'text'
})
.css(
Ox.extend({
width: self.inputWidth + 'px',
textAlign: self.options.textAlign
}, self.options.type == 'textarea' ? {
2011-11-02 10:23:15 +00:00
height: self.options.height - 6 + 'px',
} : {})
)
2011-04-22 22:03:10 +00:00
.val(self.options.value)
.blur(blur)
.change(change)
.focus(focus)
.appendTo(that.$element);
if (self.options.type == 'textarea') {
2011-11-04 15:54:28 +00:00
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'))
}
2011-04-22 22:03:10 +00:00
// 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
2011-10-26 08:59:30 +00:00
/*
2011-04-22 22:03:10 +00:00
if (self.options.type == 'textarea') {
Ox.extend(self, {
2011-04-25 09:33:39 +00:00
colors: Ox.Theme() == 'classic' ?
2011-04-22 22:03:10 +00:00
[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({
2011-05-19 10:18:39 +00:00
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(', ') + '))'
2011-04-22 22:03:10 +00:00
});
}
2011-10-26 08:59:30 +00:00
*/
2011-04-22 22:03:10 +00:00
if (self.hasPasswordPlaceholder) {
self.$input.hide();
self.$placeholder = $('<input>')
.addClass('OxInput OxMedium Ox' +
Ox.toTitleCase(self.options.style) +
' OxPlaceholder')
.attr({
type: 'text'
})
.css({
//float: 'left',
width: self.inputWidth + 'px'
})
.val(self.options.placeholder)
.focus(focus)
.appendTo(that.$element);
}
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;
2011-05-20 11:46:52 +00:00
oldCursor = Ox.isUndefined(oldCursor) ? cursor() : oldCursor;
2011-04-22 22:03:10 +00:00
Ox.Log('AUTO', 'autocomplete', oldValue, oldCursor)
2011-04-22 22:03:10 +00:00
if (self.options.value || self.options.autocompleteReplaceCorrect) {
if (Ox.isFunction(self.options.autocomplete)) {
if (self.options.key) {
self.options.autocomplete(
self.options.key, self.options.value, autocompleteCallback
);
2011-04-22 22:03:10 +00:00
} else {
self.options.autocomplete(
self.options.value, autocompleteCallback
);
2011-04-22 22:03:10 +00:00
}
} else {
autocompleteCallback(autocompleteFunction(self.options.value));
}
}
if (!self.options.value) {
2011-05-20 11:46:52 +00:00
if (self.options.autocompleteSelect) {
self.$autocompleteMenu
.unbindEvent('select')
.hideMenu();
self.selectEventBound = false;
}
2011-04-22 22:03:10 +00:00
}
function autocompleteFunction() {
var values = Ox.find(self.options.autocomplete, self.options.value);
return self.options.autocompleteReplace
? values[0] : Ox.merge(values[0], values[1]);
2011-04-22 22:03:10 +00:00
}
function autocompleteCallback(values) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'autocompleteCallback', values[0], self.options.value, self.options.value.length, oldValue, oldCursor)
2011-04-22 22:03:10 +00:00
var length = self.options.value.length,
newValue, newLength,
2011-04-22 22:03:10 +00:00
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;
2011-11-03 15:42:41 +00:00
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'selectEnd', selectEnd)
2011-04-22 22:03:10 +00:00
if (self.options.autocompleteReplace) {
2011-05-20 11:46:52 +00:00
value = self.options.value;
2011-04-22 22:03:10 +00:00
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) {
2011-05-20 11:46:52 +00:00
value = (self.options.autocompleteReplace ? value : self.options.value).toLowerCase();
2011-04-22 22:03:10 +00:00
if (values.length) {
self.oldCursor = cursor();
self.oldValue = self.options.value;
self.$autocompleteMenu.options({
items: Ox.map(values, function(v, i) {
var ret = null;
if (
!self.options.autocompleteSelectMax ||
i < self.options.autocompleteSelectMax
) {
if (value == v.toLowerCase()) {
selected = i;
}
ret = {
id: v.toLowerCase().replace(/ /g, '_'), // fixme: need function to do lowercase, underscores etc?
title: self.options.autocompleteSelectHighlight ?
Ox.highlight(v, value, 'OxHighlight') : v
};
2011-04-22 22:03:10 +00:00
}
return ret;
})
});
2011-05-20 11:46:52 +00:00
if (!self.selectEventBound) {
self.$autocompleteMenu.bindEvent({
select: selectMenu,
});
self.selectEventBound = true;
}
Ox.Log('AUTO', 'show menu')
self.$autocompleteMenu.options({
2011-04-22 22:03:10 +00:00
selected: selected
}).showMenu();
} else {
2011-05-20 11:46:52 +00:00
self.$autocompleteMenu
.unbindEvent('select')
.hideMenu();
self.selectEventBound = false;
2011-04-22 22:03:10 +00:00
}
}
that.triggerEvent('autocomplete', {
value: newValue
});
}
}
function constructAutocompleteMenu() {
var menu = Ox.Menu({
2011-04-22 22:03:10 +00:00
element: self.$input,
id: self.options.id + 'Menu', // fixme: we do this in other places ... are we doing it the same way? var name?,
2011-11-02 18:21:49 +00:00
maxWidth: self.options.autocompleteSelectMaxWidth,
2011-04-22 22:03:10 +00:00
offset: {
left: 4,
top: 0
}
//size: self.options.size
2011-04-22 22:03:10 +00:00
})
.addClass('OxAutocompleteMenu')
.bindEvent({
click: clickMenu
});
2011-04-22 22:03:10 +00:00
return menu;
}
function autovalidate() {
var blur, oldCursor, oldValue;
if (arguments.length == 1) {
blur = arguments[0];
} else {
blur = false;
oldValue = arguments[0];
oldCursor = arguments[1];
}
2011-05-17 19:08:25 +00:00
if (Ox.isFunction(self.options.autovalidate)) {
if (self.options.key) {
self.options.autovalidate(
self.options.key, self.options.value, blur, autovalidateCallback
);
2011-04-22 22:03:10 +00:00
} else {
2011-05-17 19:08:25 +00:00
self.options.autovalidate(
self.options.value, blur, autovalidateCallback
);
2011-04-22 22:03:10 +00:00
}
2011-05-17 19:08:25 +00:00
} else if (Ox.isRegExp(self.options.autovalidate)) {
autovalidateCallback(autovalidateFunction(self.options.value));
2011-05-17 19:08:25 +00:00
} else {
autovalidateTypeFunction(self.options.type, self.options.value);
2011-04-22 22:03:10 +00:00
}
function autovalidateFunction(value) {
value = value.split('').map(function(v) {
return self.options.autovalidate.test(v) ? v : null;
2011-04-22 22:03:10 +00:00
}).join('');
return {
valid: !!value.length,
value: value
};
2011-04-22 22:03:10 +00:00
}
function autovalidateTypeFunction(type, value) {
// fixme: remove trailing zeroes on blur
2011-05-21 17:56:15 +00:00
// /(^\-?\d+\.?\d{0,8}$)/('-13000.12345678')
2011-04-22 22:03:10 +00:00
var cursor,
2011-05-21 17:56:15 +00:00
length,
regexp = type == 'float' ? new RegExp(
2011-11-03 15:42:41 +00:00
'(^' + (self.options.min < 0 ? '\\-?' : '') + '\\d+\\.?\\d'
+ (self.options.decimals ? '{0,' + self.options.decimals + '}' : '*')
2011-05-21 17:56:15 +00:00
+ '$)'
) : new RegExp('(^' + (self.options.min < 0 ? '\\-?' : '') + '\\d+)');
2011-04-22 22:03:10 +00:00
if (type == 'float') {
2011-05-21 17:56:15 +00:00
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];
2011-07-23 13:44:11 +00:00
} else if (!/\./.test(value)) {
value += '.' + self.decimals;
2011-05-21 17:56:15 +00:00
cursor = [value.indexOf('.'), value.length];
2011-07-23 13:44:11 +00:00
} else if (/^\./.test(value)) {
2011-05-21 17:56:15 +00:00
value = '0' + value;
cursor = [2, value.length];
2011-07-23 13:44:11 +00:00
} else if (/\.$/.test(value)) {
2011-05-21 17:56:15 +00:00
value += self.decimals;
cursor = [value.indexOf('.') + 1, value.length];
2011-07-23 13:44:11 +00:00
} else if (/\./.test(value) && self.options.decimals) {
2011-05-21 17:56:15 +00:00
length = value.split('.')[1].length;
if (length > self.options.decimals) {
value = value.substr(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];
2011-04-22 22:03:10 +00:00
}
}
2011-05-21 17:56:15 +00:00
} else {
if (value === '') {
value = '0';
cursor = [0, 1];
2011-04-22 22:03:10 +00:00
}
}
2011-07-23 13:44:11 +00:00
while (/^0\d/.test(value)) {
2011-05-21 17:56:15 +00:00
value = value.substr(1, value.length);
}
if (!regexp.test(value) || value < self.options.min || value > self.options.max) {
2011-04-22 22:03:10 +00:00
value = oldValue;
2011-05-21 17:56:15 +00:00
cursor = oldCursor;
2011-04-22 22:03:10 +00:00
}
autovalidateCallback({
cursor: cursor,
valid: true,
value: value
});
2011-04-22 22:03:10 +00:00
}
function autovalidateCallback(data) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'autovalidateCallback', newValue, oldCursor)
self.options.value = data.value;
2011-04-22 22:03:10 +00:00
self.$input.val(self.options.value);
!blur && cursor(
data.cursor || (oldCursor[1] + data.value.length - oldValue.length)
2011-04-22 22:03:10 +00:00
);
that.triggerEvent('autovalidate', {
valid: data.valid,
value: data.value
2011-04-22 22:03:10 +00:00
});
}
}
/*
function autovalidate(blur) {
2011-11-04 15:54:28 +00:00
Ox.Log('Form', 'autovalidate', self.options.value, blur || false)
2011-04-22 22:03:10 +00:00
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) {
2011-04-22 22:03:10 +00:00
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();
if (self.bindKeyboard) {
2011-05-21 17:56:15 +00:00
Ox.UI.$document.unbind('keydown', keydown);
2011-04-22 22:03:10 +00:00
}
// fixme: for some reason, if options.type is set, no change event fires
// as a workaround, blur sends a value. remove later...
!self.cancelled && !self.submitted && that.triggerEvent('blur', {
value: self.options.value
});
2011-04-22 22:03:10 +00:00
}
function cancel() {
2011-08-12 21:00:42 +00:00
self.cancelled = true;
self.$input.blur().val(self.originalValue);
self.cancelled = false;
that.triggerEvent('cancel');
2011-04-22 22:03:10 +00:00
}
function change() {
self.options.value = self.$input.val();
that.triggerEvent('change', {
value: self.options.value
});
}
function clear() {
// fixme: set to min, not zero
// fixme: make this work for password
self.options.value = '';
2011-04-22 22:03:10 +00:00
if (self.options.type == 'float') {
self.options.value = '0.0';
2011-05-21 17:56:15 +00:00
} else if (self.options.type == 'int') {
self.options.value = '0'
2011-04-22 22:03:10 +00:00
}
self.$input.val(self.options.value);
cursor(0, self.options.value.length);
self.options.changeOnKeypress && that.triggerEvent({
change: {
value: self.options.value
}
});
2011-04-22 22:03:10 +00:00
}
function clickArrow(i) {
self.options.value = Ox.limit(
parseFloat(self.options.value) + (i == 0 ? -1 : 1) * self.options.arrowStep,
self.options.min,
self.options.max
).toString();
self.$input.val(self.options.value);//.focus();
}
function clickMenu(data) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'clickMenu', data);
2011-04-22 22:03:10 +00:00
self.options.value = data.title;
self.$input.val(self.options.value).focus();
that.gainFocus();
self.options.autocompleteSelectSubmit && 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;
self.$input[0].setSelectionRange(start, end);
}
}
function deselectMenu() {
2011-05-20 11:46:52 +00:00
return;
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'deselectMenu')
2011-04-22 22:03:10 +00:00
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
2011-04-22 22:03:10 +00:00
(self.$autocompleteMenu && self.$autocompleteMenu.is(':visible')) ||
(self.hasPasswordPlaceholder && self.$input.is(':visible'))
) {
return;
}
2011-08-12 21:00:42 +00:00
self.originalValue = self.options.value;
2011-04-22 22:03:10 +00:00
that.gainFocus();
2011-04-27 07:13:12 +00:00
that.is('.OxError') && that.removeClass('OxError');
2011-04-22 22:03:10 +00:00
self.options.placeholder && setPlaceholder();
if (self.bindKeyboard) {
// fixme: different in webkit and firefox (?), see keyboard handler, need generic function
2011-05-21 17:56:15 +00:00
Ox.UI.$document.keydown(keydown);
//Ox.UI.$document.keypress(keypress);
// temporarily disabled autocomplete on focus
//self.options.autocompleteSelect && setTimeout(autocomplete, 0); // fixme: why is the timeout needed?
2011-04-22 22:03:10 +00:00
}
2011-05-15 16:18:58 +00:00
that.triggerEvent('focus');
2011-04-22 22:03:10 +00:00
}
function getInputWidth() {
2011-11-02 10:23:15 +00:00
return self.options.width
- (self.options.arrows ? 32 : 0)
- (self.options.clear ? 16 : 0)
- (self.options.label ? self.options.labelWidth : 0)
- (self.options.style == 'rounded' ? 14 : 6);
2011-04-22 22:03:10 +00:00
}
2011-05-21 17:56:15 +00:00
function keydown(event) {
2011-04-22 22:03:10 +00:00
var oldCursor = cursor(),
oldValue = self.options.value,
newValue = oldValue.substr(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?
2011-04-22 22:03:10 +00:00
setTimeout(function() { // wait for val to be set
var value = self.$input.val();
2011-05-21 17:56:15 +00:00
if ((self.options.autocompleteReplace || self.options.decimals) && hasDeletedSelectedEnd) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'HAS DELETED SELECTED END', event.keyCode)
value = newValue;
2011-04-22 22:03:10 +00:00
self.$input.val(value);
}
if (value != self.options.value) {
self.options.value = value;
Ox.Log('AUTO', 'call autocomplete from keydown')
2011-04-22 22:03:10 +00:00
self.options.autocomplete && autocomplete(oldValue, oldCursor);
self.options.autovalidate && autovalidate(oldValue, oldCursor);
2011-05-17 19:08:25 +00:00
self.options.changeOnKeypress && that.triggerEvent({
change: {
value: self.options.value
}
});
2011-04-22 22:03:10 +00:00
}
}, 0);
}
if (
(event.keyCode == 38 || event.keyCode == 40) // up/down
&& self.options.autocompleteSelect
&& self.$autocompleteMenu.is(':visible')
) {
//return false;
2011-04-22 22:03:10 +00:00
}
}
function paste() {
var data = Ox.Clipboard.paste();
data.text && self.$input.val(data.text);
}
function selectMenu(data) {
2011-04-22 22:03:10 +00:00
var pos = cursor();
2011-05-20 11:46:52 +00:00
//if (self.options.value) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'selectMenu', pos, data.title)
2011-05-20 11:46:52 +00:00
self.options.value = data.title
self.$input.val(self.options.value);
cursor(pos[0], self.options.value.length);
//}
2011-04-22 22:03:10 +00:00
}
function setPlaceholder() {
if (self.options.placeholder) {
if (that.hasClass('OxFocus')) {
if (self.options.value === '') {
if (self.options.type == 'password') {
self.$placeholder.hide();
2011-12-18 09:44:11 +00:00
self.$input.show().focusInput(true);
2011-04-22 22:03:10 +00:00
} 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);
2011-04-22 22:03:10 +00:00
}
}
function setWidth() {
}
function submit() {
self.submitted = true;
2011-04-22 22:03:10 +00:00
self.$input.blur();
self.submitted = false;
//self.options.type == 'textarea' && self.$input.blur();
2011-04-22 22:03:10 +00:00
that.triggerEvent('submit', {
value: self.options.value
});
}
function validate() {
self.options.validate(self.options.value, function(data) {
that.triggerEvent('validate', data);
});
}
2011-04-29 12:40:51 +00:00
self.setOption = function(key, value) {
2011-04-22 22:03:10 +00:00
var inputWidth, val;
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});
2011-05-19 10:18:39 +00:00
} else if (key == 'height') {
that.css({height: value + 'px'});
self.$input.css({height: value - 6 + 'px'});
} 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'
});
2011-04-22 22:03:10 +00:00
} else if (key == 'placeholder') {
setPlaceholder();
} else if (key == 'value') {
2011-05-22 12:39:57 +00:00
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');
2011-04-22 22:03:10 +00:00
setPlaceholder();
} else if (key == 'width') {
2011-05-21 17:56:15 +00:00
that.css({width: self.options.width + 'px'});
2011-04-22 22:03:10 +00:00
inputWidth = getInputWidth();
self.$input.css({
width: inputWidth + 'px'
});
self.hasPasswordPlaceholder && self.$placeholder.css({
width: inputWidth + 'px'
});
}
};
2011-05-16 18:05:29 +00:00
that.blurInput = function() {
self.$input.blur();
return that;
};
2011-05-17 07:19:30 +00:00
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])
: 0,
stop = Ox.isNumber(arguments[1])
? (arguments[1] < 0 ? length + arguments[1] : arguments[1])
: Ox.isNumber(arguments[0]) ? arguments[0]
: arguments[0] ? length : 0;
2011-04-22 22:03:10 +00:00
self.$input.focus();
cursor(start, stop);
2011-04-22 22:03:10 +00:00
return that;
};
2011-05-12 20:02:22 +00:00
// fixme: deprecate, options are enough
2011-04-22 22:03:10 +00:00
that.value = function() {
2011-05-24 11:43:27 +00:00
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;
2011-04-22 22:03:10 +00:00
};
return that;
};
/**
delete below
*/
Ox.AutocorrectIntFunction = function(min, max, pad, year) {
var pad = pad || false,
year = year || false,
maxLength = max.toString().length,
ret = null,
values = [];
Ox.range(min, max + 1).forEach(function(v) {
values.push(v + '');
pad && v.toString().length < maxLength && values.push(Ox.pad(v, maxLength));
});
return function(value, blur, callback) {
var results;
if (year && value == '1') {
value = '1900';
} else {
results = Ox.find(values, value);
value = results[0].length == 1 && results[0][0].length < maxLength ?
(pad ? Ox.pad(results[0][0], maxLength) : results[0][0]) :
(results[0].length ? results[0][0] : null);
}
callback(value);
};
};
// fixme: remove
2011-04-22 22:03:10 +00:00
Ox.Input_ = function(options, self) {
/*
options:
clear boolean, clear button, or not
disabled boolean, disabled, or not
height height (px), if type is 'textarea'
id
label string, or
array [{ id, title, checked }] (selectable label) or
array [{ id, label: [{ id, title, checked }], width }] (multiple selectable labels)
label and placeholder are mutually exclusive
labelWidth integer (px)
placeholder string, or
array [{ id, title, checked }] (selectable placeholder)
label and placeholder are mutually exclusive
separator string, or
array of strings
to separate multiple values
separatorWidth integer (px), or
array of integers
serialize function
size 'large', 'medium' or 'small'
type 'password', 'select' or 'text'
unit string, or
array [{ id, title, checked }] (selectable unit)
unitWidth integer (px)
value string, or
array [{ id, value, width }] (multiple values)
width integer (px)
methods:
events:
*/
self = self || {};
var that = Ox.Element({}, self)
2011-04-22 22:03:10 +00:00
.defaults({
autocomplete: null,
autocorrect: null,
autosuggest: null,
autosuggestHighlight: false,
autosuggestSubmit: false,
autovalidate: null,
autovalidateName: 'Value',
clear: false,
disabled: false,
height: 128,
id: '',
key: '',
label: '',
labelWidth: 64,
placeholder: '',
separator: '',
separatorWidth: 16,
serialize: null,
size: 'medium',
type: 'text',
unit: '',
unitWidth: 64,
value: '',
width: 128
})
.options(options || {})
.addClass('OxInput Ox' + Ox.toTitleCase(self.options.size))
.css({
width: self.options.width + 'px'
});
Ox.extend(self, {
2011-04-22 22:03:10 +00:00
clearWidth: 16,
hasMultipleKeys: Ox.isArray(self.options.label) && 'label' in self.options.label[0],
hasMultipleValues: Ox.isArray(self.options.value) &&
(self.options.type != 'select' || 'items' in self.options.value[0]),
hasSelectableKeys: Ox.isArray(self.options.label) || Ox.isArray(self.options.placeholder),
hasSelectableUnits: Ox.isArray(self.options.unit),
keyName: self.options.label ? 'label' : (self.options.placeholder ? 'placeholder' : ''),
placeholderWidth: 16,
selectedKey: [0], // fixme: only set on demand?
selectedValue: 0,
selectedUnit: 0,
/* valid: autovalidateCall(true) */
});
['autocomplete', 'autocorrect', 'autosuggest', 'autovalidate'].forEach(function(v) {
//if (!Ox.isFunction(self.options[v])) {
self.options[v] = {
'': self.options[v]
};
//}
});
if (self.keyName && !self.hasMultipleKeys) {
self.options[self.keyName] = [Ox.extend({
2011-04-22 22:03:10 +00:00
id: '',
label: self.options[self.keyName],
}, self.keyName == 'label' ? {
id: '',
width: self.options.labelWidth
} : {})];
if (!self.hasSelectableKeys) {
self.options[self.keyName][0].label = [{
id: '',
title: self.options[self.keyName][0].label
}];
}
}
if (self.hasSelectableKeys) {
self.options[self.keyName].forEach(function(key, keyPos) {
if (key.width) {
self.options.labelWidth = (keyPos == 0 ? 0 : self.options.labelWidth) + key.width;
}
self.selectedKey[keyPos] = 0;
Ox.forEach(key, function(value, valuePos) {
if (value.checked) {
self.selectedKey[keyPos] = valuePos;
return false;
}
});
});
}
self.valueWidth = self.options.width -
(self.options.label ? self.options.labelWidth : 0) -
((self.options.placeholder && self.options.placeholder[0].label.length > 1) ? self.placeholderWidth : 0) -
(self.options.unit ? self.options.unitWidth : 0) -
(self.options.clear ? self.clearWidth : 0);
/*
if (self.hasMultipleValues) {
self.valueWidth -= Ox.isArray(self.options.separatorWidth) ?
Ox.sum(self.options.separatorWidth) :
(self.options.value.length - 1) * self.options.separatorWidth;
}
*/
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'self.hasMulVal', self.hasMultipleValues);
//Ox.Log('Form', 'self.options.value', self.options.value)
2011-04-22 22:03:10 +00:00
if (!self.hasMultipleValues) {
if (self.options.type == 'select') {
self.options.value = [{
id: '',
items: self.options.value,
width: self.valueWidth
}];
} else if (self.options.type == 'range') {
self.options.value = [Ox.extend({
2011-04-22 22:03:10 +00:00
id: '',
size: self.valueWidth
}, self.options.value)];
} else {
self.options.value = [{
id: '',
value: self.options.value,
width: self.valueWidth
}]
}
}
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'self.options.value', self.options.value)
2011-04-22 22:03:10 +00:00
self.values = self.options.value.length;
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', self.options.id, 'self.values', self.values)
2011-04-22 22:03:10 +00:00
if (Ox.isString(self.options.separator)) {
self.options.separator = $.map(new Array(self.values - 1), function(v, i) {
return self.options.separator;
});
}
if (Ox.isNumber(self.options.separatorWidth)) {
self.options.separatorWidth = $.map(new Array(self.values - 1), function(v, i) {
return self.options.separatorWidth;
});
}
if (self.options.unit) {
if (self.hasSelectableUnits) {
Ox.forEach(self.options.unit, function(unit, pos) {
if (unit.checked) {
self.selectedUnit = pos;
return false;
}
});
} else {
self.options.unit = [{
id: '',
title: self.options.unit
}];
}
}
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'self', self);
2011-04-22 22:03:10 +00:00
if (self.keyName) {
that.$key = [];
self.options[self.keyName].forEach(function(key, keyPos) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'keyPos key', keyPos, key)
2011-04-22 22:03:10 +00:00
if (self.keyName == 'label' && key.label.length == 1) {
that.$key[keyPos] = Ox.Label({
2011-04-22 22:03:10 +00:00
overlap: 'right',
title: key.label[0].title,
width: self.options.labelWidth
})
.css({
float: 'left'
})
.click(function() {
2011-12-18 09:44:11 +00:00
that.$input[0].focusInput(true);
2011-04-22 22:03:10 +00:00
})
.appendTo(that);
} else if (key.label.length > 1) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'key.length > 1')
2011-04-22 22:03:10 +00:00
self.selectKeyId = self.options.id + Ox.toTitleCase(self.keyName) +
(self.options[self.keyName].length == 1 ? '' : keyPos);
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'three', self.selectedKey, keyPos, self.selectedKey[keyPos]);
that.$key[keyPos] = Ox.Select({
2011-04-22 22:03:10 +00:00
id: self.selectKeyId,
items: $.map(key.label, function(value, valuePos) {
return {
checked: valuePos == self.selectedKey[keyPos],
id: value.id,
group: self.selectKeyId, // fixme: same id, works here, but should be different
title: value.title
};
}),
overlap: 'right',
type: self.options.label ? 'text' : 'image',
width: self.options.label ? (self.options.label.length == 1 ? self.options.labelWidth : key.width) : self.placeholderWidth
})
.css({
float: 'left'
})
.appendTo(that);
that.bindEvent('change_' + self.selectKeyId, changeKey);
}
});
}
if (self.options.clear) {
that.$clear = Ox.Button({
2011-04-22 22:03:10 +00:00
overlap: 'left',
title: 'close',
type: 'image'
})
.css({
float: 'right'
})
.click(clear)
.appendTo(that);
}
if (self.options.unit.length == 1) {
that.$unit = Ox.Label({
2011-04-22 22:03:10 +00:00
overlap: 'left',
title: self.options.unit[0].title,
width: self.options.unitWidth
})
.css({
float: 'right'
})
.click(function() {
2011-12-18 09:44:11 +00:00
that.$input[0].focusInput(true);
2011-04-22 22:03:10 +00:00
})
.appendTo(that);
} else if (self.options.unit.length > 1) {
self.selectUnitId = self.options.id + 'Unit';
that.$unit = Ox.Select({
2011-04-22 22:03:10 +00:00
id: self.selectUnitId,
items: $.map(self.options.unit, function(unit, i) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'unit', unit)
2011-04-22 22:03:10 +00:00
return {
checked: i == 0,
id: unit.id,
group: self.selectUnitId, // fixme: same id, works here, but should be different
title: unit.title
};
}),
overlap: 'left',
size: self.options.size,
width: self.options.unitWidth
})
.css({
float: 'right'
})
.appendTo(that);
}
if (self.values) {
that.$separator = [];
self.options.value.forEach(function(v, i) {
if (i < self.values - 1) {
that.$separator[i] = Ox.Label({
2011-04-22 22:03:10 +00:00
textAlign: 'center',
title: self.options.separator[i],
width: self.options.separatorWidth[i] + 32
})
.css({
float: 'left',
marginLeft: (v.width - (i == 0 ? 16 : 32)) + 'px'
})
.click(function() {
2011-12-18 09:44:11 +00:00
that.$input[0].focusInput(true);
2011-04-22 22:03:10 +00:00
})
.appendTo(that);
}
});
}
that.$input = [];
//self.margin = 0;
self.options.value.forEach(function(v, i) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'o k i', self.options, self.keyName, i);
2011-04-22 22:03:10 +00:00
var id = self.keyName ? $.map(self.selectedKey, function(v, i) {
return self.options[self.keyName][i].id;
}).join('.') : '';
//self.margin -= (i == 0 ? 16 : self.options.value[i - 1].width)
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'v:', v, 'id:', id)
2011-04-22 22:03:10 +00:00
if (self.options.type == 'select') {
that.$input[i] = Ox.Select({
2011-04-22 22:03:10 +00:00
id: v.id,
items: v.items,
width: v.width
}).
css({
float: 'left'
});
} else if (self.options.type == 'range') {
that.$input[i] = Ox.Range(v)
2011-04-22 22:03:10 +00:00
.css({
float: 'left'
});
} else {
that.$input[i] = Ox.InputElement({
2011-04-22 22:03:10 +00:00
autocomplete: self.options.autocomplete[id],
autocorrect: self.options.autocorrect[id],
autosuggest: self.options.autosuggest[id],
autosuggestHighlight: self.options.autosuggestHighlight,
autosuggestSubmit: self.options.autosuggestSubmit,
autovalidate: self.options.autovalidate[id],
autovalidateName: self.options.autovalidateName,
disabled: self.options.disabled,
height: self.options.height,
id: self.options.id + 'Input' + Ox.toTitleCase(v.id),
key: self.hasSelectableKeys ? self.options[self.keyName][0].label[self.selectedKey[0]].id : '',
parent: that,
placeholder: self.options.placeholder ? self.options.placeholder[0].label[0].title : '',
size: self.options.size,
type: self.options.type,
value: v.value,
width: v.width
});
}
that.$input[i]
.css(Ox.extend({}, self.options.value.length > 1 ? {
2011-04-22 22:03:10 +00:00
float: 'left',
marginLeft: -Ox.sum($.map(self.options.value, function(v_, i_) {
return i_ > i ? self.options.value[i_ - 1].width + self.options.separatorWidth[i_ - 1] : (i_ == i ? 16 : 0);
}))
} : {}))
.appendTo(that);
});
//width(self.options.width);
function changeKey(data) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'changeKey', data);
2011-04-22 22:03:10 +00:00
if (data) { // fixme: necessary?
self.key = {
id: data.id,
title: data.value // fixme: should be data.title
};
that.$input[0].options({
key: data.id
});
}
if (self.options.label) {
//that.$label.html(self.option.title);
2011-12-18 09:44:11 +00:00
that.$input[0].focusInput(true);
2011-04-22 22:03:10 +00:00
//autocompleteCall();
} else {
that.$input[0].options({
placeholder: data.value // fixme: should be data.title
});
/*
if (that.$input.hasClass('OxPlaceholder')) {
that.$input.val(self.key.title);
//that.$input.focus();
} else {
that.$input.focus();
self.options.autosuggest && autosuggestCall();
}
*/
}
}
function changeUnit() {
2011-12-18 09:44:11 +00:00
that.$input[0].focusInput(true);
2011-04-22 22:03:10 +00:00
}
function clear() {
that.$input.forEach(function(v, i) {
v.val('');
});
2011-12-18 09:44:11 +00:00
that.$input[0].focusInput(true);
2011-04-22 22:03:10 +00:00
}
function height(value) {
var stop = 8 / value;
if (self.options.type == 'textarea') {
that.$element
.height(value)
.css({
background: '-moz-linear-gradient(top, rgb(224, 224, 224), rgb(208, 208, 208) ' + (stop * 100) + '%, rgb(208, 208, 208) ' + (100 - stop * 100) + '%, rgb(192, 192, 192))'
})
.css({
background: '-webkit-gradient(linear, left top, left bottom, from(rgb(224, 224, 224)), color-stop(' + stop + ', rgb(208, 208, 208)), color-stop(' + (1 - stop) + ', rgb(208, 208, 208)), to(rgb(192, 192, 192)))'
});
that.$input
.height(value)
.css({
background: '-moz-linear-gradient(top, rgb(224, 224, 224), rgb(240, 240, 240) ' + (stop * 100) + '%, rgb(240, 240, 240) ' + (100 - stop * 100) + '%, rgb(255, 255, 255))'
})
.css({
background: '-webkit-gradient(linear, left top, left bottom, from(rgb(224, 224, 224)), color-stop(' + stop + ', rgb(240, 240, 240)), color-stop(' + (1 - stop) + ', rgb(240, 240, 240)), to(rgb(255, 255, 255)))'
});
}
}
function selectUnit() {
self.$selectUnitMenu.show();
}
function submit() {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'submit')
2011-04-22 22:03:10 +00:00
var value = that.$input.val();
that.$input.blur();
that.triggerEvent('submit', self.options.key ? {
key: self.options.key,
value: value
} : value);
}
function width(value) {
that.$element.width(value);
that.$input.width(
value - (self.options.type == 'textarea' ? 0 : 12) -
(self.options.label ? self.options.labelWidth : 0) -
(self.options.placeholder.length > 1 ? 16 : 0) -
(self.options.unit ? self.options.unitWidth : 0) -
(self.options.clear ? 16 : 0)
);
}
2011-04-29 12:40:51 +00:00
self.setOption = function(key, value) {
2011-04-22 22:03:10 +00:00
if (key == 'height') {
height(value);
} else if (key == 'width') {
width(value);
}
};
that.changeLabel = function(id) {
that.$key.html(Ox.getObjectById(self.options.label, id).title);
self.selectMenu.checkItem(id);
};
return that;
}
Ox.InputElement_ = function(options, self) {
self = self || {};
var that = Ox.Element(
2011-04-22 22:03:10 +00:00
options.type == 'textarea' ? 'textarea' : 'input', self
)
.defaults({
autocomplete: null,
autocorrect: null,
autosuggest: null,
autosuggestHighlight: false,
autosuggestSubmit: false,
autovalidate: null,
disabled: false,
height: 128,
id: '',
key: '',
parent: null,
placeholder: '',
size: 'medium',
type: 'text',
value: '',
width: 128
})
.options(options || {})
.addClass('OxInput Ox' + Ox.toTitleCase(self.options.size) + (
(self.options.placeholder && self.options.value === '') ?
' OxPlaceholder' : ''
))
.attr(self.options.type == 'textarea' ? {} : {
type: self.options.type
})
.css({
float: 'left',
width: (self.options.width - 14) + 'px'
})
.val(
(self.options.placeholder && self.options.value === '') ?
self.options.placeholder : self.options.value
)
.blur(blur)
.change(change)
.focus(focus);
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'InputElement self.options', self.options)
2011-04-22 22:03:10 +00:00
self.bindKeyboard = self.options.autocomplete || self.options.autocorrect
|| self.options.autosuggest || self.options.autovalidate;
2011-04-22 22:03:10 +00:00
if (self.options.autosuggest) {
self.autosuggestId = self.options.id + 'Menu'; // fixme: we do this in other places ... are we doing it the same way? var name?
self.$autosuggestMenu = Ox.Menu({
2011-04-22 22:03:10 +00:00
element: that.$element,
id: self.autosuggestId,
offset: {
left: 4,
top: 0
},
size: self.options.size
});
that.bindEvent('click_' + self.autosuggestId, clickMenu);
}
that.bindEvent(Ox.extend(self.options.type == 'textarea' ? {} : {
2011-04-22 22:03:10 +00:00
key_enter: submit
}, {
key_escape: cancel
}));
function autocomplete(value) {
var value = value.toLowerCase(),
ret = '';
if (value !== '') {
Ox.forEach(self.options.autocomplete, function(v, i) {
if (v.toLowerCase().indexOf(value) == 0) {
ret = v;
return false;
}
});
}
return ret;
}
function autocompleteCall() {
var value = that.$element.val();
Ox.isFunction(self.options.autocomplete) ?
self.options.autocomplete(self.options.key ? {
key: self.options.key,
value: value
} : value, autocompleteCallback) :
autocompleteCallback(autocomplete(value));
}
function autocompleteCallback(value) {
var pos = cursor()[0];
if (value) {
that.$element.val(value);
cursor(pos, value.length);
}
}
function autocorrect(value) {
var length = value.length;
return $.map(value.toLowerCase().split(''), function(v, i) {
if (new RegExp(self.options.autocorrect).test(v)) {
return v
} else {
return null;
}
}).join('');
}
function autocorrectCall(blur) {
var blur = blur || false,
value = that.$element.val(),
pos = cursor()[0];
Ox.isFunction(self.options.autocorrect) ?
self.options.autocorrect(value, blur, autocorrectCallback) :
autocorrectCallback(autocorrect(value), blue);
}
function autocorrectCallback(value, blur) {
var length = that.$element.val().length;
that.$element.val(self.options.value);
!blur && cursor(pos + value.length - length);
}
function autosuggest(value) {
var value = value.toLowerCase(),
values = [[], []];
if (value !== '') {
(
self.options.key ?
self.options.autosuggest[self.options.key] : self.options.autosuggest
).forEach(function(v, i) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'v...', v)
2011-04-22 22:03:10 +00:00
var index = v.toLowerCase().indexOf(value);
index > -1 && values[index == 0 ? 0 : 1].push(v);
});
}
return Ox.merge(values[0], values[1]);
2011-04-22 22:03:10 +00:00
}
function autosuggestCall() {
var value = that.$element.val();
Ox.isFunction(self.options.autosuggest) ?
self.options.autosuggest(self.options.key ? {
key: self.options.key,
value: value
} : value, autosuggestCallback) :
autosuggestCallback(autosuggest(value));
}
function autosuggestCallback(values) {
var values = values || [],
selected = values.length == 1 ? 0 : -1,
value = that.$element.val().toLowerCase();
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'values', values);
2011-04-22 22:03:10 +00:00
if (values.length) {
values = $.map(values, function(v, i) {
if (value == v.toLowerCase()) {
selected = i;
}
return {
id: v.toLowerCase().replace(/ /g, '_'), // fixme: need function to do lowercase, underscores etc?
title: self.options.autosuggestHighlight ? v.replace(
new RegExp('(' + value + ')', 'ig'),
'<span class="OxHighlight">$1</span>'
) : v
};
});
// self.selectMenu && self.selectMenu.hideMenu(); // fixme: need event
self.$autosuggestMenu.options({
items: values,
selected: selected
}).showMenu();
} else {
self.$autosuggestMenu.hideMenu();
}
}
function autovalidate(value) {
return {
valid: self.options.autovalidate(value) != null,
message: 'Invalid ' + self.options.name
};
}
function autovalidateCall(blur) {
var blur = blur || false,
value = that.$element.val();
if (value !== '') {
Ox.isFunction(self.options.autovalidate) ?
self.options.autovalidate(value, autovalidateCallback) :
autovalidateCallback(autovalidate(value), blur);
} else {
autovalidateCallback({
blur: blur,
valid: false,
message: 'Empty ' + self.options.name
});
}
}
function autovalidateCallback(data, blur) {
if (data.valid != self.valid) {
self.valid = data.valid;
that.triggerEvent('validate', Ox.extend(data, {
2011-04-22 22:03:10 +00:00
blur: blur
}));
}
}
function blur() {
if (!self.options.autosuggest || self.$autosuggestMenu.is(':hidden')) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'losing focus...')
2011-04-22 22:03:10 +00:00
that.loseFocus();
self.options.parent.removeClass('OxFocus');
self.options.autocorrect && autocorrectCall(true);
// self.options.autosuggest && self.$autosuggestMenu.hideMenu();
self.options.autovalidate && autovalidateCall(true);
if (self.options.placeholder && that.$element.val() === '') {
that.$element.addClass('OxPlaceholder').val(self.options.placeholder);
}
}
if (self.bindKeyboard) {
2011-05-21 17:56:15 +00:00
Ox.UI.$document.unbind('keydown', keydown);
2011-04-22 22:03:10 +00:00
}
}
function cancel() {
that.$element.blur();
}
function change() {
}
function clear() {
that.$element.val('').focus();
}
function clickMenu(data) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'clickMenu', data);
2011-04-22 22:03:10 +00:00
that.$element.val(data.title);
//self.$autosuggestMenu.hideMenu();
self.options.autosuggestSubmit && 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 [that.$element[0].selectionStart, that.$element[0].selectionEnd];
} else {
start = isArray ? start[0] : start;
end = isArray ? start[1] : (end ? end : start);
that.$element[0].setSelectionRange(start, end);
}
}
function focus() {
var val = that.$element.val();
that.gainFocus();
self.options.parent.addClass('OxFocus');
if (that.$element.hasClass('OxPlaceholder')) {
that.$element.val('').removeClass('OxPlaceholder');
}
if (self.bindKeyboard) {
// fixme: different in webkit and firefox (?), see keyboard handler, need generic function
2011-05-21 17:56:15 +00:00
Ox.UI.$document.keydown(keydown);
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'calling autosuggest...')
2011-04-22 22:03:10 +00:00
self.options.autosuggest && setTimeout(autosuggestCall, 0); // fixme: why is the timeout needed?
}
}
2011-05-21 17:56:15 +00:00
function keydown(event) {
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', 'keyCode', event.keyCode)
2011-04-22 22:03:10 +00:00
if (event.keyCode != 9 && event.keyCode != 13 && event.keyCode != 27) { // fixme: can't 13 and 27 return false?
setTimeout(function() { // fixme: document what this timeout is for
var value = that.$element.val();
if (value != self.options.value) {
self.options.value = value;
self.options.autocomplete && autocompleteCall();
self.options.autocorrect && autocorrectCall();
self.options.autosuggest && autosuggestCall();
self.options.autovalidate && autovalidateCall();
}
}, 25);
}
}
function submit() {
}
2011-04-29 12:40:51 +00:00
self.setOption = function(key, value) {
2011-04-22 22:03:10 +00:00
if (key == 'placeholder') {
that.$element.hasClass('OxPlaceholder') && that.$element.val(value);
} else if (key == 'value') {
if (self.options.placeholder) {
if (value === '') {
that.$element.addClass('OxPlaceholder').val(self.options.placeholder);
} else {
that.$element.removeClass('OxPlaceholder');
}
}
change(); // fixme: keypress too
}
}
return that;
}
Ox.Range_ = function(options, self) {
/*
init
*/
self = self || {};
var that = Ox.Element({}, self)
2011-04-22 22:03:10 +00:00
.defaults({
animate: false,
arrows: false,
arrowStep: 1,
arrowSymbols: ['left', 'right'],
max: 100,
min: 0,
orientation: 'horizontal',
step: 1,
size: 128,
thumbSize: 16,
thumbValue: false,
trackImages: [],
trackStep: 0,
value: 0,
valueNames: null
})
.options(Ox.extend(options, {
2011-04-22 22:03:10 +00:00
arrowStep: options.arrowStep ?
options.arrowStep : options.step,
trackImages: $.makeArray(options.trackImages || [])
}))
.addClass('OxRange')
.css({
width: self.options.size + 'px'
});
// fixme: self. ... ?
var trackImages = self.options.trackImages.length,
values = (self.options.max - self.options.min + self.options.step) /
self.options.step;
/*
construct
*/
that.$element
.css({
width: self.options.size + 'px'
});
if (self.options.arrows) {
var $arrowDec = Ox.Button({
style: 'symbol',
type: 'image',
value: self.options.arrowSymbols[0]
})
.addClass('OxArrow')
.mousedown(mousedownArrow)
.click(clickArrowDec)
.appendTo(that.$element);
}
var $track = Ox.Element()
2011-04-22 22:03:10 +00:00
.addClass('OxTrack')
.mousedown(clickTrack)
.appendTo(that.$element);
if (trackImages) {
var width = parseFloat(screen.width / trackImages),
$image = $('<canvas>')
.attr({
width: width * trackImages,
height: 14
})
.addClass('OxImage')
.appendTo($track.$element),
c = $image[0].getContext('2d');
c.mozImageSmoothingEnabled = false; // we may want to remove this later
self.options.trackImages.forEach(function(v, i) {
var left = 0;
$('<img/>')
.attr({
src: v
})
.load(function() {
c.drawImage(this, left, 0, self.trackImageWidth[i], 14);
});
left += self.trackImageWidth[i];
});
}
var $thumb = Ox.Button({})
.addClass('OxThumb')
.appendTo($track);
2011-11-04 15:54:28 +00:00
//Ox.Log('Form', '----')
2011-04-22 22:03:10 +00:00
if (self.options.arrows) {
var $arrowInc = Ox.Button({
style: 'symbol',
type: 'image',
value: self.options.arrowSymbols[1]
})
.addClass('OxArrow')
.mousedown(mousedownArrow)
.click(clickArrowInc)
.appendTo(that.$element);
}
var rangeWidth, trackWidth, imageWidth, thumbWidth;
setWidth(self.options.size);
/*
private functions
*/
function clickArrowDec() {
that.removeClass('OxActive');
setValue(self.options.value - self.options.arrowStep, 200)
}
function clickArrowInc() {
that.removeClass('OxActive');
setValue(self.options.value + self.options.arrowStep, 200);
}
function clickTrack(e) {
//Ox.Focus.focus();
var left = $track.offset().left,
offset = $(e.target).hasClass('OxThumb') ?
e.clientX - $thumb.offset().left - thumbWidth / 2 - 2 : 0;
function val(e) {
return getVal(e.clientX - left - offset);
}
setValue(val(e), 200);
Ox.UI.$window.mousemove(function(e) {
setValue(val(e));
});
Ox.UI.$window.one('mouseup', function() {
Ox.UI.$window.unbind('mousemove');
});
}
function getPx(val) {
var pxPerVal = (trackWidth - thumbWidth - 2) /
(self.options.max - self.options.min);
return Math.ceil((val - self.options.min) * pxPerVal + 1);
}
function getVal(px) {
var px = trackWidth / values >= 16 ? px : px - 8,
valPerPx = (self.options.max - self.options.min) /
(trackWidth - thumbWidth);
return Ox.limit(self.options.min +
Math.floor(px * valPerPx / self.options.step) * self.options.step,
self.options.min, self.options.max);
}
function mousedownArrow() {
that.addClass('OxActive');
}
function setThumb(animate) {
var animate = typeof animate != 'undefined' ? animate : 0;
$thumb.animate({
marginLeft: (getPx(self.options.value) - 2) + 'px',
width: thumbWidth + 'px'
}, self.options.animate ? animate : 0, function() {
if (self.options.thumbValue) {
$thumb.options({
value: self.options.valueNames ?
self.options.valueNames[self.options.value] :
self.options.value
});
}
});
}
function setValue(val, animate) {
val = Ox.limit(val, self.options.min, self.options.max);
if (val != self.options.value) {
that.options({
value: val
});
setThumb(animate);
that.triggerEvent('change', {value: val});
2011-04-22 22:03:10 +00:00
}
}
function setWidth(width) {
trackWidth = width - self.options.arrows * 32;
thumbWidth = Math.max(trackWidth / values - 2, self.options.thumbSize - 2);
that.$element.css({
width: (width - 2) + 'px'
});
$track.css({
width: (trackWidth - 2) + 'px'
});
if (trackImages) {
$image.css({
width: (trackWidth - 2) + 'px'
});
}
$thumb.css({
width: (thumbWidth - 2) + 'px',
padding: 0
});
setThumb();
}
/*
shared functions
*/
2011-04-29 12:40:51 +00:00
self.setOption = function(key, value) {
2011-04-22 22:03:10 +00:00
}
return that;
};