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