myfaces-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "William Hoover" <whoo...@nemours.org>
Subject RE: [Trinidad] Input Text Format That Uses A Mask
Date Thu, 07 Jun 2007 12:01:57 GMT
Question: If it was to use a converter/validator at what point would it append onfocus (and
any other events needed)? With a component it is appended when it is rendered. If it were
a validator it would have to add events to the component its validating. Is this a good practice?

I'm in the process of implementing the previously suggested JS event attachments so the only
event that has to be explicitly attached is the focus. Correct me if I'm wrong, but once the
field gains focus it should dynamically attach the onkeydown/blur events to the passed input
text element? Currently it attaches a "MaskType" object to the input text to keep track of
input info such as the last cursor position, the raw mask, the parsed mask, the viewing mask,
and the containing mask validator function. This makes it easier to validate the input content
because the validating method is attached to the input element itself. It also prevents reparsing
of code that isn't necessary. Here is the client script (does not yet have the dynamic event
attachment mentioned above) Suggestions are welcomed!:

/**
* CoreInputTextForamt component script used for mask/regexp operations
*/
var CoreInputTextFormat = {
	processMaskFocus: function(input, mask){
		CoreInputTextFormat.createInputMask(input, mask);
		if(input.value.length == 0){
			var cursorPos = CoreInputTextFormat.getCursorPosition(input, input.value);
			input.value = input.mask.viewMask;
			CoreInputTextFormat.moveCursorToPosition(input, null, cursorPos);
		}
	},
	processMaskBlur: function(input) {
		if(!input.mask.isValidValue(input)){
			input.value = '';
		}
	},
	processMaskKeyCode: function(input, onKeyDownEvent, mask) {
		CoreInputTextFormat.handleEventBubble(onKeyDownEvent);
		CoreInputTextFormat.createInputMask(input, mask);
		var keyCode = new CoreInputTextFormat.KeyCode(onKeyDownEvent);
		if(keyCode.isTab || keyCode.isLeftOrRightArrow){
			return true;
		}
		var v = input.value;
		if(v.length === 0){
			input.value = mask;
		}
		var cursorPos = CoreInputTextFormat.getCursorPosition(input, v);
		if(cursorPos.end == cursorPos.previousValue.length && !keyCode.isBackspace){
			return false;
		}
		while(input.mask.specialChars.match(RegExp.escape(cursorPos.previousValue.charAt(((keyCode.isBackspace)?
cursorPos.start-1: cursorPos.start))))){
			if(keyCode.isBackspace) {
				cursorPos.decStart();
			} else {
				cursorPos.incStart();
			}
			if(cursorPos.start >= cursorPos.previousValue.length || cursorPos.start < 0){
				return false;
			}
		}
		if(keyCode.isBackspace){
			cursorPos.decStart();
		}
		if(CoreInputTextFormat.injectValue(input, keyCode, cursorPos)){
			CoreInputTextFormat.moveCursorToPosition(input, keyCode, cursorPos);
		}
		return false;
	},
	handleEventBubble: function(onKeyDownEvent){
		try {
			onKeyDownEvent.cancelBubble = true;
			if(onKeyDownEvent.stopPropagation){
				onKeyDownEvent.stopPropagation();
			}
		} catch(e) {
			alert(e.message);
		}
	},
	createInputMask: function(input, mask) {
		if(!input.mask || input.mask.rawMask != mask){
			input.mask = new CoreInputTextFormat.MaskType(mask);
		}
	},
	getCursorPosition: function(input, previousValue) {
		var s, e, r;
		if(input.createTextRange){
			r = document.selection.createRange().duplicate();
			r.moveEnd('character', previousValue.length);
			if(r.text === ''){
				s = previousValue.length;
			} else {
				s = previousValue.lastIndexOf(r.text);
			}
			r = document.selection.createRange().duplicate();
			r.moveStart('character', -previousValue.length);
			e = r.text.length;
		} else {
			s = input.selectionStart;
			e = input.selectionEnd;
		}
		return new CoreInputTextFormat.CursorPosition(s, e, r, previousValue);
	},
	moveCursorToPosition: function(input, keyCode, cursorPosition) {
		var p = (!keyCode || (keyCode && keyCode.isBackspace))? cursorPosition.start: cursorPosition.start
+ 1;
		if(input.createTextRange){
			cursorPosition.range.move('character', p);
			cursorPosition.range.select();
		} else {
			input.selectionStart = p;
			input.selectionEnd = p;
		}
	},
	injectValue: function(input, keyCode, cursorPosition) {
		var key = (keyCode.isBackspace)? '_': input.mask.getValidatedKey(keyCode, cursorPosition);
		if(key){
			input.value = cursorPosition.previousValue.substring(0, cursorPosition.start) + key + cursorPosition.previousValue.substring(cursorPosition.start
+ 1, cursorPosition.previousValue.length);
			return true;
		}
		return false;
	},
	MaskType: function(mask) {
		this.lastValidatedKeyCode = null;
		this.rawMask = mask;
		this.viewMask = '';
		this.maskArray = new Array();
		var mai = 0;
		var regexp = '';
		for(var i=0; i<mask.length; i++){
			if(regexp){
				if(regexp == 'X'){
					regexp = '';
				}
				if(mask.charAt(i) == 'X'){
					this.maskArray[mai] = regexp;
					mai++;
					regexp = null;
				} else {
					regexp += mask.charAt(i);
				}
			} else if(mask.charAt(i) == 'X'){
				regexp += 'X';
				this.viewMask += '_';
			} else if(mask.charAt(i) == '9' || mask.charAt(i) == 'L' || mask.charAt(i) == 'l' || mask.charAt(i)
== 'A') {
				this.viewMask += '_';
				this.maskArray[mai] = mask.charAt(i);
				mai++;
			} else {
				this.viewMask += mask.charAt(i);
				this.maskArray[mai] = mask.charAt(i);
				mai++;
			}
		}
		this.specialChars = this.viewMask.replace(/(L|l|9|A|_|X)/g,'');
		this.getValidatedKey = function(keyCode, cursorPosition) {
			var maskKey = this.maskArray[cursorPosition.start];
			if(maskKey == '9'){
				return keyCode.pressedKey.match(/[0-9]/);
			} else if(maskKey == 'L'){
				return (keyCode.pressedKey.match(/[A-Za-z]/))? keyCode.pressedKey.toUpperCase(): null;
			} else if(maskKey == 'l'){
				return (keyCode.pressedKey.match(/[A-Za-z]/))? keyCode.pressedKey.toLowerCase(): null;
			} else {
				if(maskKey == 'A'){
					return keyCode.pressedKey.match(/[A-Za-z0-9]/);
				} else {
					return (this.maskArray[cursorPosition.start].length > 1)? keyCode.pressedKey.match(new
RegExp(maskKey)): null;
				}
			}
		};
		this.isValidValue = function(input){
			return input.value.indexOf('_') <= -1;
		};
	},
	KeyCode: function(onKeyDownEvent) {
		this.onKeyDownEvent = onKeyDownEvent;
		this.unicode = onKeyDownEvent.which? onKeyDownEvent.which: (onKeyDownEvent.keyCode? onKeyDownEvent.keyCode:
(onKeyDownEvent.charCode? onKeyDownEvent.charCode: 0));
		this.isShiftPressed = onKeyDownEvent.shiftKey == false || onKeyDownEvent.shiftKey == true?
onKeyDownEvent.shiftKey: (onKeyDownEvent.modifiers && (onKeyDownEvent.modifiers &
4)); //bitWise AND
		// TODO : need to get cap lock capture for onkeydown event
		//this.isCapLock = ((!this.isShiftPressed && this.unicode >= 65 && this.unicode
<= 90) || (this.unicode >= 97 && this.unicode <= 122 && this.isShiftPressed));
		if(this.unicode >= 96 && this.unicode <= 105) {
			this.unicode -= 48; // handle number keypad
		}
		if(this.unicode >= 65 && this.unicode <= 90 && !this.isShiftPressed){
			this.unicode += 32; // handle uppercase
		}
		this.isTab = (this.unicode == 9)? true: false;
		this.isBackspace = (this.unicode == 8)? true: false;
		this.isLeftOrRightArrow = (this.unicode == 37 || this.unicode == 39)? true: false;
		this.pressedKey = String.fromCharCode(this.unicode);
	},
	CursorPosition: function(start, end, range, previousValue) {
		this.start = isNaN(start)? 0: start;
		this.end = isNaN(end)? 0: end;
		this.range = range;
		this.previousValue = previousValue;
		this.incStart = function(){
			this.start++;
		};
		this.decStart = function(){
			this.start--;
		};
	}
};
// Add escape feature to RegExp object
if(!RegExp.escape) {
	RegExp.escape = function(text){
		var sp;
		if(!arguments.callee.sRE){
			sp=['/','.','*','+','?','|','(',')','[',']','{','}','\\'];
			arguments.callee.sRE = new RegExp('(\\' + sp.join('|\\') + ')','g');
		}
		return text.replace(arguments.callee.sRE, '\\$1');
	};
}

-----Original Message-----
From: Adam Winer [mailto:awiner@gmail.com]
Sent: Wednesday, June 06, 2007 8:26 PM
To: MyFaces Discussion
Subject: Re: [Trinidad] Input Text Format That Uses A Mask


Trinidad already has a validateRegExp validator, FWIW,
which attaches both client-side and server-side validation, but
has no mask functionality.  The Tomahawk validator is just a Java
JSF Validator, no client-side functionality, right?

What this masking thing adds is:
-  a simpler syntax for expressing masking.  For example,
 to do a phone number in regexp, you'd have to write something
like:
  \(\d\d\d\)-\d\d\d-\d\d\d\d
instead of:
  (999)-999-9999
- keydown/blur/focus handling so that the parts of the
  mask that are "fixed" can automatically be inserted
  and skipped over.

onblur, I imagine, is responsible for reporting errors.
That part of this should definitely be hooked into the
existing Trinidad client-side validation - which isn't
just alerts anymore thanks to Dan Robinson, but does
need a final tweak to be onblur instead of onsubmit,
at least for INLINE style.

That leaves keydown/focus.  One way this might be
implemented is having an optional method on the
JS validator instances that, if present, will get called
with the relevant DOM form element.  At that point,
the validator could attach any keydown/blur/focus
handling that it wants to.  The framework would handle
blur in general, the mask validator would just need to
attach keydown/focus.

-- Adam




On 6/6/07, Mike Kienenberger <mkienenb@gmail.com> wrote:
> How does this compare to validateRegExpr in Tomahawk, particularly if
> it becomes a validator instead of a component?
>
> http://myfaces.apache.org/tomahawk/validateRegExpr.html
>
> On 6/6/07, William Hoover <whoover@nemours.org> wrote:
> > Point well taken! The component should extend UIXInput instead and renamed CoreInputMask.
Are you are proposing to change this into a validator or converter instead of a component
extension? If it was to use a converter/validator at what point would it append the JS event
calls (onkeydown, onblur, onfocus)? As of yet it does not address server-side validation,
but it would be fairly easy to implement. Currently, using the example of a (999)999-9999
mask, and an input of (415)555-1212 the bean would see exactly what the user input i.e. (415)555-1212
If a converter/validator was to be used it would be simple enough to strip the mask characters.
I would assume that it would be best to have this as an option because it may be desirable
to maintain the mask?
> >
> >
> >
> > -----Original Message-----
> > From: Adam Winer [mailto:awiner@gmail.com]
> > Sent: Wednesday, June 06, 2007 12:03 PM
> > To: MyFaces Discussion
> > Subject: Re: [Trinidad] Input Text Format That Uses A Mask
> >
> >
> > A few and questions:
> > - Generally speaking, we don't extend CoreInputText, we just
> >   re-extend UIXInput.  The metadata system supports "includes"
> >   for generation, so you don't have to cut-and-paste that much.
> >   One good reason, in this case, is that I assume that this
> >   component doesn't support <TEXTAREA>, just <INPUT> -
> >   so you don't want "rows" or "wrap".
> > - I'd love to see this as a converter or validator tag that can be
> >   added to an ordinary af:inputText.  We'd need a bit of beefing
> >   up of our client-side code JS framework for validators, but
> >   that'd be worthwhile.
> > - What's the server-side model look like?  E.g., when you
> >    have (999)999-9999, does your bean see strings like
> >    (415)555-1212, or do you get 4155551212?  Is there server-side
> >    validation to double-check the mask was applied?
> > - If this is a component, I think CoreInputTextMasked might
> >   be clearer, if the property is named "mask".
> >
> > -- Adam
> >
> >
> >
> > On 6/6/07, William Hoover <whoover@nemours.org> wrote:
> > > Thanks for the info Adam!
> > >
> > > The component (CoreInputTextFormat) logic is fairly simple and could be directly
integrated into the CoreInputText, if desired. It extends CoreInputText and adds two extra
PropertyKeys: "mask" and "clearWhenInvalid". The "mask" attribute designates the pattern for
which will be used to prevent invalid characters at the specified slots. For Example, (999)999-9999
would be displayed as (___)___-____ allowing only numeric values to be entered where underscores
are present (see examples below for a more detailed overview). The "clearWhenInvalid" is an
option to clear the contents (onblur) of the input field when it does not meet the mask pattern
requirements- default is currently true. The only other logic contained in the component is
used to make the JS calls: onblur, onfocus, and onkeydown. The client script is contained
in a namespace called "CoreInputTextFormat" so none of the functions will interfere with other
Trinidad scripts (as you suggested in TRINIDAD-37 it would be nice if we had a Trinidad namespace
that could register component level namespaces!). It does however add a prototype extension
to RegExp to allow RegExp.escape(someText) preventing recompilation of the escape expression.
That is it! I don't think there is a significant amount of code to warrant a CLA (client script
under 200 lines, component logic is trivial). Let me know what your thoughts on all of this!
> > >
> > > Mask Individual Character Usage and Reserved Characters:
> > > 9 - designates only numeric values
> > > L - designates only uppercase letter values
> > > l - designates only lowercase letter values
> > > A - designates only alphanumeric values
> > > X - denotes that a custom client script regular expression is specified
> > > All other characters are assumed to be "special" characters used to mask the
input component
> > >
> > > Examples:
> > > (999)999-9999
> > >         only numeric values can be entered where the character position value
is 9. Parenthesis and dash are non-editable/mask characters.
> > > 99L-ll-X[^A-C]X
> > >         only numeric values for the first two characters, uppercase values
for the third character, lowercase letters for the fifth/sixth characters, and the last character
X[^A-C]X together counts as the eighth character regular expression that would allow all characters
but "A", "B", and "C". Dashes outside the regular expression are non-editable/mask characters.
> > >
> > > -----Original Message-----
> > > From: Adam Winer [mailto:awiner@gmail.com]
> > > Sent: Tuesday, June 05, 2007 7:09 PM
> > > To: MyFaces Discussion
> > > Subject: Re: [Trinidad] Input Text Format That Uses A Mask
> > >
> > >
> > > Roughly speaking, you:
> > > - Create an issue on JIRA
> > > - Attach a patch
> > > - If it's a significant quantity of code, file a CLA
> > >   http://www.apache.org/licenses/icla.txt
> > >
> > > It's also generally a good thing to talk over the
> > > design first.  I'd thing it'd be great if this were part of
> > > the client-side validation code, instead of just its
> > > own code.  I think getting this issue fixed:
> > > http://issues.apache.org/jira/browse/TRINIDAD-37
> > > ... would be important for that.
> > >
> > > I'd love to see this functionality!
> > >
> > > -- Adam
> > >
> > >
> > > On 6/5/07, William Hoover <whoover@nemours.org> wrote:
> > > >
> > > >
> > > >
> > > > Hello all,
> > > > I have created a Trinidad component that allows input text boxes to have
a
> > > > user defined mask for entries on the client (similar to Atlas MaskEdit
> > > > <http://www.fci.com.br/maskedit/MaskEdit/MaskEdit.aspx>). I
> > > > would like to know what the process/procedure is to commit this component
to
> > > > the sandbox?
> > >
> > >
> >
> >
>


Mime
View raw message