mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
BUGFIX: Fix trac ticket 7081
IE contentEditable carets and selection boxes show on top of any elements, interfering with HtmlEditorFields dialogs. This fixes that by storing the selection prior to opening the dialog, then unselecting everything. On dialog close the selection is restored
This commit is contained in:
parent
dd99fbc69e
commit
ef11a0dc78
@ -171,6 +171,13 @@
|
|||||||
*/
|
*/
|
||||||
moveToBookmark: function(bookmark) {
|
moveToBookmark: function(bookmark) {
|
||||||
this.getInstance().selection.moveToBookmark(bookmark);
|
this.getInstance().selection.moveToBookmark(bookmark);
|
||||||
|
this.getInstance().focus();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Removes any selection & de-focuses this editor
|
||||||
|
*/
|
||||||
|
blur: function() {
|
||||||
|
this.getInstance().selection.collapse();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Add new undo point with the current DOM content.
|
* Add new undo point with the current DOM content.
|
||||||
@ -318,6 +325,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
url: url,
|
url: url,
|
||||||
success: function(html) {
|
success: function(html) {
|
||||||
dialog.html(html);
|
dialog.html(html);
|
||||||
|
dialog.trigger('dialogopen');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -325,19 +333,17 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('.htmleditorfield-dialog').entwine({
|
$('.htmleditorfield-dialog').entwine({
|
||||||
onmatch: function() {
|
onadd: function() {
|
||||||
// Create jQuery dialog
|
// Create jQuery dialog
|
||||||
|
|
||||||
var height = $(window).height() * 0.8;
|
var height = $(window).height() * 0.8;
|
||||||
var width = $(window).width() * 0.8;
|
var width = $(window).width() * 0.8;
|
||||||
|
|
||||||
this.dialog({autoOpen: true, bgiframe: true, modal: true, height: height, width: width, ghost: true});
|
if (!this.is('.ui-dialog-content')) this.dialog({autoOpen: true, bgiframe: true, modal: true, height: height, width: width, ghost: true});
|
||||||
|
|
||||||
this._super();
|
this._super();
|
||||||
},
|
},
|
||||||
onunmatch: function() {
|
|
||||||
this._super();
|
|
||||||
},
|
|
||||||
getForm: function() {
|
getForm: function() {
|
||||||
return this.find('form');
|
return this.find('form');
|
||||||
},
|
},
|
||||||
@ -350,13 +356,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
toggle: function(bool) {
|
toggle: function(bool) {
|
||||||
if(this.is(':visible')) this.close();
|
if(this.is(':visible')) this.close();
|
||||||
else this.open();
|
else this.open();
|
||||||
},
|
|
||||||
ondialogopen: function(e) {
|
|
||||||
this.getForm().updateFromEditor();
|
|
||||||
this.getForm().redraw();
|
|
||||||
},
|
|
||||||
ondialogclose: function(e) {
|
|
||||||
this.getForm().resetFields();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -365,44 +364,69 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
* mostly geared towards modification and insertion of content.
|
* mostly geared towards modification and insertion of content.
|
||||||
*/
|
*/
|
||||||
$('form.htmleditorfield-form').entwine({
|
$('form.htmleditorfield-form').entwine({
|
||||||
|
Selection: null,
|
||||||
|
Bookmark: null,
|
||||||
|
|
||||||
|
setSelection: function(node) {
|
||||||
|
return this._super($(node));
|
||||||
|
},
|
||||||
|
|
||||||
// Wrapper for various HTML editors
|
// Wrapper for various HTML editors
|
||||||
Editor: null,
|
Editor: null,
|
||||||
|
|
||||||
// TODO Figure out how to keep bookmark reference in entwine, and still be allowed to delete the JS object
|
onadd: function() {
|
||||||
// Bookmark: null,
|
|
||||||
onmatch: function() {
|
|
||||||
// Move title from headline to (jQuery compatible) title attribute
|
// Move title from headline to (jQuery compatible) title attribute
|
||||||
var titleEl = this.find(':header:first');
|
var titleEl = this.find(':header:first');
|
||||||
this.getDialog().attr('title', titleEl.text());
|
this.getDialog().attr('title', titleEl.text());
|
||||||
|
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
this.redraw();
|
|
||||||
},
|
},
|
||||||
onunmatch: function() {
|
onremove: function() {
|
||||||
|
this.setSelection(null);
|
||||||
|
this.setBookmark(null);
|
||||||
|
this.setEditor(null);
|
||||||
|
|
||||||
this._super();
|
this._super();
|
||||||
},
|
},
|
||||||
redraw: function() {
|
|
||||||
},
|
|
||||||
resetFields: function() {
|
|
||||||
if(typeof window._ss_htmleditorfield_bookmark != 'undefined') window._ss_htmleditorfield_bookmark = null;
|
|
||||||
this.getEditor().onclose();
|
|
||||||
},
|
|
||||||
getDialog: function() {
|
getDialog: function() {
|
||||||
// TODO Refactor to listen to form events to remove two-way coupling
|
// TODO Refactor to listen to form events to remove two-way coupling
|
||||||
return this.closest('.htmleditorfield-dialog');
|
return this.closest('.htmleditorfield-dialog');
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* Update the view state based on the current editor selection.
|
fromDialog: {
|
||||||
*/
|
ondialogopen: function(){
|
||||||
updateFromEditor: function() {
|
var ed = this.getEditor();
|
||||||
this.getEditor().onopen();
|
ed.onopen();
|
||||||
window._ss_htmleditorfield_bookmark = this.getEditor().createBookmark();
|
|
||||||
|
this.setSelection(ed.getSelectedNode());
|
||||||
|
this.setBookmark(ed.createBookmark());
|
||||||
|
|
||||||
|
ed.blur();
|
||||||
|
|
||||||
|
this.find(':input:not(:submit)[data-skip-autofocus!="true"]').filter(':visible:enabled').eq(0).focus();
|
||||||
|
|
||||||
|
this.updateFromEditor();
|
||||||
|
this.redraw();
|
||||||
|
},
|
||||||
|
|
||||||
|
ondialogclose: function(){
|
||||||
|
var ed = this.getEditor();
|
||||||
|
ed.onclose();
|
||||||
|
|
||||||
|
ed.moveToBookmark(this.getBookmark());
|
||||||
|
|
||||||
|
this.setSelection(null);
|
||||||
|
this.setBookmark(null);
|
||||||
|
|
||||||
|
this.resetFields();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createEditor: function(){
|
createEditor: function(){
|
||||||
return ss.editorWrappers['default']();
|
return ss.editorWrappers['default']();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the tinyMCE editor
|
* Get the tinyMCE editor
|
||||||
*/
|
*/
|
||||||
@ -412,6 +436,28 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
this.setEditor(val = this.createEditor());
|
this.setEditor(val = this.createEditor());
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
|
},
|
||||||
|
|
||||||
|
modifySelection: function(callback) {
|
||||||
|
var ed = this.getEditor();
|
||||||
|
|
||||||
|
ed.moveToBookmark(this.getBookmark());
|
||||||
|
callback.call(this, ed);
|
||||||
|
|
||||||
|
this.setSelection(ed.getSelectedNode());
|
||||||
|
this.setBookmark(ed.createBookmark());
|
||||||
|
|
||||||
|
ed.blur();
|
||||||
|
},
|
||||||
|
|
||||||
|
updateFromEditor: function() {
|
||||||
|
/* NOP */
|
||||||
|
},
|
||||||
|
redraw: function() {
|
||||||
|
/* NOP */
|
||||||
|
},
|
||||||
|
resetFields: function() {
|
||||||
|
/* NOP */
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -463,7 +509,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
insertLink: function() {
|
insertLink: function() {
|
||||||
var href, target = null, anchor = this.find(':input[name=Anchor]').val(), ed = this.getEditor();
|
var href, target = null, anchor = this.find(':input[name=Anchor]').val();
|
||||||
|
|
||||||
// Determine target
|
// Determine target
|
||||||
if(this.find(':input[name=TargetBlank]').is(':checked')) target = '_blank';
|
if(this.find(':input[name=TargetBlank]').is(':checked')) target = '_blank';
|
||||||
@ -503,17 +549,16 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
title : this.find(':input[name=Description]').val()
|
title : this.find(':input[name=Description]').val()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Workaround for browsers losing focus, similar to tinyMCEPopup.restoreSelection
|
this.modifySelection(function(ed){
|
||||||
ed.moveToBookmark(window._ss_htmleditorfield_bookmark);
|
ed.insertLink(attributes);
|
||||||
window._ss_htmleditorfield_bookmark = null;
|
})
|
||||||
|
|
||||||
// Add the new link
|
|
||||||
ed.insertLink(attributes);
|
|
||||||
this.trigger('onafterinsert', attributes);
|
|
||||||
this.updateFromEditor();
|
this.updateFromEditor();
|
||||||
},
|
},
|
||||||
removeLink: function() {
|
removeLink: function() {
|
||||||
this.getEditor().removeLink();
|
this.modifySelection(function(ed){
|
||||||
|
ed.removeLink();
|
||||||
|
})
|
||||||
this.close();
|
this.close();
|
||||||
},
|
},
|
||||||
addAnchorSelector: function() {
|
addAnchorSelector: function() {
|
||||||
@ -590,7 +635,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
* form.
|
* form.
|
||||||
*/
|
*/
|
||||||
getCurrentLink: function() {
|
getCurrentLink: function() {
|
||||||
var ed = this.getEditor(), selectedEl = $(ed.getSelectedNode()),
|
var selectedEl = this.getSelection(),
|
||||||
href = "", target = "", title = "", action = "insert", style_class = "";
|
href = "", target = "", title = "", action = "insert", style_class = "";
|
||||||
|
|
||||||
// We use a separate field for linkDataSource from tinyMCE.linkElement.
|
// We use a separate field for linkDataSource from tinyMCE.linkElement.
|
||||||
@ -611,7 +656,9 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
linkDataSource = selectedEl = selectedEl.parents('a:first');
|
linkDataSource = selectedEl = selectedEl.parents('a:first');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(linkDataSource && linkDataSource.length) ed.selectNode(linkDataSource[0]);
|
if(linkDataSource && linkDataSource.length) this.modifySelection(function(ed){
|
||||||
|
ed.selectNode(linkDataSource[0]);
|
||||||
|
});
|
||||||
|
|
||||||
// Is anchor not a link
|
// Is anchor not a link
|
||||||
if (!linkDataSource.attr('href')) linkDataSource = null;
|
if (!linkDataSource.attr('href')) linkDataSource = null;
|
||||||
@ -621,7 +668,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
target = linkDataSource.attr('target');
|
target = linkDataSource.attr('target');
|
||||||
title = linkDataSource.attr('title');
|
title = linkDataSource.attr('title');
|
||||||
style_class = linkDataSource.attr('class');
|
style_class = linkDataSource.attr('class');
|
||||||
href = ed.cleanLink(href, linkDataSource);
|
href = this.getEditor().cleanLink(href, linkDataSource);
|
||||||
action = "update";
|
action = "update";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,21 +746,20 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
this.find('.overview .action-delete')[updateExisting ? 'hide' : 'show']();
|
this.find('.overview .action-delete')[updateExisting ? 'hide' : 'show']();
|
||||||
},
|
},
|
||||||
onsubmit: function() {
|
onsubmit: function() {
|
||||||
var self = this, ed = this.getEditor();
|
this.modifySelection(function(ed){
|
||||||
|
this.find('.ss-htmleditorfield-file').each(function() {
|
||||||
|
$(this).insertHTML(ed);
|
||||||
|
});
|
||||||
|
|
||||||
|
ed.repaint();
|
||||||
|
})
|
||||||
|
|
||||||
// HACK: See ondialogopen()
|
|
||||||
// jQuery(ed.getContainer()).show();
|
|
||||||
|
|
||||||
this.find('.ss-htmleditorfield-file').each(function() {
|
|
||||||
$(this).insertHTML();
|
|
||||||
});
|
|
||||||
ed.repaint();
|
|
||||||
this.getDialog().close();
|
this.getDialog().close();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
updateFromEditor: function() {
|
updateFromEditor: function() {
|
||||||
var self = this, ed = this.getEditor(), node = $(ed.getSelectedNode());
|
var self = this, node = this.getSelection();
|
||||||
|
|
||||||
// TODO Depends on managed mime type
|
// TODO Depends on managed mime type
|
||||||
if(node.is('img')) {
|
if(node.is('img')) {
|
||||||
this.showFileView(node.data('url') || node.attr('src'), function() {
|
this.showFileView(node.data('url') || node.attr('src'), function() {
|
||||||
@ -727,7 +773,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
redraw: function() {
|
redraw: function() {
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
var ed = this.getEditor(), node = $(ed.getSelectedNode()),
|
var node = this.getSelection(),
|
||||||
hasItems = Boolean(this.find('.ss-htmleditorfield-file').length),
|
hasItems = Boolean(this.find('.ss-htmleditorfield-file').length),
|
||||||
editingSelected = node.is('img'),
|
editingSelected = node.is('img'),
|
||||||
header = this.find('.header-edit');
|
header = this.find('.header-edit');
|
||||||
@ -750,11 +796,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
this.find('.Actions .media-update')[updateExisting ? 'show' : 'hide']();
|
this.find('.Actions .media-update')[updateExisting ? 'show' : 'hide']();
|
||||||
},
|
},
|
||||||
resetFields: function() {
|
resetFields: function() {
|
||||||
var ed = this.getEditor(), node = $(ed.getSelectedNode());
|
|
||||||
|
|
||||||
// HACK: See ondialogopen()
|
|
||||||
// jQuery(ed.getContainer()).show();
|
|
||||||
|
|
||||||
this.find('.ss-htmleditorfield-file').remove(); // Remove any existing views
|
this.find('.ss-htmleditorfield-file').remove(); // Remove any existing views
|
||||||
this.find('.ss-gridfield-items .ui-selected').removeClass('ui-selected'); // Unselect all items
|
this.find('.ss-gridfield-items .ui-selected').removeClass('ui-selected'); // Unselect all items
|
||||||
this.redraw();
|
this.redraw();
|
||||||
@ -869,13 +910,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
/**
|
/**
|
||||||
* Insert updated HTML content into the rich text editor
|
* Insert updated HTML content into the rich text editor
|
||||||
*/
|
*/
|
||||||
insertHTML: function() {
|
insertHTML: function(ed) {
|
||||||
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
|
// Insert content
|
||||||
ed.replaceContent(this.getHTML());
|
ed.replaceContent(this.getHTML());
|
||||||
},
|
},
|
||||||
@ -959,16 +994,9 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
/**
|
/**
|
||||||
* Logic similar to TinyMCE 'advimage' plugin, insertAndClose() method.
|
* Logic similar to TinyMCE 'advimage' plugin, insertAndClose() method.
|
||||||
*/
|
*/
|
||||||
insertHTML: function() {
|
insertHTML: function(ed) {
|
||||||
var form = this.closest('form'), ed = form.getEditor(),
|
var form = this.closest('form'),
|
||||||
node = $(ed.getSelectedNode()), captionNode = node.closest('.captionImage');
|
node = form.getSelection(), 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(node && node.is('img')) {
|
||||||
// If the image exists, update it to avoid complications with inserting TinyMCE HTML content
|
// If the image exists, update it to avoid complications with inserting TinyMCE HTML content
|
||||||
|
Loading…
Reference in New Issue
Block a user