]> git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blobdiff - sources/plugins/notification/plugin.js
Update to 4.7.3
[perso/Immae/Projets/packagist/ludivine-ckeditor-component.git] / sources / plugins / notification / plugin.js
diff --git a/sources/plugins/notification/plugin.js b/sources/plugins/notification/plugin.js
new file mode 100644 (file)
index 0000000..305db28
--- /dev/null
@@ -0,0 +1,929 @@
+/**\r
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.\r
+ * For licensing, see LICENSE.md or http://ckeditor.com/license\r
+ */\r
+\r
+/**\r
+ * @fileOverview The "Notification" plugin.\r
+ *\r
+ */\r
+\r
+'use strict';\r
+\r
+CKEDITOR.plugins.add( 'notification', {\r
+       lang: 'az,ca,cs,da,de,de-ch,en,eo,es,es-mx,eu,fr,gl,hr,hu,id,it,ja,km,ko,ku,nb,nl,oc,pl,pt,pt-br,ru,sk,sv,tr,ug,uk,zh,zh-cn', // %REMOVE_LINE_CORE%\r
+\r
+       init: function( editor ) {\r
+               editor._.notificationArea = new Area( editor );\r
+\r
+               // Overwrites default `editor.showNotification`.\r
+               editor.showNotification = function( message, type, progressOrDuration ) {\r
+                       var progress, duration;\r
+\r
+                       if ( type == 'progress' ) {\r
+                               progress = progressOrDuration;\r
+                       } else {\r
+                               duration = progressOrDuration;\r
+                       }\r
+\r
+                       var notification = new CKEDITOR.plugins.notification( editor, {\r
+                               message: message,\r
+                               type: type,\r
+                               progress: progress,\r
+                               duration: duration\r
+                       } );\r
+\r
+                       notification.show();\r
+\r
+                       return notification;\r
+               };\r
+\r
+               // Close the last notification on ESC.\r
+               editor.on( 'key', function( evt ) {\r
+                       if ( evt.data.keyCode == 27 ) { /* ESC */\r
+                               var notifications = editor._.notificationArea.notifications;\r
+\r
+                               if ( !notifications.length ) {\r
+                                       return;\r
+                               }\r
+\r
+                               // As long as this is not a common practice to inform screen-reader users about actions, in this case\r
+                               // this is the best solution (unfortunately there is no standard for accessibility for notifications).\r
+                               // Notification has an `alert` aria role what means that it does not get a focus nor is needed to be\r
+                               // closed (unlike `alertdialog`). However notification will capture ESC key so we need to inform user\r
+                               // why it does not do other actions.\r
+                               say( editor.lang.notification.closed );\r
+\r
+                               // Hide last.\r
+                               notifications[ notifications.length - 1 ].hide();\r
+\r
+                               evt.cancel();\r
+                       }\r
+               } );\r
+\r
+               // Send the message to the screen readers.\r
+               function say( text ) {\r
+                       var message = new CKEDITOR.dom.element( 'div' );\r
+                       message.setStyles( {\r
+                               position: 'fixed',\r
+                               'margin-left': '-9999px'\r
+                       } );\r
+                       message.setAttributes( {\r
+                               'aria-live': 'assertive',\r
+                               'aria-atomic': 'true'\r
+                       } );\r
+                       message.setText( text );\r
+\r
+                       CKEDITOR.document.getBody().append( message );\r
+\r
+                       setTimeout( function() {\r
+                               message.remove();\r
+                       }, 100 );\r
+               }\r
+       }\r
+} );\r
+\r
+/**\r
+ * Notification class. Notifications are used to display short messages to the user. They might be used to show the result of\r
+ * asynchronous actions or information about changes in the editor content. It is recommended to use them instead of\r
+ * alert dialogs. They should **not** be used if a user response is required nor with dialog windows (e.g. in dialog validation).\r
+ *\r
+ * There are four types of notifications available, see the {@link #type} property.\r
+ *\r
+ * Note that the notification constructor only creates a notification instance. To show it, use the {@link #show} method:\r
+ *\r
+ *             var notification = new CKEDITOR.plugins.notification( editor, { message: 'Foo' } );\r
+ *             notification.show();\r
+ *\r
+ * You can also use the {@link CKEDITOR.editor#showNotification} method:\r
+ *\r
+ *             editor.showNotification( 'Foo' );\r
+ *\r
+ * All of the notification actions: ({@link #show}, {@link #update} and {@link #hide}) fire cancelable events\r
+ * on the related {@link CKEDITOR.editor} instance so you can integrate editor notifications with your website notifications.\r
+ *\r
+ * Refer to the [Notifications](http://docs.ckeditor.com/#!/guide/dev_notifications) article for more information about this feature.\r
+ *\r
+ * @since 4.5\r
+ * @class CKEDITOR.plugins.notification\r
+ * @constructor Create a notification object. Call {@link #show} to show the created notification.\r
+ * @param {CKEDITOR.editor} editor The editor instance.\r
+ * @param {Object} options\r
+ * @param {String} options.message The message displayed in the notification.\r
+ * @param {String} [options.type='info'] Notification type, see {@link #type}.\r
+ * @param {Number} [options.progress=0] If the type is `progress` this may be a progress from 0 to 1.\r
+ * @param {Number} [options.duration] How long the notification will be visible, see {@link #duration}.\r
+ */\r
+function Notification( editor, options ) {\r
+       CKEDITOR.tools.extend( this, options, {\r
+               editor: editor,\r
+               id: 'cke-' + CKEDITOR.tools.getUniqueId(),\r
+               area: editor._.notificationArea\r
+       } );\r
+\r
+       if ( !options.type ) {\r
+               this.type = 'info';\r
+       }\r
+\r
+       this.element = this._createElement();\r
+\r
+       // Don't allow dragging on notification (http://dev.ckeditor.com/ticket/13184).\r
+       editor.plugins.clipboard && CKEDITOR.plugins.clipboard.preventDefaultDropOnElement( this.element );\r
+}\r
+\r
+/**\r
+ * The editor instance.\r
+ *\r
+ * @readonly\r
+ * @property {CKEDITOR.editor} editor\r
+ */\r
+\r
+/**\r
+ * Message displayed in the notification.\r
+ *\r
+ * @readonly\r
+ * @property {String} message\r
+ */\r
+\r
+/**\r
+ * Notification type. There are four types available:\r
+ *\r
+ * * `info` (default) – Information for the user (e.g. "File is uploading.", "ACF modified content."),\r
+ * * `warning` – Warning or error message (e.g. "This type of file is not supported.",\r
+ * "You cannot paste the script."),\r
+ * * `success` – Information that an operation finished successfully (e.g. "File uploaded.", "Data imported.").\r
+ * * `progress` – Information about the progress of an operation. When the operation is done, the notification\r
+ * type should be changed to `success`.\r
+ *\r
+ * @readonly\r
+ * @property {String} type\r
+ */\r
+\r
+/**\r
+ * If the notification {@link #type} is `'progress'`, this is the progress from `0` to `1`.\r
+ *\r
+ * @readonly\r
+ * @property {Number} progress\r
+ */\r
+\r
+/**\r
+ * Notification duration. Determines after how many milliseconds the notification should close automatically.\r
+ * `0` means that the notification will not close automatically and that the user needs to close it manually.\r
+ * The default value for `warning` and `progress` notifications is `0`. For `info` and `success` the value can\r
+ * either be set through the {@link CKEDITOR.config#notification_duration} configuration option or equals `5000`\r
+ * if the configuration option is not set.\r
+ *\r
+ * @readonly\r
+ * @property {Number} duration\r
+ */\r
+\r
+/**\r
+ * Unique notification ID.\r
+ *\r
+ * @readonly\r
+ * @property {Number} id\r
+ */\r
+\r
+/**\r
+ * Notification DOM element. There is one element per notification. It is created when the notification is created,\r
+ * even if it is not shown. If the notification is hidden, the element is detached from the document but not deleted.\r
+ * It will be reused if the notification is shown again.\r
+ *\r
+ * @readonly\r
+ * @property {CKEDITOR.dom.element} element\r
+ */\r
+\r
+/**\r
+ * {@link CKEDITOR.plugins.notification.area Notification area} reference.\r
+ *\r
+ * @readonly\r
+ * @property {CKEDITOR.plugins.notification.area} area\r
+ */\r
+\r
+Notification.prototype = {\r
+       /**\r
+        * Adds the notification element to the notification area. The notification will be hidden automatically if\r
+        * {@link #duration} is set.\r
+        *\r
+        * Fires the {@link CKEDITOR.editor#notificationShow} event.\r
+        */\r
+       show: function() {\r
+               if ( this.editor.fire( 'notificationShow', { notification: this } ) === false ) {\r
+                       return;\r
+               }\r
+\r
+               this.area.add( this );\r
+\r
+               this._hideAfterTimeout();\r
+       },\r
+\r
+       /**\r
+        * Updates the notification object and element.\r
+        *\r
+        * Fires the {@link CKEDITOR.editor#notificationUpdate} event.\r
+        *\r
+        * @param {Object} options\r
+        * @param {String} [options.message] {@link #message}\r
+        * @param {String} [options.type] {@link #type}\r
+        * @param {Number} [options.progress] {@link #progress}\r
+        * @param {Number} [options.duration] {@link #duration}\r
+        * @param {Boolean} [options.important=false] If the update is important, the notification will be shown\r
+        * if it was hidden and read by screen readers.\r
+        */\r
+       update: function( options ) {\r
+               var show = true;\r
+\r
+               if ( this.editor.fire( 'notificationUpdate', { notification: this, options: options } ) === false ) {\r
+                       // The idea of cancelable event is to let user create his own way of displaying notification, so if\r
+                       // `notificationUpdate` event will be canceled there will be no interaction with notification area, but on\r
+                       // the other hand the logic should work anyway so object will be updated (including `element` property).\r
+                       // Note: we can safely update the element's attributes below, because this element is created inside\r
+                       // the constructor. If the notificatinShow event was canceled as well, the element is detached from DOM.\r
+                       show = false;\r
+               }\r
+\r
+               var element = this.element,\r
+                       messageElement = element.findOne( '.cke_notification_message' ),\r
+                       progressElement = element.findOne( '.cke_notification_progress' ),\r
+                       type = options.type;\r
+\r
+               element.removeAttribute( 'role' );\r
+\r
+               // Change type to progress if `options.progress` is set.\r
+               if ( options.progress && this.type != 'progress' ) {\r
+                       type = 'progress';\r
+               }\r
+\r
+               if ( type ) {\r
+                       element.removeClass( this._getClass() );\r
+                       element.removeAttribute( 'aria-label' );\r
+\r
+                       this.type = type;\r
+\r
+                       element.addClass( this._getClass() );\r
+                       element.setAttribute( 'aria-label', this.type );\r
+\r
+                       if ( this.type == 'progress' && !progressElement ) {\r
+                               progressElement = this._createProgressElement();\r
+                               progressElement.insertBefore( messageElement );\r
+                       } else if ( this.type != 'progress' && progressElement ) {\r
+                               progressElement.remove();\r
+                       }\r
+               }\r
+\r
+               if ( options.message !== undefined ) {\r
+                       this.message = options.message;\r
+                       messageElement.setHtml( this.message );\r
+               }\r
+\r
+               if ( options.progress !== undefined ) {\r
+                       this.progress = options.progress;\r
+\r
+                       if ( progressElement ) {\r
+                               progressElement.setStyle( 'width', this._getPercentageProgress() );\r
+                       }\r
+               }\r
+\r
+               if ( show && options.important ) {\r
+                       element.setAttribute( 'role', 'alert' );\r
+\r
+                       if ( !this.isVisible() ) {\r
+                               this.area.add( this );\r
+                       }\r
+               }\r
+\r
+               // Overwrite even if it is undefined.\r
+               this.duration = options.duration;\r
+\r
+               this._hideAfterTimeout();\r
+       },\r
+\r
+       /**\r
+        * Removes the notification element from the notification area.\r
+        *\r
+        * Fires the {@link CKEDITOR.editor#notificationHide} event.\r
+        */\r
+       hide: function() {\r
+               if ( this.editor.fire( 'notificationHide', { notification: this } ) === false ) {\r
+                       return;\r
+               }\r
+\r
+               this.area.remove( this );\r
+       },\r
+\r
+       /**\r
+        * Returns `true` if the notification is in the notification area.\r
+        *\r
+        * @returns {Boolean} `true` if the notification is in the notification area.\r
+        */\r
+       isVisible: function() {\r
+               return CKEDITOR.tools.indexOf( this.area.notifications, this ) >= 0;\r
+       },\r
+\r
+       /**\r
+        * Creates the notification DOM element.\r
+        *\r
+        * @private\r
+        * @returns {CKEDITOR.dom.element} Notification DOM element.\r
+        */\r
+       _createElement: function() {\r
+               var notification = this,\r
+                       notificationElement, notificationMessageElement, notificationCloseElement,\r
+                       close = this.editor.lang.common.close;\r
+\r
+               notificationElement = new CKEDITOR.dom.element( 'div' );\r
+               notificationElement.addClass( 'cke_notification' );\r
+               notificationElement.addClass( this._getClass() );\r
+               notificationElement.setAttributes( {\r
+                       id: this.id,\r
+                       role: 'alert',\r
+                       'aria-label': this.type\r
+               } );\r
+\r
+               if ( this.type == 'progress' )\r
+                       notificationElement.append( this._createProgressElement() );\r
+\r
+               notificationMessageElement = new CKEDITOR.dom.element( 'p' );\r
+               notificationMessageElement.addClass( 'cke_notification_message' );\r
+               notificationMessageElement.setHtml( this.message );\r
+               notificationElement.append( notificationMessageElement );\r
+\r
+               notificationCloseElement = CKEDITOR.dom.element.createFromHtml(\r
+                       '<a class="cke_notification_close" href="javascript:void(0)" title="' + close + '" role="button" tabindex="-1">' +\r
+                               '<span class="cke_label">X</span>' +\r
+                       '</a>' );\r
+               notificationElement.append( notificationCloseElement );\r
+\r
+               notificationCloseElement.on( 'click', function() {\r
+                       // Focus editor on close (http://dev.ckeditor.com/ticket/12865)\r
+                       notification.editor.focus();\r
+\r
+                       notification.hide();\r
+               } );\r
+\r
+               return notificationElement;\r
+       },\r
+\r
+       /**\r
+        * Gets the notification CSS class.\r
+        *\r
+        * @private\r
+        * @returns {String} Notification CSS class.\r
+        */\r
+       _getClass: function() {\r
+               return ( this.type == 'progress' ) ?\r
+                       'cke_notification_info' :\r
+                       ( 'cke_notification_' + this.type );\r
+       },\r
+\r
+       /**\r
+        * Creates a progress element for the notification element.\r
+        *\r
+        * @private\r
+        * @returns {CKEDITOR.dom.element} Progress element for the notification element.\r
+        */\r
+       _createProgressElement: function() {\r
+               var element = new CKEDITOR.dom.element( 'span' );\r
+               element.addClass( 'cke_notification_progress' );\r
+               element.setStyle( 'width', this._getPercentageProgress() );\r
+               return element;\r
+       },\r
+\r
+       /**\r
+        * Gets the progress as a percentage (ex. `0.3` -> `30%`).\r
+        *\r
+        * @private\r
+        * @returns {String} Progress as a percentage.\r
+        */\r
+       _getPercentageProgress: function() {\r
+               return Math.round( ( this.progress || 0 ) * 100 ) + '%';\r
+       },\r
+\r
+       /**\r
+        * Hides the notification after a timeout.\r
+        *\r
+        * @private\r
+        */\r
+       _hideAfterTimeout: function() {\r
+               var notification = this,\r
+                       duration;\r
+\r
+               if ( this._hideTimeoutId ) {\r
+                       clearTimeout( this._hideTimeoutId );\r
+               }\r
+\r
+               if ( typeof this.duration == 'number' ) {\r
+                       duration = this.duration;\r
+               } else if ( this.type == 'info' || this.type == 'success' ) {\r
+                       duration = ( typeof this.editor.config.notification_duration == 'number' ) ?\r
+                               this.editor.config.notification_duration :\r
+                               5000;\r
+               }\r
+\r
+               if ( duration ) {\r
+                       notification._hideTimeoutId = setTimeout( function() {\r
+                               notification.hide();\r
+                       }, duration );\r
+               }\r
+       }\r
+};\r
+\r
+/**\r
+ * Notification area is an area where all notifications are put. The area is laid out dynamically.\r
+ * When the first notification is added, the area is shown and all listeners are added.\r
+ * When the last notification is removed, the area is hidden and all listeners are removed.\r
+ *\r
+ * @since 4.5\r
+ * @private\r
+ * @class CKEDITOR.plugins.notification.area\r
+ * @constructor\r
+ * @param {CKEDITOR.editor} editor The editor instance.\r
+ */\r
+function Area( editor ) {\r
+       var that = this;\r
+\r
+       this.editor = editor;\r
+       this.notifications = [];\r
+       this.element = this._createElement();\r
+       this._uiBuffer = CKEDITOR.tools.eventsBuffer( 10, this._layout, this );\r
+       this._changeBuffer = CKEDITOR.tools.eventsBuffer( 500, this._layout, this );\r
+\r
+       editor.on( 'destroy', function() {\r
+               that._removeListeners();\r
+               that.element.remove();\r
+       } );\r
+}\r
+\r
+/**\r
+ * The editor instance.\r
+ *\r
+ * @readonly\r
+ * @property {CKEDITOR.editor} editor\r
+ */\r
+\r
+/**\r
+ * The array of added notifications.\r
+ *\r
+ * @readonly\r
+ * @property {Array} notifications\r
+ */\r
+\r
+/**\r
+ * Notification area DOM element. This element is created when the area object is created. It will be attached to the document\r
+ * when the first notification is added and removed when the last notification is removed.\r
+ *\r
+ * @readonly\r
+ * @property {CKEDITOR.dom.element} element\r
+ */\r
+\r
+/**\r
+ * Notification width. Cached for performance reasons.\r
+ *\r
+ * @private\r
+ * @property {CKEDITOR.dom.element} _notificationWidth\r
+ */\r
+\r
+/**\r
+ * Notification margin. Cached for performance reasons.\r
+ *\r
+ * @private\r
+ * @property {CKEDITOR.dom.element} _notificationMargin\r
+ */\r
+\r
+/**\r
+ * Event buffer object for UI events to optimize performance.\r
+ *\r
+ * @private\r
+ * @property {Object} _uiBuffer\r
+ */\r
+\r
+/**\r
+ * Event buffer object for editor change events to optimize performance.\r
+ *\r
+ * @private\r
+ * @property {Object} _changeBuffer\r
+ */\r
+\r
+Area.prototype = {\r
+       /**\r
+        * Adds the notification to the notification area. If it is the first notification, the area will also be attached to\r
+        * the document and listeners will be attached.\r
+        *\r
+        * Note that the proper way to show a notification is to call the {@link CKEDITOR.plugins.notification#show} method.\r
+        *\r
+        * @param {CKEDITOR.plugins.notification} notification Notification to add.\r
+        */\r
+       add: function( notification ) {\r
+               this.notifications.push( notification );\r
+\r
+               this.element.append( notification.element );\r
+\r
+               if ( this.element.getChildCount() == 1 ) {\r
+                       CKEDITOR.document.getBody().append( this.element );\r
+                       this._attachListeners();\r
+               }\r
+\r
+               this._layout();\r
+       },\r
+\r
+       /**\r
+        * Removes the notification from the notification area. If it is the last notification, the area will also be\r
+        * detached from the document and listeners will be detached.\r
+        *\r
+        * Note that the proper way to hide a notification is to call the {@link CKEDITOR.plugins.notification#hide} method.\r
+        *\r
+        * @param {CKEDITOR.plugins.notification} notification Notification to remove.\r
+        */\r
+       remove: function( notification ) {\r
+               var i = CKEDITOR.tools.indexOf( this.notifications, notification );\r
+\r
+               if ( i < 0 ) {\r
+                       return;\r
+               }\r
+\r
+               this.notifications.splice( i, 1 );\r
+\r
+               notification.element.remove();\r
+\r
+               if ( !this.element.getChildCount() ) {\r
+                       this._removeListeners();\r
+                       this.element.remove();\r
+               }\r
+       },\r
+\r
+       /**\r
+        * Creates the notification area element.\r
+        *\r
+        * @private\r
+        * @returns {CKEDITOR.dom.element} Notification area element.\r
+        */\r
+       _createElement: function() {\r
+               var editor = this.editor,\r
+                       config = editor.config,\r
+                       notificationArea = new CKEDITOR.dom.element( 'div' );\r
+\r
+               notificationArea.addClass( 'cke_notifications_area' );\r
+               notificationArea.setAttribute( 'id', 'cke_notifications_area_' + editor.name );\r
+               notificationArea.setStyle( 'z-index', config.baseFloatZIndex - 2 );\r
+\r
+               return notificationArea;\r
+       },\r
+\r
+       /**\r
+        * Attaches listeners to the notification area.\r
+        *\r
+        * @private\r
+        */\r
+       _attachListeners: function() {\r
+               var win = CKEDITOR.document.getWindow(),\r
+                       editor = this.editor;\r
+\r
+               win.on( 'scroll', this._uiBuffer.input );\r
+               win.on( 'resize', this._uiBuffer.input );\r
+               editor.on( 'change', this._changeBuffer.input );\r
+               editor.on( 'floatingSpaceLayout', this._layout, this, null, 20 );\r
+               editor.on( 'blur', this._layout, this, null, 20 );\r
+       },\r
+\r
+       /**\r
+        * Detaches listeners from the notification area.\r
+        *\r
+        * @private\r
+        */\r
+       _removeListeners: function() {\r
+               var win = CKEDITOR.document.getWindow(),\r
+                       editor = this.editor;\r
+\r
+               win.removeListener( 'scroll', this._uiBuffer.input );\r
+               win.removeListener( 'resize', this._uiBuffer.input );\r
+               editor.removeListener( 'change', this._changeBuffer.input );\r
+               editor.removeListener( 'floatingSpaceLayout', this._layout );\r
+               editor.removeListener( 'blur', this._layout );\r
+       },\r
+\r
+       /**\r
+        * Sets the position of the notification area based on the editor content, toolbar as well as\r
+        * viewport position and dimensions.\r
+        *\r
+        * @private\r
+        */\r
+       _layout: function() {\r
+               var area = this.element,\r
+                       editor = this.editor,\r
+                       contentsRect = editor.ui.contentsElement.getClientRect(),\r
+                       contentsPos = editor.ui.contentsElement.getDocumentPosition(),\r
+                       top,\r
+                       topRect,\r
+                       areaRect = area.getClientRect(),\r
+                       notification,\r
+                       notificationWidth = this._notificationWidth,\r
+                       notificationMargin = this._notificationMargin,\r
+                       win = CKEDITOR.document.getWindow(),\r
+                       scrollPos = win.getScrollPosition(),\r
+                       viewRect = win.getViewPaneSize(),\r
+                       body = CKEDITOR.document.getBody(),\r
+                       bodyPos = body.getDocumentPosition(),\r
+                       cssLength = CKEDITOR.tools.cssLength;\r
+\r
+               // Cache for optimization\r
+               if ( !notificationWidth || !notificationMargin ) {\r
+                       notification = this.element.getChild( 0 );\r
+                       notificationWidth = this._notificationWidth = notification.getClientRect().width;\r
+                       notificationMargin = this._notificationMargin =\r
+                               parseInt( notification.getComputedStyle( 'margin-left' ), 10 ) +\r
+                               parseInt( notification.getComputedStyle( 'margin-right' ), 10 );\r
+               }\r
+\r
+               // Check if toolbar exist and if so, then assign values to it (#491).\r
+               if ( editor.toolbar ) {\r
+                       top = editor.ui.space( 'top' );\r
+                       topRect = top.getClientRect();\r
+               }\r
+\r
+\r
+               // --------------------------------------- Horizontal layout ----------------------------------------\r
+\r
+               // +---Viewport-------------------------------+          +---Viewport-------------------------------+\r
+               // |                                          |          |                                          |\r
+               // | +---Toolbar----------------------------+ |          | +---Content----------------------------+ |\r
+               // | |                                      | |          | |                                      | |\r
+               // | +---Content----------------------------+ |          | |                                      | |\r
+               // | |                                      | |          | +---Toolbar----------------------+     | |\r
+               // | |      +------Notification------+      | |          | |                                |     | |\r
+               // | |                                      | |    OR    | +--------------------------------+     | |\r
+               // | |                                      | |          | |                                      | |\r
+               // | |                                      | |          | |      +------Notification------+      | |\r
+               // | |                                      | |          | |                                      | |\r
+               // | |                                      | |          | |                                      | |\r
+               // | +--------------------------------------+ |          | +--------------------------------------+ |\r
+               // +------------------------------------------+          +------------------------------------------+\r
+               if ( top && top.isVisible() &&\r
+                       topRect.bottom > contentsRect.top &&\r
+                       topRect.bottom < contentsRect.bottom - areaRect.height ) {\r
+                       setBelowToolbar();\r
+\r
+               // +---Viewport-------------------------------+\r
+               // |                                          |\r
+               // | +---Content----------------------------+ |\r
+               // | |                                      | |\r
+               // | |      +------Notification------+      | |\r
+               // | |                                      | |\r
+               // | |                                      | |\r
+               // | |                                      | |\r
+               // | +--------------------------------------+ |\r
+               // |                                          |\r
+               // +------------------------------------------+\r
+               } else if ( contentsRect.top > 0 ) {\r
+                       setTopStandard();\r
+\r
+               //   +---Content----------------------------+\r
+               //   |                                      |\r
+               // +---Viewport-------------------------------+\r
+               // | |                                      | |\r
+               // | |      +------Notification------+      | |\r
+               // | |                                      | |\r
+               // | |                                      | |\r
+               // | |                                      | |\r
+               // | +--------------------------------------+ |\r
+               // |                                          |\r
+               // +------------------------------------------+\r
+               } else if ( contentsPos.y + contentsRect.height - areaRect.height > scrollPos.y ) {\r
+                       setTopFixed();\r
+\r
+               //   +---Content----------------------------+              +---Content----------------------------+\r
+               //   |                                      |              |                                      |\r
+               //   |                                      |              |                                      |\r
+               //   |                                      |              |      +------Notification------+      |\r
+               //   |                                      |              |                                      |\r
+               //   |                                      |      OR      +--------------------------------------+\r
+               // +---Viewport-------------------------------+\r
+               // | |      +------Notification------+      | |          +---Viewport-------------------------------+\r
+               // | |                                      | |          |                                          |\r
+               // | +--------------------------------------+ |          |                                          |\r
+               // |                                          |          |                                          |\r
+               // +------------------------------------------+          +------------------------------------------+\r
+               } else {\r
+                       setBottom();\r
+               }\r
+\r
+               function setTopStandard() {\r
+                       area.setStyles( {\r
+                               position: 'absolute',\r
+                               top: cssLength( contentsPos.y )\r
+                       } );\r
+               }\r
+\r
+               function setBelowToolbar() {\r
+                       area.setStyles( {\r
+                               position: 'fixed',\r
+                               top: cssLength( topRect.bottom )\r
+                       } );\r
+               }\r
+\r
+               function setTopFixed() {\r
+                       area.setStyles( {\r
+                               position: 'fixed',\r
+                               top: 0\r
+                       } );\r
+               }\r
+\r
+               function setBottom() {\r
+                       area.setStyles( {\r
+                               position: 'absolute',\r
+                               top: cssLength( contentsPos.y + contentsRect.height - areaRect.height )\r
+                       } );\r
+               }\r
+\r
+               // ---------------------------------------- Vertical layout -----------------------------------------\r
+\r
+               var leftBase = area.getStyle( 'position' ) == 'fixed' ?\r
+                       contentsRect.left :\r
+                       body.getComputedStyle( 'position' ) != 'static' ? contentsPos.x - bodyPos.x : contentsPos.x;\r
+\r
+               // Content is narrower than notification\r
+               if ( contentsRect.width < notificationWidth + notificationMargin ) {\r
+\r
+                       // +---Viewport-------------------------------+\r
+                       // |                                          |\r
+                       // |                 +---Content------------+ |\r
+                       // |                 |                      | |\r
+                       // |             +------Notification------+ | |\r
+                       // |                 |                      | |\r
+                       // |                 +----------------------+ |\r
+                       // |                                          |\r
+                       // +------------------------------------------+\r
+                       if ( contentsPos.x + notificationWidth + notificationMargin > scrollPos.x + viewRect.width ) {\r
+                               setRight();\r
+\r
+                       // +---Viewport-------------------------------+               +---Viewport--------------------------+\r
+                       // |                                          |               |                                     |\r
+                       // |     +---Content------------+             |            +---Content------------+                 |\r
+                       // |     |                      |             |            |  |                   |                 |\r
+                       // |     | +------Notification------+         |    OR      | +------Notification------+             |\r
+                       // |     |                      |             |            |  |                   |                 |\r
+                       // |     +----------------------+             |            +----------------------+                 |\r
+                       // |                                          |               |                                     |\r
+                       // +------------------------------------------+               +-------------------------------------+\r
+                       } else {\r
+                               setLeft();\r
+                       }\r
+\r
+               // Content is wider than notification.\r
+               } else {\r
+\r
+                       //                       +--+Viewport+------------------------+\r
+                       //                       |                                    |\r
+                       //                       |             +---Content-----------------------------------------+\r
+                       //                       |             |                      |                            |\r
+                       //                       |             | +-----+Notification+-----+                        |\r
+                       //                       |             |                      |                            |\r
+                       //                       |             |                      |                            |\r
+                       //                       |             |                      |                            |\r
+                       //                       |             +---------------------------------------------------+\r
+                       //                       |                                    |\r
+                       //                       +------------------------------------+\r
+                       if ( contentsPos.x + notificationWidth + notificationMargin > scrollPos.x + viewRect.width ) {\r
+                               setLeft();\r
+\r
+                       //                       +---Viewport-------------------------+\r
+                       //                       |                                    |\r
+                       //                       |  +---Content----------------------------------------------+\r
+                       //                       |  |                                 |                      |\r
+                       //                       |  |      +------Notification------+ |                      |\r
+                       //                       |  |                                 |                      |\r
+                       //                       |  |                                 |                      |\r
+                       //                       |  +--------------------------------------------------------+\r
+                       //                       |                                    |\r
+                       //                       +------------------------------------+\r
+                       } else if ( contentsPos.x + contentsRect.width / 2 +\r
+                               notificationWidth / 2 + notificationMargin > scrollPos.x + viewRect.width ) {\r
+                               setRightFixed();\r
+\r
+                       //                       +---Viewport-------------------------+\r
+                       //                       |                                    |\r
+                       //   +---Content----------------------------+                 |\r
+                       //   |                   |                  |                 |\r
+                       //   |           +------Notification------+ |                 |\r
+                       //   |                   |                  |                 |\r
+                       //   |                   |                  |                 |\r
+                       //   +--------------------------------------+                 |\r
+                       //                       |                                    |\r
+                       //                       +------------------------------------+\r
+                       } else if ( contentsRect.left + contentsRect.width - notificationWidth - notificationMargin < 0 ) {\r
+                               setRight();\r
+\r
+                       //                       +---Viewport-------------------------+\r
+                       //                       |                                    |\r
+                       // +---Content---------------------------------------------+  |\r
+                       // |                     |                                 |  |\r
+                       // |                     | +------Notification------+      |  |\r
+                       // |                     |                                 |  |\r
+                       // |                     |                                 |  |\r
+                       // +-------------------------------------------------------+  |\r
+                       //                       |                                    |\r
+                       //                       +------------------------------------+\r
+                       } else if ( contentsRect.left + contentsRect.width / 2 - notificationWidth / 2 < 0 ) {\r
+                               setLeftFixed();\r
+\r
+                       //                       +---Viewport-------------------------+\r
+                       //                       |                                    |\r
+                       //                       | +---Content----------------------+ |\r
+                       //                       | |                                | |\r
+                       //                       | |    +-----Notification-----+    | |\r
+                       //                       | |                                | |\r
+                       //                       | |                                | |\r
+                       //                       | +--------------------------------+ |\r
+                       //                       |                                    |\r
+                       //                       +------------------------------------+\r
+                       } else {\r
+                               setCenter();\r
+                       }\r
+               }\r
+\r
+               function setLeft() {\r
+                       area.setStyle( 'left', cssLength( leftBase ) );\r
+               }\r
+\r
+               function setLeftFixed() {\r
+                       area.setStyle( 'left', cssLength( leftBase - contentsPos.x + scrollPos.x ) );\r
+               }\r
+\r
+               function setCenter() {\r
+                       area.setStyle( 'left', cssLength( leftBase + contentsRect.width / 2 - notificationWidth / 2 - notificationMargin / 2 ) );\r
+               }\r
+\r
+               function setRight() {\r
+                       area.setStyle( 'left', cssLength( leftBase + contentsRect.width - notificationWidth - notificationMargin ) );\r
+               }\r
+\r
+               function setRightFixed() {\r
+                       area.setStyle( 'left', cssLength( leftBase - contentsPos.x + scrollPos.x + viewRect.width -\r
+                               notificationWidth - notificationMargin ) );\r
+               }\r
+       }\r
+};\r
+\r
+CKEDITOR.plugins.notification = Notification;\r
+\r
+/**\r
+ * After how many milliseconds the notification of the `info` and `success`\r
+ * {@link CKEDITOR.plugins.notification#type type} should close automatically.\r
+ * `0` means that notifications will not close automatically.\r
+ * Note that `warning` and `progress` notifications will never close automatically.\r
+ *\r
+ * Refer to the [Notifications](http://docs.ckeditor.com/#!/guide/dev_notifications) article\r
+ * for more information about this feature.\r
+ *\r
+ * @since 4.5\r
+ * @cfg {Number} [notification_duration=5000]\r
+ * @member CKEDITOR.config\r
+ */\r
+\r
+/**\r
+ * Event fired when the {@link CKEDITOR.plugins.notification#show} method is called, before the\r
+ * notification is shown. If this event is canceled, the notification will not be shown.\r
+ *\r
+ * Using this event allows you to fully customize how a notification will be shown. It may be used to integrate\r
+ * the CKEditor notification system with your web page notifications.\r
+ *\r
+ * @since 4.5\r
+ * @event notificationShow\r
+ * @member CKEDITOR.editor\r
+ * @param data\r
+ * @param {CKEDITOR.plugins.notification} data.notification Notification which will be shown.\r
+ * @param {CKEDITOR.editor} editor The editor instance.\r
+ */\r
+\r
+/**\r
+ * Event fired when the {@link CKEDITOR.plugins.notification#update} method is called, before the\r
+ * notification is updated. If this event is canceled, the notification will not be shown even if the update was important,\r
+ * but the object will be updated anyway. Note that canceling this event does not prevent updating {@link #element}\r
+ * attributes, but if {@link #notificationShow} was canceled as well, this element is detached from the DOM.\r
+ *\r
+ * Using this event allows you to fully customize how a notification will be updated. It may be used to integrate\r
+ * the CKEditor notification system with your web page notifications.\r
+ *\r
+ * @since 4.5\r
+ * @event notificationUpdate\r
+ * @member CKEDITOR.editor\r
+ * @param data\r
+ * @param {CKEDITOR.plugins.notification} data.notification Notification which will be updated.\r
+ * Note that it contains the data that has not been updated yet.\r
+ * @param {Object} data.options Update options, see {@link CKEDITOR.plugins.notification#update}.\r
+ * @param {CKEDITOR.editor} editor The editor instance.\r
+ */\r
+\r
+/**\r
+ * Event fired when the {@link CKEDITOR.plugins.notification#hide} method is called, before the\r
+ * notification is hidden. If this event is canceled, the notification will not be hidden.\r
+ *\r
+ * Using this event allows you to fully customize how a notification will be hidden. It may be used to integrate\r
+ * the CKEditor notification system with your web page notifications.\r
+ *\r
+ * @since 4.5\r
+ * @event notificationHide\r
+ * @member CKEDITOR.editor\r
+ * @param data\r
+ * @param {CKEDITOR.plugins.notification} data.notification Notification which will be hidden.\r
+ * @param {CKEDITOR.editor} editor The editor instance.\r
+ */\r