update to textcomplete v 1.8.0
This commit is contained in:
parent
5abe7d2dfb
commit
cca5349110
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2014 Yuku Takahashi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -136,10 +136,6 @@ if (typeof jQuery === 'undefined') {
|
||||
return Object.prototype.toString.call(obj) === '[object String]';
|
||||
};
|
||||
|
||||
var isFunction = function (obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Function]';
|
||||
};
|
||||
|
||||
var uniqueId = 0;
|
||||
|
||||
function Completer(element, option) {
|
||||
@ -147,32 +143,46 @@ if (typeof jQuery === 'undefined') {
|
||||
this.id = 'textcomplete' + uniqueId++;
|
||||
this.strategies = [];
|
||||
this.views = [];
|
||||
this.option = $.extend({}, Completer._getDefaults(), option);
|
||||
this.option = $.extend({}, Completer.defaults, option);
|
||||
|
||||
if (!this.$el.is('input[type=text]') && !this.$el.is('input[type=search]') && !this.$el.is('textarea') && !element.isContentEditable && element.contentEditable != 'true') {
|
||||
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 {
|
||||
// Initialize view objects lazily.
|
||||
var self = this;
|
||||
this.$el.one('focus.' + this.id, function () { self.initialize(); });
|
||||
|
||||
// Special handling for CKEditor: lazy init on instance load
|
||||
if ((!this.option.adapter || this.option.adapter == 'CKEditor') && typeof CKEDITOR != 'undefined' && (this.$el.is('textarea'))) {
|
||||
CKEDITOR.on("instanceReady", function(event) {
|
||||
event.editor.once("focus", function(event2) {
|
||||
// replace the element with the Iframe element and flag it as CKEditor
|
||||
self.$el = $(event.editor.editable().$);
|
||||
if (!self.option.adapter) {
|
||||
self.option.adapter = $.fn.textcomplete['CKEditor'];
|
||||
self.option.ckeditor_instance = event.editor;
|
||||
}
|
||||
self.initialize();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Completer._getDefaults = function () {
|
||||
if (!Completer.DEFAULTS) {
|
||||
Completer.DEFAULTS = {
|
||||
appendTo: $('body'),
|
||||
zIndex: '100'
|
||||
};
|
||||
}
|
||||
|
||||
return Completer.DEFAULTS;
|
||||
}
|
||||
Completer.defaults = {
|
||||
appendTo: 'body',
|
||||
className: '', // deprecated option
|
||||
dropdownClassName: 'dropdown-menu textcomplete-dropdown',
|
||||
maxCount: 10,
|
||||
zIndex: '100',
|
||||
rightEdgeOffset: 30
|
||||
};
|
||||
|
||||
$.extend(Completer.prototype, {
|
||||
// Public properties
|
||||
@ -184,12 +194,26 @@ if (typeof jQuery === 'undefined') {
|
||||
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;
|
||||
@ -281,7 +305,7 @@ if (typeof jQuery === 'undefined') {
|
||||
var strategy = this.strategies[i];
|
||||
var context = strategy.context(text);
|
||||
if (context || context === '') {
|
||||
var matchRegexp = isFunction(strategy.match) ? strategy.match(text) : strategy.match;
|
||||
var matchRegexp = $.isFunction(strategy.match) ? strategy.match(text) : strategy.match;
|
||||
if (isString(context)) { text = context; }
|
||||
var match = text.match(matchRegexp);
|
||||
if (match) { return [strategy, match[strategy.index], match]; }
|
||||
@ -399,7 +423,7 @@ if (typeof jQuery === 'undefined') {
|
||||
var $parent = option.appendTo;
|
||||
if (!($parent instanceof $)) { $parent = $($parent); }
|
||||
var $el = $('<ul></ul>')
|
||||
.addClass('dropdown-menu textcomplete-dropdown')
|
||||
.addClass(option.dropdownClassName)
|
||||
.attr('id', 'textcomplete-dropdown-' + option._oid)
|
||||
.css({
|
||||
display: 'none',
|
||||
@ -422,7 +446,7 @@ if (typeof jQuery === 'undefined') {
|
||||
footer: null,
|
||||
header: null,
|
||||
id: null,
|
||||
maxCount: 10,
|
||||
maxCount: null,
|
||||
placement: '',
|
||||
shown: false,
|
||||
data: [], // Shown zipped data.
|
||||
@ -445,8 +469,8 @@ if (typeof jQuery === 'undefined') {
|
||||
|
||||
render: function (zippedData) {
|
||||
var contentsHtml = this._buildContents(zippedData);
|
||||
var unzippedData = $.map(this.data, function (d) { return d.value; });
|
||||
if (this.data.length) {
|
||||
var unzippedData = $.map(zippedData, function (d) { return d.value; });
|
||||
if (zippedData.length) {
|
||||
var strategy = zippedData[0].strategy;
|
||||
if (strategy.id) {
|
||||
this.$el.attr('data-strategy', strategy.id);
|
||||
@ -785,7 +809,10 @@ if (typeof jQuery === 'undefined') {
|
||||
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});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -794,7 +821,7 @@ if (typeof jQuery === 'undefined') {
|
||||
// to the document width so we don't know if we would have overrun it. As a heuristic to avoid that clipping
|
||||
// (which makes our elements wrap onto the next line and corrupt the next item), if we're close to the right
|
||||
// edge, move left. We don't know how far to move left, so just keep nudging a bit.
|
||||
var tolerance = 30; // pixels. Make wider than vertical scrollbar because we might not be able to use that space.
|
||||
var tolerance = this.option.rightEdgeOffset; // pixels. Make wider than vertical scrollbar because we might not be able to use that space.
|
||||
var lastOffset = this.$el.offset().left, offset;
|
||||
var width = this.$el.width();
|
||||
var maxLeft = $window.width() - tolerance;
|
||||
@ -1005,8 +1032,14 @@ if (typeof jQuery === 'undefined') {
|
||||
switch (clickEvent.keyCode) {
|
||||
case 9: // TAB
|
||||
case 13: // ENTER
|
||||
case 16: // SHIFT
|
||||
case 17: // CTRL
|
||||
case 18: // ALT
|
||||
case 33: // PAGEUP
|
||||
case 34: // PAGEDOWN
|
||||
case 40: // DOWN
|
||||
case 38: // UP
|
||||
case 27: // ESC
|
||||
return true;
|
||||
}
|
||||
if (clickEvent.ctrlKey) switch (clickEvent.keyCode) {
|
||||
@ -1040,12 +1073,14 @@ if (typeof jQuery === 'undefined') {
|
||||
var pre = this.getTextFromHeadToCaret();
|
||||
var post = this.el.value.substring(this.el.selectionEnd);
|
||||
var newSubstr = strategy.replace(value, e);
|
||||
var regExp;
|
||||
if (typeof newSubstr !== 'undefined') {
|
||||
if ($.isArray(newSubstr)) {
|
||||
post = newSubstr[1] + post;
|
||||
newSubstr = newSubstr[0];
|
||||
}
|
||||
pre = pre.replace(strategy.match, newSubstr);
|
||||
regExp = $.isFunction(strategy.match) ? strategy.match(pre) : strategy.match;
|
||||
pre = pre.replace(regExp, newSubstr);
|
||||
this.$el.val(pre + post);
|
||||
this.el.selectionStart = this.el.selectionEnd = pre.length;
|
||||
}
|
||||
@ -1062,7 +1097,8 @@ if (typeof jQuery === 'undefined') {
|
||||
var p = $.fn.textcomplete.getCaretCoordinates(this.el, this.el.selectionStart);
|
||||
return {
|
||||
top: p.top + this._calculateLineHeight() - this.$el.scrollTop(),
|
||||
left: p.left - this.$el.scrollLeft()
|
||||
left: p.left - this.$el.scrollLeft(),
|
||||
lineHeight: this._calculateLineHeight()
|
||||
};
|
||||
},
|
||||
|
||||
@ -1111,12 +1147,14 @@ if (typeof jQuery === 'undefined') {
|
||||
var pre = this.getTextFromHeadToCaret();
|
||||
var post = this.el.value.substring(pre.length);
|
||||
var newSubstr = strategy.replace(value, e);
|
||||
var regExp;
|
||||
if (typeof newSubstr !== 'undefined') {
|
||||
if ($.isArray(newSubstr)) {
|
||||
post = newSubstr[1] + post;
|
||||
newSubstr = newSubstr[0];
|
||||
}
|
||||
pre = pre.replace(strategy.match, newSubstr);
|
||||
regExp = $.isFunction(strategy.match) ? strategy.match(pre) : strategy.match;
|
||||
pre = pre.replace(regExp, newSubstr);
|
||||
this.$el.val(pre + post);
|
||||
this.el.focus();
|
||||
var range = this.el.createTextRange();
|
||||
@ -1162,30 +1200,35 @@ if (typeof jQuery === 'undefined') {
|
||||
// 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);
|
||||
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];
|
||||
}
|
||||
pre = pre.replace(strategy.match, newSubstr);
|
||||
regExp = $.isFunction(strategy.match) ? strategy.match(pre) : strategy.match;
|
||||
pre = pre.replace(regExp, newSubstr)
|
||||
.replace(/ $/, " "); //   necessary at least for CKeditor to not eat spaces
|
||||
range.selectNodeContents(range.startContainer);
|
||||
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) {
|
||||
@ -1218,8 +1261,8 @@ if (typeof jQuery === 'undefined') {
|
||||
//
|
||||
// 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();
|
||||
@ -1228,6 +1271,17 @@ if (typeof jQuery === 'undefined') {
|
||||
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;
|
||||
},
|
||||
@ -1241,7 +1295,7 @@ if (typeof jQuery === 'undefined') {
|
||||
// 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);
|
||||
@ -1251,6 +1305,39 @@ if (typeof jQuery === 'undefined') {
|
||||
$.fn.textcomplete.ContentEditable = ContentEditable;
|
||||
}(jQuery);
|
||||
|
||||
// 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';
|
||||
|
||||
// CKEditor adapter
|
||||
// =======================
|
||||
//
|
||||
// Adapter for CKEditor, based on contenteditable elements.
|
||||
function CKEditor (element, completer, option) {
|
||||
this.initialize(element, completer, option);
|
||||
}
|
||||
|
||||
$.extend(CKEditor.prototype, $.fn.textcomplete.ContentEditable.prototype, {
|
||||
_bindEvents: function () {
|
||||
var $this = this;
|
||||
this.option.ckeditor_instance.on('key', function(event) {
|
||||
var domEvent = event.data;
|
||||
$this._onKeyup(domEvent);
|
||||
if ($this.completer.dropdown.shown && $this._skipSearch(domEvent)) {
|
||||
return false;
|
||||
}
|
||||
}, null, null, 1); // 1 = Priority = Important!
|
||||
// we actually also need the native event, as the CKEditor one is happening to late
|
||||
this.$el.on('keyup.' + this.id, $.proxy(this._onKeyup, this));
|
||||
},
|
||||
});
|
||||
|
||||
$.fn.textcomplete.CKEditor = CKEditor;
|
||||
}(jQuery);
|
||||
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 Jonathan Ong me@jongleberry.com
|
||||
|
File diff suppressed because one or more lines are too long
1
library/jquery-textcomplete/jquery.textcomplete.min.map
Normal file
1
library/jquery-textcomplete/jquery.textcomplete.min.map
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user