User:Cheetahzeng/vector.js
外观
注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google Chrome、Firefox、Microsoft Edge及Safari:按住⇧ 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;
}());