<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' version='2.0'><channel><atom:id>tag:blogger.com,1999:blog-7286295415331143255</atom:id><lastBuildDate>Fri, 04 Jul 2008 13:42:02 +0000</lastBuildDate><title>陆离斑「博」</title><description/><link>http://luliban.com/blog/</link><managingEditor>noreply@blogger.com (wyt)</managingEditor><generator>Blogger</generator><openSearch:totalResults>108</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-4901571834404718695</guid><pubDate>Fri, 04 Jul 2008 13:01:00 +0000</pubDate><atom:updated>2008-07-04T21:31:25.361+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>javascript</category><title>有备无患：浏览器端缓存（基于 jQuery）</title><description>&lt;p&gt;最近在翻《&lt;a rel="nofollow" title="Ajax 设计模式" href="http://www.douban.com/subject/2121078/"&gt;Ajax 设计模式&lt;/a&gt;》，所以想把其中的一部分模式实现出来练手 &lt;a rel="nofollow" title="jQuery" href="http://jquery.com/"&gt;jQuery&lt;/a&gt;，今天写了一个简单的引入 LRU 算法的浏览器端缓存（Browser Side Cache）。&lt;br /&gt;
&lt;br /&gt;
浏览器端缓存用来保留服务器返回的查询结果。这种缓存是一个 Javascript 里类似映射的对象，存储成对的查询结果；查询是缓存的键（Key），服务器返回的结果是缓存的值（Value）。因此，每当浏览器需要查询服务器时，先检查缓存。如果该查询是缓存中的一个键，则与键对应的值将被当作结果，而不必再向服务器查询。&lt;br /&gt;
&lt;br /&gt;
LRU （Least Recently Used）是将存储在缓存中的自上一次获取之后，最长时间未被使用的项目（Item）丢弃的一种算法。可以用两个数组（Array）来实现，其一是用来存储查询结果的键值对（Key-Value Pairs），其二是一个先进先出（FIFO）的队列，每一个新项目被塞入队列的尾部，并随着后续项目的跟进，逐渐逼近队列的头部。当队列全满时，每次向尾部塞入一个新项目，就要从头部弹出一个旧项目。而每当一个项目被缓存查询时，它会被送回到队列的尾部，这样可以确保最长时间未被使用的项目总是在开头处。&lt;br /&gt;
&lt;br /&gt;
另外，还有一种常用的缓存算法，LFU （Least Frequently Used），它将自上一次获取后最少被使用的项目丢弃。&lt;br /&gt;
&lt;br /&gt;
顺便一说，下面贴出的代码是由&lt;a id="umnt10" title="代码发芽网" href="http://www.fayaa.com/code/home/"&gt;代码发芽网&lt;/a&gt;生产的纯 HTML。代码发芽网是一个“无需插件支持 Blog 代码高亮，支持近百种编程语言，多种配色主题支持，代码版本管理”的代码片段管理网站。因为不是由 Javascript 脚本来高亮处理，所以在 feed 里也一样可以看到效果，这一点很赞。不过，由于用处不大的 id 和 class 也包含在生成的 HTML 里，所以体积偏大。比如说下面这段代码，在删除了多余的 id 和 class，套上代码专用的 &lt;a id="umnt11" rel="nofollow" title="pre" href="http://www.w3schools.com/TAGS/tag_pre.asp"&gt;pre&lt;/a&gt; 和 &lt;a id="umnt12" rel="nofollow" title="code" href="http://www.w3schools.com/TAGS/tag_phrase_elements.asp"&gt;code&lt;/a&gt; 标签，并将 &amp;amp;nbsp; 还原成空格之后，体积可以缩小一半仅17KB（原来35KB），更适合 blog 来贴代码。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;cache.js&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;&lt;span&gt;01&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt; &lt;span&gt;Cache&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;02&lt;/span&gt;     &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* A browser side LRU cache &lt;/span&gt;
&lt;span&gt;03&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * Author: Wu Yuntao &amp;lt;http://luliban.com/blog/&amp;gt;&lt;/span&gt;
&lt;span&gt;04&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * License: GPLv3&lt;/span&gt;
&lt;span&gt;05&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     *&lt;/span&gt;
&lt;span&gt;06&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * Usage:&lt;/span&gt;
&lt;span&gt;07&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * var cache = new Cache(10);   // create a new cache object&lt;/span&gt;
&lt;span&gt;08&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.put('w', 'wiki');      // put an item into cache&lt;/span&gt;
&lt;span&gt;09&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.get('w');              // get the value of item with key&lt;/span&gt;
&lt;span&gt;10&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.remove('w');           // remove an item with specified key&lt;/span&gt;
&lt;span&gt;11&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.initialize()           // re-initialize the cache&lt;/span&gt;
&lt;span&gt;12&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     * cache.size(10)               // resize the cache&lt;/span&gt;
&lt;span&gt;13&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;     */&lt;/span&gt;
&lt;span&gt;14&lt;/span&gt;     &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;15&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;16&lt;/span&gt; 
&lt;span&gt;17&lt;/span&gt; &lt;span&gt;Cache&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prototype&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;18&lt;/span&gt;     &lt;span&gt;initialize&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;19&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Initialize cache.&lt;/span&gt;
&lt;span&gt;20&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * ``size`` is the number of maxmium items this cache should hold.&lt;/span&gt;
&lt;span&gt;21&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * Default is maxium integer.&lt;/span&gt;
&lt;span&gt;22&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;23&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;new&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;Array&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;
&lt;span&gt;24&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;new&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;Array&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;
&lt;span&gt;25&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;||&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;||&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;Number&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MAX_VALUE&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;26&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;27&lt;/span&gt; 
&lt;span&gt;28&lt;/span&gt;     &lt;span&gt;is_empty&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;()&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;29&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Check if cache is empty&lt;/span&gt;
&lt;span&gt;30&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;31&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;0&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;32&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;33&lt;/span&gt; 
&lt;span&gt;34&lt;/span&gt;     &lt;span&gt;size&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;35&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Resize cache if ``size`` is specified, or return accual size of cache.&lt;/span&gt;
&lt;span&gt;36&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;37&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;typeof&lt;/span&gt; &lt;span&gt;size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'undefined'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;38&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_size&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;size&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;39&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;40&lt;/span&gt; 
&lt;span&gt;41&lt;/span&gt;     &lt;span&gt;put&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span&gt;value&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;42&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Put a new item into cache, if the size of cache reaches limit,&lt;/span&gt;
&lt;span&gt;43&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * cache will remove the least recently used (LRU) automatically.&lt;/span&gt;
&lt;span&gt;44&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * ``key`` of an item should be a string.&lt;/span&gt;
&lt;span&gt;45&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * ``value`` of an item could be anything, string, array or object.&lt;/span&gt;
&lt;span&gt;46&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * If ``value`` is not defined, returns null.&lt;/span&gt;
&lt;span&gt;47&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;48&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;typeof&lt;/span&gt; &lt;span&gt;value&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;!=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'undefined'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;49&lt;/span&gt;             &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;50&lt;/span&gt;             &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;value&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;51&lt;/span&gt; 
&lt;span&gt;52&lt;/span&gt;             &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;53&lt;/span&gt;                 &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;remove_least&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;
&lt;span&gt;54&lt;/span&gt;             &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;55&lt;/span&gt;         &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;56&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span&gt;value&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;57&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;58&lt;/span&gt; 
&lt;span&gt;59&lt;/span&gt;     &lt;span&gt;get&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;60&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Retrieve an item by its key and move it to the tail of cache.&lt;/span&gt;
&lt;span&gt;61&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * If the item of ``key`` does not exist, returns null.&lt;/span&gt;
&lt;span&gt;62&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;63&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;typeof&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'undefined'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;null&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;64&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;var&lt;/span&gt; &lt;span&gt;used_key&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_remove_key&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;65&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;used_key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;66&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;
&lt;span&gt;67&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;68&lt;/span&gt; 
&lt;span&gt;69&lt;/span&gt;     &lt;span&gt;remove_least&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;()&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;70&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Manually remove the least recently used item. */&lt;/span&gt;
&lt;span&gt;71&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;is_empty&lt;/span&gt;&lt;span&gt;())&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;0&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;
&lt;span&gt;72&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;73&lt;/span&gt; 
&lt;span&gt;74&lt;/span&gt;     &lt;span&gt;remove&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;75&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Remove an item by its key.&lt;/span&gt;
&lt;span&gt;76&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         * If the item of ``key`` does not exist, returns null.&lt;/span&gt;
&lt;span&gt;77&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;78&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;typeof&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;]&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'undefined'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;null&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;79&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_remove_key&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;80&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_remove_value&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;81&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;82&lt;/span&gt; 
&lt;span&gt;83&lt;/span&gt;     &lt;span&gt;_remove_key&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;84&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Remove the ``key`` in ``this._keys``.&lt;/span&gt;
&lt;span&gt;85&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;         */&lt;/span&gt;
&lt;span&gt;86&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;var&lt;/span&gt; &lt;span&gt;i&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span&gt;$&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;inArray&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;87&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_keys&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;splice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;
&lt;span&gt;88&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;
&lt;span&gt;89&lt;/span&gt; 
&lt;span&gt;90&lt;/span&gt;     &lt;span&gt;_remove_value&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
&lt;span&gt;91&lt;/span&gt;         &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;/* Remove the item in ``this._items``. */&lt;/span&gt;
&lt;span&gt;92&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;var&lt;/span&gt; &lt;span&gt;value&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;
&lt;span&gt;93&lt;/span&gt;         &lt;span&gt;delete&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_items&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;
&lt;span&gt;94&lt;/span&gt;         &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span&gt;value&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;
&lt;span&gt;95&lt;/span&gt;     &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;96&lt;/span&gt; 
&lt;span&gt;97&lt;/span&gt; &lt;span&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</description><link>http://luliban.com/blog/2008/07/jquery.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-1320338599645208961</guid><pubDate>Wed, 02 Jul 2008 11:41:00 +0000</pubDate><atom:updated>2008-07-02T19:51:15.003+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>python</category><title>有备无患：pinyin-urlify - 根据汉字自动生成拼音 URL</title><description>&lt;p&gt;根据文章标题中的英语单词自动生成文章的 URL 是一个很好的体验，比如标题为“&lt;a rel="nofollow" title="Girl's death sparks riot in China" href="http://edition.cnn.com/2008/WORLD/asiapcf/06/30/china.riots/index.html" id="hjgz"&gt;Girl's death sparks riot in China&lt;/a&gt;”，会被转换成“girls-death-sparks-riot-china”这样与之对应的 URL。这样既可获得 &lt;a title="Cool URI" rel="nofollow" href="http://www.w3.org/Provider/Style/URI" id="zl1v"&gt;Cool URIs&lt;/a&gt;，又便于用户记忆，同时也有利于 &lt;a rel="nofollow" title="SEO" href="http://www.chinamyhosting.com/seoblog/2006/08/19/whats-seo/" id="s92m"&gt;SEO&lt;/a&gt;，&lt;a rel="nofollow" title="Blogger" href="http://www.blogger.com" id="ied5"&gt;Blogger&lt;/a&gt; 和 &lt;a rel="nofollow" title="Wordpress" href="http://www.wordpress.com/" id="w-.u"&gt;Wordpress&lt;/a&gt; 也都支持类似体验。而中文的文章标题，也可以先将其中的汉字转化为拼音后再组成相应的 URL，比如标题为“&lt;a rel="nofollow" title="俯卧撑" href="http://www.google.com/search?%E4%BF%AF%E5%8D%A7%E6%92%91+%E7%93%AE%E5%AE%89" id="fgnm"&gt;俯卧撑&lt;/a&gt;”，可以被转换成“fu-wo-cheng”。&lt;br /&gt;
&lt;br /&gt;
&lt;a title="pinyin-urlify" href="http://code.google.com/p/pinyin-urlify/" id="hof4"&gt;pinyin-urlify&lt;/a&gt; 是我这几天写的能根据汉字自动生成拼音 URL 的 Python 脚本。它可以将汉字映射成有英语字母组成的拼音，和多种西方语言的字母（拉丁语、希腊语、俄语等）映射成相近的英语字母。要说明的是，汉字-拼音的映射表是从 &lt;a title="pyzh 项目" href="http://code.google.com/p/pyzh/" id="l_q."&gt;pyzh 项目&lt;/a&gt;中获得，而其他西方语言的映射表则是从 &lt;a title="Django 项目" href="http://www.djangoproject.com/" id="z5sj"&gt;Django 项目&lt;/a&gt;中获得，&lt;b id="mrrn"&gt;特表感谢&lt;/b&gt;。&lt;br /&gt;
&lt;br /&gt;
pinyin-urlify 支持自定义停用词列表（stop words）和保留词列表（reserved words）。停用词列表中的单词都会被过滤，不会出现在生成的 URL 中。生成的 URL 如果和保留词列表中的某一个单词匹配，则会被替换成默认的 URL 字符串。pinyin-urlify 可以指定 URL 的最大长度。不过，如果遇到有较多汉字组成的文章标题的话，实际生成的 URL 可能会比限定的最大长度小一些。我希望能在以后修正这一点。&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;使用示例&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from urlify import urlify
&amp;gt;&amp;gt;&amp;gt; urlify(u'三个俯卧撑引发的血案', default='blog_post',
...        max_length=50, stop_words=[u'is', u'a', u'an'],
...        reserved_words=[u'new', u'edit'])
u'san-ge-fu-wo-cheng-yin-fa-de-xue'&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
欢迎大家用 svn 检出 pinyin-urlify 的副本来使用。欢迎任何意见或建议 :-)&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;$ svn checkout http://pinyin-urlify.googlecode.com/svn/trunk/ pinyin-urlify&lt;/code&gt;&lt;/pre&gt;</description><link>http://luliban.com/blog/2008/07/pinyin-urlify-url.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-2921259291670305060</guid><pubDate>Sat, 21 Jun 2008 05:23:00 +0000</pubDate><atom:updated>2008-06-21T16:36:06.662+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>linux</category><category domain='http://www.blogger.com/atom/ns#'>git</category><title>有备无患：在 GitHub 上用不同的 SSH 公钥部署多个应用</title><description>&lt;p&gt;
&lt;a href="http://github.com" title="GitHub" rel="nofollow"&gt;GitHub&lt;/a&gt; 是一个免费的基于 git 的开源软件仓库托管服务，包括 &lt;a href="http://github.com/rails/rails/tree" rel="nofollow"&gt;Ruby on Rails&lt;/a&gt;， &lt;a href="http://github.com/dchelimsky/rspec/tree" rel="nofollow"&gt;Rspec&lt;/a&gt;，&lt;a href="http://github.com/sstephenson/prototype/tree" rel="nofollow"&gt;Prototype&lt;/a&gt; 在内的许多开源软件都在 GitHub 上架设了软件仓库。
&lt;/p&gt;

&lt;br /&gt;

&lt;p&gt;
如果你有两个或两个以上的 GitHub 仓库，并且想用不同的公钥／私钥来管理这些仓库的话，会遇到这样一个问题。当你 clone 第一个仓库的时候，git 会使用第一对公钥／私钥，这个时候仍然一切正常。可当你需要用另一对公钥／私钥来 clone 第二个仓库的时候，git 默认仍然会使用第一对公钥／私钥，然后 GitHub 就会返回一个授权错误。
&lt;/p&gt;

&lt;br /&gt;

&lt;p&gt;要解决这个问题，首先需要在 ~/.ssh/ 目录下新建一个 config 文件，为不同的项目分别指定公钥／私钥&lt;/p&gt;

&lt;h3&gt;~/.ssh/config&lt;/h3&gt;
&lt;pre class="codes"&gt;&lt;code&gt;Host project-1
    HostName github.com
    User git
    IdentityFile /home/wyt/.ssh/id_rsa_project_1

Host project-2
    HostName github.com
    User git
    IdentityFile /home/wyt/.ssh/id_rsa_project_2
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

&lt;p&gt;然后用 ssh-keygen 创建对应的公钥／私钥。&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ ls -l ~/.ssh&lt;/b&gt;
total 24
-rw-r--r-- 1 wyt wyt  244 2008-06-21 13:18 config
-rw------- 1 wyt wyt 1675 2008-06-21 13:04 id_rsa_project_1
-rw-r--r-- 1 wyt wyt  392 2008-06-21 13:04 id_rsa_project_1.pub
-rw------- 1 wyt wyt 1675 2008-05-20 11:24 id_rsa_project_2
-rw-r--r-- 1 wyt wyt  392 2008-05-20 11:24 id_rsa_project_2.pub
-rw-r--r-- 1 wyt wyt  802 2008-05-24 22:44 known_hosts
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

&lt;p&gt;将公钥上传到 GitHub 上之后，就可以用下面的指令 clone 相应的软件仓库了&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git clone git@project-1:username/project-1.git
$ git clone git@project-2:username/project-2.git&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
&lt;br /&gt;

&lt;h3&gt;参考页面&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://alblue.blogspot.com/2005/08/howto-ssh-logins-using-keys.html" title="[HowTo] SSH logins using keys"&gt;[HowTo] SSH logins using keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/guides/multiple-github-accounts" title="Multiple GitHub Accounts"&gt;Multiple GitHub Accounts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://logicalawesome.lighthouseapp.com/projects/8570-github/tickets/515-deploying-mulitple-apps-to-the-same-machine-public-key-restrictions#ticket-515-3" title="Deploying Mulitple Apps to the Same Machine Public Key Restrictions"&gt;Deploying Mulitple Apps to the Same Machine Public Key Restrictions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://luliban.com/blog/2008/06/github-ssh.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-3876905514274792940</guid><pubDate>Tue, 17 Jun 2008 15:59:00 +0000</pubDate><atom:updated>2008-06-18T00:06:27.637+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>互联网</category><title>悠言悠闲：Firefox 下载日</title><description>&lt;a href="http://www.spreadfirefox.com/en-US/worldrecord" &gt;&lt;img border="0" alt="Download Day 2008" title="Download Day 2008" src="http://www.spreadfirefox.com/sites/all/themes/spreadfirefox_RCS/images/download-day/buttons/en-US/dday_badge_fox.png" /&gt;&lt;/a&gt;

&lt;p&gt;Firefox 3正式下载时间为北京时间六月十八日凌晨一点。下载日活动届时开始，请在北京时间六月十九日凌晨一点之前下载Firefox 3。&lt;/p&gt;</description><link>http://luliban.com/blog/2008/06/firefox.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-4964011711521755373</guid><pubDate>Tue, 27 May 2008 13:00:00 +0000</pubDate><atom:updated>2008-06-01T14:47:35.068+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>javascript</category><category domain='http://www.blogger.com/atom/ns#'>greasemonkey</category><category domain='http://www.blogger.com/atom/ns#'>豆瓣</category><title>有备无患：增强豆瓣收藏对话框的 Greasemonkey 脚本</title><description>&lt;p&gt;经常使用 &lt;a href="http://del.icio.us/" rel="nofollow"&gt;del.icio.us&lt;/a&gt; 的人，一定也很熟悉 del.icio.us 的跟随键盘输入自动提示标签（Tag Suggest）的功能。它为用户管理大量的标签带来方便，比如不必去费神记忆单词的拼写，避免为同一语义定义多个标签等。这是靠我们的大脑来记忆管理，或者类似&lt;a rel="nofollow" href="http://www.douban.com/"&gt;豆瓣&lt;/a&gt;只提供10个最常用的标签，不能做到的。&lt;br /&gt;

&lt;br /&gt;

这个 &lt;a rel="nofollow" href="http://addons.mozilla.org/firefox/addon/748"&gt;Greasemonkey&lt;/a&gt; 脚本的主要功能，是为豆瓣收藏对话框提供类似 del.icio.us 的标签提示功能。另外还重新布置了读过／在读／想读等按钮的布局，并添加了删除收藏的按钮。&lt;br /&gt;

&lt;br /&gt;

目前脚本的版本为0.1，欢迎试用。有什么建议或意见，&lt;a href="http://luliban.com/blog/2008/05/enhanced-douban-collecting-dialog.html#comments"&gt;欢迎留言&lt;/a&gt;:-)&lt;/p&gt;

&lt;br /&gt;
&lt;div&gt;&lt;a href="http://luliban.com/blog/uploaded_images/screenshot-enhanced-douban-collecting-dialog.png" title="屏幕截图"&gt;&lt;img src="http://luliban.com/blog/uploaded_images/screenshot-enhanced-douban-collecting-dialog.png" alt="Screenshot - Enhanced Douban Collecting Dialog" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;br /&gt;

&lt;h3&gt;功能&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;类似 &lt;a href="http://del.icio.us/" title="Del.icio.us" rel="nofollow"&gt;del.icio.us&lt;/a&gt; 的随输入自动提示标签（Tag Suggest）&lt;/li&gt;
&lt;li&gt;重新安排了读过／在读／想读等按钮的布局，保证 UI 路径的一致性&lt;/li&gt;
&lt;li&gt;增加了删除收藏的按钮&lt;/li&gt;
&lt;/ul&gt;

&lt;br /&gt;
&lt;h3&gt;下载&lt;/h3&gt;
&lt;a href="http://userscripts.org/scripts/show/27345" title="Enhanced Douban Collecting Dialog"&gt;&lt;img style="border: none;" src="http://luliban.com/images/download90.gif" alt="下载脚本" /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;2008-05-31 UPDATE&lt;/h4&gt;
&lt;p&gt;脚本更新至0.2，有下列更新&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;增加了对个人收藏页，标签页，豆瓣猜，TOP250等页面中的收藏对话框的支持&lt;/li&gt;
&lt;li&gt;修正了之前脚本中已知的 BUG&lt;/li&gt;
&lt;/ul&gt;</description><link>http://luliban.com/blog/2008/05/enhanced-douban-collecting-dialog.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-6968119548310588834</guid><pubDate>Mon, 19 May 2008 14:02:00 +0000</pubDate><atom:updated>2008-06-21T16:31:21.132+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>gentoo</category><category domain='http://www.blogger.com/atom/ns#'>linux</category><category domain='http://www.blogger.com/atom/ns#'>git</category><title>有备无患：在 Gentoo 上部署 Git + Gitosis 服务器的笔记</title><description>&lt;a title="Git" href="http://git.or.cz/" rel="nofollow"&gt;Git&lt;/a&gt; 是 &lt;a rel="nofollow" href="http://en.wikipedia.org/wiki/Linus_Torvalds" title="Linus Torvalds"&gt;Linus Torvalds&lt;/a&gt; 为了帮助管理 Linux 内核开发而开发的分布式版本控制软件（Distributed SCM）。Git 汲取了 Torvalds 在维护大型的分布式项目开发方面的经验和对文件系统性能的丰富知识，正如其文档所描述的，“是一个快速、可扩展的分布式版本控制系统，它具有极为丰富的命令集，对内部系统提供了高级操作和完全访问。”目前，Linux 内核、X.org 服务器和 Ruby on Rails 等开源项目的版本控制系统都已经切换到 Git。&lt;br /&gt;

&lt;br /&gt;

&lt;a rel="nofollow" title="Gitosis" href="http://eagain.net/gitweb/?p=gitosis.git;a=summary"&gt;Gitosis&lt;/a&gt; 是 &lt;a rel="nofollow" title="Tommi Virtanen" href="http://eagain.net/about/"&gt;Tommi Virtanen&lt;/a&gt; 为了更方便和安全的辅助 Git 架设和管理软件版本库 (Software Repository) 而开发的工具软件。虽然 Git 本身也提供 git-daemon 以架设版本库，但在用户访问控制上做的并不严格。而 Gitosis 允许单个用户帐号管理多个版本库，使用 SSH keys 管理用户认证，不需要 shell 帐号就可以解决多用户访问集中版本库的问题。&lt;br /&gt;

&lt;br /&gt;

需要说明的是，我使用的 Gitosis 是 &lt;a rel="nofollow" title="robbat2" href="http://robbat2.livejournal.com/"&gt;robbat2&lt;/a&gt; 为了部署新的 &lt;a rel="nofollow" title="Gentoo Overlays" href="http://git.overlays.gentoo.org/"&gt;Gentoo Overlays&lt;/a&gt; 而开发的 &lt;a rel="nofollow" title="Gentoo 分支版本" href="http://git.overlays.gentoo.org/gitweb/?p=proj/gitosis-gentoo.git"&gt;Gentoo 分支版本&lt;/a&gt; (Gentoo Fork Version)，和上游版本 (Origin Upstream Version) 相比有如下不同：&lt;br /&gt;

&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;支持 git+ssh://HOST/REPO 风格的相对路径；&lt;/li&gt;
&lt;li&gt;支持以命令行参数方式导入 keys，如 &lt;b&gt;gitosis-init --adminkey=FILE --adminname=STRING；&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;智能处理 SSH keys，支持 SSH1 和 SSH2 keys；&lt;/li&gt;
&lt;li&gt;将上游版本默认的目录权限 0750 更改为 0755，以便用 nobody:nobody 运行 git-daemon。&lt;/li&gt;
&lt;/ul&gt;

&lt;br /&gt;
这篇文章详细记录了我在本地安装和部署 Git + Gitosis 的过程和遇到的问题，希望为大家提供一些参考。欢迎 Fix-Me :-)&lt;br /&gt;


&lt;br /&gt;


&lt;h3&gt;安装 Git + Gitosis&lt;/h3&gt;

安装 git 和 gitosis-gentoo。安装完后，Gentoo 会自动添加 git 用户和组，并将版本库的主目录设为 /var/spool/gitosis/repositories。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;# echo "&amp;gt;=dev-util/git-1.5.5" &amp;gt;&amp;gt; /etc/portage/package.keywords
# echo "&amp;gt;=dev-util/gitosis-gentoo-0.2_p20080203" &amp;gt;&amp;gt; /etc/portage/package.keywords
# emerge -av git gitosis-gentoo&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

如果你的主机上没有装 SSH 的话，还要安装 openssh， 以及 keychain 用来管理 ssh-agents（可选）。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;# emerge -av openssh keychain&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

安装完后，启动 sshd，并加入默认启动。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;# /etc/init.d/sshd start
# rc-update add sshd default&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;h3&gt;配置 Gitosis&lt;/h3&gt;

首先生成一个 ssh key。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ ssh-keygen -t rsa&lt;/b&gt;
Generating public/private rsa key pair.
Enter file in which to save the key (/home/wyt/.ssh/id_rsa):（回车）
Created directory '/home/wyt/.ssh'.
Enter passphrase (empty for no passphrase):（直接回车）
Enter same passphrase again: （直接回车）
Your identification has been saved in /home/wyt/.ssh/id_rsa.
Your public key has been saved in /home/wyt/.ssh/id_rsa.pub.
The key fingerprint is:
dd:33:cc:99:33:00:66:77:ff:00:bb:99:00:22:55:88 wyt@gentoo
&lt;/code&gt;&lt;/pre&gt;


&lt;br /&gt;
我把 ssh key 保存在默认的 ~/.ssh/id_rsa，你也可以选择其他地方。接下来把 id_rsa.pub 上传到服务器。其中的root@gentoo，root 是根用户名，gentoo 是主机名，可以从 /etc/conf.d/hostname 得知。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cat /etc/conf.d/hostname&lt;/b&gt;
hostname="gentoo"
&lt;b&gt;$ scp ~/.ssh/id_rsa.pub root@gentoo:&lt;/b&gt;
Password: （输入 root 帐户密码）
id_rsa.pub&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 100%&amp;nbsp; 392&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0.4KB/s&amp;nbsp;&amp;nbsp; 00:00&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
上传完毕后，用 gitosis-init 初始化版本库。&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ sudo su -
# sudo -H -u git gitosis-init &amp;lt; ~/id_rsa.pub&lt;/b&gt;
Initialized empty Git repository in ./
Reinitialized existing Git repository in ./
&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
上面第二条命令中，-u 表示 sudo 将切换至 git 用户，-H 表示 sudo 将切换至该用户的主目录。初始化完成后，将创建一个名为 gitosis-admin 的版本库，用于 Gitosis 的设置和管理。你需要把它 clone 到相应目录。&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ mkdir git
$ cd git
$  git clone git@gentoo:gitosis-admin.git
$ cd gitosis-admin
$ ls -lh&lt;/b&gt;
-rw-r--r-- 1 wyt wyt 80 2008-05-19 00:10 gitosis.conf
drwxr-xr-x 2 wyt wyt 80 2008-05-19 00:10 keydir&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
gitosis-admin 目录下有一个 gitosis.conf 文件和一个 keydir 目录。gitosis.conf 用来保存项目，用户和权限等版本库配置，而 keydir 用来保存用户的 ssh key。因为 gitosis-admin 也是 Git 的版本库，所以在修改完配置之后，只需要简单的 commit &amp;amp; push，就可以很快很方便的更新服务器的配置了。接下来就用 Gitosis 创建新版本库和添加授权用户。&lt;br /&gt;

&lt;br /&gt;

&lt;h3&gt;创建新的版本库&lt;/h3&gt;
gitosis.conf 里有 gitosis-admin 的默认配置，只要依样画葫芦创建一个新版本库即可。下面举例创建 local_test 版本库，把下面这段 code 追加到 gitosis.conf。&lt;br /&gt;

&lt;br /&gt;

&lt;h4&gt;gitosis.conf&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;[group localteam]
writable = local_test
members = wyt@gentoo
&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

这段 code 定义了新的版本库，包括开发小组的名称 "localteam"，版本库的名称 "local_test"，以及拥有写权限的小组成员 "wyt@gentoo"。接下来把新的版本库所配置 commit &amp;amp; push 到服务器上。&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git commit -a -m "添加新的版本库 local_test"
$ git push&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
接下来在本地创建版本库，然后将其上传到服务器&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ mkdir local_test
$ cd local_test
$ git init
$ git remote add origin git@gentoo:local_test.git&lt;/b&gt;

# add &amp;amp; commit 一些文件，然后……

&lt;b&gt;$ git push origin master:refs/heads/master&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;h3&gt;添加成员用户&lt;/h3&gt;
添加版本库的成员用户的步骤可以分为两步。一是将用户的 SSH 公钥复制到 keydir/ 目录。二是编辑 gitosis.conf，将这些用户的名字加入 "members"。&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cd gitosis-admin/
$ cp ~/zhangsan@gentoo.pub keydir/
$ cp ~/lisi@gentoo.pub keydir/
$ git add keydir/zhangsan@gentoo.pub keydir/lisi@gentoo.pub&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

编辑 gitosis.conf&lt;br /&gt;

&lt;br /&gt;
&lt;h4&gt;gitosis.conf&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;[group localteam]
writable = local_test
members = wyt@gentoo zhangsan@gentoo lisi@gentoo&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
接下来，将设置提交到服务器&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git commit -a -m '添加 local_test 版本库成员：张三、李四'
$ git push&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;选择需要输出的版本库&lt;/h3&gt;
&lt;br /&gt;
&lt;h4&gt;输出所有版本库&lt;/h4&gt;
编辑 /etc/conf.d/git-daemon&lt;br /&gt;
&lt;pre class="codes"&gt;&lt;code&gt;GITDAEMON_OPTS="--syslog --base-path=/var/spool/gitosis/repositories/ --export-all"&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;h4&gt;选择输出版本库&lt;/h4&gt;

编辑 /etc/conf.d/git-daemon&lt;br /&gt;

&lt;pre class="codes"&gt;&lt;code&gt;GITDAEMON_OPTS="--syslog --base-path=/var/spool/gitosis/repositories/"&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
然后在每个选择输出的版本库目录中，添加一个 git-daemon-export-ok 文件&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ sudo touch /var/spool/gitosis/repositories/local_test.git/git-daemon-export-ok&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;启动 git-daemon&lt;/h3&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;# /etc/init.d/git-daemon start
# rc-update add git-daemon default&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
如果一切正常的话，我们应该已经大功告成，完成部署 Git + Gitosis 服务器了。现在可以试一下远程 clone。&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git clone git://169.254.64.95/local_test.git&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;参考链接&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://technicalpickles.com/posts/gitosis-on-gentoo" title="Gitosis on Gentoo @ Technical Pickles"&gt;Gitosis on Gentoo @ Technical Pickles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://gentoo-wiki.com/Gitosis" title="Gitosis - Gentoo Linux Wiki"&gt;Gitosis - Gentoo Linux Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way" title="Hosting Git repositories, The Easy (and Secure) Way"&gt;Hosting Git repositories, The Easy (and Secure) Way&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" href="http://robbat2.livejournal.com/217665.html" title="robbat2: Gitosis on Gentoo, important notes"&gt;robbat2: Gitosis on Gentoo, important notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="nofollow" title="Git in a Nutshell" href="http://www.chem.helsinki.fi/%7Ejonas/git_guides/HTML/git_guide/git_guide.html"&gt;Git in a Nutshell&lt;/a&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;2008-05-20 UPDATE&lt;/h4&gt;
clone 只读版本库：&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git clone git://169.254.64.95/local_test.git&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

clone 可读写版本库：&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git clone git+ssh://git@169.254.64.95/local_test.git&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;

创建新的可读写版本库：&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ git remote add origin git+ssh://git@169.254.64.95/new_local_test.git
$ git push origin master:refs/heads/master&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;</description><link>http://luliban.com/blog/2008/05/gentoo-git-gitosis.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-1472011599496106035</guid><pubDate>Sat, 10 May 2008 08:24:00 +0000</pubDate><atom:updated>2008-06-26T20:01:01.256+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>blogger</category><category domain='http://www.blogger.com/atom/ns#'>google</category><title>有备无患：绕过 GHS 用自定义域名发布 Blogger</title><description>2007年1月，&lt;a rel="nofollow" title="Google Blogger" href="http://www.blogger.com"&gt;Google Blogger&lt;/a&gt; 的自定义域名功能推出不到一个星期，就&lt;a rel="nofollow" title="Blogger的自定义域名功能被废" href="http://www.gseeker.com/50226711/bloggerceaeee_56005.php"&gt;被 GFW 给废了&lt;/a&gt;。原因就是 GFW 隔断了国内用户对 Google 的免费主机托管服务 ghs.google.com 的访问。&lt;br /&gt;

&lt;br /&gt;

今天试着将 App Engine 应用部署到自己域名的时候，发现 App Engine 不需要将 CNAME record 指向 ghs.google.com，从而可以避开 GFW。而&lt;a rel="nofollow" title="App Engine URLs" href="http://www.google.com/support/a/bin/answer.py?hl=en&amp;answer=91080"&gt;同样的方法&lt;/a&gt;也可以适用于 Blogger。方法如下：&lt;br /&gt;

&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;
把 Blogger 的发布方式切换成“自定义域名（Custom Domain）”。在“你的域名（Your Domain）”一栏中填上域名，比如，py.thonic.org。保存设置（Save Settings）。&lt;/li&gt;

&lt;li&gt;在你的域名注册商那里，修改域名（thonic.org）的 DNS 设置。为子域名（py）添加一条 A record，指向下面四个 IP 地址的其中之一。举例来说，就是添加这样一条记录：&lt;b&gt;py.thonic.org  86400  IN  A  216.239.34.21&lt;/b&gt;。
&lt;ul&gt;
&lt;li&gt;216.239.32.21&lt;/li&gt;
&lt;li&gt;216.239.34.21&lt;/li&gt;
&lt;li&gt;216.239.36.21&lt;/li&gt;
&lt;li&gt;216.239.38.21&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;

&lt;/ol&gt;

&lt;br /&gt;

DNS 设置的改动需要一段时间来完成，一般不会超过24小时。这样就可以重新使用 Blogger 的自定义域名功能了，只是，这次又可以持续多久呢？

&lt;br /&gt;
&lt;br /&gt;

&lt;h4&gt;2008-06-26 UPDATE&lt;/h4&gt;
&lt;p&gt;我把上面的 IP 绑定到自己的域名了，ghs.luliban.com。以后也可以像 ghs.google.com 一样，添加一条 CNAME 记录到 ghs.luliban.com 来绑定 Google Blogger 或者 Google Apps 了。&lt;/p&gt;</description><link>http://luliban.com/blog/2008/05/ghs-blogger.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-855068127276409148</guid><pubDate>Fri, 09 May 2008 03:01:00 +0000</pubDate><atom:updated>2008-05-09T12:57:05.161+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>python</category><category domain='http://www.blogger.com/atom/ns#'>豆瓣</category><category domain='http://www.blogger.com/atom/ns#'>google</category><title>有备无患：在 Google App Engine上应用豆瓣 API 的授权认证</title><description>上次说到了&lt;a title="有备无患：在 Google App Engine 上应用豆瓣 Python 客户端" href="http://luliban.com/blog/2008/04/google-app-engine-python.html"&gt;在 App Engine 上应用的豆瓣的 Python 客户端&lt;/a&gt;，但是那个版本（&lt;a title="douban-python-0.1.1" href="http://code.google.com/p/douban-python/" rel="nofollow"&gt;0.1.1&lt;/a&gt;）的客户端并没有包括 OAuth 授权认证的功能。如果想要让用户授权，以访问那些受保护的资源，以及添加、修改或删除用户的收藏，需要从豆瓣 Python 客户端的 SVN 中获取最新的开发版本（&lt;a title="r22" href="http://code.google.com/p/douban-python/source/detail?r=22" rel="nofollow"&gt;r22&lt;/a&gt;）。&lt;a title="trunk里的oauth client完成了多少？" href="http://www.douban.com/group/topic/3073216/" rel="nofollow"&gt;据豆瓣的开发人员 hongqn 说&lt;/a&gt;，OAuth Client 基本开发完毕，已经进入内测 bug 的阶段。&lt;br /&gt;

&lt;br /&gt;

和 GData Python 客户端一样，开发版本的 OAuth Client 也是用 Python 自带的 httplib 模块来处理 HTTP 请求，所以原始的客户端不能直接在 App Engine 上使用，必须先将 httplib 替换成 &lt;a title="urlfetch" href="http://code.google.com/appengine/docs/urlfetch/overview.html" rel="nofollow"&gt;urlfetch&lt;/a&gt;。现在只需要修改两个函数，但是豆瓣如果能像 GData Python 客户端一样把使用 httplib 的部分封装起来，甚至提供一个使用 urlfetch 的替换模块就更好了，希望豆瓣能采纳&lt;a title="oauth client用了httplib，可是appengine不支持httplib" href="http://www.douban.com/group/topic/3088924/" rel="nofollow"&gt;这个建议&lt;/a&gt;。下面是具体步骤，如果有什么问题，还请留言告知。&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;1，从 trunk 中 checkout 最新的豆瓣 Python 客户端开发版本&lt;/b&gt;&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ svn co http://douban-python.googlecode.com/svn/trunk/ douban-python/
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;b&gt;2，修改客户端的 OAuth Client&lt;/b&gt;&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cp douban-python/douban ~/doupye/douban -rf
$ cd ~/doupye/douban/
$ gvim client.py
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;h4&gt;client.py&lt;/h4&gt;
&lt;pre class="codes"&gt;&lt;code&gt;# import httplib
from google.appengine.api import urlfetch

class OAuthClient:
    ... ...
    def fetch_token(self, oauth_request):
        # 被注释掉的是原来使用 httplib 的部分
        # connection = httplib.HTTPConnection("%s:%d" % (self.server, 80))
        # connection.request('GET', oauth_request.http_url,
        #     headers=oauth_request.to_header())
        # response = connection.getresponse()
        # r = response.read()
        url = oauth_request.http_url
        result = urlfetch.fetch(url, headers=oauth_request.to_header())
        r = result.content
        ... ...

    def access_resource(self, method, url, body=None):
        ... ...
        # connection = httplib.HTTPConnection("%s:%d" % (self.server, 80))
        # connection.request(method, url, body=body,
        #     headers=headers)
        # return connection.getresponse()
        result = urlfetch.fetch(url, payload=body, method=method, headers=headers)
        return result.content
&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;b&gt;3，使用 OAuth 授权的过程如下，在 App Engine SDK 提供的控制台（&lt;a title="Interactive Console" href="http://localhost:8080/_ah/admin/interactive" rel="nofollow"&gt;Interactive Console&lt;/a&gt;）中运行：&lt;/b&gt;&lt;br /&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;from douban.client import OAuthClient

client = OAuthClient(key=MY_API_KEY, secret=MY_SECRET)&lt;/b&gt;

# 获取未授权的Request Token
&lt;b&gt;key, secret = client.get_request_token()
print key, secret&lt;/b&gt;
&gt;&gt;&gt; c14023315549fe3743c17993ff4dfaa5 91af6245103ec3b7

# 获取请求用户授权的页面的 URL
&lt;b&gt;url = client.get_authorization_url(key, secret)
print url&lt;/b&gt;
&gt;&gt;&gt; http://www.douban.com/service/auth/authorize?oauth_token=a9e487ac36e0ba9efdba970534a22fce

# 将 URL 复制到浏览器中，用户可以选择同意或者拒绝授权

# 用户完成授权后，使用授权后的 Request Token 换取 Access Token
&lt;b&gt;key, secret = client.get_access_token(key, secret)
if key:&lt;/b&gt;
    # 使用 Access Token 登录
    &lt;b&gt;login = client.login(key, secret)
    print login&lt;/b&gt;
&gt;&gt;&gt; True

# 访问受保护资源
&lt;b&gt;collections = client.access_resource(method='GET',
    url='http://api.douban.com/people/wyt/collection?cat=book')
for entry in collections.entry:
    print entry.title.text&lt;/b&gt;
&gt;&gt;&gt;
听过 人として軸がぶれている
想读 Antipatterns
想读 新企业的起源与演进
看过 .hack//G.U. Trilogy
想听 ワイルドストロベリー
在听 The Flower Book
在听 E=MC²
听过 越长大越孤单
想读 Investing 101
想读 The Ecology of Commerce
&lt;/code&gt;&lt;/pre&gt;</description><link>http://luliban.com/blog/2008/05/google-app-engine-api.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-3065362534680213650</guid><pubDate>Tue, 22 Apr 2008 12:34:00 +0000</pubDate><atom:updated>2008-04-22T21:29:02.948+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>nba</category><category domain='http://www.blogger.com/atom/ns#'>火箭</category><title>悠言悠闲：火箭不死心还在</title><description>&lt;div&gt;
&lt;object type="application/x-shockwave-flash" width="425px" height="350px" data="http://www.tudou.com/v/Bu_qZGa_upQ"&gt;&lt;param name="movie" value="http://www.tudou.com/v/Bu_qZGa_upQ" /&gt;&lt;/object&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;
&lt;a rel="nofollow" title="不死心还在" href="http://www.douban.com/subject/2969163/"&gt;不死心还在&lt;/a&gt;。下午听到这首歌，真的很合适给今天的火箭当主题曲。即使被ESPN的所有“专家”&lt;a rel="nofollow" href="http://sports.espn.go.com/nba/playoffs2008/series?series=houuth"&gt;一致通过必输爵士&lt;/a&gt;，即使被CCTV“内定”必输所以一场比赛都不转播，即使有一半以上的休斯敦球迷都不相信他们能通过第一轮，即使被世界所背叛，他们也会一步一步的走向胜利的日子。知道T-Mac接受采访时最常说的一句话是什么？我不在乎人们怎么说。只有甘于忍受寂寞和敢与世界为敌的决心，才是让休斯敦火箭，让 Tracy McGrady、Shane Battier、Luis Scola、Dikembe Mutombo、Bobby Jackson、Chuck Hayes、Aaron Brooks 以及 Rick Adelman，能够坚持与爵士为之一战的精神支柱。&lt;/p&gt;</description><link>http://luliban.com/blog/2008/04/blog-post_22.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-4319665207803818452</guid><pubDate>Mon, 21 Apr 2008 09:27:00 +0000</pubDate><atom:updated>2008-05-08T09:58:19.072+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>python</category><category domain='http://www.blogger.com/atom/ns#'>豆瓣</category><category domain='http://www.blogger.com/atom/ns#'>google</category><title>有备无患：在 Google App Engine 上应用豆瓣 Python 客户端</title><description>&lt;div&gt;

&lt;a href="http://luliban.com/blog/uploaded_images/douban-gae.png" title="有备无患：在 Google App Engine 上应用豆瓣 Python 客户端"&gt;
&lt;img src="http://luliban.com/blog/uploaded_images/douban-gae.png" alt="有备无患：在 Google App Engine 上应用豆瓣 Python 客户端" /&gt;
&lt;/a&gt;

&lt;/div&gt;


&lt;p&gt;


&lt;a rel="nofollow" title="Google App Engine" href="http://appengine.google.com/"&gt;Google App Engine&lt;/a&gt; 是 Google 四月初推出的一个网络应用开发平台，它提供了一体化的分布式服务器群、供快速开发的网络应用开发框架、最多500MB的数据存储，以及可自动升级的后台流量统计服务。换言之，App Engine 让开发人员专注于应用本身，Google 将提供应用运行及维护所需要的一切平台资源。&lt;br /&gt;

&lt;br /&gt;

App Engine 目前只支持 Python 作为唯一的开发语言（wyt：谁让“Python 之父” &lt;a rel="nofollow" title="Guido van Rossum" href="http://www.python.org/%7Eguido/"&gt;Guido van Rossum&lt;/a&gt; 也在 Google 工作呢，近水楼台先得月）。所以，如果想在 App Engine 上利用&lt;a rel="nofollow" title="豆瓣" href="http://www.douban.com" &gt;豆瓣&lt;/a&gt;开放的书影音和用户数据，我们可以使用豆瓣提供的 &lt;a rel="nofollow" title="Python 客户端" href="http://code.google.com/p/douban-python/"&gt;Python 客户端&lt;/a&gt;来开发。另外，由于以前的 &lt;a rel="nofollow" title="GData Python 客户端" href="http://code.google.com/p/gdata-python-client/" id="ad6t"&gt;GData Python 客户端&lt;/a&gt; 都是用 httplib 模块来处理 HTTP 请求，而 App Engine 则规定必须通过其自带的 &lt;a rel="nofollow" title="urlfetch" href="http://code.google.com/appengine/docs/urlfetch/overview.html" id="liy5"&gt;urlfetch&lt;/a&gt; 才能实现，所以为了让豆瓣 Python 客户端所必须的 gdata.service 模块能够正常的在 App Engine 上工作，我们还需要版本号大于1.0.12.1的 gdata-python-client。下面是具体步骤，如果有什么问题，还请留言告知。&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;1，下载 &lt;a title="gdata.py-1.0.12.1.tar.gz" rel="nofollow" href="http://gdata-python-client.googlecode.com/files/gdata.py-1.0.12.1.tar.gz" id="wlnh"&gt;gdata.py-1.0.12.1.tar.gz&lt;/a&gt;&lt;br /&gt;

2，解压缩到当前目录，并编译 gdata&lt;/b&gt;&lt;br /&gt;&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ tar xf gdata.py-1.0.12.1.tar.gz
$ cd gdata.py-1.0.12.1/
$ ./setup.py build
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
&lt;p&gt;&lt;b&gt;
3，将编译好的 atom 和 gdata 目录复制到项目目录 ~/doupye/
&lt;/b&gt;&lt;/p&gt;


&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cp build/lib/* ~/doupye/&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;p&gt;&lt;b&gt;
4，下载 &lt;a rel="nofollow" title="douban-python-0.1.1.tar.gz" href="http://douban-python.googlecode.com/files/douban-python-0.1.1.tar.gz" &gt;douban-python-0.1.1.tar.gz&lt;/a&gt;&lt;br /&gt;


5，解压缩到当前目录，并编译 douban-python
&lt;/b&gt;&lt;/p&gt;


&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cd ..
$ tar xf douban-python-0.1.1.tar.gz
$ cd douban-python-0.1.1/
$ ./setup.py build
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;p&gt;&lt;b&gt;
6，将编译好的 douban 目录复制到项目目录 ~/doupye/
&lt;/b&gt;&lt;/p&gt;


&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cp build/lib/* ~/doupye/&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;br /&gt;


&lt;p&gt;&lt;b&gt;
7，修改豆瓣的 Python 客户端文件 service.py
&lt;/b&gt;&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ cd ~/doupye/douban/
$ gvim service.py
&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
&lt;h4&gt;service.py&lt;/h4&gt;

&lt;pre class="codes"&gt;&lt;code&gt;import gdata.service
# 添加下面两行，让所有的 HTTP 请求调用 App Engine 的 urlfetch (&lt;a rel="nofollow" title="Release the hounds: Support for App Engine and Contacts API" href="http://googledataapis.blogspot.com/2008/04/release-hounds-support-for-app-engine.html" id="oj1m"&gt;?&lt;/a&gt;)
import gdata.urlfetch
gdata.service.http_request_handler = gdata.urlfetch
&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;&lt;h4&gt;演示网页&lt;/h4&gt;
&lt;span style="text-decoration: line-through;"&gt;http://doupye.appspot.com/demo/douban_python_client/&lt;/span&gt;&lt;/p&gt;</description><link>http://luliban.com/blog/2008/04/google-app-engine-python.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-719603050147822500</guid><pubDate>Wed, 16 Apr 2008 12:14:00 +0000</pubDate><atom:updated>2008-04-18T13:17:45.569+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>python</category><category domain='http://www.blogger.com/atom/ns#'>豆瓣</category><title>有备无患：订阅所有豆瓣用户的广播</title><description>&lt;p&gt;以前写过&lt;a href="http://luliban.com/blog/2007/11/gtalk-skype-msn.html"&gt;输出单个用户的友邻广播 feeds 的脚本&lt;/a&gt;，后来&lt;a href="http://www.douban.com" rel="nofollow"&gt;豆瓣&lt;/a&gt;把原来的“友邻”一分为二成双向的“朋友”和单向的“关注”，那个脚本就过时了。所以我重写了 pydmb.py，并让它能够输出多层友邻关系的豆瓣广播 feeds 的 &lt;a href="http://tw.info.yahoo.com/rss/rookie.html#sub_4" rel="nofollow"&gt;OPML 文件&lt;/a&gt;。&lt;b&gt;根据&lt;a href="http://zh.wikipedia.org/wiki/%E5%85%AD%E5%BA%A6%E5%88%86%E9%9A%94%E7%90%86%E8%AB%96" rel="nofollow"&gt;六度分割理论&lt;/a&gt;，平均只需要六层关系就可以联系到任何两个互不相识的人。那么，从任何一个豆瓣用户出发输出六层友邻关系的广播，是不是最终也可以得到几乎所有豆瓣用户的广播呢？&lt;/b&gt;我没有试过，因为即使只输出了两层友邻关系的广播，我已经得到 8825个 feeds，如果输出六层的话会吓到人的。&lt;br /&gt;

&lt;br /&gt;

&lt;h3&gt;使用方法&lt;/h3&gt;
1，下载 &lt;a href="http://luliban.com/attachments/pydmb-0.2.tar.gz" title="pydmb-0.2.tar.gz"&gt;pydmb.py&lt;/a&gt;&lt;/p&gt;

&lt;div&gt;
&lt;a href="http://luliban.com/attachments/pydmb-0.2.tar.gz" title="pydmb-0.2.tar.gz"&gt;&lt;img alt="pydmb-0.2.tar.gz" style="border:none;" src="http://luliban.com/images/download90.gif" /&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;2，解压缩到当前目录。&lt;br /&gt;

&lt;br /&gt;

&lt;p&gt;3，运行脚本。这里举一个例子，比如你想获得 &lt;a rel="nofollow" href="http://www.douban.com/people/keso"&gt;keso&lt;/a&gt; 和&lt;a rel="nofollow" href="http://www.douban.com/people/ahbei"&gt;阿北&lt;/a&gt;的三层朋友（不包括他们关注的人）的广播 feeds，可以输入命令：&lt;/p&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ ./pydmb.py keso ahbei --friend --depth 3&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;p&gt;4，需要帮助可以运行命令：&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ ./pydmb.py --help&lt;/b&gt;
Usage: python pydmb.py [-fc] [-d DEPTH] user1 user2 ...

Options:
  -h, --help            show this help message and exit
  -f, --friend          output douban miniblog rss of your friends
  -c, --contact         output douban miniblog rss of your contact
  -d DEPTH, --depth=DEPTH
                        the depth of relationship to output
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;2008-04-18 UPDATE:&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://luliban.com/blog/2008/04/blog-post_16.html#comments"&gt;NullPointer 留言说&lt;/a&gt;想看看六度连接的试验结果，我也很感兴趣，所以昨天先试了试二度和三度的连接。结果从阿北出发的二度空间能连接到10214个人，三度空间能连接到117113个。&lt;/p&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from pydmb import *
&amp;gt;&amp;gt;&amp;gt; graph = UserGraph('ahbei')
&amp;gt;&amp;gt;&amp;gt; graph.search(2, 'fc')
&amp;gt;&amp;gt;&amp;gt; len(graph.dict.keys())
10214
... ...
&amp;gt;&amp;gt;&amp;gt; graph.search(3, 'fc')
&amp;gt;&amp;gt;&amp;gt; len(graph.dict.keys())
117113&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果还算理想，可是用的时间比较厉害，二度连接还好只用了十几分钟，但三度连接用了将近八个小时。照这样推算，分析六度连接（理论上说，就是要分析将近140万豆瓣用户的朋友和关注的人）可能会超过800个小时。所以用这个脚本来做就不太现实了，如果要做的话，最好把 &lt;a rel="nofollow" href="http://www.python.org/doc/lib/module-urllib2.html"&gt;urllib2&lt;/a&gt; 和 &lt;a rel="nofollow" href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt; 换成更快的库，然后用两个线程分别来抓取和分析网页，这样效率会高一些。&lt;/p&gt;</description><link>http://luliban.com/blog/2008/04/blog-post_16.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-3963515544537637724</guid><pubDate>Sat, 12 Apr 2008 14:35:00 +0000</pubDate><atom:updated>2008-04-16T16:30:26.524+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>nba</category><category domain='http://www.blogger.com/atom/ns#'>火箭</category><title>南言北哲：你最想看哪些季后赛的首轮对局？（三）</title><description>&lt;p&gt;
&lt;a rel="nofollow" title="Suns 90, Rockets 101" href="http://sports.espn.go.com/nba/boxscore?gameId=280411010" id="pfy8"&gt;昨天火箭大胜太阳&lt;/a&gt;之后，在常规赛季只剩下最后三场的情况下，&lt;a rel="nofollow" title="NBA Standings" href="http://sports.espn.go.com/nba/standings" id="fqvw"&gt;有四支球队距离西部冠军只有少于等于1场&lt;/a&gt;。这就像在银石跑到第60圈，Kimi、Hamilton 和 Alonso 从 Luffield 弯出来之后，三辆赛车并驾齐驱以超过时速280公里的速度冲向终点一样不可思议。&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;。。。我滴妈呀，太刺激了～～～&lt;/b&gt;（wyt：请模仿德云社李菁的嗓音进行想象。云里雾里的同学可以参考本文最后的郭德纲相声：）&lt;br /&gt;

&lt;br /&gt;

虽然我也期待一个戏剧性的 ending，比如火箭最终夺冠之类的，但是如果常规赛就此结束，那么今天的排名将最接近我想要看到的季后赛首轮对局，&lt;a title="南言北哲：你最想看哪些季后赛首轮的对局？（一）" href="http://luliban.com/blog/2008/03/blog-post_27.html" id="obv1"&gt;小牛（7）对湖人（2）&lt;/a&gt;，&lt;a title="南言北哲：你最想看哪些季后赛的首轮对局？（二）" href="http://luliban.com/blog/2008/04/blog-post.html" id="cq3s"&gt;太阳（6）对马刺（3）&lt;/a&gt;，以及接下来将讨论的火箭（5）对爵士（4）。这样其实也不错。&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;第三局：火箭 vs 爵士&lt;/h3&gt;

&lt;h4&gt;——从哪里跌倒，就要从哪里爬起来&lt;/h4&gt;
&lt;/p&gt;

&lt;br /&gt;

&lt;div&gt;
&lt;a href="http://luliban.com/blog/uploaded_images/tmac-dribble-ak47.jpg"&gt;
&lt;img id="fq4l" style="width: 430px; height: 594px;" src="http://luliban.com/blog/uploaded_images/tmac-dribble-ak47.jpg" /&gt;
&lt;/a&gt;
&lt;/div&gt;

&lt;br /&gt;

&lt;p&gt;


去年，火箭的第一场和最后一场常规赛都在盐湖城和犹他爵士比赛，两战皆墨。今年，火箭的第二场和倒数第二场常规赛又都在盐湖城，火箭赢了前一场，而后一场在4月15日。去年，火箭在季后赛第一轮的七场大战中输给了爵士，让爵士闯到西部决赛。今年，稳居西部第四的爵士仍然是火箭的季后赛第一轮的对手最有可能遇到的对手。&lt;br /&gt;

&lt;br /&gt;事实上，我也希望它是。去年11月2日以105-96战胜爵士之后，27投17中砍下47分的 T-Mac 看来很平静，“这不能说明什么，这仅仅是赛季的第二场比赛。”可姚明似乎“出卖”了他，“我们像是在打第八场系列赛，我们得到胜利之后才能开始新的赛季。”如果爵士真的再一次成为火箭的季后赛对手的话，那么无论是对T-Mac，还是对火箭队，都是时候来证明自己了，&lt;b&gt;IT'S TIME FOR A REVENGE，是时候报仇雪恨了！&lt;/b&gt;&lt;br /&gt;

&lt;br /&gt;
附：&lt;a rel="nofollow" title="郭德纲相声《李菁开车》" href="http://www.tudou.com/v/z7siMuSVLus"&gt;郭德纲相声《李菁开车》&lt;/a&gt;（口不对声，听听就行）&lt;/p&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;object type="application/x-shockwave-flash" width="425px" height="350px" data="http://www.tudou.com/v/z7siMuSVLus"&gt;&lt;param name="movie" value="http://www.tudou.com/v/z7siMuSVLus" /&gt;&lt;/object&gt;
&lt;/div&gt;</description><link>http://luliban.com/blog/2008/04/blog-post_12.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-7677387863603087715</guid><pubDate>Fri, 11 Apr 2008 07:17:00 +0000</pubDate><atom:updated>2008-04-11T15:36:40.379+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>linux</category><title>悠言悠闲：root敢死队</title><description>&lt;p&gt;原来平时那么爱用sudo，还真不觉得。&lt;/p&gt;
&lt;br /&gt;
&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ history |awk '{a[$2]++ } END{for(i in a){print a[i] " " i}}'|sort -rn|head&lt;/b&gt;
99 sudo
62 todo
52 python
49 eix
38 mysql
38 cd
32 ls
19 tda
16 vi
15 tde
&lt;/code&gt;&lt;/pre&gt;</description><link>http://luliban.com/blog/2008/04/root.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-6970860275498506697</guid><pubDate>Tue, 08 Apr 2008 17:37:00 +0000</pubDate><atom:updated>2008-05-11T16:35:25.411+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>google</category><category domain='http://www.blogger.com/atom/ns#'>互联网</category><category domain='http://www.blogger.com/atom/ns#'>feed</category><title>有备无患：用 AideRSS + Google Reader 辅助阅读</title><description>&lt;p&gt;

前几天我计算出自己的 &lt;a title="有备无患：怎样评价Feed阅读效率" href="http://luliban.com/blog/2008/04/feed.html" id="q6jc"&gt;FRER（Feed 阅读效率评价）&lt;/a&gt;为15.14，这意味着在 Google Reader 读到的大约85%的文章，至少对我来说，是噪声。这里所浪费的时间，虽然不能直接推算，但估计至少有三分之一。为了更有效率的利用这些时间，我尝试用 &lt;a rel="nofollow" title="AideRSS + GoogleReader Firefox extension" href="http://gr.aiderss.com/?downloadsquad" id="c_g0"&gt;AideRSS 为 Google Reader 设计的 Firefox 扩展&lt;/a&gt;来辅助阅读。&lt;br /&gt;

&lt;br /&gt;

&lt;a rel="nofollow" title="AideRSS" href="http://www.aiderss.com/" id="n2tn"&gt;AideRSS&lt;/a&gt; 原本是一款在线的 feed 过滤工具。它通过分析 PageRank，文章评论和 Trackback 的数量，以及文章被 del.icio.us 和 Digg 用户收藏的次数等指标，计算出相应的 &lt;a rel="nofollow" title="PostRank" href="http://blog.aiderss.com/faq/#postrank" id="b8mv"&gt;AideRSS PostRank&lt;/a&gt;（从1.0到10），并以此为用户过滤 feeds 减少信息过载带来的困扰。而 AideRSS 的这个 Firefox 扩展将 PostRank 整合到 Google Reader 中，我们可以在每一篇文章的标题左侧看到相应的 PostRank，在末尾看到相应的评论、Trackback、del.icio.us 和 digg 收藏的具体数目。另外，我们还可根据 PostRank 的高低过滤出 Good，Great 和 Best 的文章来选择阅读。&lt;/p&gt;

&lt;br /&gt;

&lt;div&gt;&lt;a href="http://luliban.com/blog/uploaded_images/aiderss-screenshot.jpg"&gt;&lt;img id="j3h2" style="width: 450px; height: 355px;" src="http://luliban.com/blog/uploaded_images/aiderss-screenshot-450.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;
&lt;b&gt;几天使用下来，AideRSS 确实能提高一些阅读的命中率，特别是在读流量大更新快的 blogs 的时候，比如CrunchGear，LifeHacker 等。另外读那些只输出摘要的 feeds 的时候，比如新闻类的 feeds，del.icio.us 上的 linux tag 等，我也可以根据 PostRank 快速过滤掉一些文章。&lt;/b&gt;&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;另一方面，AideRSS 对草根 blogs 不会像&lt;a rel="nofollow" title="豆瓣九点" href="http://9.douban.com/"&gt;豆瓣九点&lt;/a&gt;那么“势利眼”——死盯着订阅数不放，不用去担心 PostRank 会不会把草根的声音屏蔽了的问题。&lt;/b&gt;PostRank 的算法中，似乎流量较少的草根们反而更“容易”拿到高 PostRank。举例子来说，我的《&lt;a class="direct" href="http://luliban.com/blog/2008/03/bloggerftp.html" title="有备无患：为Blogger传统模板（FTP发布）添加标签云"&gt;有备无患：为Blogger传统模板（FTP发布）添加标签云&lt;/a&gt;》只有4个 backlink，2条评论和2个 del.icio.us 收藏，被评到8.3分，而 LifeHacker 的《&lt;a rel="nofollow" href="http://lifehacker.com/376705/bind-papers-together-without-staples-or-clips" title="Bind Papers Together Without Staples or Clips [How To]" &gt;Bind Papers Together Without Staples or Clips [How To]&lt;/a&gt;》有39条评论，7次 del.icio.us，5次 digg，却只有5.3分。&lt;br/&gt;

&lt;br/&gt;

虽然 AideRSS + Google Reader Firefox Extension 还在 private beta 阶段，虽然会拖慢 Google Reader 的响应速度（wyt：要通过 AideRSS 的服务器取数据），虽然还和 Firefox 上最流行的扩展之一 GreaseMonkey 有冲突（wyt：AideRSS 另外提供了一个 GM 脚本，实现和扩展同样的功能），它还是值得去试一试。不过，真正的结论还要等几个星期以后，看看我的 FRER 有没有因此而提高了。&lt;/p&gt;</description><link>http://luliban.com/blog/2008/04/aiderss-google-reader.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-7245259056814930827</guid><pubDate>Sat, 05 Apr 2008 09:04:00 +0000</pubDate><atom:updated>2008-04-07T19:44:47.694+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>互联网</category><category domain='http://www.blogger.com/atom/ns#'>feed</category><title>有备无患：怎样评价Feed阅读效率</title><description>&lt;div&gt;
&lt;a href="http://luliban.com/blog/uploaded_images/hatsune-miku-800.jpg"&gt;
&lt;img src="http://luliban.com/blog/uploaded_images/hatsune-miku-400.jpg" alt="你可以用葱" /&gt;
&lt;/a&gt;
&lt;/div&gt;

&lt;br /&gt;
&lt;p&gt;

Feed阅读早已超过订购报纸和浏览网页，成为互联网时代获取信息的最重要的渠道之一。但随着信息的大量聚合，噪声、冗余和陈腔滥调的信息也不可避免的混杂其中，谓之“&lt;a href="http://en.wikipedia.org/wiki/Information_overload" rel="nofollow"&gt;信息过载&lt;/a&gt;”。其实，“信息过载”也是“信息欠缺”的形式之一，后者是说绝对的信息缺乏，前者是指相对于噪声的有用信息的缺乏，即“&lt;a href="http://zh.wikipedia.org/wiki/信噪比" rel="nofollow"&gt;信噪比&lt;/a&gt;”过低。&lt;br /&gt;

&lt;br /&gt;

提高信噪比的办法有很多。比如专注于最感兴趣的feeds，删除那些不错但无关紧要的（我们不想了解整个互联网）。比如删除那些热衷于速译国外blogs 却不能自己提供内容的feeds（文字还是原汁原味的好）。又比如用标签为feeds分级，分为必读的A List，选读的B List和爱读不读的ZZ List（人工智慧还不能为我们过滤文章的时候，只能先凑合一下“&lt;a href="http://mr6.cc/?p=1550" rel="nofollow"&gt;工人智慧&lt;/a&gt;”:-）。等等。&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;可是还有一个问题：如果我们采取了上述这些措施，怎样才能知道“信噪比”是升高了还是降低了，怎样才能量化的评价自己的阅读效率呢？&lt;/b&gt;&lt;br /&gt;

&lt;br /&gt;

为此我略微改变了一下自己的阅读习惯，把每一条值得一读的信息在&lt;a href="http://reader.google.com" rel="nofollow"&gt;Google Reader&lt;/a&gt;中打上星号，以及每隔半个月记录一次自己的feed订阅数。然后用FRER（Feed阅读效率评价）来了解自己最近的阅读效率。FRER是一个通过大多数Feed阅读器都会提供的统计数据（订阅数，已读数，收藏数和推荐数）来计算最近30天的Feed阅读效率，换言之“阅读信噪比”的计算公式。&lt;br /&gt;

&lt;/p&gt;

&lt;br /&gt;

&lt;blockquote&gt;&lt;p&gt;
&lt;b&gt;FRER = (Sub / aSub) * (StI + ShI) / RdI * 100&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
说明：&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FRER - Feed Reading Efficiency Rating，Feed阅读效率评价&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Sub - Subscription，当前订阅数&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;aSub - Average Subcription，历史平均订阅数&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;StI - Starred Items，内向型“收藏”的文章&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ShI - Shared Items，外向型“推荐”的文章&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RdI - Read Items，所有已读的文章&lt;br /&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;br /&gt;

&lt;p&gt;


举例来说，我在Google Reader上订阅的feeds共有381个，这几个月平均的订阅数是373.3个，最近30天我阅读了11246篇文中，其中被我打上星号收藏的文章有1422篇，分享出去的文章有250篇。那么通过FRER计算可以得出我最近一个月的Feed阅读效率是15.14。

&lt;/p&gt;</description><link>http://luliban.com/blog/2008/04/feed.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-200119758837953318</guid><pubDate>Mon, 31 Mar 2008 19:00:00 +0000</pubDate><atom:updated>2008-04-01T03:13:36.923+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>nba</category><title>南言北哲：你最想看哪些季后赛的首轮对局？（二）</title><description>&lt;p&gt;半个星期之前，&lt;a title="南言北哲：你最想看哪些季后赛首轮的对局？（一）" href="http://luliban.com/blog/2008/03/blog-post_27.html"&gt;我还说最想看到小牛排第八挑战西部老大的西部季后赛首轮对局&lt;/a&gt;。半个星期之后，失去Nowitziki的小牛连输两场关键的比赛（&lt;a title="105-118负掘金" href="http://sports.espn.go.com/nba/boxscore?gameId=280327007" rel="nofollow"&gt;105-118负掘金&lt;/a&gt;，&lt;a title="104-114负勇士" href="http://sports.espn.go.com/nba/boxscore?gameId=280330009" rel="nofollow"&gt;104-114负勇士&lt;/a&gt;），战绩滑落到45胜28负，和掘金与勇士并列西部第七。我猜，主教练Avery Johnson多少会怀念一下小牛曾经的最好的没有之一的突破防守球员，Devin Harris，因为他们刚刚被勇士的Monta Ellis砍下30分，而本赛季之前两次对阵，Ellis总共也才拿到不过25分。&lt;b&gt;NBA WHERE AMAZING HAPPENS...&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;

Mark Cuban也许还不至于后悔交易来Jason Kidd。像这样的重量级交易，除了结果（总冠军戒指）以外似乎没有其他的评判标准。可今年六月，或者五月之后（坏笑）会怎样就很难说了。相比之下，全明星周末的另外一宗大交易的主角，Phoenix Suns的小日子却过得相当的滋润。度过了3胜6负的阵痛期之后，交易来“大仙人掌”Shaquille O'Neal的太阳9胜2负，更重要的是&lt;a title="他们干掉过去几年萦绕在头上的噩梦" href="http://sports.espn.go.com/nba/boxscore?gameId=280309021" rel="nofollow"&gt;他们干掉过去几年萦绕在头上的噩梦&lt;/a&gt;——Tim Duncan和他的卫冕冠军球队San Antonio Spurs。&lt;br /&gt;
&lt;br /&gt;

&lt;h3&gt;第二局：太阳 vs 马刺&lt;/h3&gt;
&lt;h4&gt;——Steve Kerr，你究竟是天才还是蠢货，让我们第一轮就见分晓吧！&lt;/h4&gt;


&lt;div&gt;&lt;a href="http://luliban.com/blog/uploaded_images/oneal-vs-duncan.jpg" title="O'neal vs Duncan"&gt;&lt;img alt="O'neal vs Duncan" style="width: 396px; height: 594px;" src="http://luliban.com/blog/uploaded_images/oneal-vs-duncan.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;太阳和马刺比赛的精彩之处不用多说。&lt;b&gt;但往年，同处上半区的太阳和马刺不得不等到第二轮以后才能碰面。而今年，神奇的07-08赛季，给了这两个冤家在第一轮解决战斗的机会。&lt;/b&gt;尤其在太阳在全明星周末前豪赌一把，用Shawn Marion和Marcus Banks换来了Shaquille O'Neal之后，太阳总经理Steve Kerr也立下“军令状”，“我猜，如果我成功了，我就是天才。如果没有，那么我就是蠢货。”&lt;br /&gt;
&lt;br /&gt;

太阳目前49胜24负，和火箭并列西部第五，可接下来的赛程相当艰苦。今明两天背靠背战掘金，Melo将考验Marion走后留下的最大漏洞，侧翼防守。算上4月7日主场战小牛，太阳还有一趟名为“德州7日游”的车轮战等着他们，最后两场比赛是为季后赛资格而拼命的勇士和心气颇高的开拓者。在&lt;a title="John Hollinger的季后赛预测" href="http://sports.espn.go.com/nba/hollinger/playoffodds" rel="nofollow"&gt;John Hollinger的季后赛预测&lt;/a&gt;中，太阳只有22%的机会赢得分区冠军，这也意味着他们很有可能以第五，或者第六种子的身份进入季后赛。&lt;br /&gt;
&lt;br /&gt;

同样赛程艰苦的还有马刺。尽管在经历了近几年最长的四连败之后，马刺似乎已经进入了PO Mode（季后赛模式），&lt;a href="http://scores.espn.go.com/nba/boxscore?gameId=280330024" title="昨天20分狂扫火箭" rel="nofollow"&gt;昨天20分狂扫火箭&lt;/a&gt;之后已经是7连胜。不过，马刺剩下的8场比赛中，将有五支季后赛资格球队，勇士、爵士（2次）、湖人和太阳。考虑到湖人和黄蜂的赛程很轻松，加上马刺的主教练Popovich向来无视常规赛冠军的虚名，似乎马刺夺冠的机会并不大。我认为，他们很有可能排名西部第二，或者第三。&lt;br /&gt;

&lt;br /&gt;

&lt;b&gt;如果，太阳最终排第六而马刺排第三，那么，我们就有机会在第一轮就提前验证O'Neal的大交易是否成功以及Steve Kerr究竟是天才还是蠢货了。&lt;/b&gt;欢迎大家留言，说说你心目中最期待的季后赛首轮对局。</description><link>http://luliban.com/blog/2008/04/blog-post.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-2970784945485790647</guid><pubDate>Mon, 31 Mar 2008 15:19:00 +0000</pubDate><atom:updated>2008-03-31T23:24:53.810+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>linux</category><title>有备无患：用Vista字体配置Sun JDK 1.6</title><description>&lt;p&gt;也许是升级JDK的时候，把原本配置好的fontconfig.properties给冲掉了，今天emerge &lt;a title="OmegaT" href="http://www.omegat.org/" rel="nofollow"&gt;OmegaT&lt;/a&gt;之后，发现“方框字”又重现江湖。重新&lt;a title="在Google上搜了一番" href="http://www.google.com/search?q=linux+jdk+%E5%AD%97%E4%BD%93+%E9%85%8D%E7%BD%AE" rel="nofollow"&gt;在Google上搜了一番&lt;/a&gt;，用Bitstream Vera的英文字体，和Windoze Vista的繁日韩字体——正黑，Meiryo和Malgun，重写了fontconfig.properties，直接复制到/opt/sun-jdk-1.6.x/jre/lib/即可。&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;fontconfig.properties：&lt;/h4&gt;&lt;/p&gt;
&lt;pre class="codes"&gt;&lt;code&gt;# Version

version=1

# Component Font Mappings

allfonts.chinese-gb18030=-microsoft-microsoft jhenghei-medium-r-normal--0-0-0-0-p-0-gb18030.2000-0
allfonts.chinese-gb18030.motif=-microsoft-microsoft jhenghei-medium-r-normal--0-0-0-0-p-0-gb18030.2000-0
allfonts.chinese-big5=-microsoft-microsoft jhenghei-medium-r-normal--0-0-0-0-p-0-gb18030.2000-0
allfonts.chinese-big5.motif=-microsoft-microsoft jhenghei-medium-r-normal--0-0-0-0-p-0-gb18030.2000-0
allfonts.lucida=-bitstream-bitstream vera sans-medium-r-normal--0-0-0-0-p-0-iso8859-1

serif.plain.japanese-x0208=-microsoft-meiryo-medium-r-normal--0-0-0-0-p-0-jisx0208.1990-0
serif.plain.korean=-microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
serif.plain.korean.motif=-microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
serif.plain.latin-1=-bitstream-bitstream vera serif-medium-r-normal--0-0-0-0-p-0-iso8859-1
serif.plain.latin-1.motif=-bitstream-bitstream vera serif-medium-r-normal--0-0-0-0-p-0-iso8859-1

serif.bold.japanese-x0208=-microsoft-meiryo-bold-r-normal--0-0-0-0-p-0-jisx0208.1990-0
serif.bold.korean=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
serif.bold.korean.motif=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
serif.bold.latin-1=-bitstream-bitstream vera serif-bold-r-normal--0-0-0-0-p-0-iso8859-1
serif.bold.latin-1.motif=-bitstream-bitstream vera serif-bold-r-normal--0-0-0-0-p-0-iso8859-1

serif.italic.japanese-x0208=-microsoft-meiryo-medium-i-normal--0-0-0-0-p-0-jisx0208.1990-0
serif.italic.korean=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
serif.italic.korean.motif=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
serif.italic.latin-1=-bitstream-bitstream vera serif-medium-i-normal--0-0-0-0-p-0-iso8859-1
serif.italic.latin-1.motif=-bitstream-bitstream vera serif-medium-i-normal--0-0-0-0-p-0-iso8859-1

serif.bolditalic.japanese-x0208=-microsoft-meiryo-bold-i-normal--0-0-0-0-p-0-jisx0208.1990-0
serif.bolditalic.korean=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
serif.bolditalic.korean.motif=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
serif.bolditalic.latin-1=-bitstream-bitstream vera serif-bold-i-normal--0-0-0-0-p-0-iso8859-1
serif.bolditalic.latin-1.motif=-bitstream-bitstream vera serif-bold-i-normal--0-0-0-0-p-0-iso8859-1

sansserif.plain.japanese-x0208=-microsoft-meiryo-medium-r-normal--0-0-0-0-p-0-jisx0208.1990-0
sansserif.plain.korean=-microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
sansserif.plain.korean.motif=-microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
sansserif.plain.latin-1=-bitstream-bitstream vera sans-medium-r-normal--0-0-0-0-p-0-iso8859-1
sansserif.plain.latin-1.motif=-bitstream-bitstream vera sans-medium-r-normal--0-0-0-0-p-0-iso8859-1

sansserif.bold.japanese-x0208=-microsoft-meiryo-bold-r-normal--0-0-0-0-p-0-jisx0208.1990-0
sansserif.bold.korean=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
sansserif.bold.korean.motif=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
sansserif.bold.latin-1=-bitstream-bitstream vera sans-bold-r-normal--0-0-0-0-p-0-iso8859-1
sansserif.bold.latin-1.motif=-bitstream-bitstream vera sans-bold-r-normal--0-0-0-0-p-0-iso8859-1

sansserif.italic.japanese-x0208=-microsoft-meiryo-medium-i-normal--0-0-0-0-p-0-jisx0208.1990-0
sansserif.italic.korean=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
sansserif.italic.korean.motif=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
sansserif.italic.latin-1=-bitstream-bitstream vera sans-medium-o-normal--0-0-0-0-p-0-iso8859-1
sansserif.italic.latin-1.motif=-bitstream-bitstream vera sans-medium-o-normal--0-0-0-0-p-0-iso8859-1

sansserif.bolditalic.japanese-x0208=-microsoft-meiryo-bold-i-normal--0-0-0-0-p-0-jisx0208.1990-0
sansserif.bolditalic.korean=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
sansserif.bolditalic.korean.motif=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
sansserif.bolditalic.latin-1=-bitstream-bitstream vera sans-bold-o-normal--0-0-0-0-p-0-iso8859-1
sansserif.bolditalic.latin-1.motif=-bitstream-bitstream vera sans-bold-o-normal--0-0-0-0-p-0-iso8859-1

monospaced.plain.japanese-x0208=-microsoft-meiryo-medium-r-normal--0-0-0-0-p-0-jisx0208.1990-0
monospaced.plain.korean=-microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
monospaced.plain.korean.motif=-microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
monospaced.plain.latin-1=-bitstream-bitstream vera sans mono-medium-r-normal--0-0-0-0-m-0-iso8859-1
monospaced.plain.latin-1.motif=-bitstream-bitstream vera sans mono-medium-r-normal--0-0-0-0-m-0-iso8859-1

monospaced.bold.japanese-x0208=-microsoft-meiryo-bold-r-normal--0-0-0-0-p-0-jisx0208.1990-0
monospaced.bold.korean=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
monospaced.bold.korean.motif=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
monospaced.bold.latin-1=-bitstream-bitstream vera sans mono-bold-r-normal--0-0-0-0-m-0-iso8859-1
monospaced.bold.latin-1.motif=-bitstream-bitstream vera sans mono-bold-r-normal--0-0-0-0-m-0-iso8859-1

monospaced.italic.japanese-x0208=-microsoft-meiryo-medium-i-normal--0-0-0-0-p-0-jisx0208.1990-0
monospaced.italic.korean=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
monospaced.italic.korean.motif=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
monospaced.italic.latin-1=-bitstream-bitstream vera sans mono-medium-o-normal--0-0-0-0-m-0-iso8859-1
monospaced.italic.latin-1.motif=-bitstream-bitstream vera sans mono-medium-o-normal--0-0-0-0-m-0-iso8859-1

monospaced.bolditalic.japanese-x0208=-microsoft-meiryo-bold-i-normal--0-0-0-0-p-0-jisx0208.1990-0
monospaced.bolditalic.korean=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
monospaced.bolditalic.korean.motif=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
monospaced.bolditalic.latin-1=-bitstream-bitstream vera sans mono-bold-o-normal--0-0-0-0-m-0-iso8859-1
monospaced.bolditalic.latin-1.motif=-bitstream-bitstream vera sans mono-bold-o-normal--0-0-0-0-m-0-iso8859-1

dialog.plain.japanese-x0208=-microsoft-meiryo-medium-r-normal--0-0-0-0-p-0-jisx0208.1990-0
dialog.plain.korean=-microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
dialog.plain.korean.motif=-microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
dialog.plain.latin-1=bitstream-bitstream vera sans-medium-r-normal--0-0-0-0-p-0-iso8859-1
dialog.plain.latin-1.motif=bitstream-bitstream vera sans-medium-r-normal--0-0-0-0-p-0-iso8859-1

dialog.bold.japanese-x0208=-microsoft-meiryo-bold-r-normal--0-0-0-0-p-0-jisx0208.1990-0
dialog.bold.korean=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
dialog.bold.korean.motif=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
dialog.bold.latin-1=itstream-bitstream vera sans-bold-r-normal--0-0-0-0-p-0-iso8859-1
dialog.bold.latin-1.motif=itstream-bitstream vera sans-bold-r-normal--0-0-0-0-p-0-iso8859-1

dialog.italic.japanese-x0208=-microsoft-meiryo-medium-i-normal--0-0-0-0-p-0-jisx0208.1990-0
dialog.italic.korean=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
dialog.italic.korean.motif=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
dialog.italic.latin-1=-bitstream-bitstream vera sans-medium-o-normal--0-0-0-0-p-0-iso8859-1
dialog.italic.latin-1.motif=-bitstream-bitstream vera sans-medium-o-normal--0-0-0-0-p-0-iso8859-1

dialog.bolditalic.japanese-x0208=-microsoft-meiryo-bold-i-normal--0-0-0-0-p-0-jisx0208.1990-0
dialog.bolditalic.korean=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
dialog.bolditalic.korean.motif=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
dialog.bolditalic.latin-1=-bitstream-bitstream vera sans-bold-o-normal--0-0-0-0-p-0-iso8859-1
dialog.bolditalic.latin-1.motif=-bitstream-bitstream vera sans-bold-o-normal--0-0-0-0-p-0-iso8859-1

dialoginput.plain.japanese-x0208=-microsoft-meiryo-medium-r-normal--0-0-0-0-p-0-jisx0208.1990-0
dialoginput.plain.korean=-microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
dialoginput.plain.korean.motif=--microsoft-malgun gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1
dialoginput.plain.latin-1=-bitstream-bitstream vera sans mono-medium-r-normal--0-0-0-0-m-0-iso8859-1
dialoginput.plain.latin-1.motif=-bitstream-bitstream vera sans mono-medium-r-normal--0-0-0-0-m-0-iso8859-1

dialoginput.bold.japanese-x0208=-microsoft-meiryo-bold-r-normal--0-0-0-0-p-0-jisx0208.1990-0
dialoginput.bold.korean=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
dialoginput.bold.korean.motif=-microsoft-malgun gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1
dialoginput.bold.latin-1=-bitstream-bitstream vera sans mono-bold-r-normal--0-0-0-0-m-0-iso8859-1
dialoginput.bold.latin-1.motif=-bitstream-bitstream vera sans mono-bold-r-normal--0-0-0-0-m-0-iso8859-1

dialoginput.italic.japanese-x0208=-microsoft-meiryo-medium-i-normal--0-0-0-0-p-0-jisx0208.1990-0
dialoginput.italic.korean=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
dialoginput.italic.korean.motif=-microsoft-malgun gothic-medium-i-normal--0-0-0-0-p-0-iso10646-1
dialoginput.italic.latin-1=-bitstream-bitstream vera sans mono-medium-o-normal--0-0-0-0-m-0-iso8859-1
dialoginput.italic.latin-1.motif=-bitstream-bitstream vera sans mono-medium-o-normal--0-0-0-0-m-0-iso8859-1

dialoginput.bolditalic.japanese-x0208=-microsoft-meiryo-bold-i-normal--0-0-0-0-p-0-jisx0208.1990-0
dialoginput.bolditalic.korean=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
dialoginput.bolditalic.korean.motif=-microsoft-malgun gothic-bold-i-normal--0-0-0-0-p-0-iso10646-1
dialoginput.bolditalic.latin-1=-bitstream-bitstream vera sans mono-bold-o-normal--0-0-0-0-m-0-iso8859-1
dialoginput.bolditalic.latin-1.motif=-bitstream-bitstream vera sans mono-bold-o-normal--0-0-0-0-m-0-iso8859-1

# Search Sequences

sequence.allfonts=latin-1
sequence.allfonts.Big5=latin-1,chinese-big5
sequence.allfonts.GB2312=latin-1,chinese-gb18030
sequence.allfonts.x-euc-jp-linux=latin-1,japanese-x0208
sequence.allfonts.EUC-KR=latin-1,korean
sequence.allfonts.GB18030=latin-1,chinese-gb18030
sequence.fallback=lucida,chinese-big5,chinese-gb18030,japanese-x0208,korean

# Exclusion Ranges

exclusion.japanese-x0208=0390-03d6,2200-22ef,2701-27be

# Font File Names
filename.-microsoft-microsoft_jhenghei-medium-r-normal--0-0-0-0-p-0-gb18030.2000-0=/usr/share/fonts/TTF/msjh.ttf
filename.-microsoft-microsoft_jhenghei-bold-r-normal--0-0-0-0-p-0-gb18030.2000-0=/usr/share/fonts/TTF/msjhbd.ttf
filename.-microsoft-meiryo-medium-r-normal--0-0-0-0-p-0-jisx0208.1990-0=/usr/share/fonts/TTF/meiryo.ttc
filename.-microsoft-meiryo-bold-r-normal--0-0-0-0-p-0-jisx0208.1990-0=/usr/share/fonts/TTF/meiryob.ttc
filename.-microsoft-malgun_gothic-medium-r-normal--0-0-0-0-p-0-iso10646-1=/usr/share/fonts/TTF/malgun.ttf
filename.-microsoft-malgun_gothic-bold-r-normal--0-0-0-0-p-0-iso10646-1=/usr/share/fonts/TTF/malgunbd.ttf

filename.-bitstream-bitstream_vera_sans-medium-r-normal--0-0-0-0-p-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/Vera.ttf 
filename.-bitstream-bitstream_vera_sans-bold-o-normal--0-0-0-0-p-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/VeraBI.ttf 
filename.-bitstream-bitstream_vera_sans-bold-r-normal--0-0-0-0-p-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/VeraBd.ttf 
filename.-bitstream-bitstream_vera_sans-medium-o-normal--0-0-0-0-p-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/VeraIt.ttf 
filename.-bitstream-bitstream_vera_sans_mono-bold-o-normal--0-0-0-0-m-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/VeraMoBI.ttf 
filename.-bitstream-bitstream_vera_sans_mono-bold-r-normal--0-0-0-0-m-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/VeraMoBd.ttf 
filename.-bitstream-bitstream_vera_sans_mono-medium-o-normal--0-0-0-0-m-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/VeraMoIt.ttf 
filename.-bitstream-bitstream_vera_sans_mono-medium-r-normal--0-0-0-0-m-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/VeraMono.ttf 
filename.-bitstream-bitstream_vera_serif-medium-r-normal--0-0-0-0-p-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/VeraSe.ttf 
filename.-bitstream-bitstream_vera_serif-bold-r-normal--0-0-0-0-p-0-iso8859-1=/usr/share/fonts/ttf-bitstream-vera/VeraSeBd.ttf 

# AWT X11 font paths

awtfontpath.latin-1=/usr/share/fonts/ttf-bitstream-vera
awtfontpath.chinese-big5=/usr/share/fonts/TTF
awtfontpath.chinese-gb18030=/usr/share/fonts/TTF
awtfontpath.japanese-x0208=/usr/share/fonts/TTF
awtfontpath.korean=/usr/share/fonts/TTF
&lt;/code&gt;&lt;/pre&gt;</description><link>http://luliban.com/blog/2008/03/vistasun-jdk-16.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-7673713367196822687</guid><pubDate>Wed, 26 Mar 2008 19:02:00 +0000</pubDate><atom:updated>2008-04-05T23:49:28.919+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>nba</category><title>南言北哲：你最想看哪些季后赛首轮的对局？（一）</title><description>&lt;p&gt;
狂野的西部。每个真－NBA球迷都说，这是二十年来最好看的常规赛季，可对身在其中的教练和球员们就不是那么一回事了。正徘徊于鬼门关口的掘金主帅 George Karl把它称作“哈雷彗星年”，“这种情况可能每99年才能遇到一次。我只是想知道为什么霉运会落在自己头上呢。”&lt;br /&gt;
&lt;br /&gt;
掘金战绩为目前43胜28负，仅落后勇士（43胜27负）半个胜场，排名西部第九。他们还有两场和勇士的比赛。所以，机会仍然掌握在他们自己手中。只是西部球队的官子阶段无比惨烈，很有可能有一只球队会达到50胜，或48胜却被挡在大门之外。&lt;br /&gt;
&lt;br /&gt;
这样激烈的常规赛，不禁让我们更加期待季后赛的到来，让我们数数看哪些球队的捉对厮杀最值得期待？&lt;/p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;第一局：小牛 vs 湖人&lt;/h3&gt;
&lt;h4&gt;——去年我们被“黑八”了，今年我们要缔造自己的“黑八神话”&lt;/h4&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://luliban.com/blog/uploaded_images/kobe-bryant-dribble-660.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://luliban.com/blog/uploaded_images/kobe-bryant-dribble-500.jpg" alt="小牛 vs 湖人" /&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
刚才说了掘金和勇士，其实还有一只球队也在悬崖边，达拉斯小牛。小牛现在45胜26负，领先勇士一个半胜场，排名西部第七。&lt;br /&gt;
&lt;br /&gt;
可在全明星周末之前，小牛35胜18负，高居西部第三。一夜之间，联盟顶级控卫Jason Kidd重回达拉斯，风云突变，小牛18战仅10胜，面对季后赛球队8战全负。更糟糕的是，他们在和马刺的比赛中失去了主将Dirk Nowitziki。而在Nowitziki可能缺席的两周时间里，小牛赛程并不轻松，他们将先后挑战勇士（2次），掘金，湖人和太阳，稍有不慎小牛可能会落到西部第八。&lt;br /&gt;
&lt;br /&gt;
如果他们最终以第八顺位进入季后赛，那会变得非常有意思。去年，Nowitziki捧得常规赛MVP的奖杯，率领球队打出联盟最佳战绩，但是小牛却在第一轮被勇士4比2击败，一切的荣耀化成了新世纪第一个“黑八神话”的背景。而今年，他们将很有可能会碰到湖人或者黄蜂，以及今年的两个常规赛MVP的最热门人选Kobe Bryant和Chris Paul。&lt;br /&gt;
&lt;br /&gt;
你明白我的意思了吧：去年被“黑八”的常规赛MVP今年将尝试“黑八”今年的常规赛MVP候选。这像绕口令。另外我建议，如果David Stern犹豫不决于把常规赛MVP颁给谁，Kobe or Paul，不如给和小牛对阵的那位，这样也许会创造另一个被“黑八”的常规赛MVP，hoho。&lt;br /&gt;
&lt;br /&gt;
如果在湖人和黄蜂之间，为小牛挑一个季后赛首轮的对手，我更想选湖人。（wyt：这是不是暗示Kobe应该是MVP？呵呵，I have no idea...）&lt;br /&gt;
&lt;br /&gt;
本赛季至今小牛和湖人的三场比赛都异常激烈。在一月份的一场比赛中，小牛凭借第三节的高潮以112比 105战胜了湖人。在Kidd交易完成后的两场比赛中，其中一场打成加时湖人以108比104侥幸取胜，而另一场小牛在落后25分的困境下奋起直追，但最终以100比102遗憾告负。&lt;br /&gt;
&lt;br /&gt;
除了比赛精彩以外，教练的斗智也是经典系列赛不可缺少的一部分。&lt;br /&gt;
&lt;br /&gt;
在去年的季后赛首轮对阵自家师傅Don Nelson的时候，小牛的主教练Avery Johnson排出了怪阵，他将常规赛轮转阵容的两大中锋Erick Dampier和DeSagana Diop死死摁在板凳上，试图以Nowitziki为中锋的小个阵容对抗勇士的速度，而结果就是小牛还是输了。但小牛换走Diop和Devin Harris换来Kidd，看来将在小个阵容上越走越远。&lt;br /&gt;
&lt;br /&gt;
但湖人的Phil Jackson可能是全联盟最不怕所谓的“小个阵容”的主教练，自从2005年Jackson重回湖人，他对勇士队取得了10胜2负，包括Don Nelson作为主教练的6胜2负。“这些年我与Nelson对阵，他总想抛出错位进攻的法宝。”Jackson说，“他总想让你排出小个阵容跟他的小个阵容比赛。这些年每当我遇到他，我总是告诉自己千万不能这样做。很多教练陷入其中，那就像一个蜘蛛网让你陷进去。事实上如果你排出5个小个子，你的进攻会有麻烦。”&lt;br /&gt;
&lt;br /&gt;
所以，今年季后赛首轮我最期待的对局之一，就是达拉斯小牛(8) vs 洛杉矶湖人(1)。欢迎大家留言，说说你心目中最期待的季后赛首轮对局。

&lt;br /&gt;
请继续阅读&lt;a href="http://luliban.com/blog/2008/04/blog-post.html"&gt;《你最想看哪些季后赛首轮的对局？（二）》&lt;/a&gt;
&lt;/p&gt;</description><link>http://luliban.com/blog/2008/03/blog-post_27.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-2156269484648858389</guid><pubDate>Fri, 21 Mar 2008 16:08:00 +0000</pubDate><atom:updated>2008-04-02T22:54:52.657+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>blogger</category><category domain='http://www.blogger.com/atom/ns#'>模版</category><title>有备无患：Blogger传统模板：红黑「春日」v2</title><description>&lt;p&gt;这是一个简洁风的单栏外加widget dock（wyt：直译“小玩意儿坞”-_-b）的Blogger传统模板（Blogger Classic Template）。之所以叫“红黑「春日」v2”，是因为从&lt;a href="http://luliban.com/blog/2007/09/blogger.html"&gt;前一个模板&lt;/a&gt;中继承了一些想法，比如红与黑的色调——尽管并没有继承“&lt;a href="http://www.douban.com/subject/1810517/"&gt;凉宫春日&lt;/a&gt;”的元素。希望大家喜欢。&lt;br /&gt;&lt;/p&gt;

&lt;div&gt;
&lt;a href="http://luliban.com/blog/uploaded_images/screenshot-haruhiv2.jpg" title="红黑春日v2截图"&gt;
&lt;img src="http://luliban.com/blog/uploaded_images/screenshot-haruhiv2-425.jpg" alt="红黑「春日」v2 页面截图" /&gt;
&lt;/a&gt;
&lt;/div&gt;

&lt;h3&gt;使用前说明&lt;/h3&gt;

&lt;blockquote&gt;&lt;p&gt;
尽管模板上有一些示例用的widgets，但大多数情况下，大家仍然要自己动手修改模板。也就是说，这需要一点点HTML的基础——别担心，不会太多，我会在下面说明大部分的问题。如果你支持这个模板，请保留widget dock最右面的“关于本模板”（About this template）。另外，如果你想要分享这个模板，请遵循Creative Commons的“&lt;a href="http://creativecommons.org/licenses/by-nc-sa/3.0/us/"&gt;署名-非商业性使用-相同方式共享&lt;/a&gt;”许可。
&lt;/p&gt;&lt;/blockquote&gt;

&lt;h3&gt;演示blog&lt;/h3&gt;

&lt;p&gt;&lt;a title="演示blog" href="http://demos.luliban.com/haruhiv2/"&gt;Haruhi Suzumiya 'Red and Black' v2&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;下载模板&lt;/h3&gt;

&lt;div&gt;
&lt;a title="下载模板" href="http://luliban.com/attachments/haruhiv2.tar.gz"&gt;
&lt;img src="http://luliban.com/images/download90.gif" alt="下载模板" style="border:none;"/&gt;
&lt;/a&gt;
&lt;/div&gt;

&lt;h3&gt;安装&lt;/h3&gt;

&lt;p&gt;
解压缩haruhiv2.tar.gz，将得到两个文件夹iconset72/和images/，以及7个文件template.html，haruhiv2.css，haruhiv2iehacks.css，haruhiv2.js，prototype.js，effects.js和readme.pdf。&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;iconset72/：目录下有模板默认的widget dock的图标。如果你想继续使用的话，上传到FTP服务器的/images/目录。需要说明的是，这些图标来自&lt;a href="http://www.gnome-look.org/content/show.php/Mac4Lin+Leopard+GTK+Icon+Theme?content=68413"&gt;Mac4Lin Leopard GTK Icon Theme&lt;/a&gt;，我为它们加上了倒影，大小为72x108px。推荐大家扩展dock的时候仍然用这个主题的图标，因为它们不仅漂亮，而且风格统一。&lt;/li&gt;

&lt;li&gt;images/：目录下的是背景图片，同样上传到/images/。&lt;/li&gt;

&lt;li&gt;template.html：HTML模板。接下来我们要修改的基本上就是这个文件了。&lt;/li&gt;

&lt;li&gt;haruhiv2.css：CSS层叠样式表。如果你已经把图片（包括背景和图标）上传到/images/，那么就不用改它了，不然你需要把文件里的URL指向相应的图片目录。上传到服务器的/styles/目录。&lt;/li&gt;

&lt;li&gt;haruhiv2iehack.css：IE专用的CSS，同样上传到/styles/。&lt;/li&gt;

&lt;li&gt;haruhiv2.js：Javascript脚本。上传到/scripts/目录。&lt;/li&gt;

&lt;li&gt;prototype.js：Prototype javascript框架。上传到/scripts/目录。&lt;/li&gt;

&lt;li&gt;effects.js：Script.aculo.us javascript框架的效果库。上传到/scripts/目录。&lt;/li&gt;

&lt;li&gt;readme.pdf：这篇文章的PDF版本。&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;自定义模板&lt;/h3&gt;

&lt;p&gt;&lt;b&gt;“红黑「春日」v2”模板需要自定义的部分有：导航栏（navigation），摘录栏（sinppets）和小玩意儿坞（widget dock）。&lt;/b&gt;导航栏位于整个页面的右上角，将网站的布局清晰的呈现在用户的面前。摘录栏在blog标题和导航栏下方，分为左中右三个，你既可以在这里放Twitter饭否叽歪，也可以放blog feeds，将你和blog的个性自信的表现出来。小玩意儿坞总是在页面底部，它将大量的widgets收藏其中，只在用户点击时展开相应的widget，节省了网络带宽之外，更重要的是有Fisheye效果的dock，显然很酷。（wyt：不过，可怜的IE6不会看到这个效果，因为IE6不支持有透明度的png图片）&lt;br /&gt;
&lt;br /&gt;

接下来将介绍三者的自定义方法。&lt;/p&gt;

&lt;h4&gt;导航栏 Navigation&lt;/h4&gt;

&lt;p&gt;导航栏在&amp;lt;div id="navigation" /&amp;gt;中，每个tab应该是class="nabs"的SPAN。示例如下：&lt;/p&gt;

&lt;pre class="codes"&gt;&lt;code&gt;&amp;lt;!-- Begin Navigation --&amp;gt;
&amp;lt;div id="navigation"&amp;gt;

    &amp;lt;span id="homepage" class="navs"&amp;gt;
        &amp;lt;a href="&amp;lt;$BlogURL$&amp;gt;"&amp;gt;首页&amp;lt;/a&amp;gt;
    &amp;lt;/span&amp;gt;

    ... ...

&amp;lt;/div&amp;gt;
&amp;lt;!-- End Navigation --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;h4&gt;摘录栏 Snippets&lt;/h4&gt;

&lt;p&gt;摘录栏在&amp;lt;div id="snippets" /&amp;gt;中，它包括标题和列表，标题的class="snippet-title"，列表的class="snippet-list"。不过根据实际情况的需要，你也可以把列表改成一段文字或图片，只要小心不要超过原有的大小破坏页面布局，就可以了。示例如下：&lt;/p&gt;

&lt;pre class="codes"&gt;&lt;code&gt;&amp;lt;!-- Begin Snippets --&amp;gt;
&amp;lt;div id="snippets"&amp;gt;

    ... ...

    &amp;lt;!-- Right snippet --&amp;gt;
    &amp;lt;h4 class="snippet-title"&amp;gt;订阅我的博客&amp;lt;/h4&amp;gt;

    &amp;lt;ul class="snippet-list"&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="http://luliban.com/blog/feeds/atom.xml"&amp;gt;原始feed&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="http://feed.luliban.com/"&amp;gt;Feedsky feed&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="http://feeds.feedburner.com/luliban"&amp;gt;Feedburner feed&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;

&amp;lt;/div&amp;gt;
&amp;lt;!-- End Snippets --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;h4&gt;小玩意儿坞 Widget Dock&lt;/h4&gt;

&lt;p&gt;Widget Dock比前面两者复杂一些，需要修改三个地方。首先是&amp;lt;div id="dock-container" /&amp;gt;。Dock container中定义了所有dock item，是一个id="dock-&lt;b&gt;name&lt;/b&gt;"的SPAN，其中IMG的SRC就是之前上传的图标URL，宽度默认为48，高度为72。其次是&amp;lt;ul id="widget-list" /&amp;gt;。Widget list中有widgets的实际HTML代码，每个widget应由一个id="widget-&lt;b&gt;name&lt;/b&gt;"和class="widget"的LI包装。最后是Javascript脚本haruhiv2.js，其中第二行中定义的widgets变量，包含了所有widget的&lt;b&gt;name&lt;/b&gt;。&lt;b&gt;模板中的dock container和widget list的name，必须和Javascript脚本中widgets的name相对应。&lt;/b&gt;&lt;br /&gt;

&lt;br /&gt;

示例如下：&lt;/p&gt;

&lt;h5&gt;template.html&lt;/h5&gt;
&lt;pre class="codes"&gt;&lt;code&gt;&amp;lt;!-- Begin Dock container --&amp;gt;
&amp;lt;div id="dock-container"&amp;gt;

    ... ...

    &amp;lt;span &lt;b&gt;id="dock-profile"&lt;/b&gt;&amp;gt;

        &amp;lt;a href="&amp;lt;$BlogOwnerProfileURL$&amp;gt;" class="dock-item" onclick="return false;"&amp;gt;
            &amp;lt;img src="/images/profile.png" width="48" height="72" alt="Profile" /&amp;gt;
        &amp;lt;/a&amp;gt;

    &amp;lt;/span&amp;gt;

    ... ...

&amp;lt;/div&amp;gt;
&amp;lt;!-- End Widget dock --&amp;gt;

... ...


&amp;lt;!-- Begin Widget list --&amp;gt;
&amp;lt;ul id="widget-list"&amp;gt;

    &amp;lt;!-- Begin Widget profile --&amp;gt;
    &amp;lt;li &lt;b&gt;id="widget-profile"&lt;/b&gt; class="widget" style="display: none;"&amp;gt;

        &amp;lt;h4 class="widget-title"&amp;gt;My Profile&amp;lt;/h4&amp;gt;

        &amp;lt;!-- Profile container --&amp;gt;
        &amp;lt;$BlogMemberProfile$&amp;gt;

    &amp;lt;/li&amp;gt;
    &amp;lt;!-- End Widget profile --&amp;gt;

    ... ...

&amp;lt;/ul&amp;gt;
&amp;lt;!-- End Widget list --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;br /&gt;

&lt;h5&gt;haruhiv2.js&lt;/h5&gt;

&lt;pre class="codes"&gt;&lt;code&gt;// A list of widgets' names
var widgets = [&lt;b&gt;'profile'&lt;/b&gt;,'search','recent-posts','gallery','favorite-blogs','rss','about'];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后只需要将模板发布，就大功告成了。有什么意见，建议和砖头，欢迎留言。
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
P.S. 把feed换了个目录，Feedsky又要重新验证了：869bc65b&lt;/p&gt;</description><link>http://luliban.com/blog/2008/03/bloggerv2.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-360564763380423408</guid><pubDate>Sat, 15 Mar 2008 16:19:00 +0000</pubDate><atom:updated>2008-04-04T17:11:28.867+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>javascript</category><category domain='http://www.blogger.com/atom/ns#'>blogger</category><category domain='http://www.blogger.com/atom/ns#'>python</category><title>有备无患：为Blogger传统模板（FTP发布）添加标签云</title><description>&lt;p&gt;Blogger的传统模板一直不支持标签的输出。虽然有&lt;a title="hack" href="http://phydeaux3.blogspot.com/2007/05/automatic-list-of-labels-for-classic.html" rel="nofollow"&gt;hack&lt;/a&gt;可以从feed中抓取标签列表，但不能分析每个标签所含的文章数，所以仍然不能生成随标签使用频率而字体大小和颜色变化的真正意义上的“标签云”。&lt;br /&gt;
&lt;br /&gt;
所以，我写了一个python的自动化脚本，可以从ftp服务器上分析blogger文件，计算出每个标签的使用频率，并将这些信息保存在服务器上，供模板中的javascript脚本调用。效果么，你可以看看这个&lt;a href="http://demos.luliban.com/konatag/index.html"&gt;演示网页&lt;/a&gt;，或者点击我blog下方widget dock左起第四个“文件夹”图标（wyt：还在用IE6的同学们应该看不到。因为IE6不支持widget dock用的透明png，所以我把widget dock给隐藏起来了，实际上“标签云”还在那里。IE7、Firefox、Opera、Safari等应该都没问题）&lt;br /&gt;
&lt;br /&gt;
使用方法很简单，要执行脚本首先得有python。Linux大多默认安装，Windoze上安装Python的方法见&lt;a title="1.2. Windows 上的 Python" href="http://www.woodpecker.org.cn/diveintopython/installing_python/windows.html" rel="nofollow"&gt;这篇文章&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
另外，脚本中用到了&lt;a title="BeautifulSoup" href="http://www.crummy.com/software/BeautifulSoup/" rel="nofollow"&gt;BeautifulSoup&lt;/a&gt;分析HTML文件，&lt;a title="SimpleJSON" href="http://svn.red-bean.com/bob/simplejson/tags/simplejson-1.3/docs/index.html" rel="nofollow"&gt;simpleJSON&lt;/a&gt;生成JSON格式的数据流。你也必须安装这两个python模块。如果你和我一样用Gentoo，just emerge it。Windoze用户可以用easy install工具快速安装。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;接下来下载压缩文件：&lt;a title="konatag.tar.gz" href="http://luliban.com/attachments/konatag.tar.gz" id="yaad"&gt;konatag.tar.gz&lt;/a&gt;，解压缩用得到两个文件konatag.py和konatag.js。&lt;/b&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div&gt;&lt;a title="konatag.tar.gz" href="http://luliban.com/attachments/konatag.tar.gz"&gt;&lt;img src="http://luliban.com/images/download90.gif" alt="konatag.tar.gz" style="border:none;"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;
修改konatag.py其中的ftp服务器，用户名，密码以及归档（archives）的路径参数，以便脚本可以访问你的ftp服务器。&lt;br /&gt;&lt;/p&gt;

&lt;pre class="codes"&gt;&lt;code&gt;if __name__ == '__main__':
    options = dict(
        server='yourftpserver',    # 服务器地址不用包含ftp://
        username='yourusername',   # 用户名
         password='yourpassword',   # 密码
         path='/path/to/archives/'  # blogger的归档路径
    )
    ftp = FTPServer(**options)
    blogger = Blogger(ftp)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;保存退出之后，在终端（wyt：也就是Windoze的命令行）执行：&lt;br /&gt;&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;&lt;b&gt;$ python konatag.py&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
事实上，如果高兴的话，可以像我一样把这个脚本扔到crontab的计划任务中，每星期执行一次自动更新你的Blogger的标签信息。&lt;br /&gt;

&lt;br /&gt;

执行完脚本之后，登录ftp，在你的blogger归档目录中会多出一个konatag.json的文件，这是一个包含JSON格式数据的文本文件。不用去管它，将konatag.js上传到任何你能记住的目录。接下来我们将要修改Blogger模板。需要说明的是，konatag.js使用&lt;a href="http://prototypejs.org/" rel="nofollow"&gt;prototype库&lt;/a&gt;，所以在模板里还必须包含相应的js文件。&lt;br /&gt;
&lt;/p&gt;


&lt;pre class="codes"&gt;&lt;code&gt;&amp;lt;!-- 将js的调用加入&amp;lt;head&amp;gt; --&amp;gt;
&amp;lt;script type="text/javascript" src="/scripts/prototype.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type="text/javascript" src="/scripts/konatag.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- or: 如果你想直接调用我这儿的javascript脚本的话 --&amp;gt;
&amp;lt;script type="text/javascript" src="http://demos.luliban.com/scripts/prototype.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type="text/javascript" src="http://demos.luliban.com/scripts/konatag.js"&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;!-- 这个div是“标签云”的容器，加入&amp;lt;body&amp;gt;中
     id可以随意指定，但要与下面的javascript对象的第一个参数相一致 --&amp;gt;
&amp;lt;div id="konatag"&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script type=text/javascript&amp;gt;//&amp;lt;![CDATA[ 
    // Konatag的第一个参数是刚才提到的div的id
    // 第二个参数是脚本上传的JSON数据的路径
    new Konatag('konatag', '/blog/archives/konatag.json', {
       startSize: 13,    // 使用频率最低的标签的字号，单位是px，请随意设定，下同
        stopSize: 24,     // 使用频率最高的标签的字号，单位同样是px
       startColor: '#3d81ee',    // 使用频率最低的标签的颜色
        stopColor: '#930fe3'      // 使用频率最高的标签的颜色
    });
//]]&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;
&lt;b&gt;这样就大功告成，只需要重新发布一遍模板就可以了。有什么意见，建议和砖头，欢迎留言。&lt;/b&gt;
&lt;/p&gt;</description><link>http://luliban.com/blog/2008/03/bloggerftp.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-3514757381504290312</guid><pubDate>Sat, 08 Mar 2008 17:02:00 +0000</pubDate><atom:updated>2008-04-02T23:01:54.358+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>linux</category><category domain='http://www.blogger.com/atom/ns#'>互联网</category><title>有备无患：用脚本自动更新Twitter</title><description>&lt;p&gt;

如果你也是一个&lt;a href="http://twitter.com/"&gt;Twitter&lt;/a&gt;中毒者，那么也会不消停的在网页上在IM上在手机上，或许还会在第三方服务上发送Twitter消息。不过，你有没有试过在terminal上"骚扰"你的朋友们呢？&lt;a href="http://blogs.tech-recipes.com/shamanstears/2008/03/04/ubuntu-posting-to-twitter-from-the-terminal-window/"&gt;Digital Streets&lt;/a&gt;上有一个很骇很饥渴(wyt:英议very hack very geek)的方法，只需输入一行命令：&lt;br /&gt;

&lt;/p&gt;

&lt;pre class="commands"&gt;&lt;code&gt;$ curl -u yourusername:yourpassword -d status="Your Message Here" http://twitter.com/statuses/update.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;

如果你觉得每次输入一长串用户和密码很麻烦，也可以把他们保存起来写个脚本：&lt;br /&gt;

&lt;/p&gt;

&lt;pre class="codes"&gt;&lt;code&gt;#!/bin/bash
#

USERNAME=yourusername
PASSWORD=yourpassword

if [ $# != 1 ]
then
echo "Usage: ${0##*/} your tweet as you would like it to read"
exit 1
fi
tweet=$1
curl -u $USERNAME:$PASSWORD -d status="$1″ http://twitter.com/statuses/update.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;

如果这样还不能让你提起精神，我们还可以把这段脚本修改一下加到cron里，让它每周每天甚至每小时自动发送消息。比方说，你打算在twitter上作一个奥运会倒计时，可以试试下面这个脚本，把它放到/etc/cron.daily目录下。然后，你的Twitter每天就会更新这样一段消息，"距离北京奥运会开幕，还有 DDD 天 HH 小时。"：&lt;br /&gt;

&lt;/p&gt;

&lt;pre class="codes"&gt;&lt;code&gt;#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 记得替换你的用户名和密码
USERNAME="yourusername"
PASSWORD="yourpassword"
# 奥运会将于2008年8月8日开幕，不过我家黄历说"诸事不宜"
OPENDATE="20080808"

declare -i sec_open=`date --date="$OPENDATE" +%s`
declare -i sec_now=`date +%s`

declare -i sec_total=$(($sec_open-$sec_now))
declare -i date_day=$(($sec_total/60/60/24))

if [ "$sec_total" -lt "0" ]; then
    declare status="丫睡过头了吧，北京奥运会都开幕 $((-1*$date_day)) 天了。。。"
else
    declare -i date_hour=$(($(($sec_total-$date_day*60*60*24))/60/60))
    declare status="距离北京奥运会开幕，还有 $date_day 天 $date_hour 小时。"
fi

curl -u $USERNAME:$PASSWORD -d status="$status" http://twitter.com/statuses/update.xml
&lt;/code&gt;&lt;/pre&gt;</description><link>http://luliban.com/blog/2008/03/twitter.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-941734178736958268</guid><pubDate>Fri, 07 Mar 2008 15:54:00 +0000</pubDate><atom:updated>2008-04-02T23:03:18.500+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>nba</category><title>悠言悠闲：论男女球员防守时的区别</title><description>&lt;p&gt;

OK，我不是想写一篇学术论文，或者进行什么两性方面的讨论，只是前几天Digg上一前一后的两张照片够搞笑。Korver，有这张照片为证，你这辈子没指望当上什么最佳防守人了，有没有听见WNBA在召唤你？Blake，Nowitzki的锁喉功至少还有点含金量（罚款），你这掐脖子功到底是从哪里得来的，莫非“养家狂人”在你家作客？&lt;br /&gt;
&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://luliban.com/blog/uploaded_images/nba-korver-play-like-a-girl-716271.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://luliban.com/blog/uploaded_images/nba-korver-play-like-a-girl-716271.jpg" alt="korver" /&gt;&lt;/a&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://luliban.com/blog/uploaded_images/nba-how-to-defense-kobe-747294.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://luliban.com/blog/uploaded_images/nba-how-to-defense-kobe-747294.jpg" alt="blake" /&gt;&lt;/a&gt;</description><link>http://luliban.com/blog/2008/03/blog-post.html</link><author>noreply@blogger.com (wyt)</author></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7286295415331143255.post-1564704686246353800</guid><pubDate>Sun, 03 Feb 2008 17:42:00 +0000</pubDate><atom:updated>2008-04-06T19:39:17.488+08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>nba</category><category domain='http://www.blogger.com/atom/ns#'>火箭</category><title>南言北哲：Scola 和 Hayes，上天和入地？</title><description>&lt;p&gt;

预测的秘诀在于，只要预测得足够多，总会有一些命中。&lt;br /&gt;

&lt;br /&gt;

尽管Scola在美锦赛上表现出色，&lt;a title="我当时仍说" href="http://luliban.com/blog/2007/09/scola.html"&gt;我当时仍说&lt;/a&gt;， “至少在全明星赛之前，Chuck Hayes的篮板球会和Scola分享一半的出场时间。”&lt;b&gt;截至对今天对雄鹿一役，Chuck Hayes共出场977分钟，Luis Scola共出场975分钟。&lt;/b&gt;两位大前锋有几乎相等的出场时间，我是不是黄大仙附身了？:)&lt;br /&gt;

&lt;br /&gt;

Hayes是球队最好的外围篮板球手和最好的低位防守者，按&lt;a title="火箭总经理Morey的说法" href="http://bbs.hoopchina.com/htm_data/44/0801/282265.html" rel="nofollow"&gt;火箭总经理Morey的说法&lt;/a&gt;，“Hayes的防守实力在他这个位置上是全NBA前五名，精英级别的防守型球员。”Scola是一个真正的大前锋，受限于移动弹跳和臂展，他很难成为更高级别的防守人，但在进攻端他能内突外投能背身单打，一旦熟悉了他的对手和NBA裁判以后，阿根廷人“影帝级”的假动作将为火箭赢得更多的罚球线得分。&lt;br /&gt;

&lt;br /&gt;

两者谁将在全明星后有更多的戏分，这无论是对主教练Rick Adelman，还是对火箭球迷来说，都是一个幸福的烦恼。CCTV和不少球迷似乎认为，自从和勇士的比赛中Scola坐上主力的位置，Hayes将很难翻身，甚至有可能被菜鸟Carl Landry超越。而Scola最近三场比赛连续首发，Hayes场均只有6.7分钟出场时间，似乎验证了这个观点。&lt;br /&gt;

&lt;br /&gt;

但从Hayes和Scola的每40分钟数据和&lt;a title="PER值" href="http://www.basketball-reference.com/about/per.html" rel="nofollow"&gt;PER值&lt;/a&gt;来看，Scola在进攻上更有效率，每40分钟能比Hayes多拿整整10分。可即使如此，他们的PER值仅仅相差3，原因是Hayes在篮板球，防守和失误上比Scola做的更好。Hayes对长篮板似乎有敏锐的嗅觉，总是轻松的捡到弹框而出的皮球，他能以1.98米的身高抢到场上10%的进攻篮板和19%的防守篮板。Scola略逊一筹，前后场篮板球率分别为8%和16%。当Hayes在场上时，火箭的对手们每100次出手只能得到98.9分，而当他下场后，火箭的对手们能得到107.1分，