<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop &#187; JavaScript</title>
	<atom:link href="https://zohead.com/archives/category/technology/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>Madoko Local本地使用的问题</title>
		<link>https://zohead.com/archives/madoko-local/</link>
		<comments>https://zohead.com/archives/madoko-local/#comments</comments>
		<pubDate>Mon, 27 Mar 2023 16:33:21 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[工具]]></category>
		<category><![CDATA[LaTeX]]></category>
		<category><![CDATA[Madoko]]></category>
		<category><![CDATA[Madoko Local]]></category>
		<category><![CDATA[Markdown]]></category>
		<category><![CDATA[编辑器]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1827</guid>
		<description><![CDATA[关于 Madoko Madoko 是微软研究院之前推出的一款在线 Markdown 编辑器，只不过更加偏向学术使用，主要亮点在于 Madoko 可以和 LaTeX 结合，支持 LaTeX 一些语法和功能。 LaTeX 是一种基于 TEX 的排版系统，功能非常强大，不少写学术论文的研究者们应该都用过，但其比较底层，学习曲线和难度也挺大。Madoko 可以将 LaTeX 与简单的 Markdown 语法相结合，大部分的文档格式和结构可以使用现有的 Markdown 语法，用户也可以使用 LaTeX 的语法和命令进行扩展，可以很轻松地生成 LaTeX 排版效果的文档，默认也支持输出 HTML 和 P [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="about-madoko">关于 Madoko</h2>
<p><a href="https://github.com/koka-lang/madoko" target="_blank">Madoko</a> 是微软研究院之前推出的一款在线 Markdown 编辑器，只不过更加偏向学术使用，主要亮点在于 Madoko 可以和 LaTeX 结合，支持 LaTeX 一些语法和功能。</p>
<p>LaTeX 是一种基于 TEX 的排版系统，功能非常强大，不少写学术论文的研究者们应该都用过，但其比较底层，学习曲线和难度也挺大。Madoko 可以将 LaTeX 与简单的 Markdown 语法相结合，大部分的文档格式和结构可以使用现有的 Markdown 语法，用户也可以使用 LaTeX 的语法和命令进行扩展，可以很轻松地生成 LaTeX 排版效果的文档，默认也支持输出 HTML 和 PDF 文档。</p>
<p>Madoko 官方的在线编辑器 Madoko Editor 网站是：</p>
<p><a href="https://www.madoko.net/" target="_blank">https://www.madoko.net/</a></p>
<p>最近 Madoko Editor 网站访问有点问题，不过我之前也已经把 Madoko Editor 网站备份过了，能够自行部署运行，编辑器的界面和默认的示例文档如下：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737372294/madoko-editor.png" alt="Madoko Editor"></p>
<p>可以看到 Madoko 文档的扩展名是 <code>mdk</code>，而且 mdk 文档基本都是使用现有的 Markdown 语法加上一点 LaTeX 语法，即使不使用 Madoko Editor，也可以用其它 Markdown 编辑器打开进行编辑。</p>
<h2 id="madoko-local">Madoko Local</h2>
<p>mdk 文档在 Madoko Editor 中编辑修改之后，支持保存到 Dropbox、GitHub、OneDrive 以及 Local Disk 本地：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442733/makodo-editor-save.png" alt="Madoko Local Disk 保存"></p>
<p>由于众所周知的原因，前面三个存储目标国内用起来都不太稳定，而且之前我使用 Madoko 的时候就发现用这些云存储进行文档编辑似乎也容易出问题，可能就用微软自己的 OneDrive 做存储稳定一些。</p>
<p>另外考虑到数据放在自己手里才是最安全的，因此我一般都是用 Local Disk 本地保存方式。</p>
<p>Madoko 的本地磁盘访问则需要 <a href="https://github.com/koka-lang/madoko/tree/master/support/madoko-local" target="_blank">Madoko Local</a> 这个程序的配合，另外如果想要本地生成 PDF 也需要使用 Madoko Local。</p>
<p>Madoko Local 是用 Node.js 编写的，本机安装完 Node.js 环境后，运行命令完成安装之后就可以使用 <code>madoko-local</code> 命令了：</p>
<pre class="brush: bash; title: ; notranslate">
~# npm install -g madoko-local
</pre>
<p><code>madoko-local</code> 命令使用起来也非常简单：</p>
<pre class="brush: bash; title: ; notranslate">
~# madoko-local -r -l .
listening on           : http://localhost:8080
connecting securely to : https://www.madoko.net
serving files under    : /home/zzm/Documents

---------------------------------------------------------------
access server at       : http://localhost:8080#secret=XXX
---------------------------------------------------------------
</pre>
<p>最后一个参数就是 Madoko 能够访问的本地目录，<code>.</code> 就表示只导出当前的目录。</p>
<p>Madoko Local 启动之后就会输出绑定的端口（默认为 <code>8080</code>）、导出的本地目录路径，最重要的是包含密码的访问地址，浏览器通过这个访问地址就可以打开 Madoko Editor 界面。</p>
<p>Madoko Local 程序的其它参数可以通过 <code>madoko-local -h</code> 命令来查询，另外也可以使用 <code>config.json</code> 配置文件来配置端口等参数，这里我把 Madoko Local 的监听端口改为了 <code>18080</code>，也可以使用 <code>mountdir</code> 来配置导出的本地目录：</p>
<pre class="brush: bash; title: ; notranslate">
~$ cat &gt; ~/.madoko/config.json
{
  &quot;port&quot;:18080,
  &quot;secret&quot;:&quot;XXX&quot;,
  &quot;origin&quot;:&quot;http://XXX&quot;,
  &quot;mountdir&quot;:&quot;/home/zzm/Documents&quot;
}
</pre>
<p>配置文件所在的 <code>.madoko</code> 目录默认位于当前用户的主目录下，这个主目录的路径也可以通过 <code>madoko-local</code> 命令的 <code>--homedir</code> 参数进行自定义。</p>
<p>如果对应目录下存在有效的 <code>config.json</code> 配置文件，后续就可以直接 <code>madoko-local</code> 命令启动程序而不需要加任何参数了。</p>
<h2 id="madoko-local-issue">Madoko Local 问题</h2>
<p>不过我在使用 Madoko Local 时也发现一些问题：</p>
<ol>
<li>通过 <code>madoko-local</code> 命令来指定端口、密码等各种参数可能存在问题；</li>
<li>如果 <code>madoko-local</code> 程序默认绑定了本地 IPv6 的 <code>::1</code> 地址（也就是 <code>localhost</code> 地址），会报 <code>only serving localhost</code> 错误。</li>
</ol>
<p>为此我稍微修改了一下 Madoko Local Node.js 程序源码，生成了 Madoko Local 最新 0.9.4 版本的 patch 文件，有需要的朋友可以从下面任选一个下载：</p>
<ol>
<li><a href="https://pastebin.com/raw/jcSFMVsH" target="_blank">Pastebin 分享链接</a></li>
<li><a href="https://github.com/zohead/madoko/commit/6fd522fca1f7b25bd51a1150ac988bb24d591636.patch" target="_blank">GitHub 提交</a></li>
</ol>
<p>下载了 patch 文件之后，就可以进入 Madoko Local 的安装目录运行命令进行合并了（假设下载保存的文件名是 <code>madoko-local-npm-v0.9.4.patch</code>）：</p>
<pre class="brush: bash; title: ; notranslate">
~# cd /usr/lib/node_modules/
~# patch -p2 madoko-local-npm-v0.9.4.patch
</pre>
<p>上面命令使用的是 Linux 系统下 <code>npm</code> 默认的全局安装路径 <code>/usr/lib/node_modules</code>，其他系统可以使用 <code>npm list -g | head -n 1</code> 命令来确认。</p>
<p>为了方便，我也直接 fork 了 Madoko 的源仓库，还把自己做的其它的一些修改也提交了：</p>
<p><a href="https://github.com/zohead/madoko" target="_blank">https://github.com/zohead/madoko</a></p>
<p>Madoko Local 程序的代码在 <code>support/madoko-local</code> 子目录下供大家参考。</p>
<p>最后祝跨过三年疫情的朋友们都能玩得开心 ^_^。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/madoko-local/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用lunr.js为Wiki系统增加全文搜索支持</title>
		<link>https://zohead.com/archives/wiki-lunr-js/</link>
		<comments>https://zohead.com/archives/wiki-lunr-js/#comments</comments>
		<pubDate>Mon, 09 Jan 2017 16:50:39 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[学习感悟]]></category>
		<category><![CDATA[Elasticlunr.js]]></category>
		<category><![CDATA[lunr.js]]></category>
		<category><![CDATA[MDwiki]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[Wiki]]></category>
		<category><![CDATA[Wikitten]]></category>
		<category><![CDATA[全文]]></category>
		<category><![CDATA[索引]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1338</guid>
		<description><![CDATA[搜索 Wiki 知识库的问题 今年早些时候我捣鼓了一个基于 Wikitten 和 MDwiki 的 个人知识库系统，我一般使用基于 PHP Wikitten 的 动态 Wiki 知识库，本地预览或者测试时可以用基于 MDwiki 的 静态 Wiki 知识库，两个配合使用并通过 BitTorrent Sync 与 VPS 进行数据同步，这样需要更新时也是很方便的。 我在实际使用中还是发现 Wikitten 的搜索功能比较薄弱，只支持通过文档或目录名称进行搜索（Wikitten 显示时是直接遍历 Wiki 文档的，出于效率考虑也不好直接进行目录遍历全文搜索）；MDwiki 则由于是纯静态实现，根本 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="搜索-wiki-知识库的问题">搜索 Wiki 知识库的问题</h2>
<p>今年早些时候我捣鼓了一个基于 Wikitten 和 MDwiki 的 <a href="https://zohead.com/archives/wikitten-mdwiki/">个人知识库系统</a>，我一般使用基于 PHP Wikitten 的 <a href="https://wiki.zohead.com/" target="_blank">动态 Wiki 知识库</a>，本地预览或者测试时可以用基于 MDwiki 的 <a href="https://mdwiki.zohead.com/" target="_blank">静态 Wiki 知识库</a>，两个配合使用并通过 BitTorrent Sync 与 VPS 进行数据同步，这样需要更新时也是很方便的。</p>
<p>我在实际使用中还是发现 Wikitten 的搜索功能比较薄弱，只支持通过文档或目录名称进行搜索（Wikitten 显示时是直接遍历 Wiki 文档的，出于效率考虑也不好直接进行目录遍历全文搜索）；MDwiki 则由于是纯静态实现，根本没有搜索功能，通过我写的生成 Wiki 目录索引的脚本也只能一层层目录定位到 Wiki 文档，要想快速的搜索知识库内容暂时只能通过在本地运行 grep 等命令的方式。</p>
<h2 id="关于-lunrjs">关于 lunr.js</h2>
<p>最近我在看到 <a href="http://lunrjs.com/">lunr.js</a> 这个轻量级 JavaScript 全文搜索引擎之后，搜索 Wiki 知识库的问题也迎来了曙光。目前有一些基于 Hexo 之类静态博客框架的站点就是使用 lunr.js 实现了站内搜索功能。因为我考虑将 lunr.js 加入到 Wikitten 和 MDwiki 系统中以支持直接在线全文搜索。</p>
<p>lunr.js 的官方版本目前仍然不支持中文的分词器，其目前支持的语言列表在 <a href="https://github.com/MihaiValentin/lunr-languages">这里</a>。开始我考虑过使用 <a href="http://blog.csdn.net/watkinsong">wei song</a> 博主写的基于 lunr.js 的改进版 <a href="http://elasticlunr.com/">Elasticlunr.js</a>，作者介绍的 Elasticlunr.js 相比 lunr.js 可以实现更快的搜索速度和更小的索引文件，只是 Elasticlunr.js 同样也不支持中文搜索，还是暂且作罢。</p>
<p>最终我使用的是国内网友 <a href="http://blog.codepiano.com/">codepaino</a> 分享的支持中文的修改版 lunr.js，项目地址为：</p>
<p><a href="https://github.com/codepiano/lunr.js">https://github.com/codepiano/lunr.js</a></p>
<p>在 VPS 上安装修改版的 lunr.js 也还是比较简单的，首先 VPS 上需要有 node.js 环境（这里只介绍 Linux 版本的），下载修改版的 lunr.js：</p>
<pre class="brush: bash; title: ; notranslate">
root@zoserver:~# wget https://github.com/codepiano/lunr.js/raw/master/lunr.min.js
</pre>
<p>然后 npm 安装依赖的 nodejieba 库：</p>
<pre class="brush: bash; title: ; notranslate">
root@zoserver:~# npm install -g nodejieba
|
&gt; nodejieba@2.2.4 install /usr/lib/node_modules/nodejieba
&gt; node-gyp rebuild

gyp WARN EACCES user &quot;root&quot; does not have permission to access the dev dir &quot;/root/.node-gyp/4.6.1&quot;
gyp WARN EACCES attempting to reinstall using temporary dev dir &quot;/usr/lib/node_modules/nodejieba/.node-gyp&quot;
make: Entering directory `/usr/lib/node_modules/nodejieba/build'
  CXX(target) Release/obj.target/nodejieba/lib/index.o
  CXX(target) Release/obj.target/nodejieba/lib/nodejieba.o
  SOLINK_MODULE(target) Release/obj.target/nodejieba.node
  COPY Release/nodejieba.node
make: Leaving directory `/usr/lib/node_modules/nodejieba/build'
nodejieba@2.2.4 /usr/lib/node_modules/nodejieba
└── nan@2.3.5
</pre>
<h2 id="生成-wiki-搜索索引文件">生成 Wiki 搜索索引文件</h2>
<p>在线的 Wiki 系统在实际使用搜索功能时需要在浏览器前端加载 lunr.js 的搜索索引文件，为了方便生成整个 Wiki 知识库目录的所有 Markdown 文档的索引，我写了一个生成 Wiki 搜索索引文件的 node.js 程序。</p>
<p><strong>search.js</strong> 程序的功能非常简单，遍历 Wikitten 和 MDwiki 共用的 Wiki 文档目录（默认为 <code>library</code>），获取遍历到的 Wiki 文档的标题、关键字标签和文档内容（这三者搜索权重依次降低），然后使用 lunr.js 最终生成 JSON 格式的索引文件：</p>
<pre class="brush: jscript; title: search.js; notranslate">
var fs = require('fs'), removeMd = require('remove-markdown'), lunr = require('./static/js/lunr.min.js');

var md_index = lunr(function () {
	this.field('title');
	this.field('tags');
	this.field('body');
	this.ref('url');
});

function walk_dir(wdir, path, callback) {
	var dirList = fs.readdirSync(wdir + path);
	dirList.forEach(function(item) {
		if(fs.statSync(wdir + path + '/' + item).isDirectory())
			walk_dir(wdir, path + '/' + item, callback);
		else
			callback(wdir, path + '/' + item);
	});
}

function process_markdown(wdir, path) {
	var pos = path.lastIndexOf(&quot;.&quot;);
	if (pos &lt; 0) return;
	if (path.substr(pos) != &quot;.md&quot; &amp;&amp; path.substr(pos) != &quot;.markdown&quot;) return;
	var fpos = path.lastIndexOf(&quot;/&quot;);
	var title = (fpos &gt;= 0 ? path.substring(fpos + 1, pos) : path.substr(0, pos));

	// ignore navigation menu document
	if (path == &quot;/navigation.md&quot;) return;

	var has_front = false;
	var md_doc = {
		&quot;url&quot;: path,
		&quot;title&quot;: title,
		&quot;tags&quot;: &quot;&quot;
	};

	var data = fs.readFileSync(wdir + path, &quot;utf-8&quot;);
	// ignore auto generated index.md
	if (title == &quot;index&quot; &amp;&amp; data.indexOf(&quot;Auto-index of&quot;) &gt;= 0) return;

	// process front matter
	if (data.substr(0, 3) == &quot;```&quot;) {
		pos = data.substr(3).indexOf(&quot;```&quot;);
		if (pos &gt;= 0) {
			var front = JSON.parse(&quot;{&quot; + data.substr(3, pos) + &quot;}&quot;);
			has_front = true;
			if (&quot;title&quot; in front) md_doc.title = front.title;
			if (&quot;tags&quot; in front) md_doc.tags = front.tags.join(' ');
			md_doc.body = removeMd(data.substr(pos + 6));
		}
	}
	if (!has_front) md_doc.body = removeMd(data);
	md_index.add(md_doc);
}

if (process.argv.length &lt; 4) {
	console.log(&quot;Usage:\n&quot;);
	console.log(process.argv[0] + &quot; &quot; + process.argv[1] + &quot; Wiki-document-directory Search-index-file&quot;);
	console.log(process.argv[0] + &quot; &quot; + process.argv[1] + &quot; Search-index-file Keyword&quot;);
	process.exit(1);
} else if (!fs.existsSync(process.argv[2])) {
	console.log(&quot;Invalid Wiki-document-directory or Search-index-file: &quot; + process.argv[2]);
	process.exit(1);
}

if (fs.statSync(process.argv[2]).isDirectory()) {
	walk_dir(process.argv[2], &quot;&quot;, process_markdown);
	fs.writeFileSync(process.argv[3], JSON.stringify(md_index.toJSON()));
} else {
	md_index = lunr.Index.load(JSON.parse(fs.readFileSync(process.argv[2])));
	console.log(JSON.stringify(md_index.search(process.argv[3])));
}
</pre>
<blockquote>
<p><strong>提示</strong></p>
<p>为了方便 Wikitten 和 MDwiki 能同时使用 lunr.js，我把 <strong>search.js</strong> 程序引用的修改版 <code>lunr.min.js</code> 文件放到了 Wiki 程序主目录下的 <code>static/js</code> 文件夹，如需要的话可以自行修改。</p>
</blockquote>
<p>上面的程序在遍历 Markdown 文档时忽略了我之前写的自动生成子目录索引文档脚本 <code>generate-index.sh</code> 产生的 <code>index.md</code> 文档，另外也忽略了 MDwiki 专用的 <code>navigation.md</code> Wiki 系统菜单文档。</p>
<p><strong>search.js</strong> 程序使用了 <code>remove-markdown</code> 这个去掉 Markdown 格式的 npm 包，同样用 npm 安装即可：</p>
<pre class="brush: bash; title: ; notranslate">
root@zoserver:~# npm install -g remove-markdown
</pre>
<p>一般情况下直接在 Wiki 程序目录下运行 <strong>search.js</strong> 程序生成索引文件即可：</p>
<pre class="brush: bash; title: ; notranslate">
root@zoserver:/home/wiki# node search.js library search_index.json
</pre>
<p>最后上面的命令生成出来的 <code>search_index.json</code> 就是 lunr.js 格式的索引文件，我们可以直接放到 Wiki 程序主目录下。</p>
<p>经过实际测试目前我的 Wiki 目录一共包含 44 篇有效的 Markdown 文档，文件夹占用不到 500 KB，不过生成出来的 <code>search_index.json</code> 索引文件就已经接近 1.5 MB 了，浏览器访问索引文件时通过 gzip 压缩传输之后差不多 160 KB，还在我能接受的范围内。</p>
<p>另外你也可以使用 <strong>search.js</strong> 程序通过索引文件直接在本地进行关键字搜索：</p>
<pre class="brush: bash; title: ; notranslate">
root@zoserver:/home/wiki# node search.js search_index.json shell
[{&quot;ref&quot;:&quot;/技术/Android/Android Shell控制手机.md&quot;,&quot;score&quot;:0.21389728235059557},{&quot;ref&quot;:&quot;/技术/Linux/Shell/Shell进行TCP和UDP网络编程.md&quot;,&quot;score&quot;:0.2134705596450158},{&quot;ref&quot;:&quot;/技术/Linux/Shell/sed命令相关技巧.md&quot;,&quot;score&quot;:0.13650355812409723},{&quot;ref&quot;:&quot;/技术/Linux/Shell/vim操作技巧.md&quot;,&quot;score&quot;:0.1060635524797807},{&quot;ref&quot;:&quot;/技术/Windows/rundll32运行命令列表.md&quot;,&quot;score&quot;:0.035704080604386734},{&quot;ref&quot;:&quot;/技术/Windows/Windows注册表记录.md&quot;,&quot;score&quot;:0.0019103777768910307}]
</pre>
<h2 id="将-lunrjs-整合到-wiki-系统">将 lunr.js 整合到 Wiki 系统</h2>
<p>有了正确的搜索索引文件之后，我们就可以修改 Wikitten 和 MDwiki 程序将 lunr.js 搜索功能整合进来了。</p>
<p>下面 Wikitten 和 MDwiki 支持全文搜索的修改我已经提交到 <a href="https://github.com/zohead/wikitten-and-mdwiki">wikitten-and-mdwiki</a> 项目，提交记录可以参考 <a href="https://github.com/zohead/wikitten-and-mdwiki/commit/ea6d35aa038e6da064892c9091f767f323b7864b">这里</a>。</p>
<h3 id="wikitten-修改">Wikitten 修改</h3>
<p>由于 Wikitten 自带了按文档和目录名搜索的功能，我们只要给 Wikitten 增加 AJAX 加载 lunr.js 搜索索引文件并按关键字搜索的功能，最后使用 lunr.js 的搜索结果替换原来的即可。</p>
<p>Wikitten 默认只允许直接访问 <code>static</code> 子目录中的文件，我们需要修改对应的 Web 目录转发规则，这里只贴出 Nginx 服务器的配置（Apache 用户请自行修改 <code>.htaccess</code> 文件）：</p>
<pre class="brush: plain; title: ; notranslate">
server
{
	gzip on;
	gzip_min_length 1000;
	gzip_buffers 4 8k;
	gzip_types text/plain application/x-javascript text/css application/json application/xml text/javascript;

	location ~* ^/(robots.txt|search_index.json|static/(css|js|img|fonts)/.+.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt|swf|pdf|txt|bmp|eot|svg|ttf|woff|woff2))$ {
		access_log off;
		expires max;
	}
}
</pre>
<p>location 规则指定对 Wiki 主目录下的 <code>robots.txt</code> 和 <code>search_index.json</code> 索引文件以及 <code>static</code> 子目录中的文件不经过 Wikitten 处理；另外为了提高索引文件的加载速度上面的配置中还开启了 gzip 压缩，MDwiki 也可以做同样的修改。</p>
<h3 id="mdwiki-修改">MDwiki 修改</h3>
<p>MDwiki 原本没有搜索功能，为了方便页面上使用，我在其专用的 <code>navigation.md</code> Wiki 系统菜单文档里增加了一个搜索菜单，这样所有 Wiki 文档页面就都能使用了：</p>
<hr size="1" />
<p>[&#x1f50d;]()<br />&nbsp;* # Search file name or content.<br />&nbsp;* [&lt;input id="search_input" type="text"/&gt;](#)</p>
<hr size="1" />
<p>搜索菜单显示效果如下：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442839/mdwiki-search-menu.png" alt="MDwiki 搜索菜单" title="MDwiki 搜索菜单"></p>
<p>然后在 MDwiki 对应的 <code>index.html</code> 中增加搜索处理代码：</p>
<pre class="brush: jscript; title: index.html; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;static/js/lunr.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
var search_indexes = null;

function doSearch(query) {
	if (search_indexes == null) return;
	var s_res = search_indexes.search(query);
	var t_html = '&lt;ul&gt;';
	for (var i = 0; i &lt; s_res.length; i++) {
		var t_pos = s_res[i].ref.lastIndexOf('/');
		t_html += '&lt;li&gt;&lt;a href=&quot;#!' + s_res[i].ref + '&quot;&gt;' + (t_pos &gt;= 0 ? s_res[i].ref.substr(t_pos + 1) : s_res[i].ref) + '&lt;/a&gt;&lt;/li&gt;';
	}
	t_html += '&lt;/ul&gt;';
	$('#search-result-body').html(t_html);
	$('#search-result-label').html('Search result for: ' + query);
	$('#search-result').modal('show');
}

function search_content(query) {
	if (search_indexes == null) {
		$.getJSON(&quot;search_index.json&quot;, function(data){
			if (data != null) search_indexes = lunr.Index.load(data);
			doSearch(query);
		});
	} else
		doSearch(query);
}

$(document).bind('DOMNodeInserted', function(e) {
	if ($('#search_input').length &lt;= 0) return;
	$(document).unbind('DOMNodeInserted');

	$('#search_input').bind('click', function(event) {
		event.stopPropagation();
		return false;
	});
	$('#search_input').bind('keypress', function(event){
		if (event.keyCode == '13')
			search_content($(this).val());
	});
});
&lt;/script&gt;

&lt;div class=&quot;modal fade&quot; id=&quot;search-result&quot; tabindex=&quot;-1&quot; role=&quot;dialog&quot; aria-labelledby=&quot;search-result-label&quot; aria-hidden=&quot;true&quot;&gt;
&lt;div class=&quot;modal-dialog&quot;&gt;
&lt;div class=&quot;modal-content&quot;&gt;
&lt;div class=&quot;modal-header&quot;&gt;
&lt;button type=&quot;button&quot; class=&quot;close&quot; data-dismiss=&quot;modal&quot; aria-hidden=&quot;true&quot;&gt;&amp;#x274c;&lt;/button&gt;
&lt;h4 class=&quot;modal-title&quot; id=&quot;search-result-label&quot;&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div class=&quot;modal-body&quot; id=&quot;search-result-body&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</pre>
<p>上面对搜索输入框的点击事件增加了处理，防止点击时 Bootstrap 的搜索菜单项丢失，在输入框中输入关键字回车才加载索引文件并使用 lunr.js 进行搜索。</p>
<p>MDwiki 的搜索效果如下图所示：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442840/mdwiki-search-result.png" alt="MDwiki 搜索结果" title="MDwiki 搜索结果"></p>
<p>点击搜索结果中的文档名称就会跳转显示对应 Wiki 文档内容。</p>
<h2 id="总结">总结</h2>
<p>经过对 Wikitten 和 MDwiki 进行修改，目前 Wiki 知识库的显示和查询效果基本都能满足我的需求了。虽然还存在搜索索引文件体积稍大的隐患，后续也可以考虑使用 Elasticlunr.js 或者其它方式进行改进咯。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/wiki-lunr-js/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>基于Chrome Socket的XMLHttpRequest</title>
		<link>https://zohead.com/archives/chrome-xhr/</link>
		<comments>https://zohead.com/archives/chrome-xhr/#comments</comments>
		<pubDate>Sat, 18 Jul 2015 16:49:35 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Chrome]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[chrome.sockets.tcp]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[XMLHttpRequest]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=999</guid>
		<description><![CDATA[之前在开发 Chrome OS 系统下的快盘文件系统时发现使用 Chrome 自带的 XMLHttpRequest 存在一些限制： 使用 jQuery 实现的 XHR 碰到 HTTP 302 redirect 等特殊的请求时浏览器会自动处理（例如：自动重定向），在 Chrome App 模式下会被限制，如果需要得到重定向的地址就不好实现，此时可以考虑用 Chrome Socket 来实现完整的 HTTP 协议请求。 有关 Chrome Socket 即 chrome.sockets.tcp 的说明可以参考这里： https://developer.chrome.com/apps/sockets [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>之前在开发 Chrome OS 系统下的快盘文件系统时发现使用 Chrome 自带的 XMLHttpRequest 存在一些限制：</p>
<p>使用 jQuery 实现的 XHR 碰到 HTTP 302 redirect 等特殊的请求时浏览器会自动处理（例如：自动重定向），在 Chrome App 模式下会被限制，如果需要得到重定向的地址就不好实现，此时可以考虑用 Chrome Socket 来实现完整的 HTTP 协议请求。</p>
<p>有关 Chrome Socket 即 chrome.sockets.tcp 的说明可以参考这里：</p>
<p><a href="https://developer.chrome.com/apps/sockets_tcp" target="_blank">https://developer.chrome.com/apps/sockets_tcp</a></p>
<p>后来发现网上已经有人实现了一个还算完整的基于 Chrome Socket 的 XMLHttpRequest：</p>
<p><a href="https://github.com/ahmadnassri/chrome.sockets.tcp.xhr" target="_blank">https://github.com/ahmadnassri/chrome.sockets.tcp.xhr</a></p>
<p>经过实际使用之后发现这个 XHR 类确实实现了基本的 HTTP 协议，但实际使用时还是有点问题，因此我在此项目基础上做了一些改进：</p>
<ul>
<li>原 chrome.sockets.tcp.xhr 不支持获取完整的 HTTP 响应内容，只能得到第一次接收到的数据，这样对于需要返回大量数据的下载等操作是不能接受的；</li>
<li>增加重定向判断，支持直接返回 HTTP 302 重定向的目标地址；</li>
<li>支持设置第一次接收到数据的超时；</li>
<li>支持所有接收的数据通过 ArrayBuffer 返回。</li>
</ul>
<p>修改过的基于 Chrome Socket 的 XMLHttpRequest 代码我放在这里了：</p>
<p><a href="https://github.com/zohead/chrome.sockets.tcp.xhr" target="_blank">https://github.com/zohead/chrome.sockets.tcp.xhr</a></p>
<p>chrome.sockets.tcp.xhr 的使用文档可以参考这里：</p>
<p><a href="http://chromesocketstcpxhr.readthedocs.org/en/latest/" target="_blank">http://chromesocketstcpxhr.readthedocs.org/en/latest/</a></p>
<p>修改之后的使用方法与这个文档里的基本一致，增加的部分例如可以通过 recvTimeout 属性指定第一次接受到数据的超时：</p>
<pre class="brush: jscript; title: ; notranslate">
var xhr = new chrome.sockets.tcp.xhr();
xhr.recvTimeout = 3500;
xhr.open('GET', 'http://google.com:80');
xhr.setRequestHeader('X-Requested-With', 'chrome.sockets.tcp.xhr');
xhr.send(null);
</pre>
<p>上面的代码就表示连接成功之后如果 3.5 秒之内没有接收到数据就认为超时断开连接。</p>
<p>由于此 chrome.sockets.tcp.xhr 项目功能实现的还不是特别完整，如果此项目使用中发现有什么问题欢迎提出并 fork 修改哦～～～</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/chrome-xhr/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>WordPress博客增加Google Translate翻译功能</title>
		<link>https://zohead.com/archives/wordpress-google-translate/</link>
		<comments>https://zohead.com/archives/wordpress-google-translate/#comments</comments>
		<pubDate>Fri, 08 Nov 2013 18:23:46 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[qTranslate]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[多语言]]></category>
		<category><![CDATA[小工具]]></category>
		<category><![CDATA[插件]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=623</guid>
		<description><![CDATA[本文同步自（最佳显示效果请点击）：https://zohead.com/archives/wordpress-google-translate/ 之前给 WordPress 博客上安装了 QTranslate 博客以支持多语言，目前本博客已支持中文和英文的了，实际使用时博客作者是需要分别针对中文和英文及其它所需要的语言分别进行翻译处理的，对于笔者这种比较懒的人，除非是老外会看的一些文章，一般也懒的翻译成英文，因此考虑在 WordPress 博客里加上直接使用 Google Translate 进行翻译的处理。 通过查看 Google Translate 提供的接口，我们可以在 WordPres [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（最佳显示效果请点击）：<a href="https://zohead.com/archives/wordpress-google-translate/" target="_blank">https://zohead.com/archives/wordpress-google-translate/</a></p>
<p>之前给 WordPress 博客上安装了 QTranslate 博客以支持多语言，目前本博客已支持中文和英文的了，实际使用时博客作者是需要分别针对中文和英文及其它所需要的语言分别进行翻译处理的，对于笔者这种比较懒的人，除非是老外会看的一些文章，一般也懒的翻译成英文，因此考虑在 WordPress 博客里加上直接使用 Google Translate 进行翻译的处理。</p>
<p>通过查看 Google Translate 提供的接口，我们可以在 WordPress 的仪表盘中的 外观 - 小工具 设置界面中拖动一个 “文本” 类型的小工具到对应的小工具区域中去，小工具标题为 “翻译”，“文本” 类型的小工具可以实现在博客页面的小工具区域中添加自己需要的 HTML 代码，还是比较好用的。</p>
<p>增加的 HTML 代码如下：</p>
<p><pre class="brush: xml; title: ; notranslate">
&lt;div id=&quot;google_translate_element&quot;&gt;&lt;/div&gt;
&lt;script&gt;
function googleTranslateElementInit() {
  new google.translate.TranslateElement({
    pageLanguage: document.documentElement.lang
  }, 'google_translate_element');
}
&lt;/script&gt;
&lt;script src=&quot;http://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit&quot;&gt;&lt;/script&gt;
</pre>
</p>
<p>注意上面代码中的 pageLanguage 表示 WordPress 博客当前的实际语言代码，之前网上看得代码都是写死的，这里我们使用 document.documentElement.lang 动态获取博客当前实际的语言（使用 QTranslate 切换语言时也能正常工作）。</p>
<p>修改保存之后访问主界面就可以看到小工具区域里多了一个 “翻译” 的区域，访客可以直接选择需要翻译到的语言，Google Translate 可以自动翻译当前页面内容为指定语言，实在是非常方便。</p>
<p>接着考虑到有些博主可能像我一样用到了 QTranslate 插件来实现博客多语言，这样就需要修改小工具的标题在不同语言下的显示问题，这时只要把标题改为下面的形式就又可以支持多语言了：</p>
<p>
<pre class="php" style="font-family:monospace;"><span style="color: #009900;">&#91;</span><span style="color: #339933;">:</span>zh<span style="color: #009900;">&#93;</span>翻译<span style="color: #009900;">&#91;</span><span style="color: #339933;">:</span>en<span style="color: #009900;">&#93;</span>Translate</pre>
</p>
<p>上面的形式中将中文和英文字符串写在一起，这样 WordPress 页面在显示时会自动根据当前语言选择应该用的字符串。</p>
<p>另外 Google Translate 界面的样式也是可以定制的，详细请参考 Google Translate 网站翻译页面：</p>
<p><a href="http://translate.google.com/manager/website/" target="_blank">http://translate.google.com/manager/website/</a></p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/wordpress-google-translate/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>JavaScript脚本实现随手记商家自动排序</title>
		<link>https://zohead.com/archives/sort-feidee-store/</link>
		<comments>https://zohead.com/archives/sort-feidee-store/#comments</comments>
		<pubDate>Sat, 31 Aug 2013 17:00:21 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[工具]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[财务]]></category>
		<category><![CDATA[商家]]></category>
		<category><![CDATA[排序]]></category>
		<category><![CDATA[浏览器]]></category>
		<category><![CDATA[脚本]]></category>
		<category><![CDATA[随手记]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=513</guid>
		<description><![CDATA[随手记商家显示问题 最近将手机上的记账软件由 EasyMoney 更换为随手记之后，在使用随手记时发现随手记的商家分类功能在实际记账时非常不方便。记账时选择商家只有 “最近商家” 和 “所有商家” 这两个选项，但这里面的所有商家列表是按商家分类添加的顺序排列的，而不是按商家的名称排序的，这样选择起来就非常困难。虽然随手记支持通过安装其它 App 识别当前位置旁边的商家，但小的商家往往无法识别，而且有时是消费完了过段时间才记账，并不特别实用。 看一张随手记上的商家列表图片： 像我的情况一共有将近 300 个商家，如果要在这个长长得未排序的商家列表里找到我需要的商家，还是比较麻烦的。随手记在 We [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2>随手记商家显示问题</h2>
<p>最近将手机上的记账软件由 EasyMoney 更换为随手记之后，在使用随手记时发现随手记的商家分类功能在实际记账时非常不方便。记账时选择商家只有 “最近商家” 和 “所有商家” 这两个选项，但这里面的所有商家列表是按商家分类添加的顺序排列的，而不是按商家的名称排序的，这样选择起来就非常困难。虽然随手记支持通过安装其它 App 识别当前位置旁边的商家，但小的商家往往无法识别，而且有时是消费完了过段时间才记账，并不特别实用。</p>
<p>看一张随手记上的商家列表图片：</p>
<p><a href="https://zohead.com/wp-content/uploads/feidee-store-category.jpg"><img title="未排序的随手记商家列表" alt="未排序的随手记商家列表" src="https://zohead.com/wp-content/uploads/feidee-store-category.jpg" width="483" height="231" /></a></p>
<p>像我的情况一共有将近 300 个商家，如果要在这个长长得未排序的商家列表里找到我需要的商家，还是比较麻烦的。随手记在 Web 版的设置界面中支持调整商家的位置，而且只能支持在某一个商家上点向上、向下按钮调整位置，不支持拖动，就算支持拖动，对于将近 300 个商家的情况，如果要一一拖动让所有商家按名称来排序还是极其痛苦的。</p>
<h2>JavaScript 对商家排序</h2>
<p>为此我专门用浏览器调试工具研究了下调整商家位置的请求，发现调整位置时提交的数据实际是所有商家在一起的，这样就好办了，我写了一段 JavaScript 脚本用于在浏览器上直接自动按名称排序（数字在前，英文次之，然后是中文按拼音排序）所有商家。</p>
<p>首先使用 IE9 或以上的浏览器（脚本要求，不要使用 360、搜狗之类的浏览器防止脚本运行有问题，嘿嘿）访问随手记的 Web 版，进入设置 - 分类设置 - 商家分类，也就是这个地址：<a href="https://money07.feidee.com/money/category/storeCategory.do" target="_blank">https://money07.feidee.com/money/category/storeCategory.do</a>，在这个页面上你可以看到所有商家的列表。</p>
<p>然后拷贝下面这一段 JavaScript 代码粘贴到浏览器地址栏中，注意这里只有一行，必须整行全部拷贝下来：</p>
<pre class="brush: plain; title: ; notranslate">
javascript:rows = document.getElementsByClassName(&quot;lb-row&quot;); var shops = []; var shids = []; var re = /^category\d+$/; for (var i = 0; i &lt; rows.length; i++) { if (re.test(rows[i].id)) { var t = rows[i].getElementsByClassName(&quot;li-level2&quot;); if (t.length &gt; 0) { shops.push(t[0].innerHTML); shids[t[0].innerHTML] = rows[i].id.substr(8); } } } shops.sort(function(a,b){return a.localeCompare(b)}); pstr = &quot;&quot;; for (i = 0; i &lt; shops.length; i++) { if (i &gt; 0) { pstr += &quot;-&quot;; } pstr += shids[shops[i]]; } var p = document.createElement(&quot;DIV&quot;); p.innerHTML = &quot;&lt;form name='frm' method='post' action='/money/category/storeCategory.rmi'&gt;&lt;input type='hidden' name='m' value='updateOrder'/&gt;&lt;input type='hidden' name='idList' value='&quot; + pstr + &quot;'/&gt;&lt;/form&gt;&quot;; document.body.appendChild(p); document.frm.submit(); void(0)
</pre>
<p>特别需要注意的是拷贝到 IE 浏览器的地址上你可能会发现前面少了 <code>javascript:</code> 这个前缀，这时必须你手工把 <code>javascript:</code> 这个前缀补到地址的最前面（这一步不可缺少），如下图所示：</p>
<p><a href="https://zohead.com/wp-content/uploads/feidee-store-sort-js.jpg"><img title="随手记自动排序脚本" alt="随手记自动排序脚本" src="https://zohead.com/wp-content/uploads/feidee-store-sort-js.jpg" width="484" height="156" /></a></p>
<p>然后在地址栏上按下回车键，脚本就会自动执行，并且返回一个空白的网页，这一般表示执行成功了。接着可以点击浏览器后退按钮回到商家分类页面看看执行的成果：</p>
<p><a href="https://zohead.com/wp-content/uploads/feidee-sorted-store.jpg"><img title="已排序的随手记商家列表" alt="已排序的随手记商家列表" src="https://zohead.com/wp-content/uploads/feidee-sorted-store.jpg" width="442" height="371" /></a></p>
<p>可以看到商家被已经自动按名称来排序了，接着只要在手机或者平板上同步一下就可以正常使用了，必须比随手记默认的坑爹商家排序好多了。</p>
<h2>代码解析</h2>
<p>对 Web 开发比较熟悉的同学应该比较熟悉下面的原始 JavaScript 代码哈，直接使用 JS 自带的 localeCompare 函数进行字符串排序：</p>
<pre class="brush: jscript; title: sort-feidee-store.js; notranslate">
rows = document.getElementsByClassName(&quot;lb-row&quot;);

var shops = [];
var shids = [];
var re = /^category\d+$/;

for (var i = 0; i &lt; rows.length; i++) {
	if (re.test(rows[i].id)) {
		var t = rows[i].getElementsByClassName(&quot;li-level2&quot;);
		if (t.length &gt; 0) {
			shops.push(t[0].innerHTML);
			shids[t[0].innerHTML] = rows[i].id.substr(8);
		}
	}
}

shops.sort(function(a,b){ return a.localeCompare(b)});

pstr = &quot;&quot;;
for (i = 0; i &lt; shops.length; i++) {
	if (i &gt; 0) {
		pstr += &quot;-&quot;;
	}
	pstr += shids[shops[i]];
}

var p = document.createElement(&quot;DIV&quot;);
p.innerHTML = &quot;&lt;form name='frm' method='post' action='/money/category/storeCategory.rmi'&gt;&lt;input type='hidden' name='m' value='updateOrder'/&gt;&lt;input type='hidden' name='idList' value='&quot; + pstr + &quot;'/&gt;&lt;/form&gt;&quot;;
document.body.appendChild(p);
document.frm.submit();
</pre>
<p>以上脚本和修改为个人经验，如有任何问题欢迎提出指正哦，玩的开心~~~ ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/sort-feidee-store/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
