summernote
Advanced tools
Comparing version 0.6.9 to 0.6.10
{ | ||
"name": "summernote", | ||
"homepage": "http://summernote.org", | ||
"license": "MIT", | ||
"main": [ | ||
@@ -4,0 +6,0 @@ "./dist/summernote.js", |
@@ -118,4 +118,3 @@ module.exports = function (grunt) { | ||
]); | ||
}, | ||
open: 'http://localhost:3000' | ||
} | ||
} | ||
@@ -144,4 +143,28 @@ } | ||
} | ||
}, | ||
'saucelabs-qunit': { | ||
'all': { | ||
options: { | ||
urls: ['http://localhost:3000/test/unit.html'], | ||
build: process.env.TRAVIS_BUILD_NUMBER, | ||
tags: [process.env.TRAVIS_BRANCH, process.env.TRAVIS_PULL_REQUEST], | ||
browsers: [{ | ||
browserName: 'chrome', | ||
version: '43', | ||
platform: 'windows 8' | ||
}, { | ||
browserName: 'firefox', | ||
version: '38', | ||
platform: 'windows 8' | ||
}, { | ||
browserName: 'safari', | ||
version: '8.0', | ||
platform: 'OS X 10.10' | ||
}], | ||
testname: 'unit test for summernote', | ||
'public': 'public' | ||
} | ||
} | ||
} | ||
}); | ||
@@ -161,8 +184,8 @@ | ||
// test: saucelabs test | ||
grunt.registerTask('saucelabs-test', ['connect', 'saucelabs-qunit']); | ||
// dist: make dist files | ||
grunt.registerTask('dist', ['build', 'test', 'uglify', 'recess']); | ||
grunt.registerTask('dist', ['build', 'test', 'uglify', 'recess', 'compress']); | ||
// deploy: compress dist files | ||
grunt.registerTask('deploy', ['dist', 'compress']); | ||
// default: server | ||
@@ -169,0 +192,0 @@ grunt.registerTask('default', ['server']); |
@@ -12,4 +12,4 @@ (function ($) { | ||
strikethrough: 'Üstü çizili', | ||
subscript: 'Subscript', | ||
superscript: 'Superscript', | ||
subscript: 'Alt Simge', | ||
superscript: 'Üst Simge', | ||
size: 'Yazı tipi boyutu' | ||
@@ -16,0 +16,0 @@ }, |
@@ -1,26 +0,31 @@ | ||
## Build and publish new release | ||
## Publish new version | ||
### 01. Send pull request `develop` to `master` on github repository and merge it. | ||
### 1. `develop` to `master` | ||
Send pull request `develop` to `master` on github repository and merge it. | ||
https://github.com/summernote/summernote/compare/master...develop | ||
### 02. Build dist files on master | ||
### 2. Build dist files | ||
Build dist files and push to master | ||
```bash | ||
# build dist files | ||
grunt build | ||
# now you can find dist files on `./dist`. | ||
# change branch | ||
git checkout master | ||
# fetch all changes | ||
git pull | ||
# build dist files and binary(.zip) for release post | ||
grunt dist | ||
# Push new dist files to remote repository. | ||
git commit -a -m "Update dist files" | ||
git push origin | ||
``` | ||
### 03. Post new release with tag version on github repository. | ||
### 3. Release new version | ||
Post release note with new tag version on github | ||
generate binary(.zip) for release post | ||
https://github.com/summernote/summernote/releases/new | ||
```bash | ||
grunt deploy | ||
# now you can find a binary on `./dist`. | ||
``` | ||
### 4. Publish | ||
https://github.com/summernote/summernote/releases/new | ||
### 04. Publish on npm | ||
Publish on npm | ||
```bash | ||
@@ -30,3 +35,3 @@ npm publish | ||
### 05. Publish on meteor | ||
Publish on meteor | ||
```bash | ||
@@ -36,2 +41,11 @@ meteor/publish.sh | ||
### 06. Update gh-pages | ||
### 05. Update summernote.github.io | ||
Update summernote and other bower components. | ||
```bash | ||
bower update | ||
``` | ||
Replace binary path with new version on `getting-started.html`. This binary is generated at `3. Release new version`. | ||
https://github.com/summernote/summernote.github.io/blob/master/html/getting-started.html |
{ | ||
"name": "summernote", | ||
"description": "Super Simple WYSIWYG Editor on Bootstrap", | ||
"version": "0.6.9", | ||
"version": "0.6.10", | ||
"keywords": [ | ||
@@ -19,5 +19,6 @@ "editor", | ||
"devDependencies": { | ||
"load-grunt-tasks": "0.2.0", | ||
"requirejs": "2.1.9", | ||
"connect-livereload": "*", | ||
"grunt": "*", | ||
"grunt-contrib-compress": "*", | ||
"grunt-contrib-connect": "*", | ||
"grunt-contrib-jshint": "0.7.2", | ||
@@ -27,9 +28,9 @@ "grunt-contrib-qunit": "0.5.2", | ||
"grunt-contrib-watch": "*", | ||
"grunt-contrib-connect": "*", | ||
"grunt-contrib-compress": "*", | ||
"grunt-exec": "^0.4.6", | ||
"grunt-recess": "*", | ||
"grunt-exec": "^0.4.6", | ||
"connect-livereload": "*", | ||
"open": "0.0.4" | ||
"grunt-saucelabs": "^8.6.1", | ||
"load-grunt-tasks": "0.2.0", | ||
"open": "0.0.4", | ||
"requirejs": "2.1.9" | ||
} | ||
} |
@@ -11,4 +11,4 @@ (function (factory) { | ||
}(function ($) { | ||
// import core class | ||
var range = $.summernote.core.range; | ||
var list = $.summernote.core.list; | ||
@@ -21,14 +21,49 @@ var KEY = { | ||
var DROPDOWN_KEYCODES = [38, 40, 13]; | ||
var DROPDOWN_KEYCODES = [KEY.UP, KEY.DOWN, KEY.ENTER]; | ||
/** | ||
* @class plugin.hint | ||
* | ||
* Hello Plugin | ||
* | ||
* Hint Plugin | ||
*/ | ||
$.summernote.addPlugin({ | ||
/** @property {String} name name of plugin */ | ||
/** | ||
* name name of plugin | ||
* @property {String} | ||
**/ | ||
name: 'hint', | ||
/** | ||
* @property {Regex} | ||
* @interface | ||
*/ | ||
match: /[a-z]+/g, | ||
/** | ||
* create list item template | ||
* | ||
* @interface | ||
* @param {Object} search | ||
* @returns {Array} created item list | ||
*/ | ||
template: null, | ||
/** | ||
* create inserted content to add in summernote | ||
* | ||
* @interface | ||
* @param {String} html | ||
* @param {String} keyword | ||
* @return {HTMLEleemnt|String} | ||
*/ | ||
content: null, | ||
/** | ||
* load search list | ||
* | ||
* @interface | ||
*/ | ||
load: null, | ||
/** | ||
* @param {jQuery} $node | ||
@@ -81,3 +116,3 @@ */ | ||
var $activeItem = $popover.find('.active'); | ||
var content = this.content($activeItem.html(), $activeItem.data('keyword')); | ||
var content = this.content($activeItem.data('item')); | ||
@@ -98,34 +133,20 @@ if (typeof content === 'string') { | ||
*/ | ||
searchKeyword: function (keyword) { | ||
var triggerChar = keyword.charAt(0); | ||
if (triggerChar === ':' && keyword.length > 1) { | ||
var trigger = keyword.toLowerCase().replace(':', ''); | ||
return { | ||
type: 'emoji', | ||
list: $.grep(this.emojiKeys, function (item) { | ||
return item.indexOf(trigger) === 0; | ||
}) | ||
}; | ||
searchKeyword: function (keyword, callback) { | ||
if (this.match.test(keyword)) { | ||
var matches = this.match.exec(keyword); | ||
this.search(matches[1], callback); | ||
} else { | ||
callback(); | ||
} | ||
return null; | ||
}, | ||
/** | ||
* create items | ||
* | ||
* @param {Object} searchResult | ||
* @param {String} searchResult.type | ||
* @param {String[]} searchResult.list | ||
* @return {jQuery[]} | ||
*/ | ||
createItems: function (searchResult) { | ||
var items = []; | ||
var list = searchResult.list; | ||
createTemplate: function (list) { | ||
var items = []; | ||
list = list || []; | ||
for (var i = 0, len = list.length; i < len; i++) { | ||
var $item = $('<a class="list-group-item"></a>'); | ||
$item.append(this.createItem(list[i])); | ||
$item.data('keyword', list[i]); | ||
$item.append(this.template(list[i])); | ||
$item.data('item', list[i]); | ||
items.push($item); | ||
@@ -141,57 +162,22 @@ } | ||
/** | ||
* create list item template | ||
* | ||
* @param {Object} item | ||
* @returns {String} | ||
*/ | ||
createItem: function (item) { | ||
var content = this.emojiInfo[item]; | ||
return '<img src="' + content + '" width="20" /> :' + item + ':'; | ||
search: function (keyword, callback) { | ||
keyword = keyword || ''; | ||
callback(); | ||
}, | ||
/** | ||
* create inserted content to add in summernote | ||
* | ||
* @param {String} html | ||
* @param {String} keyword | ||
* @return {Node|String} | ||
*/ | ||
content: function (html, item) { | ||
var url = this.emojiInfo[item]; | ||
if (url) { | ||
var $img = $('<img />').attr('src', url).css({ | ||
width : 20 | ||
}); | ||
return $img[0]; | ||
} | ||
return html; | ||
}, | ||
/** | ||
* @return {Promise} | ||
*/ | ||
loadEmojis: function () { | ||
init : function (layoutInfo) { | ||
var self = this; | ||
return $.getJSON('https://api.github.com/emojis').then(function (data) { | ||
self.emojiKeys = Object.keys(data); | ||
self.emojiInfo = data; | ||
}); | ||
}, | ||
init: function (layoutInfo) { | ||
var self = this; | ||
var $note = layoutInfo.holder(); | ||
var $popover = $('<div class="list-group" />').css({ | ||
position: 'absolute', | ||
'max-height': 300, | ||
'overflow-y': 'scroll', | ||
'display': 'none' | ||
var $popover = $('<div />').addClass('hint-group').css({ | ||
'position': 'absolute', | ||
'max-height': 150, | ||
'z-index' : 999, | ||
'overflow' : 'hidden', | ||
'display' : 'none', | ||
'border' : '1px solid gray', | ||
'border-radius' : '5px' | ||
}); | ||
// FIXME We need a handler for unload resources. | ||
$popover.on('click', '.list-group-item', function () { | ||
$popover.on('click', '.list-group-item', function HintItemClick() { | ||
self.replace($popover); | ||
@@ -203,9 +189,9 @@ | ||
$(document).on('click', function () { | ||
$(document).on('click', function HintClick() { | ||
$popover.hide(); | ||
}); | ||
$note.on('summernote.keydown', function (customEvent, nativeEvent) { | ||
$note.on('summernote.keydown', function HintKeyDown(customEvent, nativeEvent) { | ||
if ($popover.css('display') !== 'block') { | ||
return; | ||
return true; | ||
} | ||
@@ -228,30 +214,61 @@ | ||
$note.on('summernote.keyup', function (customEvent, nativeEvent) { | ||
if (DROPDOWN_KEYCODES.indexOf(nativeEvent.keyCode) === -1) { | ||
var wordRange = $(this).summernote('createRange').getWordRange(); | ||
var result = self.searchKeyword(wordRange.toString()); | ||
if (!result || !result.list.length) { | ||
$popover.hide(); | ||
return; | ||
var timer = null; | ||
$note.on('summernote.keyup', function HintKeyUp(customEvent, nativeEvent) { | ||
if (DROPDOWN_KEYCODES.indexOf(nativeEvent.keyCode) > -1) { | ||
if (nativeEvent.keyCode === KEY.ENTER) { | ||
if ($popover.css('display') === 'block') { | ||
return false; | ||
} | ||
} | ||
layoutInfo.popover().append($popover); | ||
} else { | ||
var rect = list.last(wordRange.getClientRects()); | ||
$popover.html(self.createItems(result)).css({ | ||
left: rect.left, | ||
top: rect.top + rect.height | ||
}).data('wordRange', wordRange).show(); | ||
clearTimeout(timer); | ||
timer = setTimeout(function () { | ||
var range = $note.summernote('createRange'); | ||
var word = range.getWordRange(); | ||
self.searchKeyword(word.toString(), function (searchList) { | ||
if (!searchList) { | ||
$popover.hide(); | ||
return; | ||
} | ||
if (searchList && !searchList.length) { | ||
$popover.hide(); | ||
return; | ||
} | ||
layoutInfo.popover().append($popover); | ||
// popover below placeholder. | ||
var rects = word.getClientRects(); | ||
var rect = rects[rects.length - 1]; | ||
$popover.html(self.createTemplate(searchList)).css({ | ||
left: rect.left, | ||
top: rect.top + rect.height | ||
}).data('wordRange', word).show(); | ||
}); | ||
}, self.throttle); | ||
} | ||
}); | ||
this.loadEmojis(); | ||
this.load($popover); | ||
}, | ||
throttle : 50, | ||
// FIXME Summernote doesn't support event pipeline yet. | ||
// - Plugin -> Base Code | ||
events: { | ||
ENTER: function () { | ||
ENTER: function (e, editor, layoutInfo) { | ||
if (layoutInfo.popover().find('.hint-group').css('display') !== 'block') { | ||
// apply default enter key | ||
layoutInfo.holder().summernote('insertParagraph'); | ||
} | ||
// prevent ENTER key | ||
return false; | ||
return true; | ||
} | ||
@@ -258,0 +275,0 @@ } |
# Summernote | ||
Super simple WYSIWYG Editor using Bootstrap (3.0 and 2.x). | ||
[![Build Status](https://secure.travis-ci.org/summernote/summernote.png)](http://travis-ci.org/summernote/summernote) | ||
[![npm version](https://badge.fury.io/js/summernote.svg)](http://badge.fury.io/js/summernote) | ||
[![Dependency Status](https://gemnasium.com/summernote/summernote.svg)](https://gemnasium.com/summernote/summernote) | ||
[![Sauce Test Status](https://saucelabs.com/browser-matrix/HackerWins.svg?auth=fb19ea6fe2ec6464f03afd7e4008a24e)](https://saucelabs.com/u/HackerWins) | ||
### Summernote | ||
@@ -159,4 +164,4 @@ Summernote is a JavaScript library that helps you create WYSIWYG editors online. | ||
```bash | ||
# this will open a browser on http://localhost:3000. | ||
grunt server | ||
# Open a browser on http://localhost:3000. | ||
# If you change source code, automatically reload your page. | ||
@@ -175,4 +180,6 @@ ``` | ||
* Twitter: http://twitter.com/hackerwins | ||
* Chat with us: | ||
[![Join the chat at https://gitter.im/summernote/summernote](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/summernote/summernote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
### License | ||
summernote may be freely distributed under the MIT license. |
@@ -5,3 +5,3 @@ require.config({ | ||
jquery: '//code.jquery.com/jquery-1.11.3', | ||
bootstrap: '//netdna.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min', | ||
bootstrap: '//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min', | ||
summernotevideo: '/../../plugin/summernote-ext-video', | ||
@@ -8,0 +8,0 @@ CodeMirror: '//cdnjs.cloudflare.com/ajax/libs/codemirror/3.20.0/codemirror', |
@@ -80,5 +80,10 @@ define([ | ||
} else { | ||
makeFinder = function (sClassName) { | ||
return function () { return $editor.find(sClassName); }; | ||
makeFinder = function (sClassName, sBaseElement) { | ||
var $baseElement = sBaseElement ? $(sBaseElement) : $editor; | ||
return function () { return $baseElement.find(sClassName); }; | ||
}; | ||
var options = $editor.data('options'); | ||
var dialogHolder = (options && options.dialogsInBody) ? document.body : null; | ||
return { | ||
@@ -94,3 +99,3 @@ editor: function () { return $editor; }, | ||
handle: makeFinder('.note-handle'), | ||
dialog: makeFinder('.note-dialog') | ||
dialog: makeFinder('.note-dialog', dialogHolder) | ||
}; | ||
@@ -544,2 +549,22 @@ } | ||
/** | ||
* returns whether point is left edge of ancestor or not. | ||
* @param {BoundaryPoint} point | ||
* @param {Node} ancestor | ||
* @return {Boolean} | ||
*/ | ||
var isLeftEdgePointOf = function (point, ancestor) { | ||
return isLeftEdgePoint(point) && isLeftEdgeOf(point.node, ancestor); | ||
}; | ||
/** | ||
* returns whether point is right edge of ancestor or not. | ||
* @param {BoundaryPoint} point | ||
* @param {Node} ancestor | ||
* @return {Boolean} | ||
*/ | ||
var isRightEdgePointOf = function (point, ancestor) { | ||
return isRightEdgePoint(point) && isRightEdgeOf(point.node, ancestor); | ||
}; | ||
/** | ||
* returns offset from parent. | ||
@@ -1052,2 +1077,4 @@ * | ||
isRightEdgeOf: isRightEdgeOf, | ||
isLeftEdgePointOf: isLeftEdgePointOf, | ||
isRightEdgePointOf: isRightEdgePointOf, | ||
prevPoint: prevPoint, | ||
@@ -1054,0 +1081,0 @@ nextPoint: nextPoint, |
@@ -197,17 +197,33 @@ define([ | ||
* @param {BoundaryPoint} point | ||
* @param {Boolean} isLeftToRight | ||
* @return {BoundaryPoint} | ||
*/ | ||
var getVisiblePoint = function (point) { | ||
if (!dom.isVisiblePoint(point)) { | ||
if (dom.isLeftEdgePoint(point)) { | ||
point = dom.nextPointUntil(point, dom.isVisiblePoint); | ||
} else { | ||
point = dom.prevPointUntil(point, dom.isVisiblePoint); | ||
var getVisiblePoint = function (point, isLeftToRight) { | ||
if ((dom.isVisiblePoint(point) && !dom.isEdgePoint(point)) || | ||
(dom.isVisiblePoint(point) && dom.isRightEdgePoint(point) && !isLeftToRight) || | ||
(dom.isVisiblePoint(point) && dom.isLeftEdgePoint(point) && isLeftToRight) || | ||
(dom.isVisiblePoint(point) && dom.isBlock(point.node) && dom.isEmpty(point.node))) { | ||
return point; | ||
} | ||
// point on block's edge | ||
var block = dom.ancestor(point.node, dom.isBlock); | ||
if ((dom.isLeftEdgePointOf(point, block) && !isLeftToRight) || | ||
(dom.isRightEdgePointOf(point, block) && isLeftToRight)) { | ||
// returns point already on visible point | ||
if (dom.isVisiblePoint(point)) { | ||
return point; | ||
} | ||
// reverse direction | ||
isLeftToRight = !isLeftToRight; | ||
} | ||
return point; | ||
var nextPoint = isLeftToRight ? dom.nextPointUntil(dom.nextPoint(point), dom.isVisiblePoint) : | ||
dom.prevPointUntil(dom.prevPoint(point), dom.isVisiblePoint); | ||
return nextPoint || point; | ||
}; | ||
var startPoint = getVisiblePoint(this.getStartPoint()); | ||
var endPoint = getVisiblePoint(this.getEndPoint()); | ||
var endPoint = getVisiblePoint(this.getEndPoint(), false); | ||
var startPoint = this.isCollapsed() ? endPoint : getVisiblePoint(this.getStartPoint(), true); | ||
@@ -446,4 +462,10 @@ return new WrappedRange( | ||
/** | ||
* [workaround] firefox often create range on not visible point. so normalize here. | ||
* - firefox: |<p>text</p>| | ||
* - chrome: <p>|text|</p> | ||
*/ | ||
var rng = this.normalize(); | ||
if (dom.isParaInline(sc) || dom.isPara(sc)) { | ||
return this.normalize(); | ||
return rng; | ||
} | ||
@@ -453,10 +475,10 @@ | ||
var topAncestor; | ||
if (dom.isInline(sc)) { | ||
var ancestors = dom.listAncestor(sc, func.not(dom.isInline)); | ||
if (dom.isInline(rng.sc)) { | ||
var ancestors = dom.listAncestor(rng.sc, func.not(dom.isInline)); | ||
topAncestor = list.last(ancestors); | ||
if (!dom.isInline(topAncestor)) { | ||
topAncestor = ancestors[ancestors.length - 2] || sc.childNodes[so]; | ||
topAncestor = ancestors[ancestors.length - 2] || rng.sc.childNodes[rng.so]; | ||
} | ||
} else { | ||
topAncestor = sc.childNodes[so > 0 ? so - 1 : 0]; | ||
topAncestor = rng.sc.childNodes[rng.so > 0 ? rng.so - 1 : 0]; | ||
} | ||
@@ -463,0 +485,0 @@ |
@@ -16,7 +16,7 @@ define([ | ||
left : -100000, | ||
'opacity' : 0 | ||
opacity : 0 | ||
}); | ||
layoutInfo.editable().after($paste); | ||
$paste.one('paste', hPasteClipboardImage); | ||
$paste.on('paste', hPasteClipboardImage); | ||
layoutInfo.editable().on('keydown', function (e) { | ||
@@ -35,2 +35,11 @@ if (e.ctrlKey && e.keyCode === 86) { // CTRL+V | ||
var hPasteContent = function (handler, $paste, $editable) { | ||
var pasteContent = $('<div />').html($paste.html()); | ||
handler.invoke('restoreRange', $editable); | ||
handler.invoke('focus', $editable); | ||
handler.invoke('pasteHTML', $editable, pasteContent.html()); | ||
$paste.empty(); | ||
}; | ||
/** | ||
@@ -52,2 +61,3 @@ * paste clipboard image | ||
if (!callbacks.onImageUpload) { | ||
hPasteContent(handler, $paste, $editable); | ||
return; | ||
@@ -63,9 +73,10 @@ } | ||
if (!imgNode) { | ||
hPasteContent(handler, $paste, $editable); | ||
return; | ||
} | ||
handler.invoke('restoreRange', $editable); | ||
if (!dom.isImg(imgNode)) { | ||
handler.invoke('pasteHTML', $editable, $paste.html()); | ||
hPasteContent(handler, $paste, $editable); | ||
} else { | ||
handler.invoke('restoreRange', $editable); | ||
var datauri = imgNode.src; | ||
@@ -83,6 +94,6 @@ | ||
handler.insertImages(layoutInfo, [blob]); | ||
$paste.empty(); | ||
} | ||
$paste.remove(); | ||
}, 0); | ||
@@ -89,0 +100,0 @@ |
@@ -25,2 +25,3 @@ define([ | ||
var self = this; | ||
var style = new Style(); | ||
@@ -114,3 +115,3 @@ var table = new Table(); | ||
this.currentStyle = function (target) { | ||
var rng = range.create(); | ||
var rng = range.create().normalize(); | ||
return rng ? rng.isOnEditable() && style.current(rng, target) : false; | ||
@@ -155,3 +156,2 @@ }; | ||
var self = this; | ||
/** | ||
@@ -488,5 +488,4 @@ * @method beforeCommand | ||
var rng = range.create(); | ||
var isCollapsed = rng.isCollapsed(); | ||
if (isCollapsed) { | ||
if (rng.isCollapsed()) { | ||
var spans = style.styleNodes(rng); | ||
@@ -582,5 +581,7 @@ var firstSpan = list.head(spans); | ||
var isNewWindow = linkInfo.newWindow; | ||
var rng = linkInfo.range; | ||
var rng = linkInfo.range || this.createRange($editable); | ||
var isTextChanged = rng.toString() !== linkText; | ||
options = options || dom.makeLayoutInfo($editable).editor().data('options'); | ||
beforeCommand($editable); | ||
@@ -587,0 +588,0 @@ |
@@ -82,3 +82,3 @@ define([ | ||
'</div>'); | ||
$popover.find('.popover-content').append(content); | ||
@@ -115,2 +115,19 @@ return $popover; | ||
/** | ||
* bootstrap dropdown template | ||
* | ||
* @param {String|String[]} contents | ||
* @param {String} [className=''] | ||
* @param {String} [nodeName=''] | ||
*/ | ||
var tplDropdown = function (contents, className, nodeName) { | ||
var classes = 'dropdown-menu' + (className ? ' ' + className : ''); | ||
nodeName = nodeName || 'ul'; | ||
if (contents instanceof Array) { | ||
contents = contents.join(''); | ||
} | ||
return '<' + nodeName + ' class="' + classes + '">' + contents + '</' + nodeName + '>'; | ||
}; | ||
var tplButtonInfo = { | ||
@@ -132,13 +149,14 @@ picture: function (lang, options) { | ||
table: function (lang, options) { | ||
var dropdown = '<ul class="note-table dropdown-menu">' + | ||
'<div class="note-dimension-picker">' + | ||
'<div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"></div>' + | ||
'<div class="note-dimension-picker-highlighted"></div>' + | ||
'<div class="note-dimension-picker-unhighlighted"></div>' + | ||
'</div>' + | ||
'<div class="note-dimension-display"> 1 x 1 </div>' + | ||
'</ul>'; | ||
var dropdown = [ | ||
'<div class="note-dimension-picker">', | ||
'<div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"></div>', | ||
'<div class="note-dimension-picker-highlighted"></div>', | ||
'<div class="note-dimension-picker-unhighlighted"></div>', | ||
'</div>', | ||
'<div class="note-dimension-display"> 1 x 1 </div>' | ||
]; | ||
return tplIconButton(options.iconPrefix + options.icons.table.table, { | ||
title: lang.table.table, | ||
dropdown: dropdown | ||
dropdown: tplDropdown(dropdown, 'note-table') | ||
}); | ||
@@ -159,3 +177,3 @@ }, | ||
title: lang.style.style, | ||
dropdown: '<ul class="dropdown-menu">' + items + '</ul>' | ||
dropdown: tplDropdown(items) | ||
}); | ||
@@ -177,3 +195,3 @@ }, | ||
var defaultFontName = (hasDefaultFont) ? options.defaultFontName : realFontList[0]; | ||
var label = '<span class="note-current-fontname">' + | ||
@@ -185,3 +203,3 @@ defaultFontName + | ||
className: 'note-fontname', | ||
dropdown: '<ul class="dropdown-menu note-check">' + items + '</ul>' | ||
dropdown: tplDropdown(items, 'note-check') | ||
}); | ||
@@ -200,3 +218,3 @@ }, | ||
className: 'note-fontsize', | ||
dropdown: '<ul class="dropdown-menu note-check">' + items + '</ul>' | ||
dropdown: tplDropdown(items, 'note-check') | ||
}); | ||
@@ -207,4 +225,5 @@ }, | ||
options.iconPrefix + options.icons.color.recent + | ||
'" style="color:black;background-color:yellow;"></i>', | ||
colorButton = tplButton(colorButtonLabel, { | ||
'" style="color:black;background-color:yellow;"></i>'; | ||
var colorButton = tplButton(colorButtonLabel, { | ||
className: 'note-recent-color', | ||
@@ -216,25 +235,20 @@ title: lang.color.recent, | ||
var dropdown = '<ul class="dropdown-menu">' + | ||
'<li>' + | ||
'<div class="btn-group">' + | ||
'<div class="note-palette-title">' + lang.color.background + '</div>' + | ||
'<div class="note-color-reset" data-event="backColor"' + | ||
' data-value="inherit" title="' + lang.color.transparent + '">' + | ||
lang.color.setTransparent + | ||
'</div>' + | ||
'<div class="note-color-palette" data-target-event="backColor"></div>' + | ||
'</div>' + | ||
'<div class="btn-group">' + | ||
'<div class="note-palette-title">' + lang.color.foreground + '</div>' + | ||
'<div class="note-color-reset" data-event="foreColor" data-value="inherit" title="' + lang.color.reset + '">' + | ||
lang.color.resetToDefault + | ||
'</div>' + | ||
'<div class="note-color-palette" data-target-event="foreColor"></div>' + | ||
'</div>' + | ||
'</li>' + | ||
'</ul>'; | ||
var items = [ | ||
'<li><div class="btn-group">', | ||
'<div class="note-palette-title">' + lang.color.background + '</div>', | ||
'<div class="note-color-reset" data-event="backColor"', | ||
' data-value="inherit" title="' + lang.color.transparent + '">' + lang.color.setTransparent + '</div>', | ||
'<div class="note-color-palette" data-target-event="backColor"></div>', | ||
'</div><div class="btn-group">', | ||
'<div class="note-palette-title">' + lang.color.foreground + '</div>', | ||
'<div class="note-color-reset" data-event="foreColor" data-value="inherit" title="' + lang.color.reset + '">', | ||
lang.color.resetToDefault, | ||
'</div>', | ||
'<div class="note-color-palette" data-target-event="foreColor"></div>', | ||
'</div></li>' | ||
]; | ||
var moreButton = tplButton('', { | ||
title: lang.color.more, | ||
dropdown: dropdown | ||
dropdown: tplDropdown(items) | ||
}); | ||
@@ -325,14 +339,13 @@ | ||
var dropdown = '<div class="dropdown-menu">' + | ||
'<div class="note-align btn-group">' + | ||
leftButton + centerButton + rightButton + justifyButton + | ||
'</div>' + | ||
'<div class="note-list btn-group">' + | ||
indentButton + outdentButton + | ||
'</div>' + | ||
'</div>'; | ||
var dropdown = [ | ||
'<div class="note-align btn-group">', | ||
leftButton + centerButton + rightButton + justifyButton, | ||
'</div><div class="note-list btn-group">', | ||
indentButton + outdentButton, | ||
'</div>' | ||
]; | ||
return tplIconButton(options.iconPrefix + options.icons.paragraph.paragraph, { | ||
title: lang.paragraph.paragraph, | ||
dropdown: dropdown | ||
dropdown: tplDropdown(dropdown, '', 'div') | ||
}); | ||
@@ -349,3 +362,3 @@ }, | ||
title: lang.font.height, | ||
dropdown: '<ul class="dropdown-menu note-check">' + items + '</ul>' | ||
dropdown: tplDropdown(items, 'note-check') | ||
}); | ||
@@ -482,3 +495,3 @@ | ||
var group = options.airPopover[idx]; | ||
var $group = $('<div class="note-' + group[0] + ' btn-group">'); | ||
@@ -489,3 +502,3 @@ for (var i = 0, lenGroup = group[1].length; i < lenGroup; i++) { | ||
$button.attr('data-name', group[1][i]); | ||
$group.append($button); | ||
@@ -500,10 +513,10 @@ } | ||
var $notePopover = $('<div class="note-popover" />'); | ||
$notePopover.append(tplLinkPopover()); | ||
$notePopover.append(tplImagePopover()); | ||
if (options.airMode) { | ||
$notePopover.append(tplAirPopover()); | ||
} | ||
return $notePopover; | ||
@@ -871,3 +884,3 @@ }; | ||
} | ||
$toolbar.prependTo($editor); | ||
@@ -886,4 +899,6 @@ var keyMap = options.keyMap[agent.isMac ? 'mac' : 'pc']; | ||
var $dialogContainer = options.dialogsInBody ? document.body : $editor; | ||
//07. create Dialog | ||
var $dialog = $(tplDialogs(langInfo, options)).prependTo($editor); | ||
var $dialog = $(tplDialogs(langInfo, options)).prependTo($dialogContainer); | ||
$dialog.find('button.close, a.modal-close').click(function () { | ||
@@ -966,2 +981,5 @@ $(this).closest('.modal').modal('hide'); | ||
if (options.dialogsInBody) { | ||
layoutInfo.dialog().remove(); | ||
} | ||
layoutInfo.editor().remove(); | ||
@@ -968,0 +986,0 @@ $holder.show(); |
@@ -40,1 +40,30 @@ /** | ||
}); | ||
/* jshint ignore:start */ | ||
var log = []; | ||
QUnit.done(function (test_results) { | ||
var tests = []; | ||
for(var i = 0, len = log.length; i < len; i++) { | ||
var details = log[i]; | ||
tests.push({ | ||
name: details.name, | ||
result: details.result, | ||
expected: details.expected, | ||
actual: details.actual, | ||
source: details.source | ||
}); | ||
} | ||
test_results.tests = tests; | ||
window.global_test_results = test_results; | ||
}); | ||
QUnit.testStart(function(testDetails){ | ||
QUnit.log(function(details){ | ||
if (!details.result) { | ||
details.name = testDetails.name; | ||
log.push(details); | ||
} | ||
}); | ||
}); | ||
/* jshint ignore:end */ |
@@ -173,3 +173,3 @@ /** | ||
var equalsToUpperCase = function (actual, expected, comment) { | ||
ok(actual.toUpperCase() === expected.toUpperCase(), comment); | ||
equal(actual.toUpperCase(), expected.toUpperCase(), comment); | ||
}; | ||
@@ -176,0 +176,0 @@ |
@@ -74,7 +74,8 @@ /** | ||
test('rng.normalize', function () { | ||
var rng, $cont, $p, $b, $u; | ||
$cont = $('<div><p><b>b</b><u>u</u></p></div>'); | ||
var rng, $cont, $p, $b, $u, $s; | ||
$cont = $('<div><p><b>b</b><u>u</u><s>s</s></p></div>'); | ||
$p = $cont.find('p'); | ||
$b = $cont.find('b'); | ||
$u = $cont.find('u'); | ||
$s = $cont.find('s'); | ||
@@ -94,4 +95,59 @@ rng = range.create($p[0], 0, $p[0], 2).normalize(); | ||
], 'rng.normalize on `<b>b</b>|<u>u</u>` should returns `<b>b|</b><u>u</u>`'); | ||
rng = range.create($p[0], 1, $p[0], 1).normalize(); | ||
deepEqual([ | ||
rng.sc, rng.so, rng.ec, rng.eo | ||
], [ | ||
$b[0].firstChild, 1, $b[0].firstChild, 1 | ||
], 'rng.normalize on `<b>b</b>|<u>u</u>` should returns `<b>b|</b><u>u</u>`'); | ||
rng = range.create($b[0].firstChild, 1, $s[0].firstChild, 0).normalize(); | ||
deepEqual([ | ||
rng.sc, rng.so, rng.ec, rng.eo | ||
], [ | ||
$u[0].firstChild, 0, $u[0].firstChild, 1 | ||
], 'rng.normalize on `<b>b|</b><u>u</u><s>|s</s>` should returns `<b>b</b><u>|u|</u><s>s</s>`'); | ||
rng = range.create($b[0].firstChild, 1, $b[0].firstChild, 1).normalize(); | ||
deepEqual([ | ||
rng.sc, rng.so, rng.ec, rng.eo | ||
], [ | ||
$b[0].firstChild, 1, $b[0].firstChild, 1 | ||
], 'rng.normalize on `<b>b|</b><u>u</u><s>s</s>` should returns `<b>b|</b><u>u</u><s>s</s>`'); | ||
}); | ||
test('rng.normalize (block level)', function () { | ||
var rng, $cont, $p; | ||
$cont = $('<div><p>text</p><p><br></p></div>'); | ||
$p = $cont.find('p'); | ||
rng = range.create($p[1], 0, $p[1], 0).normalize(); | ||
deepEqual([ | ||
rng.sc, rng.so, rng.ec, rng.eo | ||
], [ | ||
$p[1], 0, $p[1], 0 | ||
], 'rng.normalize on `<p>text</p><p>|<br></p>` should returns `<p>text</p><p>|<br></p>`'); | ||
$cont = $('<div><p>text</p><p>text</p></div>'); | ||
$p = $cont.find('p'); | ||
rng = range.create($p[1], 0, $p[1], 0).normalize(); | ||
deepEqual([ | ||
rng.sc, rng.so, rng.ec, rng.eo | ||
], [ | ||
$p[1].firstChild, 0, $p[1].firstChild, 0 | ||
], 'rng.normalize on `<p>text</p><p>|text</p>` should returns `<p>text</p><p>|text</b></p>`'); | ||
$cont = $('<div class="note-editable"><p>text</p><p>text</p></div>'); | ||
$p = $cont.find('p'); | ||
rng = range.create($cont[0], 0, $cont[0], 2).normalize(); | ||
deepEqual([ | ||
rng.sc, rng.so, rng.ec, rng.eo | ||
], [ | ||
$p[0].firstChild, 0, $p[1].firstChild, 4 | ||
], 'rng.normalize on `|<p>text</p><p>text</p>|` should returns `<p>|text</p><p>text|</b></p>`'); | ||
}); | ||
test('rng.insertNode', function () { | ||
@@ -98,0 +154,0 @@ var $cont, $p, $p2, $b, $u; |
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
Sorry, the diff of this file is not supported yet
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
1432040
34757
184
14
127
12