Skip to content

Commit

Permalink
Merge branch 't/12901' into major
Browse files Browse the repository at this point in the history
  • Loading branch information
Reinmar committed Mar 9, 2015
2 parents fb2942a + 752a1b0 commit 88d5cae
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 151 deletions.
12 changes: 9 additions & 3 deletions plugins/notification/plugin.js
Expand Up @@ -249,15 +249,21 @@ Notification.prototype = {

var element = this.element,
messageElement = element.findOne( '.cke_notification_message' ),
progressElement = element.findOne( '.cke_notification_progress' );
progressElement = element.findOne( '.cke_notification_progress' ),
type = options.type;

element.removeAttribute( 'role' );

if ( options.type ) {
// 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 = options.type;
this.type = type;

element.addClass( this._getClass() );
element.setAttribute( 'aria-label', this.type );
Expand Down
69 changes: 30 additions & 39 deletions plugins/notificationaggregator/plugin.js
Expand Up @@ -19,17 +19,29 @@
* 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.
* Once finished the aggregator state will be reset and the {@link #finished} event will be fired.
* 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 was finished. This will reopen the
* notification with message describing only the tasks that are currently in progress.
* Thanks to this a developer does not have to create multiple aggregator instances.
* New tasks can be created after the previous set of tasks was finished. This will continue the process and
* fire {@link #finished} again when it is done.
*
* Simple usage example:
*
* // Create a new notification aggregator instance.
* var aggregator = new CKEDITOR.plugins.notificationAggregator( editor, 'Loading process, step {current} of {max}...' );
* // Declare one aggregator will be used for all tasks.
* var aggregator;
*
* // ...
*
* // Create a new aggregator if the previous one fihished 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(),
Expand Down Expand Up @@ -118,7 +130,10 @@
this._singularMessage = singularMessage ? new CKEDITOR.template( singularMessage ) : null;

// Set the _tasks, _totalWeights, _doneWeights, _doneTasks properties.
this._reset();
this._tasks = [];
this._totalWeights = 0;
this._doneWeights = 0;
this._doneTasks = 0;

/**
* Array of tasks tracked by the aggregator.
Expand Down Expand Up @@ -197,7 +212,7 @@
this._updateNotification();

if ( this.isFinished() ) {
this._finish();
this.fire( 'finished' );
}
},

Expand Down Expand Up @@ -240,18 +255,6 @@
return this._doneTasks;
},

/**
* Should be called when all tasks are done.
*/
_finish: function() {
if ( this.fire( 'finished' ) !== false ) {
this.notification.hide();
this.notification = null;
}

this._reset();
},

/**
* Updates the notification content.
*
Expand Down Expand Up @@ -317,18 +320,6 @@
return task;
},

/**
* Resets the internal state of the aggregator.
*
* @private
*/
_reset: function() {
this._tasks = [];
this._totalWeights = 0;
this._doneWeights = 0;
this._doneTasks = 0;
},

/**
* Removes given task from the {@link #_tasks} array and updates the UI.
*
Expand Down Expand Up @@ -480,14 +471,14 @@
CKEDITOR.event.implementOn( Task.prototype );

/**
* Fired when all tasks are done.
*
* It can be canceled to customize how the notification should be closed.
*
* This event might be used eg. to display a follow-up success message.
* Fired when all tasks are done. On this event notification may change type to success or be hidden:
*
* aggregator.on( 'finished', function() {
* editor.showNotification( 'Uploaded ' + this.getDoneTasksCount() + ' files.', 'success', 2000 );
* if ( aggregator.getTasksCount() == 0 ) {
* aggregator.notification.hide();
* } else {
* aggregator.notification.update( { message: 'Done', type: 'success' } );
* }
* } );
*
* @event finished
Expand Down
4 changes: 3 additions & 1 deletion plugins/uploadwidget/lang/en.js
Expand Up @@ -5,7 +5,9 @@

CKEDITOR.plugins.setLang( 'uploadwidget', 'en', {
abort: 'Upload aborted by user.',
done: 'Uploading done.',
doneOne: 'File successfully uploaded.',
doneMany: 'Successfully uploaded %1 files.',
uploadOne: 'Uploading file ({percentage}%)...',
uploadMany: 'Uploading files, {current} of {max} done ({percentage}%)...'

} );
74 changes: 55 additions & 19 deletions plugins/uploadwidget/plugin.js
Expand Up @@ -15,19 +15,6 @@
// because otherwise wrong widget may handle upload placeholder element (e.g. image2 plugin would handle image).
// `data-widget` attribute is allowed only in the elements which has also `data-cke-upload-id` attribute.
editor.filter.allow( '*[!data-widget,!data-cke-upload-id]' );

// Create one notification agregator for all types of upload widgets for editor.
var aggregator = editor._.uploadWidgetNotificaionAggregator =
new CKEDITOR.plugins.notificationAggregator(
editor,
editor.lang.uploadwidget.uploadMany,
editor.lang.uploadwidget.uploadOne );

aggregator.on( 'finished', function() {
if ( aggregator.getTasksCount() > 0 ) {
editor.showNotification( editor.lang.uploadwidget.done, 'success' );
}
} );
}
} );

Expand Down Expand Up @@ -116,6 +103,32 @@
*
* fileTools.markElement( el, 'filereader', loader.id );
*
* evt.data.dataValue += el.getOuterHtml();
* }
* }
* } );
*
* Note that you can bind notifications to the upload widget on paste using
* {@link CKEDITOR.fileTools.bindNotifications bindNotifications} method, so notifications will automatically
* show the progress of the upload. Because this method show notification about upload do not use it if you only
* {@link CKEDITOR.fileTools.fileLoader#load load} (not upload) file.
*
* editor.on( 'paste', function( evt ) {
* var file, i, el, loader;
*
* for ( i = 0; i < evt.data.dataTransfer.getFilesCount(); i++ ) {
* file = evt.data.dataTransfer.getFile( i );
*
* if ( CKEDITOR.fileTools.isTypeSupported( file, /text\/pdf/ ) ) {
* el = new CKEDITOR.dom.element( 'span' ),
* loader = editor.uploadsRepository.create( file );
*
* el.setText( '...' );
*
* loader.upload();
*
* fileTools.markElement( el, 'pdfuploader', loader.id );
*
* fileTools.bindNotifications( editor, loader );
*
* evt.data.dataValue += el.getOuterHtml();
Expand Down Expand Up @@ -159,7 +172,9 @@

CKEDITOR.fileTools.markElement( el, name, loader.id );

CKEDITOR.fileTools.bindNotifications( editor, loader );
if ( loadMethod == 'loadAndUpload' || loadMethod == 'upload' ) {
CKEDITOR.fileTools.bindNotifications( editor, loader );
}

data.dataValue += el.getOuterHtml();
}
Expand Down Expand Up @@ -417,17 +432,38 @@
* notification to show the status and progress.
* This function uses {@link CKEDITOR.plugins.notificationAggregator}, so even if multiple files are uploading
* only one notification is shown. The exception are warnings, because they are shown in the separate notifications.
* This notification show only progress of the upload so this method should not be used if
* {@link CKEDITOR.fileTools.fileLoader#load loader.load} method was called, it works with
* {@link CKEDITOR.fileTools.fileLoader#upload upload} and {@link CKEDITOR.fileTools.fileLoader#loadAndUpload loadAndUpload}.
*
* @param {CKEDITOR.editor} editor The editor instance.
* @param {CKEDITOR.fileTools.fileLoader} loader The fileLoader instance.
*/
function bindNotifications( editor, loader ) {
var aggregator = editor._.uploadWidgetNotificaionAggregator,
task;
var aggregator = editor._.uploadWidgetNotificaionAggregator;

loader.on( 'uploading', function() {
task = aggregator.createTask( { weight: loader.total } );
} );
// Create one notification agregator for all types of upload widgets for editor.
if ( !aggregator || aggregator.isFinished() ) {
aggregator = editor._.uploadWidgetNotificaionAggregator = new CKEDITOR.plugins.notificationAggregator(
editor, editor.lang.uploadwidget.uploadMany, editor.lang.uploadwidget.uploadOne );

aggregator.once( 'finished', function() {
var tasks = aggregator.getTasksCount();

if ( tasks === 0 ) {
aggregator.notification.hide();
} else {
aggregator.notification.update( {
message: tasks == 1 ?
editor.lang.uploadwidget.doneOne :
editor.lang.uploadwidget.doneMany.replace( '%1', tasks ),
type: 'success'
} );
}
} );
}

var task = aggregator.createTask( { weight: loader.total } );

loader.on( 'update', function() {
if ( task && loader.status == 'uploading' ) {
Expand Down
12 changes: 4 additions & 8 deletions tests/plugins/notification/notification.js
Expand Up @@ -365,19 +365,15 @@ bender.test( {
assertNotifications( editor, [ { message: 'Foo', type: 'success', alert: false } ] );
},

'test update progress when type is not progress': function() {
'test notification change type if progress is set': function() {
var editor = this.editor,
notification = new CKEDITOR.plugins.notification( editor, { message: 'Foo' } );
notification = new CKEDITOR.plugins.notification( editor, { message: 'Foo', type: 'warning' } );

notification.show();

assertNotifications( editor, [ { message: 'Foo', type: 'info' } ] );

notification.update( { progress: 0.5 } ); // There should be no error!

assertNotifications( editor, [ { message: 'Foo', type: 'info', alert: false } ] );
assertNotifications( editor, [ { message: 'Foo', type: 'warning', alert: true } ] );

notification.update( { type: 'progress' } );
notification.update( { progress: 0.5 } );

assertNotifications( editor, [ { message: 'Foo', type: 'progress', progress: 0.5, alert: false } ] );
},
Expand Down
51 changes: 6 additions & 45 deletions tests/plugins/notificationaggregator/aggregator.js
Expand Up @@ -206,23 +206,24 @@
},

'test update finished': function() {
var instance = new Aggregator( this.editor, '' );
var instance = new Aggregator( this.editor, '' ),
finishSpy = sinon.spy();

instance.isFinished = sinon.stub().returns( true );
instance._updateNotification = sinon.spy();
instance._reset = sinon.spy();
instance._finish = sinon.spy();

instance.on( 'finished', finishSpy );

instance.update();

assert.areSame( 1, instance._updateNotification.callCount, '_updateNotification call count' );
assert.areSame( 1, instance._finish.callCount, '_finish call count' );
assert.areSame( 1, finishSpy.callCount, 'finished events count' );
},

'test update - cancel finished event': function() {
var instance = new Aggregator( this.editor, '' );
instance.isFinished = sinon.stub().returns( true );
instance._updateNotification = sinon.spy();
instance._reset = sinon.spy();
instance.fire = sinon.stub().returns( false );
instance.finished = sinon.spy();

Expand All @@ -232,37 +233,6 @@
assert.areSame( 0, instance.finished.callCount, 'finished call count' );
},

'test _finish': function() {
var instance = new Aggregator( this.editor, '' ),
notif = new NotificationMock(),
finishedSpy = sinon.spy();

instance.notification = notif;
instance.on( 'finished', finishedSpy );
instance._reset = sinon.spy();

instance._finish();

assert.areSame( 1, notif.hide.callCount, 'notification.hide call count' );
assert.areSame( 1, finishedSpy.callCount, 'finished event fire count' );
assert.areSame( 1, instance._reset.callCount, 'instance._reset call count' );
},

// Ensure that _reset() is called **after** finished event was fired. (#12874)
'test _finish resets after finished event': function() {
var instance = new Aggregator( this.editor, '' );

instance.notification = new NotificationMock();
instance.fire = function() {
assert.areSame( 0, instance._reset.callCount, 'instance._reset should not be called before firing finished event' );
};
instance._reset = sinon.spy();

instance._finish();

assert.areSame( 1, instance._reset.callCount, 'instance._reset call count' );
},

'test _updateNotification': function() {
var instance = new Aggregator( this.editor, '' ),
expectedParams = {
Expand Down Expand Up @@ -343,15 +313,6 @@
assert.areSame( 1, instance._tasks.length, 'instance._tasks length' );
},

'test _reset': function() {
var instance = new Aggregator( this.editor, '' );
instance._tasks = [ 1, 2 ];

instance._reset();

assert.areSame( 0, instance._tasks.length, 'instance._tasks cleared' );
},

'test _getNotificationMessage': function() {
var instance = new Aggregator( this.editor, '' );
instance._message = {
Expand Down
2 changes: 1 addition & 1 deletion tests/plugins/notificationaggregator/manual/classic.html
Expand Up @@ -12,7 +12,7 @@
curTask;

notification.on( 'finished', function() {
editor.showNotification( 'Everything is working just fine.', 'success', 2000 );
notification.notification.update( { message: 'Everything is working just fine.', type: 'success' } );
} );

for ( var i = 0; i < 20; i++ ) {
Expand Down
5 changes: 1 addition & 4 deletions tests/plugins/notificationaggregator/manual/classic.md
Expand Up @@ -9,7 +9,4 @@

**Expected:**

As loading is done:

* Progress notification dissapears.
* New successs notification is shown.
As loading is done progress notification change its type to success. Success notification should be hidden after 5s.

0 comments on commit 88d5cae

Please sign in to comment.