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

Commit

Permalink
Choose one mention twice (#56)
Browse files Browse the repository at this point in the history
* update dependencies
* choosing one mention twice
* add tests
  • Loading branch information
biggieman authored Jan 31, 2019
1 parent f37aa30 commit 51accde
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 116 deletions.
117 changes: 68 additions & 49 deletions dist/mention.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ angular.module('ui.mention', []).directive('uiMention', function () {
'use strict';

angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$attrs", "$q", "$timeout", "$document", function ($element, $scope, $attrs, $q, $timeout, $document) {
var _this2 = this;
var _this = this;

// Beginning of input or preceeded by spaces: @sometext
this.delimiter = '@';
Expand All @@ -43,8 +43,6 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
* @param {ngModelController} model
*/
this.init = function (model) {
var _this = this;

// Leading whitespace shows up in the textarea but not the preview
$attrs.ngTrim = 'false';

Expand All @@ -53,7 +51,9 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
ngModel.$parsers.push(function (value) {
// Removes any mentions that aren't used
_this.mentions = _this.mentions.filter(function (mention) {
if (~value.indexOf(_this.label(mention))) return value = value.replace(_this.label(mention), _this.encode(mention));
if (~value.indexOf(_this.label(mention))) {
return value = value.split(_this.label(mention)).join(_this.encode(mention));
}
});

_this.render(value);
Expand All @@ -70,7 +70,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
// Removes any mentions that aren't used
_this.mentions = _this.mentions.filter(function (mention) {
if (~value.indexOf(_this.encode(mention))) {
value = value.replace(_this.encode(mention), _this.label(mention));
value = value.split(_this.encode(mention)).join(_this.label(mention));
return true;
} else {
return false;
Expand Down Expand Up @@ -111,10 +111,10 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
html = (html || '').toString();
// Convert input to text, to prevent script injection/rich text
html = parseContentAsText(html);
_this2.mentions.forEach(function (mention) {
html = html.replace(_this2.encode(mention), _this2.highlight(mention));
_this.mentions.forEach(function (mention) {
html = html.split(_this.encode(mention)).join(_this.highlight(mention));
});
_this2.renderElement().html(html);
_this.renderElement().html(html);
return html;
};

Expand All @@ -138,7 +138,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
* @return {string} HTML highlighted version of the choice
*/
this.highlight = function (choice) {
return '<span>' + this.label(choice) + '</span>';
return '<span>' + _this.label(choice) + '</span>';
};

/**
Expand All @@ -151,7 +151,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
this.decode = function () {
var value = arguments.length <= 0 || arguments[0] === undefined ? ngModel.$modelValue : arguments[0];

return value ? value.replace(this.decodePattern, '$1') : '';
return value ? value.replace(_this.decodePattern, '$1') : '';
};

/**
Expand All @@ -175,7 +175,7 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
* @return {string} Syntax-encoded string version of choice
*/
this.encode = function (choice) {
return this.delimiter + '[' + this.label(choice) + ':' + choice.id + ']';
return _this.delimiter + '[' + _this.label(choice) + ':' + choice.id + ']';
};

/**
Expand All @@ -189,12 +189,15 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
* @return {string} Human-readable string
*/
this.replace = function (mention) {
var search = arguments.length <= 1 || arguments[1] === undefined ? this.searching : arguments[1];
var search = arguments.length <= 1 || arguments[1] === undefined ? _this.searching : arguments[1];
var text = arguments.length <= 2 || arguments[2] === undefined ? ngModel.$viewValue : arguments[2];

// TODO: come up with a better way to detect what to remove
// TODO: consider alternative to using regex match
text = text.substr(0, search.index + search[0].indexOf(this.delimiter)) + this.label(mention) + ' ' + text.substr(search.index + search[0].length);
if (search === null) {
return text;
}
text = text.substr(0, search.index + search[0].indexOf(_this.delimiter)) + _this.label(mention) + ' ' + text.substr(search.index + search[0].length);
return text;
};

Expand All @@ -206,20 +209,26 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
* @param {mixed|object} [choice] The selected choice (default: activeChoice)
*/
this.select = function () {
var choice = arguments.length <= 0 || arguments[0] === undefined ? this.activeChoice : arguments[0];
var choice = arguments.length <= 0 || arguments[0] === undefined ? _this.activeChoice : arguments[0];

if (!choice) {
return false;
}

// Add the mention
this.mentions.push(choice);
var mentionExists = ~_this.mentions.map(function (mention) {
return mention.id;
}).indexOf(choice.id);

This comment has been minimized.

Copy link
@sssats

sssats Feb 28, 2019

This check is too specific for your code)

This comment has been minimized.

Copy link
@biggieman

biggieman Mar 1, 2019

Author Contributor

Done!
#57


// Add the mention, unless its already been mentioned
if (!mentionExists) {
_this.mentions.push(choice);
}

// Replace the search with the label
ngModel.$setViewValue(this.replace(choice));
ngModel.$setViewValue(_this.replace(choice));

// Close choices panel
this.cancel();
_this.cancel();

// Update the textarea
ngModel.$render();
Expand All @@ -231,11 +240,11 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
* Moves this.activeChoice up the this.choices collection
*/
this.up = function () {
var index = this.choices.indexOf(this.activeChoice);
var index = _this.choices.indexOf(_this.activeChoice);
if (index > 0) {
this.activeChoice = this.choices[index - 1];
_this.activeChoice = _this.choices[index - 1];
} else {
this.activeChoice = this.choices[this.choices.length - 1];
_this.activeChoice = _this.choices[_this.choices.length - 1];
}
};

Expand All @@ -245,11 +254,11 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
* Moves this.activeChoice down the this.choices collection
*/
this.down = function () {
var index = this.choices.indexOf(this.activeChoice);
if (index < this.choices.length - 1) {
this.activeChoice = this.choices[index + 1];
var index = _this.choices.indexOf(_this.activeChoice);
if (index < _this.choices.length - 1) {
_this.activeChoice = _this.choices[index + 1];
} else {
this.activeChoice = this.choices[0];
_this.activeChoice = _this.choices[0];
}
};

Expand All @@ -263,13 +272,11 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
* @todo Try to avoid using a regex match object
*/
this.search = function (match) {
var _this3 = this;

this.searching = match;
_this.searching = match;

return $q.when(this.findChoices(match, this.mentions)).then(function (choices) {
_this3.choices = choices;
_this3.activeChoice = choices[0];
return $q.when(_this.findChoices(match, _this.mentions)).then(function (choices) {
_this.choices = choices;
_this.activeChoice = choices[0];
return choices;
});
};
Expand All @@ -292,30 +299,36 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
* Clears the choices dropdown info and stops searching
*/
this.cancel = function () {
this.choices = [];
this.searching = null;
_this.choices = [];
_this.searching = null;
};

this.autogrow = function () {
$element[0].style.height = 0; // autoshrink - need accurate scrollHeight
var style = getComputedStyle($element[0]);
if (style.boxSizing == 'border-box') $element[0].style.height = $element[0].scrollHeight + 'px';
if (style.boxSizing == 'border-box') {
$element[0].style.height = $element[0].scrollHeight + 'px';
}
};

// Interactions to trigger searching
$element.on('keyup click focus', function (event) {
// If event is fired AFTER activeChoice move is performed
if (_this2.moved) return _this2.moved = false;
if (_this.moved) {
return _this.moved = false;
}
// Don't trigger on selection
if ($element[0].selectionStart != $element[0].selectionEnd) return;
if ($element[0].selectionStart != $element[0].selectionEnd) {
return;
}
var text = $element.val();
// text to left of cursor ends with `@sometext`
var match = _this2.searchPattern.exec(text.substr(0, $element[0].selectionStart));
var match = _this.searchPattern.exec(text.substr(0, $element[0].selectionStart));

if (match) {
_this2.search(match);
_this.search(match);
} else {
_this2.cancel();
_this.cancel();
}

if (!$scope.$$phase) {
Expand All @@ -324,27 +337,29 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
});

$element.on('keydown', function (event) {
if (!_this2.searching) return;
if (!_this.searching) {
return;
}

switch (event.keyCode) {
case 13:
// return
_this2.select();
_this.select();
break;
case 38:
// up
_this2.up();
_this.up();
break;
case 40:
// down
_this2.down();
_this.down();
break;
default:
// Exit function
return;
}

_this2.moved = true;
_this.moved = true;
event.preventDefault();

if (!$scope.$$phase) {
Expand All @@ -353,22 +368,26 @@ angular.module('ui.mention').controller('uiMention', ["$element", "$scope", "$at
});

this.onMouseup = (function (event) {
var _this4 = this;
var _this2 = this;

if (event.target == $element[0]) return;
if (event.target == $element[0]) {
return;
}

$document.off('mouseup', this.onMouseup);

if (!this.searching) return;
if (!this.searching) {
return;
}

// Let ngClick fire first
$scope.$evalAsync(function () {
_this4.cancel();
_this2.cancel();
});
}).bind(this);

$element.on('focus', function (event) {
$document.on('mouseup', _this2.onMouseup);
$document.on('mouseup', _this.onMouseup);
});

// Autogrow is mandatory beacuse the textarea scrolls away from highlights
Expand Down
Loading

0 comments on commit 51accde

Please sign in to comment.