Permalink
Browse files

BUGFIX Retain editor focus when insert images, update attributes on e…

…xisting image to avoid IE bugs through TinyMCE
  • Loading branch information...
1 parent 5f635d8 commit 8790ba3c4f058c915401e18637ba2eea29472c3b @chillu chillu committed Feb 22, 2012
Showing with 100 additions and 25 deletions.
  1. +100 −25 javascript/HtmlEditorField.js
View
125 javascript/HtmlEditorField.js
@@ -14,8 +14,6 @@
*/
ss.editorWrappers = {};
ss.editorWrappers.tinyMCE = (function() {
- var bookmark;
-
return {
/**
* @return Mixed Implementation specific object
@@ -27,13 +25,11 @@
* Invoked when a content-modifying UI is opened.
*/
onopen: function() {
- bookmark = this.getInstance().selection.getBookmark();
},
/**
* Invoked when a content-modifying UI is closed.
*/
onclose: function() {
- bookmark = null;
},
/**
* Write the HTML back to the original text area field.
@@ -104,22 +100,28 @@
this.getInstance().selection.select(node);
},
/**
+ * Insert content at the current caret position
+ *
* @param String HTML
*/
- insertContent: function(html) {
- // Workaround for IE losing focus
- this.getInstance().selection.moveToBookmark(bookmark);
- this.getInstance().execCommand('mceInsertContent', false, html);
+ insertContent: function(html, opts) {
+ this.getInstance().execCommand('mceInsertContent', false, html, opts);
+ },
+ /**
+ * Replace currently selected content
+ *
+ * @param {String} html
+ */
+ replaceContent: function(html, opts) {
+ this.getInstance().execCommand('mceReplaceContent', false, html, opts);
},
/**
* Insert or update a link in the content area (based on current editor selection)
*
* Parameters: {Object} attrs
*/
- insertLink: function(attrs) {
- // Workaround for IE losing focus
- this.getInstance().selection.moveToBookmark(bookmark);
- this.getInstance().execCommand("mceInsertLink", false, attrs);
+ insertLink: function(attrs, opts) {
+ this.getInstance().execCommand("mceInsertLink", false, attrs, opts);
},
/**
* Remove the link from the currently selected node (if any).
@@ -147,6 +149,27 @@
if(href.match(/^javascript:\s*mctmp/)) href = '';
return href;
+ },
+ /**
+ * Creates a bookmark for the currently selected range,
+ * which can be used to reselect this range at a later point.
+ * @return {mixed}
+ */
+ createBookmark: function() {
+ return this.getInstance().selection.getBookmark();
+ },
+ /**
+ * Selects a bookmarked range previously saved through createBookmark().
+ * @param {mixed} bookmark
+ */
+ moveToBookmark: function(bookmark) {
+ this.getInstance().selection.moveToBookmark(bookmark);
+ },
+ /**
+ * Add new undo point with the current DOM content.
+ */
+ addUndo: function() {
+ this.getInstance().undoManager.add();
}
};
});
@@ -220,7 +243,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
isChanged: function() {
var ed = this.getEditor();
- return (ed && ed.isDirty());
+ return (ed && ed.getInstance() && ed.isDirty());
},
resetChanged: function() {
@@ -250,6 +273,9 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
// Wrapper for various HTML editors
Editor: null,
+ // TODO Figure out how to keep bookmark reference in entwine, and still be allowed to delete the JS object
+ // Bookmark: null,
+
onmatch: function() {
// Move title from headline to (jQuery compatible) title attribute
var titleEl = this.find(':header:first');
@@ -270,12 +296,14 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
close: function() {
this.dialog('close');
this.getEditor().onclose();
+ if(typeof window._ss_htmleditorfield_bookmark != 'undefined') window._ss_htmleditorfield_bookmark = null;
},
open: function() {
this.updateFromEditor();
this.dialog('open');
this.redraw();
this.getEditor().onopen();
+ window._ss_htmleditorfield_bookmark = this.getEditor().createBookmark();
},
/**
* Update the view state based on the current editor selection.
@@ -374,6 +402,10 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
title : this.find(':input[name=Description]').val()
};
+ // Workaround for browsers losing focus, similar to tinyMCEPopup.restoreSelection
+ ed.moveToBookmark(window._ss_htmleditorfield_bookmark);
+ window._ss_htmleditorfield_bookmark = null;
+
// Add the new link
ed.insertLink(attributes);
this.trigger('onafterinsert', attributes);
@@ -567,11 +599,11 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
var self = this, ed = this.getEditor();
// HACK: See ondialogopen()
- // if($.browser.msie) jQuery(ed.getContainer()).show();
+ // jQuery(ed.getContainer()).show();
this.find('.ss-htmleditorfield-file').each(function(el) {
- ed.insertContent($(this).getHTML());
+ $(this).insertHTML();
});
ed.repaint();
this.close();
@@ -594,13 +626,13 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
// HACK: Hide selected node in IE because its drag handles on potentially selected elements
// don't respect the z-index of the dialog overlay.
- // if($.browser.msie) jQuery(ed.getContainer()).hide();
+ // jQuery(ed.getContainer()).hide();
},
ondialogclose: function() {
var ed = this.getEditor(), node = $(ed.getSelectedNode());
// HACK: See ondialogopen()
- // if($.browser.msie) jQuery(ed.getContainer()).show();
+ // jQuery(ed.getContainer()).show();
this.find('.ss-htmleditorfield-file').remove(); // Remove any existing views
this.find('.ss-gridfield-items .ui-selected').removeClass('ui-selected'); // Unselect all items
@@ -686,6 +718,19 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
getHTML: function() {
},
/**
+ * Insert updated HTML content into the rich text editor
+ */
+ insertHTML: function() {
+ var form = this.closest('form'), ed = form.getEditor();
+
+ // Workaround for browsers losing focus, similar to tinyMCEPopup.restoreSelection
+ ed.moveToBookmark(window._ss_htmleditorfield_bookmark);
+ window._ss_htmleditorfield_bookmark = null;
+
+ // Insert content
+ ed.replaceContent(this.getHTML());
+ },
+ /**
* Updates the form values from an existing node in the editor.
*
* @param {DOMElement}
@@ -737,8 +782,8 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
return {
'src' : this.find(':input[name=URL]').val(),
'alt' : this.find(':input[name=AltText]').val(),
- 'width' : width ? parseInt(width, 10) : null,
- 'height' : height ? parseInt(height, 10) : null,
+ 'width' : width ? parseInt(width, 10) + "px" : null,
+ 'height' : height ? parseInt(height, 10) + "px" : null,
'title' : this.find(':input[name=Title]').val(),
'class' : this.find(':input[name=CSSClass]').val()
};
@@ -752,7 +797,8 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
var el,
attrs = this.getAttributes(),
extraData = this.getExtraData(),
- imgEl = $('<img id="__mce_tmp" />').attr(attrs);
+ // imgEl = $('<img id="_ss_tmp_img" />');
+ imgEl = $('<img />').attr(attrs);
if(extraData.CaptionText) {
el = $('<div style="width: ' + attrs['width'] + 'px;" class="captionImage ' + attrs['class'] + '"><p class="caption">' + extraData.CaptionText + '</p></div>').prepend(imgEl);
@@ -761,6 +807,40 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
}
return $('<div />').append(el).html(); // Little hack to get outerHTML string
},
+ /**
+ * Logic similar to TinyMCE 'advimage' plugin, insertAndClose() method.
+ */
+ insertHTML: function() {
+ var form = this.closest('form'), ed = form.getEditor(),
+ node = $(ed.getSelectedNode()), captionNode = node.closest('.captionImage');
+
+ // Workaround for browsers losing focus, similar to tinyMCEPopup.restoreSelection.
+ // TODO In TinyMCE core this is restricted to IE, but leaving it our also
+ // breaks Firefox: It doesn't save the selection because it inserts into a temporary TinyMCE
+ // marker element rather than the content DOM nodes
+ ed.moveToBookmark(window._ss_htmleditorfield_bookmark);
+ window._ss_htmleditorfield_bookmark = null;
+
+ if(node && node.is('img')) {
+ // If the image exists, update it to avoid complications with inserting TinyMCE HTML content
+ var attrs = this.getAttributes(), extraData = this.getExtraData();
+ node.attr(attrs);
+ // TODO Doesn't allow adding a caption to image after it was first added
+ if(captionNode.length) {
+ captionNode.find('.caption').text(extraData.CaptionText);
+ captionNode.css({width: attrs.width, height: attrs.height}).attr('class', attrs['class']);
+ }
+ // Undo needs to be added manually as we're doing direct DOM changes
+ ed.addUndo();
+ } else {
+ // Otherwise insert the whole HTML content
+ ed.repaint();
+ ed.insertContent(this.getHTML(), {skip_undo : 1});
+ ed.addUndo(); // Not sure why undo is separate here, replicating TinyMCE logic
+ }
+
+ ed.repaint();
+ },
updateFromNode: function(node) {
this.find(':input[name=AltText]').val(node.attr('alt'));
this.find(':input[name=Title]').val(node.attr('title'));
@@ -786,11 +866,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
'height' : height ? parseInt(height, 10) : null
};
},
- getExtraData: function() {
- return {
- 'CaptionText': this.find(':input[name=CaptionText]').val()
- };
- },
getHTML: function() {
var attrs = this.getAttributes();

0 comments on commit 8790ba3

Please sign in to comment.