You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
312 lines
10 KiB
312 lines
10 KiB
(function(factory) { |
|
/* global define */ |
|
if (typeof define === 'function' && define.amd) { |
|
// AMD. Register as an anonymous module. |
|
define(['jquery'], factory); |
|
} else if (typeof module === 'object' && module.exports) { |
|
// Node/CommonJS |
|
module.exports = factory(require('jquery')); |
|
} else { |
|
// Browser globals |
|
factory(window.jQuery); |
|
} |
|
}(function($) { |
|
$.extend($.summernote.plugins, { |
|
'specialchars': function(context) { |
|
var self = this; |
|
var ui = $.summernote.ui; |
|
|
|
var $editor = context.layoutInfo.editor; |
|
var options = context.options; |
|
var lang = options.langInfo; |
|
|
|
var KEY = { |
|
UP: 38, |
|
DOWN: 40, |
|
LEFT: 37, |
|
RIGHT: 39, |
|
ENTER: 13 |
|
}; |
|
var COLUMN_LENGTH = 15; |
|
var COLUMN_WIDTH = 35; |
|
|
|
var currentColumn = 0; |
|
var currentRow = 0; |
|
var totalColumn = 0; |
|
var totalRow = 0; |
|
|
|
// special characters data set |
|
var specialCharDataSet = [ |
|
'"', '&', '<', '>', '¡', '¢', |
|
'£', '¤', '¥', '¦', '§', |
|
'¨', '©', 'ª', '«', '¬', |
|
'®', '¯', '°', '±', '²', |
|
'³', '´', 'µ', '¶', '·', |
|
'¸', '¹', 'º', '»', '¼', |
|
'½', '¾', '¿', '×', '÷', |
|
'ƒ', 'ˆ', '˜', '–', '—', |
|
'‘', '’', '‚', '“', '”', |
|
'„', '†', '‡', '•', '…', |
|
'‰', '′', '″', '‹', '›', |
|
'‾', '⁄', '€', 'ℑ', '℘', |
|
'ℜ', '™', 'ℵ', '←', '↑', |
|
'→', '↓', '↔', '↵', '⇐', |
|
'⇑', '⇒', '⇓', '⇔', '∀', |
|
'∂', '∃', '∅', '∇', '∈', |
|
'∉', '∋', '∏', '∑', '−', |
|
'∗', '√', '∝', '∞', '∠', |
|
'∧', '∨', '∩', '∪', '∫', |
|
'∴', '∼', '≅', '≈', '≠', |
|
'≡', '≤', '≥', '⊂', '⊃', |
|
'⊄', '⊆', '⊇', '⊕', '⊗', |
|
'⊥', '⋅', '⌈', '⌉', '⌊', |
|
'⌋', '◊', '♠', '♣', '♥', |
|
'♦' |
|
]; |
|
|
|
context.memo('button.specialchars', function() { |
|
return ui.button({ |
|
contents: '<i class="fa fa-font fa-flip-vertical">', |
|
tooltip: lang.specialChar.specialChar, |
|
click: function() { |
|
self.show(); |
|
} |
|
}).render(); |
|
}); |
|
|
|
/** |
|
* Make Special Characters Table |
|
* |
|
* @member plugin.specialChar |
|
* @private |
|
* @return {jQuery} |
|
*/ |
|
this.makeSpecialCharSetTable = function() { |
|
var $table = $('<table/>'); |
|
$.each(specialCharDataSet, function(idx, text) { |
|
var $td = $('<td/>').addClass('note-specialchar-node'); |
|
var $tr = (idx % COLUMN_LENGTH === 0) ? $('<tr/>') : $table.find('tr').last(); |
|
|
|
var $button = ui.button({ |
|
callback: function($node) { |
|
$node.html(text); |
|
$node.attr('title', text); |
|
$node.attr('data-value', encodeURIComponent(text)); |
|
$node.css({ |
|
width: COLUMN_WIDTH, |
|
'margin-right': '2px', |
|
'margin-bottom': '2px' |
|
}); |
|
} |
|
}).render(); |
|
|
|
$td.append($button); |
|
|
|
$tr.append($td); |
|
if (idx % COLUMN_LENGTH === 0) { |
|
$table.append($tr); |
|
} |
|
}); |
|
|
|
totalRow = $table.find('tr').length; |
|
totalColumn = COLUMN_LENGTH; |
|
|
|
return $table; |
|
}; |
|
|
|
this.initialize = function() { |
|
var $container = options.dialogsInBody ? $(document.body) : $editor; |
|
|
|
var body = '<div class="form-group row-fluid">' + this.makeSpecialCharSetTable()[0].outerHTML + '</div>'; |
|
|
|
this.$dialog = ui.dialog({ |
|
title: lang.specialChar.select, |
|
body: body |
|
}).render().appendTo($container); |
|
}; |
|
|
|
this.show = function() { |
|
var text = context.invoke('editor.getSelectedText'); |
|
context.invoke('editor.saveRange'); |
|
this.showSpecialCharDialog(text).then(function(selectChar) { |
|
context.invoke('editor.restoreRange'); |
|
|
|
// build node |
|
var $node = $('<span></span>').html(selectChar)[0]; |
|
|
|
if ($node) { |
|
// insert video node |
|
context.invoke('editor.insertNode', $node); |
|
} |
|
}).fail(function() { |
|
context.invoke('editor.restoreRange'); |
|
}); |
|
}; |
|
|
|
/** |
|
* show image dialog |
|
* |
|
* @param {jQuery} $dialog |
|
* @return {Promise} |
|
*/ |
|
this.showSpecialCharDialog = function(text) { |
|
return $.Deferred(function(deferred) { |
|
var $specialCharDialog = self.$dialog; |
|
var $specialCharNode = $specialCharDialog.find('.note-specialchar-node'); |
|
var $selectedNode = null; |
|
var ARROW_KEYS = [KEY.UP, KEY.DOWN, KEY.LEFT, KEY.RIGHT]; |
|
var ENTER_KEY = KEY.ENTER; |
|
|
|
function addActiveClass($target) { |
|
if (!$target) { |
|
return; |
|
} |
|
$target.find('button').addClass('active'); |
|
$selectedNode = $target; |
|
} |
|
|
|
function removeActiveClass($target) { |
|
$target.find('button').removeClass('active'); |
|
$selectedNode = null; |
|
} |
|
|
|
// find next node |
|
function findNextNode(row, column) { |
|
var findNode = null; |
|
$.each($specialCharNode, function(idx, $node) { |
|
var findRow = Math.ceil((idx + 1) / COLUMN_LENGTH); |
|
var findColumn = ((idx + 1) % COLUMN_LENGTH === 0) ? COLUMN_LENGTH : (idx + 1) % COLUMN_LENGTH; |
|
if (findRow === row && findColumn === column) { |
|
findNode = $node; |
|
return false; |
|
} |
|
}); |
|
return $(findNode); |
|
} |
|
|
|
function arrowKeyHandler(keyCode) { |
|
// left, right, up, down key |
|
var $nextNode; |
|
var lastRowColumnLength = $specialCharNode.length % totalColumn; |
|
|
|
if (KEY.LEFT === keyCode) { |
|
if (currentColumn > 1) { |
|
currentColumn = currentColumn - 1; |
|
} else if (currentRow === 1 && currentColumn === 1) { |
|
currentColumn = lastRowColumnLength; |
|
currentRow = totalRow; |
|
} else { |
|
currentColumn = totalColumn; |
|
currentRow = currentRow - 1; |
|
} |
|
} else if (KEY.RIGHT === keyCode) { |
|
if (currentRow === totalRow && lastRowColumnLength === currentColumn) { |
|
currentColumn = 1; |
|
currentRow = 1; |
|
} else if (currentColumn < totalColumn) { |
|
currentColumn = currentColumn + 1; |
|
} else { |
|
currentColumn = 1; |
|
currentRow = currentRow + 1; |
|
} |
|
} else if (KEY.UP === keyCode) { |
|
if (currentRow === 1 && lastRowColumnLength < currentColumn) { |
|
currentRow = totalRow - 1; |
|
} else { |
|
currentRow = currentRow - 1; |
|
} |
|
} else if (KEY.DOWN === keyCode) { |
|
currentRow = currentRow + 1; |
|
} |
|
|
|
if (currentRow === totalRow && currentColumn > lastRowColumnLength) { |
|
currentRow = 1; |
|
} else if (currentRow > totalRow) { |
|
currentRow = 1; |
|
} else if (currentRow < 1) { |
|
currentRow = totalRow; |
|
} |
|
|
|
$nextNode = findNextNode(currentRow, currentColumn); |
|
|
|
if ($nextNode) { |
|
removeActiveClass($selectedNode); |
|
addActiveClass($nextNode); |
|
} |
|
} |
|
|
|
function enterKeyHandler() { |
|
if (!$selectedNode) { |
|
return; |
|
} |
|
|
|
deferred.resolve(decodeURIComponent($selectedNode.find('button').attr('data-value'))); |
|
$specialCharDialog.modal('hide'); |
|
} |
|
|
|
function keyDownEventHandler(event) { |
|
event.preventDefault(); |
|
var keyCode = event.keyCode; |
|
if (keyCode === undefined || keyCode === null) { |
|
return; |
|
} |
|
// check arrowKeys match |
|
if (ARROW_KEYS.indexOf(keyCode) > -1) { |
|
if ($selectedNode === null) { |
|
addActiveClass($specialCharNode.eq(0)); |
|
currentColumn = 1; |
|
currentRow = 1; |
|
return; |
|
} |
|
arrowKeyHandler(keyCode); |
|
} else if (keyCode === ENTER_KEY) { |
|
enterKeyHandler(); |
|
} |
|
return false; |
|
} |
|
|
|
// remove class |
|
removeActiveClass($specialCharNode); |
|
|
|
// find selected node |
|
if (text) { |
|
for (var i = 0; i < $specialCharNode.length; i++) { |
|
var $checkNode = $($specialCharNode[i]); |
|
if ($checkNode.text() === text) { |
|
addActiveClass($checkNode); |
|
currentRow = Math.ceil((i + 1) / COLUMN_LENGTH); |
|
currentColumn = (i + 1) % COLUMN_LENGTH; |
|
} |
|
} |
|
} |
|
|
|
ui.onDialogShown(self.$dialog, function() { |
|
$(document).on('keydown', keyDownEventHandler); |
|
|
|
self.$dialog.find('button').tooltip(); |
|
|
|
$specialCharNode.on('click', function(event) { |
|
event.preventDefault(); |
|
deferred.resolve(decodeURIComponent($(event.currentTarget).find('button').attr('data-value'))); |
|
ui.hideDialog(self.$dialog); |
|
}); |
|
}); |
|
|
|
ui.onDialogHidden(self.$dialog, function() { |
|
$specialCharNode.off('click'); |
|
|
|
self.$dialog.find('button').tooltip('destroy'); |
|
|
|
$(document).off('keydown', keyDownEventHandler); |
|
|
|
if (deferred.state() === 'pending') { |
|
deferred.reject(); |
|
} |
|
}); |
|
|
|
ui.showDialog(self.$dialog); |
|
}); |
|
}; |
|
} |
|
}); |
|
}));
|
|
|