PageSourceSearch

https://forum.nikonrumors.com/js/library/jquery.atwho.js?v=2.3.1

js nikonrumors.com collected 2026-10-02 03:28:49 UTC 35,430 bytes, 1,140 lines download raw bytes

1/*
2  Implement Github like autocomplete mentions
3  http://ichord.github.com/At.js
4
5  Copyright (c) 2013 [email protected]
6  Licensed under the MIT license.
7*/
8
9
10/*
11本插件操作 textarea 或者 input 内的插入符
12只实现了获得插入符在文本框中的位置,我设置
13插入符的位置.
14*/
15
16/**
17 * --------------------
18 * Vanilla Forums NOTE:
19 * --------------------
20 *
21 * This file has been highly modified to work with iframes, as well
22 * as custom username handling with quotation marks and spaces for Vanilla.
23 * Do not just replace with a more current version. At the time of
24 * development there was no support for iframes, or spaces in names.
25 * This may have changed, so if you do decide to upgrade this library,
26 * you're going to have to update the code that uses this library as well.
27 * It's all wrapped up in a function called `atCompleteInit`.
28 */
29
30
31(function() {
32  (function(factory) {
33    if (typeof define === 'function' && define.amd) {
34      return define(['jquery'], factory);
35    } else {
36      return factory(window.jQuery);
37    }
38  })(function($) {
39    "use strict";
40    ////var EditableCaret, InputCaret, Mirror, Utils, methods, pluginName;
41    var EditableCaret, InputCaret, Mirror, Utils, methods, pluginName, cWin;
42
43    pluginName = 'caret';
44    EditableCaret = (function() {
45      function EditableCaret($inputor) {
46        this.$inputor = $inputor;
47        this.domInputor = this.$inputor[0];
48      }
49
50      EditableCaret.prototype.setPos = function(pos) {
51        return this.domInputor;
52      };
53
54      EditableCaret.prototype.getIEPosition = function() {
55        return $.noop();
56      };
57
58      EditableCaret.prototype.getPosition = function() {
59        return $.noop();
60      };
61
62      EditableCaret.prototype.getOldIEPos = function() {
63        var preCaretTextRange, textRange;
64
65        textRange = document.selection.createRange();
66        preCaretTextRange = document.body.createTextRange();
67        preCaretTextRange.moveToElementText(this.domInputor);
68        preCaretTextRange.setEndPoint("EndToEnd", textRange);
69        return preCaretTextRange.text.length;
70      };
71
72      EditableCaret.prototype.getPos = function() {
73        var clonedRange, pos, range;
74
75        if (range = this.range()) {
76          clonedRange = range.cloneRange();
77          clonedRange.selectNodeContents(this.domInputor);
78          clonedRange.setEnd(range.endContainer, range.endOffset);
79          pos = clonedRange.toString().length;
80          clonedRange.detach();
81          return pos;
82        } else if (document.selection) {
83          return this.getOldIEPos();
84        }
85      };
86
87      EditableCaret.prototype.getOldIEOffset = function() {
88        var range, rect;
89
90        range = document.selection.createRange().duplicate();
91        range.moveStart("character", -1);
92        rect = range.getBoundingClientRect();
93        return {
94          height: rect.bottom - rect.top,
95          left: rect.left,
96          top: rect.top
97        };
98      };
99
100      EditableCaret.prototype.getOffset = function(pos) {
101        var clonedRange, offset, range, rect;
102
103        offset = null;
104        ////if (window.getSelection && (range = this.range())) {
105        if (cWin.getSelection && (range = this.range())) {
106          if (range.endOffset - 1 < 0) {
107            return null;
108          }
109          clonedRange = range.cloneRange();
110          clonedRange.setStart(range.endContainer, range.endOffset - 1);
111          clonedRange.setEnd(range.endContainer, range.endOffset);
112          rect = clonedRange.getBoundingClientRect();
113          offset = {
114            height: rect.height,
115            left: rect.left + rect.width,
116            top: rect.top
117          };
118          clonedRange.detach();
119          offset;
120        } else if (document.selection) {
121          this.getOldIEOffset();
122        }
123        return Utils.adjustOffset(offset, this.$inputor);
124      };
125
126      EditableCaret.prototype.range = function() {
127        var sel;
128
129        ////if (!window.getSelection) {
130        if (!cWin.getSelection) {
131          return;
132        }
133        ////sel = window.getSelection();
134        sel = cWin.getSelection();
135        if (sel.rangeCount > 0) {
136          return sel.getRangeAt(0);
137        } else {
138          return null;
139        }
140      };
141
142      return EditableCaret;
143
144    })();
145    InputCaret = (function() {
146      function InputCaret($inputor) {
147        this.$inputor = $inputor;
148        this.domInputor = this.$inputor[0];
149      }
150
151      InputCaret.prototype.getIEPos = function() {
152        var endRange, inputor, len, normalizedValue, pos, range, textInputRange;
153
154        inputor = this.domInputor;
155        range = document.selection.createRange();
156        pos = 0;
157        if (range && range.parentElement() === inputor) {
158          normalizedValue = inputor.value.replace(/\r\n/g, "\n");
159          len = normalizedValue.length;
160          textInputRange = inputor.createTextRange();
161          textInputRange.moveToBookmark(range.getBookmark());
162          endRange = inputor.createTextRange();
163          endRange.collapse(false);
164          if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
165            pos = len;
166          } else {
167            pos = -textInputRange.moveStart("character", -len);
168          }
169        }
170        return pos;
171      };
172
173      InputCaret.prototype.getPos = function() {
174        if (document.selection) {
175          return this.getIEPos();
176        } else {
177          return this.domInputor.selectionStart;
178        }
179      };
180
181      InputCaret.prototype.setPos = function(pos) {
182        var inputor, range;
183
184        inputor = this.domInputor;
185        if (document.selection) {
186          range = inputor.createTextRange();
187          range.move("character", pos);
188          range.select();
189        } else if (inputor.setSelectionRange) {
190          inputor.setSelectionRange(pos, pos);
191        }
192        return inputor;
193      };
194
195      InputCaret.prototype.getIEOffset = function(pos) {
196        var h, range, textRange, x, y;
197
198        textRange = this.domInputor.createTextRange();
199        if (pos) {
200          textRange.move('character', pos);
201        } else {
202          range = document.selection.createRange();
203          textRange.moveToBookmark(range.getBookmark());
204        }
205        x = textRange.boundingLeft;
206        y = textRange.boundingTop;
207        h = textRange.boundingHeight;
208        return {
209          left: x,
210          top: y,
211          height: h
212        };
213      };
214
215      InputCaret.prototype.getOffset = function(pos) {
216        var $inputor, offset, position;
217
218        $inputor = this.$inputor;
219        if (document.selection) {
220          return Utils.adjustOffset(this.getIEOffset(pos), $inputor);
221        } else {
222          offset = $inputor.offset();
223          position = this.getPosition(pos);
224          return offset = {
225            left: offset.left + position.left,
226            top: offset.top + position.top,
227            height: position.height
228          };
229        }
230      };
231
232      InputCaret.prototype.getPosition = function(pos) {
233        var $inputor, at_rect, format, html, mirror, start_range;
234
235        $inputor = this.$inputor;
236        format = function(value) {
237          return value.replace(/</g, '&lt').replace(/>/g, '&gt').replace(/`/g, '&#96').replace(/"/g, '&quot').replace(/\r\n|\r|\n/g, "<br />");
238        };
239        if (pos === void 0) {
240          pos = this.getPos();
241        }
242        start_range = $inputor.val().slice(0, pos);
243        html = "<span>" + format(start_range) + "</span>";
244        html += "<span id='caret'>|</span>";
245        mirror = new Mirror($inputor);
246        return at_rect = mirror.create(html).rect();
247      };
248
249      InputCaret.prototype.getIEPosition = function(pos) {
250        var h, inputorOffset, offset, x, y;
251
252        offset = this.getIEOffset(pos);
253        inputorOffset = this.$inputor.offset();
254        x = offset.left - inputorOffset.left;
255        y = offset.top - inputorOffset.top;
256        h = offset.height;
257        return {
258          left: x,
259          top: y,
260          height: h
261        };
262      };
263
264      return InputCaret;
265
266    })();
267    Mirror = (function() {
268      Mirror.prototype.css_attr = ["overflowY", "height", "width", "paddingTop", "paddingLeft", "paddingRight", "paddingBottom", "marginTop", "marginLeft", "marginRight", "marginBottom", "fontFamily", "borderStyle", "borderWidth", "wordWrap", "fontSize", "lineHeight", "overflowX", "text-align"];
269
270      function Mirror($inputor) {
271        this.$inputor = $inputor;
272      }
273
274      Mirror.prototype.mirrorCss = function() {
275        var css,
276          _this = this;
277
278        css = {
279          position: 'absolute',
280          left: -9999,
281          top: 0,
282          zIndex: -20000,
283          'white-space': 'pre-wrap'
284        };
285        $.each(this.css_attr, function(i, p) {
286          return css[p] = _this.$inputor.css(p);
287        });
288        return css;
289      };
290
291      Mirror.prototype.create = function(html) {
292        this.$mirror = $('<div></div>');
293        this.$mirror.css(this.mirrorCss());
294        this.$mirror.html(html);
295        this.$inputor.after(this.$mirror);
296        return this;
297      };
298
299      Mirror.prototype.rect = function() {
300        var $flag, pos, rect;
301
302        $flag = this.$mirror.find("#caret");
303        pos = $flag.position();
304        rect = {
305          left: pos.left,
306          top: pos.top,
307          height: $flag.height()
308        };
309        this.$mirror.remove();
310        return rect;
311      };
312
313      return Mirror;
314
315    })();
316    Utils = {
317      adjustOffset: function(offset, $inputor) {
318        if (!offset) {
319          return;
320        }
321        offset.top += $(window).scrollTop() + $inputor.scrollTop();
322        offset.left += +$(window).scrollLeft() + $inputor.scrollLeft();
323        return offset;
324      },
325      contentEditable: function($inputor) {
326        return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true');
327      }
328    };
329    methods = {
330      pos: function(pos) {
331        if (pos) {
332          return this.setPos(pos);
333        } else {
334          return this.getPos();
335        }
336      },
337      position: function(pos) {
338        if (document.selection) {
339          return this.getIEPosition(pos);
340        } else {
341          return this.getPosition(pos);
342        }
343      },
344      offset: function(pos) {
345        return this.getOffset(pos);
346      }
347    };
348    ////$.fn.caret = function(method) {
349    $.fn.caret = function(method, aWin) {
350      var caret;
351
352      ////
353      cWin = aWin;
354
355      caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this);
356      if (methods[method]) {
357        ////return methods[method].apply(caret, Array.prototype.slice.call(arguments, 1));
358        ///////return methods[method].apply(caret, Array.prototype.slice.call(arguments, method == 'pos' ? 2 : 1));
359        return methods[method].apply(caret, Array.prototype.slice.call(arguments, 2));
360      } else {
361        return $.error("Method " + method + " does not exist on jQuery.caret");
362      }
363    };
364    $.fn.caret.EditableCaret = EditableCaret;
365    $.fn.caret.InputCaret = InputCaret;
366    $.fn.caret.Utils = Utils;
367    return $.fn.caret.apis = methods;
368  });
369
370}).call(this);
371
372
373/*
374  Implement Github like autocomplete mentions
375  http://ichord.github.com/At.js
376
377  Copyright (c) 2013 [email protected]
378  Licensed under the MIT license.
379*/
380
381
382(function() {
383  var __slice = [].slice;
384
385  (function(factory) {
386    if (typeof define === 'function' && define.amd) {
387      return define(['jquery'], factory);
388    } else {
389      return factory(window.jQuery);
390    }
391  })(function($) {
392    var $CONTAINER, Api, App, Atwho, Controller, DEFAULT_CALLBACKS, KEY_CODE, Model, View;
393    App = (function() {
394
395      function App(inputor) {
396        this.current_flag = null;
397        this.controllers = {};
398        this.$inputor = $(inputor);
399        this.listen();
400      }
401
402      App.prototype.controller = function(at) {
403        return this.controllers[at || this.current_flag];
404      };
405
406      App.prototype.set_context_for = function(at) {
407        this.current_flag = at;
408        return this;
409      };
410
411      App.prototype.reg = function(flag, setting) {
412        var controller, _base;
413        controller = (_base = this.controllers)[flag] || (_base[flag] = new Controller(this, flag));
414        if (setting.alias) {
415          this.controllers[setting.alias] = controller;
416        }
417        controller.init(setting);
418        return this;
419      };
420
421      App.prototype.listen = function() {
422        var _this = this;
423        return this.$inputor.on('keyup.atwho', function(e) {
424          return _this.on_keyup(e);
425        }).on('keydown.atwho', function(e) {
426          return _this.on_keydown(e);
427        }).on('scroll.atwho', function(e) {
428          var _ref;
429          return (_ref = _this.controller()) != null ? _ref.view.hide() : void 0;
430        }).on('blur.atwho', function(e) {
431          var c;
432          if (c = _this.controller()) {
433            return c.view.hide(c.get_opt("display_timeout"));
434          }
435        });
436      };
437
438      App.prototype.dispatch = function() {
439        var _this = this;
440        return $.map(this.controllers, function(c) {
441          if (c.look_up()) {
442            return _this.set_context_for(c.at);
443          }
444        });
445      };
446
447      App.prototype.on_keyup = function(e) {
448        var _ref;
449        switch (e.keyCode) {
450          case KEY_CODE.ESC:
451          case KEY_CODE.TAB:
452          case KEY_CODE.ENTER:
453            e.preventDefault();
454            if ((_ref = this.controller()) != null) {
455              _ref.view.hide();
456            }
457            break;
458          case KEY_CODE.DOWN:
459          case KEY_CODE.UP:
460            $.noop();
461            break;
462          default:
463            this.dispatch();
464        }
465      };
466
467      App.prototype.on_keydown = function(e) {
468        var view, _ref;
469        view = (_ref = this.controller()) != null ? _ref.view : void 0;
470        if (!(view && view.visible())) {
471          return;
472        }
473        switch (e.keyCode) {
474          case KEY_CODE.ESC:
475            e.preventDefault();
476            view.hide();
477            break;
478          case KEY_CODE.UP:
479            e.preventDefault();
480            view.prev();
481            break;
482          case KEY_CODE.DOWN:
483            e.preventDefault();
484            view.next();
485            break;
486          case KEY_CODE.TAB:
487          case KEY_CODE.ENTER:
488            if (!view.visible()) {
489              return;
490            }
491            view.choose(e);
492            break;
493          default:
494            $.noop();
495        }
496      };
497
498      return App;
499
500    })();
501    Controller = (function() {
502      var uuid, _uuid;
503
504      _uuid = 0;
505
506      uuid = function() {
507        return _uuid += 1;
508      };
509
510      function Controller(app, at) {
511        this.app = app;
512        this.at = at;
513        this.$inputor = this.app.$inputor;
514        this.id = this.$inputor[0].id || uuid();
515        this.setting = null;
516        this.query = null;
517        this.pos = 0;
518        this.cur_rect = null;
519        this.range = null;
520        $CONTAINER.append(this.$el = $("<div id='atwho-ground-" + this.id + "'></div>"));
521        this.model = new Model(this);
522        this.view = new View(this);
523      }
524
525      Controller.prototype.init = function(setting) {
526        this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting);
527        this.view.init();
528        return this.model.reload(this.setting.data);
529      };
530
531      Controller.prototype.call_default = function() {
532        var args, func_name;
533        func_name = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
534        try {
535          return DEFAULT_CALLBACKS[func_name].apply(this, args);
536        } catch (error) {
537          return $.error("" + error + " Or maybe At.js doesn't have function " + func_name);
538        }
539      };
540
541      Controller.prototype.trigger = function(name, data) {
542        var alias, event_name;
543        data.push(this);
544        alias = this.get_opt('alias');
545        event_name = alias ? "" + name + "-" + alias + ".atwho" : "" + name + ".atwho";
546        return this.$inputor.trigger(event_name, data);
547      };
548
549      Controller.prototype.callbacks = function(func_name) {
550        return this.get_opt("callbacks")[func_name] || DEFAULT_CALLBACKS[func_name];
551      };
552
553      Controller.prototype.get_opt = function(at, default_value) {
554        try {
555          return this.setting[at];
556        } catch (e) {
557          return null;
558        }
559      };
560
561      Controller.prototype.content = function() {
562        var result = {
563          content: null,
564          offset: 0
565        };
566
567        if (this.$inputor.is('textarea, input')) {
568          result.content = this.$inputor.val();
569        } else {
570          var textNode = $(document.createElement('div'));
571          var html = this.$inputor.html();
572          var breaks = /<br(\s+)?(\/)?>/g;
573          result.offset = html.match(breaks) ? html.match(breaks).length : 0;
574          textNode.html(html.replace(breaks, "\n"));
575          result.content = textNode.text();
576        }
577
578        return result;
579      };
580
581      Controller.prototype.catch_query = function() {
582        var caret_pos, contents, end, query, start, subtext;
583        contents = this.content();
584        ////caret_pos = this.$inputor.caret('pos');
585        caret_pos = this.$inputor.caret('pos', this.setting.cWindow) + contents.offset;
586        subtext = contents.content.slice(0, caret_pos);
587        query = this.callbacks("matcher").call(this, this.at, subtext, this.get_opt('start_with_space'));
588        if (typeof query === "string" && query.length <= this.get_opt('max_len', 20)) {
589          start = caret_pos - query.length;
590          end = start + query.length;
591          this.pos = start;
592          query = {
593            'text': query.toLowerCase(),
594            'head_pos': start,
595            'end_pos': end
596          };
597          this.trigger("matched", [this.at, query.text]);
598        } else {
599          this.view.hide();
600        }
601        return this.query = query;
602      };
603
604      Controller.prototype.rect = function() {
605        var c, scale_bottom;
606        /////if (!(c = this.$inputor.caret('offset', this.pos - 1))) {
607        if (!(c = this.$inputor.caret('offset', this.setting.cWindow, this.pos - 1))) {
608          return;
609        }
610        if (this.$inputor.attr('contentEditable') === 'true') {
611          c = (this.cur_rect || (this.cur_rect = c)) || c;
612        }
613        scale_bottom = document.selection ? 0 : 2;
614        return {
615          left: c.left,
616          top: c.top,
617          bottom: c.top + c.height + scale_bottom
618        };
619      };
620
621      Controller.prototype.reset_rect = function() {
622        if (this.$inputor.attr('contentEditable') === 'true') {
623          return this.cur_rect = null;
624        }
625      };
626
627      Controller.prototype.mark_range = function() {
628        return this.range = this.get_range() || this.get_ie_range();
629      };
630
631      Controller.prototype.clear_range = function() {
632        return this.range = null;
633      };
634
635      Controller.prototype.get_range = function() {
636        ////return this.range || (window.getSelection ? window.getSelection().getRangeAt(0) : void 0);
637
638
639         var thisWin = this.setting.cWindow;
640         //////return this.range || (thisWin.getSelection ? thisWin.getSelection().getRangeAt(0) : void 0);
641         return thisWin.getSelection ? thisWin.getSelection().getRangeAt(0) : (this.range || void 0);
642
643      };
644
645      Controller.prototype.get_ie_range = function() {
646        return this.range || (document.selection ? document.selection.createRange() : void 0);
647      };
648
649      Controller.prototype.insert_content_for = function($li) {
650        var data, data_value, tpl;
651        data_value = $li.data('value');
652        tpl = this.get_opt('insert_tpl');
653        if (this.$inputor.is('textarea, input') || !tpl) {
654          return data_value;
655        }
656        data = $.extend({}, $li.data('item-data'), {
657          'atwho-data-value': data_value,
658          'atwho-at': this.at
659        });
660        return this.callbacks("tpl_eval").call(this, tpl, data);
661      };
662
663      Controller.prototype.insert = function(content, $li) {
664        ////var $inputor, $insert_node, class_name, content_node, insert_node, pos, range, sel, source, start_str, text;
665
666        //////////var $inputor, $insert_node, class_name, content_node, insert_node, pos, range, sel, source, start_str, text, thisWin;
667        var $inputor, $insert_node, class_name, content_editable, content_node, insert_node, pos, range, sel, source, start_str, text;
668        $inputor = this.$inputor;
669
670        if ($inputor.attr('contentEditable') === 'true') {
671          //////////
672          content_editable = '' + /firefox/i.test(navigator.userAgent);
673
674          //class_name = "atwho-view-flag atwho-view-flag-" + (this.get_opt('alias') || this.at);
675          class_name = "vanilla-mention-" + (this.get_opt('alias') || this.at);
676          //////////content_node = "" + content + "<span contenteditable='false'>&nbsp;<span>";
677          //////////insert_node = "<span contenteditable='false' class='" + class_name + "'>" + content_node + "</span>";
678          //content_node = ("" + content + "<span contenteditable='") + content_editable + "'>&nbsp;<span>";
679          content_node = "" + content + "&nbsp;";
680          //content_node = "" + content;
681
682          //insert_node = "<span contenteditable='" + content_editable + ("' class='" + class_name + "'>" + content_node + "</span>");
683          insert_node = '<span class="' + class_name + '">' + content_node + '</span>';
684
685          $insert_node = $(insert_node).data('atwho-data-item', $li.data('item-data'));
686          if (document.selection) {
687            $insert_node = $("<span contenteditable='true'></span>").html($insert_node);
688          }
689        }
690
691        if ($inputor.is('textarea, input')) {
692          content = '' + content;
693          source = $inputor.val();
694          start_str = source.slice(0, Math.max(this.query.head_pos - this.at.length, 0));
695          text = "" + start_str + content + " " + (source.slice(this.query['end_pos'] || 0));
696          $inputor.val(text);
697          ////$inputor.caret('pos', start_str.length + content.length + 1);
698          $inputor.caret('pos', this.setting.cWindow, start_str.length + content.length + 1);
699        } else if (range = this.get_range()) {
700          ////
701          thisWin = this.setting.cWindow;
702
703          pos = range.startOffset - (this.query.end_pos - this.query.head_pos) - this.at.length;
704          range.setStart(range.endContainer, Math.max(pos, 0));
705          range.setEnd(range.endContainer, range.endOffset);
706          range.deleteContents();
707          range.insertNode(document.createTextNode(content + " "));
708          //range.insertNode($insert_node[0]);
709          range.collapse(false);
710
711          ////sel = window.getSelection();
712          sel = thisWin.getSelection();
713
714          sel.removeAllRanges();
715          sel.addRange(range);
716        } else if (range = this.get_ie_range()) {
717          range.moveStart('character', this.query.end_pos - this.query.head_pos - this.at.length);
718          ///////
719          range.pasteHTML($insert_node[0]);
720          range.collapse(false);
721          range.select();
722        }
723        $inputor.focus();
724        return $inputor.change();
725      };
726
727      Controller.prototype.render_view = function(data) {
728        var search_key;
729        search_key = this.get_opt("search_key");
730        data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), search_key);
731        return this.view.render(data.slice(0, this.get_opt('limit')));
732      };
733
734      Controller.prototype.look_up = function() {
735        var query, _callback;
736        if (!(query = this.catch_query())) {
737          return;
738        }
739        _callback = function(data) {
740          if (data && data.length > 0) {
741            return this.render_view(data);
742          } else {
743            return this.view.hide();
744          }
745        };
746        this.model.query(query.text, $.proxy(_callback, this));
747        return query;
748      };
749
750      return Controller;
751
752    })();
753    Model = (function() {
754      var _storage;
755
756      _storage = {};
757
758      function Model(context) {
759        this.context = context;
760        this.at = this.context.at;
761      }
762
763      Model.prototype.saved = function() {
764        return this.fetch() > 0;
765      };
766
767      Model.prototype.query = function(query, callback) {
768        var data, search_key, _ref;
769        data = this.fetch();
770        search_key = this.context.get_opt("search_key");
771        callback(data = this.context.callbacks('filter').call(this.context, query, data, search_key));
772        if (!(data && data.length > 0)) {
773          return (_ref = this.context.callbacks('remote_filter')) != null ? _ref.call(this.context, query, callback) : void 0;
774        }
775      };
776
777      Model.prototype.fetch = function() {
778        return _storage[this.at] || [];
779      };
780
781      Model.prototype.save = function(data) {
782        return _storage[this.at] = this.context.callbacks("before_save").call(this.context, data || []);
783      };
784
785      Model.prototype.load = function(data) {
786        if (!(this.saved() || !data)) {
787          return this._load(data);
788        }
789      };
790
791      Model.prototype.reload = function(data) {
792        return this._load(data);
793      };
794
795      Model.prototype._load = function(data) {
796        var _this = this;
797        if (typeof data === "string") {
798          return $.ajax(data, {
799            dataType: "json"
800          }).done(function(data) {
801            return _this.save(data);
802          });
803        } else {
804          return this.save(data);
805        }
806      };
807
808      return Model;
809
810    })();
811    View = (function() {
812
813      function View(context) {
814        this.context = context;
815        this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>");
816        this.timeout_id = null;
817        this.context.$el.append(this.$el);
818        this.bind_event();
819      }
820
821      View.prototype.init = function() {
822        var id;
823        id = this.context.get_opt("alias") || this.context.at.charCodeAt(0);
824        return this.$el.attr({
825          'id': "at-view-" + id
826        });
827      };
828
829      View.prototype.bind_event = function() {
830        var $menu,
831          _this = this;
832        $menu = this.$el.find('ul');
833        $menu.on('mouseenter.atwho-view', 'li', function(e) {
834          $menu.find('.cur').removeClass('cur');
835          return $(e.currentTarget).addClass('cur');
836        }).on('click', function(e) {
837          _this.choose(e);
838          return e.preventDefault();
839        });
840        return this.$el.on('mouseenter.atwho-view', 'ul', function(e) {
841          return _this.context.mark_range();
842        }).on('mouseleave.atwho-view', 'ul', function(e) {
843          return _this.context.clear_range();
844        });
845      };
846
847      View.prototype.visible = function() {
848        return this.$el.is(":visible");
849      };
850
851      View.prototype.choose = function(event) {
852        var $li, content;
853        if (($li = this.$el.find(".cur")).length) {
854          event.preventDefault();
855          content = this.context.insert_content_for($li);
856          this.context.insert(this.context.callbacks("before_insert").call(this.context, content, $li), $li);
857          this.context.trigger("inserted", [$li]);
858          return this.hide();
859        }
860      };
861
862      View.prototype.reposition = function(rect) {
863        ////var offset;
864        ////if (rect.bottom + this.$el.height() - $(window).scrollTop() > $(window).height()) {
865
866        var offset;
867
868        ////
869        // Make sure the non-iframe version still references window.
870        var thisWin = (this.context.setting.cWindow)
871          ? null
872          : window;
873
874        if (rect.bottom + this.$el.height() - $(thisWin).scrollTop() > $(thisWin).height()) {
875          rect.bottom = rect.top - this.$el.height();
876        }
877        offset = {
878          left: rect.left,
879          top: rect.bottom
880        };
881        this.$el.offset(offset);
882        return this.context.trigger("reposition", [offset]);
883      };
884
885      View.prototype.next = function() {
886        var cur, next;
887        cur = this.$el.find('.cur').removeClass('cur');
888        next = cur.next();
889        if (!next.length) {
890          next = this.$el.find('li:first');
891        }
892        return next.addClass('cur');
893      };
894
895      View.prototype.prev = function() {
896        var cur, prev;
897        cur = this.$el.find('.cur').removeClass('cur');
898        prev = cur.prev();
899        if (!prev.length) {
900          prev = this.$el.find('li:last');
901        }
902        return prev.addClass('cur');
903      };
904
905      View.prototype.show = function() {
906        var rect;
907        if (!this.visible()) {
908          this.$el.show();
909        }
910        if (rect = this.context.rect()) {
911          return this.reposition(rect);
912        }
913      };
914
915      View.prototype.hide = function(time) {
916        var callback,
917          _this = this;
918        if (isNaN(time && this.visible())) {
919          this.context.reset_rect();
920          return this.$el.hide();
921        } else {
922          callback = function() {
923            return _this.hide();
924          };
925          clearTimeout(this.timeout_id);
926          return this.timeout_id = setTimeout(callback, time);
927        }
928      };
929
930      View.prototype.render = function(list) {
931        var $li, $ul, item, li, tpl, _i, _len;
932        if (!$.isArray(list || list.length <= 0)) {
933          this.hide();
934          return;
935        }
936        this.$el.find('ul').empty();
937        $ul = this.$el.find('ul');
938        tpl = this.context.get_opt('tpl');
939        for (_i = 0, _len = list.length; _i < _len; _i++) {
940          item = list[_i];
941          item = $.extend({}, item, {
942            'atwho-at': this.context.at
943          });
944          li = this.context.callbacks("tpl_eval").call(this.context, tpl, item);
945          $li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text));
946          $li.data("item-data", item);
947          $ul.append($li);
948        }
949        this.show();
950        return $ul.find("li:first").addClass("cur");
951      };
952
953      return View;
954
955    })();
956    KEY_CODE = {
957      DOWN: 40,
958      UP: 38,
959      ESC: 27,
960      TAB: 9,
961      ENTER: 13
962    };
963    DEFAULT_CALLBACKS = {
964      before_save: function(data) {
965        var item, _i, _len, _results;
966        if (!$.isArray(data)) {
967          return data;
968        }
969        _results = [];
970        for (_i = 0, _len = data.length; _i < _len; _i++) {
971          item = data[_i];
972          if ($.isPlainObject(item)) {
973            _results.push(item);
974          } else {
975            _results.push({
976              name: item
977            });
978          }
979        }
980        return _results;
981      },
982      matcher: function(flag, subtext, should_start_with_space) {
983        var match, regexp;
984        flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
985        if (should_start_with_space) {
986          flag = '(?:^|\\s)' + flag;
987        }
988        regexp = new RegExp(flag + '([A-Za-z0-9_\+\-]*)$|' + flag + '([^\\x00-\\xff]*)$', 'gi');
989        match = regexp.exec(subtext);
990        if (match) {
991          return match[2] || match[1];
992        } else {
993          return null;
994        }
995      },
996      filter: function(query, data, search_key) {
997        var item, _i, _len, _results;
998        _results = [];
999        for (_i = 0, _len = data.length; _i < _len; _i++) {
1000          item = data[_i];
1001          if (~item[search_key].toLowerCase().indexOf(query)) {
1002            _results.push(item);
1003          }
1004        }
1005        return _results;
1006      },
1007      remote_filter: null,
1008      sorter: function(query, items, search_key) {
1009        var item, _i, _len, _results;
1010        if (!query) {
1011          return items;
1012        }
1013        _results = [];
1014        for (_i = 0, _len = items.length; _i < _len; _i++) {
1015          item = items[_i];
1016          item.atwho_order = item[search_key].toLowerCase().indexOf(query);
1017          if (item.atwho_order > -1) {
1018            _results.push(item);
1019          }
1020        }
1021        return _results.sort(function(a, b) {
1022          return a.atwho_order - b.atwho_order;
1023        });
1024      },
1025      tpl_eval: function(tpl, map) {
1026        try {
1027          return tpl.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) {
1028            return map[key];
1029          });
1030        } catch (error) {
1031          return "";
1032        }
1033      },
1034      highlighter: function(li, query) {
1035        var regexp;
1036        if (!query) {
1037          return li;
1038        }
1039        regexp = new RegExp(">\\s*(\\w*)(" + query.replace("+", "\\+") + ")(\\w*)\\s*<", 'ig');
1040        return li.replace(regexp, function(str, $1, $2, $3) {
1041          return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <';
1042        });
1043      },
1044      before_insert: function(value, $li) {
1045        return value;
1046      }
1047    };
1048    Api = {
1049      load: function(at, data) {
1050        var c;
1051        if (c = this.controller(at)) {
1052          return c.model.load(data);
1053        }
1054      },
1055      getInsertedItemsWithIDs: function(at) {
1056        var c, ids, items;
1057        if (!(c = this.controller(at))) {
1058          return [null, null];
1059        }
1060        if (at) {
1061          at = "-" + (c.get_opt('alias') || c.at);
1062        }
1063        ids = [];
1064        items = $.map(this.$inputor.find("span.atwho-view-flag" + (at || "")), function(item) {
1065          var data;
1066          data = $(item).data('atwho-data-item');
1067          if (ids.indexOf(data.id) > -1) {
1068            return;
1069          }
1070          if (data.id) {
1071            ids.push = data.id;
1072          }
1073          return data;
1074        });
1075        return [ids, items];
1076      },
1077      getInsertedItems: function(at) {
1078        return Api.getInsertedItemsWithIDs.apply(this, [at])[1];
1079      },
1080      getInsertedIDs: function(at) {
1081        return Api.getInsertedItemsWithIDs.apply(this, [at])[0];
1082      },
1083      run: function() {
1084        return this.dispatch();
1085      }
1086    };
1087    Atwho = {
1088      init: function(options) {
1089        var $this, app;
1090        app = ($this = $(this)).data("atwho");
1091        if (!app) {
1092          $this.data('atwho', (app = new App(this)));
1093        }
1094        app.reg(options.at, options);
1095        return this;
1096      }
1097    };
1098    $CONTAINER = $("<div id='atwho-container'></div>");
1099    $.fn.atwho = function(method) {
1100      var result, _args;
1101      _args = arguments;
1102      $('body').append($CONTAINER);
1103      result = null;
1104
1105      this.filter('textarea, input, [contenteditable=true]').each(function() {
1106
1107         //console.log('at inloop');
1108         //console.log(this);
1109
1110        var app;
1111        if (typeof method === 'object' || !method) {
1112          return Atwho.init.apply(this, _args);
1113        } else if (Api[method]) {
1114          if (app = $(this).data('atwho')) {
1115            return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1));
1116          }
1117        } else {
1118          return $.error("Method " + method + " does not exist on jQuery.caret");
1119        }
1120      });
1121      return result || this;
1122    };
1123    return $.fn.atwho["default"] = {
1124      at: void 0,
1125      alias: void 0,
1126      data: null,
1127      tpl: "<li data-value='${atwho-at}${name}'>${name}</li>",
1128      insert_tpl: "<span>${atwho-data-value}</span>",
1129      callbacks: DEFAULT_CALLBACKS,
1130      search_key: "name",
1131      start_with_space: true,
1132      limit: 5,
1133      max_len: 20,
1134      display_timeout: 300,
1135      ////
1136      cWindow: window
1137    };
1138  });
1139
1140}).call(this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.