adapter.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. +function ($) {
  2. 'use strict';
  3. var now = Date.now || function () { return new Date().getTime(); };
  4. // Returns a function, that, as long as it continues to be invoked, will not
  5. // be triggered. The function will be called after it stops being called for
  6. // `wait` msec.
  7. //
  8. // This utility function was originally implemented at Underscore.js.
  9. var debounce = function (func, wait) {
  10. var timeout, args, context, timestamp, result;
  11. var later = function () {
  12. var last = now() - timestamp;
  13. if (last < wait) {
  14. timeout = setTimeout(later, wait - last);
  15. } else {
  16. timeout = null;
  17. result = func.apply(context, args);
  18. context = args = null;
  19. }
  20. };
  21. return function () {
  22. context = this;
  23. args = arguments;
  24. timestamp = now();
  25. if (!timeout) {
  26. timeout = setTimeout(later, wait);
  27. }
  28. return result;
  29. };
  30. };
  31. function Adapter () {}
  32. $.extend(Adapter.prototype, {
  33. // Public properties
  34. // -----------------
  35. id: null, // Identity.
  36. completer: null, // Completer object which creates it.
  37. el: null, // Textarea element.
  38. $el: null, // jQuery object of the textarea.
  39. option: null,
  40. // Public methods
  41. // --------------
  42. initialize: function (element, completer, option) {
  43. this.el = element;
  44. this.$el = $(element);
  45. this.id = completer.id + this.constructor.name;
  46. this.completer = completer;
  47. this.option = option;
  48. if (this.option.debounce) {
  49. this._onKeyup = debounce(this._onKeyup, this.option.debounce);
  50. }
  51. this._bindEvents();
  52. },
  53. destroy: function () {
  54. this.$el.off('.' + this.id); // Remove all event handlers.
  55. this.$el = this.el = this.completer = null;
  56. },
  57. // Update the element with the given value and strategy.
  58. //
  59. // value - The selected object. It is one of the item of the array
  60. // which was callbacked from the search function.
  61. // strategy - The Strategy associated with the selected value.
  62. select: function (/* value, strategy */) {
  63. throw new Error('Not implemented');
  64. },
  65. // Returns the caret's relative coordinates from body's left top corner.
  66. getCaretPosition: function () {
  67. var position = this._getCaretRelativePosition();
  68. var offset = this.$el.offset();
  69. // Calculate the left top corner of `this.option.appendTo` element.
  70. var $parent = this.option.appendTo;
  71. if ($parent) {
  72. if (!($parent instanceof $)) { $parent = $($parent); }
  73. var parentOffset = $parent.offsetParent().offset();
  74. offset.top -= parentOffset.top;
  75. offset.left -= parentOffset.left;
  76. }
  77. position.top += offset.top;
  78. position.left += offset.left;
  79. return position;
  80. },
  81. // Focus on the element.
  82. focus: function () {
  83. this.$el.focus();
  84. },
  85. // Private methods
  86. // ---------------
  87. _bindEvents: function () {
  88. this.$el.on('keyup.' + this.id, $.proxy(this._onKeyup, this));
  89. },
  90. _onKeyup: function (e) {
  91. if (this._skipSearch(e)) { return; }
  92. this.completer.trigger(this.getTextFromHeadToCaret(), true);
  93. },
  94. // Suppress searching if it returns true.
  95. _skipSearch: function (clickEvent) {
  96. switch (clickEvent.keyCode) {
  97. case 9: // TAB
  98. case 13: // ENTER
  99. case 40: // DOWN
  100. case 38: // UP
  101. return true;
  102. }
  103. if (clickEvent.ctrlKey) switch (clickEvent.keyCode) {
  104. case 78: // Ctrl-N
  105. case 80: // Ctrl-P
  106. return true;
  107. }
  108. }
  109. });
  110. $.fn.textcomplete.Adapter = Adapter;
  111. }(jQuery);