@@ -20,7 +20,7 @@ angular.module('ui.mention', []).directive('uiMention', function () {
20
20
'use strict' ;
21
21
22
22
angular . 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 ;
24
24
25
25
// Beginning of input or preceeded by spaces: @sometext
26
26
this . delimiter = '@' ;
@@ -43,8 +43,6 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
43
43
* @param {ngModelController } model
44
44
*/
45
45
this . init = function ( model ) {
46
- var _this = this ;
47
-
48
46
// Leading whitespace shows up in the textarea but not the preview
49
47
$attrs . ngTrim = 'false' ;
50
48
@@ -53,7 +51,9 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
53
51
ngModel . $parsers . push ( function ( value ) {
54
52
// Removes any mentions that aren't used
55
53
_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
+ }
57
57
} ) ;
58
58
59
59
_this . render ( value ) ;
@@ -70,7 +70,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
70
70
// Removes any mentions that aren't used
71
71
_this . mentions = _this . mentions . filter ( function ( mention ) {
72
72
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 ) ) ;
74
74
return true ;
75
75
} else {
76
76
return false ;
@@ -111,10 +111,10 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
111
111
html = ( html || '' ) . toString ( ) ;
112
112
// Convert input to text, to prevent script injection/rich text
113
113
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 ) ) ;
116
116
} ) ;
117
- _this2 . renderElement ( ) . html ( html ) ;
117
+ _this . renderElement ( ) . html ( html ) ;
118
118
return html ;
119
119
} ;
120
120
@@ -138,7 +138,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
138
138
* @return {string } HTML highlighted version of the choice
139
139
*/
140
140
this . highlight = function ( choice ) {
141
- return '<span>' + this . label ( choice ) + '</span>' ;
141
+ return '<span>' + _this . label ( choice ) + '</span>' ;
142
142
} ;
143
143
144
144
/**
@@ -151,7 +151,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
151
151
this . decode = function ( ) {
152
152
var value = arguments . length <= 0 || arguments [ 0 ] === undefined ? ngModel . $modelValue : arguments [ 0 ] ;
153
153
154
- return value ? value . replace ( this . decodePattern , '$1' ) : '' ;
154
+ return value ? value . replace ( _this . decodePattern , '$1' ) : '' ;
155
155
} ;
156
156
157
157
/**
@@ -175,7 +175,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
175
175
* @return {string } Syntax-encoded string version of choice
176
176
*/
177
177
this . encode = function ( choice ) {
178
- return this . delimiter + '[' + this . label ( choice ) + ':' + choice . id + ']' ;
178
+ return _this . delimiter + '[' + _this . label ( choice ) + ':' + choice . id + ']' ;
179
179
} ;
180
180
181
181
/**
@@ -189,12 +189,15 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
189
189
* @return {string } Human-readable string
190
190
*/
191
191
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 ] ;
193
193
var text = arguments . length <= 2 || arguments [ 2 ] === undefined ? ngModel . $viewValue : arguments [ 2 ] ;
194
194
195
195
// TODO: come up with a better way to detect what to remove
196
196
// 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 ) ;
198
201
return text ;
199
202
} ;
200
203
@@ -206,20 +209,26 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
206
209
* @param {mixed|object } [choice] The selected choice (default: activeChoice)
207
210
*/
208
211
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 ] ;
210
213
211
214
if ( ! choice ) {
212
215
return false ;
213
216
}
214
217
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
+ }
217
226
218
227
// Replace the search with the label
219
- ngModel . $setViewValue ( this . replace ( choice ) ) ;
228
+ ngModel . $setViewValue ( _this . replace ( choice ) ) ;
220
229
221
230
// Close choices panel
222
- this . cancel ( ) ;
231
+ _this . cancel ( ) ;
223
232
224
233
// Update the textarea
225
234
ngModel . $render ( ) ;
@@ -231,11 +240,11 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
231
240
* Moves this.activeChoice up the this.choices collection
232
241
*/
233
242
this . up = function ( ) {
234
- var index = this . choices . indexOf ( this . activeChoice ) ;
243
+ var index = _this . choices . indexOf ( _this . activeChoice ) ;
235
244
if ( index > 0 ) {
236
- this . activeChoice = this . choices [ index - 1 ] ;
245
+ _this . activeChoice = _this . choices [ index - 1 ] ;
237
246
} else {
238
- this . activeChoice = this . choices [ this . choices . length - 1 ] ;
247
+ _this . activeChoice = _this . choices [ _this . choices . length - 1 ] ;
239
248
}
240
249
} ;
241
250
@@ -245,11 +254,11 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
245
254
* Moves this.activeChoice down the this.choices collection
246
255
*/
247
256
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 ] ;
251
260
} else {
252
- this . activeChoice = this . choices [ 0 ] ;
261
+ _this . activeChoice = _this . choices [ 0 ] ;
253
262
}
254
263
} ;
255
264
@@ -263,13 +272,11 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
263
272
* @todo Try to avoid using a regex match object
264
273
*/
265
274
this . search = function ( match ) {
266
- var _this3 = this ;
267
-
268
- this . searching = match ;
275
+ _this . searching = match ;
269
276
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 ] ;
273
280
return choices ;
274
281
} ) ;
275
282
} ;
@@ -292,30 +299,36 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
292
299
* Clears the choices dropdown info and stops searching
293
300
*/
294
301
this . cancel = function ( ) {
295
- this . choices = [ ] ;
296
- this . searching = null ;
302
+ _this . choices = [ ] ;
303
+ _this . searching = null ;
297
304
} ;
298
305
299
306
this . autogrow = function ( ) {
300
307
$element [ 0 ] . style . height = 0 ; // autoshrink - need accurate scrollHeight
301
308
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
+ }
303
312
} ;
304
313
305
314
// Interactions to trigger searching
306
315
$element . on ( 'keyup click focus' , function ( event ) {
307
316
// 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
+ }
309
320
// 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
+ }
311
324
var text = $element . val ( ) ;
312
325
// 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 ) ) ;
314
327
315
328
if ( match ) {
316
- _this2 . search ( match ) ;
329
+ _this . search ( match ) ;
317
330
} else {
318
- _this2 . cancel ( ) ;
331
+ _this . cancel ( ) ;
319
332
}
320
333
321
334
if ( ! $scope . $$phase ) {
@@ -324,27 +337,29 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
324
337
} ) ;
325
338
326
339
$element . on ( 'keydown' , function ( event ) {
327
- if ( ! _this2 . searching ) return ;
340
+ if ( ! _this . searching ) {
341
+ return ;
342
+ }
328
343
329
344
switch ( event . keyCode ) {
330
345
case 13 :
331
346
// return
332
- _this2 . select ( ) ;
347
+ _this . select ( ) ;
333
348
break ;
334
349
case 38 :
335
350
// up
336
- _this2 . up ( ) ;
351
+ _this . up ( ) ;
337
352
break ;
338
353
case 40 :
339
354
// down
340
- _this2 . down ( ) ;
355
+ _this . down ( ) ;
341
356
break ;
342
357
default :
343
358
// Exit function
344
359
return ;
345
360
}
346
361
347
- _this2 . moved = true ;
362
+ _this . moved = true ;
348
363
event . preventDefault ( ) ;
349
364
350
365
if ( ! $scope . $$phase ) {
@@ -353,22 +368,26 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
353
368
} ) ;
354
369
355
370
this . onMouseup = ( function ( event ) {
356
- var _this4 = this ;
371
+ var _this2 = this ;
357
372
358
- if ( event . target == $element [ 0 ] ) return ;
373
+ if ( event . target == $element [ 0 ] ) {
374
+ return ;
375
+ }
359
376
360
377
$document . off ( 'mouseup' , this . onMouseup ) ;
361
378
362
- if ( ! this . searching ) return ;
379
+ if ( ! this . searching ) {
380
+ return ;
381
+ }
363
382
364
383
// Let ngClick fire first
365
384
$scope . $evalAsync ( function ( ) {
366
- _this4 . cancel ( ) ;
385
+ _this2 . cancel ( ) ;
367
386
} ) ;
368
387
} ) . bind ( this ) ;
369
388
370
389
$element . on ( 'focus' , function ( event ) {
371
- $document . on ( 'mouseup' , _this2 . onMouseup ) ;
390
+ $document . on ( 'mouseup' , _this . onMouseup ) ;
372
391
} ) ;
373
392
374
393
// Autogrow is mandatory beacuse the textarea scrolls away from highlights
0 commit comments