From 3c6330b5913e3575d7f3fd234803e01d18b4f45b Mon Sep 17 00:00:00 2001 From: "Alan P. Laudicina" Date: Wed, 12 Jun 2013 15:16:16 -0400 Subject: [PATCH 01/13] Bold / Italic / Strike / Underline buttons don't work correctly * Fixes #19 * Problem: addRange or removeAllRanges kills the browser command states. * Store cache in commandCache * Add updateCommandCache and restoreCommandCache methods * Add support for command-with-arg buttons to be in an active state * Update command cache when running execCommand * Store and then update selected commands when selecting a toolbar button * Create the command cache by looking up the buttons that are in the DOM, set the default value to false --- bootstrap-wysiwyg.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index 69f64a7..7a10c7f 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -23,12 +23,43 @@ selectedRange, options, toolbarBtnSelector, + commandCache = {}, + updateCommandCache = function () { + var key; + for (key in commandCache) { + var commandValue = document.queryCommandValue(key); + var commandState = document.queryCommandState(key); + + if (commandState) { + commandCache[key] = commandState; + } else if (commandValue.length > 0 && commandValue !== 'false') { + commandCache[key] = commandValue; + } else { + commandCache[key] = false; + } + } + }, + restoreCommandCache = function() { + var key; + for (key in commandCache) { + var val = commandCache[key]; + if (typeof(val) === 'boolean') { + if (val !== document.queryCommandState(key)) { + document.execCommand(key, 0, null); + } + } else if (val !== document.queryCommandValue(key)) { + document.execCommand(key, 0, val); + } + } + }, updateToolbar = function () { if (options.activeToolbarClass) { $(options.toolbarSelector).find(toolbarBtnSelector).each(function () { var command = $(this).data(options.commandRole); if (document.queryCommandState(command)) { $(this).addClass(options.activeToolbarClass); + } else if (command.split(' ')[0] + ' ' + document.queryCommandValue(command.split(' ')[0]) === command) { + $(this).addClass(options.activeToolbarClass); } else { $(this).removeClass(options.activeToolbarClass); } @@ -40,6 +71,11 @@ command = commandArr.shift(), args = commandArr.join(' ') + (valueArg || ''); document.execCommand(command, 0, args); + if (args.length > 0) { + commandCache[command] = document.queryCommandValue(command); + } else { + commandCache[command] = document.queryCommandState(command); + } updateToolbar(); }, bindHotkeys = function (hotKeys) { @@ -104,8 +140,10 @@ }, bindToolbar = function (toolbar, options) { toolbar.find(toolbarBtnSelector).click(function () { + updateCommandCache(); restoreSelection(); editor.focus(); + restoreCommandCache(); execCommand($(this).data(options.commandRole)); saveSelection(); }); @@ -164,6 +202,12 @@ saveSelection(); updateToolbar(); }); + + $(toolbarBtnSelector).each(function () { + var btnAttr = this.getAttribute('data-' + options.commandRole); + commandCache[btnAttr.split(' ')[0]] = false; + }); + $(window).bind('touchend', function (e) { var isInside = (editor.is(e.target) || editor.has(e.target).length > 0), currentRange = getCurrentRange(), From a73b7f9bcf92a16750d2708aa55e4895cc8e715a Mon Sep 17 00:00:00 2001 From: "Alan P. Laudicina" Date: Wed, 12 Jun 2013 15:17:38 -0400 Subject: [PATCH 02/13] Cleanup `split` with a `slice` --- bootstrap-wysiwyg.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index 7a10c7f..58dcbe2 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -56,9 +56,10 @@ if (options.activeToolbarClass) { $(options.toolbarSelector).find(toolbarBtnSelector).each(function () { var command = $(this).data(options.commandRole); + var commandNoArgs = command.slice(0, command.indexOf(' ')); if (document.queryCommandState(command)) { $(this).addClass(options.activeToolbarClass); - } else if (command.split(' ')[0] + ' ' + document.queryCommandValue(command.split(' ')[0]) === command) { + } else if (commandNoArgs + ' ' + document.queryCommandValue(commandNoArgs) === command) { $(this).addClass(options.activeToolbarClass); } else { $(this).removeClass(options.activeToolbarClass); From 19b17b6e3aec4e276831a919b5374fdce7f9b6c2 Mon Sep 17 00:00:00 2001 From: "Alan P. Laudicina" Date: Wed, 12 Jun 2013 15:19:52 -0400 Subject: [PATCH 03/13] Fix hotkey support, add event namespaces, add hotkey enable callback * Map multiple hotkeys in a string into single hotkey commands * Store them in a hash * When buttons are pressed (`keydown`), compute a string to match them on * They must match this format: * Keys must come in order of modifiers as shown below: * +(+) `(shift optional)` * +(+) * +(+) * + * `(i.e. tab)` * Change `bindHotKeys` to only bind single events for `keydown` and `keyup` * Re-map the keys into a hash as mentioned above * Prevent propagation and default if we match a command we're allowed * Add callback option to test a hotkey to see if it's allowed. * Add `namsepaceEvents` helper, to create all events with a namespace * Add default callback for `hotKeyEnabledCallback` * returns `true` if a hotkey can execute browser command * takes parameter `command`. i.e. `bold` --- bootstrap-wysiwyg.js | 143 +++++++++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 32 deletions(-) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index 58dcbe2..2034519 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -52,6 +52,21 @@ } } }, + namespaceEvents = function(events) { + if (events.toString() != '[object Array]') { + events = events.split(' '); + } + + events.push(''); // Add empty element for easy join() support. + + if (options.eventNamespace != null && options.eventNamespace.length > 0) + { + return events.join('.' + options.eventNamespace + ' '); + } else { + return events.join(' '); + } + + }, updateToolbar = function () { if (options.activeToolbarClass) { $(options.toolbarSelector).find(toolbarBtnSelector).each(function () { @@ -79,20 +94,82 @@ } updateToolbar(); }, + mapHotKeyToCommand = function (event, hotKeys) { + var hotKey = ''; + + if (event.ctrlKey) { + hotKey += 'ctrl+'; + } else if (event.metaKey) { + hotKey += 'meta+'; + } + + if (event.shiftKey) { + hotKey += 'shift+'; + } + + var keyCode = event.keyCode; + if (keyCode >= 20) { // Real characters + // Reduce UTF chars. + if (96 <= event.keyCode && event.keyCode <= 105) { + keyCode -= 48; + } + hotKey += String.fromCharCode(keyCode).toLowerCase(); + + } else if (event.keyCode === 9) { // TAB character + hotKey += 'tab'; + } + + return hotKeys[hotKey]; + + }, + editorActive = function () { + return (editor.attr('contenteditable') && editor.is(':visible')); + }, bindHotkeys = function (hotKeys) { - $.each(hotKeys, function (hotkey, command) { - editor.keydown(hotkey, function (e) { - if (editor.attr('contenteditable') && editor.is(':visible')) { - e.preventDefault(); - e.stopPropagation(); - execCommand(command); + /* + * Break up the hotkeys into multiple values. For example + * {'ctrl+b meta+b': 'bold'} -> {'ctrl+b': 'bold'},{'meta+b': 'bold'}. + * Also ensures everything is stored in lower case. + */ + for (var hotKey in hotKeys) { + var split; + if ((split = hotKey.split(' ')).length > 1) { + for (var i = 0; i < split.length; i++) { + hotKeys[split[i].toLowerCase()] = hotKeys[hotKey].toLowerCase(); } - }).keyup(hotkey, function (e) { - if (editor.attr('contenteditable') && editor.is(':visible')) { - e.preventDefault(); - e.stopPropagation(); + delete hotKeys[hotKey]; + } else { + hotKeys[hotKey.toLowerCase()] = hotKeys[hotKey].toLowerCase(); + } + } + + var eventNamespace = options.eventNamespace + '-' + 'bindKeys'; + + editor.on(namespaceEvents('keydown'), hotKeys, function (e) { + var command = ''; + + // Ensure we have a command to execute for this hotkey, before blocking anything. + if (editorActive() && (command = mapHotKeyToCommand(e, hotKeys)) != null) { + e.preventDefault(); + e.stopPropagation(); + + // Execute hotkey if the callback tells us it's enabled. + if ( + typeof options.hotKeyEnabledCallback === 'function' && + options.hotKeyEnabledCallback(command) + ) { + execCommand(command); } - }); + } + }); + + // Install a single keyup handler to block the keyup events matching a hotKey. + editor.on(namespaceEvents('keyup'), hotKeys, function (e) { + var command = ''; + if (editorActive() && (command = mapHotKeyToCommand(e, hotKeys)) != null) { + e.preventDefault(); + e.stopPropagation(); + } }); }, getCurrentRange = function () { @@ -124,8 +201,8 @@ $.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) { execCommand('insertimage', dataUrl); }).fail(function (e) { - options.fileUploadError("file-reader", e); - }); + options.fileUploadError("file-reader", e); + }); } else { options.fileUploadError("unsupported-file-type", fileInfo.type); } @@ -150,7 +227,7 @@ }); toolbar.find('[data-toggle=dropdown]').click(restoreSelection); - toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () { + toolbar.find('input[type=text][data-' + options.commandRole + ']').on(namespaceEvents('webkitspeechchange change'), function () { var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */ this.value = ''; restoreSelection(); @@ -159,30 +236,30 @@ execCommand($(this).data(options.commandRole), newValue); } saveSelection(); - }).on('focus', function () { - var input = $(this); - if (!input.data(options.selectionMarker)) { - markSelection(input, options.selectionColor); - input.focus(); - } - }).on('blur', function () { - var input = $(this); - if (input.data(options.selectionMarker)) { - markSelection(input, false); - } - }); - toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () { + }).on(namespaceEvents('focus'), function () { + var input = $(this); + if (!input.data(options.selectionMarker)) { + markSelection(input, options.selectionColor); + input.focus(); + } + }).on(namespaceEvents('blur'), function () { + var input = $(this); + if (input.data(options.selectionMarker)) { + markSelection(input, false); + } + }); + toolbar.find('input[type=file][data-' + options.commandRole + ']').on(namespaceEvents('change'), (function () { restoreSelection(); if (this.type === 'file' && this.files && this.files.length > 0) { insertFiles(this.files); } saveSelection(); this.value = ''; - }); + })); }, initFileDrops = function () { - editor.on('dragenter dragover', false) - .on('drop', function (e) { + editor.on(namespaceEvents('dragenter dragover'), false) + .on(namespaceEvents('drop'), function (e) { var dataTransfer = e.originalEvent.dataTransfer; e.stopPropagation(); e.preventDefault(); @@ -199,7 +276,7 @@ } bindToolbar($(options.toolbarSelector), options); editor.attr('contenteditable', true) - .on('mouseup keyup mouseout', function () { + .on(namespaceEvents('mouseup keyup mouseout'), function () { saveSelection(); updateToolbar(); }); @@ -240,6 +317,8 @@ selectionMarker: 'edit-focus-marker', selectionColor: 'darkgrey', dragAndDropImages: true, + eventNamespace: 'bootstrap-wysiwyg', + hotKeyEnabledCallback: function(command) { return true; }, fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); } }; -}(window.jQuery)); +}(window.jQuery)); \ No newline at end of file From a941e1b0a59e7cefb88a45bef5692c1376a0259e Mon Sep 17 00:00:00 2001 From: Jeff Szusz Date: Tue, 8 Oct 2013 11:47:29 -0400 Subject: [PATCH 04/13] DRCWBTE-2859 - Update command cache on , DRCWBTE-4157 - Setup custom font size handlers on wysiwyg --- bootstrap-wysiwyg.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index 2034519..d6439da 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -71,8 +71,17 @@ if (options.activeToolbarClass) { $(options.toolbarSelector).find(toolbarBtnSelector).each(function () { var command = $(this).data(options.commandRole); - var commandNoArgs = command.slice(0, command.indexOf(' ')); - if (document.queryCommandState(command)) { + var commandNoArgs; // Temporarily store index, replace with command. + + if ((commandNoArgs = command.indexOf(' ')) >= 0) { + commandNoArgs = command.slice(0, commandNoArgs); + } else { + commandNoArgs = command; + } + + if (commandNoArgs === "fontSize" && options.setRealFontSize != null) { + options.setRealFontSize(selectedRange); + } else if (document.queryCommandState(command)) { $(this).addClass(options.activeToolbarClass); } else if (commandNoArgs + ' ' + document.queryCommandValue(commandNoArgs) === command) { $(this).addClass(options.activeToolbarClass); @@ -179,6 +188,7 @@ } }, saveSelection = function () { + updateCommandCache(); selectedRange = getCurrentRange(); }, restoreSelection = function () { @@ -218,7 +228,6 @@ }, bindToolbar = function (toolbar, options) { toolbar.find(toolbarBtnSelector).click(function () { - updateCommandCache(); restoreSelection(); editor.focus(); restoreCommandCache(); @@ -269,7 +278,7 @@ }); }; options = $.extend({}, $.fn.wysiwyg.defaults, userOptions); - toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']'; + toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + '],div[data-' + options.commandRole + ']'; bindHotkeys(options.hotKeys); if (options.dragAndDropImages) { initFileDrops(); @@ -295,7 +304,7 @@ updateToolbar(); } }); - return this; + return this; }; $.fn.wysiwyg.defaults = { hotKeys: { @@ -321,4 +330,4 @@ hotKeyEnabledCallback: function(command) { return true; }, fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); } }; -}(window.jQuery)); \ No newline at end of file +}(window.jQuery)); From cad82dbcd5b18c1bfc2bccc9e5bf380152fa7194 Mon Sep 17 00:00:00 2001 From: Jeff Szusz Date: Tue, 8 Oct 2013 11:55:09 -0400 Subject: [PATCH 05/13] DRCWBTE-4165, DRCWBTE-4155 - NE Writing Tool - Cursor moves position on indent; update save/restore selection to track cursor position --- bootstrap-wysiwyg.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index d6439da..ccf91cf 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -21,6 +21,8 @@ $.fn.wysiwyg = function (userOptions) { var editor = this, selectedRange, + selectionNode, + caretPos, options, toolbarBtnSelector, commandCache = {}, @@ -190,6 +192,8 @@ saveSelection = function () { updateCommandCache(); selectedRange = getCurrentRange(); + selectionNode = window.getSelection().anchorNode; + caretPos = window.getSelection().baseOffset; }, restoreSelection = function () { var selection = window.getSelection(); @@ -201,6 +205,10 @@ document.selection.empty(); } + if (selectionNode) { + selectedRange.setStart(selectionNode, caretPos); + selectedRange.collapse(true); + ) selection.addRange(selectedRange); } }, From 479a4a4b1d221bca978c11cb8c58925d23c7288d Mon Sep 17 00:00:00 2001 From: Jeff Szusz Date: Tue, 8 Oct 2013 12:10:08 -0400 Subject: [PATCH 06/13] DRCWBTE-4165, DRCWBTE-4155 - NE Writing Tool - Cursor moves position on indent; update save/restore selection to track cursor position --- bootstrap-wysiwyg.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index d6439da..b237585 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -21,6 +21,8 @@ $.fn.wysiwyg = function (userOptions) { var editor = this, selectedRange, + selectionNode, + caretPos, options, toolbarBtnSelector, commandCache = {}, @@ -190,6 +192,8 @@ saveSelection = function () { updateCommandCache(); selectedRange = getCurrentRange(); + selectionNode = window.getSelection().anchorNode; + caretPos = window.getSelection().baseOffset; }, restoreSelection = function () { var selection = window.getSelection(); @@ -201,6 +205,10 @@ document.selection.empty(); } + if (selectionNode) { + selectedRange.setStart(selectionNode, caretPos); + selectedRange.collapse(true); + } selection.addRange(selectedRange); } }, From 77a33eaf472d10825b92e72e716638a8f5af1620 Mon Sep 17 00:00:00 2001 From: Jeff Szusz Date: Mon, 28 Oct 2013 15:20:30 -0400 Subject: [PATCH 07/13] caret position restoration is optional depending on command being given --- bootstrap-wysiwyg.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index b237585..84405b6 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -195,7 +195,7 @@ selectionNode = window.getSelection().anchorNode; caretPos = window.getSelection().baseOffset; }, - restoreSelection = function () { + restoreSelection = function (restoreCaret) { var selection = window.getSelection(); if (selectedRange) { try { @@ -205,7 +205,7 @@ document.selection.empty(); } - if (selectionNode) { + if (restoreCaret && selectionNode) { selectedRange.setStart(selectionNode, caretPos); selectedRange.collapse(true); } @@ -236,7 +236,12 @@ }, bindToolbar = function (toolbar, options) { toolbar.find(toolbarBtnSelector).click(function () { - restoreSelection(); + if ($.inArray($(this).data(options.commandRole), ['indent', 'outdent']) > -1) { + // restore caret position after certain commands + restoreSelection(true); + } else { + restoreSelection(); + } editor.focus(); restoreCommandCache(); execCommand($(this).data(options.commandRole)); From 683d9f2f28fa36f7ec555511acc5424c3ac4d8e9 Mon Sep 17 00:00:00 2001 From: Jeff Szusz Date: Wed, 30 Oct 2013 12:27:26 -0400 Subject: [PATCH 08/13] simplify caret restoration check --- bootstrap-wysiwyg.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index 84405b6..829bec1 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -236,12 +236,9 @@ }, bindToolbar = function (toolbar, options) { toolbar.find(toolbarBtnSelector).click(function () { - if ($.inArray($(this).data(options.commandRole), ['indent', 'outdent']) > -1) { - // restore caret position after certain commands - restoreSelection(true); - } else { - restoreSelection(); - } + // restore caret position after certain commands + var restoreCaret = ['indent', 'outdent'].indexOf($(this).data(options.commandRole)) > -1 + restoreSelection(restoreCaret); editor.focus(); restoreCommandCache(); execCommand($(this).data(options.commandRole)); From f4be2dd57084d9203a13f20835033a66c6042220 Mon Sep 17 00:00:00 2001 From: Jeff Szusz Date: Thu, 28 Nov 2013 09:28:57 -0500 Subject: [PATCH 09/13] pass event object through to setRealFontSize callback --- bootstrap-wysiwyg.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index 829bec1..fcb1e37 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -69,7 +69,7 @@ } }, - updateToolbar = function () { + updateToolbar = function (e) { if (options.activeToolbarClass) { $(options.toolbarSelector).find(toolbarBtnSelector).each(function () { var command = $(this).data(options.commandRole); @@ -82,7 +82,7 @@ } if (commandNoArgs === "fontSize" && options.setRealFontSize != null) { - options.setRealFontSize(selectedRange); + options.setRealFontSize(selectedRange, e); } else if (document.queryCommandState(command)) { $(this).addClass(options.activeToolbarClass); } else if (commandNoArgs + ' ' + document.queryCommandValue(commandNoArgs) === command) { @@ -295,9 +295,11 @@ } bindToolbar($(options.toolbarSelector), options); editor.attr('contenteditable', true) - .on(namespaceEvents('mouseup keyup mouseout'), function () { - saveSelection(); - updateToolbar(); + .on(namespaceEvents('mouseup keyup mouseout'), function (e) { + setTimeout(function() { + saveSelection(); + updateToolbar(e); + }, 0); }); $(toolbarBtnSelector).each(function () { From 29e4ed90635dab379587058943132d9b1cb88c1e Mon Sep 17 00:00:00 2001 From: Jeff Szusz Date: Fri, 29 Nov 2013 15:56:52 -0500 Subject: [PATCH 10/13] alan fixed a command cache bug --- bootstrap-wysiwyg.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index fcb1e37..347c208 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -69,7 +69,7 @@ } }, - updateToolbar = function (e) { + updateToolbar = function () { if (options.activeToolbarClass) { $(options.toolbarSelector).find(toolbarBtnSelector).each(function () { var command = $(this).data(options.commandRole); @@ -80,10 +80,9 @@ } else { commandNoArgs = command; } - - if (commandNoArgs === "fontSize" && options.setRealFontSize != null) { - options.setRealFontSize(selectedRange, e); - } else if (document.queryCommandState(command)) { + if (commandNoArgs === "fontSize" && options.setRealFontSize != null) { + options.setRealFontSize(selectedRange); + } else if (document.queryCommandState(command)) { $(this).addClass(options.activeToolbarClass); } else if (commandNoArgs + ' ' + document.queryCommandValue(commandNoArgs) === command) { $(this).addClass(options.activeToolbarClass); @@ -156,6 +155,12 @@ var eventNamespace = options.eventNamespace + '-' + 'bindKeys'; + editor.on(namespaceEvents('focus'), function(e) { + setTimeout(function() { + restoreCommandCache(); + }, 0); + }); + editor.on(namespaceEvents('keydown'), hotKeys, function (e) { var command = ''; @@ -295,10 +300,10 @@ } bindToolbar($(options.toolbarSelector), options); editor.attr('contenteditable', true) - .on(namespaceEvents('mouseup keyup mouseout'), function (e) { + .on(namespaceEvents('mouseup keyup mouseout'), function () { setTimeout(function() { saveSelection(); - updateToolbar(e); + updateToolbar(); }, 0); }); From 44bf7571784efa3bbc7ece692a781b5109549f96 Mon Sep 17 00:00:00 2001 From: Lee Davis Date: Tue, 7 Oct 2014 17:03:49 -0700 Subject: [PATCH 11/13] DRCWBTE-7417 * Removed changes on indent from DRCWBTE-4165. ** DRCWBTE-4165 introduces an regression when used with spellcheck. ** This issue has been remedied indirectly in DRCWBTE-6975 --- bootstrap-wysiwyg.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index 347c208..76c01c4 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -21,8 +21,6 @@ $.fn.wysiwyg = function (userOptions) { var editor = this, selectedRange, - selectionNode, - caretPos, options, toolbarBtnSelector, commandCache = {}, @@ -197,10 +195,8 @@ saveSelection = function () { updateCommandCache(); selectedRange = getCurrentRange(); - selectionNode = window.getSelection().anchorNode; - caretPos = window.getSelection().baseOffset; }, - restoreSelection = function (restoreCaret) { + restoreSelection = function () { var selection = window.getSelection(); if (selectedRange) { try { @@ -210,10 +206,6 @@ document.selection.empty(); } - if (restoreCaret && selectionNode) { - selectedRange.setStart(selectionNode, caretPos); - selectedRange.collapse(true); - } selection.addRange(selectedRange); } }, @@ -241,9 +233,7 @@ }, bindToolbar = function (toolbar, options) { toolbar.find(toolbarBtnSelector).click(function () { - // restore caret position after certain commands - var restoreCaret = ['indent', 'outdent'].indexOf($(this).data(options.commandRole)) > -1 - restoreSelection(restoreCaret); + restoreSelection(); editor.focus(); restoreCommandCache(); execCommand($(this).data(options.commandRole)); From 5655bba20e2d83685bdf65f763757e9be69ae875 Mon Sep 17 00:00:00 2001 From: Jeff Szusz Date: Wed, 29 Oct 2014 10:12:49 -0400 Subject: [PATCH 12/13] 8036_Writing_Input_Performance_Issues - namespace the 'touchend' event bound to window so we can remove it - throttle 'updateToolbar' so it doesn't get called every keypress --- bootstrap-wysiwyg.js | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index 76c01c4..3de118f 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -67,7 +67,28 @@ } }, - updateToolbar = function () { + throttle = function(handler, threshold, scope) { + var last, deferTimer; + return function() { + var context = scope || this; + var now = new Date().getTime(); + var args = arguments; + + if (last && now < last + threshold) { + // wait for it... + clearTimeout(deferTimer); + deferTimer = setTimeout(function() { + last = now; + handler.apply(context, args); + }, threshold); + + } else { + last = now; + handler.apply(context, args); + } + }; + }, + updateToolbar = throttle(function () { if (options.activeToolbarClass) { $(options.toolbarSelector).find(toolbarBtnSelector).each(function () { var command = $(this).data(options.commandRole); @@ -78,9 +99,7 @@ } else { commandNoArgs = command; } - if (commandNoArgs === "fontSize" && options.setRealFontSize != null) { - options.setRealFontSize(selectedRange); - } else if (document.queryCommandState(command)) { + if (document.queryCommandState(command)) { $(this).addClass(options.activeToolbarClass); } else if (commandNoArgs + ' ' + document.queryCommandValue(commandNoArgs) === command) { $(this).addClass(options.activeToolbarClass); @@ -88,8 +107,11 @@ $(this).removeClass(options.activeToolbarClass); } }); + if (options.setRealFontSize !== null) { + options.setRealFontSize(selectedRange); + } } - }, + }, 500), execCommand = function (commandWithArgs, valueArg) { var commandArr = commandWithArgs.split(' '), command = commandArr.shift(), @@ -154,10 +176,10 @@ var eventNamespace = options.eventNamespace + '-' + 'bindKeys'; editor.on(namespaceEvents('focus'), function(e) { - setTimeout(function() { - restoreCommandCache(); - }, 0); - }); + setTimeout(function() { + restoreCommandCache(); + }, 0); + }); editor.on(namespaceEvents('keydown'), hotKeys, function (e) { var command = ''; @@ -302,7 +324,7 @@ commandCache[btnAttr.split(' ')[0]] = false; }); - $(window).bind('touchend', function (e) { + $(window).bind(namespaceEvents('touchend'), function (e) { var isInside = (editor.is(e.target) || editor.has(e.target).length > 0), currentRange = getCurrentRange(), clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset); @@ -311,7 +333,7 @@ updateToolbar(); } }); - return this; + return this; }; $.fn.wysiwyg.defaults = { hotKeys: { From 2e968dd7f5ed494fbfca53698eb2a54c3d16f780 Mon Sep 17 00:00:00 2001 From: Taylor Zajicek Date: Mon, 3 Nov 2014 16:26:56 -0600 Subject: [PATCH 13/13] Restore command state after justifying Could be caused by a Chrome 38 change --- bootstrap-wysiwyg.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bootstrap-wysiwyg.js b/bootstrap-wysiwyg.js index 3de118f..c554441 100644 --- a/bootstrap-wysiwyg.js +++ b/bootstrap-wysiwyg.js @@ -116,12 +116,16 @@ var commandArr = commandWithArgs.split(' '), command = commandArr.shift(), args = commandArr.join(' ') + (valueArg || ''); + if (command.indexOf('justify') === 0) { + ['left', 'center', 'right'].forEach(function(val) { commandCache['justify' + val] = false; }); + } document.execCommand(command, 0, args); if (args.length > 0) { commandCache[command] = document.queryCommandValue(command); } else { commandCache[command] = document.queryCommandState(command); } + restoreCommandCache(); updateToolbar(); }, mapHotKeyToCommand = function (event, hotKeys) {