1. #1
    Sencha User eirik.lorentsen's Avatar
    Join Date
    Aug 2011
    Location
    Santiago, Chile
    Posts
    27
    Vote Rating
    2
    eirik.lorentsen is on a distinguished road

      1  

    Default Ext.ux.window.Notification

    Ext.ux.window.Notification


    Notification / Toastwindow extension for Ext JS 4.x

    I have created a 4.x rewrite of the Ext.ux.Notification plugin by efattal.

    New improved features include:
    • Multiple managers and notifications stacks
      The static manager object is eliminated completely, allowing notifications to attach to different components using their x and y coordinates to slide in the notifications.
    • All eight corners and edges of document/manager can be used: 'tl', 'tr', 'br', 'bl', 't', 'l', 'b', 'r'.
    • Both x and y axis can be used on corners. Notifications can slide in sideways.
    • When a notification is closed any notifications above it slide down automatically.
    • Hovering the mouse over the notification delays auto closing.
    • Easy customization of css, animations, delays, spacings/padding etc.
    NOTE: If you are using Ext v4.0.7 and have problems getting the 2.1 version to work, try downloading the 2.0 version: http://www.eirik.net/Ext/ux/window/t...otification.js

    Demos and instructions can be found on this page:
    http://www.eirik.net/Ext/ux/window/Notification.html

    Or simply paste this code into a Notification.js file:
    PHP Code:
    /* 
     *    Notification / Toastwindow extension for Ext JS 4.x
     *
     *    Copyright (c) 2011 Eirik Lorentsen (http://www.eirik.net/)
     *
     *    Examples and documentation at: http://www.eirik.net/Ext/ux/window/Notification.html
     *
     *    Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 
     *    and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
     *
     *    Version: 2.1
     *    Last changed date: 2012-08-12
     */

    Ext.define('Ext.ux.window.Notification', {
        
    extend'Ext.window.Window',
        
    alias'widget.uxNotification',

        
    cls'ux-notification-window',
        
    autoClosetrue,
        
    autoHeighttrue,
        
    plainfalse,
        
    draggablefalse,
        
    shadowfalse,
        
    focusExt.emptyFn,

        
    // For alignment and to store array of rendered notifications. Defaults to document if not set.
        
    managernull,

        
    useXAxisfalse,

        
    // Options: br, bl, tr, tl, t, l, b, r
        
    position'br',

        
    // Pixels between each notification
        
    spacing6,

        
    // Pixels from the managers borders to start the first notification
        
    paddingX30,
        
    paddingY10,

        
    slideInAnimation'easeIn',
        
    slideBackAnimation'bounceOut',
        
    slideInDuration1500,
        
    slideBackDuration1000,
        
    hideDuration500,
        
    autoCloseDelay7000,
        
    stickOnClicktrue,
        
    stickWhileHovertrue,

        
    // Private. Do not override!
        
    isHidingfalse,
        
    readyToHidefalse,
        
    destroyAfterHidefalse,
        
    closeOnMouseOutfalse,

        
    // Caching coordinates to be able to align to final position of siblings being animated
        
    xPos0,
        
    yPos0,

        
    statics: {
            
    defaultManager: {
                
    elnull
            
    }
        },

        
    initComponent: function() {
            var 
    me this;

            
    // Backwards compatibility
            
    if (Ext.isDefined(me.corner)) {
                
    me.position me.corner;
            }
            if (
    Ext.isDefined(me.slideDownAnimation)) {
                
    me.slideBackAnimation me.slideDownAnimation;
            }
            if (
    Ext.isDefined(me.autoDestroyDelay)) {
                
    me.autoCloseDelay me.autoDestroyDelay;
            }
            if (
    Ext.isDefined(me.autoHideDelay)) {
                
    me.autoCloseDelay me.autoHideDelay;
            }
            if (
    Ext.isDefined(me.autoHide)) {
                
    me.autoClose me.autoHide;
            }
            if (
    Ext.isDefined(me.slideInDelay)) {
                
    me.slideInDuration me.slideInDelay;
            }
            if (
    Ext.isDefined(me.slideDownDelay)) {
                
    me.slideBackDuration me.slideDownDelay;
            }
            if (
    Ext.isDefined(me.fadeDelay)) {
                
    me.hideDuration me.fadeDelay;
            }

            
    // 'bc', lc', 'rc', 'tc' compatibility
            
    me.position me.position.replace(/c/, '');

            
    me.updateAlignment(me.position);

            
    me.setManager(me.manager);

            
    me.callParent(arguments);
        },

        
    onRender: function() {
            var 
    me this;

            
    me.el.hover(
                function () {
                    
    me.mouseIsOver true;
                },
                function () {
                    
    me.mouseIsOver false;
                    if (
    me.closeOnMouseOut) {
                        
    me.closeOnMouseOut false;
                        
    me.close();
                    }
                },
                
    me
            
    );

            
    this.callParent(arguments);

        },
        
        
    updateAlignment: function (position) {
            var 
    me this;

            switch (
    position) {
                case 
    'br':
                    
    me.paddingFactorX = -1;
                    
    me.paddingFactorY = -1;
                    
    me.siblingAlignment "br-br";
                    if (
    me.useXAxis) {
                        
    me.managerAlignment "bl-br";
                    } else {
                        
    me.managerAlignment "tr-br";
                    }
                    break;
                case 
    'bl':
                    
    me.paddingFactorX 1;
                    
    me.paddingFactorY = -1;
                    
    me.siblingAlignment "bl-bl";
                    if (
    me.useXAxis) {
                        
    me.managerAlignment "br-bl";
                    } else {
                        
    me.managerAlignment "tl-bl";
                    }
                    break;
                case 
    'tr':
                    
    me.paddingFactorX = -1;
                    
    me.paddingFactorY 1;
                    
    me.siblingAlignment "tr-tr";
                    if (
    me.useXAxis) {
                        
    me.managerAlignment "tl-tr";
                    } else {
                        
    me.managerAlignment "br-tr";
                    }
                    break;
                case 
    'tl':
                    
    me.paddingFactorX 1;
                    
    me.paddingFactorY 1;
                    
    me.siblingAlignment "tl-tl";
                    if (
    me.useXAxis) {
                        
    me.managerAlignment "tr-tl";
                    } else {
                        
    me.managerAlignment "bl-tl";
                    }
                    break;
                case 
    'b':
                    
    me.paddingFactorX 0;
                    
    me.paddingFactorY = -1;
                    
    me.siblingAlignment "b-b";
                    
    me.useXAxis 0;
                    
    me.managerAlignment "t-b";
                    break;
                case 
    't':
                    
    me.paddingFactorX 0;
                    
    me.paddingFactorY 1;
                    
    me.siblingAlignment "t-t";
                    
    me.useXAxis 0;
                    
    me.managerAlignment "b-t";
                    break;
                case 
    'l':
                    
    me.paddingFactorX 1;
                    
    me.paddingFactorY 0;
                    
    me.siblingAlignment "l-l";
                    
    me.useXAxis 1;
                    
    me.managerAlignment "r-l";
                    break;
                case 
    'r':
                    
    me.paddingFactorX = -1;
                    
    me.paddingFactorY 0;
                    
    me.siblingAlignment "r-r";
                    
    me.useXAxis 1;
                    
    me.managerAlignment "l-r";
                    break;
                }
        },
        
        
    getXposAlignedToManager: function () {
            var 
    me this;

            var 
    xPos 0;

            
    // Avoid error messages if the manager does not have a dom element
            
    if (me.manager && me.manager.el && me.manager.el.dom) {
                if (!
    me.useXAxis) {
                    
    // Element should already be aligned verticaly
                    
    return me.el.getLeft();
                } else {
                    
    // Using getAnchorXY instead of getTop/getBottom should give a correct placement when document is used
                    // as the manager but is still 0 px high. Before rendering the viewport.
                    
    if (me.position == 'br' || me.position == 'tr' || me.position == 'r') {
                        
    xPos += me.manager.el.getAnchorXY('r')[0];
                        
    xPos -= (me.el.getWidth() + me.paddingX);
                    } else {
                        
    xPos += me.manager.el.getAnchorXY('l')[0];
                        
    xPos += me.paddingX;
                    }
                }
            }

            return 
    xPos;
        },

        
    getYposAlignedToManager: function () {
            var 
    me this;

            var 
    yPos 0;

            
    // Avoid error messages if the manager does not have a dom element
            
    if (me.manager && me.manager.el && me.manager.el.dom) {
                if (
    me.useXAxis) {
                    
    // Element should already be aligned horizontaly
                    
    return me.el.getTop();
                } else {
                    
    // Using getAnchorXY instead of getTop/getBottom should give a correct placement when document is used
                    // as the manager but is still 0 px high. Before rendering the viewport.
                    
    if (me.position == 'br' || me.position == 'bl' || me.position == 'b') {
                        
    yPos += me.manager.el.getAnchorXY('b')[1];
                        
    yPos -= (me.el.getHeight() + me.paddingY);
                    } else {
                        
    yPos += me.manager.el.getAnchorXY('t')[1];
                        
    yPos += me.paddingY;
                    }
                }
            }

            return 
    yPos;
        },

        
    getXposAlignedToSibling: function (sibling) {
            var 
    me this;

            if (
    me.useXAxis) {
                if (
    me.position == 'tl' || me.position == 'bl' || me.position == 'l') {
                    
    // Using sibling's width when adding
                    
    return (sibling.xPos sibling.el.getWidth() + sibling.spacing);
                } else {
                    
    // Using own width when subtracting
                    
    return (sibling.xPos me.el.getWidth() - me.spacing);
                }
            } else {
                return 
    me.el.getLeft();
            }

        },

        
    getYposAlignedToSibling: function (sibling) {
            var 
    me this;

            if (
    me.useXAxis) {
                return 
    me.el.getTop();
            } else {
                if (
    me.position == 'tr' || me.position == 'tl' || me.position == 't') {
                    
    // Using sibling's width when adding
                    
    return (sibling.yPos sibling.el.getHeight() + sibling.spacing);                
                } else {
                    
    // Using own width when subtracting
                    
    return (sibling.yPos me.el.getHeight() - sibling.spacing);
                }
            }
        },

        
    getNotifications: function (alignment) {
            var 
    me this;

            if (!
    me.manager.notifications[alignment]) {
                
    me.manager.notifications[alignment] = [];
            }

            return 
    me.manager.notifications[alignment];
        },

        
    setManager: function (manager) {
            var 
    me this;

            
    me.manager manager;

            if (
    typeof me.manager == 'string') {
                
    me.manager Ext.getCmp(me.manager);
            }

            
    // If no manager is provided or found, then the static object is used and the el property pointed to the body document.
            
    if (!me.manager) {
                
    me.manager me.statics().defaultManager;

                if (!
    me.manager.el) {
                    
    me.manager.el Ext.getBody();
                }
            }
            
            if (
    typeof me.manager.notifications == 'undefined') {
                
    me.manager.notifications = {};
            }
        },
        
        
    beforeShow: function () {
            var 
    me this;

            if (
    me.stickOnClick) {
                if (
    me.body && me.body.dom) {
                    
    Ext.fly(me.body.dom).on('click', function () {
                        
    me.cancelAutoClose();
                        
    me.addCls('notification-fixed');
                    }, 
    me);
                }
            }

            if (
    me.autoClose) {
                
    me.task = new Ext.util.DelayedTask(me.doAutoCloseme);
                
    me.task.delay(me.autoCloseDelay);
            }

            
    // Shunting offscreen to avoid flicker
            
    me.el.setX(-10000);
            
    me.el.setOpacity(1);
            
        },

        
    afterShow: function () {
            var 
    me this;

            var 
    notifications me.getNotifications(me.managerAlignment);

            if (
    notifications.length) {
                
    me.el.alignTo(notifications[notifications.length 1].elme.siblingAlignment, [00]);
                
    me.xPos me.getXposAlignedToSibling(notifications[notifications.length 1]);
                
    me.yPos me.getYposAlignedToSibling(notifications[notifications.length 1]);
            } else {
                
    me.el.alignTo(me.manager.elme.managerAlignment, [(me.paddingX me.paddingFactorX), (me.paddingY me.paddingFactorY)], false);
                
    me.xPos me.getXposAlignedToManager();
                
    me.yPos me.getYposAlignedToManager();
            }

            
    Ext.Array.include(notificationsme);

            
    me.el.animate({
                
    to: {
                    
    xme.xPos,
                    
    yme.yPos,
                    
    opacity1
                
    },
                
    easingme.slideInAnimation,
                
    durationme.slideInDuration,
                
    dynamictrue
            
    });

            
    this.callParent(arguments);
        },
        
        
    slideBack: function () {
            var 
    me this;

            var 
    notifications me.getNotifications(me.managerAlignment);
            var 
    index Ext.Array.indexOf(notificationsme)

            
    // Not animating the element if it already started to hide itself or if the manager is not present in the dom
            
    if (!me.isHiding && me.el && me.manager && me.manager.el && me.manager.el.dom && me.manager.el.isVisible()) {

                if (
    index) {
                    
    me.xPos me.getXposAlignedToSibling(notifications[index 1]);
                    
    me.yPos me.getYposAlignedToSibling(notifications[index 1]);
                } else {
                    
    me.xPos me.getXposAlignedToManager();
                    
    me.yPos me.getYposAlignedToManager();
                }

                
    me.stopAnimation();

                
    me.el.animate({
                    
    to: {
                        
    xme.xPos,
                        
    yme.yPos
                    
    },
                    
    easingme.slideBackAnimation,
                    
    durationme.slideBackDuration,
                    
    dynamictrue
                
    });
            }
        },

        
    cancelAutoClose: function() {
            var 
    me this;

            if (
    me.autoClose) {
                
    me.task.cancel();
            }
        },

        
    doAutoClose: function () {
            var 
    me this;

            if (!(
    me.stickWhileHover && me.mouseIsOver)) {
                
    // Close immediately
                
    me.close();
            } else {
                
    // Delayed closing when mouse leaves the component.
                
    me.closeOnMouseOut true;
            }
        },

        
    removeFromManager: function () {
            var 
    me this;

            if (
    me.manager) {
                var 
    notifications me.getNotifications(me.managerAlignment);
                var 
    index Ext.Array.indexOf(notificationsme);
                if (
    index != -1) {
                    
    Ext.Array.erase(notificationsindex1);

                    
    // Slide "down" all notifications "above" the hidden one
                    
    for (;index notifications.lengthindex++) {
                        
    notifications[index].slideBack();
                    }
                }
            }
        },

        
    hide: function () {
            var 
    me this;

            
    // Avoids restarting the last animation on an element already underway with its hide animation
            
    if (!me.isHiding && me.el) {

                
    me.isHiding true;

                
    me.cancelAutoClose();
                
    me.stopAnimation();

                
    me.el.animate({
                    
    to: {
                        
    opacity0
                    
    },
                    
    easing'easeIn',
                    
    durationme.hideDuration,
                    
    dynamicfalse,
                    
    listeners: {
                        
    afteranimate: function () {
                            
    me.removeFromManager();
                            
    me.readyToHide true;
                            
    me.hide(me.animateTargetme.doCloseme);
                        }
                    }
                });
            }

            
    // Calling parent's hide function to complete hiding
            
    if (me.readyToHide) {
                
    me.isHiding false;
                
    me.readyToHide false;
                
    me.removeCls('notification-fixed');
                
    me.callParent(arguments);
                if (
    me.destroyAfterHide) {
                    
    me.destroy();
                }
            }
        },

        
    destroy: function () {
            var 
    me this;

            if (!
    me.hidden) {
                
    me.destroyAfterHide true;
                
    me.hide(me.animateTargetme.doCloseme);
            } else {
                
    me.callParent(arguments);
            }
        }

    });


    /*    Changelog:
     *
     *    2011-09-01 - 1.1: Bugfix. Array.indexOf not universally implemented, causing errors in IE<=8. Replaced with Ext.Array.indexOf.
     *    2011-09-12 - 1.2: Added config options: stickOnClick and stickWhileHover.
     *    2011-09-13 - 1.3: Cleaned up component destruction.
     *    2012-03-06 - 2.0: Renamed some properties ending with "Delay" to the more correct: "Duration".
     *                    Moved the hiding animation out of destruction and into hide.
     *                      Renamed the corresponding "destroy" properties to "hide".
     *                    (Hpsam) Changed addClass to addCls.
     *                    (Hpsam) Avoiding setting 'notification-fixed' when auto hiding.
     *                    (Justmyhobby) Using separate arrays to enable managers to mix alignments.
     *                    (Kreeve_ctisn) Removed default title.
     *                      (Jmaia) Center of edges can be used for positioning. Renamed corner property to position.
     *                    (Hpsam) Hiding or destroying manager does not cause errors.
     *  2012-08-12 - 2.1: Renamed autoHide to autoClose
     *                    (Dmurat) Enabled reuse of notifications (closeAction: 'hide')
     *                    (Idonofrio) Destroying notification by default (closeAction: 'destroy')
     */ 
    Last edited by eirik.lorentsen; 16 Oct 2012 at 9:46 AM. Reason: Added link to 2.0 version

  2. #2
    Sencha User
    Join Date
    Feb 2009
    Posts
    2
    Vote Rating
    0
    shadowman1024 is on a distinguished road

      0  

    Default Very Good

    Very Good


    thanks a lot for this. Excellent work

  3. #3
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    St. Louis, MO
    Posts
    33,684
    Vote Rating
    435
    mitchellsimoens has much to be proud of mitchellsimoens has much to be proud of mitchellsimoens has much to be proud of mitchellsimoens has much to be proud of mitchellsimoens has much to be proud of mitchellsimoens has much to be proud of mitchellsimoens has much to be proud of mitchellsimoens has much to be proud of mitchellsimoens has much to be proud of

      0  

    Default


    Very nice! Love the bounce effects!
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    http://www.JSONPLint.com - Source to lint your JSONP!

    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is almost in print!

    When posting code, please use BBCode's CODE tags.

  4. #4
    Sencha User
    Join Date
    Dec 2010
    Posts
    12
    Vote Rating
    0
    kennedyt is on a distinguished road

      0  

    Default


    Very good!

  5. #5
    Sencha Premium Member
    Join Date
    Oct 2009
    Location
    Germany
    Posts
    224
    Vote Rating
    5
    Ekambos will become famous soon enough

      0  

    Default


    You saved my day

  6. #6
    Sencha User
    Join Date
    Oct 2010
    Posts
    6
    Vote Rating
    0
    thomaschang is on a distinguished road

      0  

    Default not function well within IE7 & IE8

    not function well within IE7 & IE8


    i found it got error message at line 292 within IE7 & IE8

  7. #7
    Sencha User eirik.lorentsen's Avatar
    Join Date
    Aug 2011
    Location
    Santiago, Chile
    Posts
    27
    Vote Rating
    2
    eirik.lorentsen is on a distinguished road

      0  

    Default


    thomaschang,

    Thanx for reporting that bug! The Array.indexOf method that I was using is not universally implemented and failed in older IE versions. I replaced my array manipulations with the Ext.Array library methods and now it seems to work fine in IE8. I have updated the source in my original post.

    I have testet version 1.1 in the following browsers without any issues:
    win7: FF 6.0.1, IE8, IE9, Chrome 13, Opera 11.50
    android2.3.3: Stock browser, Dolphin HD 6.1.0, FF 6.0, Opera 11.10

  8. #8
    Sencha User
    Join Date
    Oct 2010
    Posts
    6
    Vote Rating
    0
    thomaschang is on a distinguished road

      0  

    Default z-index problem

    z-index problem


    when the notification is designed to show before creating a new ext.window , the notification will be put behind the new window, then you cannot see it wholly or partially

  9. #9
    Touch Premium Member
    Join Date
    Jan 2011
    Location
    Lisbon, Portugal
    Posts
    192
    Vote Rating
    0
    jmaia is on a distinguished road

      0  

    Default


    Great stuff, just what I was looking for.

    Is there a way to make the notification window 'sticky' when the user hovers the mouse on the window and then release it when the user hovers out ? Like in growl ?

    Regards,

    Joao Maia

  10. #10
    Sencha User
    Join Date
    Dec 2010
    Posts
    12
    Vote Rating
    0
    kennedyt is on a distinguished road

      0  

    Default


    Sorry, but how use? Thanks.