220 lines
8.8 KiB
JavaScript
220 lines
8.8 KiB
JavaScript
/**
|
|
* amWiki Web端 - 搜索计算子进程模块
|
|
* @author Tevin
|
|
*/
|
|
|
|
|
|
(function (self) {
|
|
|
|
'use strict';
|
|
|
|
//通过 jQuery 检查判断是否处于子进程工作
|
|
var atWorker = typeof self.jQuery == 'undefined';
|
|
|
|
(function () {
|
|
|
|
/**
|
|
* 搜索计算器
|
|
* @constructor
|
|
*/
|
|
var Searcher = function () {
|
|
//文档存储
|
|
this._documents = null;
|
|
//搜索处理结果
|
|
this._processing = {};
|
|
//设置
|
|
this._data = {
|
|
//标题命中得分
|
|
titleScore: 100,
|
|
//接口地址命中得分
|
|
apiScore: 50,
|
|
//单次内容命中得分
|
|
textScore: 5
|
|
}
|
|
};
|
|
|
|
//初始文档
|
|
Searcher.prototype.initDocs = function (docs) {
|
|
this._documents = atWorker ? docs : JSON.parse(JSON.stringify(docs));
|
|
for (var id in this._documents) {
|
|
if (this._documents.hasOwnProperty(id)) {
|
|
this._preDoc(this._documents[id]);
|
|
}
|
|
}
|
|
};
|
|
|
|
//文档预处理
|
|
Searcher.prototype._preDoc = function (doc) {
|
|
doc.content = doc.content
|
|
.replace(/^\s+|\s+$/g, '')
|
|
//分离h1标题
|
|
.replace(/^#\s?(.*?)[\r|\n]/, function (match, s1) {
|
|
doc.title = s1;
|
|
return '';
|
|
})
|
|
//分离测试文档请求地址
|
|
.replace(/([^#]#{3} *请求地址[\n\r]{1,4})([-\w:\/\.]+?)[\n\r]{1,}(#{3} *请求类型[\s\S]+?#{3} *请求参数)/,
|
|
function (match, s1, s2, s3) {
|
|
doc.api = s2;
|
|
return s1 + s3;
|
|
})
|
|
//清除 Markdown 标题标记
|
|
.replace(/#{1,6}(.*?)#{0,6}\s*[\r\n]/g, '$1')
|
|
//清除 Markdown 强调斜体删除线标记
|
|
.replace(/[_\*~]{1,2}(.*?)[_\*~]{1,2}/g, '$1')
|
|
//直接删除 Markdown 图片
|
|
.replace(/!\[.*?]\(.*?\)/g, '')
|
|
//清除 Markdown 链接标记,还原为显示文本
|
|
.replace(/\[(.*?)]\(.*?\)/g, '$1')
|
|
//清除 Markdown 代码段标记
|
|
.replace(/`{3}.*?[\n\r]([\s\S]*?)`{3}/g, '$1')
|
|
//清除 Markdown 代码标记
|
|
.replace(/`(.*?)`/g, '$1')
|
|
//直接删除所有 html 标签
|
|
.replace(/<.+?>/g, '')
|
|
//清除 Markdown 引用标记
|
|
.replace(/> *(.+?)[n\r]/g, '$1')
|
|
//清除 Markdown 分割线标记
|
|
.replace(/-{3,} *[\n\r]/g, '')
|
|
//清除 Markdown 表格标记
|
|
.replace(/(\|.*?[\n\r]{1,2}){3,}/g, function (match) {
|
|
return match.replace(/\|.*?[\n\r]{1,2}/g, function (match) {
|
|
if (match.indexOf('---') >= 0) {
|
|
return '';
|
|
} else {
|
|
return match.replace(/\|/g, '');
|
|
}
|
|
});
|
|
})
|
|
//转换一个空白符为一空格
|
|
.replace(/[\n\r\t]/g, ' ')
|
|
//合并多个空白符为一个空格
|
|
.replace(/\s{2,}/g, ' ');
|
|
};
|
|
|
|
//给中转处理添加属性
|
|
Searcher.prototype._addPorcessing = function (id, key, value) {
|
|
//如果不存在此id则创建
|
|
if (typeof this._processing[id] == 'undefined') {
|
|
this._processing[id] = {};
|
|
this._processing[id][key] = value;
|
|
}
|
|
//如果存在此id
|
|
else {
|
|
//如果不存在此属性,直接赋值
|
|
if (typeof this._processing[id][key] == 'undefined') {
|
|
this._processing[id][key] = value;
|
|
}
|
|
//如果存在此属性,则相加(仅得分一项)
|
|
else {
|
|
this._processing[id][key] += value;
|
|
}
|
|
}
|
|
};
|
|
|
|
//匹配搜索词与得分计算
|
|
Searcher.prototype.matchWords = function (words) {
|
|
var wordsReg = new RegExp(words, 'gi');
|
|
for (var id in this._documents) {
|
|
if (this._documents.hasOwnProperty(id)) {
|
|
//标题命中
|
|
if (this._documents[id].title) {
|
|
var titleMatch = this._documents[id].title.match(wordsReg);
|
|
if (titleMatch && titleMatch.length > 0) {
|
|
var title = this._documents[id].title.replace(wordsReg, function (match) {
|
|
return '<mark>' + match + '</mark>';
|
|
});
|
|
this._addPorcessing(id, 'title', title);
|
|
this._addPorcessing(id, 'score', this._data.titleScore);
|
|
}
|
|
}
|
|
//接口地址命中
|
|
if (this._documents[id].api) {
|
|
var apiMatch = this._documents[id].api.match(wordsReg);
|
|
if (apiMatch && apiMatch.length > 0) {
|
|
var api = '<p class="p1"><em>接口</em>' +
|
|
this._documents[id].api.replace(wordsReg, function (match) {
|
|
return '<mark>' + match + '</mark>';
|
|
}) + '</p>';
|
|
this._addPorcessing(id, 'api', api);
|
|
this._addPorcessing(id, 'score', this._data.apiScore);
|
|
}
|
|
}
|
|
//内容命中
|
|
var contentMatch = this._documents[id].content.match(new RegExp('.{0,15}' + words + '.{0,30}', 'gi'));
|
|
if (contentMatch) {
|
|
var content = '<p>';
|
|
for (var i = 0, item; item = contentMatch[i]; i++) {
|
|
if (i < 2) {
|
|
content += item.replace(wordsReg, function (match) {
|
|
return '<mark>' + match + '</mark>';
|
|
}) + '... ';
|
|
}
|
|
}
|
|
content += '</p>';
|
|
this._addPorcessing(id, 'content', content);
|
|
this._addPorcessing(id, 'score', contentMatch.length * this._data.textScore);
|
|
}
|
|
}
|
|
}
|
|
this._auxiliary();
|
|
};
|
|
|
|
//辅助得分
|
|
Searcher.prototype._auxiliary = function () {
|
|
};
|
|
|
|
//排序与属性补齐
|
|
Searcher.prototype._sortByScore = function () {
|
|
var list = [];
|
|
for (var id in this._processing) {
|
|
if (this._processing.hasOwnProperty(id)) {
|
|
if (typeof this._processing[id].title == 'undefined') {
|
|
this._processing[id].title = this._documents[id].title ? this._documents[id].title : '';
|
|
}
|
|
if (typeof this._processing[id].api == 'undefined') {
|
|
this._processing[id].api = '';
|
|
}
|
|
if (typeof this._processing[id].content == 'undefined') {
|
|
this._processing[id].content = '<p>' + this._documents[id].content.substr(0, 45) + '...</p>';
|
|
}
|
|
this._processing[id].path = this._documents[id].uri;
|
|
this._processing[id].timestamp = this._documents[id].timestamp;
|
|
list.push(this._processing[id]);
|
|
}
|
|
}
|
|
list.sort(function (a, b) {
|
|
return a.score > b.score ? -1 : 1;
|
|
});
|
|
return list;
|
|
};
|
|
|
|
//获取结果
|
|
Searcher.prototype.getResult = function () {
|
|
return this._sortByScore();
|
|
};
|
|
|
|
//作为子进程加载时,仅子进程内有效
|
|
//作为全局加载时,全局有效
|
|
return this.AWSearcher = Searcher;
|
|
|
|
}).call(self);
|
|
|
|
//作为子进程工作时,通过 message 通讯工作
|
|
if (atWorker) {
|
|
//计算器
|
|
var searcher = new self.AWSearcher();
|
|
self.onmessage = function (event) {
|
|
var data = event.data;
|
|
if (data.type == 'searcher:docs') {
|
|
searcher.initDocs(data.docs);
|
|
self.postMessage({type: 'searcher:ready'});
|
|
} else if (data.type == 'searcher:search') {
|
|
searcher.matchWords(data.words);
|
|
self.postMessage({type: 'searcher:result', result: searcher.getResult()});
|
|
}
|
|
};
|
|
self.postMessage({type: 'searcher:loaded'});
|
|
}
|
|
|
|
})(self); |