1/* 2 * Mentions Input 3 * Version 1.0.2 4 * Written by: Kenneth Auchenberg (Podio) 5 * 6 * Using underscore.js 7 * 8 * License: MIT License - http://www.opensource.org/licenses/mit-license.php 9 */ 10 11(function ($, _, undefined) { 12 13 // Settings 14 var KEY = { BACKSPACE: 8, TAB: 9, RETURN: 13, ESC: 27, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, COMMA: 188, SPACE: 32, HOME: 36, END: 35 }; // Keys "enum" 15 16 //Default settings 17 var defaultSettings = { 18 triggerChar: '@', //Char that respond to event 19 onDataRequest: $.noop, //Function where we can search the data 20 minChars: 2, //Minimum chars to fire the event 21 allowRepeat: false, //Allow repeat mentions 22 showAvatars: true, //Show the avatars 23 elastic: false, //Grow the textarea automatically 24 defaultValue: '', 25 onCaret: false, 26 classes: { 27 autoCompleteItemActive: "active" //Classes to apply in each item 28 }, 29 templates: { 30 wrapper: _.template('<div class="mentions-input-box"></div>'), 31 autocompleteList: _.template('<div class="mentions-autocomplete-list"></div>'), 32 autocompleteListItem: _.template('<li data-ref-id="<%= id %>" data-ref-type="<%= type %>" data-display="<%= display %>"><%= content %></li>'), 33 autocompleteListItemAvatar: _.template('<img src="<%= avatar %>" />'), 34 autocompleteListItemIcon: _.template('<div class="icon <%= icon %>"></div>'), 35 mentionsOverlay: _.template('<div class="mentions"><div></div></div>'), 36 mentionItemSyntax: _.template('@[<%= value %>](<%= type %>:<%= id %>)'), 37 mentionItemHighlight: _.template('<strong><span><%= value %></span></strong>') 38 } 39 }; 40 41 //Class util 42 var utils = { 43 //Encodes the character with _.escape function (undersocre) 44 htmlEncode: function (str) { 45 return _.escape(str); 46 }, 47 //Encodes the character to be used with RegExp 48 regexpEncode: function (str) { 49 return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); 50 }, 51 highlightTerm: function (value, term) { 52 if (!term && !term.length) { 53 return value; 54 } 55 return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>"); 56 }, 57 //Sets the caret in a valid position 58 setCaratPosition: function (domNode, caretPos) { 59 if (domNode.createTextRange) { 60 var range = domNode.createTextRange(); 61 range.move('character', caretPos); 62 range.select(); 63 } else { 64 if (domNode.selectionStart) { 65 domNode.focus(); 66 domNode.setSelectionRange(caretPos, caretPos); 67 } else { 68 domNode.focus(); 69 } 70 } 71 }, 72 //Deletes the white spaces 73 rtrim: function (string) { 74 return string.replace(/\s+$/, ""); 75 } 76 }; 77 78 //Main class of MentionsInput plugin 79 var MentionsInput = function (settings) { 80 81 var domInput, 82 elmInputBox, 83 elmInputWrapper, 84 elmAutocompleteList, 85 elmWrapperBox, 86 elmMentionsOverlay, 87 elmActiveAutoCompleteItem, 88 mentionsCollection = [], 89 autocompleteItemCollection = {}, 90 inputBuffer = [], 91 currentDataQuery = ''; 92 93 //Mix the default setting with the users settings 94 settings = $.extend(true, {}, defaultSettings, settings); 95 96 //Initializes the text area target 97 function initTextarea() { 98 elmInputBox = $(domInput); //Get the text area target 99 100 //If the text area is already configured, return 101 if (elmInputBox.attr('data-mentions-input') === 'true') { 102 return; 103 } 104 105 elmInputWrapper = elmInputBox.parent(); //Get the DOM element parent 106 elmWrapperBox = $(settings.templates.wrapper()); 107 elmInputBox.wrapAll(elmWrapperBox);
107 //Wrap all the text area into the div elmWrapperBox 108 elmWrapperBox = elmInputWrapper.find('> div.mentions-input-box'); //Obtains the div elmWrapperBox that now contains the text area 109 110 elmInputBox.attr('data-mentions-input', 'true'); //Sets the attribute data-mentions-input to true -> Defines if the text area is already configured 111 elmInputBox.bind('keydown', onInputBoxKeyDown); //Bind the keydown event to the text area 112 elmInputBox.bind('keypress', onInputBoxKeyPress); //Bind the keypress event to the text area 113 elmInputBox.bind('click', onInputBoxClick); //Bind the click event to the text area 114 elmInputBox.bind('blur', onInputBoxBlur); //Bind the blur event to the text area 115 116 if (navigator.userAgent.indexOf("MSIE 8") > -1) { 117 elmInputBox.bind('propertychange', onInputBoxInput); //IE8 won't fire the input event, so let's bind to the propertychange 118 } else { 119 elmInputBox.bind('input', onInputBoxInput); //Bind the input event to the text area 120 } 121 122 // Elastic textareas, grow automatically 123 if (settings.elastic) { 124 elmInputBox.elastic(); 125 } 126 } 127 128 //Initializes the autocomplete list, append to elmWrapperBox and delegate the mousedown event to li elements 129 function initAutocomplete() { 130 elmAutocompleteList = $(settings.templates.autocompleteList()); //Get the HTML code for the list 131 elmAutocompleteList.appendTo(elmWrapperBox); //Append to elmWrapperBox element 132 elmAutocompleteList.delegate('li', 'mousedown', onAutoCompleteItemClick); //Delegate the event 133 } 134 135 //Initializes the mentions' overlay 136 function initMentionsOverlay() { 137 elmMentionsOverlay = $(settings.templates.mentionsOverlay()); //Get the HTML code of the mentions' overlay 138 elmMentionsOverlay.prependTo(elmWrapperBox); //Insert into elmWrapperBox the mentions overlay 139 } 140 141 //Updates the values of the main variables 142 function updateValues() { 143 var syntaxMessage = getInputBoxValue(); //Get the actual value of the text area 144 145 _.each(mentionsCollection, function (mention) { 146 var textSyntax = settings.templates.mentionItemSyntax(mention); 147 syntaxMessage = syntaxMessage.replace(new RegExp(utils.regexpEncode(mention.value), 'g'), textSyntax); 148 }); 149 150 var mentionText = utils.htmlEncode(syntaxMessage); //Encode the syntaxMessage 151 152 _.each(mentionsCollection, function (mention) { 153 var formattedMention = _.extend({}, mention, { value: utils.htmlEncode(mention.value) }); 154 var textSyntax = settings.templates.mentionItemSyntax(formattedMention); 155 var textHighlight = settings.templates.mentionItemHighlight(formattedMention); 156 157 mentionText = mentionText.replace(new RegExp(utils.regexpEncode(textSyntax), 'g'), textHighlight); 158 }); 159 160 mentionText = mentionText.replace(/\n/g, '<br />'); //Replace the escape character for <br /> 161 mentionText = mentionText.replace(/ {2}/g, ' '); //Replace the 2 preceding token to 162 163 elmInputBox.data('messageText', syntaxMessage); //Save the messageText to elmInputBox 164 elmInputBox.trigger('updated'); 165 elmMentionsOverlay.find('div').html(mentionText); //Insert into a div of the elmMentionsOverlay the mention text 166 } 167 168 //Cleans the buffer 169 function resetBuffer() { 170 inputBuffer = []; 171 } 172 173 //Updates the mentions collection 174 function updateMentionsCollection() { 175 var inputText = getInputBoxValue(); //Get the actual value of text area 176 177 //Returns the values that doesn't match the condition 178 mentionsCollection = _.reject(mentionsCollection, function (mention, index) { 179 return !mention.value || inputText.indexOf(mention.value) == -1; 180 }); 181 mentionsCollection = _.compact(mentionsCollection); //Delete all the falsy values of the array and return the new array 182 } 183 184 //Adds mention to mentions collections 185 function addMention(mention) { 186 187 var currentMessage = getInputBoxValue(), 188 caretStart = elmInputBox[0].selectionStart, 189 shortestDistance = false, 190 bestLastIndex = false; 191 192 // Using a regex to figure out positions 193 var regex = new RegExp("\\" + settings.triggerChar + currentDataQuery, "gi"), 194 regexMatch; 195 196 while (regexMatch = regex.exec(currentMessage)) { 197 if (shortestDistance === false || Math.abs(regex.lastIndex - caretStart) < shortestDistance) { 198 shortestDistance = Math.abs(regex.lastIndex - caretStart); 199 bestLastIndex = regex.lastIndex; 200 } 201 } 202 203 var startCaretPosition = bestLastIndex - currentDataQuery.length /*- 1*/; //Set the start caret position (right before the @) 204 var currentCaretPosition = bestLastIndex; //Set the current caret position (right after the end of the "mention") 205 206 207 var start = currentMessage.substr(0, startCaretPosition); 208 var end = currentMessage.substr(currentCaretPosition, currentMessage.length); 209 var startEndIndex = (start + mention.value).length + 1; 210 211 // See if there's the same mention in the list 212 if (!_.find(mentionsCollection, function (object) { return object.id == mention.id; })) { 213 mentionsCollection.push(mention);//Add the mention to mentionsColletions 214 } 215 216 // Cleaning before inserting the value, otherwise auto-complete would be triggered with "old" inputbuffer 217 resetBuffer(); 218 currentDataQuery = ''; 219 hideAutoComplete(); 220 221 // Mentions and syntax message 222 var updatedMessageText = start + mention.value + ' ' + end; 223 elmInputBox.val(updatedMessageText); //Set the value to the txt area 224 elmInputBox.trigger('mention');
225 updateValues(); 226 227 // Set correct focus and selection 228 elmInputBox.focus(); 229 utils.setCaratPosition(elmInputBox[0], startEndIndex); 230 } 231 232 //Gets the actual value of the text area without white spaces from the beginning and end of the value 233 function getInputBoxValue() { 234 return $.trim(elmInputBox.val()); 235 } 236 237 // This is taken straight from live (as of Sep 2012) GitHub code. The 238 // technique is known around the web. Just google it. Github's is quite 239 // succint though. NOTE: relies on selectionEnd, which as far as IE is concerned, 240 // it'll only work on 9+. Good news is nothing will happen if the browser 241 // doesn't support it. 242 function textareaSelectionPosition($el) { 243 var a, b, c, d, e, f, g, h, i, j, k; 244 if (!(i = $el[0])) return; 245 if (!$(i).is("textarea")) return; 246 if (i.selectionEnd == null) return; 247 g = { 248 position: "absolute", 249 overflow: "auto", 250 whiteSpace: "pre-wrap", 251 wordWrap: "break-word", 252 boxSizing: "content-box", 253 top: 0, 254 left: -9999 255 }, h = ["boxSizing", "fontFamily", "fontSize", "fontStyle", "fontVariant", "fontWeight", "height", "letterSpacing", "lineHeight", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textDecoration", "textIndent", "textTransform", "width", "word-spacing"]; 256 for (j = 0, k = h.length; j < k; j++) e = h[j], g[e] = $(i).css(e); 257 return c = document.createElement("div"), $(c).css(g), $(i).after(c), b = document.createTextNode(i.value.substring(0, i.selectionEnd)), a = document.createTextNode(i.value.substring(i.selectionEnd)), d = document.createElement("span"), d.innerHTML = " ", c.appendChild(b), c.appendChild(d), c.appendChild(a), c.scrollTop = i.scrollTop, f = $(d).position(), $(c).remove(), f 258 } 259 260 //same as above function but return offset instead of position 261 function textareaSelectionOffset($el) { 262 var a, b, c, d, e, f, g, h, i, j, k; 263 if (!(i = $el[0])) return; 264 if (!$(i).is("textarea")) return; 265 if (i.selectionEnd == null) return; 266 g = { 267 position: "absolute", 268 overflow: "auto", 269 whiteSpace: "pre-wrap", 270 wordWrap: "break-word", 271 boxSizing: "content-box", 272 top: 0, 273 left: -9999 274 }, h = ["boxSizing", "fontFamily", "fontSize", "fontStyle", "fontVariant", "fontWeight", "height", "letterSpacing", "lineHeight", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textDecoration", "textIndent", "textTransform", "width", "word-spacing"]; 275 for (j = 0, k = h.length; j < k; j++) e = h[j], g[e] = $(i).css(e); 276 return c = document.createElement("div"), $(c).css(g), $(i).after(c), b = document.createTextNode(i.value.substring(0, i.selectionEnd)), a = document.createTextNode(i.value.substring(i.selectionEnd)), d = document.createElement("span"), d.innerHTML = " ", c.appendChild(b), c.appendChild(d), c.appendChild(a), c.scrollTop = i.scrollTop, f = $(d).offset(), $(c).remove(), f 277 } 278 279 //Scrolls back to the input after autocomplete if the window has scrolled past the input 280 function scrollToInput() { 281 var elmDistanceFromTop = $(elmInputBox).offset().top; //input offset 282 var bodyDistanceFromTop = $('body').offset().top; //body offset 283 var distanceScrolled = $(window).scrollTop(); //distance scrolled 284 285 if (distanceScrolled > elmDistanceFromTop) { 286 //subtracts body distance to handle fixed headers 287 $(window).scrollTop(elmDistanceFromTop - bodyDistanceFromTop); 288 } 289 } 290 291 //Takes the click event when the user select a item of the dropdown
292 function onAutoCompleteItemClick(e) { 293 var elmTarget = $(this); //Get the item selected 294 var mention = autocompleteItemCollection[elmTarget.attr('data-uid')]; //Obtains the mention 295 var inputId = $(elmInputBox).attr("id"); 296 //if (inputId == "SearchModel_Title") { 297 // // call update get search 298 // getFilteredPosts(mention.name, null); 299 // $('#SearchModel_Title').val(''); 300 //} 301 addMention(mention); 302 scrollToInput(); 303 return false; 304 } 305 306 //Takes the click event on text area 307 function onInputBoxClick(e) { 308 resetBuffer(); 309 } 310 311 //Takes the blur event on text area 312 function onInputBoxBlur(e) { 313 hideAutoComplete(); 314 } 315 316 //Takes the input event when users write or delete something 317 function onInputBoxInput(e) { 318 updateValues(); 319 updateMentionsCollection(); 320 321 var triggerCharIndex = _.lastIndexOf(inputBuffer, settings.triggerChar); //Returns the last match of the triggerChar in the inputBuffer 322 if (triggerCharIndex > -1) { //If the triggerChar is present in the inputBuffer array 323 currentDataQuery = inputBuffer.slice(triggerCharIndex + 1).join(''); //Gets the currentDataQuery 324 currentDataQuery = utils.rtrim(currentDataQuery); //Deletes the whitespaces 325 _.defer(_.bind(doSearch, this, currentDataQuery)); //Invoking the function doSearch ( Bind the function to this) 326 } 327 } 328 329 //Takes the keypress event 330 function onInputBoxKeyPress(e) { 331 if (e.keyCode !== KEY.BACKSPACE) { //If the key pressed is not the backspace 332 var typedValue = String.fromCharCode(e.which || e.keyCode); //Takes the string that represent this CharCode 333 inputBuffer.push(typedValue); //Push the value pressed into inputBuffer 334 } 335 } 336 337 //Takes the keydown event 338 function onInputBoxKeyDown(e) { 339 340 // This also matches HOME/END on OSX which is CMD+LEFT, CMD+RIGHT 341 if (e.keyCode === KEY.LEFT || e.keyCode === KEY.RIGHT || e.keyCode === KEY.HOME || e.keyCode === KEY.END) { 342 // Defer execution to ensure carat pos has changed after HOME/END keys then call the resetBuffer function 343 _.defer(resetBuffer); 344 345 // IE9 doesn't fire the oninput event when backspace or delete is pressed. This causes the highlighting 346 // to stay on the screen whenever backspace is pressed after a highlighed word. This is simply a hack 347 // to force updateValues() to fire when backspace/delete is pressed in IE9. 348 if (navigator.userAgent.indexOf("MSIE 9") > -1) { 349 _.defer(updateValues); //Call the updateValues function 350 } 351 352 return; 353 } 354 355 //If the key pressed was the backspace 356 if (e.keyCode === KEY.BACKSPACE) { 357 inputBuffer = inputBuffer.slice(0, -1 + inputBuffer.length); // Can't use splice, not available in IE 358 return; 359 } 360 361 //If the elmAutocompleteList is hidden 362 if (!elmAutocompleteList.is(':visible')) { 363 return true; 364 } 365 366 switch (e.keyCode) { 367 case KEY.UP: //If the key pressed was UP or DOWN 368 case KEY.DOWN: 369 var elmCurrentAutoCompleteItem = null; 370 if (e.keyCode === KEY.DOWN) { //If the key pressed was DOWN 371 if (elmActiveAutoCompleteItem && elmActiveAutoCompleteItem.length) { //If elmActiveAutoCompleteItem exits 372 elmCurrentAutoCompleteItem = elmActiveAutoCompleteItem.next(); //Gets the next li element in the list 373 } else { 374 elmCurrentAutoCompleteItem = elmAutocompleteList.find('li').first(); //Gets the first li element found 375 } 376 } else { 377 elmCurrentAutoCompleteItem = $(elmActiveAutoCompleteItem).prev(); //The key pressed was UP and gets the previous li element 378 } 379 if (elmCurrentAutoCompleteItem.length) { 380 selectAutoCompleteItem(elmCurrentAutoCompleteItem); 381 } 382 return false; 383 case KEY.RETURN: //If the key pressed was RETURN or TAB 384 case KEY.TAB: 385 if (elmActiveAutoCompleteItem && elmActiveAutoCompleteItem.length) { //If the elmActiveAutoCompleteItem exists 386 elmActiveAutoCompleteItem.trigger('mousedown'); //Calls the mousedown event 387 return false; 388 } 389 break; 390 } 391 392 return true; 393 } 394 395 //Hides the autoomplete 396 function hideAutoComplete() { 397 elmActiveAutoCompleteItem = null; 398 elmAutocompleteList.empty().hide(); 399 } 400 401 //Selects the item in the autocomplete list 402 function selectAutoCompleteItem(elmItem) { 403 elmItem.addClass(settings.classes.autoCompleteItemActive); //Add the class active to item 404 elmItem.siblings().removeClass(settings.classes.autoCompleteItemActive); //Gets all li elements in autocomplete list and remove the class active 405 406 elmActiveAutoCompleteItem = elmItem; //Sets the item to elmActiveAutoCompleteItem 407 } 408 409 //Populates dropdown 410 function populateDropdown(query, results) { 411 elmAutocompleteList.show(); //Shows the autocomplete list 412 413 if (!settings.allowRepeat) { 414 // Filter items that has already been mentioned 415 var mentionValues = _.pluck(mentionsCollection, 'value'); 416 results = _.reject(results, function (item) { 417 return _.include(mentionValues, item.name); 418 }); 419 } 420 421 if (!results.length) { //If there are not elements hide the autocomplete list 422 hideAutoComplete(); 423 return; 424 } 425 426 elmAutocompleteList.empty(); //Remove all li elements in autocomplete list 427 var elmDropDownList = $("<ul>").appendTo(elmAutocompleteList).hide(); //Inserts a ul element to autocomplete div and hide it 428 429 _.each(results, function (item, index) { 430 var itemUid = _.uniqueId('mention_'); //Gets the item with unique id 431
432 autocompleteItemCollection[itemUid] = _.extend({}, item, { value: item.name }); //Inserts the new item to autocompleteItemCollection 433 434 var elmListItem = $(settings.templates.autocompleteListItem({ 435 'id': utils.htmlEncode(item.id), 436 'display': utils.htmlEncode(item.name), 437 'type': utils.htmlEncode(item.type), 438 'content': utils.highlightTerm(utils.htmlEncode((item.display ? item.display : item.name)), query) 439 })).attr('data-uid', itemUid); //Inserts the new item to list 440 441 //If the index is 0 442 if (index === 0) { 443 selectAutoCompleteItem(elmListItem); 444 } 445 446 //If show avatars is true 447 if (settings.showAvatars) { 448 var elmIcon; 449 450 //If the item has an avatar 451 if (item.avatar) { 452 elmIcon = $(settings.templates.autocompleteListItemAvatar({ avatar: item.avatar })); 453 } else { //If not then we set an default icon 454 elmIcon = $(settings.templates.autocompleteListItemIcon({ icon: item.icon })); 455 } 456 elmIcon.prependTo(elmListItem); //Inserts the elmIcon to elmListItem 457 } 458 elmListItem = elmListItem.appendTo(elmDropDownList); //Insets the elmListItem to elmDropDownList 459 }); 460 461 elmAutocompleteList.show(); //Shows the elmAutocompleteList div 462 if (settings.onCaret) { 463 positionAutocomplete(elmAutocompleteList, elmInputBox); 464 } 465 elmDropDownList.show(); //Shows the elmDropDownList 466 } 467 468 //Search into data list passed as parameter 469 function doSearch(query) { 470 //If the query is not null, undefined, empty and has the minimum chars 471 if (query && query.length && query.length >= settings.minChars) { 472 //Call the onDataRequest function and then call the populateDropDrown 473 settings.onDataRequest.call(this, 'search', query, function (responseData) { 474 populateDropdown(query, responseData); 475 }); 476 } else { //If the query is null, undefined, empty or has not the minimun chars 477 hideAutoComplete(); //Hide the autocompletelist 478 } 479 } 480 481 function positionAutocomplete(elmAutocompleteList, elmInputBox) { 482 var elmAutocompleteListPosition = elmAutocompleteList.css('position'); 483 if (elmAutocompleteListPosition == 'absolute') { 484 var position = textareaSelectionPosition(elmInputBox), 485 lineHeight = parseInt(elmInputBox.css('line-height'), 10) || 18; 486 elmAutocompleteList.css('width', '15em'); // Sort of a guess 487 elmAutocompleteList.css('left', position.left); 488 elmAutocompleteList.css('top', lineHeight + position.top); 489 490 //check if the right position of auto complete is larger than the right position of the input 491 //if yes, reset the left of auto complete list to make it fit the input 492 var elmInputBoxRight = elmInputBox.offset().left + elmInputBox.width(), 493 elmAutocompleteListRight = elmAutocompleteList.offset().left + elmAutocompleteList.width(); 494 if (elmInputBoxRight <= elmAutocompleteListRight) { 495 elmAutocompleteList.css('left', Math.abs(elmAutocompleteList.position().left - (elmAutocompleteListRight - elmInputBoxRight))); 496 } 497 } 498 else if (elmAutocompleteListPosition == 'fixed') { 499 var offset = textareaSelectionOffset(elmInputBox), 500 lineHeight = parseInt(elmInputBox.css('line-height'), 10) || 18; 501 elmAutocompleteList.css('width', '15em'); // Sort of a guess 502 elmAutocompleteList.css('left', offset.left + 10000); 503 elmAutocompleteList.css('top', lineHeight + offset.top); 504 } 505 } 506 507 //Resets the text area 508 function resetInput(currentVal) { 509 mentionsCollection = []; 510 var mentionText = utils.htmlEncode(currentVal); 511 var regex = new RegExp("(" + settings.triggerChar + ")\\[(.*?)\\]\\((.*?):(.*?)\\)", "gi"); 512 var match, newMentionText = mentionText; 513 while ((match = regex.exec(mentionText)) != null) { 514 newMentionText = newMentionText.replace(match[0], match[1] + match[2]); 515 mentionsCollection.push({ 'id': match[4], 'type': match[3], 'value': match[2], 'trigger': match[1] }); 516 } 517 elmInputBox.val(newMentionText);
518 updateValues(); 519 } 520 // Public methods 521 return { 522 //Initializes the mentionsInput component on a specific element. 523 init: function (domTarget) { 524 525 domInput = domTarget; 526 527 initTextarea(); 528 initAutocomplete(); 529 initMentionsOverlay(); 530 resetInput(settings.defaultValue); 531 532 //If the autocomplete list has prefill mentions 533 if (settings.prefillMention) { 534 addMention(settings.prefillMention); 535 } 536 }, 537 538 //An async method which accepts a callback function and returns a value of the input field (including markup) as a first parameter of this function. This is the value you want to send to your server. 539 val: function (callback) { 540 if (!_.isFunction(callback)) { 541 return; 542 } 543 callback.call(this, mentionsCollection.length ? elmInputBox.data('messageText') : getInputBoxValue()); 544 }, 545 546 //Resets the text area value and clears all mentions 547 reset: function () { 548 resetInput(); 549 }, 550 551 //Reinit with the text area value if it was changed programmatically 552 reinit: function () { 553 resetInput(false); 554 }, 555 556 //An async method which accepts a callback function and returns a collection of mentions as hash objects as a first parameter. 557 getMentions: function (callback) { 558 if (!_.isFunction(callback)) { 559 return; 560 } 561 callback.call(this, mentionsCollection); 562 } 563 }; 564 }; 565 566 //Main function to include into jQuery and initialize the plugin 567 $.fn.mentionsInput = function (method, settings) { 568 569 var outerArguments = arguments; //Gets the arguments 570 //If method is not a function 571 if (typeof method === 'object' || !method) { 572 settings = method; 573 } 574 575 return this.each(function () { 576 var instance = $.data(this, 'mentionsInput') || $.data(this, 'mentionsInput', new MentionsInput(settings)); 577 578 if (_.isFunction(instance[method])) { 579 return instance[method].apply(this, Array.prototype.slice.call(outerArguments, 1)); 580 } else if (typeof method === 'object' || !method) { 581 return instance.init.call(this, this); 582 } else { 583 $.error('Method ' + method + ' does not exist'); 584 } 585 }); 586 }; 587 588})(jQuery, _);
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.