'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 autocompleteSelectOffset <o|{left: 4, top: 0}> Offset of autocomplete menu autocompleteSelectSubmit <b> if true, submit input on menu selection autocompleteSelectUpdate <b> if true, update menu position on keypress 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 readonly <b> if true, is readonly 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, autocompleteSelectOffset: {left: 4, top: 0}, autocompleteSelectSubmit: false, autocompleteSelectUpdate: 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: '', readonly: false, 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 == 'readonly') { self.$input.attr({readonly: value}); } else if (key == 'type') { // jQuery does not allow update via attr({type: value}) due to IE 6 bug self.$input[0].type = value } 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', style: self.options.style, 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(-1); } }) .appendTo(that), Ox.Button({ overlap: 'left', title: 'right', type: 'image' }) .css({float: 'right'}) .on({ 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(); 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, readonly: self.options.readonly, 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({ click: function() { self.options.disabled && that.gainFocus(); }, input: event => { /* if (!self.options.autocomplete && Ox.contains(['text', 'password'], self.options.type)) { change(event) } */ }, 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(); that.gainFocus(); 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 && 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; 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); } } 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(); if (self.options.autocompleteSelectUpdate) { self.$autocompleteMenu.updatePosition(); } } else { self.$autocompleteMenu .unbindEvent('select') .hideMenu(); that.gainFocus(); 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 * 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: self.options.autocompleteSelectOffset }) .addClass('OxAutocompleteMenu OxKeyboardFocus') .bindEvent({ click: clickMenu, key_enter: function() { if (self.$autocompleteMenu.is(':visible')) { self.$autocompleteMenu.hideMenu(); that.gainFocus(); 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} }); if (self.options.autocompleteSelectUpdate) { self.$autocompleteMenu.updatePosition(); } //} } 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; };