Đồng thời thay vì việc phải gõ @"tên người dùng" đối với thành viên sử dụng tên có dấu cách hoặc dấu chấm, plugin này sẽ tự động chèn dấu ngoặc kép " "
CÁCH SỬ DỤNG
Gõ "@" sau đó gõ chữ cái đầu tên của thành viên "@a" một danh sách thành viên có tên bắt đầu bằng chữ "a". Khi bạn tiếp tục gõ thêm nhiều ký tự thì các kết quả sẽ giảm đi và chính xác hơn!
Plugin này có thể áp dụng cho tất cả các phiên bản diễn đàn hiện có của #forumvi
Lưu ý: Nếu bạn đã sửa đổi các temple memberlist_body plugin này có thể không hoạt động
Truy cập Admin Panel > Modules > JavaScript codes management vào tạo một script (js)
Title : @Mention Auto-suggest
Placement : In all the pages
CÁCH SỬ DỤNG
Gõ "@" sau đó gõ chữ cái đầu tên của thành viên "@a" một danh sách thành viên có tên bắt đầu bằng chữ "a". Khi bạn tiếp tục gõ thêm nhiều ký tự thì các kết quả sẽ giảm đi và chính xác hơn!
Plugin này có thể áp dụng cho tất cả các phiên bản diễn đàn hiện có của #forumvi
Lưu ý: Nếu bạn đã sửa đổi các temple memberlist_body plugin này có thể không hoạt động
CÁCH CÀI ĐẶT
Truy cập Admin Panel > Modules > JavaScript codes management vào tạo một script (js)
Title : @Mention Auto-suggest
Placement : In all the pages
- Code:
!window.fa_mentionner && !/\/privmsg|\/profile\?mode=editprofile&page_profil=signature/.test(window.location.href)&& $(function(){$(function(){ 'SCEDITOR @HANDLE AUTO-SUGGEST'; 'DEVELOPED BY ANGE TUTEUR'; 'NO DISTRIBUTION WITHOUT CONSENT OF THE AUTHOR'; 'ORIGIN : http://fmdesign.forumotion.com/t943-auto-suggest-mentions-as-you-type#19157'; var container = $('.sceditor-container')[0], text_editor =document.getElementById('text_editor_textarea'), frame, instance; if (container && text_editor) { frame = $('iframe', container); instance = $(text_editor).sceditor('instance'); window.fa_mentionner = { suggest_delay : 100, // delay before suggestions show up (100ms) // language presets lang : { placeholder : 'Đang tìm kiếm...', not_found : 'Không có thành viên này' }, // colors of the suggestion popup color : { font : '#333', hover_font : '#FFF', error_font : '#F00', background : '#FFF', hover_background : '#69C', border : '#CCC', shadow : 'rgba(0, 0, 0, 0.176)' }, // sceditor instance and rangeHelper instance : instance, rangeHelper : instance.getRangeHelper(), // cached nodes for listening and modifications frame : frame[0], body : frame.contents().find('body')[0], textarea : $('textarea', container)[0], // faux textarea and suggestion list faux_textarea : $('<div id="faux_text_editor" />')[0], // helps us mirror the cursor position in source mode list : $('<div id="fa_mention_suggestions" style="position:absolute;" />')[0], // version specific selectors selectors : $('.bodyline')[0] ? ['a.gen[href^="/u"]','.avatar.mini a'] : document.getElementById('ipbwrapper') ?['.membername', '.mini-avatar'] : null, // adjusts the scroll position of the faux textarea so the caret stays in line adjustScroll : function() { fa_mentionner.faux_textarea.scrollTop =fa_mentionner.textarea.scrollTop; }, // updates the content in the faux textarea updateFauxTextarea : function(active, key) { if (key == 16) { // 16 = SHIFT return; // return when specific keys are pressed } // clear suggestion queue when suggestions aren't active if (active != true) { fa_mentionner.clearSuggestions(); } else { return; // return when interactive keys are pressed while suggesting ; up, down, enter } // use another method if in WYSIWYG mode if (!fa_mentionner.instance.inSourceMode()) { key != 32 ? fa_mentionner.searchWYSIWYG() :fa_mentionner.clearSuggestions(); return; } var val = fa_mentionner.instance.val(), range = 0, selection, faux_caret, username; // get the position of the caret if (document.selection) { selection = document.selection.createRange(); selection.moveStart('character', -fa_mentionner.textarea.length); range = selection.text.length; } else if (fa_mentionner.textarea.selectionStart ||fa_mentionner.textarea.selectionStart == 0) { range = fa_mentionner.textarea.selectionStart; } // set the position of the caret val = val.slice(0, range) + '{FAUX_CARET}' +val.slice(range, val.length); // parse and sanitize the faux textarea content $(fa_mentionner.faux_textarea).html( val.replace(/</g, '<') .replace(/>/g, '>') .replace(/@"(.*?)"|@(.*?)(?:\s|\r|\n|$)/g, function(M,$1, $2) { var lastChar = M.substr(-1), name = ($1 || $2 || '').replace(/\{FAUX_CARET\}|"/g, ''); return '<a href="#' + name + '">' +(/\s|\r|\n/.test(M) ? M.slice(0, M.length - 1) + '</a>' + lastChar: M + '</a>'); }) .replace(/\{FAUX_CARET\}/, '<span id="faux_caret" style="position:absolute;margin-left:-3px;">|</span>') ); faux_caret = document.getElementById('faux_caret'); // mentions are parsed as <a>nchors, so when the faux caret is inside one we'll show some suggestions if (faux_caret && faux_caret.parentNode.tagName == 'A') { fa_mentionner.value = val; fa_mentionner.delay = window.setTimeout(function() { fa_mentionner.suggest(faux_caret.parentNode.href.replace(/.*?#(.*)/, '$1'), $(faux_caret).offset()); }, fa_mentionner.suggest_delay); } fa_mentionner.adjustScroll(); }, // search for active mentions in wysiwyg mode searchWYSIWYG : function() { var selected = fa_mentionner.rangeHelper.cloneSelected(), mentions = selected.startContainer.data &&selected.startContainer.data.match(/(@".*?")|(@.*?)(?:\s|\r|\n|$)/g), offset, offset_marker, hit, i; console.log(mentions); if (mentions && mentions[0]) { // clean up whitespace for (i in mentions) { mentions[i] = mentions[i].replace(/\s$/g, ''); } // search for the mention that's currently being modified for (i in mentions) { if (!fa_mentionner.wysiwyg_mentions || (mentions[i] !=fa_mentionner.wysiwyg_mentions[i])) { hit = true; fa_mentionner.delay = window.setTimeout(function() { fa_mentionner.rangeHelper.insertMarkers(); // insert markers to help get the caret offset offset = $(fa_mentionner.frame).offset(); offset_marker = $('#sceditor-end-marker',fa_mentionner.body).show().offset(); // add the marker offsets to the iframe offsets offset.left += offset_marker.left; offset.top += offset_marker.top -fa_mentionner.body.scrollTop; fa_mentionner.suggest(mentions[i].slice(1).replace(/^"|"$/g, ''),offset, true); fa_mentionner.wysiwyg_active = mentions[i]; // save the active mention for later use in finish() }, fa_mentionner.suggest_delay); break; } } // hide the suggestion list if there's no newly modified mentions if (!hit) { fa_mentionner.list.style.display = 'none'; fa_mentionner.focused = null; } fa_mentionner.wysiwyg_mentions = mentions; // update the list of mentions } }, // suggest a list of users based on the passed username suggest : function(username, offset, wysiwyg) { // insert the suggestion list to show that it's searching fa_mentionner.list.innerHTML = '<span class="fam-info">' +fa_mentionner.lang.placeholder + '</span>'; $(fa_mentionner.list).css({ left : offset.left + 'px', top : offset.top + 'px', display : 'block', overflowY : 'auto' }); document.body.appendChild(fa_mentionner.list); // send a query request to the memeberlist to find users who match the typed username fa_mentionner.request = $.get('/memberlist?username=' +username, function(d) { fa_mentionner.request = null; var suggestion = $(fa_mentionner.selectors ?fa_mentionner.selectors[0] : '.avatar-mini a', d), ava = fa_mentionner.selectors ?$(fa_mentionner.selectors[1], d) : null, i = 0, j = suggestion.length, name; fa_mentionner.list.innerHTML = ''; if (j) { for (; i < j; i++) { name = $(suggestion[i]).text().replace(/^\s+|\s+$/g,''); fa_mentionner.list.insertAdjacentHTML('beforeend', '<a href="javascript:fa_mentionner.finish(\'' +name.replace(/'/g, '\\\'') + '\', ' + wysiwyg + ');" class="fa_mention_suggestion">'+ '<img class="fa_suggested_avatar" src="' +$(fa_mentionner.selectors ? ava[i] :suggestion[i]).find('img').attr('src') + '"/>'+ '<span class="fa_suggested_name">' + name +'</span>'+ '</a>' ); } // change overflowY property when it exceeds 7 suggestions -- prevents unsightly scroll bug fa_mentionner.list.style.overflowY = j > 7 ? 'scroll': 'auto'; // update the focused suggestion and scroll it into view fa_mentionner.list.firstChild.className += ' fam-focus'; fa_mentionner.focused = fa_mentionner.list.firstChild; fa_mentionner.scrollSuggestions(); } else { fa_mentionner.list.innerHTML = '<span class="fam-info" style="color:' + fa_mentionner.color.error_font + ';">' +fa_mentionner.lang.not_found + '</span>'; } }); }, // kill the suggestion timeout while typing persists clearSuggestions : function() { if (fa_mentionner.delay) { window.clearTimeout(fa_mentionner.delay); fa_mentionner.delay = null; fa_mentionner.list.style.display = 'none'; fa_mentionner.focused = null; } if (fa_mentionner.request) { fa_mentionner.request.abort(); fa_mentionner.request = null; } }, // finish the username finish : function(username, wysiwyg) { var mention, index, i; // hide and clear suggestions fa_mentionner.clearSuggestions(); fa_mentionner.focused = null; fa_mentionner.list.style.display = 'none'; if (!wysiwyg) { fa_mentionner.value =fa_mentionner.value.replace(/(?:@".[^"]*?\{FAUX_CARET\}.*?"|@\{FAUX_CARET\}.*?(\s|\n|\r|$)|@.[^"\s]*?\{FAUX_CARET\}.*?(\s|\n|\r|$))/, function(M, $1, $2) { mention = '@"' + username + '"'; return '{MENTION_POSITION}' + ( $1 ? $1 : $2 ? $2 : ''); }); // get the index where the mention should be index =fa_mentionner.value.indexOf('{MENTION_POSITION}'); fa_mentionner.value =fa_mentionner.value.replace('{MENTION_POSITION}', ''); // save current scroll position for application after the value has been updated fa_mentionner.scrollIndex =fa_mentionner.textarea.scrollTop; // update the textarea with the completed mention fa_mentionner.instance.val(''); fa_mentionner.instance.insert(fa_mentionner.value.slice(0, index)+ mention, fa_mentionner.value.slice(index,fa_mentionner.value.length)); // restore the scroll position for the textareas fa_mentionner.textarea.scrollTop =fa_mentionner.scrollIndex; fa_mentionner.adjustScroll(); // update the fake textarea fa_mentionner.updateFauxTextarea(); } else { // save the caret range in WYSIWYG so we can restore it after replacing the HTML fa_mentionner.rangeHelper.saveRange(); fa_mentionner.body.innerHTML =fa_mentionner.body.innerHTML.replace(newRegExp(fa_mentionner.wysiwyg_active.replace(/[.*+?^${}()|[\]\\]/g,"\\$&") + '(<span.*?id="sceditor-end-marker".*?>)'), '@"' +username + '"$1'); fa_mentionner.body.focus(); fa_mentionner.rangeHelper.restoreRange(); // update the wysiwyg mention array so no new suggestions appear for (i in fa_mentionner.wysiwyg_mentions) { if (fa_mentionner.wysiwyg_mentions[i] ==fa_mentionner.wysiwyg_active) { fa_mentionner.wysiwyg_mentions[i] = '@"' + username+ '"'; break; } } } }, // scroll the selected suggestion into view scrollSuggestions : function() { $(fa_mentionner.list).scrollTop( ( $(fa_mentionner.focused).offset().top - $(fa_mentionner.list).offset().top + $(fa_mentionner.list).scrollTop() ) - (26 * 3) // 26 = the height of the suggestions, so display 3 suggestions above while scrolling ); } }; // get computed styles for the textarea and apply them to the faux textarea for (var css = window.getComputedStyle(fa_mentionner.textarea,null), i = 0, j = css.length, str = ''; i < j; i++) { str += css[i] + ':' + css.getPropertyValue(css[i]) + ';'; } // add styles to the head $('head').append('<style type="text/css">'+ '#faux_text_editor {' + str + '}'+ '#faux_text_editor { position:absolute; left:0; bottom:0; z-index:-1; visibility:hidden; display:block; overflow-y:auto; }'+ '#fa_mention_suggestions { color:' +fa_mentionner.color.font + '; font-size:10px; font-family:arial, verdana, sans-serif; background:' + fa_mentionner.color.background+ '; border:1px solid ' + fa_mentionner.color.border + '; margin-top:20px; z-index:999; max-height:182px; overflow-x:hidden; box-shadow:0 6px 12px ' + fa_mentionner.color.shadow + '; }'+ 'a.fa_mention_suggestion, .fam-info { color:' +fa_mentionner.color.font + '; height:26px; line-height:26px; padding:0 3px; display:block; white-space:nowrap; cursor:pointer; }'+ 'a.fa_mention_suggestion.fam-focus { color:' +fa_mentionner.color.hover_font + '; background:' +fa_mentionner.color.hover_background + '; }'+ '.fa_suggested_avatar { height:20px; width:20px; vertical-align:middle; margin-right:3px; }'+ '</style>'); // insert faux textarea into document fa_mentionner.textarea.parentNode.insertBefore(fa_mentionner.faux_textarea,fa_mentionner.textarea); // apply event handlers fa_mentionner.textarea.onclick =fa_mentionner.updateFauxTextarea; fa_mentionner.textarea.onscroll = fa_mentionner.adjustScroll; // update the faux textarea on keyup fa_mentionner.instance.keyUp(function(e) { if (fa_mentionner.focused && e && (e.keyCode == 13 ||e.keyCode == 38 || e.keyCode == 40)) { fa_mentionner.updateFauxTextarea(true, e.keyCode); return false; } else { fa_mentionner.updateFauxTextarea(false, e.keyCode); } }); // key events for the suggested mentions $([document, fa_mentionner.body]).on('keydown', function(e) { var that = e.target; if (fa_mentionner.focused && e && e.keyCode && (that.tagName== 'TEXTAREA' || that.tagName == 'BODY')) { // move selection down if (e.keyCode == 40) { var next = fa_mentionner.focused.nextSibling; if (next) { $(fa_mentionner.focused).removeClass('fam-focus'); next.className += ' fam-focus'; fa_mentionner.focused = next; fa_mentionner.scrollSuggestions(); } return false; } // move selection up if (e.keyCode == 38) { var prev = fa_mentionner.focused.previousSibling; if (prev) { $(fa_mentionner.focused).removeClass('fam-focus'); prev.className += ' fam-focus'; fa_mentionner.focused = prev; fa_mentionner.scrollSuggestions(); } return false; } // apply selection if (e.keyCode == 13) { fa_mentionner.focused.click(); return false; } } }); // update focused suggestion on hover $(document).on('mouseover', function(e) { var that = e.target; if (/fa_mention_suggestion/.test(that.className)) { $(fa_mentionner.focused).removeClass('fam-focus'); that.className += ' fam-focus'; fa_mentionner.focused = that; } }); }})});