From: Mark Wells Date: Sun, 8 Feb 2015 04:53:09 +0000 (-0800) Subject: paste into masked input fields, #26012 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=ffe05d16b30b3f8a044c87c28faa716052b5e8fe paste into masked input fields, #26012 --- diff --git a/httemplate/elements/masked_input_1.1.js b/httemplate/elements/masked_input_1.1.js deleted file mode 100644 index 05efa779b..000000000 --- a/httemplate/elements/masked_input_1.1.js +++ /dev/null @@ -1,195 +0,0 @@ -/*********************************************************************** - Masked Input version 1.1 -************************************************************************ -Author: Kendall Conrad -Home page: http://www.angelwatt.com/coding/masked_input.php -Created: 2008-12-16 -Modified: 2010-04-14 -Description: -License: This work is licensed under a Creative Commons Attribution-Share Alike - 3.0 United States License http://creativecommons.org/licenses/by-sa/3.0/us/ - -Argument pieces: -- elm: [req] text input node to apply the mask on -- format: [req] string format for the mask -- allowed: [opt, '0123456789'] string with chars allowed to be typed -- sep: [opt, '\/:-'] string of char(s) used as separators in mask -- typeon: [opt, '_YMDhms'] string of chars in mask that can be typed on -- onbadkey: [opt, null] function to run when user types a unallowed key -- badkeywait: [opt, 0] used with onbadkey. Indicates how long (in ms) to lock - text input for onbadkey function to run -***********************************************************************/ -function MaskedInput(args) -{ - if (args['elm'] === null || args['format'] === null) { return false; } - var el = args['elm'], - format = args['format'], - allowed = args['allowed'] || '0123456789', - sep = args['separator'] || '\/:-', - open = args['typeon'] || '_YMDhms', - onbadkey = args['onbadkey'] || function(){}, - badwait = args['badkeywait'] || 0; - - var locked = false, hold = 0; - el.value = format; - // Assign events - el.onkeydown = KeyHandlerDown; // - el.onkeypress = KeyHandlerPress; // add event handlers to element - el.onkeyup = KeyHandlerUp; // - - function GetKey(code) - { - code = code || window.event, ch = ''; - var keyCode = code.which, evt = code.type; - if (keyCode == null) { keyCode = code.keyCode; } - if (keyCode === null) { return ''; } // no key, no play - // deal with special keys - switch (keyCode) { - case 8: ch = 'bksp'; break; - case 46: // handle del and . both being 46 - ch = (evt == 'keydown') ? 'del' : '.'; break; - case 16: ch = 'shift'; break;//shift - case 0:/*CRAP*/ case 9:/*TAB*/ case 13:/*ENTER*/ - ch = 'etc'; break; - case 37: case 38: case 39: case 40: // arrow keys - ch = (!code.shiftKey && - (code.charCode != 39 && code.charCode !== undefined)) ? - 'etc' : String.fromCharCode(keyCode); - break; - // default to thinking it's a character or digit - default: ch = String.fromCharCode(keyCode); - } - return ch; - } - function KeyHandlerDown(e) - { - e = e || event; - if (locked) { return false; } - var key = GetKey(e); - if (el.value == '') { el.value = format; SetTextCursor(el,0); } - // Only do update for bksp del - if (key == 'bksp' || key == 'del') { Update(key); return false; } - else if (key == 'etc' || key == 'shift') { return true; } - else { return true; } - } - function KeyHandlerPress(e) - { - e = e || event; - if (locked) { return false; } - var key = GetKey(e); - // Check if modifier key is being pressed; command - if (key=='etc' || e.metaKey || e.ctrlKey || e.altKey) { return true; } - if (key != 'bksp' && key != 'del' && key != 'etc' && key != 'shift') { - if (!GoodOnes(key)) { return false; } - return Update(key); - } - else { return false; } - } - function KeyHandlerUp(e) { hold = 0; } - function Update(key) - { - var p = GetTextCursor(el), c = el.value, val = ''; - // Handle keys now - switch (true) { - case (allowed.indexOf(key) != -1): - if (++p > format.length) { return false; } // if text csor at end - // Handle cases where user places csor before separator - while (sep.indexOf(c.charAt(p-1)) != -1 && p <= format.length) { p++; } - val = c.substr(0, p-1) + key + c.substr(p); - // Move csor up a spot if next char is a separator char - if (allowed.indexOf(c.charAt(p)) == -1 - && open.indexOf(c.charAt(p)) == -1) { p++; } - break; - case (key=='bksp'): // backspace - if (--p < 0) return false; // at start of field - // If previous char is a separator, move a little more - while (allowed.indexOf(c.charAt(p)) == -1 - && open.indexOf(c.charAt(p)) == -1 - && p > 1) { p--; } - val = c.substr(0, p) + format.substr(p,1) + c.substr(p+1); - break; - case (key=='del'): // forward delete - if (p >= c.length) { return false; } // at end of field - // If next char is a separator and not the end of the text field - while (sep.indexOf(c.charAt(p)) != -1 - && c.charAt(p) != '') { p++; } - val = c.substr(0, p) + format.substr(p,1) + c.substr(p+1); - p++; // Move position forward - break; - case (key=='etc'): return true; // Catch other allowed chars - default: return false; // Ignore the rest - } - el.value = ''; // blank it first (Firefox issue) - el.value = val; // put updated value back in - SetTextCursor(el, p); // Set the text cursor - return false; - } - function GetTextCursor(node) - { - try { - if (node.selectionStart >= 0) { return node.selectionStart; } - else if (document.selection) {// IE - var ntxt = node.value; // getting starting text - var rng = document.selection.createRange(); - rng.text = '|%|'; - var start = node.value.indexOf('|%|'); - rng.moveStart('character', -3); - rng.text = ''; - // put starting text back in, - // fixes issue if all text was highlighted - node.value = ntxt; - return start; - } return -1; - } catch(e) { return false; } - } - function SetTextCursor(node, pos) - { - try { - if (node.selectionStart) { - node.focus(); - node.setSelectionRange(pos,pos); - } - else if (node.createTextRange) { // IE - var rng = node.createTextRange(); - rng.move('character', pos); - rng.select(); - } - } catch(e) { return false; } - } - function GoodOnes(k) - { - if (allowed.indexOf(k) == -1 && k!='bksp' && k!='del' && k!='etc') { - var p = GetTextCursor(el); // Need to ensure cursor position not lost - locked = true; onbadkey(); - // Hold lock long enough for onbadkey function to run - setTimeout(function(){locked=false; SetTextCursor(el,p);}, badwait); - return false; - } return true; - } - function resetField() { - el.value = format; - } - function setAllowed(a) { - allowed = a; - resetField(); - } - function setFormat(f) { - format = f; - resetField(); - } - function setSeparator(s) { - sep = s; - resetField(); - } - function setTypeon(t) { - open = t; - resetField(); - } - return { - resetField:resetField, - setAllowed:setAllowed, - setFormat:setFormat, - setSeparator:setSeparator, - setTypeon:setTypeon - } -} diff --git a/httemplate/elements/masked_input_1.3.js b/httemplate/elements/masked_input_1.3.js new file mode 100644 index 000000000..54e38ac86 --- /dev/null +++ b/httemplate/elements/masked_input_1.3.js @@ -0,0 +1,462 @@ +/** + * AW Masked Input + * @version 1.3 + * @author Kendall Conrad + * @url http://www.angelwatt.com/coding/masked_input.php + * @created 2008-12-16 + * @modified 2013-08-19 + * @license This work is licensed under a Creative Commons + * Attribution-Share Alike 3.0 United States License + * http://creativecommons.org/licenses/by-sa/3.0/us/ + * + * @param scope The object to attach MaskedInput to. + */ +(function(scope) { + 'use strict'; + + /** + * MaskedInput takes many possible arguments described below. + * Note: req = required, opt = optional + * @param {object} args { + * -elm [req] text input node to apply the mask on + * -format [req] string format for the mask + * -allowed [opt, '0123456789'] string with chars allowed to be typed + * -sep [opt, '\/:-'] string of char(s) used as separators in mask + * -typeon [opt, '_YMDhms'] string of chars in mask that can be typed on + * -onfilled [opt, null] function to run when the format is filled in + * -onbadkey [opt, null] function to run when user types a unallowed key + * -badkeywait [opt, 0] used with onbadkey. Indicates how long (in ms) + * to lock text input for onbadkey function to run + * -preserve [opt, true] whether to preserve existing text in + * field during init. + * } + * @returns MaskedInput + */ + scope.MaskedInput = function(args) { + // Ensure passing in valid argument + if (!args || !args.elm || !args.format) { + return null; + } + // Ensure use of 'new' + if (!(this instanceof scope.MaskedInput)) { + return new scope.MaskedInput(args); + } + // Initialize variables + var self = this, + el = args.elm, + format = args.format, + allowed = args.allowed || '0123456789', + sep = args.separator || '\/:-', + open = args.typeon || '_YMDhms', + onbadkey = args.onbadkey || function() {}, + onfilled = args.onfilled || function() {}, + badwait = args.badkeywait || 0, + preserve = args.hasOwnProperty('preserve') ? !!args.preserve : true, + // ---- + enabled = true, + locked = false, + startText = format, + /** + * Add events to objects. + */ + evtAdd = (function() { + if (window.addEventListener) { + return function(obj, type, fx, capture) { + obj.addEventListener(type, fx, + (capture === undefined) ? false : capture); + }; + } + if (window.attachEvent) { + return function(obj, type, fx) { + obj.attachEvent('on' + type, fx); + }; + } + return function(obj, type, fx) { + obj['on' + type] = fx; + }; + }()), + /** + * Checks whether the format has been completely filled out. + * @return boolean if all typeon chars have been filled. + */ + isFilled = function() { + // Check if any typeon characters are left + // Work from end of string as it's usually last filled + for (var a = el.value.length - 1; a >= 0; a--) { + // Check against each typeon character + for (var c = 0, d = open.length; c < d; c++) { + // If one matches we don't need to check anymore + if (el.value[a] === open[c]) { + return false; + } + } + } + return true; + }, + /** + * Gets the current position of the text cursor in a text field. + * @param node a input or textarea HTML node. + * @return int text cursor position index, or -1 if there was a problem. + */ + getTextCursor = function(node) { + try { + node.focus(); + if (node.selectionStart >= 0) { + return node.selectionStart; + } + if (document.selection) {// IE + var rng = document.selection.createRange(); + return -rng.moveStart('character', -node.value.length); + } + return -1; + } + catch (e) { + return -1; + } + }, + /** + * Sets the text cursor in a text field to a specific position. + * @param node a input or textarea HTML node. + * @param pos int of the position to be placed. + * @return boolean true is successful, false otherwise. + */ + setTextCursor = function(node, pos) { + try { + if (node.selectionStart) { + node.focus(); + node.setSelectionRange(pos, pos); + } + else if (node.createTextRange) { // IE + var rng = node.createTextRange(); + rng.move('character', pos); + rng.select(); + } + } + catch (e) { + return false; + } + return true; + }, + /** + * Gets the keyboard input in usable way. + * @param code integer character code + * @return string representing character code + */ + getKey = function(code) { + code = code || window.event; + var ch = '', + keyCode = code.which, + evt = code.type; + if (keyCode === undefined || keyCode === null) { + keyCode = code.keyCode; + } + // no key, no play + if (keyCode === undefined || keyCode === null) { + return ''; + } + // deal with special keys + switch (keyCode) { + case 8: + ch = 'bksp'; + break; + case 46: // handle del and . both being 46 + ch = (evt === 'keydown') ? 'del' : '.'; + break; + case 16: + ch = 'shift'; + break; + case 0: /*CRAP*/ + case 9: /*TAB*/ + case 13:/*ENTER*/ + ch = 'etc'; + break; + case 37: + case 38: + case 39: + case 40: // arrow keys + ch = (!code.shiftKey && + (code.charCode !== 39 && code.charCode !== undefined)) ? + 'etc' : String.fromCharCode(keyCode); + break; + // default to thinking it's a character or digit + default: + ch = String.fromCharCode(keyCode); + break; + } + return ch; + }, + /** + * Stop the event propogation chain. + * @param evt Event to stop + * @param ret boolean, used for IE to prevent default event + */ + stopEvent = function(evt, ret) { + // Stop default behavior the standard way + if (evt.preventDefault) { + evt.preventDefault(); + } + // Then there's IE + evt.returnValue = ret || false; + }, + /** + * Updates the text field with the given key. + * @param key string keyboard input. + */ + update = function(key) { + var p = getTextCursor(el), + c = el.value, + val = '', + cond = true; + // Handle keys now + switch (cond) { + // Allowed characters + case (allowed.indexOf(key) !== -1): + p = p + 1; + // if text cursor at end + if (p > format.length) { + return false; + } + // Handle cases where user places cursor before separator + while (sep.indexOf(c.charAt(p - 1)) !== -1 && p <= format.length) { + p = p + 1; + } + val = c.substr(0, p - 1) + key + c.substr(p); + // Move csor up a spot if next char is a separator char + if (allowed.indexOf(c.charAt(p)) === -1 + && open.indexOf(c.charAt(p)) === -1) { + p = p + 1; + } + break; + case (key === 'bksp'): // backspace + p = p - 1; + // at start of field + if (p < 0) { + return false; + } + // If previous char is a separator, move a little more + while (allowed.indexOf(c.charAt(p)) === -1 + && open.indexOf(c.charAt(p)) === -1 + && p > 1) { + p = p - 1; + } + val = c.substr(0, p) + format.substr(p, 1) + c.substr(p + 1); + break; + case (key === 'del'): // forward delete + // at end of field + if (p >= c.length) { + return false; + } + // If next char is a separator and not the end of the text field + while (sep.indexOf(c.charAt(p)) !== -1 + && c.charAt(p) !== '') { + p = p + 1; + } + val = c.substr(0, p) + format.substr(p, 1) + c.substr(p + 1); + p = p + 1; // Move position forward + break; + case (key === 'etc'): + // Catch other allowed chars + return true; + default: + return false; // Ignore the rest + } + el.value = ''; // blank it first (Firefox issue) + el.value = val; // put updated value back in + setTextCursor(el, p); // Set the text cursor + return false; + }, + /** + * Returns whether or not a given input is valid for the mask. + * @param k string of character to check. + * @return bool true if it's a valid character. + */ + goodOnes = function(k) { + // if not in allowed list, or invisible key action + if (allowed.indexOf(k) === -1 && k !== 'bksp' && k !== 'del' && k !== 'etc') { + // Need to ensure cursor position not lost + var p = getTextCursor(el); + locked = true; + onbadkey(k); + // Hold lock long enough for onbadkey function to run + setTimeout(function() { + locked = false; + setTextCursor(el, p); + }, badwait); + return false; + } + return true; + }, + /** + * Handles the key down events. + * @param e Event + */ + keyHandlerDown = function(e) { + if (!enabled) { + return true; + } + if (locked) { + stopEvent(e); + return false; + } + e = e || event; + var key = getKey(e); + // Stop copy and paste + if ((e.metaKey || e.ctrlKey) && (key === 'X' || key === 'V')) { + stopEvent(e); + return false; + } + // Allow for OS commands + if (e.metaKey || e.ctrlKey) { + return true; + } + if (el.value === '') { + el.value = format; + setTextCursor(el, 0); + } + // Only do update for bksp del + if (key === 'bksp' || key === 'del') { + update(key); + stopEvent(e); + return false; + } + return true; + }, + /** + * Handles the key press events. + * @param e Event + */ + keyHandlerPress = function(e) { + if (!enabled) { + return true; + } + if (locked) { + stopEvent(e); + return false; + } + e = e || event; + var key = getKey(e); + // Check if modifier key is being pressed; command + if (key === 'etc' || e.metaKey || e.ctrlKey || e.altKey) { + return true; + } + if (key !== 'bksp' && key !== 'del' && key !== 'shift') { + if (!goodOnes(key)) { + stopEvent(e); + return false; + } + if (update(key)) { + if (isFilled()) { + onfilled(); + } + stopEvent(e, true); + return true; + } + if (isFilled()) { + onfilled(); + } + stopEvent(e); + return false; + } + return false; + }, + /** + * Initialize the object. + */ + init = function() { + // Check if an input or textarea tag was passed in + if (!el.tagName || (el.tagName.toUpperCase() !== 'INPUT' + && el.tagName.toUpperCase() !== 'TEXTAREA')) { + return null; + } + // Only place formatted text in field when not preserving + // text or it's empty. + if (!preserve || el.value === '') { + el.value = format; + } + // Assign events + evtAdd(el, 'keydown', function(e) { + keyHandlerDown(e); + }); + evtAdd(el, 'keypress', function(e) { + keyHandlerPress(e); + }); + // Let us set the initial text state when focused + evtAdd(el, 'focus', function() { + startText = el.value; + }); + // Handle onChange event manually + evtAdd(el, 'blur', function() { + if (el.value !== startText && el.onchange) { + el.onchange(); + } + }); + return self; + }; + + /** + * Resets the text field so just the format is present. + */ + self.resetField = function() { + el.value = format; + }; + + /** + * Set the allowed characters that can be used in the mask. + * @param a string of characters that can be used. + */ + self.setAllowed = function(a) { + allowed = a; + self.resetField(); + }; + + /** + * The format to be used in the mask. + * @param f string of the format. + */ + self.setFormat = function(f) { + format = f; + self.resetField(); + }; + + /** + * Set the characters to be used as separators. + * @param s string representing the separator characters. + */ + self.setSeparator = function(s) { + sep = s; + self.resetField(); + }; + + /** + * Set the characters that the user will be typing over. + * @param t string representing the characters that will be typed over. + */ + self.setTypeon = function(t) { + open = t; + self.resetField(); + }; + + /** + * Sets whether the mask is active. + */ + self.setEnabled = function(enable) { + enabled = enable; + }; + + /** + * Local change for Freeside: sets the content of the field, + * respecting formatting rules + */ + self.setValue = function(value) { + self.resetField(); + setTextCursor(el, 0); + var i = 0; // index in value + while (i < value.length && !isFilled()) { + update(value[i]); + i++; + } + } + + return init(); + }; +}(window)); diff --git a/httemplate/elements/tr-input-mask.html b/httemplate/elements/tr-input-mask.html index 19942b58b..fdd20962d 100644 --- a/httemplate/elements/tr-input-mask.html +++ b/httemplate/elements/tr-input-mask.html @@ -1,52 +1,62 @@ % if ( !$init ) { - % $init++; % } <& /elements/tr-input-text.html, id => $id, @_ &>