297 lines
9.4 KiB
JavaScript
297 lines
9.4 KiB
JavaScript
|
|
/**
|
|||
|
|
* amWiki Web端 - 全库搜索模块
|
|||
|
|
* @author Tevin
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
;
|
|||
|
|
(function (win) {
|
|||
|
|
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 全局搜索
|
|||
|
|
* @param {Storage} _storage
|
|||
|
|
* @constructor
|
|||
|
|
*/
|
|||
|
|
var Search = function (_storage) {
|
|||
|
|
this._storage = _storage;
|
|||
|
|
this.$e = {
|
|||
|
|
//显示搜索面板按钮
|
|||
|
|
searchShow: $('#searchShow'),
|
|||
|
|
//更新全部缓存按钮
|
|||
|
|
searchUpdate: this._storage.$e.searchUpdate,
|
|||
|
|
//搜索面板
|
|||
|
|
searchBox: $('#searchBox'),
|
|||
|
|
//搜索结果列表
|
|||
|
|
results: $('#results'),
|
|||
|
|
//搜索结果信息提示
|
|||
|
|
resultMsg: $('#resultMsg'),
|
|||
|
|
//搜素结果显示更多
|
|||
|
|
resultMore: $('#resultMore'),
|
|||
|
|
//搜索按钮
|
|||
|
|
search: $('#search'),
|
|||
|
|
//搜索文本
|
|||
|
|
searchText: $('#searchText')
|
|||
|
|
};
|
|||
|
|
this._data = {
|
|||
|
|
//搜素结果
|
|||
|
|
result: [],
|
|||
|
|
//单条结果的模板
|
|||
|
|
template: $('#template\\:searchResult').text(),
|
|||
|
|
//每页结果数
|
|||
|
|
pageSize: 15,
|
|||
|
|
//当前页码
|
|||
|
|
pagination: 0
|
|||
|
|
};
|
|||
|
|
this._bindCtrl();
|
|||
|
|
//用户执行重建缓存的回调
|
|||
|
|
this.onNeedRebuildStorage = null;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 绑定用户操作
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
Search.prototype._bindCtrl = function () {
|
|||
|
|
var that = this;
|
|||
|
|
//展开折叠搜索面板
|
|||
|
|
this.$e.searchShow.on('click', function () {
|
|||
|
|
if (that.$e.searchBox.hasClass('on')) {
|
|||
|
|
that.displayBox('off');
|
|||
|
|
that.$e.searchShow.trigger('searchoff');
|
|||
|
|
} else {
|
|||
|
|
that.displayBox('on', function () {
|
|||
|
|
resetResHeight();
|
|||
|
|
});
|
|||
|
|
that.$e.searchShow.trigger('searchon');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
//设置结果区域高度
|
|||
|
|
var resetResHeight = function () {
|
|||
|
|
var hOut = that.$e.searchBox.height();
|
|||
|
|
var dt = that.$e.results.offset().top - that.$e.searchUpdate.offset().top;
|
|||
|
|
that.$e.results.height(hOut - dt);
|
|||
|
|
};
|
|||
|
|
$(win).on('resize', function () {
|
|||
|
|
if (that.$e.searchBox.hasClass('on')) {
|
|||
|
|
resetResHeight();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
//当本地浏览且存在页面挂载数据时,隐藏重建缓存按钮
|
|||
|
|
if (location.protocol == 'file:' && typeof AWPageMounts != 'undefined') {
|
|||
|
|
this.$e.searchUpdate.parent().addClass('off');
|
|||
|
|
}
|
|||
|
|
//重建缓存
|
|||
|
|
this.$e.searchUpdate.on('click', function () {
|
|||
|
|
//开启重建缓存时,如果存在搜索子进程,则干掉子进程
|
|||
|
|
if (that._worker) {
|
|||
|
|
that._worker.terminate();
|
|||
|
|
that._worker = null;
|
|||
|
|
that.$e.resultMsg.hide();
|
|||
|
|
}
|
|||
|
|
that.$e.search.prop('disabled', true);
|
|||
|
|
that.$e.searchUpdate.prop('disabled', true);
|
|||
|
|
that.onNeedRebuildStorage(function () {
|
|||
|
|
that.$e.search.prop('disabled', false);
|
|||
|
|
that.$e.searchUpdate.val('请勿频繁使用');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
//更新全部缓存按钮使用的时间限制:一小时内不允许重复使用
|
|||
|
|
var lastBuild = this._storage.getLastBuildTs();
|
|||
|
|
if (lastBuild) {
|
|||
|
|
var lave = Date.now() - lastBuild;
|
|||
|
|
if (lave < 60 * 60 * 1000) {
|
|||
|
|
this.$e.searchUpdate.prop('disabled', true).val('请勿频繁使用');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//点击搜索
|
|||
|
|
this.$e.search.on('click', function () {
|
|||
|
|
that._search();
|
|||
|
|
});
|
|||
|
|
this.$e.searchText.on('keyup', function (e) {
|
|||
|
|
if (e.keyCode == 13) {
|
|||
|
|
that._search();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
//结果翻页
|
|||
|
|
this.$e.resultMore.on('click', function () {
|
|||
|
|
that._nextResultPage();
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 显示隐藏搜索面板
|
|||
|
|
* @param {String} type - on 显示 / off 隐藏
|
|||
|
|
* @param {Function} callback
|
|||
|
|
* @public
|
|||
|
|
*/
|
|||
|
|
Search.prototype.displayBox = function (type, callback) {
|
|||
|
|
var that = this;
|
|||
|
|
var $box = this.$e.searchBox;
|
|||
|
|
if (type == 'on' && !$box.hasClass('on')) {
|
|||
|
|
$box
|
|||
|
|
.addClass('on')
|
|||
|
|
.css({
|
|||
|
|
'display': 'block',
|
|||
|
|
'width': '0',
|
|||
|
|
'opacity': 0
|
|||
|
|
})
|
|||
|
|
.animate({
|
|||
|
|
'width': '100%',
|
|||
|
|
'opacity': 1
|
|||
|
|
}, 300, 'swing', function () {
|
|||
|
|
callback && callback();
|
|||
|
|
});
|
|||
|
|
} else if (type == 'off' && $box.hasClass('on')) {
|
|||
|
|
$box
|
|||
|
|
.removeClass('on')
|
|||
|
|
.animate({
|
|||
|
|
'width': '30%',
|
|||
|
|
'opacity': 0
|
|||
|
|
}, 200, 'swing', function () {
|
|||
|
|
$box.removeAttr('style');
|
|||
|
|
callback && callback();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 启动搜素
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
Search.prototype._search = function () {
|
|||
|
|
var that = this;
|
|||
|
|
if (this.$e.searchText.val() == '') {
|
|||
|
|
this.$e.searchText.focus();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
var words = this.$e.searchText.val();
|
|||
|
|
this.$e.resultMsg.show().text('创建搜索中...');
|
|||
|
|
if (typeof win.Worker !== "undefined") {
|
|||
|
|
//开启一次新搜索时,如果存在搜索子进程,则干掉子进程
|
|||
|
|
if (this._worker) {
|
|||
|
|
this._worker.terminate();
|
|||
|
|
this._worker = null;
|
|||
|
|
this.$e.resultMsg.hide();
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
//创建搜素子进程搜素
|
|||
|
|
this._worker = new win.Worker('amWiki/js/amWiki.search.worker.js');
|
|||
|
|
this._searchByWorker(words);
|
|||
|
|
} catch (e) {
|
|||
|
|
//在当前环境搜索
|
|||
|
|
this._searchByPresent(words);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
//在当前环境搜索
|
|||
|
|
this._searchByPresent(words);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 搜索子进程通讯
|
|||
|
|
* @param {String} words
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
Search.prototype._searchByWorker = function (words) {
|
|||
|
|
var that = this;
|
|||
|
|
//收到子进程搜素消息
|
|||
|
|
this._worker.onmessage = function (event) {
|
|||
|
|
var data = event.data;
|
|||
|
|
//加载成功后发送文档数据
|
|||
|
|
if (data.type == 'searcher:loaded') {
|
|||
|
|
that._worker.postMessage({type: 'searcher:docs', docs: that._storage.getAllDocs()});
|
|||
|
|
}
|
|||
|
|
//文档预处理完成后开始搜索
|
|||
|
|
else if (data.type == 'searcher:ready') {
|
|||
|
|
that.$e.resultMsg.show().html('正在搜索,请稍后...');
|
|||
|
|
that._worker.postMessage({type: 'searcher:search', words: words});
|
|||
|
|
}
|
|||
|
|
//搜索结果排行
|
|||
|
|
else if (data.type == 'searcher:result') {
|
|||
|
|
that._data.result = data.result;
|
|||
|
|
that._showResultList();
|
|||
|
|
that._worker.terminate();
|
|||
|
|
that._worker = null;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
//子进程出错
|
|||
|
|
this._worker.onerror = function (e) {
|
|||
|
|
console.error(e);
|
|||
|
|
this.$e.resultMsg.show().text('Sorry,出错了!<br/>' + e.msg);
|
|||
|
|
that._worker.terminate();
|
|||
|
|
that._worker = null;
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 在当前环境搜索
|
|||
|
|
* @param {String} words
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
Search.prototype._searchByPresent = function (words) {
|
|||
|
|
var searcher = new AWSearcher();
|
|||
|
|
searcher.initDocs(this._storage.getAllDocs());
|
|||
|
|
this.$e.resultMsg.show().html('正在搜索,请稍后...');
|
|||
|
|
searcher.matchWords(words);
|
|||
|
|
this._data.result = searcher.getResult();
|
|||
|
|
this._showResultList();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 显示结果列表
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
Search.prototype._showResultList = function () {
|
|||
|
|
this.$e.results.children('ul').children().remove();
|
|||
|
|
this.$e.resultMsg.hide();
|
|||
|
|
this._data.pagination = 0;
|
|||
|
|
this._nextResultPage();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 显示结果列表下一页
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
Search.prototype._nextResultPage = function () {
|
|||
|
|
var html = '';
|
|||
|
|
var count = 0;
|
|||
|
|
for (var i = this._data.pagination * this._data.pageSize, item; item = this._data.result[i]; i++) {
|
|||
|
|
html += this._renderRankItem(this._data.template, item);
|
|||
|
|
//超过页码跳出
|
|||
|
|
if (++count >= this._data.pageSize) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
this.$e.results.children('ul').append(html);
|
|||
|
|
this._data.pagination++;
|
|||
|
|
//如果还有结果没显示完,显示显示更多按钮
|
|||
|
|
if (this._data.pagination * this._data.pageSize >= this._data.result.length) {
|
|||
|
|
this.$e.resultMore.hide();
|
|||
|
|
} else {
|
|||
|
|
this.$e.resultMore.show();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 渲染单条模板
|
|||
|
|
* @param {String} template
|
|||
|
|
* @param {Object} data
|
|||
|
|
* @returns {String}
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
Search.prototype._renderRankItem = function (template, data) {
|
|||
|
|
var tmpl = template;
|
|||
|
|
data.time = win.tools.formatTime(data.timestamp);
|
|||
|
|
delete data.timestamp;
|
|||
|
|
for (var p in data) {
|
|||
|
|
if (data.hasOwnProperty(p)) {
|
|||
|
|
tmpl = tmpl.replace(new RegExp('{{' + p + '}}', 'g'), data[p]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return tmpl;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return win.AWSearch = Search;
|
|||
|
|
|
|||
|
|
})(window);
|