Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/423.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/html/80.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Twitter如何实现它的Tweet框?_Javascript_Html_Twitter_Range_Selection - Fatal编程技术网

Javascript Twitter如何实现它的Tweet框?

Javascript Twitter如何实现它的Tweet框?,javascript,html,twitter,range,selection,Javascript,Html,Twitter,Range,Selection,我正在尝试实现类似Twitter的tweet box的功能,具体来说: 当总长度超过140个字符时,自动高亮显示红色背景中的文本 自动以蓝色突出显示链接、提及和标签 这些应该在用户键入时自动发生 根据我在Twitter上看到的语义标记,看起来他们使用的是contentEditablediv。每当检测到提及/标签/链接,或者长度超过140个字符时,就会修改其中的DOM: <div aria-labelledby="tweet-box-mini-home-profile-label" id

我正在尝试实现类似Twitter的tweet box的功能,具体来说:

  • 当总长度超过140个字符时,自动高亮显示红色背景中的文本
  • 自动以蓝色突出显示链接、提及和标签
这些应该在用户键入时自动发生

根据我在Twitter上看到的语义标记,看起来他们使用的是
contentEditable
div。每当检测到提及/标签/链接,或者长度超过140个字符时,就会修改其中的DOM:

<div aria-labelledby="tweet-box-mini-home-profile-label" id="tweet-box-mini-home-profile" class="tweet-box rich-editor  notie" contenteditable="true" spellcheck="true" role="textbox" aria-multiline="true" dir="ltr" aria-autocomplete="list" aria-expanded="false" aria-owns="typeahead-dropdown-6">
    <div>hello world <a class="twitter-atreply pretty-link" href="/testMention" role="presentation"><s>@</s>testMention</a> text <a href="/search?q=%23helloWorld" class="twitter-hashtag pretty-link" role="presentation"><s>#</s>helloWorld</a> text <a href="http://www.google.com" class="twitter-timeline-link" role="presentation">http://www.google.com</a> text text text text text text text text text text text text text text <em>overflow red text here</em>
    </div>
</div>

hello world文本文本文本文本文本溢出此处为红色文本
我到目前为止所做的事情

目前,我使用的是contentEditable字段。它触发一个解析文本的函数
onChange/onInput
。通过regexr检查它是否有username/link/hashtag,并用相应的标记替换它们(我现在只使用一个简单的
标记来封装username和hashtag)。 我遇到的问题是,因为我正在更改/修改contentEditable的DOM,插入符号光标位置将丢失。 我查看了这个线程,它只有在DOM没有改变的情况下才能正常工作(在我的例子中是这样的,用
标记包围标记/hashtags,用
链接)

有人有其他解决方案/方法来解决这个问题吗?也许我不应该使用
contentEditable
?或者不直接修改
contentEditable
字段的DOM以保持插入符号位置的方法?如果有任何帮助,我们将不胜感激!我尝试过使用
window.getSelection()
并在DOM更改前保存它,但它似乎总是在应用DOM更改后重置。

事实证明,这真的不是一件容易的事情。过去几天我一直在努力解决这个问题,我还没有找到解决方案

您目前最好的插入式解决方案是,它仍在维护中,但肯定不是完美的。其中一个示例演示了如何进行文本突出显示

这个问题中最让人恼火的部分是Twitter有一个漂亮的解决方案,它在我们面前似乎非常有效。我花了一些时间调查了他们实现“推文箱”的方式这绝对不是一件小事。看起来他们几乎在手动完成所有事情,包括模拟撤销/重做功能、拦截复制/粘贴、为IE/W3C提供自定义代码、自定义Mac/PC编码等等。他们使用contenteditable div,这本身就有问题,因为浏览器实现不同。这是事实上,相当令人印象深刻

这是最相关的(不幸的是,混淆了)代码,取自Twitter的启动JavaScript文件(通过检查登录的Twitter主页的标题找到)。我不想直接复制和粘贴链接,以防它被个性化到我的Twitter帐户

define("app/utils/html_text", ["module", "require", "exports"], function(module, require, exports) {
    function isTextNode(a) {
        return a.nodeType == 3 || a.nodeType == 4
    }

    function isElementNode(a) {
        return a.nodeType == 1
    }

    function isBrNode(a) {
        return isElementNode(a) && a.nodeName.toLowerCase() == "br"
    }

    function isOutsideContainer(a, b) {
        while (a !== b) {
            if (!a) return !0;
            a = a.parentNode
        }
    }
    var useW3CRange = window.getSelection,
        useMsftTextRange = !useW3CRange && document.selection,
        useIeHtmlFix = navigator.appName == "Microsoft Internet Explorer",
        NBSP_REGEX = /[\xa0\n\t]/g,
        CRLF_REGEX = /\r\n/g,
        LINES_REGEX = /(.*?)\n/g,
        SP_LEADING_OR_FOLLOWING_CLOSE_TAG_OR_PRECEDING_A_SP_REGEX = /^ |(<\/[^>]+>) | (?= )/g,
        SP_LEADING_OR_TRAILING_OR_FOLLOWING_A_SP_REGEX = /^ | $|( ) /g,
        MAX_OFFSET = Number.MAX_VALUE,
        htmlText = function(a, b) {
            function c(a, c) {
                function h(a) {
                    var i = d.length;
                    if (isTextNode(a)) {
                        var j = a.nodeValue.replace(NBSP_REGEX, " "),
                            k = j.length;
                        k && (d += j, e = !0), c(a, !0, 0, i, i + k)
                    } else if (isElementNode(a)) {
                        c(a, !1, 0, i, i);
                        if (isBrNode(a)) a == f ? g = !0 : (d += "\n", e = !1);
                        else {
                            var l = a.currentStyle || window.getComputedStyle(a, ""),
                                m = l.display == "block";
                            m && b.msie && (e = !0);
                            for (var n = a.firstChild, o = 1; n; n = n.nextSibling, o++) {
                                h(n);
                                if (g) return;
                                i = d.length, c(a, !1, o, i, i)
                            }
                            g || a == f ? g = !0 : m && e && (d += "\n", e = !1)
                        }
                    }
                }
                var d = "",
                    e, f, g;
                for (var i = a; i && isElementNode(i); i = i.lastChild) f = i;
                return h(a), d
            }

            function d(a, b) {
                var d = null,
                    e = b.length - 1;
                if (useW3CRange) {
                    var f = b.map(function() {
                            return {}
                        }),
                        g;
                    c(a, function(a, c, d, h, i) {
                        g || f.forEach(function(f, j) {
                            var k = b[j];
                            h <= k && !isBrNode(a) && (f.node = a, f.offset = c ? Math.min(k, i) - h : d, g = c && j == e && i >= k)
                        })
                    }), f[0].node && f[e].node && (d = document.createRange(), d.setStart(f[0].node, f[0].offset), d.setEnd(f[e].node, f[e].offset))
                } else if (useMsftTextRange) {
                    var h = document.body.createTextRange();
                    h.moveToElementText(a), d = h.duplicate();
                    if (b[0] == MAX_OFFSET) d.setEndPoint("StartToEnd", h);
                    else {
                        d.move("character", b[0]);
                        var i = e && b[1] - b[0];
                        i > 0 && d.moveEnd("character", i), h.inRange(d) || d.setEndPoint("EndToEnd", h)
                    }
                }
                return d
            }

            function e() {
                return document.body.contains(a)
            }

            function f(b) {
                a.innerHTML = b;
                if (useIeHtmlFix)
                    for (var c = a.firstChild; c; c = c.nextSibling) isElementNode(c) && c.nodeName.toLowerCase() == "p" && c.innerHTML == "" && (c.innerText = "")
            }

            function g(a, b) {
                return a.map(function(a) {
                    return Math.min(a, b.length)
                })
            }

            function h() {
                var b = getSelection();
                if (b.rangeCount !== 1) return null;
                var d = b.getRangeAt(0);
                if (isOutsideContainer(d.commonAncestorContainer, a)) return null;
                var e = [{
                    node: d.startContainer,
                    offset: d.startOffset
                }];
                d.collapsed || e.push({
                    node: d.endContainer,
                    offset: d.endOffset
                });
                var f = e.map(function() {
                        return MAX_OFFSET
                    }),
                    h = c(a, function(a, b, c, d) {
                        e.forEach(function(e, g) {
                            f[g] == MAX_OFFSET && a == e.node && (b || c == e.offset) && (f[g] = d + (b ? e.offset : 0))
                        })
                    });
                return g(f, h)
            }

            function i() {
                var b = document.selection.createRange();
                if (isOutsideContainer(b.parentElement(), a)) return null;
                var d = ["Start"];
                b.compareEndPoints("StartToEnd", b) && d.push("End");
                var e = d.map(function() {
                        return MAX_OFFSET
                    }),
                    f = document.body.createTextRange(),
                    h = c(a, function(c, g, h, i) {
                        function j(a, c) {
                            if (e[c] < MAX_OFFSET) return;
                            var d = f.compareEndPoints("StartTo" + a, b);
                            if (d > 0) return;
                            var g = f.compareEndPoints("EndTo" + a, b);
                            if (g < 0) return;
                            var h = f.duplicate();
                            h.setEndPoint("EndTo" + a, b), e[c] = i + h.text.length, c && !g && e[c]++
                        }!g && !h && c != a && (f.moveToElementText(c), d.forEach(j))
                    });
                return g(e, h)
            }
            return {
                getHtml: function() {
                    if (useIeHtmlFix) {
                        var b = "",
                            c = document.createElement("div");
                        for (var d = a.firstChild; d; d = d.nextSibling) isTextNode(d) ? (c.innerText = d.nodeValue, b += c.innerHTML) : b += d.outerHTML.replace(CRLF_REGEX, "");
                        return b
                    }
                    return a.innerHTML
                },
                setHtml: function(a) {
                    f(a)
                },
                getText: function() {
                    return c(a, function() {})
                },
                setTextWithMarkup: function(a) {
                    f((a + "\n").replace(LINES_REGEX, function(a, c) {
                        return b.mozilla || b.msie ? (c = c.replace(SP_LEADING_OR_FOLLOWING_CLOSE_TAG_OR_PRECEDING_A_SP_REGEX, "$1&nbsp;"), b.mozilla ? c + "<BR>" : "<P>" + c + "</P>") : (c = (c || "<br>").replace(SP_LEADING_OR_TRAILING_OR_FOLLOWING_A_SP_REGEX, "$1&nbsp;"), b.opera ? "<p>" + c + "</p>" : "<div>" + c + "</div>")
                    }))
                },
                getSelectionOffsets: function() {
                    var a = null;
                    return e() && (useW3CRange ? a = h() : useMsftTextRange && (a = i())), a
                },
                setSelectionOffsets: function(b) {
                    if (b && e()) {
                        var c = d(a, b);
                        if (c)
                            if (useW3CRange) {
                                var f = window.getSelection();
                                f.removeAllRanges(), f.addRange(c)
                            } else useMsftTextRange && c.select()
                    }
                },
                emphasizeText: function(b) {
                    var f = [];
                    b && b.length > 1 && e() && (c(a, function(a, c, d, e, g) {
                        if (c) {
                            var h = Math.max(e, b[0]),
                                i = Math.min(g, b[1]);
                            i > h && f.push([h, i])
                        }
                    }), f.forEach(function(b) {
                        var c = d(a, b);
                        c && (useW3CRange ? c.surroundContents(document.createElement("em")) : useMsftTextRange && c.execCommand("italic", !1, null))
                    }))
                }
            }
        };
    module.exports = htmlText
});








define("app/utils/tweet_helper", ["module", "require", "exports", "lib/twitter-text", "core/utils", "app/data/user_info"], function(module, require, exports) {
    var twitterText = require("lib/twitter-text"),
        utils = require("core/utils"),
        userInfo = require("app/data/user_info"),
        VALID_PROTOCOL_PREFIX_REGEX = /^https?:\/\//i,
        tweetHelper = {
            extractMentionsForReply: function(a, b) {
                var c = a.attr("data-screen-name"),
                    d = a.attr("data-retweeter"),
                    e = a.attr("data-mentions") ? a.attr("data-mentions").split(" ") : [],
                    f = a.attr("data-tagged") ? a.attr("data-tagged").split(" ") : [];
                e = e.concat(f);
                var g = [c, b, d];
                return e = e.filter(function(a) {
                    return g.indexOf(a) < 0
                }), d && d != c && d != b && e.unshift(d), (!e.length || c != b) && e.unshift(c), e
            },
            linkify: function(a, b) {
                return b = utils.merge({
                    hashtagClass: "twitter-hashtag pretty-link",
                    hashtagUrlBase: "/search?q=%23",
                    symbolTag: "s",
                    textWithSymbolTag: "b",
                    cashtagClass: "twitter-cashtag pretty-link",
                    cashtagUrlBase: "/search?q=%24",
                    usernameClass: "twitter-atreply pretty-link",
                    usernameUrlBase: "/",
                    usernameIncludeSymbol: !0,
                    listClass: "twitter-listname pretty-link",
                    urlClass: "twitter-timeline-link",
                    urlTarget: "_blank",
                    suppressNoFollow: !0,
                    htmlEscapeNonEntities: !0
                }, b || {}), twitterText.autoLinkEntities(a, twitterText.extractEntitiesWithIndices(a), b)
            }
        };
    module.exports = tweetHelper
});










define("app/ui/compose/with_rich_editor", ["module", "require", "exports", "app/utils/file", "app/utils/html_text", "app/utils/tweet_helper", "lib/twitter-text"], function(module, require, exports) {
    function withRichEditor() {
        this.defaultAttrs({
            richSelector: "div.rich-editor",
            linksSelector: "a",
            normalizerSelector: "div.rich-normalizer",
            $browser: $.browser
        }), this.linkify = function(a) {
            var b = {
                urlTarget: null,
                textWithSymbolTag: RENDER_URLS_AS_PRETTY_LINKS ? "b" : "",
                linkAttributeBlock: function(a, b) {
                    var c = a.screenName || a.url;
                    c && (this.urlAndMentionsCharCount += c.length + 2), delete b.title, delete b["data-screen-name"], b.dir = a.hashtag && this.shouldBeRTL(a.hashtag, 0) ? "rtl" : "ltr", b.role = "presentation"
                }.bind(this)
            };
            return this.urlAndMentionsCharCount = 0, tweetHelper.linkify(a, b)
        }, this.around("setSelection", function(a, b) {
            b && this.setSelectionIfFocused(b)
        }), this.around("setCursorPosition", function(a, b) {
            b === undefined && (b = this.attr.cursorPosition), b === undefined && (b = MAX_OFFSET), this.setSelectionIfFocused([b])
        }), this.around("detectUpdatedText", function(a, b, c) {
            var d = this.htmlRich.getHtml(),
                e = this.htmlRich.getSelectionOffsets() || [MAX_OFFSET],
                f = c !== undefined;
            if (d === this.prevHtml && e[0] === this.prevSelectionOffset && !b && !f) return;
            var g = f ? c : this.htmlRich.getText(),
                h = g.replace(INVALID_CHARS_REGEX, "");
            (f || !(!d && !this.hasFocus() || this.$text.attr("data-in-composition"))) && this.reformatHtml(h, d, e, f);
            if (b || this.cleanedText != h || this.prevSelectionOffset != e[0]) this.prevSelectionOffset = e[0], this.updateCleanedTextAndOffset(h, e[0])
        }), this.reformatHtml = function(a, b, c, d) {
            this.htmlNormalizer.setTextWithMarkup(this.linkify(a)), this.interceptDataImageInContent();
            var e = this.shouldBeRTL(a, this.urlAndMentionsCharCount);
            this.$text.attr("dir", e ? "rtl" : "ltr"), this.$normalizer.find(e ? "[dir=rtl]" : "[dir=ltr]").removeAttr("dir"), RENDER_URLS_AS_PRETTY_LINKS && this.$normalizer.find(".twitter-timeline-link").wrapInner("<b>").addClass("pretty-link");
            var f = this.getMaxLengthOffset(a);
            f >= 0 && (this.htmlNormalizer.emphasizeText([f, MAX_OFFSET]), this.$normalizer.find("em").each(function() {
                this.innerHTML = this.innerHTML.replace(TRAILING_SINGLE_SPACE_REGEX, "Â ")
            }));
            var g = this.htmlNormalizer.getHtml();
            if (g !== b) {
                var h = d && !this.isFocusing && this.hasFocus();
                h && this.$text.addClass("fake-focus").blur(), this.htmlRich.setHtml(g), h && this.$text.focus().removeClass("fake-focus"), this.setSelectionIfFocused(c)
            }
            this.prevHtml = g
        }, this.interceptDataImageInContent = function() {
            if (!this.triggerGotImageData) return;
            this.$text.find("img").filter(function(a, b) {
                return b.src.match(/^data:/)
            }).first().each(function(a, b) {
                var c = file.getBlobFromDataUri(b.src);
                file.getFileData("pasted.png", c).then(this.triggerGotImageData.bind(this))
            }.bind(this))
        }, this.getMaxLengthOffset = function(a) {
            var b = this.getLength(a),
                c = this.attr.maxLength;
            if (b <= c) return -1;
            c += twitterText.getUnicodeTextLength(a) - b;
            var d = [{
                indices: [c, c]
            }];
            return twitterText.modifyIndicesFromUnicodeToUTF16(a, d), d[0].indices[0]
        }, this.setSelectionIfFocused = function(a) {
            this.hasFocus() ? (this.previousSelection = null, this.htmlRich.setSelectionOffsets(a)) : this.previousSelection = a
        }, this.selectPrevCharOnBackspace = function(a) {
            if (a.which == 8 && !a.ctrlKey) {
                var b = this.htmlRich.getSelectionOffsets();
                b && b[0] != MAX_OFFSET && b.length == 1 && (b[0] ? this.setSelectionIfFocused([b[0] - 1, b[0]]) : this.stopEvent(a))
            }
        }, this.emulateCommandArrow = function(a) {
            if (a.metaKey && !a.shiftKey && (a.which == 37 || a.which == 39)) {
                var b = a.which == 37;
                this.htmlRich.setSelectionOffsets([b ? 0 : MAX_OFFSET]), this.$text.scrollTop(b ? 0 : this.$text[0].scrollHeight), this.stopEvent(a)
            }
        }, this.stopEvent = function(a) {
            a.preventDefault(), a.stopPropagation()
        }, this.saveUndoStateDeferred = function(a) {
            if (a.type == "mousemove" && a.which != 1) return;
            setTimeout(function() {
                this.detectUpdatedText(), this.saveUndoState()
            }.bind(this), 0)
        }, this.clearUndoState = function() {
            this.undoHistory = [], this.undoIndex = -1
        }, this.saveUndoState = function() {
            var a = this.htmlRich.getText(),
                b = this.htmlRich.getSelectionOffsets() || [a.length],
                c = this.undoHistory,
                d = c[this.undoIndex];
            !d || d[0] !== a ? c.splice(++this.undoIndex, c.length, [a, b]) : d && (d[1] = b)
        }, this.isUndoKey = function(a) {
            return this.isMac ? a.which == 90 && a.metaKey && !a.shiftKey && !a.ctrlKey && !a.altKey : a.which == 90 && a.ctrlKey && !a.shiftKey && !a.altKey
        }, this.emulateUndo = function(a) {
            this.isUndoKey(a) && (this.stopEvent(a), this.saveUndoState(), this.undoIndex > 0 && this.setUndoState(this.undoHistory[--this.undoIndex]))
        }, this.isRedoKey = function(a) {
            return this.isMac ? a.which == 90 && a.metaKey && a.shiftKey && !a.ctrlKey && !a.altKey : this.isWin ? a.which == 89 && a.ctrlKey && !a.shiftKey && !a.altKey : a.which == 90 && a.shiftKey && a.ctrlKey && !a.altKey
        }, this.emulateRedo = function(a) {
            var b = this.undoHistory,
                c = this.undoIndex;
            c < b.length - 1 && this.htmlRich.getText() !== b[c][0] && b.splice(c + 1, b.length), this.isRedoKey(a) && (this.stopEvent(a), c < b.length - 1 && this.setUndoState(b[++this.undoIndex]))
        }, this.setUndoState = function(a) {
            this.detectUpdatedText(!1, a[0]), this.htmlRich.setSelectionOffsets(a[1]), this.trigger("uiHideAutocomplete")
        }, this.undoStateAfterCursorMovement = function(a) {
            a.which >= 33 && a.which <= 40 && this.saveUndoStateDeferred(a)
        }, this.handleKeyDown = function(a) {
            this.isIE && this.selectPrevCharOnBackspace(a), this.attr.$browser.mozilla && this.emulateCommandArrow(a), this.undoStateAfterCursorMovement(a), this.emulateUndo(a), this.emulateRedo(a)
        }, this.interceptPaste = function(a) {
            if (a.originalEvent && a.originalEvent.clipboardData) {
                var b = a.originalEvent.clipboardData;
                (this.interceptImagePaste(b) || this.interceptTextPaste(b)) && a.preventDefault()
            }
        }, this.interceptImagePaste = function(a) {
            return this.triggerGotImageData && a.items && a.items.length === 1 && a.items[0].kind === "file" && a.items[0].type.indexOf("image/") === 0 ? (file.getFileData("pasted.png", a.items[0].getAsFile()).then(this.triggerGotImageData.bind(this)), !0) : !1
        }, this.interceptTextPaste = function(a) {
            var b = a.getData("text");
            return b && document.execCommand("insertHTML", !1, $("<div>").text(b).html().replace(LINE_FEEDS_REGEX, "<br>")) ? !0 : !1
        }, this.clearSelectionOnBlur = function() {
            window.getSelection && (this.previousSelection = this.htmlRich.getSelectionOffsets(), this.previousSelection && getSelection().removeAllRanges())
        }, this.restoreSelectionOnFocus = function() {
            this.previousSelection ? setTimeout(function() {
                this.htmlRich.setSelectionOffsets(this.previousSelection), this.previousSelection = null
            }.bind(this), 0) : this.previousSelection = null
        }, this.setFocusingState = function() {
            this.isFocusing = !0, setTimeout(function() {
                this.isFocusing = !1
            }.bind(this), 0)
        }, this.around("initTextNode", function(a) {
            this.isIE = this.attr.$browser.msie || navigator.userAgent.indexOf("Trident") > -1, this.$text = this.select("richSelector"), this.undoHistory = [
                ["", [0]]
            ], this.undoIndex = 0, this.htmlRich = htmlText(this.$text[0], this.attr.$browser), this.$text.toggleClass("notie", !this.isIE), this.$normalizer = this.select("normalizerSelector"), this.htmlNormalizer = htmlText(this.$normalizer[0], this.attr.$browser);
            var b = navigator.platform;
            this.isMac = b.indexOf("Mac") != -1, this.isWin = b.indexOf("Win") != -1, this.on(this.$text, "click", {
                linksSelector: this.stopEvent
            }), this.on(this.$text, "focusin", this.setFocusingState), this.on(this.$text, "keydown", this.handleKeyDown), this.on(this.$text, "focusout", this.ignoreDuringFakeFocus(this.clearSelectionOnBlur)), this.on(this.$text, "focusin", this.ignoreDuringFakeFocus(this.restoreSelectionOnFocus)), this.on(this.$text, "focusin", this.ignoreDuringFakeFocus(this.saveUndoStateDeferred)), this.on(this.$text, "cut paste drop", this.saveUndoState), this.on(this.$text, "cut paste drop mousedown mousemove", this.saveUndoStateDeferred), this.on("uiSaveUndoState", this.saveUndoState), this.on("uiClearUndoState", this.clearUndoState), this.on(this.$text, "paste", this.interceptPaste), this.detectUpdatedText()
        })
    }
    var file = require("app/utils/file"),
        htmlText = require("app/utils/html_text"),
        tweetHelper = require("app/utils/tweet_helper"),
        twitterText = require("lib/twitter-text");
    module.exports = withRichEditor;
    var INVALID_CHARS_REGEX = /[\uFFFE\uFEFF\uFFFF\u200E\u200F\u202A-\u202E\x00-\x09\x0B\x0C\x0E-\x1F]/g,
        RENDER_URLS_AS_PRETTY_LINKS = $.browser.mozilla && parseInt($.browser.version, 10) < 2,
        TRAILING_SINGLE_SPACE_REGEX = / $/,
        LINE_FEEDS_REGEX = /\r\n|\n\r|\n/g,
        MAX_OFFSET = Number.MAX_VALUE
});













define("app/ui/compose/tweet_box_manager", ["module", "require", "exports", "app/ui/compose/tweet_box", "app/ui/compose/dm_composer", "app/ui/geo_picker", "core/component", "app/ui/compose/with_rich_editor"], function(module, require, exports) {
    function tweetBoxManager() {
        this.createTweetBoxAtTarget = function(a, b) {
            this.createTweetBox(a.target, b)
        }, this.createTweetBox = function(a, b) {
            var c = $(a);
            if (!((b.eventData || {}).scribeContext || {}).component) throw new Error("Please specify scribing component for tweet box.");
            c.find(".geo-picker").length > 0 && GeoPicker.attachTo(c.find(".geo-picker"), b, {
                parent: c
            });
            var d = c.find("div.rich-editor").length > 0 ? [withRichEditor] : [],
                e = (b.dmOnly ? DmComposer : TweetBox).mixin.apply(this, d),
                f = {
                    typeaheadData: this.attr.typeaheadData
                };
            e.attachTo(c, f, b)
        }, this.after("initialize", function() {
            this.on("uiInitTweetbox", this.createTweetBoxAtTarget)
        })
    }
    var TweetBox = require("app/ui/compose/tweet_box"),
        DmComposer = require("app/ui/compose/dm_composer"),
        GeoPicker = require("app/ui/geo_picker"),
        defineComponent = require("core/component"),
        withRichEditor = require("app/ui/compose/with_rich_editor"),
        TweetBoxManager = defineComponent(tweetBoxManager);
    module.exports = TweetBoxManager
});
定义(“app/utils/html_text”、[“模块”、“要求”、“导出”]、函数(模块、要求、导出){
函数isTextNode(a){
返回a.nodeType==3 | | a.nodeType==4
}
函数isElementNode(a){
返回a.nodeType==1
}
函数isBrNode(a){
返回isElementNode(a)和&a.nodeName.toLowerCase()=“br”
}
函数isOutsideContainer(a,b){
而(a!==b){
如果(!a)返回!0;
a=a.parentNode
}
}
var useW3CRange=window.getSelection,
useMsftTextRange=!useW3CRange&&document.selection,
usiehtmlfix=navigator.appName==“Microsoft Internet Explorer”,
NBSP_REGEX=/[\xa0\n\t]/g,
CRLF_REGEX=/\r\n/g,
行\正则表达式=/(.*)\n/g,
SP_引导_或_跟随_关闭_标记_或_前面_A_SP_正则表达式=/^ |(]+>)|(?=)/g,
SP_LEADING_或_training_或_FOLLOWING_A_SP_REGEX=/^ |$|()/g,
最大偏移量=Number.MAX\u值,
htmlText=函数(a,b){
功能c(a,c){
职能h(a){
var i=d.长度;
if(isTextNode(a)){
var j=a.nodeValue.replace(NBSP_REGEX,“”),
k=j.长度;
k&(d+=j,e=!0),c(a,!0,0,i,i+k)
}else if(isElementNode(a)){
c(a,!1,0,i,i);
如果(isBrNode(a))a==f?g=!0:(d+=“\n”,e=!1);
否则{
var l=a.currentStyle | | window.getComputedStyle(a,“”),
m=l.显示==“块”;
m&&b.msie&(e=!0);
for(var n=a.firstChild,o=1;n;n=n.nextSibling,o++){
h(n);
如果(g)返回;
i=d.长度,c(a,!1,o,i,i)
}
g | | a==f?g=!0:m&&e&&(d+=“\n”,e=!1)
}
}
}
var d=“”,
e、 f,g;
对于(var i=a;i&&isElementNode(i);i=i.lastChild)f=i;
返回h(a),d
}
职能d(a、b){
var d=null,
e=b.长度-1;
如果(使用W3Crange){
var f=b.map(函数(){
返回{}
}),
G
c(a,函数(a,c,d,h,i){
g | | f.forEach(函数(f,j){
var k=b[j];
h=k)
})
}),f[0]。节点&&f[e]。节点&&d=document.createRa
<div class="tweet-composer">
  <textarea class="editor-textarea js-keeper-editor">This is some text that will be highlight when longer than 20 characters. Like twitter. Type something...</textarea>
  <div class="js-keeper-placeholder-back"></div>
</div>
.tweet-composer {
  position: relative;
  z-index: 1;
}

.js-keeper-editor,
.js-keeper-placeholder-back {
  background: transparent;
  border: 1px solid #eee;
  font-family: Helvetica, Arial, sans-serif;
  font-size: 14px; /* Same font for both. */
  margin: auto;
  min-height: 200px;
  outline: none;
  padding: 10px;
  width: 100%;
}

.js-keeper-placeholder-back {
  bottom: 0;
  color: transparent;
  left: 0;
  position: absolute;
  top: 0;
  white-space: pre-wrap;
  width: 100%;
  word-wrap: break-word;
  z-index: -1;
}

.js-keeper-placeholder-back em {
  background: #fcc !important;
}
if (0 > remainingLength) {
  // Split value if greater than 
  var allowedValuePart = currentValue.slice(0, realLength),
      refusedValuePart = currentValue.slice(realLength)
  ;

  // Fill the hidden div.
  $placeholderBacker.html(allowedValuePart + '<em>' + refusedValuePart + '</em>');
} else {
  $placeholderBacker.html('');
}
// Create a pseudo-element that will be hidden behind the placeholder.
var $placeholderBacker = $('<div class="js-keeper-placeholder-back"></div>');
$placeholderBacker.insertAfter($textarea);