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, '&').replace(/</g, '<') 187 .replace(/>/g, '>').replace(/"/g, '"'); 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.