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

1007 lines
38 KiB
JavaScript
Raw Normal View History

2011-11-05 16:46:53 +00:00
'use strict';
2012-05-21 10:38:18 +00:00
2011-05-16 08:24:46 +00:00
/*@
2012-05-31 10:32:54 +00:00
Ox.Input <f> Input Element
2011-05-16 08:24:46 +00:00
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
autocompleteSelectOffset <o|{left: 4, top: 0}> Offset of autocomplete menu
2011-05-16 08:24:46 +00:00
autocompleteSelectSubmit <b> if true, submit input on menu selection
autocompleteSelectUpdate <b> if true, update menu position on keypress
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
2013-08-11 09:50:43 +00:00
clearTooltip <s|f|''> clear button tooltip
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
readonly <b> if true, is readonly
2011-05-16 08:24:46 +00:00
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
([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
2011-05-16 08:24:46 +00:00
@*/
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({
element: (options || {}).element || '<div>'
}, 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,
autocompleteSelectOffset: {left: 4, top: 0},
2011-04-22 22:03:10 +00:00
autocompleteSelectSubmit: false,
autocompleteSelectUpdate: false,
2011-04-22 22:03:10 +00:00
autovalidate: null,
2011-05-17 19:08:25 +00:00
changeOnKeypress: false,
2011-04-22 22:03:10 +00:00
clear: false,
2013-08-11 09:50:43 +00:00
clearTooltip: '',
2011-05-21 17:56:15 +00:00
decimals: 0,
2013-02-25 16:39:18 +00:00
disabled: false,
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: '',
readonly: false,
2011-04-22 22:03:10 +00:00
serialize: null,
style: 'rounded',
textAlign: 'left',
type: 'text',
validate: null,
value: '',
width: 128
})
.options(options || {})
2012-05-28 19:35:41 +00:00
.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'});
2014-02-16 07:06:48 +00:00
} else if (key == 'label') {
self.$label.options({title: value});
2012-05-28 19:35:41 +00:00
} 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 == 'readonly') {
self.$input.attr({readonly: value});
2012-05-28 19:35:41 +00:00
} 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'
});
}
})
2011-04-22 22:03:10 +00:00
.addClass(
'OxInput OxKeyboardFocus OxMedium Ox' + Ox.toTitleCase(self.options.style)
+ (self.options.type == 'textarea' ? ' OxTextarea' : '') /*+ (
2011-04-22 22:03:10 +00:00
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'
} : {})
)
2012-01-16 14:02:30 +00:00
.bindEvent(Ox.extend(self.options.type != 'textarea' ? {
2011-04-22 22:03:10 +00:00
key_enter: submit
} : {}, {
2012-02-16 16:35:59 +00:00
key_control_i: insert,
key_escape: cancel,
key_shift_enter: submit
2011-04-22 22:03:10 +00:00
}));
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',
2016-01-12 05:41:08 +00:00
style: self.options.style,
2011-04-22 22:03:10 +00:00
textAlign: 'right',
title: self.options.label,
width: self.options.labelWidth
})
.css({
2012-05-26 15:48:19 +00:00
float: 'left' // fixme: use css rule
2011-04-22 22:03:10 +00:00
})
2013-12-06 20:43:00 +00:00
.on({
click: function() {
// fixme: ???
// that.focus();
}
2011-04-22 22:03:10 +00:00
})
.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'
})
2013-12-06 20:43:00 +00:00
.css({float: 'left'})
.on({
click: function() {
2015-02-21 07:17:11 +00:00
clickArrow(-1);
2013-12-06 20:43:00 +00:00
}
2011-04-22 22:03:10 +00:00
})
.appendTo(that),
Ox.Button({
2011-04-22 22:03:10 +00:00
overlap: 'left',
title: 'right',
type: 'image'
})
2013-12-06 20:43:00 +00:00
.css({float: 'right'})
.on({
click: function() {
2015-02-21 07:17:11 +00:00
clickArrow(1);
2013-12-06 20:43:00 +00:00
}
2011-04-22 22:03:10 +00:00
})
.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',
2014-06-06 12:38:15 +00:00
// FIXME: should always be self.options.style, but there
// is a CSS bug for rounded image buttons
style: self.options.style == 'squared' ? 'squared' : '',
2011-04-22 22:03:10 +00:00
title: 'close',
2013-08-11 09:50:43 +00:00
tooltip: self.options.clearTooltip,
2011-04-22 22:03:10 +00:00
type: 'image'
})
.css({
float: 'right' // fixme: use css rule
})
.bindEvent({
click: clear,
doubleclick: submit
})
2011-04-22 22:03:10 +00:00
.appendTo(that);
}
2013-12-07 14:07:39 +00:00
self.$input = $(self.options.type == 'textarea' ? '<textarea>' : '<input>')
.addClass('OxInput OxKeyboardFocus OxMedium Ox' + Ox.toTitleCase(self.options.style))
.attr({
disabled: self.options.disabled,
readonly: self.options.readonly,
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' ? {
2012-05-26 15:48:19 +00:00
height: self.options.height - 6 + 'px'
} : {})
)
2011-04-22 22:03:10 +00:00
.val(self.options.value)
2013-12-06 20:17:39 +00:00
.on({
click: function() {
self.options.disabled && that.gainFocus();
},
2013-12-06 20:17:39 +00:00
blur: blur,
change: change,
focus: focus
})
.appendTo(that);
2011-04-22 22:03:10 +00:00
if (self.bindKeyboard) {
self.$input.on({
paste: keydown
});
}
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, {
2013-02-11 10:47:55 +00:00
colors: Ox.Theme() == 'oxlight' ?
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();
2013-12-07 14:07:39 +00:00
self.$placeholder = $('<input>')
.addClass('OxInput OxKeyboardFocus OxMedium Ox' +
2011-04-22 22:03:10 +00:00
Ox.toTitleCase(self.options.style) +
' OxPlaceholder')
2013-12-07 14:47:56 +00:00
.attr({type: 'text'})
.css({width: self.inputWidth + 'px'})
2011-04-22 22:03:10 +00:00
.val(self.options.placeholder)
2013-12-06 20:17:39 +00:00
.on({focus: focus})
.appendTo(that);
2011-04-22 22:03:10 +00:00
}
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) {
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
);
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());
2011-04-22 22:03:10 +00:00
}
}
if (!self.options.value) {
2011-05-20 11:46:52 +00:00
if (self.options.autocompleteSelect) {
self.$autocompleteMenu
.unbindEvent('select')
.hideMenu();
that.gainFocus();
2011-05-20 11:46:52 +00:00
self.selectEventBound = false;
}
2011-04-22 22:03:10 +00:00
}
function autocompleteFunction() {
2012-05-25 07:46:29 +00:00
return Ox.find(
self.options.autocomplete,
self.options.value,
self.options.autocompleteReplace
);
2011-04-22 22:03:10 +00:00
}
function autocompleteCallback(values) {
if (self.autocompleteId != id) {
return;
}
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 &&
Ox.startsWith(values[0].toLowerCase(),
self.options.value.toLowerCase())) {
newValue = values[0];
selected = 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-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);
}
}
if (self.options.autocompleteSelect) {
2012-05-22 14:29:37 +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({
2012-05-22 14:29:37 +00:00
items: Ox.filter(values, function(v, i) {
var ret = false;
if (
!self.options.autocompleteSelectMax ||
i < self.options.autocompleteSelectMax
) {
2012-05-22 14:29:37 +00:00
if (v.toLowerCase() === value) {
selected = i;
}
2012-05-22 14:29:37 +00:00
ret = true;
2011-04-22 22:03:10 +00:00
}
return ret;
2012-05-22 14:29:37 +00:00
}).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
2012-05-22 14:29:37 +00:00
};
})
});
2011-05-20 11:46:52 +00:00
if (!self.selectEventBound) {
self.$autocompleteMenu.bindEvent({
2012-05-26 15:48:19 +00:00
select: selectMenu
2011-05-20 11:46:52 +00:00
});
self.selectEventBound = true;
}
self.$autocompleteMenu.options({
2011-04-22 22:03:10 +00:00
selected: selected
}).showMenu();
2014-12-17 14:54:59 +00:00
if (self.options.autocompleteSelectUpdate) {
2014-12-17 15:43:43 +00:00
self.$autocompleteMenu.updatePosition();
2014-12-17 14:54:59 +00:00
}
2011-04-22 22:03:10 +00:00
} else {
2011-05-20 11:46:52 +00:00
self.$autocompleteMenu
.unbindEvent('select')
.hideMenu();
that.gainFocus();
2011-05-20 11:46:52 +00:00
self.selectEventBound = false;
2011-04-22 22:03:10 +00:00
}
}
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];
}
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
+ '$)'
2012-07-09 13:59:28 +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) {
2012-05-24 09:47:33 +00:00
value = value.slice(0, value.indexOf('.') + 1 + self.options.decimals);
2011-05-21 17:56:15 +00:00
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)) {
2012-05-24 09:47:33 +00:00
value = value.slice(1);
2011-05-21 17:56:15 +00:00
}
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();
2014-09-25 16:35:17 +00:00
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
});
}
2011-04-22 22:03:10 +00:00
}
function cancel() {
2011-08-12 21:00:42 +00:00
self.cancelled = true;
self.$input.val(self.originalValue).blur();
2011-08-12 21:00:42 +00:00
self.cancelled = false;
that.triggerEvent('cancel');
2011-04-22 22:03:10 +00:00
}
function cancelAutocomplete() {
self.autocompleteId = null;
}
2011-04-22 22:03:10 +00:00
function change() {
// change gets invoked before blur
2011-04-22 22:03:10 +00:00
self.options.value = self.$input.val();
self.originalValue = self.options.value;
!self.options.changeOnKeypress && that.triggerEvent('change', {
2011-04-22 22:03:10 +00:00
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);
2011-04-22 22:03:10 +00:00
}
}
function clickArrow(i) {
var originalValue = self.options.value;
2011-04-22 22:03:10 +00:00
self.options.value = Ox.limit(
2015-02-21 07:17:11 +00:00
parseFloat(self.options.value) + i * self.options.arrowStep,
2011-04-22 22:03:10 +00:00
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});
}
2011-04-22 22:03:10 +00:00
}
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 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: self.options.autocompleteSelectOffset
})
.addClass('OxAutocompleteMenu OxKeyboardFocus')
.bindEvent({
click: clickMenu,
key_enter: function() {
if (self.$autocompleteMenu.is(':visible')) {
self.$autocompleteMenu.hideMenu();
that.gainFocus();
submit();
}
}
});
}
2011-04-22 22:03:10 +00:00
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;
2012-05-25 21:42:10 +00:00
//IE8 does not have setSelectionRange
self.$input[0].setSelectionRange && self.$input[0].setSelectionRange(start, end);
2011-04-22 22:03:10 +00:00
}
}
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);
*/
2011-04-22 22:03:10 +00:00
}
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
2014-09-25 16:35:17 +00:00
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?
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)
2014-05-10 13:10:54 +00:00
- (Ox.contains(['rounded', 'squared'], self.options.style) ? 14 : 6);
2011-04-22 22:03:10 +00:00
}
2012-02-16 16:35:59 +00:00
function insert() {
var input = self.$input[0];
that.triggerEvent('insert', {
end: input.selectionEnd,
2012-05-22 14:08:09 +00:00
id: that.oxid,
2012-05-24 09:47:33 +00:00
selection: input.value.slice(input.selectionStart, input.selectionEnd),
2012-02-16 16:35:59 +00:00
start: input.selectionStart,
value: input.value
});
}
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.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?
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-05-17 19:08:25 +00:00
});
2011-04-22 22:03:10 +00:00
}
});
2011-04-22 22:03:10 +00:00
}
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() {
2012-02-16 16:35:59 +00:00
// fixme: unused
2011-04-22 22:03:10 +00:00
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)
self.options.value = Ox.decodeHTMLEntities(data.title);
2011-05-20 11:46:52 +00:00
self.$input.val(self.options.value);
cursor(pos[0], self.options.value.length);
self.options.changeOnKeypress && that.triggerEvent({
change: {value: self.options.value}
});
2014-12-17 14:57:04 +00:00
if (self.options.autocompleteSelectUpdate) {
2014-12-17 15:43:43 +00:00
self.$autocompleteMenu.updatePosition();
2014-12-17 14:57:04 +00:00
}
2011-05-20 11:46:52 +00:00
//}
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-21 13:42:47 +00:00
self.$input.show().focus();
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() {
cancelAutocomplete();
self.submitted = true;
2011-04-22 22:03:10 +00:00
self.$input.blur();
self.submitted = false;
//self.options.type == 'textarea' && self.$input.blur();
that.triggerEvent('submit', {value: self.options.value});
2011-04-22 22:03:10 +00:00
}
function validate() {
self.options.validate(self.options.value, function(data) {
that.triggerEvent('validate', data);
});
}
2012-05-21 10:38:18 +00:00
/*@
blurInput <f> blurInput
@*/
2011-05-16 18:05:29 +00:00
that.blurInput = function() {
self.$input.blur();
return that;
};
2012-05-21 10:38:18 +00:00
/*@
clearInput <f> clearInput
@*/
2011-05-17 07:19:30 +00:00
that.clearInput = function() {
clear();
return that;
2011-12-30 15:06:55 +00:00
};
2011-05-17 07:19:30 +00:00
/*@
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;
2011-04-22 22:03:10 +00:00
self.$input.focus();
cursor(start, stop);
2011-04-22 22:03:10 +00:00
return that;
};
2012-05-21 10:38:18 +00:00
/*@
value <f> get/set value
@*/
2011-12-22 05:52:46 +00:00
// FIXME: deprecate, options are enough
2011-04-22 22:03:10 +00:00
that.value = function() {
2011-12-22 05:52:46 +00:00
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]});
2011-05-24 11:43:27 +00:00
}
2011-04-22 22:03:10 +00:00
};
return that;
};