/** * 多個關鍵詞列表高亮(word_list1,color1,word_list2,color2,...) * @param word_list 關鍵詞列表(例: ["關鍵詞a","關鍵詞b"],不區分大小寫) * @param color 顏色值(例: "#ff0000") * @retur ...
/**
* 多個關鍵詞列表高亮(word_list1,color1,word_list2,color2,...)
* @param word_list 關鍵詞列表(例: ["關鍵詞a","關鍵詞b"],不區分大小寫)
* @param color 顏色值(例: "#ff0000")
* @returns {{render: Function}}
* 優先順序:word_list1>word_list2>word_list3...
* @example
* var highlight = new wordHighlight(["B","C"],"#ff0000",["ABC","DEF"],"blue");
* console.log(highlight.render("ABC")); // <b style="color:blue">A</b><b style="color:#ff0000">BC</b>
* console.log(highlight.render("Bc,DEF")); // <b style="color:#ff0000">Bc</b>,<b style="color:blue">DEF</b>
*/
function wordHighlight(word_list,color)
{
if (!(this instanceof wordHighlight)) {
throw 'should be called with the new!';
}
var that = this;
that.root = null;
that.color_list = [];
that.color_range = null;
var createNode = function(value){
var node = {};
node.value = value || "";
node.next = {};
node.fail = null;
node.len = 0; // word長度,0表示非末尾,非0表示word的長度
node.color = -1;
return node;
}
var addWord = function(word,color){
word = word.trim().toLowerCase();
if (!word) {
return;
}
var cur = that.root;
var len = word.length;
for (var i = 0,v; i < len; i++) {
v = word[i];
if (cur.next[v] === undefined) {
node = createNode(v);
cur.next[v] = node;
}
cur = cur.next[v];
}
cur.len = len;
if (cur.color == -1) {
cur.color = color;
}
}
var buildFailIndex = function(){
var queue = [];
var key;
for (key in that.root.next) {
that.root.next[key].fail = that.root;
queue.push(that.root.next[key]);
}
while (queue.length) {
node = queue.shift();
for (key in node.next) {
var child_node = node.next[key];
var val = child_node.value;
var p = node.fail;
while (p != null) {
if (p.next[val] !== undefined) {
child_node.fail = p.next[val];
break;
}
p = p.fail;
}
if (p === null) {
child_node.fail = that.root;
}
queue.push(child_node);
};
}
}
var mergeRange = function(color,range_list){
if (!range_list || !range_list.length) {
return;
}
range_list = (function(input_list){
// 區間合併
var output_list = [];
var tmp = {}, left = -1, right = -1;
input_list.forEach(function(range){
var start = range[0], end = range[1];
if (tmp[start] === undefined || tmp[start] < end) {
tmp[start] = end;
}
});
for (var start in tmp) {
var end = tmp[start];
if (left == -1) {
left = 0 | start;
right = end;
}
if (start > right) {
output_list.push([left,right])
left = 0 | start;
right = end;
} else if (end > right) {
right = end;
}
}
if (left != -1) {
output_list.push([left, right])
}
return output_list;
})(range_list);
if (that.color_range === null) {
that.color_range = {};
range_list.forEach(function(v){
that.color_range[v[0]] = [v[1],color];
});
} else {
var xy_list = [0],x,y;
for (var x in that.color_range) {
xy_list.push(0|x);
xy_list.push(that.color_range[x][0]);
}
xy_list.push(Infinity);
//console.log(xy_list);
var i = 0,len = range_list.length;
do {
x = xy_list.shift();
y = xy_list.shift();
} while (x == y);
var _x,_y;
while (i < len) {
_x = range_list[i][0];
_y = range_list[i][1];
if (y < _y) {
if (_x < y) {
that.color_range[_x < x ? x : _x] = [y,color];
}
do {
x = xy_list.shift();
y = xy_list.shift();
} while (x == y);
continue;
}
if (_y > x) {
that.color_range[_x < x ? x : _x] = [_y,color];
}
i++;
}
}
}
that.root = createNode();
var args = Array.prototype.slice.call(arguments);
while (args.length) {
var color = that.color_list.length;
word_list = args.shift();
if (!word_list.length) {
args.shift();
continue;
}
that.color_list.push(args.shift());
for (var i in word_list) {
addWord(word_list[i],color);
}
}
buildFailIndex();
//console.log(that.root);
return {
render: function(content){
if (!that.color_list.length) {
return content;
}
var range_list = [];
var p = that.root;
var len = content.length;
var _content = content.toLowerCase();
for (var i = 0,val,temp; i < len; i++) {
val = _content[i];
while (p.next[val] === undefined && p != that.root) {
p = p.fail;
}
p = p.next[val] ? p.next[val] : that.root;
temp = p;
while (temp != that.root) {
if (temp.len) {
if (!range_list[temp.color]) {
range_list[temp.color] = [];
}
range_list[temp.color].push([i + 1 - temp.len, i + 1]);
}
temp = temp.fail;
}
}
for (var color in range_list) {
//console.log(range_list[color]);
mergeRange(color,range_list[color]);
}
if (that.color_range === null) {
return content;
}
var new_content = '',x = 0,y;
for (y in that.color_range) {
new_content += content.substring(x,y);
x = that.color_range[y][0];
new_content += '<b style="color:' + that.color_list[that.color_range[y][1]] + '">' + content.substring(y,x) + '</b>';
}
new_content += content.substring(x);
that.color_range = null;
return new_content;
}
}
}