Ticket #8001: 8001.02.diff
File 8001.02.diff, 139.3 KB (added by , 4 years ago) |
---|
-
.gitignore
diff --git a/.gitignore b/.gitignore index cc3f92c79..c5008ece8 100644
a b logs 17 17 results 18 18 src/vendor 19 19 vendor 20 !src/bp-core/js/vendor 20 21 21 22 node_modules 22 23 npm-debug.log -
src/bp-activity/bp-activity-cssjs.php
diff --git a/src/bp-activity/bp-activity-cssjs.php b/src/bp-activity/bp-activity-cssjs.php index 26748b440..2495272f7 100644
a b function bp_activity_mentions_script() { 33 33 34 34 $min = bp_core_get_minified_asset_suffix(); 35 35 36 wp_enqueue_script( 'bp-mentions', buddypress()->plugin_url . "bp-activity/js/mentions{$min}.js", array( ' jquery', 'jquery-atwho' ), bp_get_version(), true );36 wp_enqueue_script( 'bp-mentions', buddypress()->plugin_url . "bp-activity/js/mentions{$min}.js", array( 'bp-api-request', 'zurb-tribute' ), bp_get_version(), true ); 37 37 wp_enqueue_style( 'bp-mentions-css', buddypress()->plugin_url . "bp-activity/css/mentions{$min}.css", array(), bp_get_version() ); 38 38 39 39 wp_style_add_data( 'bp-mentions-css', 'rtl', 'replace' ); … … function bp_activity_mentions_script() { 41 41 wp_style_add_data( 'bp-mentions-css', 'suffix', $min ); 42 42 } 43 43 44 // If the script has been enqueued, let's attach our mentions TinyMCE init callback.45 add_filter( 'tiny_mce_before_init', 'bp_add_mentions_on_tinymce_init', 10, 2 );46 47 44 /** 48 45 * Fires at the end of the Activity Mentions script. 49 46 * … … function bp_activity_mentions_script() { 57 54 add_action( 'bp_enqueue_scripts', 'bp_activity_mentions_script' ); 58 55 add_action( 'bp_admin_enqueue_scripts', 'bp_activity_mentions_script' ); 59 56 60 /**61 * Bind the mentions listener to a wp_editor instance when TinyMCE initializes.62 *63 * @since 2.3.364 *65 * @param array $settings An array with TinyMCE config.66 * @param string $editor_id Unique editor identifier, e.g. 'content'.67 * @return array $mceInit An array with TinyMCE config.68 */69 function bp_add_mentions_on_tinymce_init( $settings, $editor_id ) {70 // We only apply the mentions init to the visual post editor in the WP dashboard.71 if ( 'content' === $editor_id ) {72 $settings['init_instance_callback'] = 'window.bp.mentions.tinyMCEinit';73 }74 75 return $settings;76 } -
src/bp-activity/css/mentions.css
diff --git a/src/bp-activity/css/mentions.css b/src/bp-activity/css/mentions.css index 7773c75ec..f7bc47d12 100644
a b 1 .atwho-view { 2 background: rgba(204, 204, 204, 0.8); 3 border-radius: 2px; 4 border: 1px solid rgb(204, 204, 204); 5 box-shadow: 0 0 5px rgba(204, 204, 204, 0.25), 0 0 1px #fff; 6 color: #d84800; 7 display: none; 8 font-family: sans-serif; 9 margin-top: 18px; 1 .tribute-container { 10 2 position: absolute; 11 3 top: 0; 12 z-index: 1000; /* >999 for wp-admin */13 }14 15 /* rtl:ignore */16 .atwho-view {17 4 left: 0; 5 height: auto; 6 max-height: 300px; 7 max-width: 500px; 8 overflow: auto; 9 display: block; 10 z-index: 999999; 18 11 } 19 20 .atwho-view ul { 21 background: #fff; 22 list-style: none; 23 margin: auto; 12 .tribute-container ul { 13 margin: 0; 14 margin-top: 2px; 24 15 padding: 0; 16 list-style: none; 17 background: #efefef; 25 18 } 26 27 .atwho-view ul li { 28 border-bottom: 1px solid #efefef; 29 box-sizing: content-box; 19 .tribute-container li { 20 padding: 5px 5px; 30 21 cursor: pointer; 31 display: block;32 font-size: 14px;33 height: 20px;34 line-height: 20px;35 margin: 0;36 overflow: hidden;37 padding: 5px 10px;38 }39 40 .atwho-view img {41 border-radius: 2px;42 float: right;43 height: 20px;44 margin-top: 0;45 width: 20px;46 22 } 47 48 .atwho-view strong { 49 background: #efefef; 50 font-weight: 700; 23 .tribute-container li.highlight, .tribute-container li:hover { 24 background: #ddd; 51 25 } 52 53 .atwho-view .username strong { 54 color: #d54e21; 26 .tribute-container li span { 27 font-weight: bold; 55 28 } 56 57 .atwho-view small { 58 color: #aaa; 59 float: right; 60 font-size: smaller; 61 font-weight: 400; 62 margin: 0 10px 0 40px; 29 .tribute-container li.no-match { 30 cursor: default; 63 31 } 64 65 .atwho-view .cur { 66 background: rgba(239, 239, 239, 0.5); 32 .tribute-container .menu-highlighted { 33 font-weight: bold; 67 34 } 68 69 @media (max-width: 900px) { 70 71 .atwho-view img { 72 float: left; 73 margin: 0 10px 0 0; 74 } 75 } 76 77 @media (max-width: 400px) { 78 79 .atwho-view ul li { 80 font-size: 16px; 81 line-height: 23px; 82 padding: 13px; 83 } 84 85 .atwho-view ul li img { 86 height: 30px; 87 margin-top: -5px; 88 width: 30px; 89 } 90 91 .atwho-view { 92 border-radius: 0; 93 left: 0 !important; 94 width: 100%; 95 } 96 97 .atwho-view ul li .username { 98 display: inline-block; 99 margin: -10px 0 0 0; 100 padding: 10px 0; 101 } 102 103 .atwho-view ul li small { 104 display: inline-block; 105 margin-left: 20px; 106 } 35 .tribute-container li img { 36 border-radius: 2px; 37 float: right; 38 height: 20px; 39 /*margin-top: 0;*/ 40 width: 20px; 107 41 } -
src/bp-activity/js/mentions.js
diff --git a/src/bp-activity/js/mentions.js b/src/bp-activity/js/mentions.js index 3fbdd9d0c..2c9a94025 100644
a b window.bp = window.bp || {}; 16 16 /** 17 17 * Adds BuddyPress @mentions to form inputs. 18 18 * 19 * @param {array |object} options If array, becomes the suggestions' data source. If object, passed as config to $.atwho().19 * @param {array} defaultList If array, becomes the suggestions' default data source. 20 20 * @since 2.1.0 21 21 */ 22 $.fn.bp_mentions = function( options ) { 23 if ( Array.isArray( options ) ) { 24 options = { data: options }; 25 } 26 27 /** 28 * Default options for at.js; see https://github.com/ichord/At.js/. 29 */ 30 var suggestionsDefaults = { 31 delay: 200, 32 hideWithoutSuffix: true, 33 insertTpl: '@${ID}', 34 limit: 10, 35 startWithSpace: false, 36 suffix: '', 37 38 callbacks: { 39 /** 40 * Custom filter to only match the start of spaced words. 41 * Based on the core/default one. 42 * 43 * @param {string} query 44 * @param {array} data 45 * @param {string} search_key 46 * @return {array} 47 * @since 2.1.0 48 */ 49 filter: function( query, data, search_key ) { 50 var item, _i, _len, _results = [], 51 regxp = new RegExp( '^' + query + '| ' + query, 'ig' ); // start of string, or preceded by a space. 52 53 for ( _i = 0, _len = data.length; _i < _len; _i++ ) { 54 item = data[ _i ]; 55 if ( item[ search_key ].toLowerCase().match( regxp ) ) { 56 _results.push( item ); 57 } 58 } 59 60 return _results; 61 }, 62 63 /** 64 * Removes some spaces around highlighted string and tweaks regex to allow spaces 65 * (to match display_name). Based on the core default. 66 * 67 * @param {unknown} li 68 * @param {string} query 69 * @return {string} 70 * @since 2.1.0 71 */ 72 highlighter: function( li, query ) { 73 if ( ! query ) { 74 return li; 75 } 76 77 var regexp = new RegExp( '>(\\s*|[\\w\\s]*)(' + this.at.replace( '+', '\\+') + '?' + query.replace( '+', '\\+' ) + ')([\\w ]*)\\s*<', 'ig' ); 78 return li.replace( regexp, function( str, $1, $2, $3 ) { 79 return '>' + $1 + '<strong>' + $2 + '</strong>' + $3 + '<'; 80 }); 81 }, 82 83 /** 84 * Reposition the suggestion list dynamically. 85 * 86 * @param {unknown} offset 87 * @since 2.1.0 88 */ 89 before_reposition: function( offset ) { 90 // get the iframe, if any, already applied with atwho.js library. 91 var caret, 92 line, 93 iframeOffset, 94 move, 95 $view = $( '#atwho-ground-' + this.id + ' .atwho-view' ), 96 $body = $( 'body' ), 97 atwhoDataValue = this.$inputor.data( 'atwho' ); 98 99 if ( 'undefined' !== atwhoDataValue && 'undefined' !== atwhoDataValue.iframe && null !== atwhoDataValue.iframe ) { 100 caret = this.$inputor.caret( 'offset', { iframe: atwhoDataValue.iframe } ); 101 // Caret.js no longer calculates iframe caret position from the window (it's now just within the iframe). 102 // We need to get the iframe offset from the window and merge that into our object. 103 iframeOffset = $( atwhoDataValue.iframe ).offset(); 104 if ( 'undefined' !== iframeOffset ) { 105 caret.left += iframeOffset.left; 106 caret.top += iframeOffset.top; 107 } 108 } else { 109 caret = this.$inputor.caret( 'offset' ); 110 } 111 112 // If the caret is past horizontal half, then flip it, yo. 113 if ( caret.left > ( $body.width() / 2 ) ) { 114 $view.addClass( 'right' ); 115 move = caret.left - offset.left - this.view.$el.width(); 116 } else { 117 $view.removeClass( 'right' ); 118 move = caret.left - offset.left + 1; 119 } 120 121 // If we're on a small screen, scroll to caret. 122 if ( $body.width() <= 400 ) { 123 $( document ).scrollTop( caret.top - 6 ); 124 } 125 126 // New position is under the caret (never above) and positioned to follow. 127 // Dynamic sizing based on the input area (remove 'px' from end). 128 line = parseInt( this.$inputor.css( 'line-height' ).substr( 0, this.$inputor.css( 'line-height' ).length - 2 ), 10 ); 129 if ( !line || line < 5 ) { // Sanity check, and catch no line-height. 130 line = 19; 131 } 132 133 offset.top = caret.top + line; 134 offset.left += move; 135 }, 136 137 /** 138 * Override default behavior which inserts junk tags in the WordPress Visual editor. 139 * 140 * @param {unknown} $inputor Element which we're inserting content into. 141 * @param {string} content The content that will be inserted. 142 * @param {string} suffix Applied to the end of the content string. 143 * @return {string} 144 * @since 2.1.0 145 */ 146 inserting_wrapper: function( $inputor, content, suffix ) { 147 return '' + content + suffix; 148 } 22 $.fn.bp_mentions = function( defaultList ) { 23 var debouncer = function(func, wait) { 24 var timeout; 25 return function() { 26 var context = this; 27 var args = arguments; 28 29 var callFunction = function() { 30 func.apply(context, args) 31 }; 32 33 clearTimeout(timeout); 34 timeout = setTimeout(callFunction, wait); 35 }; 36 }; 37 38 var remoteSearch = function( text, cb ) { 39 /** 40 * Immediately show the pre-created friends list, if it's populated, 41 * and the user has hesitated after hitting @ (no search text provided). 42 */ 43 if ( text.length === 0 && $.isArray( defaultList ) && defaultList.length > 0 ) { 44 cb(defaultList); 45 return; 149 46 } 150 },151 47 152 /** 153 * Default options for our @mentions; see https://github.com/ichord/At.js/. 154 */ 155 mentionsDefaults = { 156 callbacks: { 157 /** 158 * If there are no matches for the query in this.data, then query BuddyPress. 159 * 160 * @param {string} query Partial @mention to search for. 161 * @param {function} render_view Render page callback function. 162 * @since 2.1.0 163 * @since 3.0.0. Renamed from "remote_filter" for at.js v1.5.4 support. 164 */ 165 remoteFilter: function( query, render_view ) { 166 var self = $( this ), 167 params = {}; 168 169 mentionsItem = mentionsQueryCache[ query ]; 170 if ( typeof mentionsItem === 'object' ) { 171 render_view( mentionsItem ); 172 return; 173 } 174 175 if ( self.xhr ) { 176 self.xhr.abort(); 177 } 178 179 params = { 'action': 'bp_get_suggestions', 'term': query, 'type': 'members' }; 48 mentionsItem = mentionsQueryCache[ text ]; 49 if ( typeof mentionsItem === 'object' ) { 50 cb( mentionsItem ); 51 return; 52 } 180 53 181 if ( $.isNumeric( this.$inputor.data( 'suggestions-group-id' ) ) ) { 182 params['group-id'] = parseInt( this.$inputor.data( 'suggestions-group-id' ), 10 ); 54 return bp.apiRequest( { 55 path: 'buddypress/v1/members/?search=' + text, 56 type: 'GET' 57 } ).done( function( data ) { 58 var retval = $.map( data, 59 /** 60 * Create a composite index to determine ordering of results; 61 * nicename matches will appear on top. 62 * 63 * @param {array} suggestion A suggestion's original data. 64 * @return {array} A suggestion's new data. 65 * @since 2.1.0 66 */ 67 function( suggestion ) { 68 suggestion.search = suggestion.user_login + ' ' + suggestion.name; 69 return suggestion; 183 70 } 184 185 self.xhr = $.getJSON( ajaxurl, params ) 186 /** 187 * Success callback for the @suggestions lookup. 188 * 189 * @param {object} response Details of users matching the query. 190 * @since 2.1.0 191 */ 192 .done(function( response ) { 193 if ( ! response.success ) { 194 return; 195 } 196 197 var data = $.map( response.data, 198 /** 199 * Create a composite index to determine ordering of results; 200 * nicename matches will appear on top. 201 * 202 * @param {array} suggestion A suggestion's original data. 203 * @return {array} A suggestion's new data. 204 * @since 2.1.0 205 */ 206 function( suggestion ) { 207 suggestion.search = suggestion.search || suggestion.ID + ' ' + suggestion.name; 208 return suggestion; 209 } 210 ); 211 212 mentionsQueryCache[ query ] = data; 213 render_view( data ); 214 }); 215 } 71 ); 72 73 mentionsQueryCache[ text ] = retval; 74 cb(retval); 75 } ).fail( function( error ) { 76 return error; 77 } ); 78 }; 79 80 var tributeParams = { 81 values: debouncer( function (text, cb) { 82 remoteSearch(text, users => cb(users)); 83 }, 250), 84 lookup: 'search', 85 fillAttr: 'user_login', 86 menuItemTemplate: function (item) { 87 return '<img src="' + item.original.avatar_urls.thumb + '" alt="Profile picture of ' + item.original.name + '"> @' + item.string; 216 88 }, 89 }; 217 90 218 data: $.map( options.data, 219 /** 220 * Create a composite index to search against of nicename + display name. 221 * This will also determine ordering of results, so nicename matches will appear on top. 222 * 223 * @param {array} suggestion A suggestion's original data. 224 * @return {array} A suggestion's new data. 225 * @since 2.1.0 226 */ 227 function( suggestion ) { 228 suggestion.search = suggestion.search || suggestion.ID + ' ' + suggestion.name; 229 return suggestion; 230 } 231 ), 232 233 at: '@', 234 searchKey: 'search', 235 displayTpl: '<li data-value="@${ID}"><img src="${image}" alt="" /><span class="username">@${ID}</span><small>${name}</small></li>' 236 }, 91 var tribute = new Tribute( tributeParams ); 237 92 238 opts = $.extend( true, {}, suggestionsDefaults, mentionsDefaults, options ); 239 return $.fn.atwho.call( this, opts ); 93 $( this ).each( function() { 94 tribute.attach( document.getElementById( $( this ).attr( "id" ) ) ); 95 }); 240 96 }; 241 97 242 $( document ).ready( function() {243 // Activity/reply, post comments, dashboard post 'text' editor.244 $( '.bp-suggestions, #comments form textarea, . wp-editor-area' ).bp_mentions( bp.mentions.users );98 $( document ).ready( function() { 99 // Activity/reply, post comments, bp-nouveau messages composer. 100 $( '.bp-suggestions, #comments form textarea, .bp-messages-content .send-to-input' ).bp_mentions( bp.mentions.users ); 245 101 }); 246 102 247 bp.mentions.tinyMCEinit = function() {248 if ( typeof window.tinyMCE === 'undefined' || window.tinyMCE.activeEditor === null || typeof window.tinyMCE.activeEditor === 'undefined' ) {249 return;250 } else {251 $( window.tinyMCE.activeEditor.contentDocument.activeElement )252 .atwho( 'setIframe', $( '.wp-editor-wrap iframe' )[0] )253 .bp_mentions( bp.mentions.users );254 }255 };256 103 })( bp, jQuery ); -
src/bp-core/bp-core-cssjs.php
diff --git a/src/bp-core/bp-core-cssjs.php b/src/bp-core/bp-core-cssjs.php index 5e5691f51..3d9f06022 100644
a b function bp_core_register_common_scripts() { 70 70 'bp-jquery-cookie' => array( 'file' => "{$url}vendor/jquery-cookie{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ), 71 71 'bp-jquery-scroll-to' => array( 'file' => "{$url}vendor/jquery-scroll-to{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => false ), 72 72 73 // Version 2.1.74 'jquery-caret' => array( 'file' => "{$url}vendor/jquery.caret{$min}.js", 'dependencies' => array( 'jquery' ), 'footer' => true ),75 'jquery-atwho' => array( 'file' => "{$url}vendor/jquery.atwho{$min}.js", 'dependencies' => array( 'jquery', 'jquery-caret' ), 'footer' => true ),76 77 73 // Version 2.3. 78 74 'bp-plupload' => array( 'file' => "{$url}bp-plupload{$min}.js", 'dependencies' => array( 'plupload', 'jquery', 'json2', 'wp-backbone' ), 'footer' => true ), 79 75 'bp-avatar' => array( 'file' => "{$url}avatar{$min}.js", 'dependencies' => array( 'jcrop' ), 'footer' => true ), … … function bp_core_register_common_scripts() { 85 81 // Version 2.7. 86 82 'bp-moment' => array( 'file' => "{$url}vendor/moment-js/moment{$min}.js", 'dependencies' => array(), 'footer' => true ), 87 83 'bp-livestamp' => array( 'file' => "{$url}vendor/livestamp{$min}.js", 'dependencies' => array( 'jquery', 'bp-moment' ), 'footer' => true ), 84 85 // Version 8 86 'zurb-tribute' => array( 'file' => "{$url}vendor/tribute{$min}.js", 'dependencies' => array(), 'footer' => true ), 88 87 ); 89 88 90 89 // Version 2.7 - Add Moment.js locale to our $scripts array if we found one. -
deleted file src/bp-core/js/vendor/jquery.atwho.js
diff --git a/src/bp-core/js/vendor/jquery.atwho.js b/src/bp-core/js/vendor/jquery.atwho.js deleted file mode 100755 index b0685d2b1..000000000
+ - 1 /**2 * at.js - 1.5.43 * Copyright (c) 2017 chord.luo <chord.luo@gmail.com>;4 * Homepage: http://ichord.github.com/At.js5 * License: MIT6 */7 (function (root, factory) {8 if (typeof define === 'function' && define.amd) {9 // AMD. Register as an anonymous module unless amdModuleId is set.10 define(["jquery"], function (a0) {11 return (factory(a0));12 });13 } else if (typeof exports === 'object') {14 // Node. Does not work with strict CommonJS, but15 // only CommonJS-like environments that support module.exports,16 // like Node.17 module.exports = factory(require("jquery"));18 } else {19 factory(jQuery);20 }21 }(this, function ($) {22 var DEFAULT_CALLBACKS, KEY_CODE;23 24 KEY_CODE = {25 ESC: 27,26 TAB: 9,27 ENTER: 13,28 CTRL: 17,29 A: 65,30 P: 80,31 N: 78,32 LEFT: 37,33 UP: 38,34 RIGHT: 39,35 DOWN: 40,36 BACKSPACE: 8,37 SPACE: 3238 };39 40 DEFAULT_CALLBACKS = {41 beforeSave: function(data) {42 return Controller.arrayToDefaultHash(data);43 },44 matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) {45 var _a, _y, match, regexp, space;46 flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");47 if (should_startWithSpace) {48 flag = '(?:^|\\s)' + flag;49 }50 _a = decodeURI("%C3%80");51 _y = decodeURI("%C3%BF");52 space = acceptSpaceBar ? "\ " : "";53 regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi');54 match = regexp.exec(subtext);55 if (match) {56 return match[2] || match[1];57 } else {58 return null;59 }60 },61 filter: function(query, data, searchKey) {62 var _results, i, item, len;63 _results = [];64 for (i = 0, len = data.length; i < len; i++) {65 item = data[i];66 if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) {67 _results.push(item);68 }69 }70 return _results;71 },72 remoteFilter: null,73 sorter: function(query, items, searchKey) {74 var _results, i, item, len;75 if (!query) {76 return items;77 }78 _results = [];79 for (i = 0, len = items.length; i < len; i++) {80 item = items[i];81 item.atwho_order = new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase());82 if (item.atwho_order > -1) {83 _results.push(item);84 }85 }86 return _results.sort(function(a, b) {87 return a.atwho_order - b.atwho_order;88 });89 },90 tplEval: function(tpl, map) {91 var error, error1, template;92 template = tpl;93 try {94 if (typeof tpl !== 'string') {95 template = tpl(map);96 }97 return template.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) {98 return map[key];99 });100 } catch (error1) {101 error = error1;102 return "";103 }104 },105 highlighter: function(li, query) {106 var regexp;107 if (!query) {108 return li;109 }110 regexp = new RegExp(">\\s*([^\<]*?)(" + query.replace("+", "\\+") + ")([^\<]*)\\s*<", 'ig');111 return li.replace(regexp, function(str, $1, $2, $3) {112 return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <';113 });114 },115 beforeInsert: function(value, $li, e) {116 return value;117 },118 beforeReposition: function(offset) {119 return offset;120 },121 afterMatchFailed: function(at, el) {}122 };123 124 var App;125 126 App = (function() {127 function App(inputor) {128 this.currentFlag = null;129 this.controllers = {};130 this.aliasMaps = {};131 this.$inputor = $(inputor);132 this.setupRootElement();133 this.listen();134 }135 136 App.prototype.createContainer = function(doc) {137 var ref;138 if ((ref = this.$el) != null) {139 ref.remove();140 }141 return $(doc.body).append(this.$el = $("<div class='atwho-container'></div>"));142 };143 144 App.prototype.setupRootElement = function(iframe, asRoot) {145 var error, error1;146 if (asRoot == null) {147 asRoot = false;148 }149 if (iframe) {150 this.window = iframe.contentWindow;151 this.document = iframe.contentDocument || this.window.document;152 this.iframe = iframe;153 } else {154 this.document = this.$inputor[0].ownerDocument;155 this.window = this.document.defaultView || this.document.parentWindow;156 try {157 this.iframe = this.window.frameElement;158 } catch (error1) {159 error = error1;160 this.iframe = null;161 if ($.fn.atwho.debug) {162 throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n" + error);163 }164 }165 }166 return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document);167 };168 169 App.prototype.controller = function(at) {170 var c, current, currentFlag, ref;171 if (this.aliasMaps[at]) {172 current = this.controllers[this.aliasMaps[at]];173 } else {174 ref = this.controllers;175 for (currentFlag in ref) {176 c = ref[currentFlag];177 if (currentFlag === at) {178 current = c;179 break;180 }181 }182 }183 if (current) {184 return current;185 } else {186 return this.controllers[this.currentFlag];187 }188 };189 190 App.prototype.setContextFor = function(at) {191 this.currentFlag = at;192 return this;193 };194 195 App.prototype.reg = function(flag, setting) {196 var base, controller;197 controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag));198 if (setting.alias) {199 this.aliasMaps[setting.alias] = flag;200 }201 controller.init(setting);202 return this;203 };204 205 App.prototype.listen = function() {206 return this.$inputor.on('compositionstart', (function(_this) {207 return function(e) {208 var ref;209 if ((ref = _this.controller()) != null) {210 ref.view.hide();211 }212 _this.isComposing = true;213 return null;214 };215 })(this)).on('compositionend', (function(_this) {216 return function(e) {217 _this.isComposing = false;218 setTimeout(function(e) {219 return _this.dispatch(e);220 });221 return null;222 };223 })(this)).on('keyup.atwhoInner', (function(_this) {224 return function(e) {225 return _this.onKeyup(e);226 };227 })(this)).on('keydown.atwhoInner', (function(_this) {228 return function(e) {229 return _this.onKeydown(e);230 };231 })(this)).on('blur.atwhoInner', (function(_this) {232 return function(e) {233 var c;234 if (c = _this.controller()) {235 c.expectedQueryCBId = null;236 return c.view.hide(e, c.getOpt("displayTimeout"));237 }238 };239 })(this)).on('click.atwhoInner', (function(_this) {240 return function(e) {241 return _this.dispatch(e);242 };243 })(this)).on('scroll.atwhoInner', (function(_this) {244 return function() {245 var lastScrollTop;246 lastScrollTop = _this.$inputor.scrollTop();247 return function(e) {248 var currentScrollTop, ref;249 currentScrollTop = e.target.scrollTop;250 if (lastScrollTop !== currentScrollTop) {251 if ((ref = _this.controller()) != null) {252 ref.view.hide(e);253 }254 }255 lastScrollTop = currentScrollTop;256 return true;257 };258 };259 })(this)());260 };261 262 App.prototype.shutdown = function() {263 var _, c, ref;264 ref = this.controllers;265 for (_ in ref) {266 c = ref[_];267 c.destroy();268 delete this.controllers[_];269 }270 this.$inputor.off('.atwhoInner');271 return this.$el.remove();272 };273 274 App.prototype.dispatch = function(e) {275 var _, c, ref, results;276 ref = this.controllers;277 results = [];278 for (_ in ref) {279 c = ref[_];280 results.push(c.lookUp(e));281 }282 return results;283 };284 285 App.prototype.onKeyup = function(e) {286 var ref;287 switch (e.keyCode) {288 case KEY_CODE.ESC:289 e.preventDefault();290 if ((ref = this.controller()) != null) {291 ref.view.hide();292 }293 break;294 case KEY_CODE.DOWN:295 case KEY_CODE.UP:296 case KEY_CODE.CTRL:297 case KEY_CODE.ENTER:298 $.noop();299 break;300 case KEY_CODE.P:301 case KEY_CODE.N:302 if (!e.ctrlKey) {303 this.dispatch(e);304 }305 break;306 default:307 this.dispatch(e);308 }309 };310 311 App.prototype.onKeydown = function(e) {312 var ref, view;313 view = (ref = this.controller()) != null ? ref.view : void 0;314 if (!(view && view.visible())) {315 return;316 }317 switch (e.keyCode) {318 case KEY_CODE.ESC:319 e.preventDefault();320 view.hide(e);321 break;322 case KEY_CODE.UP:323 e.preventDefault();324 view.prev();325 break;326 case KEY_CODE.DOWN:327 e.preventDefault();328 view.next();329 break;330 case KEY_CODE.P:331 if (!e.ctrlKey) {332 return;333 }334 e.preventDefault();335 view.prev();336 break;337 case KEY_CODE.N:338 if (!e.ctrlKey) {339 return;340 }341 e.preventDefault();342 view.next();343 break;344 case KEY_CODE.TAB:345 case KEY_CODE.ENTER:346 case KEY_CODE.SPACE:347 if (!view.visible()) {348 return;349 }350 if (!this.controller().getOpt('spaceSelectsMatch') && e.keyCode === KEY_CODE.SPACE) {351 return;352 }353 if (!this.controller().getOpt('tabSelectsMatch') && e.keyCode === KEY_CODE.TAB) {354 return;355 }356 if (view.highlighted()) {357 e.preventDefault();358 view.choose(e);359 } else {360 view.hide(e);361 }362 break;363 default:364 $.noop();365 }366 };367 368 return App;369 370 })();371 372 var Controller,373 slice = [].slice;374 375 Controller = (function() {376 Controller.prototype.uid = function() {377 return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime());378 };379 380 function Controller(app, at1) {381 this.app = app;382 this.at = at1;383 this.$inputor = this.app.$inputor;384 this.id = this.$inputor[0].id || this.uid();385 this.expectedQueryCBId = null;386 this.setting = null;387 this.query = null;388 this.pos = 0;389 this.range = null;390 if ((this.$el = $("#atwho-ground-" + this.id, this.app.$el)).length === 0) {391 this.app.$el.append(this.$el = $("<div id='atwho-ground-" + this.id + "'></div>"));392 }393 this.model = new Model(this);394 this.view = new View(this);395 }396 397 Controller.prototype.init = function(setting) {398 this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting);399 this.view.init();400 return this.model.reload(this.setting.data);401 };402 403 Controller.prototype.destroy = function() {404 this.trigger('beforeDestroy');405 this.model.destroy();406 this.view.destroy();407 return this.$el.remove();408 };409 410 Controller.prototype.callDefault = function() {411 var args, error, error1, funcName;412 funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];413 try {414 return DEFAULT_CALLBACKS[funcName].apply(this, args);415 } catch (error1) {416 error = error1;417 return $.error(error + " Or maybe At.js doesn't have function " + funcName);418 }419 };420 421 Controller.prototype.trigger = function(name, data) {422 var alias, eventName;423 if (data == null) {424 data = [];425 }426 data.push(this);427 alias = this.getOpt('alias');428 eventName = alias ? name + "-" + alias + ".atwho" : name + ".atwho";429 return this.$inputor.trigger(eventName, data);430 };431 432 Controller.prototype.callbacks = function(funcName) {433 return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName];434 };435 436 Controller.prototype.getOpt = function(at, default_value) {437 var e, error1;438 try {439 return this.setting[at];440 } catch (error1) {441 e = error1;442 return null;443 }444 };445 446 Controller.prototype.insertContentFor = function($li) {447 var data, tpl;448 tpl = this.getOpt('insertTpl');449 data = $.extend({}, $li.data('item-data'), {450 'atwho-at': this.at451 });452 return this.callbacks("tplEval").call(this, tpl, data, "onInsert");453 };454 455 Controller.prototype.renderView = function(data) {456 var searchKey;457 searchKey = this.getOpt("searchKey");458 data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), searchKey);459 return this.view.render(data.slice(0, this.getOpt('limit')));460 };461 462 Controller.arrayToDefaultHash = function(data) {463 var i, item, len, results;464 if (!$.isArray(data)) {465 return data;466 }467 results = [];468 for (i = 0, len = data.length; i < len; i++) {469 item = data[i];470 if ($.isPlainObject(item)) {471 results.push(item);472 } else {473 results.push({474 name: item475 });476 }477 }478 return results;479 };480 481 Controller.prototype.lookUp = function(e) {482 var query, wait;483 if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) {484 return;485 }486 if (this.getOpt('suspendOnComposing') && this.app.isComposing) {487 return;488 }489 query = this.catchQuery(e);490 if (!query) {491 this.expectedQueryCBId = null;492 return query;493 }494 this.app.setContextFor(this.at);495 if (wait = this.getOpt('delay')) {496 this._delayLookUp(query, wait);497 } else {498 this._lookUp(query);499 }500 return query;501 };502 503 Controller.prototype._delayLookUp = function(query, wait) {504 var now, remaining;505 now = Date.now ? Date.now() : new Date().getTime();506 this.previousCallTime || (this.previousCallTime = now);507 remaining = wait - (now - this.previousCallTime);508 if ((0 < remaining && remaining < wait)) {509 this.previousCallTime = now;510 this._stopDelayedCall();511 return this.delayedCallTimeout = setTimeout((function(_this) {512 return function() {513 _this.previousCallTime = 0;514 _this.delayedCallTimeout = null;515 return _this._lookUp(query);516 };517 })(this), wait);518 } else {519 this._stopDelayedCall();520 if (this.previousCallTime !== now) {521 this.previousCallTime = 0;522 }523 return this._lookUp(query);524 }525 };526 527 Controller.prototype._stopDelayedCall = function() {528 if (this.delayedCallTimeout) {529 clearTimeout(this.delayedCallTimeout);530 return this.delayedCallTimeout = null;531 }532 };533 534 Controller.prototype._generateQueryCBId = function() {535 return {};536 };537 538 Controller.prototype._lookUp = function(query) {539 var _callback;540 _callback = function(queryCBId, data) {541 if (queryCBId !== this.expectedQueryCBId) {542 return;543 }544 if (data && data.length > 0) {545 return this.renderView(this.constructor.arrayToDefaultHash(data));546 } else {547 return this.view.hide();548 }549 };550 this.expectedQueryCBId = this._generateQueryCBId();551 return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId));552 };553 554 return Controller;555 556 })();557 558 var TextareaController,559 extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },560 hasProp = {}.hasOwnProperty;561 562 TextareaController = (function(superClass) {563 extend(TextareaController, superClass);564 565 function TextareaController() {566 return TextareaController.__super__.constructor.apply(this, arguments);567 }568 569 TextareaController.prototype.catchQuery = function() {570 var caretPos, content, end, isString, query, start, subtext;571 content = this.$inputor.val();572 caretPos = this.$inputor.caret('pos', {573 iframe: this.app.iframe574 });575 subtext = content.slice(0, caretPos);576 query = this.callbacks("matcher").call(this, this.at, subtext, this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));577 isString = typeof query === 'string';578 if (isString && query.length < this.getOpt('minLen', 0)) {579 return;580 }581 if (isString && query.length <= this.getOpt('maxLen', 20)) {582 start = caretPos - query.length;583 end = start + query.length;584 this.pos = start;585 query = {586 'text': query,587 'headPos': start,588 'endPos': end589 };590 this.trigger("matched", [this.at, query.text]);591 } else {592 query = null;593 this.view.hide();594 }595 return this.query = query;596 };597 598 TextareaController.prototype.rect = function() {599 var c, iframeOffset, scaleBottom;600 if (!(c = this.$inputor.caret('offset', this.pos - 1, {601 iframe: this.app.iframe602 }))) {603 return;604 }605 if (this.app.iframe && !this.app.iframeAsRoot) {606 iframeOffset = $(this.app.iframe).offset();607 c.left += iframeOffset.left;608 c.top += iframeOffset.top;609 }610 scaleBottom = this.app.document.selection ? 0 : 2;611 return {612 left: c.left,613 top: c.top,614 bottom: c.top + c.height + scaleBottom615 };616 };617 618 TextareaController.prototype.insert = function(content, $li) {619 var $inputor, source, startStr, suffix, text;620 $inputor = this.$inputor;621 source = $inputor.val();622 startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0));623 suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " ";624 content += suffix;625 text = "" + startStr + content + (source.slice(this.query['endPos'] || 0));626 $inputor.val(text);627 $inputor.caret('pos', startStr.length + content.length, {628 iframe: this.app.iframe629 });630 if (!$inputor.is(':focus')) {631 $inputor.focus();632 }633 return $inputor.change();634 };635 636 return TextareaController;637 638 })(Controller);639 640 var EditableController,641 extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },642 hasProp = {}.hasOwnProperty;643 644 EditableController = (function(superClass) {645 extend(EditableController, superClass);646 647 function EditableController() {648 return EditableController.__super__.constructor.apply(this, arguments);649 }650 651 EditableController.prototype._getRange = function() {652 var sel;653 sel = this.app.window.getSelection();654 if (sel.rangeCount > 0) {655 return sel.getRangeAt(0);656 }657 };658 659 EditableController.prototype._setRange = function(position, node, range) {660 if (range == null) {661 range = this._getRange();662 }663 if (!(range && node)) {664 return;665 }666 node = $(node)[0];667 if (position === 'after') {668 range.setEndAfter(node);669 range.setStartAfter(node);670 } else {671 range.setEndBefore(node);672 range.setStartBefore(node);673 }674 range.collapse(false);675 return this._clearRange(range);676 };677 678 EditableController.prototype._clearRange = function(range) {679 var sel;680 if (range == null) {681 range = this._getRange();682 }683 sel = this.app.window.getSelection();684 if (this.ctrl_a_pressed == null) {685 sel.removeAllRanges();686 return sel.addRange(range);687 }688 };689 690 EditableController.prototype._movingEvent = function(e) {691 var ref;692 return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN);693 };694 695 EditableController.prototype._unwrap = function(node) {696 var next;697 node = $(node).unwrap().get(0);698 if ((next = node.nextSibling) && next.nodeValue) {699 node.nodeValue += next.nodeValue;700 $(next).remove();701 }702 return node;703 };704 705 EditableController.prototype.catchQuery = function(e) {706 var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range;707 if (!(range = this._getRange())) {708 return;709 }710 if (!range.collapsed) {711 return;712 }713 if (e.which === KEY_CODE.ENTER) {714 ($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap();715 if ($query.is(':empty')) {716 $query.remove();717 }718 ($query = $(".atwho-query", this.app.document)).text($query.text()).contents().last().unwrap();719 this._clearRange();720 return;721 }722 if (/firefox/i.test(navigator.userAgent)) {723 if ($(range.startContainer).is(this.$inputor)) {724 this._clearRange();725 return;726 }727 if (e.which === KEY_CODE.BACKSPACE && range.startContainer.nodeType === document.ELEMENT_NODE && (offset = range.startOffset - 1) >= 0) {728 _range = range.cloneRange();729 _range.setStart(range.startContainer, offset);730 if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) {731 inserted = $(range.startContainer).contents().get(offset);732 this._setRange('after', $(inserted).contents().last());733 }734 } else if (e.which === KEY_CODE.LEFT && range.startContainer.nodeType === document.TEXT_NODE) {735 $inserted = $(range.startContainer.previousSibling);736 if ($inserted.is('.atwho-inserted') && range.startOffset === 0) {737 this._setRange('after', $inserted.contents().last());738 }739 }740 }741 $(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query');742 if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) {743 $query.remove();744 }745 if (!this._movingEvent(e)) {746 $query.removeClass('atwho-inserted');747 }748 if ($query.length > 0) {749 switch (e.which) {750 case KEY_CODE.LEFT:751 this._setRange('before', $query.get(0), range);752 $query.removeClass('atwho-query');753 return;754 case KEY_CODE.RIGHT:755 this._setRange('after', $query.get(0).nextSibling, range);756 $query.removeClass('atwho-query');757 return;758 }759 }760 if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) {761 $query.empty().html(query_content).attr('data-atwho-at-query', null);762 this._setRange('after', $query.get(0), range);763 }764 _range = range.cloneRange();765 _range.setStart(range.startContainer, 0);766 matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));767 isString = typeof matched === 'string';768 if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) {769 range.setStart(range.startContainer, index);770 $query = $('<span/>', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query');771 range.surroundContents($query.get(0));772 lastNode = $query.contents().last().get(0);773 if (lastNode) {774 if (/firefox/i.test(navigator.userAgent)) {775 range.setStart(lastNode, lastNode.length);776 range.setEnd(lastNode, lastNode.length);777 this._clearRange(range);778 } else {779 this._setRange('after', lastNode, range);780 }781 }782 }783 if (isString && matched.length < this.getOpt('minLen', 0)) {784 return;785 }786 if (isString && matched.length <= this.getOpt('maxLen', 20)) {787 query = {788 text: matched,789 el: $query790 };791 this.trigger("matched", [this.at, query.text]);792 return this.query = query;793 } else {794 this.view.hide();795 this.query = {796 el: $query797 };798 if ($query.text().indexOf(this.at) >= 0) {799 if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) {800 $query.removeClass('atwho-query');801 } else if (false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)) {802 this._setRange("after", this._unwrap($query.text($query.text()).contents().first()));803 }804 }805 return null;806 }807 };808 809 EditableController.prototype.rect = function() {810 var $iframe, iframeOffset, rect;811 rect = this.query.el.offset();812 if (!(rect && this.query.el[0].getClientRects().length)) {813 return;814 }815 if (this.app.iframe && !this.app.iframeAsRoot) {816 iframeOffset = ($iframe = $(this.app.iframe)).offset();817 rect.left += iframeOffset.left - this.$inputor.scrollLeft();818 rect.top += iframeOffset.top - this.$inputor.scrollTop();819 }820 rect.bottom = rect.top + this.query.el.height();821 return rect;822 };823 824 EditableController.prototype.insert = function(content, $li) {825 var data, overrides, range, suffix, suffixNode;826 if (!this.$inputor.is(':focus')) {827 this.$inputor.focus();828 }829 overrides = this.getOpt('functionOverrides');830 if (overrides.insert) {831 return overrides.insert.call(this, content, $li);832 }833 suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0";834 data = $li.data('item-data');835 this.query.el.removeClass('atwho-query').addClass('atwho-inserted').html(content).attr('data-atwho-at-query', "" + data['atwho-at'] + this.query.text).attr('contenteditable', "false");836 if (range = this._getRange()) {837 if (this.query.el.length) {838 range.setEndAfter(this.query.el[0]);839 }840 range.collapse(false);841 range.insertNode(suffixNode = this.app.document.createTextNode("" + suffix));842 this._setRange('after', suffixNode, range);843 }844 if (!this.$inputor.is(':focus')) {845 this.$inputor.focus();846 }847 return this.$inputor.change();848 };849 850 return EditableController;851 852 })(Controller);853 854 var Model;855 856 Model = (function() {857 function Model(context) {858 this.context = context;859 this.at = this.context.at;860 this.storage = this.context.$inputor;861 }862 863 Model.prototype.destroy = function() {864 return this.storage.data(this.at, null);865 };866 867 Model.prototype.saved = function() {868 return this.fetch() > 0;869 };870 871 Model.prototype.query = function(query, callback) {872 var _remoteFilter, data, searchKey;873 data = this.fetch();874 searchKey = this.context.getOpt("searchKey");875 data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || [];876 _remoteFilter = this.context.callbacks('remoteFilter');877 if (data.length > 0 || (!_remoteFilter && data.length === 0)) {878 return callback(data);879 } else {880 return _remoteFilter.call(this.context, query, callback);881 }882 };883 884 Model.prototype.fetch = function() {885 return this.storage.data(this.at) || [];886 };887 888 Model.prototype.save = function(data) {889 return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || []));890 };891 892 Model.prototype.load = function(data) {893 if (!(this.saved() || !data)) {894 return this._load(data);895 }896 };897 898 Model.prototype.reload = function(data) {899 return this._load(data);900 };901 902 Model.prototype._load = function(data) {903 if (typeof data === "string") {904 return $.ajax(data, {905 dataType: "json"906 }).done((function(_this) {907 return function(data) {908 return _this.save(data);909 };910 })(this));911 } else {912 return this.save(data);913 }914 };915 916 return Model;917 918 })();919 920 var View;921 922 View = (function() {923 function View(context) {924 this.context = context;925 this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>");926 this.$elUl = this.$el.children();927 this.timeoutID = null;928 this.context.$el.append(this.$el);929 this.bindEvent();930 }931 932 View.prototype.init = function() {933 var header_tpl, id;934 id = this.context.getOpt("alias") || this.context.at.charCodeAt(0);935 header_tpl = this.context.getOpt("headerTpl");936 if (header_tpl && this.$el.children().length === 1) {937 this.$el.prepend(header_tpl);938 }939 return this.$el.attr({940 'id': "at-view-" + id941 });942 };943 944 View.prototype.destroy = function() {945 return this.$el.remove();946 };947 948 View.prototype.bindEvent = function() {949 var $menu, lastCoordX, lastCoordY;950 $menu = this.$el.find('ul');951 lastCoordX = 0;952 lastCoordY = 0;953 return $menu.on('mousemove.atwho-view', 'li', (function(_this) {954 return function(e) {955 var $cur;956 if (lastCoordX === e.clientX && lastCoordY === e.clientY) {957 return;958 }959 lastCoordX = e.clientX;960 lastCoordY = e.clientY;961 $cur = $(e.currentTarget);962 if ($cur.hasClass('cur')) {963 return;964 }965 $menu.find('.cur').removeClass('cur');966 return $cur.addClass('cur');967 };968 })(this)).on('click.atwho-view', 'li', (function(_this) {969 return function(e) {970 $menu.find('.cur').removeClass('cur');971 $(e.currentTarget).addClass('cur');972 _this.choose(e);973 return e.preventDefault();974 };975 })(this));976 };977 978 View.prototype.visible = function() {979 return $.expr.filters.visible(this.$el[0]);980 };981 982 View.prototype.highlighted = function() {983 return this.$el.find(".cur").length > 0;984 };985 986 View.prototype.choose = function(e) {987 var $li, content;988 if (($li = this.$el.find(".cur")).length) {989 content = this.context.insertContentFor($li);990 this.context._stopDelayedCall();991 this.context.insert(this.context.callbacks("beforeInsert").call(this.context, content, $li, e), $li);992 this.context.trigger("inserted", [$li, e]);993 this.hide(e);994 }995 if (this.context.getOpt("hideWithoutSuffix")) {996 return this.stopShowing = true;997 }998 };999 1000 View.prototype.reposition = function(rect) {1001 var _window, offset, overflowOffset, ref;1002 _window = this.context.app.iframeAsRoot ? this.context.app.window : window;1003 if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) {1004 rect.bottom = rect.top - this.$el.height();1005 }1006 if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) {1007 rect.left = overflowOffset;1008 }1009 offset = {1010 left: rect.left,1011 top: rect.bottom1012 };1013 if ((ref = this.context.callbacks("beforeReposition")) != null) {1014 ref.call(this.context, offset);1015 }1016 this.$el.offset(offset);1017 return this.context.trigger("reposition", [offset]);1018 };1019 1020 View.prototype.next = function() {1021 var cur, next, nextEl, offset;1022 cur = this.$el.find('.cur').removeClass('cur');1023 next = cur.next();1024 if (!next.length) {1025 next = this.$el.find('li:first');1026 }1027 next.addClass('cur');1028 nextEl = next[0];1029 offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0);1030 return this.scrollTop(Math.max(0, offset - this.$el.height()));1031 };1032 1033 View.prototype.prev = function() {1034 var cur, offset, prev, prevEl;1035 cur = this.$el.find('.cur').removeClass('cur');1036 prev = cur.prev();1037 if (!prev.length) {1038 prev = this.$el.find('li:last');1039 }1040 prev.addClass('cur');1041 prevEl = prev[0];1042 offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0);1043 return this.scrollTop(Math.max(0, offset - this.$el.height()));1044 };1045 1046 View.prototype.scrollTop = function(scrollTop) {1047 var scrollDuration;1048 scrollDuration = this.context.getOpt('scrollDuration');1049 if (scrollDuration) {1050 return this.$elUl.animate({1051 scrollTop: scrollTop1052 }, scrollDuration);1053 } else {1054 return this.$elUl.scrollTop(scrollTop);1055 }1056 };1057 1058 View.prototype.show = function() {1059 var rect;1060 if (this.stopShowing) {1061 this.stopShowing = false;1062 return;1063 }1064 if (!this.visible()) {1065 this.$el.show();1066 this.$el.scrollTop(0);1067 this.context.trigger('shown');1068 }1069 if (rect = this.context.rect()) {1070 return this.reposition(rect);1071 }1072 };1073 1074 View.prototype.hide = function(e, time) {1075 var callback;1076 if (!this.visible()) {1077 return;1078 }1079 if (isNaN(time)) {1080 this.$el.hide();1081 return this.context.trigger('hidden', [e]);1082 } else {1083 callback = (function(_this) {1084 return function() {1085 return _this.hide();1086 };1087 })(this);1088 clearTimeout(this.timeoutID);1089 return this.timeoutID = setTimeout(callback, time);1090 }1091 };1092 1093 View.prototype.render = function(list) {1094 var $li, $ul, i, item, len, li, tpl;1095 if (!($.isArray(list) && list.length > 0)) {1096 this.hide();1097 return;1098 }1099 this.$el.find('ul').empty();1100 $ul = this.$el.find('ul');1101 tpl = this.context.getOpt('displayTpl');1102 for (i = 0, len = list.length; i < len; i++) {1103 item = list[i];1104 item = $.extend({}, item, {1105 'atwho-at': this.context.at1106 });1107 li = this.context.callbacks("tplEval").call(this.context, tpl, item, "onDisplay");1108 $li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text));1109 $li.data("item-data", item);1110 $ul.append($li);1111 }1112 this.show();1113 if (this.context.getOpt('highlightFirst')) {1114 return $ul.find("li:first").addClass("cur");1115 }1116 };1117 1118 return View;1119 1120 })();1121 1122 var Api;1123 1124 Api = {1125 load: function(at, data) {1126 var c;1127 if (c = this.controller(at)) {1128 return c.model.load(data);1129 }1130 },1131 isSelecting: function() {1132 var ref;1133 return !!((ref = this.controller()) != null ? ref.view.visible() : void 0);1134 },1135 hide: function() {1136 var ref;1137 return (ref = this.controller()) != null ? ref.view.hide() : void 0;1138 },1139 reposition: function() {1140 var c;1141 if (c = this.controller()) {1142 return c.view.reposition(c.rect());1143 }1144 },1145 setIframe: function(iframe, asRoot) {1146 this.setupRootElement(iframe, asRoot);1147 return null;1148 },1149 run: function() {1150 return this.dispatch();1151 },1152 destroy: function() {1153 this.shutdown();1154 return this.$inputor.data('atwho', null);1155 }1156 };1157 1158 $.fn.atwho = function(method) {1159 var _args, result;1160 _args = arguments;1161 result = null;1162 this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function() {1163 var $this, app;1164 if (!(app = ($this = $(this)).data("atwho"))) {1165 $this.data('atwho', (app = new App(this)));1166 }1167 if (typeof method === 'object' || !method) {1168 return app.reg(method.at, method);1169 } else if (Api[method] && app) {1170 return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1));1171 } else {1172 return $.error("Method " + method + " does not exist on jQuery.atwho");1173 }1174 });1175 if (result != null) {1176 return result;1177 } else {1178 return this;1179 }1180 };1181 1182 $.fn.atwho["default"] = {1183 at: void 0,1184 alias: void 0,1185 data: null,1186 displayTpl: "<li>${name}</li>",1187 insertTpl: "${atwho-at}${name}",1188 headerTpl: null,1189 callbacks: DEFAULT_CALLBACKS,1190 functionOverrides: {},1191 searchKey: "name",1192 suffix: void 0,1193 hideWithoutSuffix: false,1194 startWithSpace: true,1195 acceptSpaceBar: false,1196 highlightFirst: true,1197 limit: 5,1198 maxLen: 20,1199 minLen: 0,1200 displayTimeout: 300,1201 delay: null,1202 spaceSelectsMatch: false,1203 tabSelectsMatch: true,1204 editableAtwhoQueryAttrs: {},1205 scrollDuration: 150,1206 suspendOnComposing: true,1207 lookUpOnClick: true1208 };1209 1210 $.fn.atwho.debug = false;1211 1212 })); -
deleted file src/bp-core/js/vendor/jquery.atwho.txt
diff --git a/src/bp-core/js/vendor/jquery.atwho.txt b/src/bp-core/js/vendor/jquery.atwho.txt deleted file mode 100644 index 36cd1c122..000000000
+ - 1 Copyright (c) 2013 chord.luo@gmail.com2 3 Permission is hereby granted, free of charge, to any person4 obtaining a copy of this software and associated documentation5 files (the "Software"), to deal in the Software without6 restriction, including without limitation the rights to use,7 copy, modify, merge, publish, distribute, sublicense, and/or sell8 copies of the Software, and to permit persons to whom the9 Software is furnished to do so, subject to the following10 conditions:11 12 The above copyright notice and this permission notice shall be13 included in all copies or substantial portions of the Software.14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES17 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT19 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR22 OTHER DEALINGS IN THE SOFTWARE. -
deleted file src/bp-core/js/vendor/jquery.caret.js
diff --git a/src/bp-core/js/vendor/jquery.caret.js b/src/bp-core/js/vendor/jquery.caret.js deleted file mode 100755 index a63ceda93..000000000
+ - 1 (function (root, factory) {2 if (typeof define === 'function' && define.amd) {3 // AMD. Register as an anonymous module.4 define(["jquery"], function ($) {5 return (root.returnExportsGlobal = factory($));6 });7 } else if (typeof exports === 'object') {8 // Node. Does not work with strict CommonJS, but9 // only CommonJS-like environments that support module.exports,10 // like Node.11 module.exports = factory(require("jquery"));12 } else {13 factory(jQuery);14 }15 }(this, function ($) {16 17 /*18 Implement Github like autocomplete mentions19 http://ichord.github.com/At.js20 21 Copyright (c) 2013 chord.luo@gmail.com22 Licensed under the MIT license.23 */24 25 /*26 本插件操作 textarea 或者 input 内的插入符27 只实现了获得插入符在文本框中的位置,我设置28 插入符的位置.29 */30 31 "use strict";32 var EditableCaret, InputCaret, Mirror, Utils, discoveryIframeOf, methods, oDocument, oFrame, oWindow, pluginName, setContextBy;33 34 pluginName = 'caret';35 36 EditableCaret = (function() {37 function EditableCaret($inputor) {38 this.$inputor = $inputor;39 this.domInputor = this.$inputor[0];40 }41 42 EditableCaret.prototype.setPos = function(pos) {43 var fn, found, offset, sel;44 if (sel = oWindow.getSelection()) {45 offset = 0;46 found = false;47 (fn = function(pos, parent) {48 var node, range, _i, _len, _ref, _results;49 _ref = parent.childNodes;50 _results = [];51 for (_i = 0, _len = _ref.length; _i < _len; _i++) {52 node = _ref[_i];53 if (found) {54 break;55 }56 if (node.nodeType === 3) {57 if (offset + node.length >= pos) {58 found = true;59 range = oDocument.createRange();60 range.setStart(node, pos - offset);61 sel.removeAllRanges();62 sel.addRange(range);63 break;64 } else {65 _results.push(offset += node.length);66 }67 } else {68 _results.push(fn(pos, node));69 }70 }71 return _results;72 })(pos, this.domInputor);73 }74 return this.domInputor;75 };76 77 EditableCaret.prototype.getIEPosition = function() {78 return this.getPosition();79 };80 81 EditableCaret.prototype.getPosition = function() {82 var inputor_offset, offset;83 offset = this.getOffset();84 inputor_offset = this.$inputor.offset();85 offset.left -= inputor_offset.left;86 offset.top -= inputor_offset.top;87 return offset;88 };89 90 EditableCaret.prototype.getOldIEPos = function() {91 var preCaretTextRange, textRange;92 textRange = oDocument.selection.createRange();93 preCaretTextRange = oDocument.body.createTextRange();94 preCaretTextRange.moveToElementText(this.domInputor);95 preCaretTextRange.setEndPoint("EndToEnd", textRange);96 return preCaretTextRange.text.length;97 };98 99 EditableCaret.prototype.getPos = function() {100 var clonedRange, pos, range;101 if (range = this.range()) {102 clonedRange = range.cloneRange();103 clonedRange.selectNodeContents(this.domInputor);104 clonedRange.setEnd(range.endContainer, range.endOffset);105 pos = clonedRange.toString().length;106 clonedRange.detach();107 return pos;108 } else if (oDocument.selection) {109 return this.getOldIEPos();110 }111 };112 113 EditableCaret.prototype.getOldIEOffset = function() {114 var range, rect;115 range = oDocument.selection.createRange().duplicate();116 range.moveStart("character", -1);117 rect = range.getBoundingClientRect();118 return {119 height: rect.bottom - rect.top,120 left: rect.left,121 top: rect.top122 };123 };124 125 EditableCaret.prototype.getOffset = function(pos) {126 var clonedRange, offset, range, rect, shadowCaret;127 if (oWindow.getSelection && (range = this.range())) {128 if (range.endOffset - 1 > 0 && range.endContainer !== this.domInputor) {129 clonedRange = range.cloneRange();130 clonedRange.setStart(range.endContainer, range.endOffset - 1);131 clonedRange.setEnd(range.endContainer, range.endOffset);132 rect = clonedRange.getBoundingClientRect();133 offset = {134 height: rect.height,135 left: rect.left + rect.width,136 top: rect.top137 };138 clonedRange.detach();139 }140 if (!offset || (offset != null ? offset.height : void 0) === 0) {141 clonedRange = range.cloneRange();142 shadowCaret = $(oDocument.createTextNode("|"));143 clonedRange.insertNode(shadowCaret[0]);144 clonedRange.selectNode(shadowCaret[0]);145 rect = clonedRange.getBoundingClientRect();146 offset = {147 height: rect.height,148 left: rect.left,149 top: rect.top150 };151 shadowCaret.remove();152 clonedRange.detach();153 }154 } else if (oDocument.selection) {155 offset = this.getOldIEOffset();156 }157 if (offset) {158 offset.top += $(oWindow).scrollTop();159 offset.left += $(oWindow).scrollLeft();160 }161 return offset;162 };163 164 EditableCaret.prototype.range = function() {165 var sel;166 if (!oWindow.getSelection) {167 return;168 }169 sel = oWindow.getSelection();170 if (sel.rangeCount > 0) {171 return sel.getRangeAt(0);172 } else {173 return null;174 }175 };176 177 return EditableCaret;178 179 })();180 181 InputCaret = (function() {182 function InputCaret($inputor) {183 this.$inputor = $inputor;184 this.domInputor = this.$inputor[0];185 }186 187 InputCaret.prototype.getIEPos = function() {188 var endRange, inputor, len, normalizedValue, pos, range, textInputRange;189 inputor = this.domInputor;190 range = oDocument.selection.createRange();191 pos = 0;192 if (range && range.parentElement() === inputor) {193 normalizedValue = inputor.value.replace(/\r\n/g, "\n");194 len = normalizedValue.length;195 textInputRange = inputor.createTextRange();196 textInputRange.moveToBookmark(range.getBookmark());197 endRange = inputor.createTextRange();198 endRange.collapse(false);199 if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {200 pos = len;201 } else {202 pos = -textInputRange.moveStart("character", -len);203 }204 }205 return pos;206 };207 208 InputCaret.prototype.getPos = function() {209 if (oDocument.selection) {210 return this.getIEPos();211 } else {212 return this.domInputor.selectionStart;213 }214 };215 216 InputCaret.prototype.setPos = function(pos) {217 var inputor, range;218 inputor = this.domInputor;219 if (oDocument.selection) {220 range = inputor.createTextRange();221 range.move("character", pos);222 range.select();223 } else if (inputor.setSelectionRange) {224 inputor.setSelectionRange(pos, pos);225 }226 return inputor;227 };228 229 InputCaret.prototype.getIEOffset = function(pos) {230 var h, textRange, x, y;231 textRange = this.domInputor.createTextRange();232 pos || (pos = this.getPos());233 textRange.move('character', pos);234 x = textRange.boundingLeft;235 y = textRange.boundingTop;236 h = textRange.boundingHeight;237 return {238 left: x,239 top: y,240 height: h241 };242 };243 244 InputCaret.prototype.getOffset = function(pos) {245 var $inputor, offset, position;246 $inputor = this.$inputor;247 if (oDocument.selection) {248 offset = this.getIEOffset(pos);249 offset.top += $(oWindow).scrollTop() + $inputor.scrollTop();250 offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft();251 return offset;252 } else {253 offset = $inputor.offset();254 position = this.getPosition(pos);255 return offset = {256 left: offset.left + position.left - $inputor.scrollLeft(),257 top: offset.top + position.top - $inputor.scrollTop(),258 height: position.height259 };260 }261 };262 263 InputCaret.prototype.getPosition = function(pos) {264 var $inputor, at_rect, end_range, format, html, mirror, start_range;265 $inputor = this.$inputor;266 format = function(value) {267 value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, "<br/>");268 if (/firefox/i.test(navigator.userAgent)) {269 value = value.replace(/\s/g, ' ');270 }271 return value;272 };273 if (pos === void 0) {274 pos = this.getPos();275 }276 start_range = $inputor.val().slice(0, pos);277 end_range = $inputor.val().slice(pos);278 html = "<span style='position: relative; display: inline;'>" + format(start_range) + "</span>";279 html += "<span id='caret' style='position: relative; display: inline;'>|</span>";280 html += "<span style='position: relative; display: inline;'>" + format(end_range) + "</span>";281 mirror = new Mirror($inputor);282 return at_rect = mirror.create(html).rect();283 };284 285 InputCaret.prototype.getIEPosition = function(pos) {286 var h, inputorOffset, offset, x, y;287 offset = this.getIEOffset(pos);288 inputorOffset = this.$inputor.offset();289 x = offset.left - inputorOffset.left;290 y = offset.top - inputorOffset.top;291 h = offset.height;292 return {293 left: x,294 top: y,295 height: h296 };297 };298 299 return InputCaret;300 301 })();302 303 Mirror = (function() {304 Mirror.prototype.css_attr = ["borderBottomWidth", "borderLeftWidth", "borderRightWidth", "borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle", "borderTopWidth", "boxSizing", "fontFamily", "fontSize", "fontWeight", "height", "letterSpacing", "lineHeight", "marginBottom", "marginLeft", "marginRight", "marginTop", "outlineWidth", "overflow", "overflowX", "overflowY", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textAlign", "textOverflow", "textTransform", "whiteSpace", "wordBreak", "wordWrap"];305 306 function Mirror($inputor) {307 this.$inputor = $inputor;308 }309 310 Mirror.prototype.mirrorCss = function() {311 var css,312 _this = this;313 css = {314 position: 'absolute',315 left: -9999,316 top: 0,317 zIndex: -20000318 };319 if (this.$inputor.prop('tagName') === 'TEXTAREA') {320 this.css_attr.push('width');321 }322 $.each(this.css_attr, function(i, p) {323 return css[p] = _this.$inputor.css(p);324 });325 return css;326 };327 328 Mirror.prototype.create = function(html) {329 this.$mirror = $('<div></div>');330 this.$mirror.css(this.mirrorCss());331 this.$mirror.html(html);332 this.$inputor.after(this.$mirror);333 return this;334 };335 336 Mirror.prototype.rect = function() {337 var $flag, pos, rect;338 $flag = this.$mirror.find("#caret");339 pos = $flag.position();340 rect = {341 left: pos.left,342 top: pos.top,343 height: $flag.height()344 };345 this.$mirror.remove();346 return rect;347 };348 349 return Mirror;350 351 })();352 353 Utils = {354 contentEditable: function($inputor) {355 return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true');356 }357 };358 359 methods = {360 pos: function(pos) {361 if (pos || pos === 0) {362 return this.setPos(pos);363 } else {364 return this.getPos();365 }366 },367 position: function(pos) {368 if (oDocument.selection) {369 return this.getIEPosition(pos);370 } else {371 return this.getPosition(pos);372 }373 },374 offset: function(pos) {375 var offset;376 offset = this.getOffset(pos);377 return offset;378 }379 };380 381 oDocument = null;382 383 oWindow = null;384 385 oFrame = null;386 387 setContextBy = function(settings) {388 var iframe;389 if (iframe = settings != null ? settings.iframe : void 0) {390 oFrame = iframe;391 oWindow = iframe.contentWindow;392 return oDocument = iframe.contentDocument || oWindow.document;393 } else {394 oFrame = void 0;395 oWindow = window;396 return oDocument = document;397 }398 };399 400 discoveryIframeOf = function($dom) {401 var error;402 oDocument = $dom[0].ownerDocument;403 oWindow = oDocument.defaultView || oDocument.parentWindow;404 try {405 return oFrame = oWindow.frameElement;406 } catch (_error) {407 error = _error;408 }409 };410 411 $.fn.caret = function(method, value, settings) {412 var caret;413 if (methods[method]) {414 if ($.isPlainObject(value)) {415 setContextBy(value);416 value = void 0;417 } else {418 setContextBy(settings);419 }420 caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this);421 return methods[method].apply(caret, [value]);422 } else {423 return $.error("Method " + method + " does not exist on jQuery.caret");424 }425 };426 427 $.fn.caret.EditableCaret = EditableCaret;428 429 $.fn.caret.InputCaret = InputCaret;430 431 $.fn.caret.Utils = Utils;432 433 $.fn.caret.apis = methods;434 435 436 })); -
deleted file src/bp-core/js/vendor/jquery.caret.txt
diff --git a/src/bp-core/js/vendor/jquery.caret.txt b/src/bp-core/js/vendor/jquery.caret.txt deleted file mode 100644 index 36cd1c122..000000000
+ - 1 Copyright (c) 2013 chord.luo@gmail.com2 3 Permission is hereby granted, free of charge, to any person4 obtaining a copy of this software and associated documentation5 files (the "Software"), to deal in the Software without6 restriction, including without limitation the rights to use,7 copy, modify, merge, publish, distribute, sublicense, and/or sell8 copies of the Software, and to permit persons to whom the9 Software is furnished to do so, subject to the following10 conditions:11 12 The above copyright notice and this permission notice shall be13 included in all copies or substantial portions of the Software.14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES17 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT19 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR22 OTHER DEALINGS IN THE SOFTWARE. -
new file src/bp-core/js/vendor/tribute.js
diff --git a/src/bp-core/js/vendor/tribute.js b/src/bp-core/js/vendor/tribute.js new file mode 100644 index 000000000..9c46e6357
- + 1 (function (global, factory) { 2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 typeof define === 'function' && define.amd ? define(factory) : 4 (global = global || self, global.Tribute = factory()); 5 }(this, (function () { 'use strict'; 6 7 function _classCallCheck(instance, Constructor) { 8 if (!(instance instanceof Constructor)) { 9 throw new TypeError("Cannot call a class as a function"); 10 } 11 } 12 13 function _defineProperties(target, props) { 14 for (var i = 0; i < props.length; i++) { 15 var descriptor = props[i]; 16 descriptor.enumerable = descriptor.enumerable || false; 17 descriptor.configurable = true; 18 if ("value" in descriptor) descriptor.writable = true; 19 Object.defineProperty(target, descriptor.key, descriptor); 20 } 21 } 22 23 function _createClass(Constructor, protoProps, staticProps) { 24 if (protoProps) _defineProperties(Constructor.prototype, protoProps); 25 if (staticProps) _defineProperties(Constructor, staticProps); 26 return Constructor; 27 } 28 29 function _slicedToArray(arr, i) { 30 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); 31 } 32 33 function _arrayWithHoles(arr) { 34 if (Array.isArray(arr)) return arr; 35 } 36 37 function _iterableToArrayLimit(arr, i) { 38 if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; 39 var _arr = []; 40 var _n = true; 41 var _d = false; 42 var _e = undefined; 43 44 try { 45 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { 46 _arr.push(_s.value); 47 48 if (i && _arr.length === i) break; 49 } 50 } catch (err) { 51 _d = true; 52 _e = err; 53 } finally { 54 try { 55 if (!_n && _i["return"] != null) _i["return"](); 56 } finally { 57 if (_d) throw _e; 58 } 59 } 60 61 return _arr; 62 } 63 64 function _unsupportedIterableToArray(o, minLen) { 65 if (!o) return; 66 if (typeof o === "string") return _arrayLikeToArray(o, minLen); 67 var n = Object.prototype.toString.call(o).slice(8, -1); 68 if (n === "Object" && o.constructor) n = o.constructor.name; 69 if (n === "Map" || n === "Set") return Array.from(n); 70 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); 71 } 72 73 function _arrayLikeToArray(arr, len) { 74 if (len == null || len > arr.length) len = arr.length; 75 76 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; 77 78 return arr2; 79 } 80 81 function _nonIterableRest() { 82 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); 83 } 84 85 if (!Array.prototype.find) { 86 Array.prototype.find = function (predicate) { 87 if (this === null) { 88 throw new TypeError('Array.prototype.find called on null or undefined'); 89 } 90 91 if (typeof predicate !== 'function') { 92 throw new TypeError('predicate must be a function'); 93 } 94 95 var list = Object(this); 96 var length = list.length >>> 0; 97 var thisArg = arguments[1]; 98 var value; 99 100 for (var i = 0; i < length; i++) { 101 value = list[i]; 102 103 if (predicate.call(thisArg, value, i, list)) { 104 return value; 105 } 106 } 107 108 return undefined; 109 }; 110 } 111 112 if (window && typeof window.CustomEvent !== "function") { 113 var CustomEvent$1 = function CustomEvent(event, params) { 114 params = params || { 115 bubbles: false, 116 cancelable: false, 117 detail: undefined 118 }; 119 var evt = document.createEvent('CustomEvent'); 120 evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); 121 return evt; 122 }; 123 124 if (typeof window.Event !== 'undefined') { 125 CustomEvent$1.prototype = window.Event.prototype; 126 } 127 128 window.CustomEvent = CustomEvent$1; 129 } 130 131 var TributeEvents = /*#__PURE__*/function () { 132 function TributeEvents(tribute) { 133 _classCallCheck(this, TributeEvents); 134 135 this.tribute = tribute; 136 this.tribute.events = this; 137 } 138 139 _createClass(TributeEvents, [{ 140 key: "bind", 141 value: function bind(element) { 142 element.boundKeydown = this.keydown.bind(element, this); 143 element.boundKeyup = this.keyup.bind(element, this); 144 element.boundInput = this.input.bind(element, this); 145 element.addEventListener("keydown", element.boundKeydown, false); 146 element.addEventListener("keyup", element.boundKeyup, false); 147 element.addEventListener("input", element.boundInput, false); 148 } 149 }, { 150 key: "unbind", 151 value: function unbind(element) { 152 element.removeEventListener("keydown", element.boundKeydown, false); 153 element.removeEventListener("keyup", element.boundKeyup, false); 154 element.removeEventListener("input", element.boundInput, false); 155 delete element.boundKeydown; 156 delete element.boundKeyup; 157 delete element.boundInput; 158 } 159 }, { 160 key: "keydown", 161 value: function keydown(instance, event) { 162 if (instance.shouldDeactivate(event)) { 163 instance.tribute.isActive = false; 164 instance.tribute.hideMenu(); 165 } 166 167 var element = this; 168 instance.commandEvent = false; 169 TributeEvents.keys().forEach(function (o) { 170 if (o.key === event.keyCode) { 171 instance.commandEvent = true; 172 instance.callbacks()[o.value.toLowerCase()](event, element); 173 } 174 }); 175 } 176 }, { 177 key: "input", 178 value: function input(instance, event) { 179 instance.inputEvent = true; 180 instance.keyup.call(this, instance, event); 181 } 182 }, { 183 key: "click", 184 value: function click(instance, event) { 185 var tribute = instance.tribute; 186 187 if (tribute.menu && tribute.menu.contains(event.target)) { 188 var li = event.target; 189 event.preventDefault(); 190 event.stopPropagation(); 191 192 while (li.nodeName.toLowerCase() !== "li") { 193 li = li.parentNode; 194 195 if (!li || li === tribute.menu) { 196 throw new Error("cannot find the <li> container for the click"); 197 } 198 } 199 200 tribute.selectItemAtIndex(li.getAttribute("data-index"), event); 201 tribute.hideMenu(); // TODO: should fire with externalTrigger and target is outside of menu 202 } else if (tribute.current.element && !tribute.current.externalTrigger) { 203 tribute.current.externalTrigger = false; 204 setTimeout(function () { 205 return tribute.hideMenu(); 206 }); 207 } 208 } 209 }, { 210 key: "keyup", 211 value: function keyup(instance, event) { 212 if (instance.inputEvent) { 213 instance.inputEvent = false; 214 } 215 216 instance.updateSelection(this); 217 if (event.keyCode === 27) return; 218 219 if (!instance.tribute.allowSpaces && instance.tribute.hasTrailingSpace) { 220 instance.tribute.hasTrailingSpace = false; 221 instance.commandEvent = true; 222 instance.callbacks()["space"](event, this); 223 return; 224 } 225 226 if (!instance.tribute.isActive) { 227 if (instance.tribute.autocompleteMode) { 228 instance.callbacks().triggerChar(event, this, ""); 229 } else { 230 var keyCode = instance.getKeyCode(instance, this, event); 231 if (isNaN(keyCode) || !keyCode) return; 232 var trigger = instance.tribute.triggers().find(function (trigger) { 233 return trigger.charCodeAt(0) === keyCode; 234 }); 235 236 if (typeof trigger !== "undefined") { 237 instance.callbacks().triggerChar(event, this, trigger); 238 } 239 } 240 } 241 242 if (instance.tribute.current.mentionText.length < instance.tribute.current.collection.menuShowMinLength) { 243 return; 244 } 245 246 if ((instance.tribute.current.trigger || instance.tribute.autocompleteMode) && instance.commandEvent === false || instance.tribute.isActive && event.keyCode === 8) { 247 instance.tribute.showMenuFor(this, true); 248 } 249 } 250 }, { 251 key: "shouldDeactivate", 252 value: function shouldDeactivate(event) { 253 if (!this.tribute.isActive) return false; 254 255 if (this.tribute.current.mentionText.length === 0) { 256 var eventKeyPressed = false; 257 TributeEvents.keys().forEach(function (o) { 258 if (event.keyCode === o.key) eventKeyPressed = true; 259 }); 260 return !eventKeyPressed; 261 } 262 263 return false; 264 } 265 }, { 266 key: "getKeyCode", 267 value: function getKeyCode(instance, el, event) { 268 269 var tribute = instance.tribute; 270 var info = tribute.range.getTriggerInfo(false, tribute.hasTrailingSpace, true, tribute.allowSpaces, tribute.autocompleteMode); 271 272 if (info) { 273 return info.mentionTriggerChar.charCodeAt(0); 274 } else { 275 return false; 276 } 277 } 278 }, { 279 key: "updateSelection", 280 value: function updateSelection(el) { 281 this.tribute.current.element = el; 282 var info = this.tribute.range.getTriggerInfo(false, this.tribute.hasTrailingSpace, true, this.tribute.allowSpaces, this.tribute.autocompleteMode); 283 284 if (info) { 285 this.tribute.current.selectedPath = info.mentionSelectedPath; 286 this.tribute.current.mentionText = info.mentionText; 287 this.tribute.current.selectedOffset = info.mentionSelectedOffset; 288 } 289 } 290 }, { 291 key: "callbacks", 292 value: function callbacks() { 293 var _this = this; 294 295 return { 296 triggerChar: function triggerChar(e, el, trigger) { 297 var tribute = _this.tribute; 298 tribute.current.trigger = trigger; 299 var collectionItem = tribute.collection.find(function (item) { 300 return item.trigger === trigger; 301 }); 302 tribute.current.collection = collectionItem; 303 304 if (tribute.current.mentionText.length >= tribute.current.collection.menuShowMinLength && tribute.inputEvent) { 305 tribute.showMenuFor(el, true); 306 } 307 }, 308 enter: function enter(e, el) { 309 // choose selection 310 if (_this.tribute.isActive && _this.tribute.current.filteredItems) { 311 e.preventDefault(); 312 e.stopPropagation(); 313 setTimeout(function () { 314 _this.tribute.selectItemAtIndex(_this.tribute.menuSelected, e); 315 316 _this.tribute.hideMenu(); 317 }, 0); 318 } 319 }, 320 escape: function escape(e, el) { 321 if (_this.tribute.isActive) { 322 e.preventDefault(); 323 e.stopPropagation(); 324 _this.tribute.isActive = false; 325 326 _this.tribute.hideMenu(); 327 } 328 }, 329 tab: function tab(e, el) { 330 // choose first match 331 _this.callbacks().enter(e, el); 332 }, 333 space: function space(e, el) { 334 if (_this.tribute.isActive) { 335 if (_this.tribute.spaceSelectsMatch) { 336 _this.callbacks().enter(e, el); 337 } else if (!_this.tribute.allowSpaces) { 338 e.stopPropagation(); 339 setTimeout(function () { 340 _this.tribute.hideMenu(); 341 342 _this.tribute.isActive = false; 343 }, 0); 344 } 345 } 346 }, 347 up: function up(e, el) { 348 // navigate up ul 349 if (_this.tribute.isActive && _this.tribute.current.filteredItems) { 350 e.preventDefault(); 351 e.stopPropagation(); 352 var count = _this.tribute.current.filteredItems.length, 353 selected = _this.tribute.menuSelected; 354 355 if (count > selected && selected > 0) { 356 _this.tribute.menuSelected--; 357 358 _this.setActiveLi(); 359 } else if (selected === 0) { 360 _this.tribute.menuSelected = count - 1; 361 362 _this.setActiveLi(); 363 364 _this.tribute.menu.scrollTop = _this.tribute.menu.scrollHeight; 365 } 366 } 367 }, 368 down: function down(e, el) { 369 // navigate down ul 370 if (_this.tribute.isActive && _this.tribute.current.filteredItems) { 371 e.preventDefault(); 372 e.stopPropagation(); 373 var count = _this.tribute.current.filteredItems.length - 1, 374 selected = _this.tribute.menuSelected; 375 376 if (count > selected) { 377 _this.tribute.menuSelected++; 378 379 _this.setActiveLi(); 380 } else if (count === selected) { 381 _this.tribute.menuSelected = 0; 382 383 _this.setActiveLi(); 384 385 _this.tribute.menu.scrollTop = 0; 386 } 387 } 388 }, 389 "delete": function _delete(e, el) { 390 if (_this.tribute.isActive && _this.tribute.current.mentionText.length < 1) { 391 _this.tribute.hideMenu(); 392 } else if (_this.tribute.isActive) { 393 _this.tribute.showMenuFor(el); 394 } 395 } 396 }; 397 } 398 }, { 399 key: "setActiveLi", 400 value: function setActiveLi(index) { 401 var lis = this.tribute.menu.querySelectorAll("li"), 402 length = lis.length >>> 0; 403 if (index) this.tribute.menuSelected = parseInt(index); 404 405 for (var i = 0; i < length; i++) { 406 var li = lis[i]; 407 408 if (i === this.tribute.menuSelected) { 409 li.classList.add(this.tribute.current.collection.selectClass); 410 var liClientRect = li.getBoundingClientRect(); 411 var menuClientRect = this.tribute.menu.getBoundingClientRect(); 412 413 if (liClientRect.bottom > menuClientRect.bottom) { 414 var scrollDistance = liClientRect.bottom - menuClientRect.bottom; 415 this.tribute.menu.scrollTop += scrollDistance; 416 } else if (liClientRect.top < menuClientRect.top) { 417 var _scrollDistance = menuClientRect.top - liClientRect.top; 418 419 this.tribute.menu.scrollTop -= _scrollDistance; 420 } 421 } else { 422 li.classList.remove(this.tribute.current.collection.selectClass); 423 } 424 } 425 } 426 }, { 427 key: "getFullHeight", 428 value: function getFullHeight(elem, includeMargin) { 429 var height = elem.getBoundingClientRect().height; 430 431 if (includeMargin) { 432 var style = elem.currentStyle || window.getComputedStyle(elem); 433 return height + parseFloat(style.marginTop) + parseFloat(style.marginBottom); 434 } 435 436 return height; 437 } 438 }], [{ 439 key: "keys", 440 value: function keys() { 441 return [{ 442 key: 9, 443 value: "TAB" 444 }, { 445 key: 8, 446 value: "DELETE" 447 }, { 448 key: 13, 449 value: "ENTER" 450 }, { 451 key: 27, 452 value: "ESCAPE" 453 }, { 454 key: 32, 455 value: "SPACE" 456 }, { 457 key: 38, 458 value: "UP" 459 }, { 460 key: 40, 461 value: "DOWN" 462 }]; 463 } 464 }]); 465 466 return TributeEvents; 467 }(); 468 469 var TributeMenuEvents = /*#__PURE__*/function () { 470 function TributeMenuEvents(tribute) { 471 _classCallCheck(this, TributeMenuEvents); 472 473 this.tribute = tribute; 474 this.tribute.menuEvents = this; 475 this.menu = this.tribute.menu; 476 } 477 478 _createClass(TributeMenuEvents, [{ 479 key: "bind", 480 value: function bind(menu) { 481 var _this = this; 482 483 this.menuClickEvent = this.tribute.events.click.bind(null, this); 484 this.menuContainerScrollEvent = this.debounce(function () { 485 if (_this.tribute.isActive) { 486 _this.tribute.showMenuFor(_this.tribute.current.element, false); 487 } 488 }, 300, false); 489 this.windowResizeEvent = this.debounce(function () { 490 if (_this.tribute.isActive) { 491 _this.tribute.range.positionMenuAtCaret(true); 492 } 493 }, 300, false); // fixes IE11 issues with mousedown 494 495 this.tribute.range.getDocument().addEventListener("MSPointerDown", this.menuClickEvent, false); 496 this.tribute.range.getDocument().addEventListener("mousedown", this.menuClickEvent, false); 497 window.addEventListener("resize", this.windowResizeEvent); 498 499 if (this.menuContainer) { 500 this.menuContainer.addEventListener("scroll", this.menuContainerScrollEvent, false); 501 } else { 502 window.addEventListener("scroll", this.menuContainerScrollEvent); 503 } 504 } 505 }, { 506 key: "unbind", 507 value: function unbind(menu) { 508 this.tribute.range.getDocument().removeEventListener("mousedown", this.menuClickEvent, false); 509 this.tribute.range.getDocument().removeEventListener("MSPointerDown", this.menuClickEvent, false); 510 window.removeEventListener("resize", this.windowResizeEvent); 511 512 if (this.menuContainer) { 513 this.menuContainer.removeEventListener("scroll", this.menuContainerScrollEvent, false); 514 } else { 515 window.removeEventListener("scroll", this.menuContainerScrollEvent); 516 } 517 } 518 }, { 519 key: "debounce", 520 value: function debounce(func, wait, immediate) { 521 var _arguments = arguments, 522 _this2 = this; 523 524 var timeout; 525 return function () { 526 var context = _this2, 527 args = _arguments; 528 529 var later = function later() { 530 timeout = null; 531 if (!immediate) func.apply(context, args); 532 }; 533 534 var callNow = immediate && !timeout; 535 clearTimeout(timeout); 536 timeout = setTimeout(later, wait); 537 if (callNow) func.apply(context, args); 538 }; 539 } 540 }]); 541 542 return TributeMenuEvents; 543 }(); 544 545 var TributeRange = /*#__PURE__*/function () { 546 function TributeRange(tribute) { 547 _classCallCheck(this, TributeRange); 548 549 this.tribute = tribute; 550 this.tribute.range = this; 551 } 552 553 _createClass(TributeRange, [{ 554 key: "getDocument", 555 value: function getDocument() { 556 var iframe; 557 558 if (this.tribute.current.collection) { 559 iframe = this.tribute.current.collection.iframe; 560 } 561 562 if (!iframe) { 563 return document; 564 } 565 566 return iframe.contentWindow.document; 567 } 568 }, { 569 key: "positionMenuAtCaret", 570 value: function positionMenuAtCaret(scrollTo) { 571 var _this = this; 572 573 var context = this.tribute.current, 574 coordinates; 575 var info = this.getTriggerInfo(false, this.tribute.hasTrailingSpace, true, this.tribute.allowSpaces, this.tribute.autocompleteMode); 576 577 if (typeof info !== 'undefined') { 578 if (!this.tribute.positionMenu) { 579 this.tribute.menu.style.cssText = "display: block;"; 580 return; 581 } 582 583 if (!this.isContentEditable(context.element)) { 584 coordinates = this.getTextAreaOrInputUnderlinePosition(this.tribute.current.element, info.mentionPosition); 585 } else { 586 coordinates = this.getContentEditableCaretPosition(info.mentionPosition); 587 } 588 589 this.tribute.menu.style.cssText = "top: ".concat(coordinates.top, "px;\n left: ").concat(coordinates.left, "px;\n right: ").concat(coordinates.right, "px;\n bottom: ").concat(coordinates.bottom, "px;\n position: absolute;\n display: block;"); 590 591 if (coordinates.left === 'auto') { 592 this.tribute.menu.style.left = 'auto'; 593 } 594 595 if (coordinates.top === 'auto') { 596 this.tribute.menu.style.top = 'auto'; 597 } 598 599 if (scrollTo) this.scrollIntoView(); 600 window.setTimeout(function () { 601 var menuDimensions = { 602 width: _this.tribute.menu.offsetWidth, 603 height: _this.tribute.menu.offsetHeight 604 }; 605 606 var menuIsOffScreen = _this.isMenuOffScreen(coordinates, menuDimensions); 607 608 var menuIsOffScreenHorizontally = window.innerWidth > menuDimensions.width && (menuIsOffScreen.left || menuIsOffScreen.right); 609 var menuIsOffScreenVertically = window.innerHeight > menuDimensions.height && (menuIsOffScreen.top || menuIsOffScreen.bottom); 610 611 if (menuIsOffScreenHorizontally || menuIsOffScreenVertically) { 612 _this.tribute.menu.style.cssText = 'display: none'; 613 614 _this.positionMenuAtCaret(scrollTo); 615 } 616 }, 0); 617 } else { 618 this.tribute.menu.style.cssText = 'display: none'; 619 } 620 } 621 }, { 622 key: "selectElement", 623 value: function selectElement(targetElement, path, offset) { 624 var range; 625 var elem = targetElement; 626 627 if (path) { 628 for (var i = 0; i < path.length; i++) { 629 elem = elem.childNodes[path[i]]; 630 631 if (elem === undefined) { 632 return; 633 } 634 635 while (elem.length < offset) { 636 offset -= elem.length; 637 elem = elem.nextSibling; 638 } 639 640 if (elem.childNodes.length === 0 && !elem.length) { 641 elem = elem.previousSibling; 642 } 643 } 644 } 645 646 var sel = this.getWindowSelection(); 647 range = this.getDocument().createRange(); 648 range.setStart(elem, offset); 649 range.setEnd(elem, offset); 650 range.collapse(true); 651 652 try { 653 sel.removeAllRanges(); 654 } catch (error) {} 655 656 sel.addRange(range); 657 targetElement.focus(); 658 } 659 }, { 660 key: "replaceTriggerText", 661 value: function replaceTriggerText(text, requireLeadingSpace, hasTrailingSpace, originalEvent, item) { 662 var info = this.getTriggerInfo(true, hasTrailingSpace, requireLeadingSpace, this.tribute.allowSpaces, this.tribute.autocompleteMode); 663 664 if (info !== undefined) { 665 var context = this.tribute.current; 666 var replaceEvent = new CustomEvent('tribute-replaced', { 667 detail: { 668 item: item, 669 instance: context, 670 context: info, 671 event: originalEvent 672 } 673 }); 674 675 if (!this.isContentEditable(context.element)) { 676 var myField = this.tribute.current.element; 677 var textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : ' '; 678 text += textSuffix; 679 var startPos = info.mentionPosition; 680 var endPos = info.mentionPosition + info.mentionText.length + textSuffix.length; 681 682 if (!this.tribute.autocompleteMode) { 683 endPos += info.mentionTriggerChar.length - 1; 684 } 685 686 myField.value = myField.value.substring(0, startPos) + text + myField.value.substring(endPos, myField.value.length); 687 myField.selectionStart = startPos + text.length; 688 myField.selectionEnd = startPos + text.length; 689 } else { 690 // add a space to the end of the pasted text 691 var _textSuffix = typeof this.tribute.replaceTextSuffix == 'string' ? this.tribute.replaceTextSuffix : '\xA0'; 692 693 text += _textSuffix; 694 695 var _endPos = info.mentionPosition + info.mentionText.length; 696 697 if (!this.tribute.autocompleteMode) { 698 _endPos += info.mentionTriggerChar.length; 699 } 700 701 this.pasteHtml(text, info.mentionPosition, _endPos); 702 } 703 704 context.element.dispatchEvent(new CustomEvent('input', { 705 bubbles: true 706 })); 707 context.element.dispatchEvent(replaceEvent); 708 } 709 } 710 }, { 711 key: "pasteHtml", 712 value: function pasteHtml(html, startPos, endPos) { 713 var range, sel; 714 sel = this.getWindowSelection(); 715 range = this.getDocument().createRange(); 716 range.setStart(sel.anchorNode, startPos); 717 range.setEnd(sel.anchorNode, endPos); 718 range.deleteContents(); 719 var el = this.getDocument().createElement('div'); 720 el.innerHTML = html; 721 var frag = this.getDocument().createDocumentFragment(), 722 node, 723 lastNode; 724 725 while (node = el.firstChild) { 726 lastNode = frag.appendChild(node); 727 } 728 729 range.insertNode(frag); // Preserve the selection 730 731 if (lastNode) { 732 range = range.cloneRange(); 733 range.setStartAfter(lastNode); 734 range.collapse(true); 735 sel.removeAllRanges(); 736 sel.addRange(range); 737 } 738 } 739 }, { 740 key: "getWindowSelection", 741 value: function getWindowSelection() { 742 if (this.tribute.collection.iframe) { 743 return this.tribute.collection.iframe.contentWindow.getSelection(); 744 } 745 746 return window.getSelection(); 747 } 748 }, { 749 key: "getNodePositionInParent", 750 value: function getNodePositionInParent(element) { 751 if (element.parentNode === null) { 752 return 0; 753 } 754 755 for (var i = 0; i < element.parentNode.childNodes.length; i++) { 756 var node = element.parentNode.childNodes[i]; 757 758 if (node === element) { 759 return i; 760 } 761 } 762 } 763 }, { 764 key: "getContentEditableSelectedPath", 765 value: function getContentEditableSelectedPath(ctx) { 766 var sel = this.getWindowSelection(); 767 var selected = sel.anchorNode; 768 var path = []; 769 var offset; 770 771 if (selected != null) { 772 var i; 773 var ce = selected.contentEditable; 774 775 while (selected !== null && ce !== 'true') { 776 i = this.getNodePositionInParent(selected); 777 path.push(i); 778 selected = selected.parentNode; 779 780 if (selected !== null) { 781 ce = selected.contentEditable; 782 } 783 } 784 785 path.reverse(); // getRangeAt may not exist, need alternative 786 787 offset = sel.getRangeAt(0).startOffset; 788 return { 789 selected: selected, 790 path: path, 791 offset: offset 792 }; 793 } 794 } 795 }, { 796 key: "getTextPrecedingCurrentSelection", 797 value: function getTextPrecedingCurrentSelection() { 798 var context = this.tribute.current, 799 text = ''; 800 801 if (!this.isContentEditable(context.element)) { 802 var textComponent = this.tribute.current.element; 803 804 if (textComponent) { 805 var startPos = textComponent.selectionStart; 806 807 if (textComponent.value && startPos >= 0) { 808 text = textComponent.value.substring(0, startPos); 809 } 810 } 811 } else { 812 var selectedElem = this.getWindowSelection().anchorNode; 813 814 if (selectedElem != null) { 815 var workingNodeContent = selectedElem.textContent; 816 var selectStartOffset = this.getWindowSelection().getRangeAt(0).startOffset; 817 818 if (workingNodeContent && selectStartOffset >= 0) { 819 text = workingNodeContent.substring(0, selectStartOffset); 820 } 821 } 822 } 823 824 return text; 825 } 826 }, { 827 key: "getLastWordInText", 828 value: function getLastWordInText(text) { 829 text = text.replace(/\u00A0/g, ' '); // https://stackoverflow.com/questions/29850407/how-do-i-replace-unicode-character-u00a0-with-a-space-in-javascript 830 831 var wordsArray; 832 833 if (this.tribute.autocompleteSeparator) { 834 wordsArray = text.split(this.tribute.autocompleteSeparator); 835 } else { 836 wordsArray = text.split(/\s+/); 837 } 838 839 var worldsCount = wordsArray.length - 1; 840 return wordsArray[worldsCount].trim(); 841 } 842 }, { 843 key: "getTriggerInfo", 844 value: function getTriggerInfo(menuAlreadyActive, hasTrailingSpace, requireLeadingSpace, allowSpaces, isAutocomplete) { 845 var _this2 = this; 846 847 var ctx = this.tribute.current; 848 var selected, path, offset; 849 850 if (!this.isContentEditable(ctx.element)) { 851 selected = this.tribute.current.element; 852 } else { 853 var selectionInfo = this.getContentEditableSelectedPath(ctx); 854 855 if (selectionInfo) { 856 selected = selectionInfo.selected; 857 path = selectionInfo.path; 858 offset = selectionInfo.offset; 859 } 860 } 861 862 var effectiveRange = this.getTextPrecedingCurrentSelection(); 863 var lastWordOfEffectiveRange = this.getLastWordInText(effectiveRange); 864 865 if (isAutocomplete) { 866 return { 867 mentionPosition: effectiveRange.length - lastWordOfEffectiveRange.length, 868 mentionText: lastWordOfEffectiveRange, 869 mentionSelectedElement: selected, 870 mentionSelectedPath: path, 871 mentionSelectedOffset: offset 872 }; 873 } 874 875 if (effectiveRange !== undefined && effectiveRange !== null) { 876 var mostRecentTriggerCharPos = -1; 877 var triggerChar; 878 this.tribute.collection.forEach(function (config) { 879 var c = config.trigger; 880 var idx = config.requireLeadingSpace ? _this2.lastIndexWithLeadingSpace(effectiveRange, c) : effectiveRange.lastIndexOf(c); 881 882 if (idx > mostRecentTriggerCharPos) { 883 mostRecentTriggerCharPos = idx; 884 triggerChar = c; 885 requireLeadingSpace = config.requireLeadingSpace; 886 } 887 }); 888 889 if (mostRecentTriggerCharPos >= 0 && (mostRecentTriggerCharPos === 0 || !requireLeadingSpace || /[\xA0\s]/g.test(effectiveRange.substring(mostRecentTriggerCharPos - 1, mostRecentTriggerCharPos)))) { 890 var currentTriggerSnippet = effectiveRange.substring(mostRecentTriggerCharPos + triggerChar.length, effectiveRange.length); 891 triggerChar = effectiveRange.substring(mostRecentTriggerCharPos, mostRecentTriggerCharPos + triggerChar.length); 892 var firstSnippetChar = currentTriggerSnippet.substring(0, 1); 893 var leadingSpace = currentTriggerSnippet.length > 0 && (firstSnippetChar === ' ' || firstSnippetChar === '\xA0'); 894 895 if (hasTrailingSpace) { 896 currentTriggerSnippet = currentTriggerSnippet.trim(); 897 } 898 899 var regex = allowSpaces ? /[^\S ]/g : /[\xA0\s]/g; 900 this.tribute.hasTrailingSpace = regex.test(currentTriggerSnippet); 901 902 if (!leadingSpace && (menuAlreadyActive || !regex.test(currentTriggerSnippet))) { 903 return { 904 mentionPosition: mostRecentTriggerCharPos, 905 mentionText: currentTriggerSnippet, 906 mentionSelectedElement: selected, 907 mentionSelectedPath: path, 908 mentionSelectedOffset: offset, 909 mentionTriggerChar: triggerChar 910 }; 911 } 912 } 913 } 914 } 915 }, { 916 key: "lastIndexWithLeadingSpace", 917 value: function lastIndexWithLeadingSpace(str, trigger) { 918 var reversedStr = str.split('').reverse().join(''); 919 var index = -1; 920 921 for (var cidx = 0, len = str.length; cidx < len; cidx++) { 922 var firstChar = cidx === str.length - 1; 923 var leadingSpace = /\s/.test(reversedStr[cidx + 1]); 924 var match = true; 925 926 for (var triggerIdx = trigger.length - 1; triggerIdx >= 0; triggerIdx--) { 927 if (trigger[triggerIdx] !== reversedStr[cidx - triggerIdx]) { 928 match = false; 929 break; 930 } 931 } 932 933 if (match && (firstChar || leadingSpace)) { 934 index = str.length - 1 - cidx; 935 break; 936 } 937 } 938 939 return index; 940 } 941 }, { 942 key: "isContentEditable", 943 value: function isContentEditable(element) { 944 return element.nodeName !== 'INPUT' && element.nodeName !== 'TEXTAREA'; 945 } 946 }, { 947 key: "isMenuOffScreen", 948 value: function isMenuOffScreen(coordinates, menuDimensions) { 949 var windowWidth = window.innerWidth; 950 var windowHeight = window.innerHeight; 951 var doc = document.documentElement; 952 var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0); 953 var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); 954 var menuTop = typeof coordinates.top === 'number' ? coordinates.top : windowTop + windowHeight - coordinates.bottom - menuDimensions.height; 955 var menuRight = typeof coordinates.right === 'number' ? coordinates.right : coordinates.left + menuDimensions.width; 956 var menuBottom = typeof coordinates.bottom === 'number' ? coordinates.bottom : coordinates.top + menuDimensions.height; 957 var menuLeft = typeof coordinates.left === 'number' ? coordinates.left : windowLeft + windowWidth - coordinates.right - menuDimensions.width; 958 return { 959 top: menuTop < Math.floor(windowTop), 960 right: menuRight > Math.ceil(windowLeft + windowWidth), 961 bottom: menuBottom > Math.ceil(windowTop + windowHeight), 962 left: menuLeft < Math.floor(windowLeft) 963 }; 964 } 965 }, { 966 key: "getMenuDimensions", 967 value: function getMenuDimensions() { 968 // Width of the menu depends of its contents and position 969 // We must check what its width would be without any obstruction 970 // This way, we can achieve good positioning for flipping the menu 971 var dimensions = { 972 width: null, 973 height: null 974 }; 975 this.tribute.menu.style.cssText = "top: 0px;\n left: 0px;\n position: fixed;\n display: block;\n visibility; hidden;"; 976 dimensions.width = this.tribute.menu.offsetWidth; 977 dimensions.height = this.tribute.menu.offsetHeight; 978 this.tribute.menu.style.cssText = "display: none;"; 979 return dimensions; 980 } 981 }, { 982 key: "getTextAreaOrInputUnderlinePosition", 983 value: function getTextAreaOrInputUnderlinePosition(element, position, flipped) { 984 var properties = ['direction', 'boxSizing', 'width', 'height', 'overflowX', 'overflowY', 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontSizeAdjust', 'lineHeight', 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'letterSpacing', 'wordSpacing']; 985 var isFirefox = window.mozInnerScreenX !== null; 986 var div = this.getDocument().createElement('div'); 987 div.id = 'input-textarea-caret-position-mirror-div'; 988 this.getDocument().body.appendChild(div); 989 var style = div.style; 990 var computed = window.getComputedStyle ? getComputedStyle(element) : element.currentStyle; 991 style.whiteSpace = 'pre-wrap'; 992 993 if (element.nodeName !== 'INPUT') { 994 style.wordWrap = 'break-word'; 995 } // position off-screen 996 997 998 style.position = 'absolute'; 999 style.visibility = 'hidden'; // transfer the element's properties to the div 1000 1001 properties.forEach(function (prop) { 1002 style[prop] = computed[prop]; 1003 }); 1004 1005 if (isFirefox) { 1006 style.width = "".concat(parseInt(computed.width) - 2, "px"); 1007 if (element.scrollHeight > parseInt(computed.height)) style.overflowY = 'scroll'; 1008 } else { 1009 style.overflow = 'hidden'; 1010 } 1011 1012 div.textContent = element.value.substring(0, position); 1013 1014 if (element.nodeName === 'INPUT') { 1015 div.textContent = div.textContent.replace(/\s/g, ' '); 1016 } 1017 1018 var span = this.getDocument().createElement('span'); 1019 span.textContent = element.value.substring(position) || '.'; 1020 div.appendChild(span); 1021 var rect = element.getBoundingClientRect(); 1022 var doc = document.documentElement; 1023 var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0); 1024 var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); 1025 var top = 0; 1026 var left = 0; 1027 1028 if (this.menuContainerIsBody) { 1029 top = rect.top; 1030 left = rect.left; 1031 } 1032 1033 var coordinates = { 1034 top: top + windowTop + span.offsetTop + parseInt(computed.borderTopWidth) + parseInt(computed.fontSize) - element.scrollTop, 1035 left: left + windowLeft + span.offsetLeft + parseInt(computed.borderLeftWidth) 1036 }; 1037 var windowWidth = window.innerWidth; 1038 var windowHeight = window.innerHeight; 1039 var menuDimensions = this.getMenuDimensions(); 1040 var menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions); 1041 1042 if (menuIsOffScreen.right) { 1043 coordinates.right = windowWidth - coordinates.left; 1044 coordinates.left = 'auto'; 1045 } 1046 1047 var parentHeight = this.tribute.menuContainer ? this.tribute.menuContainer.offsetHeight : this.getDocument().body.offsetHeight; 1048 1049 if (menuIsOffScreen.bottom) { 1050 var parentRect = this.tribute.menuContainer ? this.tribute.menuContainer.getBoundingClientRect() : this.getDocument().body.getBoundingClientRect(); 1051 var scrollStillAvailable = parentHeight - (windowHeight - parentRect.top); 1052 coordinates.bottom = scrollStillAvailable + (windowHeight - rect.top - span.offsetTop); 1053 coordinates.top = 'auto'; 1054 } 1055 1056 menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions); 1057 1058 if (menuIsOffScreen.left) { 1059 coordinates.left = windowWidth > menuDimensions.width ? windowLeft + windowWidth - menuDimensions.width : windowLeft; 1060 delete coordinates.right; 1061 } 1062 1063 if (menuIsOffScreen.top) { 1064 coordinates.top = windowHeight > menuDimensions.height ? windowTop + windowHeight - menuDimensions.height : windowTop; 1065 delete coordinates.bottom; 1066 } 1067 1068 this.getDocument().body.removeChild(div); 1069 return coordinates; 1070 } 1071 }, { 1072 key: "getContentEditableCaretPosition", 1073 value: function getContentEditableCaretPosition(selectedNodePosition) { 1074 var range; 1075 var sel = this.getWindowSelection(); 1076 range = this.getDocument().createRange(); 1077 range.setStart(sel.anchorNode, selectedNodePosition); 1078 range.setEnd(sel.anchorNode, selectedNodePosition); 1079 range.collapse(false); 1080 var rect = range.getBoundingClientRect(); 1081 var doc = document.documentElement; 1082 var windowLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0); 1083 var windowTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); 1084 var left = rect.left; 1085 var top = rect.top; 1086 var coordinates = { 1087 left: left + windowLeft, 1088 top: top + rect.height + windowTop 1089 }; 1090 var windowWidth = window.innerWidth; 1091 var windowHeight = window.innerHeight; 1092 var menuDimensions = this.getMenuDimensions(); 1093 var menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions); 1094 1095 if (menuIsOffScreen.right) { 1096 coordinates.left = 'auto'; 1097 coordinates.right = windowWidth - rect.left - windowLeft; 1098 } 1099 1100 var parentHeight = this.tribute.menuContainer ? this.tribute.menuContainer.offsetHeight : this.getDocument().body.offsetHeight; 1101 1102 if (menuIsOffScreen.bottom) { 1103 var parentRect = this.tribute.menuContainer ? this.tribute.menuContainer.getBoundingClientRect() : this.getDocument().body.getBoundingClientRect(); 1104 var scrollStillAvailable = parentHeight - (windowHeight - parentRect.top); 1105 coordinates.top = 'auto'; 1106 coordinates.bottom = scrollStillAvailable + (windowHeight - rect.top); 1107 } 1108 1109 menuIsOffScreen = this.isMenuOffScreen(coordinates, menuDimensions); 1110 1111 if (menuIsOffScreen.left) { 1112 coordinates.left = windowWidth > menuDimensions.width ? windowLeft + windowWidth - menuDimensions.width : windowLeft; 1113 delete coordinates.right; 1114 } 1115 1116 if (menuIsOffScreen.top) { 1117 coordinates.top = windowHeight > menuDimensions.height ? windowTop + windowHeight - menuDimensions.height : windowTop; 1118 delete coordinates.bottom; 1119 } 1120 1121 if (!this.menuContainerIsBody) { 1122 coordinates.left = coordinates.left ? coordinates.left - this.tribute.menuContainer.offsetLeft : coordinates.left; 1123 coordinates.top = coordinates.top ? coordinates.top - this.tribute.menuContainer.offsetTop : coordinates.top; 1124 } 1125 1126 return coordinates; 1127 } 1128 }, { 1129 key: "scrollIntoView", 1130 value: function scrollIntoView(elem) { 1131 var reasonableBuffer = 20, 1132 clientRect; 1133 var maxScrollDisplacement = 100; 1134 var e = this.menu; 1135 if (typeof e === 'undefined') return; 1136 1137 while (clientRect === undefined || clientRect.height === 0) { 1138 clientRect = e.getBoundingClientRect(); 1139 1140 if (clientRect.height === 0) { 1141 e = e.childNodes[0]; 1142 1143 if (e === undefined || !e.getBoundingClientRect) { 1144 return; 1145 } 1146 } 1147 } 1148 1149 var elemTop = clientRect.top; 1150 var elemBottom = elemTop + clientRect.height; 1151 1152 if (elemTop < 0) { 1153 window.scrollTo(0, window.pageYOffset + clientRect.top - reasonableBuffer); 1154 } else if (elemBottom > window.innerHeight) { 1155 var maxY = window.pageYOffset + clientRect.top - reasonableBuffer; 1156 1157 if (maxY - window.pageYOffset > maxScrollDisplacement) { 1158 maxY = window.pageYOffset + maxScrollDisplacement; 1159 } 1160 1161 var targetY = window.pageYOffset - (window.innerHeight - elemBottom); 1162 1163 if (targetY > maxY) { 1164 targetY = maxY; 1165 } 1166 1167 window.scrollTo(0, targetY); 1168 } 1169 } 1170 }, { 1171 key: "menuContainerIsBody", 1172 get: function get() { 1173 return this.tribute.menuContainer === document.body || !this.tribute.menuContainer; 1174 } 1175 }]); 1176 1177 return TributeRange; 1178 }(); 1179 1180 // Thanks to https://github.com/mattyork/fuzzy 1181 var TributeSearch = /*#__PURE__*/function () { 1182 function TributeSearch(tribute) { 1183 _classCallCheck(this, TributeSearch); 1184 1185 this.tribute = tribute; 1186 this.tribute.search = this; 1187 } 1188 1189 _createClass(TributeSearch, [{ 1190 key: "simpleFilter", 1191 value: function simpleFilter(pattern, array) { 1192 var _this = this; 1193 1194 return array.filter(function (string) { 1195 return _this.test(pattern, string); 1196 }); 1197 } 1198 }, { 1199 key: "test", 1200 value: function test(pattern, string) { 1201 return this.match(pattern, string) !== null; 1202 } 1203 }, { 1204 key: "match", 1205 value: function match(pattern, string, opts) { 1206 opts = opts || {}; 1207 var len = string.length, 1208 pre = opts.pre || '', 1209 post = opts.post || '', 1210 compareString = opts.caseSensitive && string || string.toLowerCase(); 1211 1212 if (opts.skip) { 1213 return { 1214 rendered: string, 1215 score: 0 1216 }; 1217 } 1218 1219 pattern = opts.caseSensitive && pattern || pattern.toLowerCase(); 1220 var patternCache = this.traverse(compareString, pattern, 0, 0, []); 1221 1222 if (!patternCache) { 1223 return null; 1224 } 1225 1226 return { 1227 rendered: this.render(string, patternCache.cache, pre, post), 1228 score: patternCache.score 1229 }; 1230 } 1231 }, { 1232 key: "traverse", 1233 value: function traverse(string, pattern, stringIndex, patternIndex, patternCache) { 1234 if (this.tribute.autocompleteSeparator) { 1235 // if the pattern search at end 1236 pattern = pattern.split(this.tribute.autocompleteSeparator).splice(-1)[0]; 1237 } 1238 1239 if (pattern.length === patternIndex) { 1240 // calculate score and copy the cache containing the indices where it's found 1241 return { 1242 score: this.calculateScore(patternCache), 1243 cache: patternCache.slice() 1244 }; 1245 } // if string at end or remaining pattern > remaining string 1246 1247 1248 if (string.length === stringIndex || pattern.length - patternIndex > string.length - stringIndex) { 1249 return undefined; 1250 } 1251 1252 var c = pattern[patternIndex]; 1253 var index = string.indexOf(c, stringIndex); 1254 var best, temp; 1255 1256 while (index > -1) { 1257 patternCache.push(index); 1258 temp = this.traverse(string, pattern, index + 1, patternIndex + 1, patternCache); 1259 patternCache.pop(); // if downstream traversal failed, return best answer so far 1260 1261 if (!temp) { 1262 return best; 1263 } 1264 1265 if (!best || best.score < temp.score) { 1266 best = temp; 1267 } 1268 1269 index = string.indexOf(c, index + 1); 1270 } 1271 1272 return best; 1273 } 1274 }, { 1275 key: "calculateScore", 1276 value: function calculateScore(patternCache) { 1277 var score = 0; 1278 var temp = 1; 1279 patternCache.forEach(function (index, i) { 1280 if (i > 0) { 1281 if (patternCache[i - 1] + 1 === index) { 1282 temp += temp + 1; 1283 } else { 1284 temp = 1; 1285 } 1286 } 1287 1288 score += temp; 1289 }); 1290 return score; 1291 } 1292 }, { 1293 key: "render", 1294 value: function render(string, indices, pre, post) { 1295 var rendered = string.substring(0, indices[0]); 1296 indices.forEach(function (index, i) { 1297 rendered += pre + string[index] + post + string.substring(index + 1, indices[i + 1] ? indices[i + 1] : string.length); 1298 }); 1299 return rendered; 1300 } 1301 }, { 1302 key: "filter", 1303 value: function filter(pattern, arr, opts) { 1304 var _this2 = this; 1305 1306 opts = opts || {}; 1307 return arr.reduce(function (prev, element, idx, arr) { 1308 var str = element; 1309 1310 if (opts.extract) { 1311 str = opts.extract(element); 1312 1313 if (!str) { 1314 // take care of undefineds / nulls / etc. 1315 str = ''; 1316 } 1317 } 1318 1319 var rendered = _this2.match(pattern, str, opts); 1320 1321 if (rendered != null) { 1322 prev[prev.length] = { 1323 string: rendered.rendered, 1324 score: rendered.score, 1325 index: idx, 1326 original: element 1327 }; 1328 } 1329 1330 return prev; 1331 }, []).sort(function (a, b) { 1332 var compare = b.score - a.score; 1333 if (compare) return compare; 1334 return a.index - b.index; 1335 }); 1336 } 1337 }]); 1338 1339 return TributeSearch; 1340 }(); 1341 1342 var Tribute = /*#__PURE__*/function () { 1343 function Tribute(_ref) { 1344 var _this = this; 1345 1346 var _ref$values = _ref.values, 1347 values = _ref$values === void 0 ? null : _ref$values, 1348 _ref$loadingItemTempl = _ref.loadingItemTemplate, 1349 loadingItemTemplate = _ref$loadingItemTempl === void 0 ? null : _ref$loadingItemTempl, 1350 _ref$iframe = _ref.iframe, 1351 iframe = _ref$iframe === void 0 ? null : _ref$iframe, 1352 _ref$selectClass = _ref.selectClass, 1353 selectClass = _ref$selectClass === void 0 ? "highlight" : _ref$selectClass, 1354 _ref$containerClass = _ref.containerClass, 1355 containerClass = _ref$containerClass === void 0 ? "tribute-container" : _ref$containerClass, 1356 _ref$itemClass = _ref.itemClass, 1357 itemClass = _ref$itemClass === void 0 ? "" : _ref$itemClass, 1358 _ref$trigger = _ref.trigger, 1359 trigger = _ref$trigger === void 0 ? "@" : _ref$trigger, 1360 _ref$autocompleteMode = _ref.autocompleteMode, 1361 autocompleteMode = _ref$autocompleteMode === void 0 ? false : _ref$autocompleteMode, 1362 _ref$autocompleteSepa = _ref.autocompleteSeparator, 1363 autocompleteSeparator = _ref$autocompleteSepa === void 0 ? null : _ref$autocompleteSepa, 1364 _ref$selectTemplate = _ref.selectTemplate, 1365 selectTemplate = _ref$selectTemplate === void 0 ? null : _ref$selectTemplate, 1366 _ref$menuItemTemplate = _ref.menuItemTemplate, 1367 menuItemTemplate = _ref$menuItemTemplate === void 0 ? null : _ref$menuItemTemplate, 1368 _ref$lookup = _ref.lookup, 1369 lookup = _ref$lookup === void 0 ? "key" : _ref$lookup, 1370 _ref$fillAttr = _ref.fillAttr, 1371 fillAttr = _ref$fillAttr === void 0 ? "value" : _ref$fillAttr, 1372 _ref$collection = _ref.collection, 1373 collection = _ref$collection === void 0 ? null : _ref$collection, 1374 _ref$menuContainer = _ref.menuContainer, 1375 menuContainer = _ref$menuContainer === void 0 ? null : _ref$menuContainer, 1376 _ref$noMatchTemplate = _ref.noMatchTemplate, 1377 noMatchTemplate = _ref$noMatchTemplate === void 0 ? null : _ref$noMatchTemplate, 1378 _ref$requireLeadingSp = _ref.requireLeadingSpace, 1379 requireLeadingSpace = _ref$requireLeadingSp === void 0 ? true : _ref$requireLeadingSp, 1380 _ref$allowSpaces = _ref.allowSpaces, 1381 allowSpaces = _ref$allowSpaces === void 0 ? false : _ref$allowSpaces, 1382 _ref$replaceTextSuffi = _ref.replaceTextSuffix, 1383 replaceTextSuffix = _ref$replaceTextSuffi === void 0 ? null : _ref$replaceTextSuffi, 1384 _ref$positionMenu = _ref.positionMenu, 1385 positionMenu = _ref$positionMenu === void 0 ? true : _ref$positionMenu, 1386 _ref$spaceSelectsMatc = _ref.spaceSelectsMatch, 1387 spaceSelectsMatch = _ref$spaceSelectsMatc === void 0 ? false : _ref$spaceSelectsMatc, 1388 _ref$searchOpts = _ref.searchOpts, 1389 searchOpts = _ref$searchOpts === void 0 ? {} : _ref$searchOpts, 1390 _ref$menuItemLimit = _ref.menuItemLimit, 1391 menuItemLimit = _ref$menuItemLimit === void 0 ? null : _ref$menuItemLimit, 1392 _ref$menuShowMinLengt = _ref.menuShowMinLength, 1393 menuShowMinLength = _ref$menuShowMinLengt === void 0 ? 0 : _ref$menuShowMinLengt; 1394 1395 _classCallCheck(this, Tribute); 1396 1397 this.autocompleteMode = autocompleteMode; 1398 this.autocompleteSeparator = autocompleteSeparator; 1399 this.menuSelected = 0; 1400 this.current = {}; 1401 this.inputEvent = false; 1402 this.isActive = false; 1403 this.menuContainer = menuContainer; 1404 this.allowSpaces = allowSpaces; 1405 this.replaceTextSuffix = replaceTextSuffix; 1406 this.positionMenu = positionMenu; 1407 this.hasTrailingSpace = false; 1408 this.spaceSelectsMatch = spaceSelectsMatch; 1409 1410 if (this.autocompleteMode) { 1411 trigger = ""; 1412 allowSpaces = false; 1413 } 1414 1415 if (values) { 1416 this.collection = [{ 1417 // symbol that starts the lookup 1418 trigger: trigger, 1419 // is it wrapped in an iframe 1420 iframe: iframe, 1421 // class applied to selected item 1422 selectClass: selectClass, 1423 // class applied to the Container 1424 containerClass: containerClass, 1425 // class applied to each item 1426 itemClass: itemClass, 1427 // function called on select that retuns the content to insert 1428 selectTemplate: (selectTemplate || Tribute.defaultSelectTemplate).bind(this), 1429 // function called that returns content for an item 1430 menuItemTemplate: (menuItemTemplate || Tribute.defaultMenuItemTemplate).bind(this), 1431 // function called when menu is empty, disables hiding of menu. 1432 noMatchTemplate: function (t) { 1433 if (typeof t === "string") { 1434 if (t.trim() === "") return null; 1435 return t; 1436 } 1437 1438 if (typeof t === "function") { 1439 return t.bind(_this); 1440 } 1441 1442 return noMatchTemplate || function () { 1443 return "<li>No Match Found!</li>"; 1444 }.bind(_this); 1445 }(noMatchTemplate), 1446 // column to search against in the object 1447 lookup: lookup, 1448 // column that contains the content to insert by default 1449 fillAttr: fillAttr, 1450 // array of objects or a function returning an array of objects 1451 values: values, 1452 // useful for when values is an async function 1453 loadingItemTemplate: loadingItemTemplate, 1454 requireLeadingSpace: requireLeadingSpace, 1455 searchOpts: searchOpts, 1456 menuItemLimit: menuItemLimit, 1457 menuShowMinLength: menuShowMinLength 1458 }]; 1459 } else if (collection) { 1460 if (this.autocompleteMode) console.warn("Tribute in autocomplete mode does not work for collections"); 1461 this.collection = collection.map(function (item) { 1462 return { 1463 trigger: item.trigger || trigger, 1464 iframe: item.iframe || iframe, 1465 selectClass: item.selectClass || selectClass, 1466 containerClass: item.containerClass || containerClass, 1467 itemClass: item.itemClass || itemClass, 1468 selectTemplate: (item.selectTemplate || Tribute.defaultSelectTemplate).bind(_this), 1469 menuItemTemplate: (item.menuItemTemplate || Tribute.defaultMenuItemTemplate).bind(_this), 1470 // function called when menu is empty, disables hiding of menu. 1471 noMatchTemplate: function (t) { 1472 if (typeof t === "string") { 1473 if (t.trim() === "") return null; 1474 return t; 1475 } 1476 1477 if (typeof t === "function") { 1478 return t.bind(_this); 1479 } 1480 1481 return noMatchTemplate || function () { 1482 return "<li>No Match Found!</li>"; 1483 }.bind(_this); 1484 }(noMatchTemplate), 1485 lookup: item.lookup || lookup, 1486 fillAttr: item.fillAttr || fillAttr, 1487 values: item.values, 1488 loadingItemTemplate: item.loadingItemTemplate, 1489 requireLeadingSpace: item.requireLeadingSpace, 1490 searchOpts: item.searchOpts || searchOpts, 1491 menuItemLimit: item.menuItemLimit || menuItemLimit, 1492 menuShowMinLength: item.menuShowMinLength || menuShowMinLength 1493 }; 1494 }); 1495 } else { 1496 throw new Error("[Tribute] No collection specified."); 1497 } 1498 1499 new TributeRange(this); 1500 new TributeEvents(this); 1501 new TributeMenuEvents(this); 1502 new TributeSearch(this); 1503 } 1504 1505 _createClass(Tribute, [{ 1506 key: "triggers", 1507 value: function triggers() { 1508 return this.collection.map(function (config) { 1509 return config.trigger; 1510 }); 1511 } 1512 }, { 1513 key: "attach", 1514 value: function attach(el) { 1515 if (!el) { 1516 throw new Error("[Tribute] Must pass in a DOM node or NodeList."); 1517 } // Check if it is a jQuery collection 1518 1519 1520 if (typeof jQuery !== "undefined" && el instanceof jQuery) { 1521 el = el.get(); 1522 } // Is el an Array/Array-like object? 1523 1524 1525 if (el.constructor === NodeList || el.constructor === HTMLCollection || el.constructor === Array) { 1526 var length = el.length; 1527 1528 for (var i = 0; i < length; ++i) { 1529 this._attach(el[i]); 1530 } 1531 } else { 1532 this._attach(el); 1533 } 1534 } 1535 }, { 1536 key: "_attach", 1537 value: function _attach(el) { 1538 if (el.hasAttribute("data-tribute")) { 1539 console.warn("Tribute was already bound to " + el.nodeName); 1540 } 1541 1542 this.ensureEditable(el); 1543 this.events.bind(el); 1544 el.setAttribute("data-tribute", true); 1545 } 1546 }, { 1547 key: "ensureEditable", 1548 value: function ensureEditable(element) { 1549 if (Tribute.inputTypes().indexOf(element.nodeName) === -1) { 1550 if (element.contentEditable) { 1551 element.contentEditable = true; 1552 } else { 1553 throw new Error("[Tribute] Cannot bind to " + element.nodeName); 1554 } 1555 } 1556 } 1557 }, { 1558 key: "createMenu", 1559 value: function createMenu(containerClass) { 1560 var wrapper = this.range.getDocument().createElement("div"), 1561 ul = this.range.getDocument().createElement("ul"); 1562 wrapper.className = containerClass; 1563 wrapper.appendChild(ul); 1564 1565 if (this.menuContainer) { 1566 return this.menuContainer.appendChild(wrapper); 1567 } 1568 1569 return this.range.getDocument().body.appendChild(wrapper); 1570 } 1571 }, { 1572 key: "showMenuFor", 1573 value: function showMenuFor(element, scrollTo) { 1574 var _this2 = this; 1575 1576 // Only proceed if menu isn't already shown for the current element & mentionText 1577 if (this.isActive && this.current.element === element && this.current.mentionText === this.currentMentionTextSnapshot) { 1578 return; 1579 } 1580 1581 this.currentMentionTextSnapshot = this.current.mentionText; // create the menu if it doesn't exist. 1582 1583 if (!this.menu) { 1584 this.menu = this.createMenu(this.current.collection.containerClass); 1585 element.tributeMenu = this.menu; 1586 this.menuEvents.bind(this.menu); 1587 } 1588 1589 this.isActive = true; 1590 this.menuSelected = 0; 1591 1592 if (!this.current.mentionText) { 1593 this.current.mentionText = ""; 1594 } 1595 1596 var processValues = function processValues(values) { 1597 // Tribute may not be active any more by the time the value callback returns 1598 if (!_this2.isActive) { 1599 return; 1600 } 1601 1602 var items = _this2.search.filter(_this2.current.mentionText, values, { 1603 pre: _this2.current.collection.searchOpts.pre || "<span>", 1604 post: _this2.current.collection.searchOpts.post || "</span>", 1605 skip: _this2.current.collection.searchOpts.skip, 1606 extract: function extract(el) { 1607 if (typeof _this2.current.collection.lookup === "string") { 1608 return el[_this2.current.collection.lookup]; 1609 } else if (typeof _this2.current.collection.lookup === "function") { 1610 return _this2.current.collection.lookup(el, _this2.current.mentionText); 1611 } else { 1612 throw new Error("Invalid lookup attribute, lookup must be string or function."); 1613 } 1614 } 1615 }); 1616 1617 if (_this2.current.collection.menuItemLimit) { 1618 items = items.slice(0, _this2.current.collection.menuItemLimit); 1619 } 1620 1621 _this2.current.filteredItems = items; 1622 1623 var ul = _this2.menu.querySelector("ul"); 1624 1625 _this2.range.positionMenuAtCaret(scrollTo); 1626 1627 if (!items.length) { 1628 var noMatchEvent = new CustomEvent("tribute-no-match", { 1629 detail: _this2.menu 1630 }); 1631 1632 _this2.current.element.dispatchEvent(noMatchEvent); 1633 1634 if (typeof _this2.current.collection.noMatchTemplate === "function" && !_this2.current.collection.noMatchTemplate() || !_this2.current.collection.noMatchTemplate) { 1635 _this2.hideMenu(); 1636 } else { 1637 typeof _this2.current.collection.noMatchTemplate === "function" ? ul.innerHTML = _this2.current.collection.noMatchTemplate() : ul.innerHTML = _this2.current.collection.noMatchTemplate; 1638 } 1639 1640 return; 1641 } 1642 1643 ul.innerHTML = ""; 1644 1645 var fragment = _this2.range.getDocument().createDocumentFragment(); 1646 1647 items.forEach(function (item, index) { 1648 var li = _this2.range.getDocument().createElement("li"); 1649 1650 li.setAttribute("data-index", index); 1651 li.className = _this2.current.collection.itemClass; 1652 li.addEventListener("mousemove", function (e) { 1653 var _this2$_findLiTarget = _this2._findLiTarget(e.target), 1654 _this2$_findLiTarget2 = _slicedToArray(_this2$_findLiTarget, 2), 1655 li = _this2$_findLiTarget2[0], 1656 index = _this2$_findLiTarget2[1]; 1657 1658 if (e.movementY !== 0) { 1659 _this2.events.setActiveLi(index); 1660 } 1661 }); 1662 1663 if (_this2.menuSelected === index) { 1664 li.classList.add(_this2.current.collection.selectClass); 1665 } 1666 1667 li.innerHTML = _this2.current.collection.menuItemTemplate(item); 1668 fragment.appendChild(li); 1669 }); 1670 ul.appendChild(fragment); 1671 }; 1672 1673 if (typeof this.current.collection.values === "function") { 1674 if (this.current.collection.loadingItemTemplate) { 1675 this.menu.querySelector("ul").innerHTML = this.current.collection.loadingItemTemplate; 1676 this.range.positionMenuAtCaret(scrollTo); 1677 } 1678 1679 this.current.collection.values(this.current.mentionText, processValues); 1680 } else { 1681 processValues(this.current.collection.values); 1682 } 1683 } 1684 }, { 1685 key: "_findLiTarget", 1686 value: function _findLiTarget(el) { 1687 if (!el) return []; 1688 var index = el.getAttribute("data-index"); 1689 return !index ? this._findLiTarget(el.parentNode) : [el, index]; 1690 } 1691 }, { 1692 key: "showMenuForCollection", 1693 value: function showMenuForCollection(element, collectionIndex) { 1694 if (element !== document.activeElement) { 1695 this.placeCaretAtEnd(element); 1696 } 1697 1698 this.current.collection = this.collection[collectionIndex || 0]; 1699 this.current.externalTrigger = true; 1700 this.current.element = element; 1701 if (element.isContentEditable) this.insertTextAtCursor(this.current.collection.trigger);else this.insertAtCaret(element, this.current.collection.trigger); 1702 this.showMenuFor(element); 1703 } // TODO: make sure this works for inputs/textareas 1704 1705 }, { 1706 key: "placeCaretAtEnd", 1707 value: function placeCaretAtEnd(el) { 1708 el.focus(); 1709 1710 if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") { 1711 var range = document.createRange(); 1712 range.selectNodeContents(el); 1713 range.collapse(false); 1714 var sel = window.getSelection(); 1715 sel.removeAllRanges(); 1716 sel.addRange(range); 1717 } else if (typeof document.body.createTextRange != "undefined") { 1718 var textRange = document.body.createTextRange(); 1719 textRange.moveToElementText(el); 1720 textRange.collapse(false); 1721 textRange.select(); 1722 } 1723 } // for contenteditable 1724 1725 }, { 1726 key: "insertTextAtCursor", 1727 value: function insertTextAtCursor(text) { 1728 var sel, range; 1729 sel = window.getSelection(); 1730 range = sel.getRangeAt(0); 1731 range.deleteContents(); 1732 var textNode = document.createTextNode(text); 1733 range.insertNode(textNode); 1734 range.selectNodeContents(textNode); 1735 range.collapse(false); 1736 sel.removeAllRanges(); 1737 sel.addRange(range); 1738 } // for regular inputs 1739 1740 }, { 1741 key: "insertAtCaret", 1742 value: function insertAtCaret(textarea, text) { 1743 var scrollPos = textarea.scrollTop; 1744 var caretPos = textarea.selectionStart; 1745 var front = textarea.value.substring(0, caretPos); 1746 var back = textarea.value.substring(textarea.selectionEnd, textarea.value.length); 1747 textarea.value = front + text + back; 1748 caretPos = caretPos + text.length; 1749 textarea.selectionStart = caretPos; 1750 textarea.selectionEnd = caretPos; 1751 textarea.focus(); 1752 textarea.scrollTop = scrollPos; 1753 } 1754 }, { 1755 key: "hideMenu", 1756 value: function hideMenu() { 1757 if (this.menu) { 1758 this.menu.style.cssText = "display: none;"; 1759 this.isActive = false; 1760 this.menuSelected = 0; 1761 this.current = {}; 1762 } 1763 } 1764 }, { 1765 key: "selectItemAtIndex", 1766 value: function selectItemAtIndex(index, originalEvent) { 1767 index = parseInt(index); 1768 if (typeof index !== "number" || isNaN(index)) return; 1769 var item = this.current.filteredItems[index]; 1770 var content = this.current.collection.selectTemplate(item); 1771 if (content !== null) this.replaceText(content, originalEvent, item); 1772 } 1773 }, { 1774 key: "replaceText", 1775 value: function replaceText(content, originalEvent, item) { 1776 this.range.replaceTriggerText(content, true, true, originalEvent, item); 1777 } 1778 }, { 1779 key: "_append", 1780 value: function _append(collection, newValues, replace) { 1781 if (typeof collection.values === "function") { 1782 throw new Error("Unable to append to values, as it is a function."); 1783 } else if (!replace) { 1784 collection.values = collection.values.concat(newValues); 1785 } else { 1786 collection.values = newValues; 1787 } 1788 } 1789 }, { 1790 key: "append", 1791 value: function append(collectionIndex, newValues, replace) { 1792 var index = parseInt(collectionIndex); 1793 if (typeof index !== "number") throw new Error("please provide an index for the collection to update."); 1794 var collection = this.collection[index]; 1795 1796 this._append(collection, newValues, replace); 1797 } 1798 }, { 1799 key: "appendCurrent", 1800 value: function appendCurrent(newValues, replace) { 1801 if (this.isActive) { 1802 this._append(this.current.collection, newValues, replace); 1803 } else { 1804 throw new Error("No active state. Please use append instead and pass an index."); 1805 } 1806 } 1807 }, { 1808 key: "detach", 1809 value: function detach(el) { 1810 if (!el) { 1811 throw new Error("[Tribute] Must pass in a DOM node or NodeList."); 1812 } // Check if it is a jQuery collection 1813 1814 1815 if (typeof jQuery !== "undefined" && el instanceof jQuery) { 1816 el = el.get(); 1817 } // Is el an Array/Array-like object? 1818 1819 1820 if (el.constructor === NodeList || el.constructor === HTMLCollection || el.constructor === Array) { 1821 var length = el.length; 1822 1823 for (var i = 0; i < length; ++i) { 1824 this._detach(el[i]); 1825 } 1826 } else { 1827 this._detach(el); 1828 } 1829 } 1830 }, { 1831 key: "_detach", 1832 value: function _detach(el) { 1833 var _this3 = this; 1834 1835 this.events.unbind(el); 1836 1837 if (el.tributeMenu) { 1838 this.menuEvents.unbind(el.tributeMenu); 1839 } 1840 1841 setTimeout(function () { 1842 el.removeAttribute("data-tribute"); 1843 _this3.isActive = false; 1844 1845 if (el.tributeMenu) { 1846 el.tributeMenu.remove(); 1847 } 1848 }); 1849 } 1850 }, { 1851 key: "isActive", 1852 get: function get() { 1853 return this._isActive; 1854 }, 1855 set: function set(val) { 1856 if (this._isActive != val) { 1857 this._isActive = val; 1858 1859 if (this.current.element) { 1860 var noMatchEvent = new CustomEvent("tribute-active-".concat(val)); 1861 this.current.element.dispatchEvent(noMatchEvent); 1862 } 1863 } 1864 } 1865 }], [{ 1866 key: "defaultSelectTemplate", 1867 value: function defaultSelectTemplate(item) { 1868 if (typeof item === "undefined") return "".concat(this.current.collection.trigger).concat(this.current.mentionText); 1869 1870 if (this.range.isContentEditable(this.current.element)) { 1871 return '<span class="tribute-mention">' + (this.current.collection.trigger + item.original[this.current.collection.fillAttr]) + "</span>"; 1872 } 1873 1874 return this.current.collection.trigger + item.original[this.current.collection.fillAttr]; 1875 } 1876 }, { 1877 key: "defaultMenuItemTemplate", 1878 value: function defaultMenuItemTemplate(matchItem) { 1879 return matchItem.string; 1880 } 1881 }, { 1882 key: "inputTypes", 1883 value: function inputTypes() { 1884 return ["TEXTAREA", "INPUT"]; 1885 } 1886 }]); 1887 1888 return Tribute; 1889 }(); 1890 1891 /** 1892 * Tribute.js 1893 * Native ES6 JavaScript @mention Plugin 1894 **/ 1895 1896 return Tribute; 1897 1898 }))); -
src/bp-friends/bp-friends-functions.php
diff --git a/src/bp-friends/bp-friends-functions.php b/src/bp-friends/bp-friends-functions.php index a2a906b5a..8aae0eac6 100644
a b function bp_friends_prime_mentions_results() { 819 819 $result = new stdClass(); 820 820 $result->ID = $user->user_nicename; 821 821 $result->image = bp_core_fetch_avatar( array( 'html' => false, 'item_id' => $user->ID ) ); 822 $result->user_id = $user->ID; 822 823 823 824 if ( ! empty( $user->display_name ) && ! bp_disable_profile_sync() ) { 824 825 $result->name = $user->display_name; 825 826 } else { 826 827 $result->name = bp_core_get_user_displayname( $user->ID ); 827 828 } 829 $result->search = "{$result->ID} {$result->name}"; 828 830 829 831 $results[] = $result; 830 832 } -
src/bp-templates/bp-nouveau/includes/messages/functions.php
diff --git a/src/bp-templates/bp-nouveau/includes/messages/functions.php b/src/bp-templates/bp-nouveau/includes/messages/functions.php index c10e9280a..2774185ae 100644
a b function bp_nouveau_messages_register_scripts( $scripts = array() ) { 49 49 return array_merge( $scripts, array( 50 50 'bp-nouveau-messages-at' => array( 51 51 'file' => buddypress()->plugin_url . 'bp-activity/js/mentions%s.js', 52 'dependencies' => array( 'bp-nouveau', 'jquery', ' jquery-atwho' ),52 'dependencies' => array( 'bp-nouveau', 'jquery', 'bp-api-request', 'zurb-tribute' ), 53 53 'version' => bp_get_version(), 54 54 'footer' => true, 55 55 ), … … function bp_nouveau_messages_enqueue_scripts() { 72 72 } 73 73 74 74 wp_enqueue_script( 'bp-nouveau-messages' ); 75 76 // Add The tiny MCE init specific function.77 add_filter( 'tiny_mce_before_init', 'bp_nouveau_messages_at_on_tinymce_init', 10, 2 );78 75 } 79 76 80 77 /** … … function bp_nouveau_messages_mce_buttons( $buttons = array() ) { 314 311 return $buttons; 315 312 } 316 313 317 /**318 * @since 3.0.0319 */320 function bp_nouveau_messages_at_on_tinymce_init( $settings, $editor_id ) {321 // We only apply the mentions init to the visual post editor in the WP dashboard.322 if ( 'message_content' === $editor_id ) {323 $settings['init_instance_callback'] = 'window.bp.Nouveau.Messages.tinyMCEinit';324 }325 326 return $settings;327 }328 329 314 /** 330 315 * @since 3.0.0 331 316 */ -
src/bp-templates/bp-nouveau/js/buddypress-messages.js
diff --git a/src/bp-templates/bp-nouveau/js/buddypress-messages.js b/src/bp-templates/bp-nouveau/js/buddypress-messages.js index 44732ceaa..cfd0ee9af 100644
a b window.bp = window.bp || {}; 101 101 } 102 102 }, 103 103 104 tinyMCEinit: function() {105 if ( typeof window.tinyMCE === 'undefined' || window.tinyMCE.activeEditor === null || typeof window.tinyMCE.activeEditor === 'undefined' ) {106 return;107 } else {108 // Mentions isn't available, so bail.109 if ( _.isEmpty( exports.mentions ) ) {110 return;111 }112 113 $( window.tinyMCE.activeEditor.contentDocument.activeElement )114 .atwho( 'setIframe', $( '#message_content_ifr' )[0] )115 .bp_mentions( {116 data: [],117 suffix: ' '118 } );119 }120 },121 122 104 removeFeedback: function() { 123 105 var feedback; 124 106