跳转到内容

User:Cheetahzeng/vector.js

维基百科,自由的百科全书

这是本页的一个历史版本,由Cheetahzeng留言 | 贡献2020年4月5日 (日) 04:11 (// Edit via Wikiplus)编辑。这可能和当前版本存在着巨大的差异。

注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
//port from https://github.com/mastermay/text-autospace.js/blob/master/text-autospace.js since loader won't work
;
(function(window, $) {
    init = function() {
            $('body').each(function() {
                var hanzi = '[\u2E80-\u2FFF\u31C0-\u31EF\u3300-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFE30-\uFE4F]',
                    punc = {
                        base: '[@&=_\,\.\?\!\$\%\^\*\-\+\/]',
                        open: '[\(\[\{\'"<‘“]',
                        close: '[\)\]\}\'">’”]'
                    },
                    latin = '[A-Za-z0-9\u00C0-\u00FF\u0100-\u017F\u0180-\u024F\u1E00-\u1EFF]' + '|' + punc.base,
                    patterns = ['/(' + hanzi + ')(' + latin + '|' + punc.open + ')/ig', '/(' + latin + '|' + punc.close + ')(' + hanzi + ')/ig'];

                patterns.forEach(function(exp) {
                        findAndReplaceDOMText(this, {
                            find: eval(exp),
                            replace: '$1<hanla>$2'
                        })
                    },
                    this);

                findAndReplaceDOMText(this, {
                    find: '<hanla>',
                    replace: function() {
                        return document.createElement('hanla')
                    }
                });

                this.normalize();

                $('* > hanla:first-child').parent().each(function() {
                    if (this.firstChild.nodeType == 1) {
                        $(this).before($('<hanla/>'));
                        $(this).find('hanla:first-child').remove();
                    }
                });
            })
        },

        findAndReplaceDOMText = function(a, b) {
            var b = b;

            b.filterElements = function(el) {
                var name = el.nodeName.toLowerCase(),
                    classes = (el.nodeType == 1) ? el.getAttribute('class') : '',
                    charized = (classes && classes.match(/han-js-charized/) != null) ? true : false;

                return name !== 'style' && name !== 'script' && !charized;
            }

            return window.findAndReplaceDOMText(a, b);
        }

    $(document).on('ready',
        function() {
            init();
        });
})(window, window.jQuery, undefined);

/**
 * findAndReplaceDOMText v 0.4.0
 * @author James Padolsey http://james.padolsey.com
 * @license http://unlicense.org/UNLICENSE
 *
 * Matches the text of a DOM node against a regular expression
 * and replaces each match (or node-separated portions of the match)
 * in the specified element.
 */
;
window.findAndReplaceDOMText = (function() {

    var PORTION_MODE_RETAIN = 'retain';
    var PORTION_MODE_FIRST = 'first';

    var doc = document;
    var toString = {}.toString;

    function isArray(a) {
        return toString.call(a) == '[object Array]';
    }

    function escapeRegExp(s) {
        return String(s).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
    }

    function exposed() {
        // Try deprecated arg signature first:
        return deprecated.apply(null, arguments) || findAndReplaceDOMText.apply(null, arguments);
    }

    function deprecated(regex, node, replacement, captureGroup, elFilter) {
        if ((node && !node.nodeType) && arguments.length <= 2) {
            return false;
        }
        var isReplacementFunction = typeof replacement == 'function';

        if (isReplacementFunction) {
            replacement = (function(original) {
                return function(portion, match) {
                    return original(portion.text, match.startIndex);
                };
            }(replacement));
        }

        // Awkward support for deprecated argument signature (<0.4.0)
        var instance = findAndReplaceDOMText(node, {

            find: regex,

            wrap: isReplacementFunction ? null : replacement,
            replace: isReplacementFunction ? replacement : '$' + (captureGroup || '&'),

            prepMatch: function(m, mi) {

                // Support captureGroup (a deprecated feature)
                if (!m[0]) throw 'findAndReplaceDOMText cannot handle zero-length matches';

                if (captureGroup > 0) {
                    var cg = m[captureGroup];
                    m.index += m[0].indexOf(cg);
                    m[0] = cg;
                }

                m.endIndex = m.index + m[0].length;
                m.startIndex = m.index;
                m.index = mi;

                return m;
            },
            filterElements: elFilter
        });

        exposed.revert = function() {
            return instance.revert();
        };

        return true;
    }

    /**
     * findAndReplaceDOMText
     *
     * Locates matches and replaces with replacementNode
     *
     * @param {Node} node Element or Text node to search within
     * @param {RegExp} options.find The regular expression to match
     * @param {String|Element} [options.wrap] A NodeName, or a Node to clone
     * @param {String|Function} [options.replace='$&'] What to replace each match with
     * @param {Function} [options.filterElements] A Function to be called to check whether to
     *    process an element. (returning true = process element,
     *    returning false = avoid element)
     */
    function findAndReplaceDOMText(node, options) {
        return new Finder(node, options);
    }

    exposed.Finder = Finder;

    /**
     * Finder -- encapsulates logic to find and replace.
     */
    function Finder(node, options) {

        options.portionMode = options.portionMode || PORTION_MODE_RETAIN;

        this.node = node;
        this.options = options;

        // ENable match-preparation method to be passed as option:
        this.prepMatch = options.prepMatch || this.prepMatch;

        this.reverts = [];

        this.matches = this.search();

        if (this.matches.length) {
            this.processMatches();
        }

    }

    Finder.prototype = {

        /**
         * Searches for all matches that comply with the instance's 'match' option
         */
        search: function() {

            var match;
            var matchIndex = 0;
            var regex = this.options.find;
            var text = this.getAggregateText();
            var matches = [];

            regex = typeof regex === 'string' ? RegExp(escapeRegExp(regex), 'g') : regex;

            if (regex.global) {
                while (match = regex.exec(text)) {
                    matches.push(this.prepMatch(match, matchIndex++));
                }
            } else {
                if (match = text.match(regex)) {
                    matches.push(this.prepMatch(match, 0));
                }
            }

            return matches;

        },

        /**
         * Prepares a single match with useful meta info:
         */
        prepMatch: function(match, matchIndex) {

            if (!match[0]) {
                throw new Error('findAndReplaceDOMText cannot handle zero-length matches');
            }

            match.endIndex = match.index + match[0].length;
            match.startIndex = match.index;
            match.index = matchIndex;

            return match;
        },

        /**
         * Gets aggregate text within subject node
         */
        getAggregateText: function() {

            var elementFilter = this.options.filterElements;

            return getText(this.node);

            /**
             * Gets aggregate text of a node without resorting
             * to broken innerText/textContent
             */
            function getText(node) {

                if (node.nodeType === 3) {
                    return node.data;
                }

                if (elementFilter && !elementFilter(node)) {
                    return '';
                }

                var txt = '';

                if (node = node.firstChild)
                    do {
                        txt += getText(node);
                    } while (node = node.nextSibling);

                return txt;

            }

        },

        /**
         * Steps through the target node, looking for matches, and
         * calling replaceFn when a match is found.
         */
        processMatches: function() {

            var matches = this.matches;
            var node = this.node;
            var elementFilter = this.options.filterElements;

            var startPortion, endPortion, innerPortions = [],
                curNode = node,
                match = matches.shift(),
                atIndex = 0,
                // i.e. nodeAtIndex
                matchIndex = 0,
                portionIndex = 0,
                doAvoidNode;

            out: while (true) {

                if (curNode.nodeType === 3) {

                    if (!endPortion && curNode.length + atIndex >= match.endIndex) {

                        // We've found the ending
                        endPortion = {
                            node: curNode,
                            index: portionIndex++,
                            text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex),
                            indexInMatch: atIndex - match.startIndex,
                            indexInNode: match.startIndex - atIndex,
                            // always zero for end-portions
                            endIndexInNode: match.endIndex - atIndex,
                            isEnd: true
                        };

                    } else if (startPortion) {
                        // Intersecting node
                        innerPortions.push({
                            node: curNode,
                            index: portionIndex++,
                            text: curNode.data,
                            indexInMatch: atIndex - match.startIndex,
                            indexInNode: 0 // always zero for inner-portions
                        });
                    }

                    if (!startPortion && curNode.length + atIndex > match.startIndex) {
                        // We've found the match start
                        startPortion = {
                            node: curNode,
                            index: portionIndex++,
                            indexInMatch: 0,
                            indexInNode: match.startIndex - atIndex,
                            endIndexInNode: match.endIndex - atIndex,
                            text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex)
                        };
                    }

                    atIndex += curNode.data.length;

                }

                doAvoidNode = curNode.nodeType === 1 && elementFilter && !elementFilter(curNode);

                if (startPortion && endPortion) {

                    curNode = this.replaceMatch(match, startPortion, innerPortions, endPortion);

                    // processMatches has to return the node that replaced the endNode
                    // and then we step back so we can continue from the end of the
                    // match:
                    atIndex -= (endPortion.node.data.length - endPortion.endIndexInNode);

                    startPortion = null;
                    endPortion = null;
                    innerPortions = [];
                    match = matches.shift();
                    portionIndex = 0;
                    matchIndex++;

                    if (!match) {
                        break; // no more matches
                    }

                } else if (!doAvoidNode && (curNode.firstChild || curNode.nextSibling)) {
                    // Move down or forward:
                    curNode = curNode.firstChild || curNode.nextSibling;
                    continue;
                }

                // Move forward or up:
                while (true) {
                    if (curNode.nextSibling) {
                        curNode = curNode.nextSibling;
                        break;
                    } else if (curNode.parentNode !== node) {
                        curNode = curNode.parentNode;
                    } else {
                        break out;
                    }
                }

            }

        },

        /**
         * Reverts ... TODO
         */
        revert: function() {
            // Reversion occurs backwards so as to avoid nodes subsequently
            // replaced during the matching phase (a forward process):
            for (var l = this.reverts.length; l--;) {
                this.reverts[l]();
            }
            this.reverts = [];
        },

        prepareReplacementString: function(string, portion, match, matchIndex) {
            var portionMode = this.options.portionMode;
            if (portionMode === PORTION_MODE_FIRST && portion.indexInMatch > 0) {
                return '';
            }
            string = string.replace(/\$(\d+|&|`|')/g,
                function($0, t) {
                    var replacement;
                    switch (t) {
                        case '&':
                            replacement = match[0];
                            break;
                        case '`':
                            replacement = match.input.substring(0, match.startIndex);
                            break;
                        case '\'':
                            replacement = match.input.substring(match.endIndex);
                            break;
                        default:
                            replacement = match[+t];
                    }
                    return replacement;
                });

            if (portionMode === PORTION_MODE_FIRST) {
                return string;
            }

            if (portion.isEnd) {
                return string.substring(portion.indexInMatch);
            }

            return string.substring(portion.indexInMatch, portion.indexInMatch + portion.text.length);
        },

        getPortionReplacementNode: function(portion, match, matchIndex) {

            var replacement = this.options.replace || '$&';
            var wrapper = this.options.wrap;

            if (wrapper && wrapper.nodeType) {
                // Wrapper has been provided as a stencil-node for us to clone:
                var clone = doc.createElement('div');
                clone.innerHTML = wrapper.outerHTML || new XMLSerializer().serializeToString(wrapper);
                wrapper = clone.firstChild;
            }

            if (typeof replacement == 'function') {
                replacement = replacement(portion, match, matchIndex);
                if (replacement && replacement.nodeType) {
                    return replacement;
                }
                return doc.createTextNode(String(replacement));
            }

            var el = typeof wrapper == 'string' ? doc.createElement(wrapper) : wrapper;

            replacement = doc.createTextNode(this.prepareReplacementString(replacement, portion, match, matchIndex));

            if (!el) {
                return replacement;
            }

            el.appendChild(replacement);

            return el;
        },

        replaceMatch: function(match, startPortion, innerPortions, endPortion) {

            var matchStartNode = startPortion.node;
            var matchEndNode = endPortion.node;

            var preceedingTextNode;
            var followingTextNode;

            if (matchStartNode === matchEndNode) {

                var node = matchStartNode;

                if (startPortion.indexInNode > 0) {
                    // Add `before` text node (before the match)
                    preceedingTextNode = doc.createTextNode(node.data.substring(0, startPortion.indexInNode));
                    node.parentNode.insertBefore(preceedingTextNode, node);
                }

                // Create the replacement node:
                var newNode = this.getPortionReplacementNode(endPortion, match);

                node.parentNode.insertBefore(newNode, node);

                if (endPortion.endIndexInNode < node.length) { // ?????
                    // Add `after` text node (after the match)
                    followingTextNode = doc.createTextNode(node.data.substring(endPortion.endIndexInNode));
                    node.parentNode.insertBefore(followingTextNode, node);
                }

                node.parentNode.removeChild(node);

                this.reverts.push(function() {
                    if (preceedingTextNode === newNode.previousSibling) {
                        preceedingTextNode.parentNode.removeChild(preceedingTextNode);
                    }
                    if (followingTextNode === newNode.nextSibling) {
                        followingTextNode.parentNode.removeChild(followingTextNode);
                    }
                    newNode.parentNode.replaceChild(node, newNode);
                });

                return newNode;

            } else {
                // Replace matchStartNode -> [innerMatchNodes...] -> matchEndNode (in that order)

                preceedingTextNode = doc.createTextNode(matchStartNode.data.substring(0, startPortion.indexInNode));

                followingTextNode = doc.createTextNode(matchEndNode.data.substring(endPortion.endIndexInNode));

                var firstNode = this.getPortionReplacementNode(startPortion, match);

                var innerNodes = [];

                for (var i = 0,
                        l = innerPortions.length; i < l; ++i) {
                    var portion = innerPortions[i];
                    var innerNode = this.getPortionReplacementNode(portion, match);
                    portion.node.parentNode.replaceChild(innerNode, portion.node);
                    this.reverts.push((function(portion, innerNode) {
                        return function() {
                            innerNode.parentNode.replaceChild(portion.node, innerNode);
                        };
                    }(portion, innerNode)));
                    innerNodes.push(innerNode);
                }

                var lastNode = this.getPortionReplacementNode(endPortion, match);

                matchStartNode.parentNode.insertBefore(preceedingTextNode, matchStartNode);
                matchStartNode.parentNode.insertBefore(firstNode, matchStartNode);
                matchStartNode.parentNode.removeChild(matchStartNode);

                matchEndNode.parentNode.insertBefore(lastNode, matchEndNode);
                matchEndNode.parentNode.insertBefore(followingTextNode, matchEndNode);
                matchEndNode.parentNode.removeChild(matchEndNode);

                this.reverts.push(function() {
                    preceedingTextNode.parentNode.removeChild(preceedingTextNode);
                    firstNode.parentNode.replaceChild(matchStartNode, firstNode);
                    followingTextNode.parentNode.removeChild(followingTextNode);
                    lastNode.parentNode.replaceChild(matchEndNode, lastNode);
                });

                return lastNode;
            }
        }

    };

    return exposed;

}());