mirror of
https://github.com/php/web-doc-editor.git
synced 2026-03-25 09:42:09 +01:00
454 lines
14 KiB
JavaScript
454 lines
14 KiB
JavaScript
/*
|
|
* 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.0
|
|
* Last changed date: 2012-03-06
|
|
*/
|
|
|
|
Ext.define('Ext.ux.window.Notification', {
|
|
extend: 'Ext.window.Window',
|
|
alias: 'widget.uxNotification',
|
|
|
|
cls: 'ux-notification-window',
|
|
autoHide: true,
|
|
autoHeight: true,
|
|
plain: false,
|
|
draggable: false,
|
|
shadow: false,
|
|
focus: Ext.emptyFn,
|
|
|
|
// For alignment and to store array of rendered notifications. Defaults to document if not set.
|
|
manager: null,
|
|
|
|
useXAxis: false,
|
|
|
|
// Options: br, bl, tr, tl, t, l, b, r
|
|
position: 'br',
|
|
|
|
// Pixels between each notification
|
|
spacing: 6,
|
|
|
|
// Pixels from the managers borders to start the first notification
|
|
paddingX: 30,
|
|
paddingY: 10,
|
|
|
|
slideInAnimation: 'easeIn',
|
|
slideBackAnimation: 'bounceOut',
|
|
slideInDuration: 1500,
|
|
slideBackDuration: 1000,
|
|
hideDuration: 500,
|
|
autoHideDelay: 7000,
|
|
stickOnClick: true,
|
|
stickWhileHover: true,
|
|
|
|
// Private. Do not override!
|
|
isHiding: false,
|
|
readyToHide: false,
|
|
|
|
// Caching coordinates to be able to align to final position of siblings being animated
|
|
xPos: 0,
|
|
yPos: 0,
|
|
|
|
statics: {
|
|
defaultManager: {
|
|
el: null
|
|
}
|
|
},
|
|
|
|
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];
|
|
},
|
|
|
|
beforeShow: function () {
|
|
var me = this;
|
|
|
|
// 1.x 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.autoHideDelay = me.autoDestroyDelay;
|
|
}
|
|
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/, '');
|
|
|
|
switch (me.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;
|
|
}
|
|
|
|
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 = {};
|
|
}
|
|
|
|
if (me.stickOnClick) {
|
|
if (me.body && me.body.dom) {
|
|
Ext.fly(me.body.dom).on('click', function () {
|
|
me.cancelAutoHide();
|
|
me.addCls('notification-fixed');
|
|
}, me);
|
|
}
|
|
}
|
|
|
|
me.el.hover(
|
|
function () {
|
|
me.mouseIsOver = true;
|
|
},
|
|
function () {
|
|
me.mouseIsOver = false;
|
|
},
|
|
me
|
|
);
|
|
|
|
if (me.autoHide) {
|
|
me.task = new Ext.util.DelayedTask(me.doAutoHide, me);
|
|
me.task.delay(me.autoHideDelay);
|
|
}
|
|
|
|
var notifications = me.getNotifications(me.managerAlignment);
|
|
|
|
if (notifications.length) {
|
|
me.el.alignTo(notifications[notifications.length - 1].el, me.siblingAlignment, [0, 0]);
|
|
me.xPos = me.getXposAlignedToSibling(notifications[notifications.length - 1]);
|
|
me.yPos = me.getYposAlignedToSibling(notifications[notifications.length - 1]);
|
|
} else {
|
|
me.el.alignTo(me.manager.el, me.managerAlignment, [(me.paddingX * me.paddingFactorX), (me.paddingY * me.paddingFactorY)]);
|
|
me.xPos = me.getXposAlignedToManager();
|
|
me.yPos = me.getYposAlignedToManager();
|
|
}
|
|
|
|
Ext.Array.include(notifications, me);
|
|
|
|
me.stopAnimation();
|
|
|
|
me.el.animate({
|
|
to: {
|
|
x: me.xPos,
|
|
y: me.yPos,
|
|
opacity: 1
|
|
},
|
|
easing: me.slideInAnimation,
|
|
duration: me.slideInDuration,
|
|
dynamic: true
|
|
});
|
|
|
|
},
|
|
|
|
slideBack: function () {
|
|
var me = this;
|
|
|
|
var notifications = me.getNotifications(me.managerAlignment);
|
|
var index = Ext.Array.indexOf(notifications, me)
|
|
|
|
// 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: {
|
|
x: me.xPos,
|
|
y: me.yPos
|
|
},
|
|
easing: me.slideBackAnimation,
|
|
duration: me.slideBackDuration,
|
|
dynamic: true
|
|
});
|
|
}
|
|
},
|
|
|
|
cancelAutoHide: function() {
|
|
var me = this;
|
|
|
|
if (me.autoHide) {
|
|
me.task.cancel();
|
|
me.autoHide = false;
|
|
}
|
|
},
|
|
|
|
doAutoHide: function () {
|
|
var me = this;
|
|
|
|
/* Delayed hiding 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.hide();
|
|
},
|
|
me
|
|
);
|
|
|
|
if (!(me.stickWhileHover && me.mouseIsOver)) {
|
|
// Hide immediately
|
|
me.hide();
|
|
}
|
|
},
|
|
|
|
|
|
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.cancelAutoHide();
|
|
me.stopAnimation();
|
|
|
|
me.el.animate({
|
|
to: {
|
|
opacity: 0
|
|
},
|
|
easing: 'easeIn',
|
|
duration: me.hideDuration,
|
|
dynamic: false,
|
|
listeners: {
|
|
afteranimate: function () {
|
|
if (me.manager) {
|
|
var notifications = me.getNotifications(me.managerAlignment);
|
|
var index = Ext.Array.indexOf(notifications, me);
|
|
if (index != -1) {
|
|
Ext.Array.erase(notifications, index, 1);
|
|
|
|
// Slide "down" all notifications "above" the hidden one
|
|
for (;index < notifications.length; index++) {
|
|
notifications[index].slideBack();
|
|
}
|
|
}
|
|
}
|
|
|
|
me.readyToHide = true;
|
|
me.hide();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Calling parents hide function to complete hiding
|
|
if (me.readyToHide) {
|
|
me.isHiding = false;
|
|
me.readyToHide = false;
|
|
me.removeCls('notification-fixed');
|
|
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.
|
|
* 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.
|
|
*/ |