fork download
  1. //【登録場所】リンク、URLExec、選択テキスト(ユーザーポップアップのみ対応)
  2. //【ラベル】ツイートのポップアップ
  3. //【内容】ツイート、ツイッターユーザーのポップアップ
  4. // ツイートかユーザーかはURLから自動的に判別
  5. // 選択テキストから起動された場合は、それをユーザーIDとして処理する
  6. //【コマンド1】${SCRIPT:FrwS} popupTwitterInfo.js
  7. //【コマンド2】${SCRIPT:FrwS} popupTwitterInfo.js テンプレートのファイル名 (popupTweetフォルダ内の任意のテンプレートファイルを指定)
  8. //【URLExec】https?://(?:mobile\.|m\.)?twitter\.com/(?:#!/)?\w+[#/]?$ $& ${V2CSCRIPT:FrS} popupTwitterInfo.js  (ユーザー)
  9. // https?://(?:mobile\.|m\.)?twitter\.com/(?:#!/)?[^/]+/status(?:es)?/\d+ $& ${V2CSCRIPT:FrS} popupTwitterInfo.js (ツイート)
  10.  
  11. //設定(onがtrue、offがfalse)-----------------------
  12. //ポップアップの最大幅
  13. var maxPopupWidth = 400;
  14.  
  15. //マウス移動でポップアップを閉じる
  16. v2c.context.setDefaultCloseOnMouseExit(true);
  17.  
  18. //終了後に残りアクセス回数をステータスバーに表示
  19. var showRemainingHits = false;
  20.  
  21. //一度読み込んだテンプレートとtwitterデータはキャッシュし、同じデータを読み込む場合はこれを表示する。
  22. //キャッシュはV2C再起動でクリアされる。使用にはT20110522以降が必要。
  23. var cacheMode = true;
  24.  
  25. //ポップアップ上の@userクリックをスクリプトによるポップアップするかどうか
  26. var userPopup = false;
  27. //-----------------------------------------------
  28.  
  29. //デフォルトテンプレートファイル名
  30. var TEMPLATE_TWEET = "TemplateStatus.txt";
  31. var TEMPLATE_USER = "TemplateUser.txt";
  32.  
  33. //キャッシュ管理
  34. var cacheData = {
  35. data: null,
  36. load: function() {
  37. if (cacheMode) {
  38. this.data = v2c.getScriptObject();
  39. }
  40. if (!this.data) {
  41. this.data = {};
  42. }
  43. },
  44. set: function(key, value) {
  45. this.load();
  46. this.data[key] = value;
  47. if (cacheMode) {
  48. v2c.setScriptObject(this.data);
  49. }
  50. },
  51. get: function(key) {
  52. this.load();
  53. return this.data[key];
  54. }
  55. }
  56.  
  57. var userRx =/https?:\/\/(?:mobile\.|m\.)?twitter\.com\/(?:#!\/)?(\w+)/i;
  58. var statusRx =/https?:\/\/(?:mobile\.|m\.)?twitter\.com\/(?:#!\/)?[^\/]+\/status(?:es)?\/(\d+)/i;
  59.  
  60.  
  61. Twitter = function(accessToken, accessTokenSecret, userId) {
  62. this.requestTokenUrl = 'https://a...content-available-to-author-only...r.com/oauth/request_token';
  63. this.authorizeUrl = 'https://a...content-available-to-author-only...r.com/oauth/authorize';
  64. this.accessTokenUrl = 'https://a...content-available-to-author-only...r.com/oauth/access_token';
  65.  
  66. this.consumerKey = '3tLPbTiq8xvHXN4ZtJYsA';
  67. this.consumerSecret = 'mozHVgfsRZzFujV1vKYPMPHGmgJsvPYdzaNvwTf7So';
  68.  
  69. this.requestToken;
  70. this.requestTokenSecret;
  71.  
  72. this.accessToken = accessToken;
  73. this.accessTokenSecret = accessTokenSecret;
  74. this.userId = userId;
  75.  
  76. }
  77. Twitter.prototype.getUnixTime = function()
  78. {
  79. var time = java.lang.System.currentTimeMillis() / 1000;
  80. return Math.floor(time);
  81. }
  82. Twitter.prototype.createNonce = function()
  83. {
  84. const min = 123400;
  85. const max = 9999999;
  86.  
  87. // min以上 max未満の値を生成
  88. var v = Math.floor(Math.random() * (max - min)) + min;
  89. return v;
  90. }
  91. Twitter.prototype.concatParameters = function(dic, func)
  92. {
  93. var array = [];
  94. for(var key in dic)
  95. {
  96. array.push(key);
  97. }
  98. array.sort();
  99. var param = '';
  100. for(var i = 0; i < array.length; i++)
  101. {
  102. var key = array[i];
  103. var value = dic[key];
  104. if(key == undefined || value == undefined)
  105. throw 'undefined parameter'
  106. param += func(key, value);
  107. }
  108.  
  109. return param;
  110. }
  111. Twitter.prototype.urlEncode = function(str)
  112. {
  113. return java.net.URLEncoder.encode(str, 'UTF-8');
  114. }
  115.  
  116. Twitter.prototype.createAuthorizationHeader = function(params)
  117. {
  118. var self = this;
  119.  
  120. // oauth_* という名前のパラメータだけを連結
  121. var header = this.concatParameters(params,
  122. function(k, v)
  123. {
  124. if(k.indexOf('oauth_') == 0)
  125. return stringFormat(', {0}="{1}"', self.urlEncode(k), self.urlEncode(v));
  126. return '';
  127. });
  128. header = 'OAuth ' + header.substr(2);
  129.  
  130. return header;
  131. }
  132.  
  133. Twitter.prototype.createSignature = function(message, accessTokenSecret)
  134. {
  135. // byte-array -> base64-string
  136. // ref: http://s...content-available-to-author-only...o.com/research/1308640339
  137. function toBase64String(arr)
  138. {
  139. var seed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  140. if (arr == null)
  141. return "====";
  142. var result = "";
  143. var c = 0;
  144. for (var i = 0; i < arr.length; i++) {
  145. switch(i % 3) {
  146. case 0:
  147. result += seed.charAt((arr[i]>>2)&0x3f);
  148. c = (arr[i]&0x03)<<4;
  149. break;
  150. case 1:
  151. result += seed.charAt(c | ((arr[i]>>4)&0x0f));
  152. c = (arr[i]&0x0f)<<2;
  153. break;
  154. case 2:
  155. result += seed.charAt(c | ((arr[i]>>6)&0x0f));
  156. result += seed.charAt(arr[i]&0x3f);
  157. c = 0;
  158. break;
  159. }
  160. }
  161. if (arr.length % 3 == 1) {
  162. result += seed.charAt(c);
  163. result += "==";
  164. } else if (arr.length % 3 == 2) {
  165. result += seed.charAt(c);
  166. result += "=";
  167. }
  168. return result;
  169. }
  170.  
  171. printlnLog('-- createSignature --');
  172. dumpObject(message.parameters);
  173.  
  174. // oauth_signature を除く全てのパラメータを、sortして順番に連結
  175. var urlParam = this.concatParameters(message.parameters, function(k, v)
  176. {
  177. return ('&' + k + '=' + v);
  178. });
  179. urlParam = urlParam.substr(1); // &を抜く
  180.  
  181. // method&url&params
  182. var text = message.method + '&'
  183. + this.urlEncode(message.action)
  184. + '&'
  185. + this.urlEncode(urlParam);
  186. text = new java.lang.String(text);
  187.  
  188. var rawKey = this.urlEncode(this.consumerSecret) + '&';
  189. // アクセストークンがなくても、'&'を残しておく
  190. // ある時は、アクセストークン秘密鍵も追加してキーとして使う
  191. if(accessTokenSecret)
  192. rawKey += this.urlEncode(accessTokenSecret);
  193. rawKey = new java.lang.String(rawKey);
  194.  
  195. printlnLog('text: {0}, key: {1}', text, rawKey);
  196.  
  197. var signingKey = new javax.crypto.spec.SecretKeySpec(rawKey.getBytes(), "HmacSHA1");
  198. var mac = javax.crypto.Mac.getInstance(signingKey.getAlgorithm());
  199. mac.init(signingKey);
  200. var rawHmac = mac.doFinal(text.getBytes());
  201.  
  202. var bary = [];
  203. for(var i = 0; i < rawHmac.length; i++)
  204. {
  205. // Java の byte は signed なので、unsigned にしてやる。
  206. bary.push(rawHmac[i] & 0xFF);
  207. }
  208.  
  209. var signature = toBase64String(bary);
  210. return signature;
  211. }
  212.  
  213. // function(url : string, addParam : Array -> void)
  214. //
  215. // 認証トークン取得に使う、基本的なパラメータを指定したリクエストを生成。
  216. // addParam引数を使ってパラメータを追加できる。
  217. Twitter.prototype.getOAuthToken = function(url, addParam)
  218. {
  219. if(addParam == undefined)
  220. addParam = function(params) { };
  221. if(typeof addParam != 'function')
  222. throw 'addParamを指定する場合、function型のみが有効です。'
  223.  
  224. var accessor = {
  225. consumerSecret: this.consumerSecret
  226. };
  227.  
  228. var message = {
  229. method: "POST",
  230. action: url,
  231. parameters: {
  232. oauth_signature_method: "HMAC-SHA1",
  233. oauth_consumer_key: this.consumerKey,
  234. oauth_version: '1.0',
  235. oauth_timestamp: this.getUnixTime(),
  236. oauth_nonce: this.createNonce()
  237. }
  238. };
  239.  
  240. addParam(message.parameters);
  241.  
  242. // signature 以外のパラメータはここまでに message.parameters に入れておく。
  243. message.parameters['oauth_signature'] = this.createSignature(message);
  244.  
  245. // POST
  246. var req = v2c.createHttpRequest(message.action, '');
  247. var header = this.createAuthorizationHeader(message.parameters);
  248.  
  249. req.setRequestProperty('Content-Type', 'application/x-www-form-urlencoded');
  250. //req.setRequestProperty('Content-Length', 0);
  251. req.setRequestProperty('Authorization', header);
  252.  
  253. var res = req.getContentsAsString();
  254. if(req.responseCode != 200)
  255. {
  256. printlnLog('--- dump ---')
  257. dumpObject(req, function(name, value) { return typeof value != typeof function() { }; });
  258. throw '認証エラー';
  259. }
  260.  
  261. return res;
  262. }
  263.  
  264. Twitter.prototype.getRequestToken = function()
  265. {
  266. var res = this.getOAuthToken(this.requestTokenUrl);
  267.  
  268. var results = this.parseToken(res);
  269. // リクエストトークンを保存する
  270. this.requestToken = results['oauth_token'];
  271. this.requestTokenSecret = results['oauth_token_secret'];
  272.  
  273. printlnLog('requestToken: {0}, requestTokenSecret: {1}', this.requestToken, this.requestTokenSecret);
  274. }
  275.  
  276.  
  277. // アクセストークンを取得し、メンバに保存する。
  278. //
  279. // TODO: これやってないんだけど、なんで動くんだろう
  280. // 署名のキーに、リクエストトークン秘密鍵も加わる。
  281. //
  282. // oauth_tokenとしてリクエストトークンを、oauth_verifierとしてPINを送信
  283. // アクセストークンは永続化して使い回し可能。
  284. Twitter.prototype.getAccessToken = function(pin)
  285. {
  286. assert(this.requestToken && this.requestTokenSecret);
  287. assert(pin);
  288.  
  289. var self = this;
  290. var res = this.getOAuthToken(this.accessTokenUrl, function(params)
  291. {
  292. params['oauth_token'] = self.requestToken;
  293. params['oauth_verifier'] = pin;
  294. })
  295.  
  296. var results = this.parseToken(res);
  297.  
  298. // アクセストークンを保存する
  299. this.accessToken = results['oauth_token'];
  300. this.accessTokenSecret = results['oauth_token_secret'];
  301. this.userId = results['user_id'];
  302.  
  303. printlnLog('accessToken: {0}, accessTokenSecret: {1}, userId: {2}',
  304. this.accessToken, this.accessTokenSecret, this.userId)
  305.  
  306. assert(this.isAuthorized());
  307. }
  308.  
  309. Twitter.prototype.parseToken = function(data)
  310. {
  311. var tokens = data.split('&');
  312. var parsedToken = {};
  313.  
  314. tokens.forEach(function(token) {
  315. var kv = token.split('=');
  316.  
  317. parsedToken[kv[0]] = kv[1];
  318. });
  319.  
  320. return parsedToken;
  321. };
  322.  
  323. // public
  324. Twitter.prototype.authenticate = function()
  325. {
  326. this.getRequestToken();
  327.  
  328. // ユーザーにこのクライアントを承認してもらい、表示されたPINを入力してもらう
  329. v2c.alert('[popupTwitterInfo.js] ブラウザで開かれるページで認証を行なってください。');
  330.  
  331. var target = this.authorizeUrl + '?' + 'oauth_token=' + this.requestToken;
  332. v2c.browseURLDefExt(target);
  333. var pin = v2c.prompt('PIN を入力してください', '');
  334.  
  335. this.getAccessToken(pin);
  336.  
  337. if(!this.isAuthorized())
  338. {
  339. v2c.alert('error: 認証に失敗しました。');
  340. }
  341. }
  342.  
  343. Twitter.prototype.isAuthorized = function()
  344. {
  345. return this.accessToken != null
  346. && this.accessTokenSecret != null
  347. && this.userId != null;
  348. }
  349. Twitter.prototype.getAuthorizedData = function()
  350. {
  351. return [ this.accessToken, this.accessTokenSecret, this.userId ];
  352. }
  353.  
  354. // TODO: 今のところ GET しかサポートしてない
  355. Twitter.prototype.createRequest = function(method, api)
  356. {
  357. if(method != "GET"/* && method != "POST"*/)
  358. {
  359. throw 'サポートしていないメソッドです。'
  360. }
  361.  
  362. var accessor = {
  363. consumerSecret: this.consumerSecret,
  364. tokenSecret: this.accessTokenSecret
  365. };
  366.  
  367. var message = {
  368. method: method,
  369. action: api,
  370. parameters: {
  371. oauth_signature_method: "HMAC-SHA1",
  372. oauth_consumer_key: this.consumerKey,
  373. oauth_token: this.accessToken,
  374. oauth_version: '1.0',
  375. oauth_timestamp: this.getUnixTime(),
  376. oauth_nonce: this.createNonce()
  377. }
  378. };
  379.  
  380. // TODO: postするパラメータを parameters にpush
  381.  
  382. message.parameters['oauth_signature'] = this.createSignature(message, this.accessTokenSecret);
  383.  
  384. var target = message.action;
  385. var header = this.createAuthorizationHeader(message.parameters);
  386. printlnLog('header: {0}', header);
  387.  
  388.  
  389. var req = method == "GET"
  390. ? v2c.createHttpRequest(target)
  391. : v2c.createHttpRequest(target, requestBody);
  392.  
  393. //req.setRequestProperty('Content-Type', 'application/x-www-form-urlencoded');
  394. //req.setRequestProperty('Content-Length', 0);
  395. req.setRequestProperty('Authorization', header);
  396.  
  397. return req;
  398. }
  399.  
  400. function initializeTwitter()
  401. {
  402. var settingFileName = 'popupTwitterInfo_oauth.bin';
  403. var settings = v2c.getScriptDataFile(settingFileName);
  404. var dataArray = v2c.readLinesFromFile(settings);
  405. if(dataArray != null && 3 == dataArray.length)
  406. {
  407. var tw_ = new Twitter(dataArray[0], dataArray[1], dataArray[2]);
  408. if(tw_.isAuthorized())
  409. return tw_;
  410. }
  411.  
  412. // データがない場合、または認証されていない(ファイル書き換えたとか)場合は再度認証する。
  413. var tw = new Twitter();
  414. tw.authenticate();
  415. if(!tw.isAuthorized())
  416. {
  417. throw 'error: 認証に失敗しました。';
  418. }
  419. // save
  420. var settings = v2c.getScriptDataFile(settingFileName);
  421. var dataArray = tw.getAuthorizedData();
  422. v2c.writeLinesToFile(settings, dataArray);
  423.  
  424. printlnLog('save oauth data: {0}', dataArray);
  425.  
  426. return tw;
  427. }
  428.  
  429. var twitter = initializeTwitter();
  430.  
  431. var u = v2c.context.link;
  432. if (!u) {//選択テキストから起動された場合は、ユーザーIDとして処理する
  433. var selectedText = v2c.getSelectedText();
  434. if (selectedText && selectedText.match(/@?([0-9A-Za-z_]{1,15})/)) {
  435. u = "http://t...content-available-to-author-only...r.com/" + RegExp.$1;
  436. }
  437. }
  438. if (u) {
  439. popupTwitterInfo(u.toString(), false);
  440. }
  441. else {
  442. v2c.alert('URL取得失敗');
  443. }
  444.  
  445. function popupTwitterInfo(url,isRefresh) {
  446.  
  447. var html,key,getHtml;
  448. var tm = v2c.context.args[0];
  449.  
  450. //URLが正しいかどうかの確認
  451. if (url.match(statusRx)) {
  452. templateFilename = tm ? tm : TEMPLATE_TWEET;
  453. getHtml = getTwitterStatusHTML;
  454. }
  455. else if (url.match(userRx)) {
  456. templateFilename = tm ? tm : TEMPLATE_USER;
  457. getHtml = getTwitterUserHTML;
  458. }
  459. else {
  460. v2c.alert("非対応のURLです\n" + url);
  461. return;
  462. }
  463.  
  464. if (getHtml) {
  465. key = RegExp.$1 + '/' + templateFilename;
  466. if (!isRefresh) {
  467. //同じURLのポップアップを開いていたら終了
  468. if (v2c.context.getPopupOfID(key)) {
  469. return;
  470. }
  471. html = cacheData.get(key);
  472. }
  473. if (!html){
  474. html = getHtml(RegExp.$1, templateFilename);
  475. }
  476. }
  477. if (html) {
  478. //ポップアップの設定
  479. html = html.replace('%url%',url);//更新ボタン用
  480. v2c.context.setPopupHTML(html);
  481. v2c.context.setMaxPopupWidth(maxPopupWidth);
  482. v2c.context.setPopupID(key);
  483. v2c.context.setRedirectURL(true);
  484. v2c.context.setCloseOnLinkClick(!userPopup);
  485. v2c.context.setTrapFormSubmission(true);
  486. cacheData.set(key,html);
  487. }
  488.  
  489. if (showRemainingHits) {
  490. var limit = getJson("http://a...content-available-to-author-only...r.com/1.1/account/rate_limit_status.json");
  491. if (limit) {
  492. v2c.context.setStatusBarText("残り回数:" + limit.remaining_hits +
  493. " 次のリセット時間:" +
  494. getDateText(limit.reset_time));
  495. }
  496. }
  497. return;
  498. }
  499.  
  500. function formSubmitted(url, sm, sd) {
  501. v2c.context.closeOriginalPanel();
  502. popupTwitterInfo(url.toString(),true);
  503. }
  504.  
  505. function redirectURL(url) {
  506. if (userPopup) {
  507. url = url + '';
  508. if (!url.match(statusRx) && url.match(userRx) && url.indexOf('#!') == -1) {
  509. popupTwitterInfo(url, false);
  510. return null;
  511. }
  512. }
  513. return url;
  514. }
  515. function getTwitterStatusHTML(sid,template) {
  516.  
  517. var url = "http://a...content-available-to-author-only...r.com/1.1/statuses/show/" + sid + ".json";
  518. var json = getJson(url);
  519. if (!json) {
  520. return null;
  521. }
  522.  
  523. //テンプレートを読み込み
  524. var templateText = readTemplate(template);
  525. if (!templateText) {
  526. v2c.alert("ファイルがない? " + template);
  527. return null;
  528. }
  529.  
  530. var html = getTwitterUserFromJson(json.user, templateText);
  531. html = getTwitterStatusFromJson(json, html);
  532.  
  533. return html;
  534. }
  535.  
  536. function getTwitterUserHTML(user,template) {
  537. var url = "http://a...content-available-to-author-only...r.com/1.1/users/show/" + user + ".json";
  538. var json = getJson(url);
  539. if (!json) {
  540. return null;
  541. }
  542.  
  543. //テンプレートを読み込み
  544. var templateText = readTemplate(template);
  545. if (!templateText) {
  546. v2c.alert("ファイルがない? " + template);
  547. return null;
  548. }
  549.  
  550. var html = getTwitterUserFromJson(json, templateText);
  551. //最新ツイート取得
  552. //if (json.statuses_count > 0) {
  553. html = getTwitterStatusFromJson(json.status, html);
  554. //}
  555.  
  556. return html;
  557. }
  558.  
  559.  
  560. function getTwitterStatusFromJson(statusJson, templateText) {
  561. var text = '';
  562. var date = '';
  563. var client = '';
  564. var retweetCount = '0';
  565. var statusID = '0';
  566.  
  567. if (statusJson) {
  568. //本文の取得
  569. if (statusJson.text) {
  570. text = addLinkTag(statusJson.text + '');
  571. }
  572.  
  573. //投稿日時の取得
  574. if (statusJson.created_at) {
  575. date = getDateText(statusJson.created_at);
  576. }
  577.  
  578. //リツイート数の取得
  579. if (statusJson.retweet_count) {
  580. retweetCount = statusJson.retweet_count + '';
  581. }
  582.  
  583. //クライアント
  584. if (statusJson.source) {
  585. client = statusJson.source;
  586. }
  587.  
  588. //statusID取得
  589. if (statusJson.id) {
  590. statusID = statusJson.id + '';
  591. }
  592. }
  593. else {
  594. text = "取得できず"
  595. }
  596.  
  597. templateText = templateText.replace('%date%', date);
  598. templateText = templateText.replace('%sid%', statusID);
  599. templateText = templateText.replace('%via%', client);
  600. templateText = templateText.replace('%retweet%', retweetCount);
  601. templateText = templateText.replace('%text%', text);
  602.  
  603. return templateText;
  604. }
  605.  
  606. function getTwitterUserFromJson(userJson, templateText) {
  607. var followersCount = '0';
  608. var friendsCount = '0';
  609. var statusesCount = '0';
  610. var verified = '';
  611. var createdAt = '';
  612. var icon = '';
  613. var icolink = '';
  614. var name = '';
  615. var screenName = '';
  616. var homeurl = '';
  617. var description = '';
  618.  
  619. if (userJson) {
  620. //フォロワー数取得
  621. if (userJson.followers_count) {
  622. followersCount = userJson.followers_count + '';
  623. }
  624.  
  625. //フォロー数取得
  626. if (userJson.friends_count) {
  627. friendsCount = userJson.friends_count + '';
  628. }
  629.  
  630. //ツイート数
  631. if (userJson.statuses_count) {
  632. statusesCount = userJson.statuses_count + '';
  633. }
  634.  
  635. //urlの取得
  636. if (userJson.url) {
  637. homeurl = userJson.url;
  638. }
  639.  
  640. //認証済み
  641. if (userJson.verified) {
  642. verified = '認証済み';
  643. }
  644.  
  645. //アカウント名の取得
  646. if (userJson.screen_name) {
  647. screenName = userJson.screen_name;
  648. }
  649.  
  650. //アイコンURL取得
  651. if (userJson.profile_image_url) {
  652. icon = userJson.profile_image_url;
  653. icolink = "http://t...content-available-to-author-only...r.com/#!/" + screenName;
  654. }
  655.  
  656. //表示名取得
  657. if (userJson.name) {
  658. name = userJson.name;
  659. }
  660.  
  661. //紹介文の取得
  662. if (userJson.description) {
  663. description = addLinkTag(userJson.description + '');
  664. }
  665.  
  666. //作成日の取得
  667. if (userJson.created_at) {
  668. createdAt = getDateText(userJson.created_at,"yyyy/MM/dd");
  669. }
  670. }
  671. //パラメータの置換
  672. templateText = templateText.replace('%aname%', screenName);
  673. templateText = templateText.replace('%uname%', name);
  674. templateText = templateText.replace('%icon%', icon);
  675. templateText = templateText.replace('%icolink%', icolink);
  676. templateText = templateText.replace('%verified%', verified);
  677. templateText = templateText.replace('%followers_count%', followersCount);
  678. templateText = templateText.replace('%statuses_count%', statusesCount);
  679. templateText = templateText.replace('%friends_count%', friendsCount);
  680. templateText = templateText.replace('%created_at%', createdAt);
  681. templateText = templateText.replace('%homeurl%', homeurl);
  682. templateText = templateText.replace('%description%', description);
  683. return templateText;
  684. }
  685.  
  686. function addLinkTag(htmlText) {
  687. var http = "(?:(?:ftp|https?)://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+)";
  688. var user = '(?:[@][0-9A-Za-z_]{1,15})';
  689. var hashtag = '(?:(^|[^a-zA-Z0-9&?]+)#(\\w*[a-zA-Z_]\\w*))';
  690.  
  691. var r = new RegExp([http, user, hashtag].join('|'), 'g');
  692. return htmlText.replace(r, function(m0) {
  693. if (m0.match(/^(?:ftp|http)/)) {
  694. return '<a href="' + m0 + '">' + m0 + '</a>';
  695. }
  696. else if (m0.match(/^@/)) {
  697. return '@<a href="http://t...content-available-to-author-only...r.com/' +
  698. m0.substr(1) + '">' + m0.substr(1) + '</a>';
  699. }
  700. else if (m0.match('^' + hashtag)) {
  701. return RegExp.$1 + '<a href="http://t...content-available-to-author-only...r.com/search?q=' +
  702. encodeURIComponent('#' + RegExp.$2) + '">#' + RegExp.$2 + '</a>';
  703. }
  704. else {
  705. return m0;
  706. }
  707. })
  708. }
  709.  
  710. function getDateText(d, pattern) {
  711. var dd = new Date(d);
  712. if (!pattern) {
  713. pattern = "yyyy/MM/dd HH:mm:ss (E)";
  714. }
  715. var sdf = new java.text.SimpleDateFormat(pattern);
  716. return sdf.format(dd);
  717. }
  718.  
  719. function getJson(url) {
  720. v2c.setStatus('popupTwitterInfo通信中...');
  721.  
  722. var hr = twitter.createRequest('GET', url);
  723. var sr = hr.getContentsAsString();
  724. if (!sr) {
  725. v2c.context.setStatusBarText('PopupTwitterInfo ページの取得に失敗しました。: ' + hr.responseCode + ' ' + hr.responseMessage + ' ' + url);
  726. return null;
  727. }
  728.  
  729. //データ取得
  730. return eval("(" + sr + ")");
  731. }
  732.  
  733. function readTemplate(fileName) {
  734. var template = cacheData.get(fileName);
  735. if (!template) {
  736. template = v2c.readFile(combinePath(v2c.saveDir, 'script', 'popupTweet', fileName));
  737. cacheData.set(fileName, template);
  738. }
  739. return template;
  740. }
  741.  
  742. function combinePath() {
  743. if (arguments.length == 0) {
  744. return "";
  745. }
  746.  
  747. var combined = new java.io.File(arguments[0]);
  748. for (var i = 1; i < arguments.length; i++) {
  749. combined = new java.io.File(combined, arguments[i]);
  750. }
  751. return combined.getPath();
  752. }
  753.  
  754. // predicate(name, value)
  755. function dumpObject(obj, predicate)
  756. {
  757. if(predicate == null) predicate = function() { return true; };
  758.  
  759. v2c.println("> " + (obj == null ? "null" : obj.toString()));
  760. for(var name in obj)
  761. {
  762. var value = null;
  763. try { value = obj[name]; } catch(e) { value = e; };
  764. if(predicate(name, value))
  765. v2c.println(name + ": " + value)
  766. }
  767. };
  768.  
  769. function stringFormat(format /*, ...*/)
  770. {
  771. var args = arguments;
  772. var result = format.replace(/\{(\d)\}/g, function(m, c) { return args[parseInt(c) + 1] });
  773. return result;
  774. }
  775. function printlnLog(format /*, ...*/)
  776. {
  777. var args = arguments;
  778. var message = args.length <= 1
  779. ? format
  780. : format.replace(/\{(\d)\}/g, function(m, c) { return args[parseInt(c) + 1] });
  781.  
  782. v2c.println("[popupTwitterInfo.js] " + (message ? message : 'null'));
  783. }
  784. function assert(condition)
  785. {
  786. if(!condition)
  787. {
  788. throw 'assertion failed!';
  789. }
  790. }
  791.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty