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