PageSourceSearch

https://www.texasfootball.com/js/libs/emoji/jquery.emojiarea.js

js texasfootball.com collected 2026-09-24 09:17:10 UTC 24,766 bytes, 773 lines download raw bytes

1/**
2 * emojiarea - A rich textarea control that supports emojis, WYSIWYG-style.
3 * Copyright (c) 2012 DIY Co
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6 * file except in compliance with the License. You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under
10 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 * ANY KIND, either express or implied. See the License for the specific language
12 * governing permissions and limitations under the License.
13 *
14 * @author Brian Reavis <[email protected]>
15 */
16
17/**
18 * This file also contains some modifications by Igor Zhukov in order to add
19 * custom scrollbars to EmojiMenu See keyword `MODIFICATION` in source code.
20 */
21(function($, window, document) {
22
23  var ELEMENT_NODE = 1;
24  var TEXT_NODE = 3;
25  var TAGS_BLOCK = [ 'p', 'div', 'pre', 'form' ];
26  var KEY_ESC = 27;
27  var KEY_TAB = 9;
28  /* Keys that are not intercepted and canceled when the textbox has reached its max length:
29        Backspace, Tab, Ctrl, Alt, Left Arrow, Up Arrow, Right Arrow, Down Arrow, Cmd Key, Delete
30  */
31  var MAX_LENGTH_ALLOWED_KEYS = [8, 9, 17, 18, 37, 38, 39, 40, 91, 46];
32
33  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
34
35  /*
36   * ! MODIFICATION START Options 'spritesheetPath', 'spritesheetDimens',
37   * 'iconSize' added by Andre Staltz.
38   */
39  $.emojiarea = {
40    assetsPath: '',
41    spriteSheetPath: '/img/emoji/emoji_spritesheet_!.png',
42    blankGifPath: '/img/emoji/blank.gif',
43    iconSize : 25,
44    icons : {},
45  };
46  var defaultRecentEmojis = ':joy:,:kissing_heart:,:heart:,:heart_eyes:,:blush:,:grin:,:+1:,:relaxed:,:pensive:,:smile:,:sob:,:kiss:,:unamused:,:flushed:,:stuck_out_tongue_winking_eye:,:see_no_evil:,:wink:,:smiley:,:cry:,:stuck_out_tongue_closed_eyes:,:scream:,:rage:,:smirk:,:disappointed:,:sweat_smile:,:kissing_closed_eyes:,:speak_no_evil:,:relieved:,:grinning:,:yum:,:laughing:,:ok_hand:,:neutral_face:,:confused:'
47      .split(',');
48  /* ! MODIFICATION END */
49
50  $.fn.emojiarea = function(options) {
51    options = $.extend({}, options);
52    return this
53      .each(function () {
54        var originalInput = $(this);
55        if ('contentEditable' in document.body
56          && options.wysiwyg !== false) {
57          var id = getGuid();
58          new EmojiArea_WYSIWYG(originalInput, id, $.extend({}, options));
59        } else {
60          var id = getGuid();
61          new EmojiArea_Plain(originalInput, id, options);
62        }
63        originalInput.attr(
64          {
65            'data-emojiable': 'converted',
66            'data-id': id,
67            'data-type': 'original-input'
68          });
69      });
70  };
71
72  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73
74  var util = {};
75
76  util.restoreSelection = (function() {
77    if (window.getSelection) {
78      return function(savedSelection) {
79        var sel = window.getSelection();
80        sel.removeAllRanges();
81        for (var i = 0, len = savedSelection.length; i < len; ++i) {
82          sel.addRange(savedSelection[i]);
83        }
84      };
85    } else if (document.selection && document.selection.createRange) {
86      return function(savedSelection) {
87        if (savedSelection) {
88          savedSelection.select();
89        }
90      };
91    }
92  })();
93
94  util.saveSelection = (function() {
95    if (window.getSelection) {
96      return function() {
97        var sel = window.getSelection(), ranges = [];
98        if (sel.rangeCount) {
99          for (var i = 0, len = sel.rangeCount; i < len; ++i) {
100            ranges.push(sel.getRangeAt(i));
101          }
102        }
103        return ranges;
104      };
105    } else if (document.selection && document.selection.createRange) {
106      return function() {
107        var sel = document.selection;
108        return (sel.type.toLowerCase() !== 'none') ? sel.createRange()
109            : null;
110      };
111    }
112  })();
113
114  util.replaceSelection = (function() {
115    if (window.getSelection) {
116      return function(content) {
117        var range, sel = window.getSelection();
118        var node = typeof content === 'string' ? document
119            .createTextNode(content) : content;
120        if (sel.getRangeAt && sel.rangeCount) {
121          range = sel.getRangeAt(0);
122          range.deleteContents();
123          //range.insertNode(document.createTextNode(''));
124          range.insertNode(node);
125          range.setStart(node, 0);
126
127          window.setTimeout(function() {
128            range = document.createRange();
129            range.setStartAfter(node);
130            range.collapse(true);
131            sel.removeAllRanges();
132            sel.addRange(range);
133          }, 0);
134        }
135      }
136    } else if (document.selection && document.selection.createRange) {
137      return function(content) {
138        var range = document.selection.createRange();
139        if (typeof content === 'string') {
140          range.text = content;
141        } else {
142          range.pasteHTML(content.outerHTML);
143        }
144      }
145    }
146  })();
147
148  util.insertAtCursor = function(text, el) {
149    text = ' ' + text;
150    var val = el.value, endIndex, startIndex, range;
151    if (typeof el.selectionStart != 'undefined'
152        && typeof el.selectionEnd != 'undefined') {
153      startIndex = el.selectionStart;
154      endIndex = el.selectionEnd;
155      el.value = val.substring(0, startIndex) + text
156          + val.substring(el.selectionEnd);
157      el.selectionStart = el.selectionEnd = startIndex + text.length;
158    } else if (typeof document.selection != 'undefined'
159        && typeof document.selection.createRange != 'undefined') {
160      el.focus();
161      range = document.selection.createRange();
162      range.text = text;
163      range.select();
164    }
165  };
166
167  util.extend = function(a, b) {
168    if (typeof a === 'undefined' || !a) {
169      a = {};
170    }
171    if (typeof b === 'object') {
172      for ( var key in b) {
173        if (b.hasOwnProperty(key)) {
174          a[key] = b[key];
175        }
176      }
177    }
178    return a;
179  };
180
181  util.escapeRegex = function(str) {
182    return (str + '').replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
183  };
184
185  util.htmlEntities = function(str) {
186    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;')
187        .replace(/>/g, '&gt;').replace(/"/g, '&quot;');
188  };
189
190  /*
191   * ! MODIFICATION START This function was added by Igor Zhukov to save
192   * recent used emojis.
193   */
194  util.emojiInserted = function(emojiKey, menu) {
195    ConfigStorage.get('emojis_recent', function(curEmojis) {
196      curEmojis = curEmojis || defaultRecentEmojis || [];
197
198      var pos = curEmojis.indexOf(emojiKey);
199      if (!pos) {
200        return false;
201      }
202      if (pos != -1) {
203        curEmojis.splice(pos, 1);
204      }
205      curEmojis.unshift(emojiKey);
206      if (curEmojis.length > 42) {
207        curEmojis = curEmojis.slice(42);
208      }
209
210      ConfigStorage.set({
211        emojis_recent : curEmojis
212      });
213    })
214  };
215  /* ! MODIFICATION END */
216
217  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
218  var EmojiArea = function() {
219  };
220
221  EmojiArea.prototype.setup = function() {
222    var self = this;
223
224    this.$editor.on('focus', function() {
225      self.hasFocus = true;
226    });
227    this.$editor.on('blur', function() {
228      self.hasFocus = false;
229    });
230
231    // Assign a unique instance of an emojiMenu to
232    self.emojiMenu = new EmojiMenu(self);
233
234    this.setupButton();
235  };
236
237  EmojiArea.prototype.setupButton = function() {
238    var self = this;
239    var $button = $('[data-id=' + this.id + '][data-type=picker]');
240
241    $button.on('click', function(e) {
242      self.emojiMenu.show(self);
243    });
244
245    this.$button = $button;
246    this.$dontHideOnClick = 'emoji-picker';
247  };
248
249  /*
250   * ! MODIFICATION START This function was modified by Andre Staltz so that
251   * the icon is created from a spritesheet.
252   */
253  EmojiArea.createIcon = function(emoji, menu) {
254    var category = emoji[0];
255    var row = emoji[1];
256    var column = emoji[2];
257    var name = emoji[3];
258    var filename = $.emojiarea.spriteSheetPath ? $.emojiarea.spriteSheetPath : $.emojiarea.assetsPath + '/emoji_spritesheet_!.png';
259    var blankGifPath = $.emojiarea.blankGifPath ? $.emojiarea.blankGifPath : $.emojiarea.assetsPath + '/blank.gif';
260    var iconSize = menu && Config.Mobile ? 26 : $.emojiarea.iconSize
261    var xoffset = -(iconSize * column);
262    var yoffset = -(iconSize * row);
263    var scaledWidth = (Config.EmojiCategorySpritesheetDimens[category][1] * iconSize);
264    var scaledHeight = (Config.EmojiCategorySpritesheetDimens[category][0] * iconSize);
265
266    var style = 'display:inline-block;';
267    style += 'width:' + iconSize + 'px;';
268    style += 'height:' + iconSize + 'px;';
269    style += 'background:url(\'' + filename.replace('!', category) + '\') '
270        + xoffset + 'px ' + yoffset + 'px no-repeat;';
271    style += 'background-size:' + scaledWidth + 'px ' + scaledHeight
272        + 'px;';
273    return '<img src="' + blankGifPath + '" class="img" style="'
274        + style + '" alt="' + util.htmlEntities(name) + '">';
275  };
276
277  $.emojiarea.createIcon = EmojiArea.createIcon;
278  /* ! MODIFICATION END */
279
280  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
281  /**
282   * Editor (plain-text)
283   *
284   * @constructor
285   * @param {object}
286   *            $textarea
287   * @param {object}
288   *            options
289   */
290
291  var EmojiArea_Plain = function($textarea, id, options) {
292    this.options = options;
293    this.$textarea = $textarea;
294    this.$editor = $textarea;
295    this.id = id;
296    this.setup();
297  };
298
299  EmojiArea_Plain.prototype.insert = function(emoji) {
300    if (!$.emojiarea.icons.hasOwnProperty(emoji))
301      return;
302    util.insertAtCursor(emoji, this.$textarea[0]);
303    /*
304     * MODIFICATION: Following line was added by Igor Zhukov, in order to
305     * save recent emojis
306     */
307    util.emojiInserted(emoji, this.menu);
308    this.$textarea.trigger('change');
309  };
310
311  EmojiArea_Plain.prototype.val = function() {
312    if (this.$textarea == '\n')
313      return '';
314    return this.$textarea.val();
315  };
316
317  util.extend(EmojiArea_Plain.prototype, EmojiArea.prototype);
318
319  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
320
321  /**
322   * Editor (rich)
323   *
324   * @constructor
325   * @param {object}
326   *            $textarea
327   * @param {object}
328   *            options
329   */
330
331  var EmojiArea_WYSIWYG = function($textarea, id, options) {
332    var self = this;
333
334    this.options = options || {};
335    if ($($textarea).attr('data-emoji-input') === 'unicode')
336      this.options.inputMethod = 'unicode';
337    else
338      this.options.inputMethod = 'image';
339    this.id = id;
340    this.$textarea = $textarea;
341    this.emojiPopup = options.emojiPopup;
342    this.$editor = $('<div>').addClass('emoji-wysiwyg-editor').addClass($($textarea)[0].className);
343    this.$editor.data('self', this);
344
345    if ($textarea.attr('maxlength')) {
346      this.$editor.attr('maxlength', $textarea.attr('maxlength'));
347    }
348    this.$editor.height($textarea.outerHeight()); //auto adjust height
349    this.emojiPopup.appendUnicodeAsImageToElement(this.$editor, $textarea.val());
350
351    this.$editor.attr({
352      'data-id': id,
353      'data-type': 'input',
354      'placeholder': $textarea.attr('placeholder'),
355      'contenteditable': 'true',
356    });
357
358    /*
359     * ! MODIFICATION START Following code was modified by Igor Zhukov, in
360     * order to improve rich text paste
361     */
362    var changeEvents = 'blur change';
363    if (!this.options.norealTime) {
364      changeEvents += ' keyup';
365    }
366    this.$editor.on(changeEvents, function(e) {
367      return self.onChange.apply(self, [ e ]);
368    });
369    /* ! MODIFICATION END */
370
371    this.$editor.on('mousedown focus', function() {
372      document.execCommand('enableObjectResizing', false, false);
373    });
374    this.$editor.on('blur', function() {
375      document.execCommand('enableObjectResizing', true, true);
376    });
377
378    var editorDiv = this.$editor;
379    this.$editor.on("change keydown keyup resize scroll", function(e) {
380      if(MAX_LENGTH_ALLOWED_KEYS.indexOf(e.which) == -1 &&
381        !((e.ctrlKey || e.metaKey) && e.which == 65) && // Ctrl + A
382        !((e.ctrlKey || e.metaKey) && e.which == 67) && // Ctrl + C
383        editorDiv.text().length + editorDiv.find('img').length >= editorDiv.attr('maxlength'))
384      {
385        e.preventDefault();
386      }
387      self.updateBodyPadding(editorDiv);
388    });
389
390    this.$editor.on("paste", function (e) {
391      e.preventDefault();
392      var content;
393      var charsRemaining = editorDiv.attr('maxlength') - (editorDiv.text().length + editorDiv.find('img').length);
394      if ((e.originalEvent || e).clipboardData) {
395        content = (e.originalEvent || e).clipboardData.getData('text/plain');
396        if (self.options.onPaste) {
397          content = self.options.onPaste(content);
398        }
399        if (charsRemaining < content.length) {
400          content = content.substring(0, charsRemaining);
401        }
402        document.execCommand('insertText', false, content);
403      }
404      else if (window.clipboardData) {
405        content = window.clipboardData.getData('Text');
406        if (self.options.onPaste) {
407          content = self.options.onPaste(content);
408        }
409        if (charsRemaining < content.length) {
410          content = content.substring(0, charsRemaining);
411        }
412        document.selection.createRange().pasteHTML(content);
413      }
414      editorDiv.scrollTop(editorDiv[0].scrollHeight);
415    });
416
417    $textarea.after("<i class='emoji-picker-icon emoji-picker fa fa-smile-o' data-id='" + id + "' data-type='picker'></i>");
418
419    $textarea.hide().after(this.$editor);
420    this.setup();
421
422    /*
423     * MODIFICATION: Following line was modified by Igor Zhukov, in order to
424     * improve emoji insert behaviour
425     */
426    $(document.body).on('mousedown', function() {
427      if (self.hasFocus) {
428        self.selection = util.saveSelection();
429      }
430    });
431  };
432
433  EmojiArea_WYSIWYG.prototype.updateBodyPadding = function(target) {
434    var emojiPicker = $('[data-id=' + this.id + '][data-type=picker]');
435    if ($(target).hasScrollbar()) {
436      if (!(emojiPicker.hasClass('parent-has-scroll')))
437        emojiPicker.addClass('parent-has-scroll');
438      if (!($(target).hasClass('parent-has-scroll')))
439        $(target).addClass('parent-has-scroll');
440    } else {
441      if ((emojiPicker.hasClass('parent-has-scroll')))
442        emojiPicker.removeClass('parent-has-scroll');
443      if (($(target).hasClass('parent-has-scroll')))
444        $(target).removeClass('parent-has-scroll');
445    }
446  };
447
448  EmojiArea_WYSIWYG.prototype.onChange = function(e) {
449    var event = new CustomEvent('input', { bubbles: true });
450    this.$textarea.val(this.val())[0].dispatchEvent(event);
451  };
452
453  EmojiArea_WYSIWYG.prototype.insert = function(emoji) {
454    var content;
455    /*
456     * MODIFICATION: Following line was modified by Andre Staltz, to use new
457     * implementation of createIcon function.
458     */
459    var insertionContent = '';
460    if (this.options.inputMethod == 'unicode') {
461      insertionContent = this.emojiPopup.colonToUnicode(emoji);
462    } else {
463      var $img = $(EmojiArea.createIcon($.emojiarea.icons[emoji]));
464      if ($img[0].attachEvent) {
465        $img[0].attachEvent('onresizestart', function(e) {
466          e.returnValue = false;
467        }, false);
468      }
469      insertionContent = $img[0];
470    }
471
472    this.$editor.trigger('focus');
473    if (this.selection) {
474      util.restoreSelection(this.selection);
475    }
476    try {
477      util.replaceSelection(insertionContent);
478    } catch (e) {
479    }
480
481    /*
482     * MODIFICATION: Following line was added by Igor Zhukov, in order to
483     * save recent emojis
484     */
485    util.emojiInserted(emoji, this.menu);
486
487    this.onChange();
488  };
489
490  EmojiArea_WYSIWYG.prototype.val = function() {
491    var lines = [];
492    var line = [];
493    var emojiPopup = this.emojiPopup;
494
495    var flush = function() {
496      lines.push(line.join(''));
497      line = [];
498    };
499
500    var sanitizeNode = function(node) {
501      if (node.nodeType === TEXT_NODE) {
502        line.push(node.nodeValue);
503      } else if (node.nodeType === ELEMENT_NODE) {
504        var tagName = node.tagName.toLowerCase();
505        var isBlock = TAGS_BLOCK.indexOf(tagName) !== -1;
506
507        if (isBlock && line.length)
508          flush();
509
510        if (tagName === 'img') {
511          var alt = node.getAttribute('alt') || '';
512          if (alt) {
513              line.push(alt);
514          }
515          return;
516        } else if (tagName === 'br') {
517          flush();
518        }
519
520        var children = node.childNodes;
521        for (var i = 0; i < children.length; i++) {
522           sanitizeNode(children[i]);
523        }
524
525        if (isBlock && line.length)
526          flush();
527      }
528    };
529
530    var children = this.$editor[0].childNodes;
531    for (var i = 0; i < children.length; i++) {
532      sanitizeNode(children[i]);
533    }
534
535    if (line.length)
536      flush();
537
538    var returnValue = lines.join('\n');
539    return emojiPopup.colonToUnicode(returnValue);
540  };
541
542  util.extend(EmojiArea_WYSIWYG.prototype, EmojiArea.prototype);
543
544  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
545
546  jQuery.fn.hasScrollbar = function() {
547    var scrollHeight = this.get(0).scrollHeight;
548
549    //safari's scrollHeight includes padding
550    //if ($.browser.safari)
551//      scrollHeight -= parseInt(this.css('padding-top')) + parseInt(this.css('padding-bottom'));
552    if (this.outerHeight() < scrollHeight)
553      return true;
554    else
555      return false;
556  }
557
558  /**
559   * Emoji Dropdown Menu
560   *
561   * @constructor
562   * @param {object}
563   *            emojiarea
564   */
565  var EmojiMenu = function(emojiarea) {
566    var self = this;
567    self.id = emojiarea.id;
568    var $body = $(document.body);
569    var $window = $(window);
570
571    this.visible = false;
572    this.emojiarea = emojiarea;
573    EmojiMenu.menuZIndex = 5000;
574    this.$menu = $('<div>');
575    this.$menu.addClass('emoji-menu');
576    this.$menu.attr('data-id', self.id);
577    this.$menu.attr('data-type', 'menu');
578    this.$menu.hide();
579
580    this.$itemsTailWrap = $('<div class="emoji-items-wrap1"></div>')
581        .appendTo(this.$menu);
582    this.$categoryTabs = $(
583        '<table class="emoji-menu-tabs"><tr>'
584            + '<td><a class="emoji-menu-tab icon-recent" ></a></td>'
585            + '<td><a class="emoji-menu-tab icon-smile" ></a></td>'
586            + '<td><a class="emoji-menu-tab icon-flower"></a></td>'
587            + '<td><a class="emoji-menu-tab icon-bell"></a></td>'
588            + '<td><a class="emoji-menu-tab icon-car"></a></td>'
589            + '<td><a class="emoji-menu-tab icon-grid"></a></td>'
590            + '</tr></table>').appendTo(this.$itemsTailWrap);
591    this.$itemsWrap = $(
592        '<div class="emoji-items-wrap mobile_scrollable_wrap"></div>')
593        .appendTo(this.$itemsTailWrap);
594    this.$items = $('<div class="emoji-items">').appendTo(
595        this.$itemsWrap);
596
597    this.emojiarea.$editor.after(this.$menu)
598
599    $body.on('keydown', function(e) {
600      if (e.keyCode === KEY_ESC || e.keyCode === KEY_TAB) {
601        self.hide();
602      }
603    });
604
605    /*
606     * ! MODIFICATION: Following 3 lines were added by Igor Zhukov, in order
607     * to hide menu on message submit with keyboard
608     */
609    $body.on('message_send', function(e) {
610      self.hide();
611    });
612
613    $body.on('mouseup', function(e) {
614      e = e.originalEvent || e;
615      var target = e.target || window;
616
617      if ($(target).hasClass(self.emojiarea.$dontHideOnClick)) {
618        return;
619      }
620
621      while (target && target != window) {
622        target = target.parentNode;
623        if (target == self.$menu[0] || self.emojiarea
624            && target == self.emojiarea.$button[0]) {
625          return;
626        }
627      }
628      self.hide();
629    });
630
631    this.$menu.on('mouseup', 'a', function(e) {
632      e.stopPropagation();
633      return false;
634    });
635
636    this.$menu.on('click', 'a', function(e) {
637
638      self.emojiarea.updateBodyPadding(self.emojiarea.$editor);
639      if ($(this).hasClass('emoji-menu-tab')) {
640        if (self.getTabIndex(this) !== self.currentCategory) {
641          self.selectCategory(self.getTabIndex(this));
642        }
643        return false;
644      }
645
646      var emoji = $('.label', $(this)).text();
647      window.setTimeout(function() {
648        self.onItemSelected(emoji);
649        if (e.ctrlKey || e.metaKey) {
650          self.hide();
651        }
652      }, 0);
653      e.stopPropagation();
654      return false;
655    });
656
657    this.selectCategory(0);
658  };
659
660  /*
661   * ! MODIFICATION START Following code was added by Andre Staltz, to
662   * implement category selection.
663   */
664  EmojiMenu.prototype.getTabIndex = function(tab) {
665    return this.$categoryTabs.find('.emoji-menu-tab').index(tab);
666  };
667
668  EmojiMenu.prototype.selectCategory = function(category) {
669    var self = this;
670    this.$categoryTabs.find('.emoji-menu-tab').each(function(index) {
671      if (index === category) {
672        this.className += '-selected';
673      } else {
674        this.className = this.className.replace('-selected', '');
675      }
676    });
677    this.currentCategory = category;
678    this.load(category);
679  };
680  /* ! MODIFICATION END */
681
682  EmojiMenu.prototype.onItemSelected = function(emoji) {
683    if(this.emojiarea.$editor.text().length + this.emojiarea.$editor.find('img').length >= this.emojiarea.$editor.attr('maxlength'))
684    {
685      return;
686    }
687    this.emojiarea.insert(emoji);
688  };
689
690  /*
691   * MODIFICATION: The following function argument was modified by Andre
692   * Staltz, in order to load only icons from a category. Also function was
693   * modified by Igor Zhukov in order to display recent emojis from
694   * localStorage
695   */
696  EmojiMenu.prototype.load = function(category) {
697    var html = [];
698    var options = $.emojiarea.icons;
699    var path = $.emojiarea.assetsPath;
700    var self = this;
701    if (path.length && path.charAt(path.length - 1) !== '/') {
702      path += '/';
703    }
704
705    /*
706     * ! MODIFICATION: Following function was added by Igor Zhukov, in order
707     * to add scrollbars to EmojiMenu
708     */
709    var updateItems = function() {
710      self.$items.html(html.join(''));
711    }
712
713    if (category > 0) {
714      for ( var key in options) {
715        /*
716         * MODIFICATION: The following 2 lines were modified by Andre
717         * Staltz, in order to load only icons from the specified
718         * category.
719         */
720        if (options.hasOwnProperty(key)
721            && options[key][0] === (category - 1)) {
722          html.push('<a href="javascript:void(0)" title="'
723              + util.htmlEntities(key) + '">'
724              + EmojiArea.createIcon(options[key], true)
725              + '<span class="label">' + util.htmlEntities(key)
726              + '</span></a>');
727        }
728      }
729      updateItems();
730    } else {
731      ConfigStorage.get('emojis_recent', function(curEmojis) {
732        curEmojis = curEmojis || defaultRecentEmojis || [];
733        var key, i;
734        for (i = 0; i < curEmojis.length; i++) {
735          key = curEmojis[i]
736          if (options[key]) {
737            html.push('<a href="javascript:void(0)" title="'
738                + util.htmlEntities(key) + '">'
739                + EmojiArea.createIcon(options[key], true)
740                + '<span class="label">'
741                + util.htmlEntities(key) + '</span></a>');
742          }
743        }
744        updateItems();
745      });
746    }
747  };
748
749  EmojiMenu.prototype.hide = function(callback) {
750    this.visible = false;
751    this.$menu.hide("fast");
752  };
753
754  EmojiMenu.prototype.show = function(emojiarea) {
755    /*
756     * MODIFICATION: Following line was modified by Igor Zhukov, in order to
757     * improve EmojiMenu behaviour
758     */
759    if (this.visible)
760      return this.hide();
761    $(this.$menu).css('z-index', ++EmojiMenu.menuZIndex);
762    this.$menu.show("fast");
763    /*
764     * MODIFICATION: Following 3 lines were added by Igor Zhukov, in order
765     * to update EmojiMenu contents
766     */
767    if (!this.currentCategory) {
768      this.load(0);
769    }
770    this.visible = true;
771  };
772
773})(jQuery, window, document);

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.