如何在我的脚本中自定义此插件以提及或标记?

时间:2018-03-05 03:53:02

标签: javascript jquery ajax

我在项目中使用此插件来标记/提及人物。我想再添加一个选项来标记项目中的项目。这里用于标记人们' @'用来。我想用'#'也用于标记。哪个部分我必须添加或修改标记人和列表在我的project.i想标记人们用' @'并希望用'#'标记列表。

     var tmpEle = null;

(function ($) {
    var KEY = {
        AT:              64,
        BACKSPACE:       8,
        DELETE:          46,
        TAB:             9,
        ESC:             27,
        RETURN:          13,
        LEFT:            37,
        UP:              38,
        RIGHT:           39,
        DOWN:            40,
        SPACE:           32,
        HOME:            36,
        END:             35,
        COMMA:           188,
        NUMPAD_ADD:      107,
        NUMPAD_DECIMAL:  110,
        NUMPAD_DIVIDE:   111,
        NUMPAD_ENTER:    108,
        NUMPAD_MULTIPLY: 106,
        NUMPAD_SUBTRACT: 109,
        PAGE_DOWN:       34,
        PAGE_UP:         33,
        PERIOD:          190,
    };

    jQuery.fn.mentiony = function (method, options) {
        var defaults = {
            debug:              0, // Set 1 to see console log message of this plugin

            applyInitialSize:   true,

            globalTimeout:      null, // Don't overwrite this config
            timeOut:            400, // Do mention only when user input idle time > this value
            triggerChar:        '@', // @keyword-to-mention

            onDataRequest: function (mode, keyword, onDataRequestCompleteCallback) {

            },


            onKeyPress: function (event, oldInputEle, newEditableEle) {
                oldInputEle.trigger(event);
            },
            onKeyUp:    function (event, oldInputEle, newEditableEle) {
                oldInputEle.trigger(event);
            },
            onBlur:     function (event, oldInputEle, newEditableEle) {
                oldInputEle.trigger(event);
            },
            onPaste:    function (event, oldInputEle, newEditableEle) {
                oldInputEle.trigger(event);
            },
            onInput: function (oldInputEle, newEditableEle) {

            },

            // adjust popover relative position with its parent.
            popoverOffset:      {
                x: -30,
                y: 0
            },

            templates:          {
                container:        '<div id="mentiony-container-[ID]" class="mentiony-container"></div>',
                content:          '<div id="mentiony-content-[ID]" class="mentiony-content" contenteditable="true"></div>',
                popover:          '<div id="mentiony-popover-[ID]" class="mentiony-popover"></div>',
                list:             '<ul id="mentiony-popover-[ID]" class="mentiony-list"></ul>',
                listItem:         '<li class="mentiony-item" data-item-id="">' +
                                  '<div class="row">' +
                                  '<div class="col-xs-3 col-sm-3 col-md-3 col-lg-3">' +
                                  '<img src="https://avatars2.githubusercontent.com/u/1859127?v=3&s=140">' +
                                  '</div>' +
                                  '<div class="pl0 col-xs-9 col-sm-9 col-md-9 col-lg-9">' +
                                  '<p class="title">Company name</p>' +
                                  '<p class="help-block">Addition information</p>' +
                                  '</div>' +
                                  '</div>' +
                                  '</li>',
                normalText:       '<span class="normal-text">&nbsp;</span>',
                highlight:        '<span class="highlight"></span>',
                highlightContent: '<a href="[HREF]" data-item-id="[ITEM_ID]" class="mentiony-link">[TEXT]</a>',
            }
        };

        if (typeof method === 'object' || !method) {
            options = method;
        }

        var settings = $.extend({}, defaults, options);

        return this.each(function () {
            var instance = $.data(this, 'mentiony') || $.data(this, 'mentiony', new MentionsInput(settings));

            if (typeof instance[method] === 'function') {
                return instance[method].apply(this, Array.prototype.slice.call(outerArguments, 1));
            } else if (typeof method === 'object' || !method) {
                return instance.init.call(this, this);
            } else {
                $.error('Method ' + method + ' does not exist');
            }
        });
    };

    var MentionsInput = function (settings) {
        var elmInputBoxContainer, elmInputBoxContent, elmInputBox,
            elmInputBoxInitialWidth, elmInputBoxInitialHeight,
            editableContentLineHeightPx,
            popoverEle, list, elmInputBoxId,
            elmInputBoxContentAbsPosition = {top: 0, left: 0},
            dropDownShowing = false,
            events          = {
                keyDown:  false,
                keyPress: false,
                input:    false,
                keyup:    false,
            },
            currentMention  = {
                keyword:            '',
                jqueryDomNode:      null, // represent jQuery dom data
                mentionItemDataSet: [], // list item json data was store here
                lastActiveNode:     0,
                charAtFound:        false, // tracking @ char appear or not
            }
            ;
        var needMention = false; // Mention state
        var inputId = Math.random().toString(36).substr(2, 6); // generate 6 character rand string

        var onDataRequestCompleteCallback = function (responseData) {
            populateDropdown(currentMention.keyword, responseData);
        };

        function initTextArea(ele) {
            elmInputBox = $(ele);

            if (elmInputBox.attr('data-mentions-input') == 'true') {
                return;
            } else {
                elmInputBox.attr('data-mentions-input', 'true');

                if (elmInputBox.attr('id').length == 0) {
                    elmInputBoxId = 'mentiony-input-' + inputId;
                    elmInputBox.attr('id', elmInputBoxId);
                } else {
                    elmInputBoxId = elmInputBox.attr('id');
                }
            }


            // Initial UI information
            elmInputBoxInitialWidth = elmInputBox.prop('scrollWidth');
            elmInputBoxInitialHeight = elmInputBox.prop('scrollHeight');

            // Container
            elmInputBoxContainer = $(settings.templates.container.replace('[ID]', inputId));
            elmInputBoxContent = $(settings.templates.content.replace('[ID]', inputId));

            // Make UI and hide the textarea
            var placeholderText = elmInputBox.attr('placeholder');
            if (typeof placeholderText === 'undefined') {
                placeholderText = elmInputBox.text();
            }
            elmInputBoxContent.attr('data-placeholder', placeholderText);

            elmInputBoxContainer.append(elmInputBox.clone().addClass('mention-input-hidden'));
            elmInputBoxContainer.append(elmInputBoxContent);
            elmInputBox.replaceWith(elmInputBoxContainer);

            popoverEle = $(settings.templates.popover.replace('[ID]', inputId));
            list = $(settings.templates.list.replace('[ID]', inputId));
            elmInputBoxContainer.append(popoverEle);
            popoverEle.append(list);

            // Reset the input
            elmInputBox = $('#' + elmInputBoxId);

            // Update initial UI
            var containerPadding = parseInt(elmInputBoxContainer.css('padding'));

            if (settings.applyInitialSize) {
                elmInputBoxContainer.addClass('initial-size');
                elmInputBoxContainer.css({width: (elmInputBoxInitialWidth) + 'px'});
                elmInputBoxContent.width((elmInputBoxInitialWidth - 2 * containerPadding) + 'px');
            }
            else {
                elmInputBoxContainer.addClass('auto-size');
            }

            elmInputBoxContent.css({minHeight: elmInputBoxInitialHeight + 'px'});

            elmInputBoxContentAbsPosition = elmInputBoxContent.offset();
            editableContentLineHeightPx = parseInt($(elmInputBoxContent.css('line-height')).selector);


            elmInputBoxContent.bind('keydown', onInputBoxKeyDown);
            elmInputBoxContent.bind('keypress', onInputBoxKeyPress);
            elmInputBoxContent.bind('input', onInputBoxInput);
            elmInputBoxContent.bind('keyup', onInputBoxKeyUp);
            elmInputBoxContent.bind('click', onInputBoxClick);
            elmInputBoxContent.bind('blur', onInputBoxBlur);
            elmInputBoxContent.bind('paste', onInputBoxPaste);
        }

        function onInputBoxKeyDown(e) {

            events = {
                keyDown:  true,
                keyPress: false,
                input:    false,
                keyup:    false,
            };


            if (dropDownShowing) {
                return handleUserChooseOption(e);
            }
        }


        function onInputBoxKeyPress(e) {
            // log('onInputBoxKeyPress');
            events.keyPress = true;

            if (!needMention) {
                // Try to check if need mention
                needMention = (e.keyCode === KEY.AT);
                // log(needMention, 'needMention', 'info');
            }

            settings.onKeyPress.call(this, e, elmInputBox, elmInputBoxContent);
        }

        function onInputBoxInput() {
            // log('onInputBoxInput');
            events.input = true;

            settings.onInput.call(this, elmInputBox, elmInputBoxContent);
        }

        /**
         * Put all special key handle here
         * @param e
         */
        function onInputBoxKeyUp(e) {
            // log('onInputBoxKeyUp');
            events.keyup = true;
            // log(events, 'events');

            if (events.input) {
                updateDataInputData(e);
            }

            if (needMention) {
                // Update mention keyword only inputing(not enter), left, right
                if (e.keyCode !== KEY.RETURN && (events.input || e.keyCode === KEY.LEFT || e.keyCode === KEY.RIGHT)) {
                    updateMentionKeyword(e);
                    doSearchAndShow();
                }
            }

            settings.onKeyUp.call(this, e, elmInputBox, elmInputBoxContent);
        }

        function onInputBoxClick(e) {
            // log('onInputBoxClick');

            if (needMention) {
                updateMentionKeyword(e);
                doSearchAndShow();
            }
        }

        function onInputBoxBlur(e) {
            // log('onInputBoxBlur');

            settings.onBlur.call(this, e, elmInputBox, elmInputBoxContent);
        }

        function onInputBoxPaste(e) {
            // log('onInputBoxPaste');

            settings.onPaste.call(this, e, elmInputBox, elmInputBoxContent);
        }

        function onListItemClick(e) {
            //$(this) is the clicked listItem
            setSelectedMention($(this));
            choseMentionOptions(true);
        }

        function updateDataInputData(e) {
            var elmInputBoxText = elmInputBoxContent.html();
            elmInputBox.val(convertSpace(elmInputBoxText));
            log(elmInputBox.val(), 'elmInputBoxText : ');
            tmpEle = elmInputBox;
        }

        function trimSpace(text) {
            return text.replace(/^(&nbsp;|&nbsp|\s)+|(&nbsp;|&nbsp|\s)+$/g, '');
        }


        function convertSpace(text) {
            return text.replace(/(&nbsp;)+/g, ' ');
        }

        /**
         * Handle User search action with timeout, and show mention if needed
         */
        function doSearchAndShow() {

            if (settings.timeOut > 0) {
                if (settings.globalTimeout !== null) {
                    clearTimeout(settings.globalTimeout);
                }
                settings.globalTimeout = setTimeout(function () {
                    settings.globalTimeout = null;

                    settings.onDataRequest.call(this, 'search', currentMention.keyword, onDataRequestCompleteCallback);

                }, settings.timeOut);

            } else {
                settings.onDataRequest.call(this, 'search', currentMention.keyword, onDataRequestCompleteCallback);
            }
        }

        function populateDropdown(keyword, responseData) {
            list.empty();
            currentMention.jqueryDomNode = null;
            currentMention.mentionItemDataSet = responseData;

            if (responseData.length) {
                if (currentMention.charAtFound === true) {
                    showDropDown();
                }

                responseData.forEach(function (item, index) {
                    var listItem = $(settings.templates.listItem);
                    listItem.attr('data-item-id', item.id);
                    listItem.find('img:first').attr('src', item.avatar);
                    listItem.find('p.title:first').html(item.name);
                    listItem.find('p.help-block:first').html(item.info);
                    listItem.bind('click', onListItemClick);
                    list.append(listItem);
                });

            } else {
                hideDropDown();
            }
        }

        function showDropDown() {
            var curAbsPos = getSelectionCoords();
            dropDownShowing = true;
            popoverEle.css({
                display: 'block',
                top:     curAbsPos.y - (elmInputBoxContentAbsPosition.top - $(document).scrollTop()) + (editableContentLineHeightPx + 10),
                left:    curAbsPos.x - elmInputBoxContentAbsPosition.left
            });
        }

        function hideDropDown() {
            dropDownShowing = false;
            popoverEle.css({display: 'none'});
        }

        function handleUserChooseOption(e) {
            if (!dropDownShowing) {
                return true;
            }

            if (e.keyCode === KEY.UP || e.keyCode === KEY.DOWN) {
                choosingMentionOptions(e);
                return false;
            }

            // Try to exit mention state: Stop mention if @, Home, Enter, Tabs
            if ((e.keyCode === KEY.HOME)
                || (e.keyCode === KEY.RETURN)
                || (e.keyCode === KEY.TAB)
            ) {
                choseMentionOptions();
                return false;
            }

            return true;
        }

        function updateMentionKeyword(e) {
            if (document.selection) {
                // var node = document.selection.createRange().parentElement(); // IE
                var node = document.selection.createRange(); // IE
                // TODO: Test on IE
            } else {
                // var node = window.getSelection().anchorNode.parentNode; // everyone else
                var node = window.getSelection().anchorNode; // everyone else
            }

            var textNodeData = node.data;
            if (typeof textNodeData === 'undefined') {
                textNodeData = '';
            }
            var cursorPosition = getSelectionEndPositionInCurrentLine(); // passing the js DOM ele

            currentMention.lastActiveNode = node;
            currentMention.keyword = '';

            var i = cursorPosition - 1; // NOTE: cursorPosition is Non-zero base
            var next = true;
            while (next) {
                var charAt = textNodeData.charAt(i);
                if (charAt === '' || charAt === settings.triggerChar) {
                    next = false;
                }
                i--;
            }

            currentMention.keyword = textNodeData.substring(i + 1, cursorPosition);
            if (currentMention.keyword.indexOf(settings.triggerChar) === -1) {
                currentMention.keyword = '';
                currentMention.charAtFound = false;

                // NOTE: Still need mention but turn off dropdown now
                hideDropDown();
            } else {
                currentMention.keyword = currentMention.keyword.substring(1, cursorPosition);
                currentMention.charAtFound = true;
            }

            log(currentMention.keyword, 'currentMention.keyword');
        }

        function getMentionKeyword() {
            return currentMention.keyword;
        }

        function setSelectedMention(item) {
            currentMention.jqueryDomNode = item;
            updateSelectedMentionUI(item);

            log(item, 'setSelectedMention item: ');
        }

        function updateSelectedMentionUI(selectedMentionItem) {
            $.each(list.children(), function (i, listItem) {
                $(listItem).removeClass('active');
            });
            selectedMentionItem.addClass('active');
        }

        function choosingMentionOptions(e) {
            log('choosingMentionOptions');

            // Get Selected mention Item
            if (currentMention.jqueryDomNode === null) {
                setSelectedMention(list.children().first());
            }

            var item = [];

            if (e.keyCode === KEY.DOWN) {
                item = currentMention.jqueryDomNode.next();
            } else if (e.keyCode === KEY.UP) {
                item = currentMention.jqueryDomNode.prev();
            }

            if (item.length === 0) {
                item = currentMention.jqueryDomNode;
            }

            setSelectedMention(item);
        }

        function choseMentionOptions(chooseByMouse) {
            if (chooseByMouse === 'undefined') {
                chooseByMouse = false;
            }
            log('choosedMentionOptions by ' + (chooseByMouse ? 'Mouse' : 'Keyboard'));

            var currentMentionItemData = {};

            var selectedId = currentMention.jqueryDomNode.attr('data-item-id');
            for (var i = 0, len = currentMention.mentionItemDataSet.length; i < len; i++) {
                if (selectedId == currentMention.mentionItemDataSet[i].id) {
                    currentMentionItemData = currentMention.mentionItemDataSet[i];
                    break;
                }
            }

            var highlightNode = $(settings.templates.highlight);
            var highlightContentNode = $(settings.templates.highlightContent
                .replace('[HREF]', currentMentionItemData.href)
                .replace('[TEXT]', currentMentionItemData.name)
                .replace('[ITEM_ID]', currentMentionItemData.id)
            );
            highlightNode.append(highlightContentNode);
            replaceTextInRange('@' + currentMention.keyword, highlightNode.prop('outerHTML'), chooseByMouse);


            // Finish mention
            log('Finish mention', '', 'warn');

            needMention = false; // Reset mention state
            currentMention.keyword = ''; // reset current Data if start with @

            hideDropDown();
            updateDataInputData();
        }

        function log(msg, prefix, level) {
            if (typeof level === 'undefined') {
                level = 'log';
            }
            if (settings.debug === 1) {
                eval("console." + level + "(inputId, prefix ? prefix + ':' : '', msg);");
            }
        }

        function replaceTextInRange(fromString, toTextHtml, choosedByMouse) {
            var positionInfo = {
                startBefore: 0,
                startAfter:  0,
                stopBefore:  0,
                stopAfter:   0,
            };

            var sel = window.getSelection();
            var range;
            if (choosedByMouse !== 'undefined' && choosedByMouse === true) {
                var lastActiveNode = currentMention.lastActiveNode;
                try {
                    var offset = lastActiveNode.data.length;
                } catch (e) {
                    log(e, 'lastActiveNode Error:');
                    var offset = 0;
                }

                range = document.createRange();
                range.setStart(lastActiveNode, offset);
                range.collapse(true);
                sel.removeAllRanges();
                sel.addRange(range);
            }

            var isIE = false;

            var stopPos = sel.focusOffset;
            var startPos = stopPos - fromString.length;

            if (window.getSelection) {
                isIE = false;
                sel = window.getSelection();
                if (sel.rangeCount > 0) {
                    range = sel.getRangeAt(0).cloneRange();
                    range.collapse(true);
                }
            } else if ((sel = document.selection) && sel.type != "Control") {
                range = sel.createRange();
                isIE = true;
            }

            if (startPos !== stopPos) {
                // replace / Remove content
                range.setStart(sel.anchorNode, startPos);
                range.setEnd(sel.anchorNode, stopPos);
                range.deleteContents();
            }

            // insert
            var node = document.createElement('span');
            node.setAttribute('class', 'mention-area');
            node.innerHTML = toTextHtml;
            range.insertNode(node);
            range.setEnd(sel.focusNode, range.endContainer.length);

            positionInfo.startBefore = startPos;
            positionInfo.stopBefore = stopPos;
            positionInfo.startAfter = startPos;
            positionInfo.stopAfter = startPos + node.innerText.length;


            // move cursor to end of keyword after replace
            var stop = false;
            node = $(sel.anchorNode);
            while (!stop) {
                if (node.next().text().length === 0) {
                    stop = true;
                }
                else {
                    node = node.next();
                }
            }

            // insert <newElem> after list
            var newElem = $(settings.templates.normalText).insertAfter(node);

            // move caret to after <newElem>
            range = document.createRange();
            range.setStartAfter(newElem.get(0));
            range.setEndAfter(newElem.get(0));
            sel.removeAllRanges();
            sel.addRange(range);

            return positionInfo;
        }


        // Public methods
        return {
            init: function (domTarget) {
                initTextArea(domTarget);
            },
        };
    };

    function getSelectionCoords(win) {
        win = win || window;
        var doc = win.document;
        var sel = doc.selection, range, rects, rect;
        var x = 0, y = 0;
        if (sel) {
            if (sel.type != "Control") {
                range = sel.createRange();
                range.collapse(true);
                x = range.boundingLeft;
                y = range.boundingTop;
            }
        } else if (win.getSelection) {
            sel = win.getSelection();
            if (sel.rangeCount) {
                range = sel.getRangeAt(0).cloneRange();

                if (range.getClientRects) {
                    range.collapse(true);
                    rects = range.getClientRects();
                    if (rects.length > 0) {
                        rect = rects[0];
                    }
                    x = rect.left;
                    y = rect.top;
                }

                if (x == 0 && y == 0) {
                    var span = doc.createElement("span");
                    if (span.getClientRects) {
                        span.appendChild(doc.createTextNode("\u200b"));
                        range.insertNode(span);
                        rect = span.getClientRects()[0];
                        x = rect.left;
                        y = rect.top;
                        var spanParent = span.parentNode;
                        spanParent.removeChild(span);
                        spanParent.normalize();
                    }
                }
            }
        }
        return {x: x, y: y};
    }
    function getSelectionEndPositionInCurrentLine() {
        var selectionEndPos = 0;
        if (window.getSelection) {
            var sel = window.getSelection();
            selectionEndPos = sel.focusOffset;
        }

        return selectionEndPos;
    }

}(jQuery));
  <textarea id="t3" name="mention" class="mention" rows="6">Try to @John</textarea>

$('textarea').mentiony({ //#user_list
                onDataRequestUser: function (mode, keyword, onDataRequestCompleteCallbackUser) {
                    $.ajax({
                    url: "xyz.php",
                    method: "post",
                    data: {keyword: keyword},
                    dataType: "json",
                    success: function (response) {
                       var data = response;
                       console.log(data);
                        data = jQuery.grep(data, function( item ) {
                            return item.name.toLowerCase().indexOf(keyword.toLowerCase()) > -1;
                        });
                        onDataRequestCompleteCallbackUser.call(this, data);
                        }

                    });
                },
                timeOut: 500, // Timeout to show mention after press @//#user_list   
            });

0 个答案:

没有答案