Skip to content

Commit

Permalink
Merge pull request #252 from soxhub/iframe-support
Browse files Browse the repository at this point in the history
better support for contenteditables in iframes (ckeditor, tinymce, etc)
  • Loading branch information
Yuku TAKAHASHI committed May 27, 2016
2 parents 7846dc5 + d2997ae commit d8b97dc
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 9 deletions.
17 changes: 16 additions & 1 deletion src/completer.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
throw new Error('textcomplete must be called on a Textarea or a ContentEditable.');
}

if (element === document.activeElement) {
// use ownerDocument to fix iframe / IE issues
if (element === element.ownerDocument.activeElement) {
// element has already been focused. Initialize view objects immediately.
this.initialize()
} else {
Expand Down Expand Up @@ -109,12 +110,26 @@
adapter: null,
dropdown: null,
$el: null,
$iframe: null,

// Public methods
// --------------

initialize: function () {
var element = this.$el.get(0);

// check if we are in an iframe
// we need to alter positioning logic if using an iframe
if (this.$el.prop('ownerDocument') !== document && window.frames.length) {
for (var iframeIndex = 0; iframeIndex < window.frames.length; iframeIndex++) {
if (this.$el.prop('ownerDocument') === window.frames[iframeIndex].document) {
this.$iframe = $(window.frames[iframeIndex].frameElement);
break;
}
}
}


// Initialize view objects.
this.dropdown = new $.fn.textcomplete.Dropdown(element, this, this.option);
var Adapter, viewName;
Expand Down
27 changes: 20 additions & 7 deletions src/content_editable.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
// When an dropdown item is selected, it is executed.
select: function (value, strategy, e) {
var pre = this.getTextFromHeadToCaret();
var sel = window.getSelection()
// use ownerDocument instead of window to support iframes
var sel = this.el.ownerDocument.getSelection();

var range = sel.getRangeAt(0);
var selection = range.cloneRange();
selection.selectNodeContents(range.startContainer);
Expand All @@ -38,13 +40,13 @@
range.deleteContents();

// create temporary elements
var preWrapper = document.createElement("div");
var preWrapper = this.el.ownerDocument.createElement("div");
preWrapper.innerHTML = pre;
var postWrapper = document.createElement("div");
var postWrapper = this.el.ownerDocument.createElement("div");
postWrapper.innerHTML = post;

// create the fragment thats inserted
var fragment = document.createDocumentFragment();
var fragment = this.el.ownerDocument.createDocumentFragment();
var childNode;
var lastOfPre;
while (childNode = preWrapper.firstChild) {
Expand Down Expand Up @@ -77,8 +79,8 @@
//
// Dropdown's position will be decided using the result.
_getCaretRelativePosition: function () {
var range = window.getSelection().getRangeAt(0).cloneRange();
var node = document.createElement('span');
var range = this.el.ownerDocument.getSelection().getRangeAt(0).cloneRange();
var node = this.el.ownerDocument.createElement('span');
range.insertNode(node);
range.selectNodeContents(node);
range.deleteContents();
Expand All @@ -87,6 +89,17 @@
position.left -= this.$el.offset().left;
position.top += $node.height() - this.$el.offset().top;
position.lineHeight = $node.height();

// special positioning logic for iframes
// this is typically used for contenteditables such as tinymce or ckeditor
if (this.completer.$iframe) {
var iframePosition = this.completer.$iframe.offset();
position.top += iframePosition.top;
position.left += iframePosition.left;
//subtract scrollTop from element in iframe
position.top -= this.$el.scrollTop();
}

$node.remove();
return position;
},
Expand All @@ -100,7 +113,7 @@
// this.getTextFromHeadToCaret()
// // => ' wor' // not '<b>hello</b> wor'
getTextFromHeadToCaret: function () {
var range = window.getSelection().getRangeAt(0);
var range = this.el.ownerDocument.getSelection().getRangeAt(0);
var selection = range.cloneRange();
selection.selectNodeContents(range.startContainer);
return selection.toString().substring(0, range.startOffset);
Expand Down
5 changes: 4 additions & 1 deletion src/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,10 @@
var windowScrollBottom = $window.scrollTop() + $window.height();
var height = this.$el.height();
if ((this.$el.position().top + height) > windowScrollBottom) {
this.$el.offset({top: windowScrollBottom - height});
// only do this if we are not in an iframe
if (!this.completer.$iframe) {
this.$el.offset({top: windowScrollBottom - height});
}
}
},

Expand Down

0 comments on commit d8b97dc

Please sign in to comment.