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]';
|
return Object.prototype.toString.call(obj) === '[object String]';
|
||||||
};
|
};
|
||||||
|
|
||||||
var isFunction = function (obj) {
|
|
||||||
return Object.prototype.toString.call(obj) === '[object Function]';
|
|
||||||
};
|
|
||||||
|
|
||||||
var uniqueId = 0;
|
var uniqueId = 0;
|
||||||
|
|
||||||
function Completer(element, option) {
|
function Completer(element, option) {
|
||||||
@ -147,32 +143,46 @@ if (typeof jQuery === 'undefined') {
|
|||||||
this.id = 'textcomplete' + uniqueId++;
|
this.id = 'textcomplete' + uniqueId++;
|
||||||
this.strategies = [];
|
this.strategies = [];
|
||||||
this.views = [];
|
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') {
|
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.');
|
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.
|
// element has already been focused. Initialize view objects immediately.
|
||||||
this.initialize()
|
this.initialize()
|
||||||
} else {
|
} else {
|
||||||
// Initialize view objects lazily.
|
// Initialize view objects lazily.
|
||||||
var self = this;
|
var self = this;
|
||||||
this.$el.one('focus.' + this.id, function () { self.initialize(); });
|
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 () {
|
Completer.defaults = {
|
||||||
if (!Completer.DEFAULTS) {
|
appendTo: 'body',
|
||||||
Completer.DEFAULTS = {
|
className: '', // deprecated option
|
||||||
appendTo: $('body'),
|
dropdownClassName: 'dropdown-menu textcomplete-dropdown',
|
||||||
zIndex: '100'
|
maxCount: 10,
|
||||||
|
zIndex: '100',
|
||||||
|
rightEdgeOffset: 30
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return Completer.DEFAULTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
$.extend(Completer.prototype, {
|
$.extend(Completer.prototype, {
|
||||||
// Public properties
|
// Public properties
|
||||||
@ -184,12 +194,26 @@ if (typeof jQuery === 'undefined') {
|
|||||||
adapter: null,
|
adapter: null,
|
||||||
dropdown: null,
|
dropdown: null,
|
||||||
$el: null,
|
$el: null,
|
||||||
|
$iframe: null,
|
||||||
|
|
||||||
// Public methods
|
// Public methods
|
||||||
// --------------
|
// --------------
|
||||||
|
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
var element = this.$el.get(0);
|
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.
|
// Initialize view objects.
|
||||||
this.dropdown = new $.fn.textcomplete.Dropdown(element, this, this.option);
|
this.dropdown = new $.fn.textcomplete.Dropdown(element, this, this.option);
|
||||||
var Adapter, viewName;
|
var Adapter, viewName;
|
||||||
@ -281,7 +305,7 @@ if (typeof jQuery === 'undefined') {
|
|||||||
var strategy = this.strategies[i];
|
var strategy = this.strategies[i];
|
||||||
var context = strategy.context(text);
|
var context = strategy.context(text);
|
||||||
if (context || context === '') {
|
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; }
|
if (isString(context)) { text = context; }
|
||||||
var match = text.match(matchRegexp);
|
var match = text.match(matchRegexp);
|
||||||
if (match) { return [strategy, match[strategy.index], match]; }
|
if (match) { return [strategy, match[strategy.index], match]; }
|
||||||
@ -399,7 +423,7 @@ if (typeof jQuery === 'undefined') {
|
|||||||
var $parent = option.appendTo;
|
var $parent = option.appendTo;
|
||||||
if (!($parent instanceof $)) { $parent = $($parent); }
|
if (!($parent instanceof $)) { $parent = $($parent); }
|
||||||
var $el = $('<ul></ul>')
|
var $el = $('<ul></ul>')
|
||||||
.addClass('dropdown-menu textcomplete-dropdown')
|
.addClass(option.dropdownClassName)
|
||||||
.attr('id', 'textcomplete-dropdown-' + option._oid)
|
.attr('id', 'textcomplete-dropdown-' + option._oid)
|
||||||
.css({
|
.css({
|
||||||
display: 'none',
|
display: 'none',
|
||||||
@ -422,7 +446,7 @@ if (typeof jQuery === 'undefined') {
|
|||||||
footer: null,
|
footer: null,
|
||||||
header: null,
|
header: null,
|
||||||
id: null,
|
id: null,
|
||||||
maxCount: 10,
|
maxCount: null,
|
||||||
placement: '',
|
placement: '',
|
||||||
shown: false,
|
shown: false,
|
||||||
data: [], // Shown zipped data.
|
data: [], // Shown zipped data.
|
||||||
@ -445,8 +469,8 @@ if (typeof jQuery === 'undefined') {
|
|||||||
|
|
||||||
render: function (zippedData) {
|
render: function (zippedData) {
|
||||||
var contentsHtml = this._buildContents(zippedData);
|
var contentsHtml = this._buildContents(zippedData);
|
||||||
var unzippedData = $.map(this.data, function (d) { return d.value; });
|
var unzippedData = $.map(zippedData, function (d) { return d.value; });
|
||||||
if (this.data.length) {
|
if (zippedData.length) {
|
||||||
var strategy = zippedData[0].strategy;
|
var strategy = zippedData[0].strategy;
|
||||||
if (strategy.id) {
|
if (strategy.id) {
|
||||||
this.$el.attr('data-strategy', strategy.id);
|
this.$el.attr('data-strategy', strategy.id);
|
||||||
@ -785,8 +809,11 @@ if (typeof jQuery === 'undefined') {
|
|||||||
var windowScrollBottom = $window.scrollTop() + $window.height();
|
var windowScrollBottom = $window.scrollTop() + $window.height();
|
||||||
var height = this.$el.height();
|
var height = this.$el.height();
|
||||||
if ((this.$el.position().top + height) > windowScrollBottom) {
|
if ((this.$el.position().top + height) > windowScrollBottom) {
|
||||||
|
// only do this if we are not in an iframe
|
||||||
|
if (!this.completer.$iframe) {
|
||||||
this.$el.offset({top: windowScrollBottom - height});
|
this.$el.offset({top: windowScrollBottom - height});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_fitToRight: function() {
|
_fitToRight: function() {
|
||||||
@ -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
|
// 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
|
// (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.
|
// 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 lastOffset = this.$el.offset().left, offset;
|
||||||
var width = this.$el.width();
|
var width = this.$el.width();
|
||||||
var maxLeft = $window.width() - tolerance;
|
var maxLeft = $window.width() - tolerance;
|
||||||
@ -1005,8 +1032,14 @@ if (typeof jQuery === 'undefined') {
|
|||||||
switch (clickEvent.keyCode) {
|
switch (clickEvent.keyCode) {
|
||||||
case 9: // TAB
|
case 9: // TAB
|
||||||
case 13: // ENTER
|
case 13: // ENTER
|
||||||
|
case 16: // SHIFT
|
||||||
|
case 17: // CTRL
|
||||||
|
case 18: // ALT
|
||||||
|
case 33: // PAGEUP
|
||||||
|
case 34: // PAGEDOWN
|
||||||
case 40: // DOWN
|
case 40: // DOWN
|
||||||
case 38: // UP
|
case 38: // UP
|
||||||
|
case 27: // ESC
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (clickEvent.ctrlKey) switch (clickEvent.keyCode) {
|
if (clickEvent.ctrlKey) switch (clickEvent.keyCode) {
|
||||||
@ -1040,12 +1073,14 @@ if (typeof jQuery === 'undefined') {
|
|||||||
var pre = this.getTextFromHeadToCaret();
|
var pre = this.getTextFromHeadToCaret();
|
||||||
var post = this.el.value.substring(this.el.selectionEnd);
|
var post = this.el.value.substring(this.el.selectionEnd);
|
||||||
var newSubstr = strategy.replace(value, e);
|
var newSubstr = strategy.replace(value, e);
|
||||||
|
var regExp;
|
||||||
if (typeof newSubstr !== 'undefined') {
|
if (typeof newSubstr !== 'undefined') {
|
||||||
if ($.isArray(newSubstr)) {
|
if ($.isArray(newSubstr)) {
|
||||||
post = newSubstr[1] + post;
|
post = newSubstr[1] + post;
|
||||||
newSubstr = newSubstr[0];
|
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.val(pre + post);
|
||||||
this.el.selectionStart = this.el.selectionEnd = pre.length;
|
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);
|
var p = $.fn.textcomplete.getCaretCoordinates(this.el, this.el.selectionStart);
|
||||||
return {
|
return {
|
||||||
top: p.top + this._calculateLineHeight() - this.$el.scrollTop(),
|
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 pre = this.getTextFromHeadToCaret();
|
||||||
var post = this.el.value.substring(pre.length);
|
var post = this.el.value.substring(pre.length);
|
||||||
var newSubstr = strategy.replace(value, e);
|
var newSubstr = strategy.replace(value, e);
|
||||||
|
var regExp;
|
||||||
if (typeof newSubstr !== 'undefined') {
|
if (typeof newSubstr !== 'undefined') {
|
||||||
if ($.isArray(newSubstr)) {
|
if ($.isArray(newSubstr)) {
|
||||||
post = newSubstr[1] + post;
|
post = newSubstr[1] + post;
|
||||||
newSubstr = newSubstr[0];
|
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.val(pre + post);
|
||||||
this.el.focus();
|
this.el.focus();
|
||||||
var range = this.el.createTextRange();
|
var range = this.el.createTextRange();
|
||||||
@ -1162,30 +1200,35 @@ if (typeof jQuery === 'undefined') {
|
|||||||
// When an dropdown item is selected, it is executed.
|
// When an dropdown item is selected, it is executed.
|
||||||
select: function (value, strategy, e) {
|
select: function (value, strategy, e) {
|
||||||
var pre = this.getTextFromHeadToCaret();
|
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 range = sel.getRangeAt(0);
|
||||||
var selection = range.cloneRange();
|
var selection = range.cloneRange();
|
||||||
selection.selectNodeContents(range.startContainer);
|
selection.selectNodeContents(range.startContainer);
|
||||||
var content = selection.toString();
|
var content = selection.toString();
|
||||||
var post = content.substring(range.startOffset);
|
var post = content.substring(range.startOffset);
|
||||||
var newSubstr = strategy.replace(value, e);
|
var newSubstr = strategy.replace(value, e);
|
||||||
|
var regExp;
|
||||||
if (typeof newSubstr !== 'undefined') {
|
if (typeof newSubstr !== 'undefined') {
|
||||||
if ($.isArray(newSubstr)) {
|
if ($.isArray(newSubstr)) {
|
||||||
post = newSubstr[1] + post;
|
post = newSubstr[1] + post;
|
||||||
newSubstr = newSubstr[0];
|
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.selectNodeContents(range.startContainer);
|
||||||
range.deleteContents();
|
range.deleteContents();
|
||||||
|
|
||||||
// create temporary elements
|
// create temporary elements
|
||||||
var preWrapper = document.createElement("div");
|
var preWrapper = this.el.ownerDocument.createElement("div");
|
||||||
preWrapper.innerHTML = pre;
|
preWrapper.innerHTML = pre;
|
||||||
var postWrapper = document.createElement("div");
|
var postWrapper = this.el.ownerDocument.createElement("div");
|
||||||
postWrapper.innerHTML = post;
|
postWrapper.innerHTML = post;
|
||||||
|
|
||||||
// create the fragment thats inserted
|
// create the fragment thats inserted
|
||||||
var fragment = document.createDocumentFragment();
|
var fragment = this.el.ownerDocument.createDocumentFragment();
|
||||||
var childNode;
|
var childNode;
|
||||||
var lastOfPre;
|
var lastOfPre;
|
||||||
while (childNode = preWrapper.firstChild) {
|
while (childNode = preWrapper.firstChild) {
|
||||||
@ -1218,8 +1261,8 @@ if (typeof jQuery === 'undefined') {
|
|||||||
//
|
//
|
||||||
// Dropdown's position will be decided using the result.
|
// Dropdown's position will be decided using the result.
|
||||||
_getCaretRelativePosition: function () {
|
_getCaretRelativePosition: function () {
|
||||||
var range = window.getSelection().getRangeAt(0).cloneRange();
|
var range = this.el.ownerDocument.getSelection().getRangeAt(0).cloneRange();
|
||||||
var node = document.createElement('span');
|
var node = this.el.ownerDocument.createElement('span');
|
||||||
range.insertNode(node);
|
range.insertNode(node);
|
||||||
range.selectNodeContents(node);
|
range.selectNodeContents(node);
|
||||||
range.deleteContents();
|
range.deleteContents();
|
||||||
@ -1228,6 +1271,17 @@ if (typeof jQuery === 'undefined') {
|
|||||||
position.left -= this.$el.offset().left;
|
position.left -= this.$el.offset().left;
|
||||||
position.top += $node.height() - this.$el.offset().top;
|
position.top += $node.height() - this.$el.offset().top;
|
||||||
position.lineHeight = $node.height();
|
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();
|
$node.remove();
|
||||||
return position;
|
return position;
|
||||||
},
|
},
|
||||||
@ -1241,7 +1295,7 @@ if (typeof jQuery === 'undefined') {
|
|||||||
// this.getTextFromHeadToCaret()
|
// this.getTextFromHeadToCaret()
|
||||||
// // => ' wor' // not '<b>hello</b> wor'
|
// // => ' wor' // not '<b>hello</b> wor'
|
||||||
getTextFromHeadToCaret: function () {
|
getTextFromHeadToCaret: function () {
|
||||||
var range = window.getSelection().getRangeAt(0);
|
var range = this.el.ownerDocument.getSelection().getRangeAt(0);
|
||||||
var selection = range.cloneRange();
|
var selection = range.cloneRange();
|
||||||
selection.selectNodeContents(range.startContainer);
|
selection.selectNodeContents(range.startContainer);
|
||||||
return selection.toString().substring(0, range.startOffset);
|
return selection.toString().substring(0, range.startOffset);
|
||||||
@ -1251,6 +1305,39 @@ if (typeof jQuery === 'undefined') {
|
|||||||
$.fn.textcomplete.ContentEditable = ContentEditable;
|
$.fn.textcomplete.ContentEditable = ContentEditable;
|
||||||
}(jQuery);
|
}(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)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2015 Jonathan Ong me@jongleberry.com
|
// 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