123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or http://ckeditor.com/license
- */
- /**
- * @fileOverview The "Notification Aggregator" plugin.
- *
- */
- ( function() {
- 'use strict';
- CKEDITOR.plugins.add( 'notificationaggregator', {
- requires: 'notification'
- } );
- /**
- * An aggregator of multiple tasks (progresses) which should be displayed using one
- * {@link CKEDITOR.plugins.notification notification}.
- *
- * Once all the tasks are done, it means that the whole process is finished and the {@link #finished}
- * event will be fired.
- *
- * New tasks can be created after the previous set of tasks is finished. This will continue the process and
- * fire the {@link #finished} event again when it is done.
- *
- * Simple usage example:
- *
- * // Declare one aggregator that will be used for all tasks.
- * var aggregator;
- *
- * // ...
- *
- * // Create a new aggregator if the previous one finished all tasks.
- * if ( !aggregator || aggregator.isFinished() ) {
- * // Create a new notification aggregator instance.
- * aggregator = new CKEDITOR.plugins.notificationAggregator( editor, 'Loading process, step {current} of {max}...' );
- *
- * // Change the notification type to 'success' on finish.
- * aggregator.on( 'finished', function() {
- * aggregator.notification.update( { message: 'Done', type: 'success' } );
- * } );
- * }
- *
- * // Create 3 tasks.
- * var taskA = aggregator.createTask(),
- * taskB = aggregator.createTask(),
- * taskC = aggregator.createTask();
- *
- * // At this point the notification contains a message: "Loading process, step 0 of 3...".
- *
- * // Let's close the first one immediately.
- * taskA.done(); // "Loading process, step 1 of 3...".
- *
- * // One second later the message will be: "Loading process, step 2 of 3...".
- * setTimeout( function() {
- * taskB.done();
- * }, 1000 );
- *
- * // Two seconds after the previous message the last task will be completed, meaning that
- * // the notification will be closed.
- * setTimeout( function() {
- * taskC.done();
- * }, 3000 );
- *
- * @since 4.5
- * @class CKEDITOR.plugins.notificationAggregator
- * @mixins CKEDITOR.event
- * @constructor Creates a notification aggregator instance.
- * @param {CKEDITOR.editor} editor
- * @param {String} message The template for the message to be displayed in the notification. The template can use
- * the following variables:
- *
- * * `{current}` – The number of completed tasks.
- * * `{max}` – The number of tasks.
- * * `{percentage}` – The progress (number 0-100).
- *
- * @param {String/null} [singularMessage=null] An optional template for the message to be displayed in the notification
- * when there is only one task remaining. This template can use the same variables as the `message` template.
- * If `null`, then the `message` template will be used.
- */
- function Aggregator( editor, message, singularMessage ) {
- /**
- * @readonly
- * @property {CKEDITOR.editor} editor
- */
- this.editor = editor;
- /**
- * Notification created by the aggregator.
- *
- * The notification object is modified as aggregator tasks are being closed.
- *
- * @readonly
- * @property {CKEDITOR.plugins.notification/null}
- */
- this.notification = null;
- /**
- * A template for the notification message.
- *
- * The template can use the following variables:
- *
- * * `{current}` – The number of completed tasks.
- * * `{max}` – The number of tasks.
- * * `{percentage}` – The progress (number 0-100).
- *
- * @private
- * @property {CKEDITOR.template}
- */
- this._message = new CKEDITOR.template( message );
- /**
- * A template for the notification message used when only one task is loading.
- *
- * Sometimes there might be a need to specify a special message when there
- * is only one task loading, and to display standard messages in other cases.
- *
- * For example, you might want to show a message "Translating a widget" rather than
- * "Translating widgets (1 of 1)", but still you would want to have a message
- * "Translating widgets (2 of 3)" if more widgets are being translated at the same
- * time.
- *
- * Template variables are the same as in {@link #_message}.
- *
- * @private
- * @property {CKEDITOR.template/null}
- */
- this._singularMessage = singularMessage ? new CKEDITOR.template( singularMessage ) : null;
- // Set the _tasks, _totalWeights, _doneWeights, _doneTasks properties.
- this._tasks = [];
- this._totalWeights = 0;
- this._doneWeights = 0;
- this._doneTasks = 0;
- /**
- * Array of tasks tracked by the aggregator.
- *
- * @private
- * @property {CKEDITOR.plugins.notificationAggregator.task[]} _tasks
- */
- /**
- * Stores the sum of declared weights for all contained tasks.
- *
- * @private
- * @property {Number} _totalWeights
- */
- /**
- * Stores the sum of done weights for all contained tasks.
- *
- * @private
- * @property {Number} _doneWeights
- */
- /**
- * Stores the count of tasks done.
- *
- * @private
- * @property {Number} _doneTasks
- */
- }
- Aggregator.prototype = {
- /**
- * Creates a new task that can be updated to indicate the progress.
- *
- * @param [options] Options object for the task creation.
- * @param [options.weight] For more information about weight, see the
- * {@link CKEDITOR.plugins.notificationAggregator.task} overview.
- * @returns {CKEDITOR.plugins.notificationAggregator.task} An object that represents the task state, and allows
- * for its manipulation.
- */
- createTask: function( options ) {
- options = options || {};
- var initialTask = !this.notification,
- task;
- if ( initialTask ) {
- // It's a first call.
- this.notification = this._createNotification();
- }
- task = this._addTask( options );
- task.on( 'updated', this._onTaskUpdate, this );
- task.on( 'done', this._onTaskDone, this );
- task.on( 'canceled', function() {
- this._removeTask( task );
- }, this );
- // Update the aggregator.
- this.update();
- if ( initialTask ) {
- this.notification.show();
- }
- return task;
- },
- /**
- * Triggers an update on the aggregator, meaning that its UI will be refreshed.
- *
- * When all the tasks are done, the {@link #finished} event is fired.
- */
- update: function() {
- this._updateNotification();
- if ( this.isFinished() ) {
- this.fire( 'finished' );
- }
- },
- /**
- * Returns a number from `0` to `1` representing the done weights to total weights ratio
- * (showing how many of the tasks are done).
- *
- * Note: For an empty aggregator (without any tasks created) it will return `1`.
- *
- * @returns {Number} Returns the percentage of tasks done as a number ranging from `0` to `1`.
- */
- getPercentage: function() {
- // In case there are no weights at all we'll return 1.
- if ( this.getTaskCount() === 0 ) {
- return 1;
- }
- return this._doneWeights / this._totalWeights;
- },
- /**
- * @returns {Boolean} Returns `true` if all notification tasks are done
- * (or there are no tasks at all).
- */
- isFinished: function() {
- return this.getDoneTaskCount() === this.getTaskCount();
- },
- /**
- * @returns {Number} Returns a total tasks count.
- */
- getTaskCount: function() {
- return this._tasks.length;
- },
- /**
- * @returns {Number} Returns the number of tasks done.
- */
- getDoneTaskCount: function() {
- return this._doneTasks;
- },
- /**
- * Updates the notification content.
- *
- * @private
- */
- _updateNotification: function() {
- this.notification.update( {
- message: this._getNotificationMessage(),
- progress: this.getPercentage()
- } );
- },
- /**
- * Returns a message used in the notification.
- *
- * @private
- * @returns {String}
- */
- _getNotificationMessage: function() {
- var tasksCount = this.getTaskCount(),
- doneTasks = this.getDoneTaskCount(),
- templateParams = {
- current: doneTasks,
- max: tasksCount,
- percentage: Math.round( this.getPercentage() * 100 )
- },
- template;
- // If there's only one remaining task and we have a singular message, we should use it.
- if ( tasksCount == 1 && this._singularMessage ) {
- template = this._singularMessage;
- } else {
- template = this._message;
- }
- return template.output( templateParams );
- },
- /**
- * Creates a notification object.
- *
- * @private
- * @returns {CKEDITOR.plugins.notification}
- */
- _createNotification: function() {
- return new CKEDITOR.plugins.notification( this.editor, {
- type: 'progress'
- } );
- },
- /**
- * Creates a {@link CKEDITOR.plugins.notificationAggregator.task} instance based
- * on `options`, and adds it to the task list.
- *
- * @private
- * @param options Options object coming from the {@link #createTask} method.
- * @returns {CKEDITOR.plugins.notificationAggregator.task}
- */
- _addTask: function( options ) {
- var task = new Task( options.weight );
- this._tasks.push( task );
- this._totalWeights += task._weight;
- return task;
- },
- /**
- * Removes a given task from the {@link #_tasks} array and updates the UI.
- *
- * @private
- * @param {CKEDITOR.plugins.notificationAggregator.task} task Task to be removed.
- */
- _removeTask: function( task ) {
- var index = CKEDITOR.tools.indexOf( this._tasks, task );
- if ( index !== -1 ) {
- // If task was already updated with some weight, we need to remove
- // this weight from our cache.
- if ( task._doneWeight ) {
- this._doneWeights -= task._doneWeight;
- }
- this._totalWeights -= task._weight;
- this._tasks.splice( index, 1 );
- // And we also should inform the UI about this change.
- this.update();
- }
- },
- /**
- * A listener called on the {@link CKEDITOR.plugins.notificationAggregator.task#update} event.
- *
- * @private
- * @param {CKEDITOR.eventInfo} evt Event object of the {@link CKEDITOR.plugins.notificationAggregator.task#update} event.
- */
- _onTaskUpdate: function( evt ) {
- this._doneWeights += evt.data;
- this.update();
- },
- /**
- * A listener called on the {@link CKEDITOR.plugins.notificationAggregator.task#event-done} event.
- *
- * @private
- * @param {CKEDITOR.eventInfo} evt Event object of the {@link CKEDITOR.plugins.notificationAggregator.task#event-done} event.
- */
- _onTaskDone: function() {
- this._doneTasks += 1;
- this.update();
- }
- };
- CKEDITOR.event.implementOn( Aggregator.prototype );
- /**
- * # Overview
- *
- * This type represents a single task in the aggregator, and exposes methods to manipulate its state.
- *
- * ## Weights
- *
- * Task progess is based on its **weight**.
- *
- * As you create a task, you need to declare its weight. As you want the update to inform about the
- * progress, you will need to {@link #update} the task, telling how much of this weight is done.
- *
- * For example, if you declare that your task has a weight that equals `50` and then call `update` with `10`,
- * you will end up with telling that the task is done in 20%.
- *
- * ### Example Usage of Weights
- *
- * Let us say that you use tasks for file uploading.
- *
- * A single task is associated with a single file upload. You can use the file size in bytes as a weight,
- * and then as the file upload progresses you just call the `update` method with the number of bytes actually
- * downloaded.
- *
- * @since 4.5
- * @class CKEDITOR.plugins.notificationAggregator.task
- * @mixins CKEDITOR.event
- * @constructor Creates a task instance for notification aggregator.
- * @param {Number} [weight=1]
- */
- function Task( weight ) {
- /**
- * Total weight of the task.
- *
- * @private
- * @property {Number}
- */
- this._weight = weight || 1;
- /**
- * Done weight of the task.
- *
- * @private
- * @property {Number}
- */
- this._doneWeight = 0;
- /**
- * Indicates when the task is canceled.
- *
- * @private
- * @property {Boolean}
- */
- this._isCanceled = false;
- }
- Task.prototype = {
- /**
- * Marks the task as done.
- */
- done: function() {
- this.update( this._weight );
- },
- /**
- * Updates the done weight of a task.
- *
- * @param {Number} weight Number indicating how much of the total task {@link #_weight} is done.
- */
- update: function( weight ) {
- // If task is already done or canceled there is no need to update it, and we don't expect
- // progress to be reversed.
- if ( this.isDone() || this.isCanceled() ) {
- return;
- }
- // Note that newWeight can't be higher than _doneWeight.
- var newWeight = Math.min( this._weight, weight ),
- weightChange = newWeight - this._doneWeight;
- this._doneWeight = newWeight;
- // Fire updated event even if task is done in order to correctly trigger updating the
- // notification's message. If we wouldn't do this, then the last weight change would be ignored.
- this.fire( 'updated', weightChange );
- if ( this.isDone() ) {
- this.fire( 'done' );
- }
- },
- /**
- * Cancels the task (the task will be removed from the aggregator).
- */
- cancel: function() {
- // If task is already done or canceled.
- if ( this.isDone() || this.isCanceled() ) {
- return;
- }
- // Mark task as canceled.
- this._isCanceled = true;
- // We'll fire cancel event it's up to aggregator to listen for this event,
- // and remove the task.
- this.fire( 'canceled' );
- },
- /**
- * Checks if the task is done.
- *
- * @returns {Boolean}
- */
- isDone: function() {
- return this._weight === this._doneWeight;
- },
- /**
- * Checks if the task is canceled.
- *
- * @returns {Boolean}
- */
- isCanceled: function() {
- return this._isCanceled;
- }
- };
- CKEDITOR.event.implementOn( Task.prototype );
- /**
- * Fired when all tasks are done. When this event occurs, the notification may change its type to `success` or be hidden:
- *
- * aggregator.on( 'finished', function() {
- * if ( aggregator.getTaskCount() == 0 ) {
- * aggregator.notification.hide();
- * } else {
- * aggregator.notification.update( { message: 'Done', type: 'success' } );
- * }
- * } );
- *
- * @event finished
- * @member CKEDITOR.plugins.notificationAggregator
- */
- /**
- * Fired upon each weight update of the task.
- *
- * var myTask = new Task( 100 );
- * myTask.update( 30 );
- * // Fires updated event with evt.data = 30.
- * myTask.update( 40 );
- * // Fires updated event with evt.data = 10.
- * myTask.update( 20 );
- * // Fires updated event with evt.data = -20.
- *
- * @event updated
- * @param {Number} data The difference between the new weight and the previous one.
- * @member CKEDITOR.plugins.notificationAggregator.task
- */
- /**
- * Fired when the task is done.
- *
- * @event done
- * @member CKEDITOR.plugins.notificationAggregator.task
- */
- /**
- * Fired when the task is canceled.
- *
- * @event canceled
- * @member CKEDITOR.plugins.notificationAggregator.task
- */
- // Expose Aggregator type.
- CKEDITOR.plugins.notificationAggregator = Aggregator;
- CKEDITOR.plugins.notificationAggregator.task = Task;
- } )();
|