On 6/7/07, William Hoover <[EMAIL PROTECTED]> wrote:
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 was thinking we'd add a hook to the general client-side validation
framework that, during onload (and after PPR replacement, if new
fields are added or rewritten), would let  the validators have a pass
at the DOM.

There's a few big advantages to doing this in JS, instead of in rendering:
- There's no I/O cost of sending across the JS
- You're writing JS in JS, instead of writing Java that outputs JS,
which is always awkward IMO
- The Renderer doesn't need to change

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!:

Dynamically attaching the onkeydown/blur when you receive the focus definitely
seems like a good idea.

I guess my first suggestion for the JS is that some comments would be nice!

-- Adam


/**
* 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:[EMAIL PROTECTED]
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 <[EMAIL PROTECTED]> 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 <[EMAIL PROTECTED]> 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:[EMAIL PROTECTED]
> > 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 <[EMAIL PROTECTED]> 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:[EMAIL PROTECTED]
> > > 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 <[EMAIL PROTECTED]> 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?
> > >
> > >
> >
> >
>


Reply via email to