/* Implement Github like autocomplete mentions http://ichord.github.com/At.js Copyright (c) 2013 chord.luo@gmail.com Licensed under the MIT license. */ (function() { var __slice = [].slice; (function(factory) { if (typeof define === 'function' && define.amd) { return define(['jquery'], factory); } else { return factory(window.jQuery); } })(function($) { var $CONTAINER, Api, App, Controller, DEFAULT_CALLBACKS, DEFAULT_TPL, KEY_CODE, Model, View; App = (function() { function App(inputor) { this.current_flag = null; this.controllers = {}; this.$inputor = $(inputor); this.listen(); } App.prototype.controller = function(key) { return this.controllers[key || this.current_flag]; }; App.prototype.set_context_for = function(key) { this.current_flag = key; return this; }; App.prototype.reg = function(flag, setting) { var controller, _base; controller = (_base = this.controllers)[flag] || (_base[flag] = new Controller(this, flag)); if (setting.alias) { this.controllers[setting.alias] = controller; } controller.init(setting); return this; }; App.prototype.listen = function() { var _this = this; return this.$inputor.on('keyup.atwho', function(e) { return _this.on_keyup(e); }).on('keydown.atwho', function(e) { return _this.on_keydown(e); }).on('scroll.atwho', function(e) { var _ref; return (_ref = _this.controller()) != null ? _ref.view.hide() : void 0; }).on('blur.atwho', function(e) { var c; if (c = _this.controller()) { return c.view.hide(c.get_opt("display_timeout")); } }); }; App.prototype.dispatch = function() { var _this = this; return $.map(this.controllers, function(c) { if (c.look_up()) { return _this.set_context_for(c.key); } }); }; App.prototype.on_keyup = 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: $.noop(); break; default: this.dispatch(); } }; App.prototype.on_keydown = function(e) { var view, _ref; 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(); break; case KEY_CODE.UP: e.preventDefault(); view.prev(); break; case KEY_CODE.DOWN: e.preventDefault(); view.next(); break; case KEY_CODE.TAB: case KEY_CODE.ENTER: if (!view.visible()) { return; } e.preventDefault(); view.choose(); break; default: $.noop(); } }; return App; })(); Controller = (function() { var uuid, _uuid; _uuid = 0; uuid = function() { return _uuid += 1; }; function Controller(app, key) { this.app = app; this.key = key; this.$inputor = this.app.$inputor; this.id = this.$inputor[0].id || uuid(); this.setting = null; this.query = null; this.pos = 0; $CONTAINER.append(this.$el = $("
")); this.model = new Model(this); this.view = new View(this); } Controller.prototype.init = function(setting) { this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting); return this.model.reload(this.setting.data); }; Controller.prototype.super_call = function() { var args, func_name; func_name = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; try { return DEFAULT_CALLBACKS[func_name].apply(this, args); } catch (error) { return $.error("" + error + " Or maybe At.js doesn't have function " + func_name); } }; Controller.prototype.trigger = function(name, data) { var alias, event_name; data.push(this); alias = this.get_opt('alias'); event_name = alias ? "" + name + "-" + alias + ".atwho" : "" + name + ".atwho"; return this.$inputor.trigger(event_name, data); }; Controller.prototype.callbacks = function(func_name) { return this.get_opt("callbacks")[func_name] || DEFAULT_CALLBACKS[func_name]; }; Controller.prototype.get_opt = function(key, default_value) { try { return this.setting[key]; } catch (e) { return null; } }; Controller.prototype.catch_query = function() { var caret_pos, content, end, query, start, subtext; content = this.$inputor.val(); caret_pos = this.$inputor.caret('pos'); subtext = content.slice(0, caret_pos); query = this.callbacks("matcher").call(this, this.key, subtext); if (typeof query === "string" && query.length <= this.get_opt('max_len', 20)) { start = caret_pos - query.length; end = start + query.length; this.pos = start; query = { 'text': query.toLowerCase(), 'head_pos': start, 'end_pos': end }; this.trigger("matched", [this.key, query.text]); } else { this.view.hide(); } return this.query = query; }; Controller.prototype.rect = function() { var c, scale_bottom; c = this.$inputor.caret('offset', this.pos - 1); scale_bottom = document.selection ? 0 : 2; return { left: c.left, top: c.top, bottom: c.top + c.height + scale_bottom }; }; Controller.prototype.insert = function(str) { var $inputor, flag_len, source, start_str, text; $inputor = this.$inputor; str = '' + str; source = $inputor.val(); flag_len = this.get_opt("display_flag") ? 0 : this.key.length; start_str = source.slice(0, (this.query['head_pos'] || 0) - flag_len); text = "" + start_str + str + " " + (source.slice(this.query['end_pos'] || 0)); $inputor.val(text); $inputor.caret('pos', start_str.length + str.length + 1); return $inputor.change(); }; Controller.prototype.render_view = function(data) { var search_key; search_key = this.get_opt("search_key"); data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), search_key); return this.view.render(data.slice(0, this.get_opt('limit'))); }; Controller.prototype.look_up = function() { var query, _callback; if (!(query = this.catch_query())) { return; } _callback = function(data) { if (data && data.length > 0) { return this.render_view(data); } else { return this.view.hide(); } }; this.model.query(query.text, $.proxy(_callback, this)); return query; }; return Controller; })(); Model = (function() { var _storage; _storage = {}; function Model(context) { this.context = context; this.key = this.context.key; } Model.prototype.saved = function() { return this.fetch() > 0; }; Model.prototype.query = function(query, callback) { var data, search_key, _ref; data = this.fetch(); search_key = this.context.get_opt("search_key"); callback(data = this.context.callbacks('filter').call(this.context, query, data, search_key)); if (!(data && data.length > 0)) { return (_ref = this.context.callbacks('remote_filter')) != null ? _ref.call(this.context, query, callback) : void 0; } }; Model.prototype.fetch = function() { return _storage[this.key] || []; }; Model.prototype.save = function(data) { return _storage[this.key] = this.context.callbacks("before_save").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) { var _this = this; if (typeof data === "string") { return $.ajax(data, { dataType: "json" }).done(function(data) { return _this.save(data); }); } else { return this.save(data); } }; return Model; })(); View = (function() { function View(context) { this.context = context; this.key = this.context.key; this.id = this.context.get_opt("alias") || ("at-view-" + (this.key.charCodeAt(0))); this.$el = $("