Skip to content
Closed
Show file tree
Hide file tree
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
6 changes: 6 additions & 0 deletions src/BootstrapRTE/BootstrapRTE.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@
<caption>(re)do</caption>
<category>Toolbar</category>
<description/>
</property>

<property key="toolbarButtonHtml" type="boolean" required="true" defaultValue="true">
<caption>HTML</caption>
<category>Toolbar</category>
<description/>
</property>
</properties>
</widget>
208 changes: 170 additions & 38 deletions src/BootstrapRTE/lib/bootstrap-wysiwyg.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,40 @@
/* http://github.com/mindmup/bootstrap-wysiwyg */
/*global jQuery, $, FileReader*/
/*jslint browser:true*/
/* @fileoverview
* Provides full Bootstrap based, multi-instance WYSIWYG editor.
*
* "Name" = 'bootstrap-wysiwyg'
* "Author" = 'Various, see LICENCE'
* "Version" = '1.0.3-beta'
* "About" = 'A tiny Bootstrap and jQuery based WYSIWYG rich text editor based on the browser function execCommand.'
*/
(function ($) {
'use strict';
/** underscoreThrottle()
* From underscore http://underscorejs.org/docs/underscore.html
*/
var underscoreThrottle = function(func, wait) {
var context, args, timeout, result;
var previous = 0;
var later = function() {
previous = new Date();
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date();
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
var readFileIntoDataUrl = function (fileInfo) {
var loader = $.Deferred(),
fReader = new FileReader();
Expand All @@ -14,32 +46,71 @@
fReader.readAsDataURL(fileInfo);
return loader.promise();
};
$.fn.cleanHtml = function () {
$.fn.cleanHtml = function (o) {
if ( $(this).data("wysiwyg-html-mode") === true ) {
$(this).html($(this).text());
$(this).attr('contenteditable',true);
$(this).data('wysiwyg-html-mode',false);
}

// Strip the images with src="data:image/.." out;
if ( o === true && $(this).parent().is("form") ) {
var gGal = $(this).html;
if ( $(gGal).has( "img" ).length ) {
var gImages = $( "img", $(gGal));
var gResults = [];
var gEditor = $(this).parent();
$.each(gImages, function(i,v) {
if ( $(v).attr('src').match(/^data:image\/.*$/) ) {
gResults.push(gImages[i]);
$(gEditor).prepend("<input value='"+$(v).attr('src')+"' type='hidden' name='postedimage/"+i+"' />");
$(v).attr('src', 'postedimage/'+i);
}});
}
}
var html = $(this).html();
return html && html.replace(/(<br>|\s|<div><br><\/div>|&nbsp;)*$/, '');
};
$.fn.wysiwyg = function (userOptions) {
var editor = this,
wrapper = $(editor).parent(),
selectedRange,
options,
toolbarBtnSelector,
updateToolbar = function () {
if (options.activeToolbarClass) {
$(options.toolbarSelector).find(toolbarBtnSelector).each(function () {
var command = $(this).data(options.commandRole);
if (document.queryCommandState(command)) {
$(options.toolbarSelector,wrapper).find(toolbarBtnSelector).each(underscoreThrottle(function () {
var commandArr = $(this).data(options.commandRole).split(' '),
command = commandArr[0];

// If the command has an argument and its value matches this button. == used for string/number comparison
if (commandArr.length > 1 && document.queryCommandEnabled(command) && document.queryCommandValue(command) === commandArr[1]) {
$(this).addClass(options.activeToolbarClass);
// Else if the command has no arguments and it is active
} else if (commandArr.length === 1 && document.queryCommandEnabled(command) && document.queryCommandState(command)) {
$(this).addClass(options.activeToolbarClass);
// Else the command is not active
} else {
$(this).removeClass(options.activeToolbarClass);
}
});
}, options.keypressTimeout));
}
},
execCommand = function (commandWithArgs, valueArg) {
var commandArr = commandWithArgs.split(' '),
command = commandArr.shift(),
args = commandArr.join(' ') + (valueArg || '');
document.execCommand(command, 0, args);

var parts = commandWithArgs.split('-');

if ( parts.length === 1 ) {
document.execCommand(command, 0, args);
}
else if ( parts[0] === 'format' && parts.length === 2 ) {
document.execCommand('formatBlock', false, parts[1] );
}

editor.trigger('change');
updateToolbar();
},
bindHotkeys = function (hotKeys) {
Expand All @@ -57,35 +128,70 @@
}
});
});

editor.keyup(function(){ editor.trigger('change'); });
},
getCurrentRange = function () {
var sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
}
} else if (document.selection) {
range = document.selection.createRange();
} return range;
},
saveSelection = function () {
selectedRange = getCurrentRange();
},
restoreSelection = function () {
var selection = window.getSelection();
if (selectedRange) {
try {
selection.removeAllRanges();
} catch (ex) {
document.body.createTextRange().select();
document.selection.empty();
}

selection.addRange(selectedRange);
}
var selection;
if (window.getSelection || document.createRange) {
selection = window.getSelection();
if (selectedRange) {
try {
selection.removeAllRanges();
} catch (ex) {
document.body.createTextRange().select();
document.selection.empty();
}
selection.addRange(selectedRange);
}
}
else if (document.selection && selectedRange) {
selectedRange.select();
}
},

// Adding Toggle HTML based on the work by @jd0000, but cleaned up a little to work in this context.
toggleHtmlEdit = function() {
if ( $(editor).data("wysiwyg-html-mode") !== true ) {
var oContent = $(editor).html();
var editorPre = $( "<pre />" );
$(editorPre).append( document.createTextNode( oContent ) );
$(editorPre).attr('contenteditable',true);
$(editor).html(' ');
$(editor).append($(editorPre));
$(editor).attr('contenteditable', false);
$(editor).data("wysiwyg-html-mode", true);
$(editorPre).focus();
}
else {
$(editor).html($(editor).text());
$(editor).attr('contenteditable',true);
$(editor).data('wysiwyg-html-mode',false);
$(editor).focus();
}
},

insertFiles = function (files) {
editor.focus();
$.each(files, function (idx, fileInfo) {
if (/^image\//.test(fileInfo.type)) {
$.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
execCommand('insertimage', dataUrl);
editor.trigger('image-inserted');
}).fail(function (e) {
options.fileUploadError("file-reader", e);
});
Expand All @@ -103,10 +209,16 @@
input.data(options.selectionMarker, color);
},
bindToolbar = function (toolbar, options) {
toolbar.find(toolbarBtnSelector).click(function () {
toolbar.find(toolbarBtnSelector, wrapper).click(function () {
restoreSelection();
editor.focus();
execCommand($(this).data(options.commandRole));

if ($(this).data(options.commandRole) === 'html') {
toggleHtmlEdit();
}
else {
execCommand($(this).data(options.commandRole));
}
saveSelection();
});
toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
Expand Down Expand Up @@ -152,9 +264,28 @@
}
});
};
options = $.extend({}, $.fn.wysiwyg.defaults, userOptions);
options = $.extend(true, {}, $.fn.wysiwyg.defaults, userOptions);
toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']';
bindHotkeys(options.hotKeys);

// Support placeholder attribute on the DIV
if ($(this).attr('placeholder') !== '') {
$(this).addClass('placeholderText');
$(this).html($(this).attr('placeholder'));
$(this).bind('focus',function() {
if ( $(this).attr('placeholder') !== '' && $(this).text() === $(this).attr('placeholder') ) {
$(this).removeClass('placeholderText');
$(this).html('');
}
});
$(this).bind('blur',function() {
if ( $(this).attr('placeholder') !== '' && $(this).text() === '' ) {
$(this).addClass('placeholderText');
$(this).html($(this).attr('placeholder'));
}
});
}

if (options.dragAndDropImages) {
initFileDrops();
}
Expand All @@ -177,16 +308,16 @@
};
$.fn.wysiwyg.defaults = {
hotKeys: {
'ctrl+b meta+b': 'bold',
'ctrl+i meta+i': 'italic',
'ctrl+u meta+u': 'underline',
'ctrl+z meta+z': 'undo',
'ctrl+y meta+y meta+shift+z': 'redo',
'ctrl+l meta+l': 'justifyleft',
'ctrl+r meta+r': 'justifyright',
'ctrl+e meta+e': 'justifycenter',
'ctrl+j meta+j': 'justifyfull',
'shift+tab': 'outdent',
'Ctrl+b meta+b': 'bold',
'Ctrl+i meta+i': 'italic',
'Ctrl+u meta+u': 'underline',
'Ctrl+z': 'undo',
'Ctrl+y meta+y meta+shift+z': 'redo',
'Ctrl+l meta+l': 'justifyleft',
'Ctrl+r meta+r': 'justifyright',
'Ctrl+e meta+e': 'justifycenter',
'Ctrl+j meta+j': 'justifyfull',
'Shift+tab': 'outdent',
'tab': 'indent'
},
toolbarSelector: '[data-role=editor-toolbar]',
Expand All @@ -195,6 +326,7 @@
selectionMarker: 'edit-focus-marker',
selectionColor: 'darkgrey',
dragAndDropImages: true,
keypressTimeout: 200,
fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
};
}($));
}(window.jQuery));
8 changes: 7 additions & 1 deletion src/BootstrapRTE/widget/BootstrapRTE.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ require({
toolbarButtonLink: true,
toolbarButtonPicture: true,
toolbarButtonDoRedo: true,
toolbarButtonHtml: true,

// Internal variables. Non-primitives created in the prototype are shared between all widget instances.
_handles: null,
Expand Down Expand Up @@ -287,11 +288,16 @@ require({
domConstruct.place(domConstruct.toDom(dojo.cache('BootstrapRTE.widget', 'template/BootstrapRTE_toolbar_list.html')), this._toolbarNode);
} else if (this.toolbarButtonDent) {
domConstruct.place(domConstruct.toDom(dojo.cache('BootstrapRTE.widget', 'template/BootstrapRTE_toolbar_dent.html')), this._toolbarNode);
}
}


if (this.toolbarButtonJustify) {
domConstruct.place(domConstruct.toDom(dojo.cache('BootstrapRTE.widget', 'template/BootstrapRTE_toolbar_justify.html')), this._toolbarNode);
}

if (this.toolbarButtonHtml) {
domConstruct.place(domConstruct.toDom(dojo.cache('BootstrapRTE.widget', 'template/BootstrapRTE_toolbar_html.html')), this._toolbarNode);
}

if (this.toolbarButtonLink) {
var template = domConstruct.toDom(dojo.cache('BootstrapRTE.widget', 'template/BootstrapRTE_toolbar_url.html')),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="btn-group">
<a class="btn btn-default" data-edit="html" title="Clear Formatting"><i class='glyphicon glyphicon-pencil'></i></a>
</div>
Binary file modified test/BootstrapRTETest.mpr
Binary file not shown.
Binary file modified test/widgets/BootstrapRTE.mpk
Binary file not shown.