UNPKG

jquery-textcomplete

Version:

[![npm version](https://badge.fury.io/js/jquery-textcomplete.svg)](http://badge.fury.io/js/jquery-textcomplete) [![Bower version](https://badge.fury.io/bo/jquery-textcomplete.svg)](http://badge.fury.io/bo/jquery-textcomplete) [![Analytics](https://ga-beac

128 lines (112 loc) 4.6 kB
// NOTE: TextComplete plugin has contenteditable support but it does not work // fine especially on old IEs. // Any pull requests are REALLY welcome. +function ($) { 'use strict'; // ContentEditable adapter // ======================= // // Adapter for contenteditable elements. function ContentEditable (element, completer, option) { this.initialize(element, completer, option); } $.extend(ContentEditable.prototype, $.fn.textcomplete.Adapter.prototype, { // Public methods // -------------- // Update the content with the given value and strategy. // When an dropdown item is selected, it is executed. select: function (value, strategy, e) { var pre = this.getTextFromHeadToCaret(); // 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); var content = selection.toString(); var post = content.substring(range.startOffset); var newSubstr = strategy.replace(value, e); var regExp; if (typeof newSubstr !== 'undefined') { if ($.isArray(newSubstr)) { post = newSubstr[1] + post; newSubstr = newSubstr[0]; } regExp = $.isFunction(strategy.match) ? strategy.match(pre) : strategy.match; pre = pre.replace(regExp, newSubstr) .replace(/ $/, "&nbsp"); // &nbsp necessary at least for CKeditor to not eat spaces range.selectNodeContents(range.startContainer); range.deleteContents(); // create temporary elements var preWrapper = this.el.ownerDocument.createElement("div"); preWrapper.innerHTML = pre; var postWrapper = this.el.ownerDocument.createElement("div"); postWrapper.innerHTML = post; // create the fragment thats inserted var fragment = this.el.ownerDocument.createDocumentFragment(); var childNode; var lastOfPre; while (childNode = preWrapper.firstChild) { lastOfPre = fragment.appendChild(childNode); } while (childNode = postWrapper.firstChild) { fragment.appendChild(childNode); } // insert the fragment & jump behind the last node in "pre" range.insertNode(fragment); range.setStartAfter(lastOfPre); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); } }, // Private methods // --------------- // Returns the caret's relative position from the contenteditable's // left top corner. // // Examples // // this._getCaretRelativePosition() // //=> { top: 18, left: 200, lineHeight: 16 } // // Dropdown's position will be decided using the result. _getCaretRelativePosition: function () { var range = this.el.ownerDocument.getSelection().getRangeAt(0).cloneRange(); var node = this.el.ownerDocument.createElement('span'); range.insertNode(node); range.selectNodeContents(node); range.deleteContents(); var $node = $(node); var position = $node.offset(); 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; }, // Returns the string between the first character and the caret. // Completer will be triggered with the result for start autocompleting. // // Example // // // Suppose the html is '<b>hello</b> wor|ld' and | is the caret. // this.getTextFromHeadToCaret() // // => ' wor' // not '<b>hello</b> wor' getTextFromHeadToCaret: function () { var range = this.el.ownerDocument.getSelection().getRangeAt(0); var selection = range.cloneRange(); selection.selectNodeContents(range.startContainer); return selection.toString().substring(0, range.startOffset); } }); $.fn.textcomplete.ContentEditable = ContentEditable; }(jQuery);