Skip to content
This repository has been archived by the owner on Jul 24, 2019. It is now read-only.

Bold / Italic / Strike / Underline buttons don't work correctly #74

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 152 additions & 29 deletions bootstrap-wysiwyg.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,58 @@
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);
}
}
},
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 () {
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);
}
Expand All @@ -40,22 +86,89 @@
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();
},
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 () {
Expand Down Expand Up @@ -104,14 +217,16 @@
},
bindToolbar = function (toolbar, options) {
toolbar.find(toolbarBtnSelector).click(function () {
updateCommandCache();
restoreSelection();
editor.focus();
restoreCommandCache();
execCommand($(this).data(options.commandRole));
saveSelection();
});
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();
Expand All @@ -120,30 +235,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();
Expand All @@ -160,10 +275,16 @@
}
bindToolbar($(options.toolbarSelector), options);
editor.attr('contenteditable', true)
.on('mouseup keyup mouseout', function () {
.on(namespaceEvents('mouseup keyup mouseout'), function () {
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(),
Expand Down Expand Up @@ -195,6 +316,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));