summernote
Advanced tools
Comparing version 0.6.6 to 0.6.7
{ | ||
"name": "summernote", | ||
"version": "0.6.6", | ||
"version": "0.6.7", | ||
"main": [ | ||
@@ -5,0 +5,0 @@ "./dist/summernote.js", |
(function ($) { | ||
console.log('hit'); | ||
$.extend($.summernote.lang, { | ||
@@ -4,0 +3,0 @@ 'ko-KR': { |
{ | ||
"name": "summernote", | ||
"description": "Super Simple WYSIWYG Editor on Bootstrap", | ||
"version": "0.6.6", | ||
"version": "0.6.7", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "editor", |
@@ -99,9 +99,8 @@ # Summernote | ||
summernote.js - Renderer.js (Generate markup) - Locale.js (Locale object) | ||
ㄴEventHandler.js - Editor.js (Abstract editor) | ||
ㄴStyle.js (Style Getter and Setter) | ||
ㄴHistory.js (Store on jQuery.data) | ||
ㄴToolbar.js (Toolbar module) | ||
ㄴEventHandler.js - Toolbar.js (Toolbar module) | ||
ㄴPopover.js (Popover module) | ||
ㄴHandle.js (Handle module) | ||
ㄴDialog.js (Dialog module) | ||
ㄴEditor.js (Abstract editor module) - Style.js (Style Getter and Setter) | ||
ㄴHistory.js (Store on jQuery.data) | ||
-----------------------------Core Script----------------------------- | ||
@@ -117,2 +116,19 @@ agent.js (agent information) | ||
#### document structure | ||
``` | ||
- body container: <div class="note-editable">, <td>, <blockquote>, <ul> | ||
- block node: <div>, <p>, <li>, <h1>, <table> | ||
- void block node: <hr> | ||
- inline node: <span>, <b>, <font>, <a>, ... | ||
- void inline node: <img> | ||
- text node: #text | ||
``` | ||
1. A body container has block node, but `<ul>` has only `<li>` nodes. | ||
2. A body container also has inline nodes sometimes. This inline nodes will be wraped with `<p>` when enter key pressed. | ||
4. A block node only has inline nodes. | ||
5. A inline nodes has another inline nodes | ||
6. `#text` and void inline node doesn't have children. | ||
#### build summernote | ||
@@ -119,0 +135,0 @@ ```bash |
@@ -40,74 +40,4 @@ require.config({ | ||
theme: 'monokai' | ||
}, | ||
onInit: function () { | ||
console.log('init', arguments, $('.summernote')[0] === this); | ||
}, | ||
onFocus: function () { | ||
console.log('focus', arguments, $('.summernote')[0] === this); | ||
}, | ||
onBlur: function () { | ||
console.log('blur', arguments, $('.summernote')[0] === this); | ||
}, | ||
onKeydown: function () { | ||
console.log('keydown', arguments, $('.summernote')[0] === this); | ||
}, | ||
onKeyup: function () { | ||
console.log('keyup', arguments, $('.summernote')[0] === this); | ||
}, | ||
onEnter: function () { | ||
console.log('enter', arguments, $('.summernote')[0] === this); | ||
}, | ||
onMousedown: function () { | ||
console.log('onMousedown', arguments, $('.summernote')[0] === this); | ||
}, | ||
onMouseup: function () { | ||
console.log('onMouseup', arguments, $('.summernote')[0] === this); | ||
}, | ||
onScroll: function () { | ||
console.log('onscroll', arguments, $('.summernote')[0] === this); | ||
}, | ||
onPaste: function () { | ||
console.log('paste', arguments, $('.summernote')[0] === this); | ||
}, | ||
onBeforeCommand: function () { | ||
console.log('onBeforeCommand', arguments, $('.summernote')[0] === this); | ||
}, | ||
onChange: function () { | ||
console.log('onChange', arguments, $('.summernote')[0] === this); | ||
}, | ||
onImageUpload: function () { | ||
console.log('onImageUpload', arguments, $('.summernote')[0] === this); | ||
}, | ||
onImageUploadError: function () { | ||
console.log('onImageUploadError', arguments, $('.summernote')[0] === this); | ||
} | ||
}).on('summernote.init', function () { | ||
console.log('summernote.init', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.focus', function () { | ||
console.log('summernote.focus', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.blur', function () { | ||
console.log('summernote.blur', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.keydown', function () { | ||
console.log('summernote.keydown', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.keyup', function () { | ||
console.log('summernote.keyup', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.enter', function () { | ||
console.log('summernote.enter', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.mousedown', function () { | ||
console.log('summernote.mousedown', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.mouseup', function () { | ||
console.log('summernote.mouseup', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.scroll', function () { | ||
console.log('summernote.scroll', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.paste', function () { | ||
console.log('summernote.paste', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.before.command', function () { | ||
console.log('summernote.before.command', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.change', function () { | ||
console.log('summernote.change', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.image.upload', function () { | ||
console.log('summernote.image.upload', arguments, $('.summernote')[0] === this); | ||
}).on('summernote.image.upload.error', function () { | ||
console.log('summernote.image.error', arguments, $('.summernote')[0] === this); | ||
}); | ||
}); |
@@ -86,2 +86,4 @@ define(['jquery'], function ($) { | ||
var userAgent = navigator.userAgent; | ||
/** | ||
@@ -99,5 +101,8 @@ * @class core.agent | ||
/** @property {Boolean} [isMSIE=false] true if this agent is a Internet Explorer */ | ||
isMSIE: navigator.userAgent.indexOf('MSIE') > -1 || navigator.userAgent.indexOf('Trident') > -1, | ||
isMSIE: /MSIE|Trident/i.test(userAgent), | ||
/** @property {Boolean} [isFF=false] true if this agent is a Firefox */ | ||
isFF: navigator.userAgent.indexOf('Firefox') > -1, | ||
isFF: /firefox/i.test(userAgent), | ||
isWebkit: /webkit/i.test(userAgent), | ||
/** @property {Boolean} [isSafari=false] true if this agent is a Safari */ | ||
isSafari: /safari/i.test(userAgent), | ||
/** @property {String} jqueryVersion current jQuery version string */ | ||
@@ -104,0 +109,0 @@ jqueryVersion: parseFloat($.fn.jquery), |
@@ -273,5 +273,8 @@ define([ | ||
return true; | ||
} else if (!dom.isText(node) && len === 1 && node.innerHTML === blankHTML) { | ||
} else if (!isText(node) && len === 1 && node.innerHTML === blankHTML) { | ||
// ex) <p><br></p>, <span><br></span> | ||
return true; | ||
} else if (list.all(node.childNodes, isText) && node.innerHTML === '') { | ||
// ex) <p></p>, <span></span> | ||
return true; | ||
} | ||
@@ -278,0 +281,0 @@ |
@@ -61,2 +61,11 @@ define([ | ||
/** | ||
* @method isMove | ||
* | ||
* @param {Number} keyCode | ||
* @return {Boolean} | ||
*/ | ||
isMove: function (keyCode) { | ||
return list.contains([37, 38, 39, 40], keyCode); | ||
}, | ||
/** | ||
* @property {Object} nameFromCode | ||
@@ -63,0 +72,0 @@ * @property {String} nameFromCode.8 "BACKSPACE" |
@@ -623,3 +623,3 @@ define([ | ||
var selection = document.getSelection(); | ||
if (selection.rangeCount === 0) { | ||
if (!selection || selection.rangeCount === 0) { | ||
return null; | ||
@@ -696,2 +696,22 @@ } else if (dom.isBody(selection.anchorNode)) { | ||
/** | ||
* create WrappedRange from node after position | ||
* | ||
* @param {Node} node | ||
* @return {WrappedRange} | ||
*/ | ||
createFromNodeBefore: function (node) { | ||
return this.createFromNode(node).collapse(true); | ||
}, | ||
/** | ||
* create WrappedRange from node after position | ||
* | ||
* @param {Node} node | ||
* @return {WrappedRange} | ||
*/ | ||
createFromNodeAfter: function (node) { | ||
return this.createFromNode(node).collapse(); | ||
}, | ||
/** | ||
* @method | ||
@@ -698,0 +718,0 @@ * |
@@ -74,3 +74,3 @@ define('summernote/defaults', function () { | ||
['fontname', ['fontname']], | ||
// ['fontsize', ['fontsize']], | ||
['fontsize', ['fontsize']], | ||
['color', ['color']], | ||
@@ -85,2 +85,4 @@ ['para', ['ul', 'ol', 'paragraph']], | ||
plugin : { }, | ||
// air mode: inline editor | ||
@@ -87,0 +89,0 @@ airMode: false, |
@@ -70,3 +70,3 @@ define([ | ||
if (rng.isCollapsed()) { | ||
return rng.insertNode(dom.create(nodeName)); | ||
return [rng.insertNode(dom.create(nodeName))]; | ||
} | ||
@@ -73,0 +73,0 @@ |
@@ -177,2 +177,8 @@ define([ | ||
var hKeyupAndMouseup = function (event) { | ||
var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target); | ||
modules.editor.removeBogus(layoutInfo.editable()); | ||
hToolbarAndPopoverUpdate(event); | ||
}; | ||
var hToolbarAndPopoverUpdate = function (event) { | ||
@@ -195,3 +201,3 @@ // delay for range after mouseup | ||
var hScroll = function (event) { | ||
var hScrollAndBlur = function (event) { | ||
var layoutInfo = dom.makeLayoutInfo(event.currentTarget || event.target); | ||
@@ -323,9 +329,19 @@ //hide popover and handle when scrolled | ||
var eventName = keyMap[keys.join('+')]; | ||
var pluginEvent; | ||
var keyString = keys.join('+'); | ||
var eventName = keyMap[keyString]; | ||
if (eventName) { | ||
if ($.summernote.pluginEvents[eventName]) { | ||
var plugin = $.summernote.pluginEvents[eventName]; | ||
if ($.isFunction(plugin)) { | ||
plugin(event, modules.editor, layoutInfo); | ||
// FIXME Summernote doesn't support event pipeline yet. | ||
// - Plugin -> Base Code | ||
pluginEvent = $.summernote.pluginEvents[keyString]; | ||
if ($.isFunction(pluginEvent)) { | ||
if (pluginEvent(event, modules.editor, layoutInfo)) { | ||
return false; | ||
} | ||
} | ||
pluginEvent = $.summernote.pluginEvents[eventName]; | ||
if ($.isFunction(pluginEvent)) { | ||
pluginEvent(event, modules.editor, layoutInfo); | ||
} else if (modules.editor[eventName]) { | ||
@@ -356,4 +372,6 @@ modules.editor[eventName]($editable, $editor.data('options')); | ||
layoutInfo.editable().on('mousedown', hMousedown); | ||
layoutInfo.editable().on('keyup mouseup', hToolbarAndPopoverUpdate); | ||
layoutInfo.editable().on('scroll', hScroll); | ||
layoutInfo.editable().on('keyup mouseup', hKeyupAndMouseup); | ||
layoutInfo.editable().on('scroll blur', hScrollAndBlur); | ||
// handler for clipboard | ||
modules.clipboard.attach(layoutInfo, options); | ||
@@ -427,9 +445,3 @@ | ||
layoutInfo.holder().closest('form').submit(function () { | ||
var contents = layoutInfo.holder().code(); | ||
layoutInfo.holder().val(contents); | ||
// callback on submit | ||
if (options.onsubmit) { | ||
options.onsubmit(contents); | ||
} | ||
layoutInfo.holder().val(layoutInfo.holder().code()); | ||
}); | ||
@@ -473,3 +485,2 @@ } | ||
// callbacks for advanced features (camel) | ||
if (!options.airMode) { | ||
@@ -476,0 +487,0 @@ layoutInfo.toolbar().click(bindCustomEvent($holder, callbacks, 'toolbar.click')); |
@@ -14,2 +14,5 @@ define([ | ||
Style, Typing, Table, Bullet) { | ||
var KEY_BOGUS = 'bogus'; | ||
/** | ||
@@ -37,3 +40,3 @@ * @class editing.Editor | ||
this.createRange = function ($editable) { | ||
$editable.focus(); | ||
this.focus($editable); | ||
return range.create(); | ||
@@ -51,3 +54,3 @@ }; | ||
this.saveRange = function ($editable, thenCollapse) { | ||
$editable.focus(); | ||
this.focus($editable); | ||
$editable.data('range', range.create()); | ||
@@ -86,3 +89,3 @@ if (thenCollapse) { | ||
rng.select(); | ||
$editable.focus(); | ||
this.focus($editable); | ||
} | ||
@@ -154,2 +157,3 @@ }; | ||
var self = this; | ||
/** | ||
@@ -162,2 +166,4 @@ * @method beforeCommand | ||
triggerOnBeforeChange($editable); | ||
// keep focus on editable before command execution | ||
self.focus($editable); | ||
}; | ||
@@ -312,3 +318,3 @@ | ||
this.tab = function ($editable, options) { | ||
var rng = range.create(); | ||
var rng = this.createRange($editable); | ||
if (rng.isCollapsed() && rng.isOnCell()) { | ||
@@ -329,4 +335,4 @@ table.tab(rng); | ||
*/ | ||
this.untab = function () { | ||
var rng = range.create(); | ||
this.untab = function ($editable) { | ||
var rng = this.createRange($editable); | ||
if (rng.isCollapsed() && rng.isOnCell()) { | ||
@@ -402,9 +408,9 @@ table.tab(rng, true); | ||
range.create().insertNode($image[0]); | ||
range.createFromNode($image[0]).collapse().select(); | ||
range.createFromNodeAfter($image[0]).select(); | ||
afterCommand($editable); | ||
}).fail(function () { | ||
var callbacks = $editable.data('callbacks'); | ||
if (callbacks.onImageUploadError) { | ||
callbacks.onImageUploadError(); | ||
} | ||
var $holder = dom.makeLayoutInfo($editable).holder(); | ||
handler.bindCustomEvent( | ||
$holder, $editable.data('callbacks'), 'image.upload.error' | ||
)(); | ||
}); | ||
@@ -421,5 +427,4 @@ }; | ||
beforeCommand($editable); | ||
var rng = this.createRange($editable); | ||
rng.insertNode(node); | ||
range.createFromNode(node).collapse().select(); | ||
range.create().insertNode(node); | ||
range.createFromNodeAfter(node).select(); | ||
afterCommand($editable); | ||
@@ -435,4 +440,3 @@ }; | ||
beforeCommand($editable); | ||
var rng = this.createRange($editable); | ||
var textNode = rng.insertNode(dom.createText(text)); | ||
var textNode = range.create().insertNode(dom.createText(text)); | ||
range.create(textNode, dom.nodeLength(textNode)).select(); | ||
@@ -449,5 +453,4 @@ afterCommand($editable); | ||
beforeCommand($editable); | ||
var rng = this.createRange($editable); | ||
var contents = rng.pasteHTML(markup); | ||
range.createFromNode(list.last(contents)).collapse().select(); | ||
var contents = range.create().pasteHTML(markup); | ||
range.createFromNodeAfter(list.last(contents)).select(); | ||
afterCommand($editable); | ||
@@ -493,16 +496,53 @@ }; | ||
this.fontSize = function ($editable, value) { | ||
beforeCommand($editable); | ||
var rng = range.create(); | ||
var isCollapsed = rng.isCollapsed(); | ||
var rng = this.createRange($editable); | ||
var spans = style.styleNodes(rng); | ||
$.each(spans, function (idx, span) { | ||
$(span).css({ | ||
if (isCollapsed) { | ||
var spans = style.styleNodes(rng); | ||
var firstSpan = list.head(spans); | ||
$(spans).css({ | ||
'font-size': value + 'px' | ||
}); | ||
}); | ||
afterCommand($editable); | ||
// [workaround] added styled bogus span for style | ||
// - also bogus character needed for cursor position | ||
if (firstSpan && !dom.nodeLength(firstSpan)) { | ||
firstSpan.innerHTML = dom.ZERO_WIDTH_NBSP_CHAR; | ||
range.createFromNodeAfter(firstSpan.firstChild).select(); | ||
$editable.data(KEY_BOGUS, firstSpan); | ||
} | ||
} else { | ||
beforeCommand($editable); | ||
$(style.styleNodes(rng)).css({ | ||
'font-size': value + 'px' | ||
}); | ||
afterCommand($editable); | ||
} | ||
}; | ||
/** | ||
* remove bogus node and character | ||
*/ | ||
this.removeBogus = function ($editable) { | ||
var bogusNode = $editable.data(KEY_BOGUS); | ||
if (!bogusNode) { | ||
return; | ||
} | ||
var textNode = list.find(list.from(bogusNode.childNodes), dom.isText); | ||
var bogusCharIdx = textNode.nodeValue.indexOf(dom.ZERO_WIDTH_NBSP_CHAR); | ||
if (bogusCharIdx !== -1) { | ||
textNode.deleteData(bogusCharIdx, 1); | ||
} | ||
if (dom.isEmpty(bogusNode)) { | ||
dom.remove(bogusNode); | ||
} | ||
$editable.removeData(KEY_BOGUS); | ||
}; | ||
/** | ||
* lineHeight | ||
@@ -528,3 +568,3 @@ * @param {jQuery} $editable | ||
this.unlink = function ($editable) { | ||
var rng = range.create(); | ||
var rng = this.createRange(); | ||
if (rng.isOnAnchor()) { | ||
@@ -561,7 +601,7 @@ var anchor = dom.ancestor(rng.sc, dom.isAnchor); | ||
var anchors; | ||
var anchors = []; | ||
if (isTextChanged) { | ||
// Create a new link when text changed. | ||
var anchor = rng.insertNode($('<A>' + linkText + '</A>')[0]); | ||
anchors = [anchor]; | ||
anchors.push(anchor); | ||
} else { | ||
@@ -584,5 +624,5 @@ anchors = style.styleNodes(rng, { | ||
var startRange = range.createFromNode(list.head(anchors)).collapse(true); | ||
var startRange = range.createFromNodeBefore(list.head(anchors)); | ||
var startPoint = startRange.getStartPoint(); | ||
var endRange = range.createFromNode(list.last(anchors)).collapse(); | ||
var endRange = range.createFromNodeAfter(list.last(anchors)); | ||
var endPoint = endRange.getEndPoint(); | ||
@@ -610,3 +650,3 @@ | ||
this.getLinkInfo = function ($editable) { | ||
$editable.focus(); | ||
this.focus($editable); | ||
@@ -656,4 +696,3 @@ var rng = range.create().expand(dom.isAnchor); | ||
var rng = range.create(); | ||
rng = rng.deleteContents(); | ||
var rng = range.create().deleteContents(); | ||
rng.insertNode(table.createTable(dimension[0], dimension[1])); | ||
@@ -749,3 +788,3 @@ afterCommand($editable); | ||
$(), $editable.data('callbacks'), 'media.delete' | ||
).call($target, this.$editable); | ||
)($target, $editable); | ||
@@ -764,4 +803,7 @@ afterCommand($editable); | ||
// [workaround] for firefox bug http://goo.gl/lVfAaI | ||
if (agent.isFF) { | ||
range.createFromNode($editable[0].firstChild || $editable[0]).collapse().select(); | ||
if (agent.isFF && !range.create().isOnEditable()) { | ||
range.createFromNode($editable[0]) | ||
.normalize() | ||
.collapse() | ||
.select(); | ||
} | ||
@@ -768,0 +810,0 @@ }; |
@@ -54,2 +54,3 @@ define([ | ||
$linkText.on('input', function () { | ||
toggleBtn($linkBtn, $linkText.val() && $linkUrl.val()); | ||
// if linktext was modified by keyup, | ||
@@ -67,3 +68,3 @@ // stop cloning text from linkUrl | ||
$linkUrl.on('input', function () { | ||
toggleBtn($linkBtn, $linkUrl.val()); | ||
toggleBtn($linkBtn, $linkText.val() && $linkUrl.val()); | ||
// display same link on `Text to display` input | ||
@@ -70,0 +71,0 @@ // when create a new link |
@@ -176,3 +176,3 @@ define([ | ||
title: lang.font.name, | ||
dropdown: '<ul class="dropdown-menu">' + items + '</ul>' | ||
dropdown: '<ul class="dropdown-menu note-check">' + items + '</ul>' | ||
}); | ||
@@ -190,3 +190,3 @@ }, | ||
title: lang.font.size, | ||
dropdown: '<ul class="dropdown-menu">' + items + '</ul>' | ||
dropdown: '<ul class="dropdown-menu note-check">' + items + '</ul>' | ||
}); | ||
@@ -334,3 +334,3 @@ }, | ||
title: lang.font.height, | ||
dropdown: '<ul class="dropdown-menu">' + items + '</ul>' | ||
dropdown: '<ul class="dropdown-menu note-check">' + items + '</ul>' | ||
}); | ||
@@ -337,0 +337,0 @@ |
@@ -46,2 +46,3 @@ define([ | ||
agent: agent, | ||
list : list, | ||
dom: dom, | ||
@@ -190,6 +191,7 @@ range: range | ||
var isExternalAPICalled = type === 'string'; | ||
var isInitOptions = type === 'object'; | ||
var hasInitOptions = type === 'object'; | ||
// extend default options with custom user options | ||
var options = isInitOptions ? list.head(arguments) : {}; | ||
var options = hasInitOptions ? list.head(arguments) : {}; | ||
options = $.extend({}, $.summernote.options, options); | ||
@@ -201,2 +203,13 @@ | ||
// override plugin options | ||
if (!isExternalAPICalled && hasInitOptions) { | ||
for (var i = 0, len = $.summernote.plugins.length; i < len; i++) { | ||
var plugin = $.summernote.plugins[i]; | ||
if (options.plugin[plugin.name]) { | ||
$.summernote.plugins[i] = $.extend(true, plugin, options.plugin[plugin.name]); | ||
} | ||
} | ||
} | ||
this.each(function (idx, holder) { | ||
@@ -210,2 +223,3 @@ var $holder = $(holder); | ||
var layoutInfo = renderer.layoutInfoFromHolder($holder); | ||
$holder.data('layoutInfo', layoutInfo); | ||
@@ -218,7 +232,2 @@ eventHandler.attach(layoutInfo, options); | ||
// callback on init | ||
if (!isExternalAPICalled && this.length && options.oninit) { | ||
options.oninit(); | ||
} | ||
var $first = this.first(); | ||
@@ -225,0 +234,0 @@ if ($first.length) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
1411931
128
170
34126