1. #51
    Sencha User
    Join Date
    Nov 2011
    Location
    France
    Posts
    10
    Vote Rating
    0
    hpsam is on a distinguished road

      0  

    Default improvements / modifications

    improvements / modifications


    Thanks for this extension.

    I've done some improvements / modifications for my needs, if it can be useful to others or integrated to the extension.
    • Change AddClass to AddCls in cancelAutoDestroy,
    • Add multi positions notifications in one manager (use getNotifications),
    • No addCls('notification-fixed') when the notification is detroyed,
    • To avoid errors, beforedestroy of manager, destroy all associated notifications.

    PHP Code:
    /*
     * Hpsam: 
     * - Change AddClass to AddCls in cancelAutoDestroy,
     * - Add multi positions notifications in one manager (use getNotifications),
     * - No addCls('notification-fixed') when the notification is detroyed,
     * - To avoid errors, beforedestroy of manager, destroy all associated notifications.
     */
    /* 
     *    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: 1.3
     *    Last changed date: 2011-09-13
     */
    Ext.define('Ext.ux.window.Notification', {
        
    extend'Ext.window.Window',
        
    alias'widget.uxNotification',

        
    title'Notification',

        
    cls'ux-notification-window',
        
    autoDestroytrue,
        
    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
        
    corner'br',

        
    // Pixels between each notification
        
    spacing6,

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

        
    slideInAnimation'easeIn',
        
    slideDownAnimation'bounceOut',
        
    autoDestroyDelay7000,
        
    slideInDelay1500,
        
    slideDownDelay1000,
        
    fadeDelay500,
        
    stickOnClicktrue,
        
    stickWhileHovertrue,

        
    // Private. Do not override!
        
    underDestructionfalse,
        
    readyToDestroyfalse,
        
    // Caching position coordinate to avoid windows overlapping when fading in simultaneously
        
    xPos0,
        
    yPos0,

        
    statics: {
            
    defaultManager: {
                
    notifications: {},
                
    elnull
            
    }
        },

        
    initComponent: function() {
            var 
    me this;

            
    me.callParent(arguments);

            switch (
    me.corner) {
                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;
            }

            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();
                }
            } else if (!
    me.manager.destroyNotifications) {
                
    me.manager.destroyNotifications = function(manager){
                    
    Ext.Object.each(manager.notifications, function(alignmentnotifications){
                        
    Ext.Array.each(notifications, function(notification) {
                            
    notification.destroy();
                        });
                    });
                }
                
    me.manager.on('beforedestroy'me.manager.destroyNotificationsme.manager);
            }
            
            if (
    typeof me.manager.notifications == 'undefined') {
                
    me.manager.notifications = {};
            }
        },
        
        
    getNotifications: function() {
            var 
    alignment this.managerAlignment
                notifs 
    this.manager.notifications;
            if (!
    notifs[alignment]) {
                
    notifs[alignment] = [];
            }
            return 
    notifs[alignment];
        },
        
        
    onRender: function() {
            var 
    me this;

            
    me.callParent(arguments);

            if (
    me.stickOnClick) {
                if (
    me.body && me.body.dom) {
                    
    Ext.fly(me.body.dom).on('click'me.cancelAutoDestroyme);
                }
            }

            if (
    me.autoDestroy) {
                
    me.task = new Ext.util.DelayedTask(me.doAutoDestroyme);
                
    me.task.delay(me.autoDestroyDelay);
            }

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

        },

        
    getXposAlignedToManager: function () {
            var 
    me this;

            var 
    xPos 0;

            if (
    me.corner == 'br' || me.corner == 'tr') {
                
    xPos += me.manager.el.getRight();
                
    xPos -= (me.el.getWidth() + me.paddingX);
            } else {
                
    xPos += me.manager.el.getLeft();
                
    xPos += me.paddingX;
            }

            return 
    xPos;
        },

        
    getYposAlignedToManager: function () {
            var 
    me this;

            var 
    yPos 0;

            if (
    me.corner == 'br' || me.corner == 'bl') {
                
    yPos += me.manager.el.getBottom();
                
    yPos -= (me.el.getHeight() + me.paddingY);
            } else {
                
    yPos += me.manager.el.getTop();
                
    yPos += me.paddingY;
            }

            return 
    yPos;
        },

        
    getXposAlignedToSibling: function (sibling) {
            var 
    me this;

            if (
    me.useXAxis) {
                if (
    me.corner == 'tl' || me.corner == 'bl') {
                    
    // 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.corner == 'tr' || me.corner == 'tl') {
                    
    // 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);
                }
            }
        },

        
    beforeShow: function () {
            var 
    me this,
                
    notifications me.getNotifications();

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

            
    Ext.Array.include(notificationsme);

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

        },

        
    slideDown: function () {
            var 
    me this;

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

            
    // Not animating the element if it already started to destroy itself
            
    if (!me.underDestruction && me.el) {

                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.el.animate({
                    
    to: {
                        
    xme.xPos,
                        
    yme.yPos
                    
    },
                    
    easingme.slideDownAnimation,
                    
    durationme.slideDownDelay,
                    
    dynamictrue
                
    });
            }
        },

        
    cancelAutoDestroy: function() {
            var 
    me this;
            
    // No AddCls when called from destroy
            
    !me.underDestruction && me.addCls('notification-fixed');
            if (
    me.autoDestroy) {
                
    me.task.cancel();
                
    me.autoDestroy false;
            }
        },

        
    doAutoDestroy: function () {
            var 
    me this;

            
    /* Delayed destruction when mouse leaves the component.
               Doing this before me.mouseIsOver is checked below to avoid a race condition while resetting event handlers */
            
    me.el.hover(
                function () {
                },
                function () {
                    
    me.destroy();
                },
                
    me
            
    );
            
            if (!(
    me.stickWhileHover && me.mouseIsOver)) {
                
    // Destroy immediately
                
    me.destroy();
            }
        },

        
    listeners: {
            
    'beforehide': function (meeOpts) {
                if (!
    me.underDestruction) {
                    
    // Force window to animate and destroy, instead of hiding
                    
    me.destroy();
                    return 
    false;
                }
            }
        },

        
    destroy: function () {
            var 
    me this;

            
    // Avoids starting the last animation on an element already underway with its destruction
            
    if (!me.underDestruction) {

                
    me.underDestruction true;

                
    me.cancelAutoDestroy();
                
    me.stopAnimation();

                
    me.el.animate({
                    
    to: {
                        
    opacity0
                    
    },
                    
    easing'easeIn',
                    
    durationme.fadeDelay,
                    
    dynamictrue,
                    
    listeners: {
                        
    afteranimate: function () {

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

                                
    // Slide "down" all notifications "above" the destroyed one
                                
    for (;index notifications.lengthindex++) {
                                    
    notifications[index].slideDown();
                                }
                            }
                            
    me.readyToDestroy true;
                            
    me.destroy();
                        }
                    }
                });
            }

            
    // After animation is complete the component may be destroyed
            
    if (me.readyToDestroy) {
                
    this.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
     *
     */ 

  2. #52
    Ext JS Premium Member
    Join Date
    Jun 2011
    Posts
    259
    Vote Rating
    7
    ontho is on a distinguished road

      0  

    Default


    Thanks for this entension! It works fine in a viewport, however on a "normal" html-page which can be scrolled horizontally and vertically, the notifications might not be visible to the user (for example: Use "tr" on a scrolled-down browser-window). Any idea how to solve this?

  3. #53
    Sencha User
    Join Date
    Jun 2008
    Posts
    49
    Vote Rating
    0
    Psychokrameur is on a distinguished road

      0  

    Default


    Quote Originally Posted by eirik.lorentsen View Post
    jmaia:
    Nice suggestion adding center alignment. It shouldn't be too difficult to add it in a future version.

    Meanwhile it should be possible to fake it relatively easy by overriding the paddingX or paddingY properties. For example to align the notification 'bc' you poll the manager's width add the notification's width then divide by two. Using the result as the paddingX value and 'bl' as corner when creating the notifications should center them.
    One more uxNotification fan and user which is interested on support off center functionnality ('tc' and 'bc')

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

      0  

    Default


    Quote Originally Posted by Psychokrameur View Post
    One more uxNotification fan and user which is interested on support off center functionnality ('tc' and 'bc')
    I have been working on a new version with all the suggested improvments including center alignment. I'm expecting to have it ready pretty soon!

  5. #55
    Sencha User
    Join Date
    Jun 2008
    Posts
    49
    Vote Rating
    0
    Psychokrameur is on a distinguished road

      0  

    Default


    Joy !!!

    Thanks Eirik

  6. #56
    Sencha User
    Join Date
    May 2008
    Location
    Hyderabad, India
    Posts
    8
    Vote Rating
    0
    ashokvilvanathan is on a distinguished road

      0  

    Default Great Extension

    Great Extension


    Thanks for the extension. Am getting an error that says 'Ext.Array.erase is not a function'. Could anyone help...

  7. #57
    Sencha Premium Member lorezyra's Avatar
    Join Date
    Dec 2007
    Location
    Japan -- 日本
    Posts
    638
    Vote Rating
    18
    lorezyra will become famous soon enough lorezyra will become famous soon enough

      0  

    Default Kick aZZ!!

    Kick aZZ!!


    I'm loving these new features! Can't wait to consume them in my next project.
    Perfection as a goal is a nice idea that can point one in a specific direction. However, since "perfection" is an ever changing (evolving?) and moving target, one must admit that perfection can never be obtained...

    When in doubt, check the d4mn source code!
    ================================================
    And here are my terms...
    1. I don't care if you use my source code. (Known as "Code.")
    2. I don't care if I get any monetary compensation.
    3. I do care to receive credit for Code provided. So, please keep my name in the comments for Code provided.
    4. Code is provided without warranty "AS-IS" and I claim absolutely no warranty nor liability to the quality, security, and run-ability on any platform.
    5. By using Code, you accept all risk inherit with Code regardless if Code has known and yet to be discovered bugs.
    6. You are welcome to change and improve the Code to best meet your needs.
    7. I don't care if you use the Code in a commercial or open-source project.
    8. You are not required to contact me prior to using the Code.
    ================================================
    Simple. Enjoy.

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

      1  

    Default


    The wait is over! Version 2.0 is here!

    The new features/improvements are as following:
    • 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.
    The most requested feature was the centering of the notifications. This can now be done through the position property which is the 2.0 equivalent of the corner property.
    Set position to 'l', 'r', 't' or 'b' for left, right, top or bottom alignment. I also included 'lc', 'rc', 'tc' and 'bc' as equivalent options as "left center" probably will sound logical to most users. Se the demo for examples.

    I figured it was a good idea to rename corner to position since the sides now are included. Position is also the term used in the framework. There is also a backwards compatibility layer so it is not strictly neccessary to rename the properties when upgrading.

    For those who do wish to use the correct property names the changes from 1.3 are:
    corner = position
    slideDownAnimation = slideBackAnimation
    autoDestroyDelay = autoHideDelay
    slideInDelay = slideInDuration
    slideDownDelay = slideBackDuration
    fadeDelay = hideDuration
    autoDestroy = autoHide
    (autoDestroy is a property on the AbstractContainer component so there used to be an unfortunate overlap here)

    The updated source can be found on the demo page or the first post in this thread.

  9. #59
    Sencha User
    Join Date
    Jun 2008
    Posts
    49
    Vote Rating
    0
    Psychokrameur is on a distinguished road

      0  

    Default


    Oh yes!

    Thanks a lot Eric, very good job. Changes well documented.
    I will change the version right now in my app!

  10. #60
    Sencha User
    Join Date
    Jun 2010
    Location
    Buenos Aires, Argentina
    Posts
    213
    Vote Rating
    9
    ldonofrio will become famous soon enough

      0  

    Default


    eirik.lorentsen, thanks for the great UX!

    I need to destroy the notifications after hide so i'm trying to update the plugin to do that, but i can't, i got a lot of errors doing me.destroy after "hide method".

    Can anyone give some advice? What i'm missing here? There is any purpose for hidding the windows and leaving in the dom?

    Thanks again for the good work!