[CLOSED] Mask Plugin

  1. #1

    [CLOSED] Mask Plugin

    Hi,

    In another threads with another forum user, I finally get a mask plugin with changeMask functionallity, but it seems that ir doesn't works fine.
    When I change the mask the first time (usually in load) goes right, but if i change it more times; goes wrong. I supose that an internal parameter with wrong data; but I don't know which is it.

    I've a simplified example with the two steps to follow.
    But I can attach it to the thread.

    Can I send you to a mail address or something?
    Last edited by Daniil; May 21, 2011 at 11:04 AM. Reason: [CLOSED]
  2. #2
    Hi,

    Quote Originally Posted by softmachine2011 View Post
    I've a simplified example with the two steps to follow.
    But I can attach it to the thread.
    Please clarify why?

    Also, in the future, please post links you are referring to. I guess you mean the following thread, right?
    http://forums.ext.net/showthread.php?11541
  3. #3
    Hi,

    Yes this is the old thread, but my finally code is this one:

    JS Plugin code
    // $Id: InputTextMask.js 100 2008-02-26 09:38:02Z bobbicat71 $
    
    Ext.namespace('Ext.ux.netbox');
    
    Ext.ux.netbox.InputTextMask = function (config) {
    
        var mask = config.mask;
        var clearWhenInvalid = config.clearWhenInvalid;
        var relinkEvents = config.relinkEvents;
    
        if (clearWhenInvalid === undefined) {
            this.clearWhenInvalid = true;
        } else {
            this.clearWhenInvalid = clearWhenInvalid;
        }
        
        //Añadido para controlar la vinculación de eventos en el cambio de mascara
        if (relinkEvents === undefined) {
            this.relinkEvents = true;
        } else {
            this.relinkEvents = relinkEvents;
        }
    
        this.rawMask = mask;
        this.viewMask = '';
        this.maskArray = [];
        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 = '';
                } else {
                    regexp += mask.charAt(i);
                }
            } else if (mask.charAt(i) == 'X') {
                regexp += 'X';
                this.viewMask += '_';
            } else if (mask.charAt(i) == 'L' || mask.charAt(i) == 'l' || mask.charAt(i) == 'A' || mask.charAt(i) == '9' || mask.charAt(i) == '?') {
                this.viewMask += '_';
                this.maskArray[mai] = mask.charAt(i);
                mai++;
            } else {
                this.viewMask += mask.charAt(i);
                this.maskArray[mai] = RegExp.escape(mask.charAt(i));
                mai++;
            }
        }
    
        this.specialChars = this.viewMask.replace(/(L|l|9|A|_|X|\?)/g, '');
    };
    
    Ext.ux.netbox.InputTextMask.prototype = {
    
        init: function (field) {
            this.field = field;
    
            if (field.rendered) {
                this.assignEl();
            } else {
                field.on('render', this.assignEl, this);
            }
    
            field.on('blur', this.removeValueWhenInvalid, this);
            field.on('focus', this.processMaskFocus, this);
        },
    
        assignEl: function () {
            this.inputTextElement = this.field.getEl().dom;
    
            if (this.relinkEvents == true) {
                this.field.getEl().on('keypress', this.processKeyPress, this);
                this.field.getEl().on('keydown', this.processKeyDown, this);
    
                if (Ext.isSafari || Ext.isIE) {
                    this.field.getEl().on('paste', this.startTask, this);
                    this.field.getEl().on('cut', this.startTask, this);
                }
                if (Ext.isGecko || Ext.isOpera) {
                    this.field.getEl().on('mousedown', this.setPreviousValue, this);
                }
                if (Ext.isGecko) {
                    this.field.getEl().on('input', this.onInput, this);
                }
                if (Ext.isOpera) {
                    this.field.getEl().on('input', this.onInputOpera, this);
                }
            }
        },
        onInput: function () {
    
            this.startTask(false);
        },
        onInputOpera: function () {
    
            if (!this.prevValueOpera) {
                this.startTask(false);
            } else {
                this.manageBackspaceAndDeleteOpera();
            }
        },
    
        manageBackspaceAndDeleteOpera: function () {
    
            this.inputTextElement.value = this.prevValueOpera.cursorPos.previousValue;
            this.manageTheText(this.prevValueOpera.keycode, this.prevValueOpera.cursorPos);
            this.prevValueOpera = null;
        },
    
        setPreviousValue: function (event) {
    
            this.oldCursorPos = this.getCursorPosition();
        },
    
        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 if (maskKey == '?') {
                return keycode.pressedKey.match(/[\-\+\ ]/);
            } else if (maskKey) {
                return (keycode.pressedKey.match(new RegExp(maskKey)));
            }
            return (null);
        },
    
        removeValueWhenInvalid: function () {
            //Rellenado de los espacios vacios, con los valores por defecto (en números)
            for (var i = 0; i < this.inputTextElement.value.length; i++) {
                if (this.rawMask[i] == '9' && this.inputTextElement.value[i] == '_') {
                    this.inputTextElement.value = this.inputTextElement.value.substring(0, i) + '0' + this.inputTextElement.value.substring(i + 1);
                }
    
                if (this.rawMask[i] == '?' && this.inputTextElement.value[i] == '_') {
                    this.inputTextElement.value = this.inputTextElement.value.substring(0, i) + ' ' + this.inputTextElement.value.substring(i + 1);
                }
            }
    
            this.field.validate();
    
            if (this.clearWhenInvalid && this.inputTextElement.value.indexOf('_') > -1) {
                this.inputTextElement.value = '';
            }
        },
    
        managePaste: function () {
    
            if (this.oldCursorPos === null) {
                return;
            }
            var valuePasted = this.inputTextElement.value.substring(this.oldCursorPos.start, this.inputTextElement.value.length - (this.oldCursorPos.previousValue.length - this.oldCursorPos.end));
            if (this.oldCursorPos.start < this.oldCursorPos.end) {//there is selection...
                this.oldCursorPos.previousValue =
                this.oldCursorPos.previousValue.substring(0, this.oldCursorPos.start) +
                this.viewMask.substring(this.oldCursorPos.start, this.oldCursorPos.end) +
                this.oldCursorPos.previousValue.substring(this.oldCursorPos.end, this.oldCursorPos.previousValue.length);
                valuePasted = valuePasted.substr(0, this.oldCursorPos.end - this.oldCursorPos.start);
            }
            this.inputTextElement.value = this.oldCursorPos.previousValue;
            keycode = { unicode: '',
                isShiftPressed: false,
                isTab: false,
                isBackspace: false,
                isLeftOrRightArrow: false,
                isDelete: false,
                pressedKey: ''
            };
            var charOk = false;
            for (var i = 0; i < valuePasted.length; i++) {
                keycode.pressedKey = valuePasted.substr(i, 1);
                keycode.unicode = valuePasted.charCodeAt(i);
                this.oldCursorPos = this.skipMaskCharacters(keycode, this.oldCursorPos);
                if (this.oldCursorPos === false) {
                    break;
                }
                if (this.injectValue(keycode, this.oldCursorPos)) {
                    charOk = true;
                    this.moveCursorToPosition(keycode, this.oldCursorPos);
                    this.oldCursorPos.previousValue = this.inputTextElement.value;
                    this.oldCursorPos.start = this.oldCursorPos.start + 1;
                }
            }
            if (!charOk && this.oldCursorPos !== false) {
                this.moveCursorToPosition(null, this.oldCursorPos);
            }
            this.oldCursorPos = null;
        },
    
        processKeyDown: function (e) {
            this.processMaskFormatting(e, 'keydown');
        },
    
        processKeyPress: function (e) {
            this.processMaskFormatting(e, 'keypress');
        },
    
        startTask: function (setOldCursor) {
    
            if (this.task === undefined) {
                this.task = new Ext.util.DelayedTask(this.managePaste, this);
            }
            if (setOldCursor !== false) {
                this.oldCursorPos = this.getCursorPosition();
            }
            this.task.delay(0);
        },
    
        skipMaskCharacters: function (keycode, cursorPos) {
            if (cursorPos.start != cursorPos.end && (keycode.isDelete || keycode.isBackspace)) {
                return (cursorPos);
            }
            while (this.specialChars.match(RegExp.escape(this.viewMask.charAt(((keycode.isBackspace) ? cursorPos.start - 1 : cursorPos.start))))) {
                if (keycode.isBackspace) {
                    cursorPos.dec();
                } else {
                    cursorPos.inc();
                }
                if (cursorPos.start >= cursorPos.previousValue.length || cursorPos.start < 0) {
                    return false;
                }
            }
            return (cursorPos);
        },
    
        isManagedByKeyDown: function (keycode) {
            if (keycode.isDelete || keycode.isBackspace) {
                return (true);
            }
            return (false);
        },
    
        processMaskFormatting: function (e, type) {
            this.oldCursorPos = null;
            var cursorPos = this.getCursorPosition();
            var keycode = this.getKeyCode(e, type);
            if (keycode.unicode === 0) {//?? sometimes on Safari
                return;
            }
            if ((keycode.unicode == 67 || keycode.unicode == 99) && e.ctrlKey) {//Ctrl+c, let's the browser manage it!
                return;
            }
            if ((keycode.unicode == 88 || keycode.unicode == 120) && e.ctrlKey) {//Ctrl+x, manage paste
                this.startTask();
                return;
            }
            if ((keycode.unicode == 86 || keycode.unicode == 118) && e.ctrlKey) {//Ctrl+v, manage paste....
                this.startTask();
                return;
            }
            if ((keycode.isBackspace || keycode.isDelete) && Ext.isOpera) {
                this.prevValueOpera = { cursorPos: cursorPos, keycode: keycode };
                return;
            }
            if (type == 'keydown' && !this.isManagedByKeyDown(keycode)) {
                return true;
            }
            if (type == 'keypress' && this.isManagedByKeyDown(keycode)) {
                return true;
            }
            if (this.handleEventBubble(e, keycode, type)) {
                return true;
            }
            return (this.manageTheText(keycode, cursorPos));
        },
    
        manageTheText: function (keycode, cursorPos) {
    
            if (this.inputTextElement.value.length === 0) {
                this.inputTextElement.value = this.viewMask;
            }
            cursorPos = this.skipMaskCharacters(keycode, cursorPos);
            if (cursorPos === false) {
                return false;
            }
            if (this.injectValue(keycode, cursorPos)) {
                this.moveCursorToPosition(keycode, cursorPos);
            }
            return (false);
        },
    
        processMaskFocus: function () {
            if (this.inputTextElement.value.length === 0) {
                var cursorPos = this.getCursorPosition();
                this.inputTextElement.value = this.viewMask;
                this.moveCursorToPosition(null, cursorPos);
            }
        },
    
        isManagedByBrowser: function (keyEvent, keycode, type) {
    
            if (((type == 'keypress' && keyEvent.charCode === 0) ||
                type == 'keydown') && (keycode.unicode == Ext.EventObject.TAB ||
                keycode.unicode == Ext.EventObject.RETURN ||
                keycode.unicode == Ext.EventObject.ENTER ||
                keycode.unicode == Ext.EventObject.SHIFT ||
                keycode.unicode == Ext.EventObject.CONTROL ||
                keycode.unicode == Ext.EventObject.ESC ||
                keycode.unicode == Ext.EventObject.PAGEUP ||
                keycode.unicode == Ext.EventObject.PAGEDOWN ||
                keycode.unicode == Ext.EventObject.END ||
                keycode.unicode == Ext.EventObject.HOME ||
                keycode.unicode == Ext.EventObject.LEFT ||
                keycode.unicode == Ext.EventObject.UP ||
                keycode.unicode == Ext.EventObject.RIGHT ||
                keycode.unicode == Ext.EventObject.DOWN)) {
                return (true);
            }
            return (false);
        },
    
        handleEventBubble: function (keyEvent, keycode, type) {
    
            try {
                if (keycode && this.isManagedByBrowser(keyEvent, keycode, type)) {
                    return true;
                }
                keyEvent.stopEvent();
                return false;
            } catch (e) {
                alert(e.message);
            }
        },
    
        getCursorPosition: function () {
            var s, e, r;
            if (this.inputTextElement.createTextRange) {
                r = document.selection.createRange().duplicate();
                r.moveEnd('character', this.inputTextElement.value.length);
                if (r.text === '') {
                    s = this.inputTextElement.value.length;
                } else {
                    s = this.inputTextElement.value.lastIndexOf(r.text);
                }
                r = document.selection.createRange().duplicate();
                r.moveStart('character', -this.inputTextElement.value.length);
                e = r.text.length;
            } else {
                s = this.inputTextElement.selectionStart;
                e = this.inputTextElement.selectionEnd;
            }
            return this.CursorPosition(s, e, r, this.inputTextElement.value);
        },
    
        moveCursorToPosition: function (keycode, cursorPosition) {
            var p = (!keycode || (keycode && keycode.isBackspace)) ? cursorPosition.start : cursorPosition.start + 1;
            if (this.inputTextElement.createTextRange) {
                cursorPosition.range.move('character', p);
                cursorPosition.range.select();
            } else {
                this.inputTextElement.selectionStart = p;
                this.inputTextElement.selectionEnd = p;
            }
        },
    
        injectValue: function (keycode, cursorPosition) {
            if (!keycode.isDelete && keycode.unicode == cursorPosition.previousValue.charCodeAt(cursorPosition.start)) {
                return true;
            }
            var key;
            if (!keycode.isDelete && !keycode.isBackspace) {
                key = this.getValidatedKey(keycode, cursorPosition);
            } else {
                if (cursorPosition.start == cursorPosition.end) {
                    key = '_';
                    if (keycode.isBackspace) {
                        cursorPosition.dec();
                    }
                } else {
                    key = this.viewMask.substring(cursorPosition.start, cursorPosition.end);
                }
            }
            if (key) {
                this.inputTextElement.value = cursorPosition.previousValue.substring(0, cursorPosition.start);
                this.inputTextElement.value += key + cursorPosition.previousValue.substring(cursorPosition.start + key.length, cursorPosition.previousValue.length);
                return true;
            }
            return false;
        },
    
        getKeyCode: function (onKeyDownEvent, type) {
            var keycode = {};
            keycode.unicode = onKeyDownEvent.getKey();
            keycode.isShiftPressed = onKeyDownEvent.shiftKey;
    
            keycode.isDelete = ((onKeyDownEvent.getKey() == Ext.EventObject.DELETE && type == 'keydown') || (type == 'keypress' && onKeyDownEvent.charCode === 0 && onKeyDownEvent.keyCode == Ext.EventObject.DELETE)) ? true : false;
            keycode.isTab = (onKeyDownEvent.getKey() == Ext.EventObject.TAB) ? true : false;
            keycode.isBackspace = (onKeyDownEvent.getKey() == Ext.EventObject.BACKSPACE) ? true : false;
            keycode.isLeftOrRightArrow = (onKeyDownEvent.getKey() == Ext.EventObject.LEFT || onKeyDownEvent.getKey() == Ext.EventObject.RIGHT) ? true : false;
            keycode.pressedKey = String.fromCharCode(keycode.unicode);
            return (keycode);
        },
    
        CursorPosition: function (start, end, range, previousValue) {
            var cursorPosition = {};
            cursorPosition.start = isNaN(start) ? 0 : start;
            cursorPosition.end = isNaN(end) ? 0 : end;
            cursorPosition.range = range;
            cursorPosition.previousValue = previousValue;
            cursorPosition.inc = function () { cursorPosition.start++; cursorPosition.end++; };
            cursorPosition.dec = function () { cursorPosition.start--; cursorPosition.end--; };
            return (cursorPosition);
        },
    
        changeMask: function (newMask, newClearWhenInvalid, relinkEvents) {
            var maskPlugin = this;
            var field = maskPlugin.field;
    
            field.un('render', maskPlugin.assignEl, maskPlugin);
            field.un('blur', maskPlugin.removeValueWhenInvalid, maskPlugin);
            field.un('focus', maskPlugin.processMaskFocus, maskPlugin);
    
            field.getEl().un('keypress', maskPlugin.processKeyPress, maskPlugin);
            field.getEl().un('keydown', maskPlugin.processKeyDown, maskPlugin);
    
            if (Ext.isSafari || Ext.isIE) {
                field.getEl().un('paste', maskPlugin.startTask, maskPlugin);
                field.getEl().un('cut', maskPlugin.startTask, maskPlugin);
            }
    
            if (Ext.isGecko || Ext.isOpera) {
                field.getEl().un('mousedown', maskPlugin.setPreviousValue, maskPlugin);
            }
    
            if (Ext.isGecko) {
                field.getEl().un('input', maskPlugin.onInput, maskPlugin);
            }
    
            if (Ext.isOpera) {
                field.getEl().un('input', maskPlugin.onInputOpera, maskPlugin);
            }
    
            if (Ext.isArray(field.plugins)) {
                field.plugins.pop(maskPlugin);
                field.plugins.pop(maskPlugin);
            }
            else {
                field.plugins = null;
            }
            
            maskPlugin = new Ext.ux.netbox.InputTextMask({ mask: newMask, clearWhenInvalid: newClearWhenInvalid, relinkEvents: relinkEvents });
    
            this.viewMask = maskPlugin.viewMask;
            this.rawMask = maskPlugin.rawMask;
            this.maskArray = maskPlugin.maskArray;
    
            if (Ext.isArray(field.plugins)) {
                field.plugins.push(maskPlugin);
            }
            else {
                field.plugins = maskPlugin;
            }
    
            maskPlugin.init(field);
        }
    };
    
    Ext.applyIf(RegExp, {
        escape: function (str) {
            return str.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
        }
    });
    
    Ext.ux.InputTextMask = Ext.ux.netbox.InputTextMask;
    The parameter relinkEvents is used for linkEvents of the plugin only the first time, because in next mask change the event fires more than one time (number of mask changes). If you don't understand it use, set this parameter to true all the time in changeMask function and type it the textfield.

    Solving this...; when you have a mask with length of 10 characters and change it to a mask of 5 characters for example, anything inside the plugin has wrong data or something because you could tip 10 characters. In otherwise if the first mask has 5 characters and the second one has 7 charactrs you only can type 5 characters.

    The first mask length persists or something similar.
    I've a sample project if you request it.

    But with this piece of code and the plugin you can reproduce it fine. Only change the reference to the plugin

    ASP code
    <%@ Page Language="C#" %>
    
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    <%@ Register Assembly="Softmachine.Millenium.Presentation.Web.MVC.Client" Namespace="Ext.Net"
        TagPrefix="Custom" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Ext.Net Example</title>
    </head>
    <body>
        <form runat="server">
        <ext:ResourceManager runat="server" />
        <ext:Viewport ID="ViewPort1" runat="server" Layout="fit">
            <Items>
                <ext:Panel ID="ToolbarsPanel" runat="server" Border="false" Layout="Fit">
                    <TopBar>
                        <ext:Toolbar ID="Toolbar1" runat="server">
                            <Items>
                                <ext:Button runat="server" Text="First Action">
                                    <Listeners>
                                        <Click Handler="mask.changeMask('99-99-99',false,true); textBox.setValue('33-12-45');" />
                                    </Listeners>
                                </ext:Button>
                                <ext:Button runat="server" Text="Rest of Actions">
                                    <Listeners>
                                        <Click Handler="mask.changeMask('99,999',false,false); textBox.setValue('12,456');" />
                                    </Listeners>
                                </ext:Button>
                            </Items>
                        </ext:Toolbar>
                    </TopBar>
                    <Items>
                        <ext:Panel runat="server" AutoHeight="true">
                            <Items>
                                <ext:TextField ID="textBox" runat="server">
                                    <Plugins>
                                        <Custom:InputTextMask ID="mask" runat="server" Mask="999:999:999" ClearWhenInvalid="false" />
                                    </Plugins>
                                </ext:TextField>
                            </Items>
                        </ext:Panel>
                    </Items>
                </ext:Panel>
            </Items>
        </ext:Viewport>
        </form>
    </body>
    </html>
    .NET plugin code
    using System.ComponentModel;
    using System.Collections.Generic;
    using System.Web.UI;
    using System.Xml.Serialization;
    using Newtonsoft.Json;
    
    [assembly: WebResource("Softmachine.Millenium.Presentation.Web.MVC.Client.Plugins.InputTextMask.js", "text/javascript")]
    
    namespace Ext.Net
    {
        [ToolboxItem(true)]
        [ToolboxData("<{0}:InputTextMask runat=\"server\" />")]
        [Description("InputTextMask plugin used for mask/regexp operations")]
        public class InputTextMask : Plugin
        {
            /// <summary>
            /// 
            /// </summary>
            [Description("")]
            protected override List<ResourceItem> Resources
            {
                get
                {
                    List<ResourceItem> baseList = base.Resources;
    
                    baseList.Capacity += 1;
    
                    ClientScriptItem _csi = new ClientScriptItem(typeof(InputTextMask), "Softmachine.Millenium.Presentation.Web.MVC.Client.Plugins.InputTextMask.js", "/ux/plugins/inputtextmask/inputtextmask.js");
    
                    baseList.Add(_csi);
    
                    return baseList;
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            [Category("0. About")]
            [Description("")]
            public override string InstanceOf
            {
                get
                {
                    return "Ext.ux.netbox.InputTextMask";
                }
            }
    
            [ConfigOption]
            [DefaultValue("")]
            [NotifyParentProperty(true)]
            [Description("The InputTextMask")]
            public string Mask
            {
                get
                {
                    object obj = this.ViewState["Mask"];
                    return obj == null ? "" : (string)obj;
                }
                set
                {
                    this.ViewState["Mask"] = value;
                }
            }
    
            [ConfigOption]
            [DefaultValue(true)]
            [NotifyParentProperty(true)]
            [Description("True to clear the mask when the field blurs and the text is invalid. Optional, default is true")]
            public bool ClearWhenInvalid
            {
                get
                {
                    object obj = this.ViewState["ClearWhenInvalid"];
                    return obj == null ? true : (bool)obj;
                }
                set
                {
                    this.ViewState["ClearWhenInvalid"] = value;
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            [Browsable(false)]
            [EditorBrowsable(EditorBrowsableState.Never)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            [XmlIgnore]
            [JsonIgnore]
            public override ConfigOptionsCollection ConfigOptions
            {
                get
                {
                    ConfigOptionsCollection list = base.ConfigOptions;
    
                    list.Add("mask", new ConfigOption("mask", null, string.Empty, this.Mask));
                    list.Add("clearWhenInvalid", new ConfigOption("clearWhenInvalid", null, true, this.ClearWhenInvalid));
    
                    return list;
                }
            }
        }
    }
  4. #4
    Just to isolate the problem, I've tried your js code without the custom control.

    It appears that the problem is not in the custom control. The example below reproduces the same problem that you described. Can you confirm?

    Example
    <%@ Page Language="C#" %>
     
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"      
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Ext.Net Example</title>
        
        <ext:ResourcePlaceHolder runat="server" Mode="ScriptFiles" />
        <script type="text/javascript" src="resources\js\InputTextMask.js"></script>
    </head>
    <body>
        <form runat="server">
        <ext:ResourceManager runat="server" />
        <ext:Viewport runat="server" Layout="fit">
            <Items>
                <ext:Panel runat="server" Layout="Fit">
                    <TopBar>
                        <ext:Toolbar runat="server">
                            <Items>
                                <ext:Button runat="server" Text="First Action">
                                    <Listeners>
                                        <Click Handler="mask.changeMask('99-99-99',false,true); textBox.setValue('33-12-45');" />
                                    </Listeners>
                                </ext:Button>
                                <ext:Button runat="server" Text="Rest of Actions">
                                    <Listeners>
                                        <Click Handler="mask.changeMask('99,999',false,false); textBox.setValue('12,456');" />
                                    </Listeners>
                                </ext:Button>
                            </Items>
                        </ext:Toolbar>
                    </TopBar>
                    <Items>
                        <ext:Panel runat="server">
                            <Items>
                                <ext:TextField ID="textBox" runat="server">
                                    <Plugins>
                                        <ext:GenericPlugin 
                                            ID="mask" 
                                            InstanceName="Ext.ux.netbox.InputTextMask" 
                                            Mask="999:999:999" 
                                            ClearWhenInvalid="false" />
                                    </Plugins>
                                </ext:TextField>
                            </Items>
                        </ext:Panel>
                    </Items>
                </ext:Panel>
            </Items>
        </ext:Viewport>
        </form>
    </body>
    </html>
  5. #5
    Hi,

    Yes your sample has the same behavior.
    The problem is inside the JS. I think it could be the parameter named inputTextElement, but not sure.
  6. #6
    Well, it looks like you are right and some field.getEl() listener(-s) is not removed, but I have not discovered yet.

    You could double check the changeMask() method that I suggested in that thread:) Generally, we don't officially support UX project.

    But I can suggest you the solution.

    1. Remove your workaround with 'relinkEvents'.
    2. Place
    field.getEl().purgeAllListeners();
    into the changeMask() to ensure that all listeners are removed().
    3. You can remove all
    field.getEl().un(...)
    appearances for the .changeMask(), because .purgeAllListener() makes all job.
  7. #7
    Hi Daniil!

    Your workaround works so fine.
    Finally I removed 'relinkEvents' like you tell me and all field.getEl().un(...) and field.un(...) from changeMask method too, and I replace it for purgeAllListeners.

    I paste the final code if anyone would require this functionallity too.

    // $Id: InputTextMask.js 100 2008-02-26 09:38:02Z bobbicat71 $
    
    Ext.namespace('Ext.ux.netbox');
    
    Ext.ux.netbox.InputTextMask = function (config) {
    
        var mask = config.mask;
        var clearWhenInvalid = config.clearWhenInvalid;
    
        if (clearWhenInvalid === undefined) {
            this.clearWhenInvalid = true;
        } else {
            this.clearWhenInvalid = clearWhenInvalid;
        }
    
        this.rawMask = mask;
        this.viewMask = '';
        this.maskArray = [];
        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 = '';
                } else {
                    regexp += mask.charAt(i);
                }
            } else if (mask.charAt(i) == 'X') {
                regexp += 'X';
                this.viewMask += '_';
            } else if (mask.charAt(i) == 'L' || mask.charAt(i) == 'l' || mask.charAt(i) == 'A' || mask.charAt(i) == '9' || mask.charAt(i) == '?') {
                this.viewMask += '_';
                this.maskArray[mai] = mask.charAt(i);
                mai++;
            } else {
                this.viewMask += mask.charAt(i);
                this.maskArray[mai] = RegExp.escape(mask.charAt(i));
                mai++;
            }
        }
    
        this.specialChars = this.viewMask.replace(/(L|l|9|A|_|X|\?)/g, '');
    };
    
    Ext.ux.netbox.InputTextMask.prototype = {
    
        init: function (field) {
            this.field = field;
    
            if (field.rendered) {
                this.assignEl();
            } else {
                field.on('render', this.assignEl, this);
            }
    
            field.on('blur', this.removeValueWhenInvalid, this);
            field.on('focus', this.processMaskFocus, this);
        },
    
        assignEl: function () {
            this.inputTextElement = this.field.getEl().dom;
            this.field.getEl().on('keypress', this.processKeyPress, this);
            this.field.getEl().on('keydown', this.processKeyDown, this);
    
            if (Ext.isSafari || Ext.isIE) {
                this.field.getEl().on('paste', this.startTask, this);
                this.field.getEl().on('cut', this.startTask, this);
            }
            if (Ext.isGecko || Ext.isOpera) {
                this.field.getEl().on('mousedown', this.setPreviousValue, this);
            }
            if (Ext.isGecko) {
                this.field.getEl().on('input', this.onInput, this);
            }
            if (Ext.isOpera) {
                this.field.getEl().on('input', this.onInputOpera, this);
            }
        },
        onInput: function () {
    
            this.startTask(false);
        },
        onInputOpera: function () {
    
            if (!this.prevValueOpera) {
                this.startTask(false);
            } else {
                this.manageBackspaceAndDeleteOpera();
            }
        },
    
        manageBackspaceAndDeleteOpera: function () {
    
            this.inputTextElement.value = this.prevValueOpera.cursorPos.previousValue;
            this.manageTheText(this.prevValueOpera.keycode, this.prevValueOpera.cursorPos);
            this.prevValueOpera = null;
        },
    
        setPreviousValue: function (event) {
    
            this.oldCursorPos = this.getCursorPosition();
        },
    
        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 if (maskKey == '?') {
                return keycode.pressedKey.match(/[\-\+\ ]/);
            } else if (maskKey) {
                return (keycode.pressedKey.match(new RegExp(maskKey)));
            }
            return (null);
        },
    
        removeValueWhenInvalid: function () {
            //Rellenado de los espacios vacios, con los valores por defecto (en números)
            for (var i = 0; i < this.inputTextElement.value.length; i++) {
                if (this.rawMask[i] == '9' && this.inputTextElement.value[i] == '_') {
                    this.inputTextElement.value = this.inputTextElement.value.substring(0, i) + '0' + this.inputTextElement.value.substring(i + 1);
                }
    
                if (this.rawMask[i] == '?' && this.inputTextElement.value[i] == '_') {
                    this.inputTextElement.value = this.inputTextElement.value.substring(0, i) + ' ' + this.inputTextElement.value.substring(i + 1);
                }
            }
    
            this.field.validate();
    
            if (this.clearWhenInvalid && this.inputTextElement.value.indexOf('_') > -1) {
                this.inputTextElement.value = '';
            }
        },
    
        managePaste: function () {
    
            if (this.oldCursorPos === null) {
                return;
            }
            var valuePasted = this.inputTextElement.value.substring(this.oldCursorPos.start, this.inputTextElement.value.length - (this.oldCursorPos.previousValue.length - this.oldCursorPos.end));
            if (this.oldCursorPos.start < this.oldCursorPos.end) {//there is selection...
                this.oldCursorPos.previousValue =
                this.oldCursorPos.previousValue.substring(0, this.oldCursorPos.start) +
                this.viewMask.substring(this.oldCursorPos.start, this.oldCursorPos.end) +
                this.oldCursorPos.previousValue.substring(this.oldCursorPos.end, this.oldCursorPos.previousValue.length);
                valuePasted = valuePasted.substr(0, this.oldCursorPos.end - this.oldCursorPos.start);
            }
            this.inputTextElement.value = this.oldCursorPos.previousValue;
            keycode = { unicode: '',
                isShiftPressed: false,
                isTab: false,
                isBackspace: false,
                isLeftOrRightArrow: false,
                isDelete: false,
                pressedKey: ''
            };
            var charOk = false;
            for (var i = 0; i < valuePasted.length; i++) {
                keycode.pressedKey = valuePasted.substr(i, 1);
                keycode.unicode = valuePasted.charCodeAt(i);
                this.oldCursorPos = this.skipMaskCharacters(keycode, this.oldCursorPos);
                if (this.oldCursorPos === false) {
                    break;
                }
                if (this.injectValue(keycode, this.oldCursorPos)) {
                    charOk = true;
                    this.moveCursorToPosition(keycode, this.oldCursorPos);
                    this.oldCursorPos.previousValue = this.inputTextElement.value;
                    this.oldCursorPos.start = this.oldCursorPos.start + 1;
                }
            }
            if (!charOk && this.oldCursorPos !== false) {
                this.moveCursorToPosition(null, this.oldCursorPos);
            }
            this.oldCursorPos = null;
        },
    
        processKeyDown: function (e) {
            this.processMaskFormatting(e, 'keydown');
        },
    
        processKeyPress: function (e) {
            this.processMaskFormatting(e, 'keypress');
        },
    
        startTask: function (setOldCursor) {
    
            if (this.task === undefined) {
                this.task = new Ext.util.DelayedTask(this.managePaste, this);
            }
            if (setOldCursor !== false) {
                this.oldCursorPos = this.getCursorPosition();
            }
            this.task.delay(0);
        },
    
        skipMaskCharacters: function (keycode, cursorPos) {
            if (cursorPos.start != cursorPos.end && (keycode.isDelete || keycode.isBackspace)) {
                return (cursorPos);
            }
            while (this.specialChars.match(RegExp.escape(this.viewMask.charAt(((keycode.isBackspace) ? cursorPos.start - 1 : cursorPos.start))))) {
                if (keycode.isBackspace) {
                    cursorPos.dec();
                } else {
                    cursorPos.inc();
                }
                if (cursorPos.start >= cursorPos.previousValue.length || cursorPos.start < 0) {
                    return false;
                }
            }
            return (cursorPos);
        },
    
        isManagedByKeyDown: function (keycode) {
            if (keycode.isDelete || keycode.isBackspace) {
                return (true);
            }
            return (false);
        },
    
        processMaskFormatting: function (e, type) {
            this.oldCursorPos = null;
            var cursorPos = this.getCursorPosition();
            var keycode = this.getKeyCode(e, type);
            if (keycode.unicode === 0) {//?? sometimes on Safari
                return;
            }
            if ((keycode.unicode == 67 || keycode.unicode == 99) && e.ctrlKey) {//Ctrl+c, let's the browser manage it!
                return;
            }
            if ((keycode.unicode == 88 || keycode.unicode == 120) && e.ctrlKey) {//Ctrl+x, manage paste
                this.startTask();
                return;
            }
            if ((keycode.unicode == 86 || keycode.unicode == 118) && e.ctrlKey) {//Ctrl+v, manage paste....
                this.startTask();
                return;
            }
            if ((keycode.isBackspace || keycode.isDelete) && Ext.isOpera) {
                this.prevValueOpera = { cursorPos: cursorPos, keycode: keycode };
                return;
            }
            if (type == 'keydown' && !this.isManagedByKeyDown(keycode)) {
                return true;
            }
            if (type == 'keypress' && this.isManagedByKeyDown(keycode)) {
                return true;
            }
            if (this.handleEventBubble(e, keycode, type)) {
                return true;
            }
            return (this.manageTheText(keycode, cursorPos));
        },
    
        manageTheText: function (keycode, cursorPos) {
    
            if (this.inputTextElement.value.length === 0) {
                this.inputTextElement.value = this.viewMask;
            }
            cursorPos = this.skipMaskCharacters(keycode, cursorPos);
            if (cursorPos === false) {
                return false;
            }
            if (this.injectValue(keycode, cursorPos)) {
                this.moveCursorToPosition(keycode, cursorPos);
            }
            return (false);
        },
    
        processMaskFocus: function () {
            if (this.inputTextElement.value.length === 0) {
                var cursorPos = this.getCursorPosition();
                this.inputTextElement.value = this.viewMask;
                this.moveCursorToPosition(null, cursorPos);
            }
        },
    
        isManagedByBrowser: function (keyEvent, keycode, type) {
    
            if (((type == 'keypress' && keyEvent.charCode === 0) ||
                type == 'keydown') && (keycode.unicode == Ext.EventObject.TAB ||
                keycode.unicode == Ext.EventObject.RETURN ||
                keycode.unicode == Ext.EventObject.ENTER ||
                keycode.unicode == Ext.EventObject.SHIFT ||
                keycode.unicode == Ext.EventObject.CONTROL ||
                keycode.unicode == Ext.EventObject.ESC ||
                keycode.unicode == Ext.EventObject.PAGEUP ||
                keycode.unicode == Ext.EventObject.PAGEDOWN ||
                keycode.unicode == Ext.EventObject.END ||
                keycode.unicode == Ext.EventObject.HOME ||
                keycode.unicode == Ext.EventObject.LEFT ||
                keycode.unicode == Ext.EventObject.UP ||
                keycode.unicode == Ext.EventObject.RIGHT ||
                keycode.unicode == Ext.EventObject.DOWN)) {
                return (true);
            }
            return (false);
        },
    
        handleEventBubble: function (keyEvent, keycode, type) {
    
            try {
                if (keycode && this.isManagedByBrowser(keyEvent, keycode, type)) {
                    return true;
                }
                keyEvent.stopEvent();
                return false;
            } catch (e) {
                alert(e.message);
            }
        },
    
        getCursorPosition: function () {
            var s, e, r;
            if (this.inputTextElement.createTextRange) {
                r = document.selection.createRange().duplicate();
                r.moveEnd('character', this.inputTextElement.value.length);
                if (r.text === '') {
                    s = this.inputTextElement.value.length;
                } else {
                    s = this.inputTextElement.value.lastIndexOf(r.text);
                }
                r = document.selection.createRange().duplicate();
                r.moveStart('character', -this.inputTextElement.value.length);
                e = r.text.length;
            } else {
                s = this.inputTextElement.selectionStart;
                e = this.inputTextElement.selectionEnd;
            }
            return this.CursorPosition(s, e, r, this.inputTextElement.value);
        },
    
        moveCursorToPosition: function (keycode, cursorPosition) {
            var p = (!keycode || (keycode && keycode.isBackspace)) ? cursorPosition.start : cursorPosition.start + 1;
            if (this.inputTextElement.createTextRange) {
                cursorPosition.range.move('character', p);
                cursorPosition.range.select();
            } else {
                this.inputTextElement.selectionStart = p;
                this.inputTextElement.selectionEnd = p;
            }
        },
    
        injectValue: function (keycode, cursorPosition) {
            if (!keycode.isDelete && keycode.unicode == cursorPosition.previousValue.charCodeAt(cursorPosition.start)) {
                return true;
            }
            var key;
            if (!keycode.isDelete && !keycode.isBackspace) {
                key = this.getValidatedKey(keycode, cursorPosition);
            } else {
                if (cursorPosition.start == cursorPosition.end) {
                    key = '_';
                    if (keycode.isBackspace) {
                        cursorPosition.dec();
                    }
                } else {
                    key = this.viewMask.substring(cursorPosition.start, cursorPosition.end);
                }
            }
            if (key) {
                this.inputTextElement.value = cursorPosition.previousValue.substring(0, cursorPosition.start);
                this.inputTextElement.value += key + cursorPosition.previousValue.substring(cursorPosition.start + key.length, cursorPosition.previousValue.length);
                return true;
            }
            return false;
        },
    
        getKeyCode: function (onKeyDownEvent, type) {
            var keycode = {};
            keycode.unicode = onKeyDownEvent.getKey();
            keycode.isShiftPressed = onKeyDownEvent.shiftKey;
    
            keycode.isDelete = ((onKeyDownEvent.getKey() == Ext.EventObject.DELETE && type == 'keydown') || (type == 'keypress' && onKeyDownEvent.charCode === 0 && onKeyDownEvent.keyCode == Ext.EventObject.DELETE)) ? true : false;
            keycode.isTab = (onKeyDownEvent.getKey() == Ext.EventObject.TAB) ? true : false;
            keycode.isBackspace = (onKeyDownEvent.getKey() == Ext.EventObject.BACKSPACE) ? true : false;
            keycode.isLeftOrRightArrow = (onKeyDownEvent.getKey() == Ext.EventObject.LEFT || onKeyDownEvent.getKey() == Ext.EventObject.RIGHT) ? true : false;
            keycode.pressedKey = String.fromCharCode(keycode.unicode);
            return (keycode);
        },
    
        CursorPosition: function (start, end, range, previousValue) {
            var cursorPosition = {};
            cursorPosition.start = isNaN(start) ? 0 : start;
            cursorPosition.end = isNaN(end) ? 0 : end;
            cursorPosition.range = range;
            cursorPosition.previousValue = previousValue;
            cursorPosition.inc = function () { cursorPosition.start++; cursorPosition.end++; };
            cursorPosition.dec = function () { cursorPosition.start--; cursorPosition.end--; };
            return (cursorPosition);
        },
    
        changeMask: function (newMask, newClearWhenInvalid) {
            var maskPlugin = this;
            var field = maskPlugin.field;
    
            field.getEl().purgeAllListeners();
    
            if (Ext.isArray(field.plugins)) {
                field.plugins.pop(maskPlugin);
            }
            else {
                field.plugins = null;
            }
    
            maskPlugin = new Ext.ux.netbox.InputTextMask({ mask: newMask, clearWhenInvalid: newClearWhenInvalid });
    
            this.viewMask = maskPlugin.viewMask;
            this.rawMask = maskPlugin.rawMask;
            this.maskArray = maskPlugin.maskArray;
    
            if (Ext.isArray(field.plugins)) {
                field.plugins.push(maskPlugin);
            }
            else {
                field.plugins = maskPlugin;
            }
    
            maskPlugin.init(field);
        }
    };
    
    Ext.applyIf(RegExp, {
        escape: function (str) {
            return str.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
        }
    });
    
    Ext.ux.InputTextMask = Ext.ux.netbox.InputTextMask;
    Thanks!
  8. #8
    Hi again,

    It seems that purgeAllListeners remove some listeners/events.
    I have an onBlur directEvent linked to the textfield with the mask, but when I use the changeMask function this directEvent doesn't fire more.

    The direct event is so simple like that:
    <ext:TextField ID="RedondeoValorTexto" runat="server" FieldLabel="<%$ AppRecursos : sm_txt_valorRedondeo %>" Width="140">
    			<Plugins>													<SoftMachine:InputTextMask ID="RedondeoValorMask" runat="server" Mask="?999:99" ClearWhenInvalid="false" />			</Plugins>
                                                                    <DirectEvents>
                                                                        <Blur Url='<%# this.Page.GetRouteUrl("Presencia", new { controller = "Contadores", action= "ValidarFormato" }) %>' Method="POST" AutoDataBind="true"
                                                                            Success="Millenium.Presencia.Contadores.Details.onTextFieldBlurCallBack(result, this);"
                                                                            Failure="Millenium.Presencia.Contadores.Details.onTextFieldBlurCallBack(result, this);">
                                                                            <ExtraParams>
                                                                                <ext:Parameter Name="formatoValue" Value="#{Formato}.getValue()" Mode="Raw" />
    																            <ext:Parameter Name="rebose" Value="#{ReboseValorTexto}.getValue()" Mode="Raw" />
                                                                                <ext:Parameter Name="redondeo" Value="#{RedondeoValorTexto}.getValue()" Mode="Raw" />
                                                                            </ExtraParams>
                                                                            <EventMask MinDelay="500" Msg="<%$ AppRecursos : sm_txt_validandoValores %>" Target="Page" RemoveMask="true" ShowMask="true" />
                                                                        </Blur>
                                                           </DirectEvents>
    </ext:TextField>
    Any idea?
  9. #9
    No, .purgeListeners() for .getEl() doesn't remove listeners for a field itself, but it removes also all internal .getEl() listeners.

    Call .initEvents() for a field after .purgeAllLIsteners().

    Example
    <%@ Page Language="C#" %>
    
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    
    <script runat="server">
        protected void TestDirectEventHandler(object sender, DirectEventArgs e)
        {
            X.Msg.Alert("DirectEvent", "Hello from Server!").Show();
        }
    </script>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Ext.Net Example</title>
    </head>
    <body>
        <form runat="server">
        <ext:ResourceManager runat="server" />
        <ext:TextField ID="TextField1" runat="server">
            <DirectEvents>
                <Blur OnEvent="TestDirectEventHandler" />
            </DirectEvents>
        </ext:TextField>
        <ext:Button runat="server" Text="Test">
            <Listeners>
                <Click Handler="TextField1.getEl().purgeAllListeners(); TextField1.initEvents();" />
            </Listeners>
        </ext:Button>
        </form>
    </body>
    </html>
  10. #10
    Thanks Daniil.

    It works correctly now adding this line.

    You can mark it as solved.

Similar Threads

  1. [CLOSED] Call plugin
    By ppettigrew in forum 1.x Legacy Premium Help
    Replies: 7
    Last Post: Jun 14, 2012, 8:14 PM
  2. [CLOSED] Plugin
    By boris in forum 1.x Legacy Premium Help
    Replies: 6
    Last Post: Nov 23, 2011, 1:10 PM
  3. [CLOSED] EditableGrid Plugin bug
    By Pablo_Azevedo in forum 1.x Legacy Premium Help
    Replies: 9
    Last Post: Oct 24, 2011, 7:31 PM
  4. [CLOSED] [1.0] BottomTitle Plugin Bug
    By Timothy in forum 1.x Legacy Premium Help
    Replies: 4
    Last Post: Apr 18, 2011, 10:19 AM
  5. [CLOSED] GridFilters Plugin and MVC
    By Stefanaccio in forum 1.x Legacy Premium Help
    Replies: 10
    Last Post: Jan 06, 2011, 3:23 PM

Tags for this Thread

Posting Permissions