Skip to content

Commit

Permalink
Fixed missing custom event and refactoring.
Browse files Browse the repository at this point in the history
 - refactor: callback and custom event trigger with `bindCustomEvent`.
 - spec change: callback's name always camel case. ex) `onfocus` to `onFocus`
 - bug fix: native command trigger twice `change`
  • Loading branch information
hackerwins committed Apr 24, 2015
1 parent 636e55e commit 95299ce
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 120 deletions.
2 changes: 1 addition & 1 deletion .jshintrc
Expand Up @@ -11,7 +11,7 @@
"unused": true,
"trailing": true,
"white": true,
"maxparams": 18,
"maxparams": 20,
"maxdepth": 5,
"maxstatements": 300,
"maxlen": 140,
Expand Down
140 changes: 51 additions & 89 deletions src/js/EventHandler.js
@@ -1,5 +1,6 @@
define([
'summernote/core/agent',
'summernote/core/func',
'summernote/core/dom',
'summernote/core/async',
'summernote/core/key',
Expand All @@ -17,7 +18,7 @@ define([
'summernote/module/LinkDialog',
'summernote/module/ImageDialog',
'summernote/module/HelpDialog'
], function (agent, dom, async, key, list, History,
], function (agent, func, dom, async, key, list, History,
Editor, Toolbar, Statusbar, Popover, Handle, Fullscreen, Codeview,
DragAndDrop, Clipboard, LinkDialog, ImageDialog, HelpDialog) {

Expand Down Expand Up @@ -74,6 +75,22 @@ define([
return this.modules[moduleName] || this.modules.editor;
};

/**
* @param {jQuery} $holder
* @param {Object} callbacks
* @param {String} eventNamespace
* @returns {Function}
*/
var bindCustomEvent = this.bindCustomEvent = function ($holder, callbacks, eventNamespace) {
return function () {
var callback = callbacks[func.namespaceToCamel(eventNamespace, 'on')];
if (callback) {
callback(arguments);
}
return $holder.trigger('summernote.' + eventNamespace, arguments);
};
};

/**
* insert Images from file array.
*
Expand All @@ -91,27 +108,18 @@ define([

// If onImageUpload options setted
if (callbacks.onImageUpload) {
callbacks.onImageUpload(files, modules.editor, $editable);
bindCustomEvent($holder, 'image.upload')([files]);
bindCustomEvent($holder, callbacks, 'image.upload')([files]);
// else insert Image as dataURL
} else {
$.each(files, function (idx, file) {
var filename = file.name;
if (options.maximumImageFileSize && options.maximumImageFileSize < file.size) {
if (callbacks.onImageUploadError) {
callbacks.onImageUploadError(options.langInfo.image.maximumFileSizeError);
bindCustomEvent($holder, 'image.upload.error')(options.langInfo.image.maximumFileSizeError);
} else {
alert(options.langInfo.image.maximumFileSizeError);
}
bindCustomEvent($holder, callbacks, 'image.upload.error')(options.langInfo.image.maximumFileSizeError);
} else {
async.readFileAsDataURL(file).then(function (sDataURL) {
modules.editor.insertImage($editable, sDataURL, filename);
}).fail(function () {
if (callbacks.onImageUploadError) {
callbacks.onImageUploadError();
bindCustomEvent($holder, 'image.upload.error')(options.langInfo.image.maximumFileSizeError);
}
bindCustomEvent($holder, callbacks, 'image.upload.error')(options.langInfo.image.maximumFileSizeError);
});
}
});
Expand Down Expand Up @@ -282,12 +290,6 @@ define([
$dimensionDisplay.html(dim.c + ' x ' + dim.r);
};

var bindCustomEvent = function ($holder, eventName) {
return function () {
return $holder.trigger('summernote.' + eventName, arguments);
};
};

/**
* bind KeyMap on keydown
*
Expand Down Expand Up @@ -337,14 +339,6 @@ define([
*
* @param {Object} layoutInfo - layout Informations
* @param {Object} options - user options include custom event handlers
* @param {function(event)} [options.onenter] - enter key handler
* @param {function(event)} [options.onfocus]
* @param {function(event)} [options.onblur]
* @param {function(event)} [options.onkeyup]
* @param {function(event)} [options.onkeydown]
* @param {function(event)} [options.onpaste]
* @param {function(event)} [options.onToolBarclick]
* @param {function(event)} [options.onChange]
*/
this.attach = function (layoutInfo, options) {
// handlers for editable
Expand Down Expand Up @@ -401,52 +395,21 @@ define([
var history = new History(layoutInfo.editable());
layoutInfo.editable().data('NoteHistory', history);

// basic event callbacks (lowercase)
// enter, focus, blur, keyup, keydown
if (options.onenter) {
layoutInfo.editable().keypress(function (event) {
if (event.keyCode === key.code.ENTER) { options.onenter(event); }
});
}

if (options.onfocus) { layoutInfo.editable().focus(options.onfocus); }
if (options.onblur) { layoutInfo.editable().blur(options.onblur); }
if (options.onkeyup) { layoutInfo.editable().keyup(options.onkeyup); }
if (options.onkeydown) { layoutInfo.editable().keydown(options.onkeydown); }
if (options.onpaste) { layoutInfo.editable().on('paste', options.onpaste); }

// callbacks for advanced features (camel)

// onToolbarClick
if (options.onToolbarClick) {
layoutInfo.toolbar().click(options.onToolbarClick);
}

// onChange
if (options.onChange) {
var hChange = function () {
modules.editor.triggerOnChange(layoutInfo.editable());
};

// [workaround] for old IE - IE8 don't have input events
if (agent.isMSIE) {
var sDomEvents = 'DOMCharacterDataModified DOMSubtreeModified DOMNodeInserted';
layoutInfo.editable().on(sDomEvents, hChange);
} else {
layoutInfo.editable().on('input', hChange);
}
}

// All editor status will be saved on editable with jquery's data
// for support multiple editor with singleton object.
layoutInfo.editable().data('callbacks', {
onBeforeChange: options.onBeforeChange,
onInit: options.onInit,
onFocus: options.onFocus,
onBlur: options.onBlur,
onKeydown: options.onKeydown,
onKeyup: options.onKeyup,
onMousedown: options.onMousedown,
onEnter: options.onEnter,
onPaste: options.onPaste,
onBeforeCommand: options.onBeforeCommand,
onChange: options.onChange,
onAutoSave: options.onAutoSave,
onImageUpload: options.onImageUpload,
onImageUploadError: options.onImageUploadError,
onFileUpload: options.onFileUpload,
onFileUploadError: options.onFileUpload,
onMediaDelete : options.onMediaDelete
});

Expand All @@ -472,49 +435,48 @@ define([
this.attachCustomEvent = function (layoutInfo, options) {
var $holder = layoutInfo.holder();
var $editable = layoutInfo.editable();
var callbacks = $editable.data('callbacks');

$editable.on('mousedown', bindCustomEvent($holder, 'mousedown'));
$editable.on('keyup mouseup', bindCustomEvent($holder, 'update'));
$editable.on('scroll', bindCustomEvent($holder, 'scroll'));
$editable.focus(bindCustomEvent($holder, callbacks, 'focus'));
$editable.blur(bindCustomEvent($holder, callbacks, 'blur'));

// basic event callbacks (lowercase)
// enter, focus, blur, keyup, keydown
$editable.keypress(function (event) {
$editable.keydown(function (event) {
if (event.keyCode === key.code.ENTER) {
bindCustomEvent($holder, 'enter').call(this, event);
bindCustomEvent($holder, callbacks, 'enter').call(this, event);
}
bindCustomEvent($holder, callbacks, 'keydown').call(this, event);
});
$editable.keyup(bindCustomEvent($holder, callbacks, 'keyup'));

$editable.focus(bindCustomEvent($holder, 'focus'));
$editable.blur(bindCustomEvent($holder, 'blur'));
$editable.keyup(bindCustomEvent($holder, 'keyup'));
$editable.keydown(bindCustomEvent($holder, 'keydown'));
$editable.on('paste', bindCustomEvent($holder, 'paste'));
$editable.on('mousedown', bindCustomEvent($holder, callbacks, 'mousedown'));
$editable.on('mouseup', bindCustomEvent($holder, callbacks, 'mouseup'));
$editable.on('scroll', bindCustomEvent($holder, callbacks, 'scroll'));

// callbacks for advanced features (camel)
if (!options.airMode) {
layoutInfo.toolbar().click(bindCustomEvent($holder, 'toolbar.click'));
layoutInfo.popover().click(bindCustomEvent($holder, 'popover.click'));
}
$editable.on('paste', bindCustomEvent($holder, callbacks, 'paste'));

// [workaround] for old IE - IE8 don't have input events
if (agent.isMSIE) {
var sDomEvents = 'DOMCharacterDataModified DOMSubtreeModified DOMNodeInserted';
$editable.on(sDomEvents, bindCustomEvent($holder, 'change'));
$editable.on(sDomEvents, bindCustomEvent($holder, callbacks, 'change'));
} else {
$editable.on('input', bindCustomEvent($holder, 'change'));
$editable.on('input', bindCustomEvent($holder, callbacks, 'change'));
}

// callbacks for advanced features (camel)
if (!options.airMode) {
layoutInfo.toolbar().click(bindCustomEvent($holder, callbacks, 'toolbar.click'));
layoutInfo.popover().click(bindCustomEvent($holder, callbacks, 'popover.click'));
}

// Textarea: auto filling the code before form submit.
if (dom.isTextarea(list.head($holder))) {
$holder.closest('form').submit(function (e) {
var contents = $holder.code();
bindCustomEvent($holder, 'submit').call(this, e, contents);
bindCustomEvent($holder, callbacks, 'submit').call(this, e, $holder.code());
});
}

// fire init event
bindCustomEvent($holder, 'init')(layoutInfo);
bindCustomEvent($holder, callbacks, 'init')(layoutInfo);

// fire plugin init event
for (var i = 0, len = $.summernote.plugins.length; i < len; i++) {
Expand Down
4 changes: 3 additions & 1 deletion src/js/Renderer.js
@@ -1,5 +1,7 @@
define([
'summernote/core/agent', 'summernote/core/dom', 'summernote/core/func'
'summernote/core/agent',
'summernote/core/dom',
'summernote/core/func'
], function (agent, dom, func) {
/**
* @class Renderer
Expand Down
22 changes: 13 additions & 9 deletions src/js/core/dom.js
Expand Up @@ -955,6 +955,18 @@ define([

var isTextarea = makePredByNodeName('TEXTAREA');

/**
* @param {jQuery} $node
* @param {Boolean} [stripLinebreaks] - default: false
*/
var value = function ($node, stripLinebreaks) {
var val = isTextarea($node[0]) ? $node.val() : $node.html();
if (stripLinebreaks) {
return val.replace(/[\n\r]/g, '');
}
return val;
};

/**
* @method html
*
Expand All @@ -964,7 +976,7 @@ define([
* @param {Boolean} [isNewlineOnBlock]
*/
var html = function ($node, isNewlineOnBlock) {
var markup = isTextarea($node[0]) ? $node.val() : $node.html();
var markup = value($node);

if (isNewlineOnBlock) {
var regexTag = /<(\/?)(\b(?!!)[^>\s]*)(.*?)(\s*\/?>)/g;
Expand All @@ -982,14 +994,6 @@ define([
return markup;
};

var value = function ($textarea, stripLinebreaks) {
var val = $textarea.val();
if (stripLinebreaks) {
return val.replace(/[\n\r]/g, '');
}
return val;
};

return {
/** @property {String} NBSP_CHAR */
NBSP_CHAR: NBSP_CHAR,
Expand Down
15 changes: 14 additions & 1 deletion src/js/core/func.js
Expand Up @@ -98,6 +98,18 @@ define('summernote/core/func', function () {
return inverted;
};

/**
* @param {String} namespace
* @param {String} [prefix]
* @return {String}
*/
var namespaceToCamel = function (namespace, prefix) {
prefix = prefix || '';
return prefix + namespace.split('.').map(function (name) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}).join('');
};

return {
eq: eq,
eq2: eq2,
Expand All @@ -109,7 +121,8 @@ define('summernote/core/func', function () {
and: and,
uniqueId: uniqueId,
rect2bnd: rect2bnd,
invertObject: invertObject
invertObject: invertObject,
namespaceToCamel: namespaceToCamel
};
})();

Expand Down
38 changes: 20 additions & 18 deletions src/js/module/Editor.js
Expand Up @@ -17,7 +17,7 @@ define([
* Editor
*
*/
var Editor = function () {
var Editor = function (handler) {

var style = new Style();
var table = new Table();
Expand Down Expand Up @@ -111,18 +111,18 @@ define([
return rng ? rng.isOnEditable() && style.current(rng, target) : false;
};

var triggerOnBeforeChange = this.triggerOnBeforeChange = function ($editable) {
var onBeforeChange = $editable.data('callbacks').onBeforeChange;
if (onBeforeChange) {
onBeforeChange($editable.html(), $editable);
}
var triggerOnBeforeChange = function ($editable) {
// TODO find holder
handler.bindCustomEvent(
$(), $editable.data('callbacks'), 'before.command'
).call($editable.html(), $editable);
};

var triggerOnChange = this.triggerOnChange = function ($editable) {
var onChange = $editable.data('callbacks').onChange;
if (onChange) {
onChange($editable.html(), $editable);
}
var triggerOnChange = function ($editable) {
// TODO find holder
handler.bindCustomEvent(
$(), $editable.data('callbacks'), 'change'
).call($editable.html(), $editable);
};

/**
Expand Down Expand Up @@ -160,10 +160,13 @@ define([
* @method afterCommand
* after command
* @param {jQuery} $editable
* @param {Boolean} isPreventTrigger
*/
var afterCommand = this.afterCommand = function ($editable) {
var afterCommand = this.afterCommand = function ($editable, isPreventTrigger) {
$editable.data('NoteHistory').recordUndo();
triggerOnChange($editable);
if (!isPreventTrigger) {
triggerOnChange($editable);
}
};

/**
Expand Down Expand Up @@ -285,7 +288,7 @@ define([

document.execCommand(sCmd, false, value);

afterCommand($editable);
afterCommand($editable, true);
};
})(commands[idx]);
}
Expand Down Expand Up @@ -723,10 +726,9 @@ define([
beforeCommand($editable);
$target.detach();

var callbacks = $editable.data('callbacks');
if (callbacks && callbacks.onMediaDelete) {
callbacks.onMediaDelete($target, this, $editable);
}
handler.bindCustomEvent(
$(), $editable.data('callbacks'), 'media.delete'
).call($target, this.$editable);

afterCommand($editable);
};
Expand Down

0 comments on commit 95299ce

Please sign in to comment.