| 1 | // |
|---|
| 2 | // pino.js - Open livedoor Reader (and clone server) pinned items - |
|---|
| 3 | // |
|---|
| 4 | // LICENSE: {{{ |
|---|
| 5 | // |
|---|
| 6 | // This software distributable under the terms of an MIT-style license. |
|---|
| 7 | // |
|---|
| 8 | // Copyright (c) 2009 snaka<snaka.gml@gmail.com> |
|---|
| 9 | // |
|---|
| 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
|---|
| 11 | // of this software and associated documentation files (the "Software"), to deal |
|---|
| 12 | // in the Software without restriction, including without limitation the rights |
|---|
| 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|---|
| 14 | // copies of the Software, and to permit persons to whom the Software is |
|---|
| 15 | // furnished to do so, subject to the following conditions: |
|---|
| 16 | // |
|---|
| 17 | // The above copyright notice and this permission notice shall be included in |
|---|
| 18 | // all copies or substantial portions of the Software. |
|---|
| 19 | // |
|---|
| 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|---|
| 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|---|
| 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|---|
| 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|---|
| 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|---|
| 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|---|
| 26 | // THE SOFTWARE. |
|---|
| 27 | // |
|---|
| 28 | // OSI page : http://opensource.org/licenses/mit-license.php |
|---|
| 29 | // Japanese : http://sourceforge.jp/projects/opensource/wiki/licenses%2FMIT_license |
|---|
| 30 | // |
|---|
| 31 | // }}} |
|---|
| 32 | // PLUGIN INFO: {{{ |
|---|
| 33 | var PLUGIN_INFO = |
|---|
| 34 | <VimperatorPlugin> |
|---|
| 35 | <name>{NAME}</name> |
|---|
| 36 | <description>Open livedoor Reader pinned items</description> |
|---|
| 37 | <description lang="ja">livedoor Reader でピンを立てたページを開く</description> |
|---|
| 38 | <minVersion>2.3</minVersion> |
|---|
| 39 | <maxVersion>2.4</maxVersion> |
|---|
| 40 | <updateURL>http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/pino.js</updateURL> |
|---|
| 41 | <require type="plugin">_libly.js</require> |
|---|
| 42 | <author mail="snaka.gml@gmail.com" homepage="http://vimperator.g.hatena.ne.jp/snaka72/">snaka</author> |
|---|
| 43 | <license>MIT style license</license> |
|---|
| 44 | <version>1.4.0</version> |
|---|
| 45 | <detail><![CDATA[ |
|---|
| 46 | == Subject == |
|---|
| 47 | Open livedoor Reader pinned items. |
|---|
| 48 | |
|---|
| 49 | == Commands == |
|---|
| 50 | :[count]pino |
|---|
| 51 | Following option is avilable. |
|---|
| 52 | -list: |
|---|
| 53 | Show pinned item list. |
|---|
| 54 | |
|---|
| 55 | == Global variables == |
|---|
| 56 | g:pinoOpenItemsCount: |
|---|
| 57 | default: 5 |
|---|
| 58 | |
|---|
| 59 | g:pinoOpenBehavior: |
|---|
| 60 | default: liberator.NEW_BACKGROUND_TAB |
|---|
| 61 | |
|---|
| 62 | g:pinoAscendingOrder: |
|---|
| 63 | default: "false" |
|---|
| 64 | |
|---|
| 65 | g:pinoBaseURL: |
|---|
| 66 | If you want to use fastladder, set "http://fastladder.com" into this variable. |
|---|
| 67 | default: "http://reader.livedoor.com" |
|---|
| 68 | |
|---|
| 69 | g:pinoOpenInterval: |
|---|
| 70 | Interval of opening tabs. (msec) |
|---|
| 71 | |
|---|
| 72 | == API == |
|---|
| 73 | plugins.pino.items(): |
|---|
| 74 | Return pinned items list array. |
|---|
| 75 | Each item is following structure. |
|---|
| 76 | >|| |
|---|
| 77 | { |
|---|
| 78 | created_on : (create date), |
|---|
| 79 | link : (url), |
|---|
| 80 | title : (page title) |
|---|
| 81 | } |
|---|
| 82 | ||< |
|---|
| 83 | |
|---|
| 84 | plugins.pino.shift(): |
|---|
| 85 | Return first item and remove pin. |
|---|
| 86 | |
|---|
| 87 | plugins.pino.remove(link): |
|---|
| 88 | Remove pin from item that matched by 'link'. |
|---|
| 89 | |
|---|
| 90 | ]]></detail> |
|---|
| 91 | <detail lang="ja"><![CDATA[ |
|---|
| 92 | == 概要 == |
|---|
| 93 | livedoor Reader でピンを立てた記事をVimperatorのコマンドラインから開く |
|---|
| 94 | ことができます。 |
|---|
| 95 | |
|---|
| 96 | == コマンド == |
|---|
| 97 | :[count]pino: |
|---|
| 98 | そのまま<Enter>で先頭のn件(デフォルト5件、グローバル変数で調整可能) |
|---|
| 99 | をバックグラウンドのタブで開きます。 |
|---|
| 100 | <TAB>で補完候補の一覧にピンを立てた記事の一覧から選択することもできます。 |
|---|
| 101 | count を指定すると、その件数だけ開きます。 |
|---|
| 102 | 以下のオプションが指定可能です。 |
|---|
| 103 | -list: |
|---|
| 104 | ピンの一覧を表示します。 |
|---|
| 105 | |
|---|
| 106 | == グローバル変数 == |
|---|
| 107 | g:pinoOpenItemsCount: |
|---|
| 108 | 一度に開くピンの数 |
|---|
| 109 | default: 5 |
|---|
| 110 | |
|---|
| 111 | g:pinoOpenBehavior: |
|---|
| 112 | ピンを開くときの挙動、liberator.open()の第2引数として使用する値 |
|---|
| 113 | 参考)http://wiki.livedoor.jp/shin_yan/d/liberator%282%2e0%29#content_34 |
|---|
| 114 | default: liberator.NEW_BACKGROUND_TAB |
|---|
| 115 | |
|---|
| 116 | g:pinoAscendingOrder: |
|---|
| 117 | ピンの一覧の表示順を昇順(古い順)とするかどうか |
|---|
| 118 | default: "false" (新しい順) |
|---|
| 119 | |
|---|
| 120 | g:pinoBaseURL: |
|---|
| 121 | fastladder を使う場合は、この変数を "http://fastladder.com" とする。 |
|---|
| 122 | default: "http://reader.livedoor.com" |
|---|
| 123 | |
|---|
| 124 | g:pinoOpenInterval: |
|---|
| 125 | タブを開く間隔(ミリ秒) |
|---|
| 126 | |
|---|
| 127 | == API == |
|---|
| 128 | plugins.pino.items(): |
|---|
| 129 | ピンの一覧を配列で取得する。 |
|---|
| 130 | ピンのデータ構造は以下のとおりとなっている。 |
|---|
| 131 | >|| |
|---|
| 132 | { |
|---|
| 133 | created_on : (create date), |
|---|
| 134 | link : (url), |
|---|
| 135 | title : (page title) |
|---|
| 136 | } |
|---|
| 137 | ||< |
|---|
| 138 | |
|---|
| 139 | plugins.pino.shift(): |
|---|
| 140 | 先頭のピンを取得して、そのピンを一覧から削除する。 |
|---|
| 141 | |
|---|
| 142 | plugins.pino.remove(link): |
|---|
| 143 | linkに該当するピンを一覧から削除する。 |
|---|
| 144 | |
|---|
| 145 | ]]></detail> |
|---|
| 146 | </VimperatorPlugin>; |
|---|
| 147 | // }}} |
|---|
| 148 | let self = liberator.plugins.pino = (function() { |
|---|
| 149 | // COMMAND /////////////////////////////////////////////////////// {{{ |
|---|
| 150 | commands.addUserCommand( |
|---|
| 151 | ["pinneditemopen", "pino"], |
|---|
| 152 | "Open livedoor Reader(and clone server) pinned item", |
|---|
| 153 | function(args) { |
|---|
| 154 | let pins = new Pins(); |
|---|
| 155 | let items = pins.items(); |
|---|
| 156 | if (!items || items.length == 0) { |
|---|
| 157 | liberator.echo("Pinned item doesn't exists."); |
|---|
| 158 | return; |
|---|
| 159 | } |
|---|
| 160 | |
|---|
| 161 | if (args["-list"]) { |
|---|
| 162 | //let items = pins.items(); |
|---|
| 163 | let list = <div>{items.length} items. |
|---|
| 164 | <ul>{ |
|---|
| 165 | [<li><a href={i.link}>{i.title}</a><br/></li> |
|---|
| 166 | for each (i in items) |
|---|
| 167 | ].reduce(function(a, b) a + b) |
|---|
| 168 | }</ul> |
|---|
| 169 | </div>; |
|---|
| 170 | liberator.echo(list, commandline.FORCE_MULTILINE); |
|---|
| 171 | return; |
|---|
| 172 | } |
|---|
| 173 | |
|---|
| 174 | if (args.string == "") { |
|---|
| 175 | let pin; |
|---|
| 176 | let max = (args.count >= 1) ? args.count : openItemsCount(); |
|---|
| 177 | for(let i = 0; i < max; i++) { |
|---|
| 178 | if (!(pin = pins.shift())) |
|---|
| 179 | break; |
|---|
| 180 | setTimeout(function(link) liberator.open(link, openBehavior()), openInterval() * i, pin.link); |
|---|
| 181 | } |
|---|
| 182 | } |
|---|
| 183 | else { |
|---|
| 184 | liberator.open(args.string, openBehavior()); |
|---|
| 185 | pins.remove(args.string); |
|---|
| 186 | } |
|---|
| 187 | }, |
|---|
| 188 | { |
|---|
| 189 | literal: 0, |
|---|
| 190 | count: true, |
|---|
| 191 | completer: function(context) { |
|---|
| 192 | var pins = new Pins(); |
|---|
| 193 | context.title = ["url", "title"]; |
|---|
| 194 | context.filters = [CompletionContext.Filter.textDescription]; |
|---|
| 195 | context.anchored = false; |
|---|
| 196 | context.completions = [ |
|---|
| 197 | [i.link, i.title] for each (i in pins.items()) |
|---|
| 198 | ]; |
|---|
| 199 | }, |
|---|
| 200 | options: [ |
|---|
| 201 | [["-list", "-l"], commands.OPTION_NOARG] |
|---|
| 202 | ] |
|---|
| 203 | }, |
|---|
| 204 | true // for Debug |
|---|
| 205 | ); |
|---|
| 206 | // }}} |
|---|
| 207 | // GLOBAL VARIABLES ////////////////////////////////////////////// {{{ |
|---|
| 208 | var gv = liberator.globalVariables; |
|---|
| 209 | function openItemsCount() |
|---|
| 210 | gv.pinoOpenItemsCount || 5; |
|---|
| 211 | |
|---|
| 212 | function ascending() |
|---|
| 213 | window.eval(gv.pinoAscendingOrder) == true; // default: false |
|---|
| 214 | |
|---|
| 215 | function openBehavior() |
|---|
| 216 | window.eval(gv.pinoOpenBehavior) || liberator.NEW_BACKGROUND_TAB; |
|---|
| 217 | |
|---|
| 218 | function baseURL() |
|---|
| 219 | gv.pinoBaseURL || "http://reader.livedoor.com"; |
|---|
| 220 | |
|---|
| 221 | function openInterval() |
|---|
| 222 | gv.pinoOpenInterval || 200; |
|---|
| 223 | |
|---|
| 224 | // }}} |
|---|
| 225 | // CLASS ///////////////////////////////////////////////////////// {{{ |
|---|
| 226 | |
|---|
| 227 | function Pins() { |
|---|
| 228 | this.cache = null; |
|---|
| 229 | this.apiKey = getLDRApiKey(); |
|---|
| 230 | this.sortOrder = ascending() |
|---|
| 231 | ? function(a, b) (a.created_on < b.created_on ? -1 : 1) |
|---|
| 232 | : function(a, b) (a.created_on > b.created_on ? -1 : 1); |
|---|
| 233 | } |
|---|
| 234 | Pins.prototype = { |
|---|
| 235 | items : function() { |
|---|
| 236 | let result = this.cache |
|---|
| 237 | ? this.cache |
|---|
| 238 | : this.cache = this._getPinnedItems(); |
|---|
| 239 | return (result || []).sort(this.sortOrder); |
|---|
| 240 | }, |
|---|
| 241 | |
|---|
| 242 | shift : function() { |
|---|
| 243 | if (this.items().length == 0) |
|---|
| 244 | return null; |
|---|
| 245 | var pin = this.items().shift(); |
|---|
| 246 | this.remove(pin.link); |
|---|
| 247 | return pin; |
|---|
| 248 | }, |
|---|
| 249 | |
|---|
| 250 | remove : function(link) { |
|---|
| 251 | var unescapedLink = unescapeHTML(link); |
|---|
| 252 | var request = new libly.Request( |
|---|
| 253 | baseURL() + "/api/pin/remove", |
|---|
| 254 | { |
|---|
| 255 | //Cookie: "reader_sid=" + this.apiKey, |
|---|
| 256 | //Referer: "http://reader.livedoor.com/reader/" |
|---|
| 257 | }, |
|---|
| 258 | { |
|---|
| 259 | postBody: toQuery({link: unescapedLink, ApiKey: this.apiKey}) |
|---|
| 260 | } |
|---|
| 261 | ); |
|---|
| 262 | |
|---|
| 263 | request.addEventListener("onSuccess", function(data) { |
|---|
| 264 | liberator.log("Removed pin from '" + link + "' was succeeded."); |
|---|
| 265 | }); |
|---|
| 266 | request.addEventListener("onFailure", function(data) { |
|---|
| 267 | liberator.echoerr("Cannot remove pin"); |
|---|
| 268 | }); |
|---|
| 269 | request.post(); |
|---|
| 270 | }, |
|---|
| 271 | |
|---|
| 272 | _getPinnedItems : function() { |
|---|
| 273 | var result = null; |
|---|
| 274 | var request = new libly.Request( |
|---|
| 275 | baseURL() + "/api/pin/all", |
|---|
| 276 | null, |
|---|
| 277 | { |
|---|
| 278 | asynchronous: false, |
|---|
| 279 | postBody: toQuery({ApiKey: this.apiKey}) |
|---|
| 280 | } |
|---|
| 281 | ); |
|---|
| 282 | |
|---|
| 283 | request.addEventListener("onSuccess", function(data) { |
|---|
| 284 | if (isLoginPage(data)) { |
|---|
| 285 | liberator.echoerr("Can't get pinned list. Maybe you should login to livedoor."); |
|---|
| 286 | return; |
|---|
| 287 | } |
|---|
| 288 | result = liberator.eval(data.responseText); |
|---|
| 289 | }); |
|---|
| 290 | request.addEventListener("onFailure", function(data) { |
|---|
| 291 | liberator.echoerr("Can't get pinned list!!!"); |
|---|
| 292 | }); |
|---|
| 293 | request.post(); |
|---|
| 294 | |
|---|
| 295 | return result; |
|---|
| 296 | }, |
|---|
| 297 | } |
|---|
| 298 | // }}} |
|---|
| 299 | // FUNCTIONS ///////////////////////////////////////////////////// {{{ |
|---|
| 300 | var libly = plugins.libly; |
|---|
| 301 | |
|---|
| 302 | function getLDRApiKey() { |
|---|
| 303 | var ioService = Cc["@mozilla.org/network/io-service;1"] |
|---|
| 304 | .getService(Ci.nsIIOService); |
|---|
| 305 | var uri = ioService.newURI(baseURL(), null, null); |
|---|
| 306 | var channel = ioService.newChannelFromURI(uri); |
|---|
| 307 | var cookie = Cc["@mozilla.org/cookieService;1"] |
|---|
| 308 | .getService(Ci.nsICookieService) |
|---|
| 309 | .getCookieString(uri, channel); |
|---|
| 310 | var apiKey = cookie.match(/reader_sid=([^;]+)/); |
|---|
| 311 | return apiKey ? apiKey[1]: null; |
|---|
| 312 | } |
|---|
| 313 | |
|---|
| 314 | function unescapeHTML(source) { |
|---|
| 315 | var result = source; |
|---|
| 316 | [ |
|---|
| 317 | [/</g, "<"], |
|---|
| 318 | [/>/g, ">"], |
|---|
| 319 | [/&/g, "&"] |
|---|
| 320 | ].forEach( function(rule) { |
|---|
| 321 | result = result.replace(rule[0], rule[1]); |
|---|
| 322 | }); |
|---|
| 323 | return result; |
|---|
| 324 | } |
|---|
| 325 | |
|---|
| 326 | function toQuery(source) |
|---|
| 327 | [encodeURIComponent(i) + "=" + encodeURIComponent(source[i]) |
|---|
| 328 | for (i in source) |
|---|
| 329 | ].join('&'); |
|---|
| 330 | |
|---|
| 331 | function isLoginPage(response) |
|---|
| 332 | response.responseText.substr(0, 5) == '<?xml' |
|---|
| 333 | |
|---|
| 334 | // }}} |
|---|
| 335 | // API /////////////////////////////////////////////////////////// {{{ |
|---|
| 336 | return { |
|---|
| 337 | items : function() |
|---|
| 338 | (new Pins).items(), |
|---|
| 339 | |
|---|
| 340 | shift : function() |
|---|
| 341 | (new Pins).shift(), |
|---|
| 342 | |
|---|
| 343 | head : function() // @deprecated |
|---|
| 344 | self.shift(), |
|---|
| 345 | |
|---|
| 346 | remove : function(link) |
|---|
| 347 | (new Pins).remove(link), |
|---|
| 348 | }; |
|---|
| 349 | // }}} |
|---|
| 350 | })(); |
|---|
| 351 | // for backward compatibility |
|---|
| 352 | self.api = {}; |
|---|
| 353 | for each (p in "items head remove".split(' ')) |
|---|
| 354 | self.api[p] = self[p]; |
|---|
| 355 | // vim: ts=2 sw=2 et fdm=marker |
|---|