summaryrefslogtreecommitdiff
path: root/httemplate/elements
diff options
context:
space:
mode:
authorMark Wells <mark@freeside.biz>2015-02-07 20:53:09 -0800
committerMark Wells <mark@freeside.biz>2015-02-07 20:53:17 -0800
commitffe05d16b30b3f8a044c87c28faa716052b5e8fe (patch)
tree9c0ed511c18a6a2054e64d6ae25bc4c5dfb10d57 /httemplate/elements
parent1b1e3ed155e9888eaa4e6f28d5c55dca817426cb (diff)
paste into masked input fields, #26012
Diffstat (limited to 'httemplate/elements')
-rw-r--r--httemplate/elements/masked_input_1.1.js195
-rw-r--r--httemplate/elements/masked_input_1.3.js462
-rw-r--r--httemplate/elements/tr-input-mask.html68
3 files changed, 501 insertions, 224 deletions
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 ) {
-<script type="text/javascript" src="<%$p%>elements/masked_input_1.1.js">
+<script type="text/javascript" src="<%$p%>elements/masked_input_1.3.js">
</script>
% $init++;
% }
<& /elements/tr-input-text.html, id => $id, @_ &>
<script type="text/javascript">
<&| /elements/onload.js &>
-MaskedInput({
- elm: document.getElementById('<%$id%>'),
+var el = document.getElementById('<%$id%>');
+el.MaskedInput = window.MaskedInput({
+ elm: el,
format: '<% $opt{format} %>',
<% $opt{allowed} ? "allowed: '$opt{allowed}'," : '' %>
<% $opt{typeon} ? "typeon: '$opt{typeon}'," : '' %>
});
-document.getElementById('<%$id%>').value = <% $value |js_string %>;
+el.value = <% $value |js_string %>;
% if ( $clipboard_hack ) {
-var t = document.getElementById('<% $id %>');
var container = document.getElementById('<%$id%>_clipboard');
-var KeyHandlerDown = t.onkeydown
-t.onkeydown = function(e) {
- if (typeof(e) == 'undefined') {
- // ie8 hack
- e = event;
- }
+var KeyDownHandler = function(e) {
+ e = e || event; // IE8
// intercept ctrl-c and ctrl-x
// and cmd-c and cmd-x on mac
- // when text is selected
if ( ( e.ctrlKey || e.metaKey ) ) {
- // do the dance
- var separators = /[\\/:-]/g;
- var s = t.value.substr(t.selectionStart, t.selectionEnd);
- if ( s ) {
- container.value = s.replace(separators, '');
- container.previous = t;
- container.focus();
- container.select();
- return true;
+ // grab contents of the field, strip out delimiters and copy to container,
+ // and select its contents so that the next "ctrl-c" copies it
+
+ el.select(); // just a visual hint to the user
+ var reject = /[^A-Za-z0-9]/g;
+ container.value = el.value.replace(reject, '');
+ container.focus();
+ container.select();
+ // don't confuse the maskedinput key handlers by letting them see this
+ if (e.stopImmediatePropagation) {
+ e.stopImmediatePropagation();
+ } else {
+ // IE8
+ e.returnValue = false;
+ e.cancelBubble = true;
}
}
- return KeyHandlerDown.call(t, e);
};
-container.onkeyup = function(e) {
- if ( container.previous ) {
- setTimeout(function() {
- //container.previous.value = container.value;
- container.previous.focus();
- }, 10);
- }
+var KeyUpHandler = function(e) {
+ e = e || event;
+ setTimeout( function() { el.focus() } , 10);
return true;
+};
+var PasteHandler = function(e) {
+ setTimeout( function() {
+ el.MaskedInput.setValue(container.value);
+ }, 10);
+};
+if ( el.addEventListener ) {
+ el.addEventListener('keydown', KeyDownHandler);
+ container.addEventListener('keyup', KeyUpHandler);
+ container.addEventListener('paste', PasteHandler);
+} else if ( el.attachEvent ) {
+ el.attachEvent('onkeydown', KeyDownHandler);
+ container.attachEvent('onkeyup', KeyUpHandler);
+ container.attachEvent('onpaste', PasteHandler);
}
% } # clipboard hack
</&>