Created
June 3, 2019 19:38
-
-
Save philippejadin/2fa0c608e568a90e668a88b0a5613adf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* at.js - 1.5.4 | |
* Copyright (c) 2018 chord.luo <[email protected]>; | |
* Homepage: http://ichord.github.com/At.js | |
* License: MIT | |
*/ | |
(function (root, factory) { | |
if (typeof define === 'function' && define.amd) { | |
// AMD. Register as an anonymous module unless amdModuleId is set | |
define(["jquery"], function (a0) { | |
return (factory(a0)); | |
}); | |
} else if (typeof exports === 'object') { | |
// Node. Does not work with strict CommonJS, but | |
// only CommonJS-like environments that support module.exports, | |
// like Node. | |
module.exports = factory(require("jquery")); | |
} else { | |
factory(jQuery); | |
} | |
}(this, function ($) { | |
var DEFAULT_CALLBACKS, KEY_CODE; | |
KEY_CODE = { | |
ESC: 27, | |
TAB: 9, | |
ENTER: 13, | |
CTRL: 17, | |
A: 65, | |
P: 80, | |
N: 78, | |
LEFT: 37, | |
UP: 38, | |
RIGHT: 39, | |
DOWN: 40, | |
BACKSPACE: 8, | |
SPACE: 32 | |
}; | |
DEFAULT_CALLBACKS = { | |
beforeSave: function(data) { | |
return Controller.arrayToDefaultHash(data); | |
}, | |
matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) { | |
var _a, _y, match, regexp, space; | |
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); | |
if (should_startWithSpace) { | |
flag = '(?:^|\\s)' + flag; | |
} | |
_a = decodeURI("%C3%80"); | |
_y = decodeURI("%C3%BF"); | |
space = acceptSpaceBar ? "\ " : ""; | |
regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi'); | |
match = regexp.exec(subtext); | |
if (match) { | |
return match[2] || match[1]; | |
} else { | |
return null; | |
} | |
}, | |
filter: function(query, data, searchKey) { | |
var _results, i, item, len; | |
_results = []; | |
for (i = 0, len = data.length; i < len; i++) { | |
item = data[i]; | |
if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) { | |
_results.push(item); | |
} | |
} | |
return _results; | |
}, | |
remoteFilter: null, | |
sorter: function(query, items, searchKey) { | |
var _results, i, item, len; | |
if (!query) { | |
return items; | |
} | |
_results = []; | |
for (i = 0, len = items.length; i < len; i++) { | |
item = items[i]; | |
item.atwho_order = new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase()); | |
if (item.atwho_order > -1) { | |
_results.push(item); | |
} | |
} | |
return _results.sort(function(a, b) { | |
return a.atwho_order - b.atwho_order; | |
}); | |
}, | |
tplEval: function(tpl, map) { | |
var error, error1, template; | |
template = tpl; | |
try { | |
if (typeof tpl !== 'string') { | |
template = tpl(map); | |
} | |
return template.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) { | |
return map[key]; | |
}); | |
} catch (error1) { | |
error = error1; | |
return ""; | |
} | |
}, | |
highlighter: function(li, query) { | |
var regexp; | |
if (!query) { | |
return li; | |
} | |
regexp = new RegExp(">\\s*([^\<]*?)(" + query.replace("+", "\\+") + ")([^\<]*)\\s*<", 'ig'); | |
return li.replace(regexp, function(str, $1, $2, $3) { | |
return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <'; | |
}); | |
}, | |
beforeInsert: function(value, $li, e) { | |
return value; | |
}, | |
beforeReposition: function(offset) { | |
return offset; | |
}, | |
afterMatchFailed: function(at, el) {} | |
}; | |
var App; | |
App = (function() { | |
function App(inputor) { | |
this.currentFlag = null; | |
this.controllers = {}; | |
this.aliasMaps = {}; | |
this.$inputor = $(inputor); | |
this.setupRootElement(); | |
this.listen(); | |
} | |
App.prototype.createContainer = function(doc) { | |
var ref; | |
if ((ref = this.$el) != null) { | |
ref.remove(); | |
} | |
return $(doc.body).append(this.$el = $("<div class='atwho-container'></div>")); | |
}; | |
App.prototype.setupRootElement = function(iframe, asRoot) { | |
var error, error1; | |
if (asRoot == null) { | |
asRoot = false; | |
} | |
if (iframe) { | |
this.window = iframe.contentWindow; | |
this.document = iframe.contentDocument || this.window.document; | |
this.iframe = iframe; | |
} else { | |
this.document = this.$inputor[0].ownerDocument; | |
this.window = this.document.defaultView || this.document.parentWindow; | |
try { | |
this.iframe = this.window.frameElement; | |
} catch (error1) { | |
error = error1; | |
this.iframe = null; | |
if ($.fn.atwho.debug) { | |
throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n" + error); | |
} | |
} | |
} | |
return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document); | |
}; | |
App.prototype.controller = function(at) { | |
var c, current, currentFlag, ref; | |
if (this.aliasMaps[at]) { | |
current = this.controllers[this.aliasMaps[at]]; | |
} else { | |
ref = this.controllers; | |
for (currentFlag in ref) { | |
c = ref[currentFlag]; | |
if (currentFlag === at) { | |
current = c; | |
break; | |
} | |
} | |
} | |
if (current) { | |
return current; | |
} else { | |
return this.controllers[this.currentFlag]; | |
} | |
}; | |
App.prototype.setContextFor = function(at) { | |
this.currentFlag = at; | |
return this; | |
}; | |
App.prototype.reg = function(flag, setting) { | |
var base, controller; | |
controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag)); | |
if (setting.alias) { | |
this.aliasMaps[setting.alias] = flag; | |
} | |
controller.init(setting); | |
return this; | |
}; | |
App.prototype.listen = function() { | |
return this.$inputor.on('compositionstart', (function(_this) { | |
return function(e) { | |
var ref; | |
if ((ref = _this.controller()) != null) { | |
ref.view.hide(); | |
} | |
_this.isComposing = true; | |
return null; | |
}; | |
})(this)).on('compositionend', (function(_this) { | |
return function(e) { | |
_this.isComposing = false; | |
setTimeout(function(e) { | |
return _this.dispatch(e); | |
}); | |
return null; | |
}; | |
})(this)).on('keyup.atwhoInner', (function(_this) { | |
return function(e) { | |
return _this.onKeyup(e); | |
}; | |
})(this)).on('keydown.atwhoInner', (function(_this) { | |
return function(e) { | |
return _this.onKeydown(e); | |
}; | |
})(this)).on('blur.atwhoInner', (function(_this) { | |
return function(e) { | |
var c; | |
if (c = _this.controller()) { | |
c.expectedQueryCBId = null; | |
return c.view.hide(e, c.getOpt("displayTimeout")); | |
} | |
}; | |
})(this)).on('click.atwhoInner', (function(_this) { | |
return function(e) { | |
return _this.dispatch(e); | |
}; | |
})(this)).on('scroll.atwhoInner', (function(_this) { | |
return function() { | |
var lastScrollTop; | |
lastScrollTop = _this.$inputor.scrollTop(); | |
return function(e) { | |
var currentScrollTop, ref; | |
currentScrollTop = e.target.scrollTop; | |
if (lastScrollTop !== currentScrollTop) { | |
if ((ref = _this.controller()) != null) { | |
ref.view.hide(e); | |
} | |
} | |
lastScrollTop = currentScrollTop; | |
return true; | |
}; | |
}; | |
})(this)()); | |
}; | |
App.prototype.shutdown = function() { | |
var _, c, ref; | |
ref = this.controllers; | |
for (_ in ref) { | |
c = ref[_]; | |
c.destroy(); | |
delete this.controllers[_]; | |
} | |
this.$inputor.off('.atwhoInner'); | |
return this.$el.remove(); | |
}; | |
App.prototype.dispatch = function(e) { | |
var _, c, ref, results; | |
if (void 0 === e) { | |
return; | |
} | |
ref = this.controllers; | |
results = []; | |
for (_ in ref) { | |
c = ref[_]; | |
results.push(c.lookUp(e)); | |
} | |
return results; | |
}; | |
App.prototype.onKeyup = function(e) { | |
var ref; | |
switch (e.keyCode) { | |
case KEY_CODE.ESC: | |
e.preventDefault(); | |
if ((ref = this.controller()) != null) { | |
ref.view.hide(); | |
} | |
break; | |
case KEY_CODE.DOWN: | |
case KEY_CODE.UP: | |
case KEY_CODE.CTRL: | |
case KEY_CODE.ENTER: | |
$.noop(); | |
break; | |
case KEY_CODE.P: | |
case KEY_CODE.N: | |
if (!e.ctrlKey) { | |
this.dispatch(e); | |
} | |
break; | |
default: | |
this.dispatch(e); | |
} | |
}; | |
App.prototype.onKeydown = function(e) { | |
var ref, view; | |
view = (ref = this.controller()) != null ? ref.view : void 0; | |
if (!(view && view.visible())) { | |
return; | |
} | |
switch (e.keyCode) { | |
case KEY_CODE.ESC: | |
e.preventDefault(); | |
view.hide(e); | |
break; | |
case KEY_CODE.UP: | |
e.preventDefault(); | |
view.prev(); | |
break; | |
case KEY_CODE.DOWN: | |
e.preventDefault(); | |
view.next(); | |
break; | |
case KEY_CODE.P: | |
if (!e.ctrlKey) { | |
return; | |
} | |
e.preventDefault(); | |
view.prev(); | |
break; | |
case KEY_CODE.N: | |
if (!e.ctrlKey) { | |
return; | |
} | |
e.preventDefault(); | |
view.next(); | |
break; | |
case KEY_CODE.TAB: | |
case KEY_CODE.ENTER: | |
case KEY_CODE.SPACE: | |
if (!view.visible()) { | |
return; | |
} | |
if (!this.controller().getOpt('spaceSelectsMatch') && e.keyCode === KEY_CODE.SPACE) { | |
return; | |
} | |
if (!this.controller().getOpt('tabSelectsMatch') && e.keyCode === KEY_CODE.TAB) { | |
return; | |
} | |
if (view.highlighted()) { | |
e.preventDefault(); | |
view.choose(e); | |
} else { | |
view.hide(e); | |
} | |
break; | |
default: | |
$.noop(); | |
} | |
}; | |
return App; | |
})(); | |
var Controller, | |
slice = [].slice; | |
Controller = (function() { | |
Controller.prototype.uid = function() { | |
return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime()); | |
}; | |
function Controller(app, at1) { | |
this.app = app; | |
this.at = at1; | |
this.$inputor = this.app.$inputor; | |
this.id = this.$inputor[0].id || this.uid(); | |
this.expectedQueryCBId = null; | |
this.setting = null; | |
this.query = null; | |
this.pos = 0; | |
this.range = null; | |
if ((this.$el = $("#atwho-ground-" + this.id, this.app.$el)).length === 0) { | |
this.app.$el.append(this.$el = $("<div id='atwho-ground-" + this.id + "'></div>")); | |
} | |
this.model = new Model(this); | |
this.view = new View(this); | |
} | |
Controller.prototype.init = function(setting) { | |
this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting); | |
this.view.init(); | |
return this.model.reload(this.setting.data); | |
}; | |
Controller.prototype.destroy = function() { | |
this.trigger('beforeDestroy'); | |
this.model.destroy(); | |
this.view.destroy(); | |
return this.$el.remove(); | |
}; | |
Controller.prototype.callDefault = function() { | |
var args, error, error1, funcName; | |
funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; | |
try { | |
return DEFAULT_CALLBACKS[funcName].apply(this, args); | |
} catch (error1) { | |
error = error1; | |
return $.error(error + " Or maybe At.js doesn't have function " + funcName); | |
} | |
}; | |
Controller.prototype.trigger = function(name, data) { | |
var alias, eventName; | |
if (data == null) { | |
data = []; | |
} | |
data.push(this); | |
alias = this.getOpt('alias'); | |
eventName = alias ? name + "-" + alias + ".atwho" : name + ".atwho"; | |
return this.$inputor.trigger(eventName, data); | |
}; | |
Controller.prototype.callbacks = function(funcName) { | |
return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName]; | |
}; | |
Controller.prototype.getOpt = function(at, default_value) { | |
var e, error1; | |
try { | |
return this.setting[at]; | |
} catch (error1) { | |
e = error1; | |
return null; | |
} | |
}; | |
Controller.prototype.insertContentFor = function($li) { | |
var data, tpl; | |
tpl = this.getOpt('insertTpl'); | |
data = $.extend({}, $li.data('item-data'), { | |
'atwho-at': this.at | |
}); | |
return this.callbacks("tplEval").call(this, tpl, data, "onInsert"); | |
}; | |
Controller.prototype.renderView = function(data) { | |
var searchKey; | |
searchKey = this.getOpt("searchKey"); | |
data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), searchKey); | |
return this.view.render(data.slice(0, this.getOpt('limit'))); | |
}; | |
Controller.arrayToDefaultHash = function(data) { | |
var i, item, len, results; | |
if (!$.isArray(data)) { | |
return data; | |
} | |
results = []; | |
for (i = 0, len = data.length; i < len; i++) { | |
item = data[i]; | |
if ($.isPlainObject(item)) { | |
results.push(item); | |
} else { | |
results.push({ | |
name: item | |
}); | |
} | |
} | |
return results; | |
}; | |
Controller.prototype.lookUp = function(e) { | |
var query, wait; | |
if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) { | |
return; | |
} | |
if (this.getOpt('suspendOnComposing') && this.app.isComposing) { | |
return; | |
} | |
query = this.catchQuery(e); | |
if (!query) { | |
this.expectedQueryCBId = null; | |
return query; | |
} | |
this.app.setContextFor(this.at); | |
if (wait = this.getOpt('delay')) { | |
this._delayLookUp(query, wait); | |
} else { | |
this._lookUp(query); | |
} | |
return query; | |
}; | |
Controller.prototype._delayLookUp = function(query, wait) { | |
var now, remaining; | |
now = Date.now ? Date.now() : new Date().getTime(); | |
this.previousCallTime || (this.previousCallTime = now); | |
remaining = wait - (now - this.previousCallTime); | |
if ((0 < remaining && remaining < wait)) { | |
this.previousCallTime = now; | |
this._stopDelayedCall(); | |
return this.delayedCallTimeout = setTimeout((function(_this) { | |
return function() { | |
_this.previousCallTime = 0; | |
_this.delayedCallTimeout = null; | |
return _this._lookUp(query); | |
}; | |
})(this), wait); | |
} else { | |
this._stopDelayedCall(); | |
if (this.previousCallTime !== now) { | |
this.previousCallTime = 0; | |
} | |
return this._lookUp(query); | |
} | |
}; | |
Controller.prototype._stopDelayedCall = function() { | |
if (this.delayedCallTimeout) { | |
clearTimeout(this.delayedCallTimeout); | |
return this.delayedCallTimeout = null; | |
} | |
}; | |
Controller.prototype._generateQueryCBId = function() { | |
return {}; | |
}; | |
Controller.prototype._lookUp = function(query) { | |
var _callback; | |
_callback = function(queryCBId, data) { | |
if (queryCBId !== this.expectedQueryCBId) { | |
return; | |
} | |
if (data && data.length > 0) { | |
return this.renderView(this.constructor.arrayToDefaultHash(data)); | |
} else { | |
return this.view.hide(); | |
} | |
}; | |
this.expectedQueryCBId = this._generateQueryCBId(); | |
return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId)); | |
}; | |
return Controller; | |
})(); | |
var TextareaController, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
TextareaController = (function(superClass) { | |
extend(TextareaController, superClass); | |
function TextareaController() { | |
return TextareaController.__super__.constructor.apply(this, arguments); | |
} | |
TextareaController.prototype.catchQuery = function() { | |
var caretPos, content, end, isString, query, start, subtext; | |
content = this.$inputor.val(); | |
caretPos = this.$inputor.caret('pos', { | |
iframe: this.app.iframe | |
}); | |
subtext = content.slice(0, caretPos); | |
query = this.callbacks("matcher").call(this, this.at, subtext, this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar")); | |
isString = typeof query === 'string'; | |
if (isString && query.length < this.getOpt('minLen', 0)) { | |
return; | |
} | |
if (isString && query.length <= this.getOpt('maxLen', 20)) { | |
start = caretPos - query.length; | |
end = start + query.length; | |
this.pos = start; | |
query = { | |
'text': query, | |
'headPos': start, | |
'endPos': end | |
}; | |
this.trigger("matched", [this.at, query.text]); | |
} else { | |
query = null; | |
this.view.hide(); | |
} | |
return this.query = query; | |
}; | |
TextareaController.prototype.rect = function() { | |
var c, iframeOffset, scaleBottom; | |
if (!(c = this.$inputor.caret('offset', this.pos - 1, { | |
iframe: this.app.iframe | |
}))) { | |
return; | |
} | |
if (this.app.iframe && !this.app.iframeAsRoot) { | |
iframeOffset = $(this.app.iframe).offset(); | |
c.left += iframeOffset.left; | |
c.top += iframeOffset.top; | |
} | |
scaleBottom = this.app.document.selection ? 0 : 2; | |
return { | |
left: c.left, | |
top: c.top, | |
bottom: c.top + c.height + scaleBottom | |
}; | |
}; | |
TextareaController.prototype.insert = function(content, $li) { | |
var $inputor, source, startStr, suffix, text; | |
$inputor = this.$inputor; | |
source = $inputor.val(); | |
startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0)); | |
suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " "; | |
content += suffix; | |
text = "" + startStr + content + (source.slice(this.query['endPos'] || 0)); | |
$inputor.val(text); | |
$inputor.caret('pos', startStr.length + content.length, { | |
iframe: this.app.iframe | |
}); | |
if (!$inputor.is(':focus')) { | |
$inputor.focus(); | |
} | |
return $inputor.change(); | |
}; | |
return TextareaController; | |
})(Controller); | |
var EditableController, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
EditableController = (function(superClass) { | |
extend(EditableController, superClass); | |
function EditableController() { | |
return EditableController.__super__.constructor.apply(this, arguments); | |
} | |
EditableController.prototype._getRange = function() { | |
var sel; | |
sel = this.app.window.getSelection(); | |
if (sel.rangeCount > 0) { | |
return sel.getRangeAt(0); | |
} | |
}; | |
EditableController.prototype._setRange = function(position, node, range) { | |
if (range == null) { | |
range = this._getRange(); | |
} | |
if (!(range && node)) { | |
return; | |
} | |
node = $(node)[0]; | |
if (position === 'after') { | |
range.setEndAfter(node); | |
range.setStartAfter(node); | |
} else { | |
range.setEndBefore(node); | |
range.setStartBefore(node); | |
} | |
range.collapse(false); | |
return this._clearRange(range); | |
}; | |
EditableController.prototype._clearRange = function(range) { | |
var sel; | |
if (range == null) { | |
range = this._getRange(); | |
} | |
sel = this.app.window.getSelection(); | |
if (this.ctrl_a_pressed == null) { | |
sel.removeAllRanges(); | |
return sel.addRange(range); | |
} | |
}; | |
EditableController.prototype._movingEvent = function(e) { | |
var ref; | |
return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN); | |
}; | |
EditableController.prototype._unwrap = function(node) { | |
var next; | |
node = $(node).unwrap().get(0); | |
if ((next = node.nextSibling) && next.nodeValue) { | |
node.nodeValue += next.nodeValue; | |
$(next).remove(); | |
} | |
return node; | |
}; | |
EditableController.prototype.catchQuery = function(e) { | |
var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range; | |
if (!(range = this._getRange())) { | |
return; | |
} | |
if (!range.collapsed) { | |
return; | |
} | |
if (e.which === KEY_CODE.ENTER) { | |
($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap(); | |
if ($query.is(':empty')) { | |
$query.remove(); | |
} | |
($query = $(".atwho-query", this.app.document)).text($query.text()).contents().last().unwrap(); | |
this._clearRange(); | |
return; | |
} | |
if (/firefox/i.test(navigator.userAgent)) { | |
if ($(range.startContainer).is(this.$inputor)) { | |
this._clearRange(); | |
return; | |
} | |
if (e.which === KEY_CODE.BACKSPACE && range.startContainer.nodeType === document.ELEMENT_NODE && (offset = range.startOffset - 1) >= 0) { | |
_range = range.cloneRange(); | |
_range.setStart(range.startContainer, offset); | |
if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) { | |
inserted = $(range.startContainer).contents().get(offset); | |
this._setRange('after', $(inserted).contents().last()); | |
} | |
} else if (e.which === KEY_CODE.LEFT && range.startContainer.nodeType === document.TEXT_NODE) { | |
$inserted = $(range.startContainer.previousSibling); | |
if ($inserted.is('.atwho-inserted') && range.startOffset === 0) { | |
this._setRange('after', $inserted.contents().last()); | |
} | |
} | |
} | |
$(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query'); | |
if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) { | |
$query.remove(); | |
} | |
if (!this._movingEvent(e)) { | |
$query.removeClass('atwho-inserted'); | |
} | |
if ($query.length > 0) { | |
switch (e.which) { | |
case KEY_CODE.LEFT: | |
this._setRange('before', $query.get(0), range); | |
$query.removeClass('atwho-query'); | |
return; | |
case KEY_CODE.RIGHT: | |
this._setRange('after', $query.get(0).nextSibling, range); | |
$query.removeClass('atwho-query'); | |
return; | |
} | |
} | |
if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) { | |
$query.empty().html(query_content).attr('data-atwho-at-query', null); | |
this._setRange('after', $query.get(0), range); | |
} | |
_range = range.cloneRange(); | |
_range.setStart(range.startContainer, 0); | |
matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar")); | |
isString = typeof matched === 'string'; | |
if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) { | |
range.setStart(range.startContainer, index); | |
$query = $('<span/>', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query'); | |
range.surroundContents($query.get(0)); | |
lastNode = $query.contents().last().get(0); | |
if (lastNode) { | |
if (/firefox/i.test(navigator.userAgent)) { | |
range.setStart(lastNode, lastNode.length); | |
range.setEnd(lastNode, lastNode.length); | |
this._clearRange(range); | |
} else { | |
this._setRange('after', lastNode, range); | |
} | |
} | |
} | |
if (isString && matched.length < this.getOpt('minLen', 0)) { | |
return; | |
} | |
if (isString && matched.length <= this.getOpt('maxLen', 20)) { | |
query = { | |
text: matched, | |
el: $query | |
}; | |
this.trigger("matched", [this.at, query.text]); | |
return this.query = query; | |
} else { | |
this.view.hide(); | |
this.query = { | |
el: $query | |
}; | |
if ($query.text().indexOf(this.at) >= 0) { | |
if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) { | |
$query.removeClass('atwho-query'); | |
} else if (false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)) { | |
this._setRange("after", this._unwrap($query.text($query.text()).contents().first())); | |
} | |
} | |
return null; | |
} | |
}; | |
EditableController.prototype.rect = function() { | |
var $iframe, iframeOffset, rect; | |
rect = this.query.el.offset(); | |
if (!(rect && this.query.el[0].getClientRects().length)) { | |
return; | |
} | |
if (this.app.iframe && !this.app.iframeAsRoot) { | |
iframeOffset = ($iframe = $(this.app.iframe)).offset(); | |
rect.left += iframeOffset.left - this.$inputor.scrollLeft(); | |
rect.top += iframeOffset.top - this.$inputor.scrollTop(); | |
} | |
rect.bottom = rect.top + this.query.el.height(); | |
return rect; | |
}; | |
EditableController.prototype.insert = function(content, $li) { | |
var data, overrides, range, suffix, suffixNode; | |
if (!this.$inputor.is(':focus')) { | |
this.$inputor.focus(); | |
} | |
overrides = this.getOpt('functionOverrides'); | |
if (overrides.insert) { | |
return overrides.insert.call(this, content, $li); | |
} | |
suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0"; | |
data = $li.data('item-data'); | |
this.query.el.removeClass('atwho-query').addClass('atwho-inserted').html(content).attr('data-atwho-at-query', "" + data['atwho-at'] + this.query.text).attr('contenteditable', "false"); | |
if (range = this._getRange()) { | |
if (this.query.el.length) { | |
range.setEndAfter(this.query.el[0]); | |
} | |
range.collapse(false); | |
range.insertNode(suffixNode = this.app.document.createTextNode("" + suffix)); | |
this._setRange('after', suffixNode, range); | |
} | |
if (!this.$inputor.is(':focus')) { | |
this.$inputor.focus(); | |
} | |
return this.$inputor.change(); | |
}; | |
return EditableController; | |
})(Controller); | |
var Model; | |
Model = (function() { | |
function Model(context) { | |
this.context = context; | |
this.at = this.context.at; | |
this.storage = this.context.$inputor; | |
} | |
Model.prototype.destroy = function() { | |
return this.storage.data(this.at, null); | |
}; | |
Model.prototype.saved = function() { | |
return this.fetch() > 0; | |
}; | |
Model.prototype.query = function(query, callback) { | |
var _remoteFilter, data, searchKey; | |
data = this.fetch(); | |
searchKey = this.context.getOpt("searchKey"); | |
data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || []; | |
_remoteFilter = this.context.callbacks('remoteFilter'); | |
if (data.length > 0 || (!_remoteFilter && data.length === 0)) { | |
return callback(data); | |
} else { | |
return _remoteFilter.call(this.context, query, callback); | |
} | |
}; | |
Model.prototype.fetch = function() { | |
return this.storage.data(this.at) || []; | |
}; | |
Model.prototype.save = function(data) { | |
return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || [])); | |
}; | |
Model.prototype.load = function(data) { | |
if (!(this.saved() || !data)) { | |
return this._load(data); | |
} | |
}; | |
Model.prototype.reload = function(data) { | |
return this._load(data); | |
}; | |
Model.prototype._load = function(data) { | |
if (typeof data === "string") { | |
return $.ajax(data, { | |
dataType: "json" | |
}).done((function(_this) { | |
return function(data) { | |
return _this.save(data); | |
}; | |
})(this)); | |
} else { | |
return this.save(data); | |
} | |
}; | |
return Model; | |
})(); | |
var View; | |
View = (function() { | |
function View(context) { | |
this.context = context; | |
this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>"); | |
this.$elUl = this.$el.children(); | |
this.timeoutID = null; | |
this.context.$el.append(this.$el); | |
this.bindEvent(); | |
} | |
View.prototype.init = function() { | |
var header_tpl, id; | |
id = this.context.getOpt("alias") || this.context.at.charCodeAt(0); | |
header_tpl = this.context.getOpt("headerTpl"); | |
if (header_tpl && this.$el.children().length === 1) { | |
this.$el.prepend(header_tpl); | |
} | |
return this.$el.attr({ | |
'id': "at-view-" + id | |
}); | |
}; | |
View.prototype.destroy = function() { | |
return this.$el.remove(); | |
}; | |
View.prototype.bindEvent = function() { | |
var $menu, lastCoordX, lastCoordY; | |
$menu = this.$el.find('ul'); | |
lastCoordX = 0; | |
lastCoordY = 0; | |
return $menu.on('mousemove.atwho-view', 'li', (function(_this) { | |
return function(e) { | |
var $cur; | |
if (lastCoordX === e.clientX && lastCoordY === e.clientY) { | |
return; | |
} | |
lastCoordX = e.clientX; | |
lastCoordY = e.clientY; | |
$cur = $(e.currentTarget); | |
if ($cur.hasClass('cur')) { | |
return; | |
} | |
$menu.find('.cur').removeClass('cur'); | |
return $cur.addClass('cur'); | |
}; | |
})(this)).on('click.atwho-view', 'li', (function(_this) { | |
return function(e) { | |
$menu.find('.cur').removeClass('cur'); | |
$(e.currentTarget).addClass('cur'); | |
_this.choose(e); | |
return e.preventDefault(); | |
}; | |
})(this)); | |
}; | |
View.prototype.visible = function() { | |
return $.expr.filters.visible(this.$el[0]); | |
}; | |
View.prototype.highlighted = function() { | |
return this.$el.find(".cur").length > 0; | |
}; | |
View.prototype.choose = function(e) { | |
var $li, content; | |
if (($li = this.$el.find(".cur")).length) { | |
content = this.context.insertContentFor($li); | |
this.context._stopDelayedCall(); | |
this.context.insert(this.context.callbacks("beforeInsert").call(this.context, content, $li, e), $li); | |
this.context.trigger("inserted", [$li, e]); | |
this.hide(e); | |
} | |
if (this.context.getOpt("hideWithoutSuffix")) { | |
return this.stopShowing = true; | |
} | |
}; | |
View.prototype.reposition = function(rect) { | |
var _window, offset, overflowOffset, ref; | |
_window = this.context.app.iframeAsRoot ? this.context.app.window : window; | |
if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) { | |
rect.bottom = rect.top - this.$el.height(); | |
} | |
if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) { | |
rect.left = overflowOffset; | |
} | |
offset = { | |
left: rect.left, | |
top: rect.bottom | |
}; | |
if ((ref = this.context.callbacks("beforeReposition")) != null) { | |
ref.call(this.context, offset); | |
} | |
this.$el.offset(offset); | |
return this.context.trigger("reposition", [offset]); | |
}; | |
View.prototype.next = function() { | |
var cur, next, nextEl, offset; | |
cur = this.$el.find('.cur').removeClass('cur'); | |
next = cur.next(); | |
if (!next.length) { | |
next = this.$el.find('li:first'); | |
} | |
next.addClass('cur'); | |
nextEl = next[0]; | |
offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0); | |
return this.scrollTop(Math.max(0, offset - this.$el.height())); | |
}; | |
View.prototype.prev = function() { | |
var cur, offset, prev, prevEl; | |
cur = this.$el.find('.cur').removeClass('cur'); | |
prev = cur.prev(); | |
if (!prev.length) { | |
prev = this.$el.find('li:last'); | |
} | |
prev.addClass('cur'); | |
prevEl = prev[0]; | |
offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0); | |
return this.scrollTop(Math.max(0, offset - this.$el.height())); | |
}; | |
View.prototype.scrollTop = function(scrollTop) { | |
var scrollDuration; | |
scrollDuration = this.context.getOpt('scrollDuration'); | |
if (scrollDuration) { | |
return this.$elUl.animate({ | |
scrollTop: scrollTop | |
}, scrollDuration); | |
} else { | |
return this.$elUl.scrollTop(scrollTop); | |
} | |
}; | |
View.prototype.show = function() { | |
var rect; | |
if (this.stopShowing) { | |
this.stopShowing = false; | |
return; | |
} | |
if (!this.visible()) { | |
this.$el.show(); | |
this.$el.scrollTop(0); | |
this.context.trigger('shown'); | |
} | |
if (rect = this.context.rect()) { | |
return this.reposition(rect); | |
} | |
}; | |
View.prototype.hide = function(e, time) { | |
var callback; | |
if (!this.visible()) { | |
return; | |
} | |
if (isNaN(time)) { | |
this.$el.hide(); | |
return this.context.trigger('hidden', [e]); | |
} else { | |
callback = (function(_this) { | |
return function() { | |
return _this.hide(); | |
}; | |
})(this); | |
clearTimeout(this.timeoutID); | |
return this.timeoutID = setTimeout(callback, time); | |
} | |
}; | |
View.prototype.render = function(list) { | |
var $li, $ul, i, item, len, li, tpl; | |
if (!($.isArray(list) && list.length > 0)) { | |
this.hide(); | |
return; | |
} | |
this.$el.find('ul').empty(); | |
$ul = this.$el.find('ul'); | |
tpl = this.context.getOpt('displayTpl'); | |
for (i = 0, len = list.length; i < len; i++) { | |
item = list[i]; | |
item = $.extend({}, item, { | |
'atwho-at': this.context.at | |
}); | |
li = this.context.callbacks("tplEval").call(this.context, tpl, item, "onDisplay"); | |
$li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text)); | |
$li.data("item-data", item); | |
$ul.append($li); | |
} | |
this.show(); | |
if (this.context.getOpt('highlightFirst')) { | |
return $ul.find("li:first").addClass("cur"); | |
} | |
}; | |
return View; | |
})(); | |
var Api; | |
Api = { | |
load: function(at, data) { | |
var c; | |
if (c = this.controller(at)) { | |
return c.model.load(data); | |
} | |
}, | |
isSelecting: function() { | |
var ref; | |
return !!((ref = this.controller()) != null ? ref.view.visible() : void 0); | |
}, | |
hide: function() { | |
var ref; | |
return (ref = this.controller()) != null ? ref.view.hide() : void 0; | |
}, | |
reposition: function() { | |
var c; | |
if (c = this.controller()) { | |
return c.view.reposition(c.rect()); | |
} | |
}, | |
setIframe: function(iframe, asRoot) { | |
this.setupRootElement(iframe, asRoot); | |
return null; | |
}, | |
run: function() { | |
return this.dispatch(); | |
}, | |
destroy: function() { | |
this.shutdown(); | |
return this.$inputor.data('atwho', null); | |
} | |
}; | |
$.fn.atwho = function(method) { | |
var _args, result; | |
_args = arguments; | |
result = null; | |
this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function() { | |
var $this, app; | |
if (!(app = ($this = $(this)).data("atwho"))) { | |
$this.data('atwho', (app = new App(this))); | |
} | |
if (typeof method === 'object' || !method) { | |
return app.reg(method.at, method); | |
} else if (Api[method] && app) { | |
return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1)); | |
} else { | |
return $.error("Method " + method + " does not exist on jQuery.atwho"); | |
} | |
}); | |
if (result != null) { | |
return result; | |
} else { | |
return this; | |
} | |
}; | |
$.fn.atwho["default"] = { | |
at: void 0, | |
alias: void 0, | |
data: null, | |
displayTpl: "<li>${name}</li>", | |
insertTpl: "${atwho-at}${name}", | |
headerTpl: null, | |
callbacks: DEFAULT_CALLBACKS, | |
functionOverrides: {}, | |
searchKey: "name", | |
suffix: void 0, | |
hideWithoutSuffix: false, | |
startWithSpace: true, | |
acceptSpaceBar: false, | |
highlightFirst: true, | |
limit: 5, | |
maxLen: 20, | |
minLen: 0, | |
displayTimeout: 300, | |
delay: null, | |
spaceSelectsMatch: false, | |
tabSelectsMatch: true, | |
editableAtwhoQueryAttrs: {}, | |
scrollDuration: 150, | |
suspendOnComposing: true, | |
lookUpOnClick: true | |
}; | |
$.fn.atwho.debug = false; | |
})); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment