Skip to content
This repository was archived by the owner on Apr 7, 2020. It is now read-only.

Commit 51accde

Browse files
authored
Choose one mention twice (#56)
* update dependencies * choosing one mention twice * add tests
1 parent f37aa30 commit 51accde

File tree

6 files changed

+188
-116
lines changed

6 files changed

+188
-116
lines changed

dist/mention.js

Lines changed: 68 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ angular.module('ui.mention', []).directive('uiMention', function () {
2020
'use strict';
2121

2222
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;
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

Comments
 (0)