@@ -20,7 +20,7 @@ angular.module('ui.mention', []).directive('uiMention', function () {
2020'use strict' ;
2121
2222angular . module ( 'ui.mention' ) . controller ( 'uiMention' , [ "$element" , "$scope" , "$attrs" , "$q" , "$timeout" , "$document" , function ( $element , $scope , $attrs , $q , $timeout , $document ) {
23- var _this2 = this ;
23+ var _this = this ;
2424
2525 // Beginning of input or preceeded by spaces: @sometext
2626 this . delimiter = '@' ;
@@ -43,8 +43,6 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
4343 * @param {ngModelController } model
4444 */
4545 this . init = function ( model ) {
46- var _this = this ;
47-
4846 // Leading whitespace shows up in the textarea but not the preview
4947 $attrs . ngTrim = 'false' ;
5048
@@ -53,7 +51,9 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
5351 ngModel . $parsers . push ( function ( value ) {
5452 // Removes any mentions that aren't used
5553 _this . mentions = _this . mentions . filter ( function ( mention ) {
56- if ( ~ value . indexOf ( _this . label ( mention ) ) ) return value = value . replace ( _this . label ( mention ) , _this . encode ( mention ) ) ;
54+ if ( ~ value . indexOf ( _this . label ( mention ) ) ) {
55+ return value = value . split ( _this . label ( mention ) ) . join ( _this . encode ( mention ) ) ;
56+ }
5757 } ) ;
5858
5959 _this . render ( value ) ;
@@ -70,7 +70,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
7070 // Removes any mentions that aren't used
7171 _this . mentions = _this . mentions . filter ( function ( mention ) {
7272 if ( ~ value . indexOf ( _this . encode ( mention ) ) ) {
73- value = value . replace ( _this . encode ( mention ) , _this . label ( mention ) ) ;
73+ value = value . split ( _this . encode ( mention ) ) . join ( _this . label ( mention ) ) ;
7474 return true ;
7575 } else {
7676 return false ;
@@ -111,10 +111,10 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
111111 html = ( html || '' ) . toString ( ) ;
112112 // Convert input to text, to prevent script injection/rich text
113113 html = parseContentAsText ( html ) ;
114- _this2 . mentions . forEach ( function ( mention ) {
115- html = html . replace ( _this2 . encode ( mention ) , _this2 . highlight ( mention ) ) ;
114+ _this . mentions . forEach ( function ( mention ) {
115+ html = html . split ( _this . encode ( mention ) ) . join ( _this . highlight ( mention ) ) ;
116116 } ) ;
117- _this2 . renderElement ( ) . html ( html ) ;
117+ _this . renderElement ( ) . html ( html ) ;
118118 return html ;
119119 } ;
120120
@@ -138,7 +138,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
138138 * @return {string } HTML highlighted version of the choice
139139 */
140140 this . highlight = function ( choice ) {
141- return '<span>' + this . label ( choice ) + '</span>' ;
141+ return '<span>' + _this . label ( choice ) + '</span>' ;
142142 } ;
143143
144144 /**
@@ -151,7 +151,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
151151 this . decode = function ( ) {
152152 var value = arguments . length <= 0 || arguments [ 0 ] === undefined ? ngModel . $modelValue : arguments [ 0 ] ;
153153
154- return value ? value . replace ( this . decodePattern , '$1' ) : '' ;
154+ return value ? value . replace ( _this . decodePattern , '$1' ) : '' ;
155155 } ;
156156
157157 /**
@@ -175,7 +175,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
175175 * @return {string } Syntax-encoded string version of choice
176176 */
177177 this . encode = function ( choice ) {
178- return this . delimiter + '[' + this . label ( choice ) + ':' + choice . id + ']' ;
178+ return _this . delimiter + '[' + _this . label ( choice ) + ':' + choice . id + ']' ;
179179 } ;
180180
181181 /**
@@ -189,12 +189,15 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
189189 * @return {string } Human-readable string
190190 */
191191 this . replace = function ( mention ) {
192- var search = arguments . length <= 1 || arguments [ 1 ] === undefined ? this . searching : arguments [ 1 ] ;
192+ var search = arguments . length <= 1 || arguments [ 1 ] === undefined ? _this . searching : arguments [ 1 ] ;
193193 var text = arguments . length <= 2 || arguments [ 2 ] === undefined ? ngModel . $viewValue : arguments [ 2 ] ;
194194
195195 // TODO: come up with a better way to detect what to remove
196196 // TODO: consider alternative to using regex match
197- text = text . substr ( 0 , search . index + search [ 0 ] . indexOf ( this . delimiter ) ) + this . label ( mention ) + ' ' + text . substr ( search . index + search [ 0 ] . length ) ;
197+ if ( search === null ) {
198+ return text ;
199+ }
200+ text = text . substr ( 0 , search . index + search [ 0 ] . indexOf ( _this . delimiter ) ) + _this . label ( mention ) + ' ' + text . substr ( search . index + search [ 0 ] . length ) ;
198201 return text ;
199202 } ;
200203
@@ -206,20 +209,26 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
206209 * @param {mixed|object } [choice] The selected choice (default: activeChoice)
207210 */
208211 this . select = function ( ) {
209- var choice = arguments . length <= 0 || arguments [ 0 ] === undefined ? this . activeChoice : arguments [ 0 ] ;
212+ var choice = arguments . length <= 0 || arguments [ 0 ] === undefined ? _this . activeChoice : arguments [ 0 ] ;
210213
211214 if ( ! choice ) {
212215 return false ;
213216 }
214217
215- // Add the mention
216- this . mentions . push ( choice ) ;
218+ var mentionExists = ~ _this . mentions . map ( function ( mention ) {
219+ return mention . id ;
220+ } ) . indexOf ( choice . id ) ;
221+
222+ // Add the mention, unless its already been mentioned
223+ if ( ! mentionExists ) {
224+ _this . mentions . push ( choice ) ;
225+ }
217226
218227 // Replace the search with the label
219- ngModel . $setViewValue ( this . replace ( choice ) ) ;
228+ ngModel . $setViewValue ( _this . replace ( choice ) ) ;
220229
221230 // Close choices panel
222- this . cancel ( ) ;
231+ _this . cancel ( ) ;
223232
224233 // Update the textarea
225234 ngModel . $render ( ) ;
@@ -231,11 +240,11 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
231240 * Moves this.activeChoice up the this.choices collection
232241 */
233242 this . up = function ( ) {
234- var index = this . choices . indexOf ( this . activeChoice ) ;
243+ var index = _this . choices . indexOf ( _this . activeChoice ) ;
235244 if ( index > 0 ) {
236- this . activeChoice = this . choices [ index - 1 ] ;
245+ _this . activeChoice = _this . choices [ index - 1 ] ;
237246 } else {
238- this . activeChoice = this . choices [ this . choices . length - 1 ] ;
247+ _this . activeChoice = _this . choices [ _this . choices . length - 1 ] ;
239248 }
240249 } ;
241250
@@ -245,11 +254,11 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
245254 * Moves this.activeChoice down the this.choices collection
246255 */
247256 this . down = function ( ) {
248- var index = this . choices . indexOf ( this . activeChoice ) ;
249- if ( index < this . choices . length - 1 ) {
250- this . activeChoice = this . choices [ index + 1 ] ;
257+ var index = _this . choices . indexOf ( _this . activeChoice ) ;
258+ if ( index < _this . choices . length - 1 ) {
259+ _this . activeChoice = _this . choices [ index + 1 ] ;
251260 } else {
252- this . activeChoice = this . choices [ 0 ] ;
261+ _this . activeChoice = _this . choices [ 0 ] ;
253262 }
254263 } ;
255264
@@ -263,13 +272,11 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
263272 * @todo Try to avoid using a regex match object
264273 */
265274 this . search = function ( match ) {
266- var _this3 = this ;
267-
268- this . searching = match ;
275+ _this . searching = match ;
269276
270- return $q . when ( this . findChoices ( match , this . mentions ) ) . then ( function ( choices ) {
271- _this3 . choices = choices ;
272- _this3 . activeChoice = choices [ 0 ] ;
277+ return $q . when ( _this . findChoices ( match , _this . mentions ) ) . then ( function ( choices ) {
278+ _this . choices = choices ;
279+ _this . activeChoice = choices [ 0 ] ;
273280 return choices ;
274281 } ) ;
275282 } ;
@@ -292,30 +299,36 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
292299 * Clears the choices dropdown info and stops searching
293300 */
294301 this . cancel = function ( ) {
295- this . choices = [ ] ;
296- this . searching = null ;
302+ _this . choices = [ ] ;
303+ _this . searching = null ;
297304 } ;
298305
299306 this . autogrow = function ( ) {
300307 $element [ 0 ] . style . height = 0 ; // autoshrink - need accurate scrollHeight
301308 var style = getComputedStyle ( $element [ 0 ] ) ;
302- if ( style . boxSizing == 'border-box' ) $element [ 0 ] . style . height = $element [ 0 ] . scrollHeight + 'px' ;
309+ if ( style . boxSizing == 'border-box' ) {
310+ $element [ 0 ] . style . height = $element [ 0 ] . scrollHeight + 'px' ;
311+ }
303312 } ;
304313
305314 // Interactions to trigger searching
306315 $element . on ( 'keyup click focus' , function ( event ) {
307316 // If event is fired AFTER activeChoice move is performed
308- if ( _this2 . moved ) return _this2 . moved = false ;
317+ if ( _this . moved ) {
318+ return _this . moved = false ;
319+ }
309320 // Don't trigger on selection
310- if ( $element [ 0 ] . selectionStart != $element [ 0 ] . selectionEnd ) return ;
321+ if ( $element [ 0 ] . selectionStart != $element [ 0 ] . selectionEnd ) {
322+ return ;
323+ }
311324 var text = $element . val ( ) ;
312325 // text to left of cursor ends with `@sometext`
313- var match = _this2 . searchPattern . exec ( text . substr ( 0 , $element [ 0 ] . selectionStart ) ) ;
326+ var match = _this . searchPattern . exec ( text . substr ( 0 , $element [ 0 ] . selectionStart ) ) ;
314327
315328 if ( match ) {
316- _this2 . search ( match ) ;
329+ _this . search ( match ) ;
317330 } else {
318- _this2 . cancel ( ) ;
331+ _this . cancel ( ) ;
319332 }
320333
321334 if ( ! $scope . $$phase ) {
@@ -324,27 +337,29 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
324337 } ) ;
325338
326339 $element . on ( 'keydown' , function ( event ) {
327- if ( ! _this2 . searching ) return ;
340+ if ( ! _this . searching ) {
341+ return ;
342+ }
328343
329344 switch ( event . keyCode ) {
330345 case 13 :
331346 // return
332- _this2 . select ( ) ;
347+ _this . select ( ) ;
333348 break ;
334349 case 38 :
335350 // up
336- _this2 . up ( ) ;
351+ _this . up ( ) ;
337352 break ;
338353 case 40 :
339354 // down
340- _this2 . down ( ) ;
355+ _this . down ( ) ;
341356 break ;
342357 default :
343358 // Exit function
344359 return ;
345360 }
346361
347- _this2 . moved = true ;
362+ _this . moved = true ;
348363 event . preventDefault ( ) ;
349364
350365 if ( ! $scope . $$phase ) {
@@ -353,22 +368,26 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
353368 } ) ;
354369
355370 this . onMouseup = ( function ( event ) {
356- var _this4 = this ;
371+ var _this2 = this ;
357372
358- if ( event . target == $element [ 0 ] ) return ;
373+ if ( event . target == $element [ 0 ] ) {
374+ return ;
375+ }
359376
360377 $document . off ( 'mouseup' , this . onMouseup ) ;
361378
362- if ( ! this . searching ) return ;
379+ if ( ! this . searching ) {
380+ return ;
381+ }
363382
364383 // Let ngClick fire first
365384 $scope . $evalAsync ( function ( ) {
366- _this4 . cancel ( ) ;
385+ _this2 . cancel ( ) ;
367386 } ) ;
368387 } ) . bind ( this ) ;
369388
370389 $element . on ( 'focus' , function ( event ) {
371- $document . on ( 'mouseup' , _this2 . onMouseup ) ;
390+ $document . on ( 'mouseup' , _this . onMouseup ) ;
372391 } ) ;
373392
374393 // Autogrow is mandatory beacuse the textarea scrolls away from highlights
0 commit comments