You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

179 lines
6.8 KiB

/**
* Show a dropdown of users following a @ char if...
*/
$.fn.userMentions = function () {
this.each(function () {
let $textarea = $(this), selection, mention,
usernamePattern = jqueryTiki.usernamePattern ?? "/^['\-_a-zA-Z0-9\.]*$/";
usernamePattern = usernamePattern.substring(2, usernamePattern.length - 2); // trim /^ and $/
let mentionPattern = new RegExp("@(" + usernamePattern + ")$");
$textarea.tiki("autocomplete", "userrealname", {
tiki_replace_term: function (val) {
selection = $textarea.selection();
let start = val.lastIndexOf("@", selection.end);
// find the last word typed starting with a @
mention = val.substring(start, selection.end).match(mentionPattern);
if (mention && mention[1].length > 1) {
mention = mention[1];
return mention;
} else {
return "";
}
},
select: function (event, data) {
let val = $textarea.val();
let regex = new RegExp("\\((" + usernamePattern + ")\\)$"),
userName = data.item.value.match(regex);
if (userName) {
userName = userName[1];
} else {
userName = data.item.value;
}
let start = val.lastIndexOf("@" + mention, selection.end);
val = val.substring(0, start) + "@" + userName + val.substring(start + 1 + mention.length);
$textarea.val(val).selection(start + 1 + userName.length);
return false;
},
open: function( event, ui ) {
let $menu = $textarea.autocomplete("widget"),
offset = $textarea.offset(),
coords = getCaretCoordinates($textarea[0], selection.end);
let lineHeight = parseInt($menu.css("font-size").replace(/\D+/, ""));
$menu.css({
left: (offset.left + coords.left) + "px",
top: (offset.top + coords.top + lineHeight) + "px",
width: "auto",
});
},
})
});
}
// from https://stackoverflow.com/a/22446703/2459703
if (typeof getCaretCoordinates === "undefined") {
function getCaretCoordinates(element, position) {
// The properties that we copy into a mirrored div.
// Note that some browsers, such as Firefox,
// do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
// so we have to do every single property specifically.
var properties = [
'boxSizing',
'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
'height',
'overflowX',
'overflowY', // copy the scrollbar for IE
'borderTopWidth',
'borderRightWidth',
'borderBottomWidth',
'borderLeftWidth',
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft',
// https://developer.mozilla.org/en-US/docs/Web/CSS/font
'fontStyle',
'fontVariant',
'fontWeight',
'fontStretch',
'fontSize',
'lineHeight',
'fontFamily',
'textAlign',
'textTransform',
'textIndent',
'textDecoration', // might not make a difference, but better be safe
'letterSpacing',
'wordSpacing'
];
var isFirefox = !(window.mozInnerScreenX == null);
//var mirrorDivDisplayCheckbox = document.getElementById('mirrorDivDisplay');
var mirrorDiv, computed, style;
// mirrored div
mirrorDiv = document.getElementById(element.nodeName + '--mirror-div');
if (!mirrorDiv) {
mirrorDiv = document.createElement('div');
mirrorDiv.id = element.nodeName + '--mirror-div';
document.body.appendChild(mirrorDiv);
}
style = mirrorDiv.style;
computed = getComputedStyle(element);
// default textarea styles
style.whiteSpace = 'pre-wrap';
if (element.nodeName !== 'INPUT') {
style.wordWrap = 'break-word';
} // only for textarea-s
// position off-screen
style.position = 'absolute'; // required to return coordinates properly
style.top = element.offsetTop + parseInt(computed.borderTopWidth) + 'px';
style.left = "400px";
// style.visibility = mirrorDivDisplayCheckbox.checked ? 'visible' : 'hidden'; // not 'display: none' because we want rendering
style.visibility = 'hidden';
// transfer the element's properties to the div
properties.forEach(function (prop) {
style[prop] = computed[prop];
});
if (isFirefox) {
style.width = parseInt(computed.width) - 2 + 'px' // Firefox adds 2 pixels to the padding -
// https://bugzilla.mozilla.org/show_bug.cgi?id=753662
// Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
if (element.scrollHeight > parseInt(computed.height)) {
style.overflowY = 'scroll';
}
} else {
style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
}
mirrorDiv.textContent = element.value.substring(0, position);
// the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces -
// http://stackoverflow.com/a/13402035/1269037
if (element.nodeName === 'INPUT') {
mirrorDiv.textContent = mirrorDiv.textContent.replace(/\s/g, "\u00a0");
}
var span = document.createElement('span');
// Wrapping must be replicated *exactly*, including when a long word gets
// onto the next line, with whitespace at the end of the line before (#7).
// The *only* reliable way to do that is to copy the *entire* rest of the
// textarea's content into the <span> created at the caret position.
// for inputs, just '.' would be enough, but why bother?
span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
span.style.backgroundColor = "lightgrey";
mirrorDiv.appendChild(span);
var coordinates = {
top: span.offsetTop + parseInt(computed['borderTopWidth']),
left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
};
return coordinates;
}
}