「Json」を含む日記 RSS

はてなキーワード: Jsonとは

2011-09-17

Google Reader+Firefoxはてなブックマークを快適に使う方法

やりたいこと

以下を全てGoogle Reader上で行いたい。

  1. フィードを登録する。
  2. リスト表示で記事タイトルを眺める。
  3. 気に入った記事をクリックして全文読む。
  4. 気に入った記事へはてなスターを付ける。
  5. はてブコメントを見る。
  6. ブコメはてなスターを付ける。
  7. はてブコメントを登録する。

まず、やったこと

以下をインストール
Google Reader Full Feed Modの設定変更

タイトルクリックしたら自動的に全文を取得して表示して欲しい。以下の設定を行うと出来る。

  1. Google Reader右上のギアマーククリック→「Full Feed Mod設定」をクリック→「Auto Load」をクリック
  2. 自動的に全文表示するアイテム」を「すべて」に変更
  3. 「OK」をクリック
Google Reader plus Hatenaの改造

ブコメが表示されないので、これを直したい。

id:northappleさんが「googleリーダー内ではてなブックマークのコメントを参照したいで.. - 人力検索はてな」という質問の回答及びコメント欄でこれを直す方法を教えてくれている。よって、その通りに変更すると直る。2ヶ所置換が必要。

不満な点

  1. Google Readerからワンクリックで「はてなブックマークFirefox拡張」の「ブックマークを追加」ウィンドウを開いて欲しい。
  2. ブコメには作成年月日だけではなく、時分も表示して欲しい。
  3. 記事を開いたら自動ブコメをロードして欲しい。
  4. Google Reader上で記事へはてなスターを付けたい。
  5. Google Reader上でブコメはてなスターを付けたい。
  6. タイトルが長いとブクマ数が隠れてしまうので、それを見えるようにしたい。

改造方法

以下、Google Reader plus Hatenaの変更点です。変更するには、Firefoxの右上にある猿(Greasemonkey)のアイコンの右にある下矢印をクリック→「ユーザスクリプト管理」をクリック→「Google Reader plus Hatena」の「設定」をクリック→「このユーザスクリプト編集します」をクリックするとエディタで「Google Reader plus Hatena」のスクリプトが表示されるのでそれを編集する。

1. Google Readerからワンクリックで「はてなブックマークFirefox拡張」の「ブックマークを追加」ウィンドウを開く。

62行目

mySpan.innerHTML = '<a href="http://b.hatena.ne.jp/entry/'+url+'" onClick="window.open(this.href,\'\',\'status=yes,scrollbars=yes,directories=yes,menubar=yes,resizable=yes,toolbar=yes\'); return false;" ><img src="'+ BookmarkImageURL +'" /></a>';

を、

mySpan.innerHTML = '<a href="http://b.hatena.ne.jp/my/add.confirm?url='+url+'"><img src="'+ BookmarkImageURL +'" /></a>';

に変更。これで「○○ users」をクリックすると、目的ウィンドウが開くようになる。

2. ブコメには作成年月日だけではなく、時分も表示する。

147行目

var t = bookmark.timestamp.split(" ")[0].split("/");

を、

var t = bookmark.timestamp;

に変更。

156行目、

html += "<li><span class=\"__hatena_bookmark_anywhere_timestamp\">"+escapeHTML(t[0])+"年"+escapeHTML(t[1])+"月"+escapeHTML(t[2])+"日</span><img src=\"http://www.hatena.ne.jp/users/"+escapeHTML(bookmark.user.substring(0,2))+"/"+bookmark.user+"/profile_s.gif\" width=\"16\" height=\"16\"><a href=\"http://b.hatena.ne.jp/"+escapeHTML(bookmark.user)+"/"+escapeHTML(t.join(""))+"\" class=\"hatena_bookmark_anywhere_user\">"+escapeHTML(bookmark.user)+"</a><span class=\"hatena_bookmark_anywhere_tags\">"+tags.join(", ")+"</span>"+escapeHTML(bookmark.comment)+"</li>";

を、

html += "<li><span class=\"__hatena_bookmark_anywhere_timestamp\">"+escapeHTML(t.slice(2,-3))+"</span><img src=\"http://www.hatena.ne.jp/users/"+escapeHTML(bookmark.user.substring(0,2))+"/"+bookmark.user+"/profile_s.gif\" width=\"16\" height=\"16\"><a href=\"http://b.hatena.ne.jp/"+escapeHTML(bookmark.user)+"/"+escapeHTML(t.split(" ")[0].split("/").join(""))+"#bookmark-"+escapeHTML(json.eid)+"\" class=\"hatena_bookmark_anywhere_user\">"+escapeHTML(bookmark.user)+"</a><span class=\"hatena_bookmark_anywhere_tags\">"+tags.join(", ")+"</span>"+escapeHTML(bookmark.comment)+"</li>";

に変更。

# ブコメはてなスターを取得しやすいように、ブコメリンクブコメパーマリンクへ変更してあります

3. 記事を開いたら自動ブコメをロードする。

300行目(インデントしか無い空行)


を、


var evt = document.createEvent( "MouseEvents" );
evt.initEvent( "click", false, true );
createSpan.dispatchEvent( evt );

に変更。(「 JavaScriptの動かないコード (中級編) clickイベントを強制的に発生させたい (fireEvent/createEventの使い方) - 主に言語とシステム開発に関して」を参考にさせて頂きました。)

4-5.

出来た。けど、めちゃくちゃソースが汚いので、差分として公開するのが難しい。ごめんなさい。

# どーやるのがベストプラクティスなのか分からない・・・

6.

どなたかご教示してくださると嬉しいです _ _

書いた人

id:al001

人力検索はてなで質問しました:人力検索はてな

2011-01-15

12時間ほどでTwitter連携webサービスを作った記録

2010年年末から年始にかけて10連休ほどあったので、新しいサイトを作ろうと思い立った。

自分スペック

何を作るか

小遣いを稼げるサイトしたい、とまず思った。

月に1万円だと、毎日コーヒーを飲んでるだけでなくなってしまうので、コーヒー代くらい稼げたらうれしいなあ。じゃあどうする。何を作る?

ということで、まずTwitterを使ったものを作ることにした

テーマ

ひとつジャンルにしぼってツイートをかき集めれば、面白い流れになるんじゃないか。人が来るんじゃないか。そう思った。togetterたいな。で、ジャンルは、個人的に興味がある子育て。ていうか毎日帰宅してから朝まで子どもの寝かしつけや夜泣き対応サイト更新する暇も、俺が寝る暇もあんまりない。ので、手がかからないことが大前提。なんだったら自動更新でもいい。

自動更新かー。と思って「ブログ 自動更新」でググったら、wordpressRSSから更新するプラグインがあるらしいことを知った。はい決定。その瞬間、「TwitterAPIからRSSを引っ張ってwordpress投稿するサイト」に決まった。

やってみた

12時間は実装を初めてから時間になります

1時間

さくらインターネットスタンダードを申し込んだ。14日お試しがあるらしいけど、仮申し込みの時点で住所も入れてコンビニ請求にしたら、数日後に請求書が送られてきてビビった。(同時にドメインも申請しちゃった)

まあ、webで申し込んで、すぐにサーバコントロールパネルという画面に入れるようになった。「クイックインストール」というリンクがあったので見てみたらMovableTypeWordPress自動インストールしてくれるらしかったので、ボタンを押したインストールできましたというので発行されたURLクリックしたけど404だった。1時間くらい404で、その日はもう寝た。

2時間

次の日の夜。これはもう、10連休を利用して毎晩1時間ずつ捻出するしかない、さくらのお試し14日あるから約14時間で作りきるしかねえ、と思った。

サイトアクセスしたwordpressが入ったページが出てきた。おお、サイトができてる!

まずTwitterを調べるか、と思って、「Twitter API」で検索したけどOauth?とかいう面倒なことをしないといけないらしかったのでやめた。じゃあ普通に検索は?と思って「Twitter 検索」で検索したら、search.twitter.comの結果はjsonatomで取得できるし、APIコール制限もないらしいのでこれに決定。検索だけで1時間たった。

3時間

夜も更けて、続けて作業した。「wordpress xml 投稿」で検索していくつか探したらFeedWordpressというプラグインがあったので入れた。あ、事前知識としてMovableTypeでのブログはやったことがあったので、プラグインを入れるみたいな話はスムーズに進められた。

で、twitter検索結果をatomで返した結果を入れてみた。ら、本当に投稿されてた。よっしゃできた、と思った。1ツイートが1エントリになってたし、投稿者もツイートした人になってた。よかった。でも、満足できなかった。

4時間

次の日。同じことを自力でやる方法を探した。「wordpress xml 投稿」で検索して、XMLパースできるようになればいいんじゃないかと思い、simplepieというPHPライブラリにたどり着いた。が、PHPなんてまったく知らないし、憶える気もなかった。actionscriptで書かせてよ、とずっと思ってた。

5時間め・6時間

次の日。「wordpress xml 投稿」でまた検索。どうやらwordpress投稿って、xmlrpcというやり方を使ってるらしかった。ので、「wordpress xmlrpc 自動投稿」で検索したら、なんかサンプルコードが載ってたのでそのまんまコピペ(結局PHPだった)。したらちゃんと投稿されていた。ふむ。ここで何を思いついたのか、「wordpress xml パース」と昨日みたいなことを検索した。simpleXML?というライブラリがあるらしかったので、それを試してみることにした。(たぶんPHPが動いたので気をよくしてたんだと思う)

こういう流れでいけると思った。考え方はactionscriptエディタに書いて、ノリであてにいった。変数に宣言するのはできた。$var1とかで宣言したことになるらしいURLRequestに相当するコードを探したら「file_get_contents」らしいことが分かった。(「PHP 外部ファイル」で検索

で、ゲットしたのはXMLなんだけど、上記検索したかにたまたま書いてあった「simplexml_load_string」というのを使うとXMLパースできそうな気がしたので、ノリで書いたactionscriptでは

var req:String = "http://search.twitter.com/?q.atom=mogemoge";

var r:URLRequest = new URLRequest(req);

var kekka:XML = r.send() as XML; ←いまここ

なので、XMLキャストしたんだろうなみたいな感じだった。E4Xを使えればいいのにPHPって馬鹿ねと思いながら寝た。

7時間

年があけて、3が日が終わりそうだった。年末にやってたこと(上記までのこと)を思い出しながら、XMLの必要な部分だけ抜き出す方法を模索したatomっていってもentryがたくさん入ってたか配列にするんだろうけど、ってんで「php foreach」を検索。なんとなくサンプルコードをまねしながら、記事タイトル、記事本文だけ取得した。あとはxmlrpcのサンプルにあわせて投稿するようにした。できた。寝た。

8時間

次の日の朝、ブログを見た。昨日更新したのしかあがってない。自動じゃねーじゃん。

で、「自動 投稿」で検索したら、クローン(cron)という仕組みを使わないといけないのだった。クローンサーバの仕組みらしく、そういえば俺はPHPをはじめDBサーバという単語を極力さけて仕事してきたので、もう気持ちが悪くなってきた。「さくらインターネット cron php」で検索して、なんとかやり方を見つけて、cronを登録した。(1時間に1回にした。設定は * * * 0)

9時間

仕事から帰ってきて、サイトを見ると、投稿が大量にたまっていた。やった!で、調子に乗ってツイッターアカウントを作った。なんだったらツイッター自動したかったので「twitter bot」で検索した。Easybotterというサンプルボットがあったので使わせてもらった。自動で一行ずつつぶやくようにした

時間外)

サイトテーマを考えてた。通勤電車で悶々とする時間

ツイートを集めることは成功したけど(毎時間100件のツイートを1エントリとして投稿してる)、それを眺めて面白いんだろうか? ボットを動かしてるけど人がくるんだろうか?

そんなとき「trivist」がはてブに載ってた。なんかにたものを感じた。やっぱツイートを引っ張ってきて投稿するサイトはアリなのか?アリなはずだ!

10時間

サイトの体裁を整えた

11時間

trivistをまねて、記事を評価(はてなスターかいいねボタンかにいもの)する仕組みが欲しくなった。「wordpress 評価 プラグイン」で「wp-postratings」というプラグイン発見して、入れてみた。どうやら1エントリーに1評価しかできないらしい。俺のサイトは1エントリーに100ツイートあるから、どのツイートを評価するのかが分からない。

いったん、wordpressの全投稿を削除した。で、cron に登録されてるPHPを、1記事に1エントリーした

12時間

エントリー投稿するついでに、Yahoo日本語解析APIをつかってツイートを分析して、名詞動詞だけを取り出そうと思った。それをタグにすれば、タグクラウドが作れると思った。はてブはずっとずっと昔からやってるからYahoo日本語解析っていうのが2006年くらいに流行ったことをなぜか憶えてたので、やってみた。できた。

なんか俺、PHP書くのが早くなってね?

そして微調整をしながら今に至る

アクセス解析を入れてみた。サイトに来てる人は、俺だけだった。

どうにかして人を増やしたい。サイト広告募集はする気がないしベタベタバナーを貼りたくなかった。みんなが気軽に見に来て、軽い気持ちで評価してくれて、更新を楽しみにしてくれるサイトしたかった。コミュニティサイトじゃないけど、やっぱりサイトコミュニケーション設計をしないと意味がないんじゃないか、見てくれるユーザはどうやったら楽しいんだろう、ということを考え続けて10日ほど経った。Twitter経由で来てくれた人が3人ほどいるようだけど、何がダメなのか分からないので増田にお願い。


ここまで書いて教えてくんじゃねーか、と思われるかもしれないが、ググレカス的な検索は上記で書いたみたいにいろいろやってきた。でも、サイトを作ってみてはじめて、ユーザに向けたサイトってどう作ればいいのかが分からないということに気づいた。

小遣い稼ぎもしたいんだけど、面白いサイトを作るヒントがほしいと思った。

kanzen21やtrivistみたいに、俺も過程を全部さらしたから辛辣意見を求む。そしてはてブされるのを待ってます

http://kosodate-now.com/

2010-12-19

ヌケるWebサービスを作ったのでeHubインタビューズっぽく宣伝してみる

あなたウェブアプリケーション/サービスは何ですか?

エロ注意】eroino http://eroino.net/

eroinoは毎日更新される大量のアダルト動画を、AV女優キーワードで分類して表示したり、お気に入りリストクリップできるサイトです現在動画数は、約28万件。

このプロジェクトを始めた理由は?

製作にかかった時間は?また、本業がありますか?

チームの規模はどれくらいですか?また、あなたの素性および経歴は?

現在使用しているインフラ技術は何ですか?

技術的な特徴があれば、紹介してください。

お気に入りリスト

開発の際に気を付けたことはありますか?

データの取得元、動画投稿サイトに迷惑をかけない」

プロジェクトは次の半年でどこへ向かうと思いますか?

アクセス増への対策」
広告
「機能追加」
  • まったくの白紙です。まずは安定稼働。

自分Webサービスを作りたいと思っている人に向けて何かありますか?

利用者に向けて何かありますか?

  • 自分では、かなり実用的だと思っているのですが、実際の所、どうなんでしょう。使ってみて、ダメ出しでも何でも良いので、感想を聞かせてもらえると嬉しいです

元ネタ

自分WEBサービスを作りたいと思っている人へ

http://anond.hatelabo.jp/20101203150748

eHub Interviews

http://emilychang.com/ehub/app/category/ehub-interviews/

eHub インタビューズ - Last.fm翻訳

http://d.hatena.ne.jp/brazil/20051102/1130901002

2010-12-03

自分WEBサービスを作りたいと思っている人へ

もう、いいおっさんの年齢なんですが、先日、とあるWEBサービス公開しました

5年ほど前からぼーっと考えていたんですが、如何せん、事務職の自分には”創る技術”が無かった。

優れた若い技術者id:amachangとかうらやましい)や、チャレンジ精神あふれる経営者id:hiroyukiegamiとか)が出てくる中うつうつとしている自分に嫌気がさし、4か月前の7月からHTMLプログラム勉強を始めた。


本屋立ち読みしたら、まずはHTML勉強する必要があると、書いてあった。同時にCSSを学んだ。

プログラムを作りたかったので、次にJavascriptをやった。

jQueryがすごい。「プログラムって誰でもできるんだ。」この時そう思った。

検索システムを作りたかったので、本屋に行ったらCGI/Perlの本がいっぱいあったので、Perl勉強した

しかし、HTMLテンプレートが使いたかったのでPHP+Smarty勉強した

作りたかったWEBサービス大手サイトデータ検索サイトだったので、自動データを集める必要があった。

クローラーというらしい

PerlのLWPを勉強したが、データを集めた後に加工する必要があった。簡単そうだったRubyとMechanizeを勉強した

Rubyはものすごくきれいにプログラムがかけることを知った。話し言葉に近い気がする。

プログラムを作っている時、最初自分パソコンの中でやっていて気付かなかったが、実際に公開するときレンタルサーバーを使うというのを知って調べると、Linuxサーバーが多いということを知った。

から、今度は自宅のあいているパソコンLinuxを入れた。

Linuxを入れたはいいものの、全く使い方が分から四苦八苦してRubyのインストールした

世界中メインで動いているWEBサーバーApacheということも3か月前に知った。

Apacheの設定がテキストファイルなのも驚いた。cd,ls,vi,mv,cp,chmod等、基本的なUNIXコマンドを覚えた。

例の図書館の事件があったので、クローラーを動かすのをためらったが定期的にちょっとずつなら怒られないんじゃないかと、Crontabを勉強した

自宅のサーバーが壊れてしまい、構築が大変だったので今度はVPSサーバーを借りた。

同じように構築はしたがかなり苦労した。このとき、始めてmakeというコマンドを使った。コンパイルというらしい

クローラー自動的にデータを集めていたが、動かし始めて2カ月目でデータファイルが1GBを超えていることに気がついた。

このときテキストファイルデータを扱おうと思っていたが大きすぎて動かない。

SQLサーバーというものを触り始めた。

最終的にデータ量は5GBを超えた。


11月も後半、本稼働用のサーバーを探していたら、丁度カゴヤVPSサーバーベータ版を募集していた。

ここぞとばかりに申し込みボタンクリックして申し込んだ。

すごく、快適です。まだベータですが、本番稼動でも、50GBで900円という激安プランです

http://www.kagoya.jp/cloud/vps/

さくらさんの2.5倍の容量でさくらさんよりちょっと安い。

ベータ版では、3つまでOSインストールができます。もちろんそれぞれにIPアドレスが振られます

このVPSサーバー管理システムインストールし、もろもろの環境も作って、11月末についに、公開。

AV女優スリーサイズから検索できるシステム、「完全に一致です

 【完全に一致AV女優類似検索システム

 http://www.kanzen21.com/

類似検索機能付きで、2次元3次元をつなげる夢のシステムですはい

「なんだエロかよ」とか言わないでください。

真剣に作ったんだ。仕事をしながらよく頑張ったと自分をほめてあげたい





このシステムは、下記のような構成になっています。

----------------------------------------------

サーバー:カゴヤVPSサーバーベータ版)

WEBサーバーApache

SQLサーバーMySQL

HTMLXHTML+CSS

インターフェースjQuery+selectToUISlider

検索プログラムPHP

テンプレートエンジンSmarty

クローラーRuby+Mechanize+Cron

-----------------------------------------------

サーバー上にある静的なHTMLは1ページもなく、mod_rewriteですべてPHPが処理しています。

ボタン等の画像は、GIMP作りました


一番大変だった事は、、、

このサイトデータDMM社のデータを使わせてもらったのですが、AV女優顔写真をそのまま使うのは、肖像権的にNGらしく、AV女優の作品の中からその女優の顔が一番大きく写っているパッケージを使うことにしました

しかし、女優データは約5万件。作品データ12万件。とても手作業でやるわけにもいきませんでした

結局どうしたかというと、Face.com(http://face.com/)という、画像の顔認識ができるAPI無料提供しているサービスを利用しました

同様のことができる、OpenCVというソフトがあるのですが、最初から付いているパターンデータでは人の正面の写真しか顔として認識しませんでした

それに比べて、Face.comの認識精度は驚くほど高く、横だろうが斜めだろうがかなりの精度で顔を認識してくれました

データJSON形式で返してくれる(JSONもこのとき初めて知った)為、取得したデータを後で加工しやすかったです

1.このAPIを使い12万件の作品データをすべてスキャンするプログラムを書く※1

2.顔の縦の長さと横の長さを取得

3.縦×横で顔の面積を計算

4.作品テーブルの中に3で計算した顔の面積を追加

5.SQL女優テーブルと作品テーブルを結合

6.その女優の作品の中で顔面積が一番大きなパッケージ写真をその女優顔写真として代用しました。※2

※1 APIの制限が1時間1000リクエスト迄だったので、これまたCronで・・・

※2 実際には女優テーブルと作品テーブルを繋ぐ中間テーブルのフラグONした。若干の間違いはあるものの、かなり正確に出ました



長々と書きましたが、ズブの素人から約4ヵ月でここまで出来ました

勉強する前、SEをやっている友人に話したら、「3年はかかるんじゃないか?」と言われましたが、できたものを見せたら褒めてくれました

WEBサービスを作りたいと思っていて、技術がないからとあきらめている人は、とりあえずやってみてください。意外に簡単にできますよ。

あと、クローラーが動いていると、全能感を味わえるので楽しいです




あ、あと、椎名舞さんのファンです

-----------------------------------------

19:30追記

サーバーソフトからアラートが上がって、見てみてたらなんかすごいアクセス貰ってまして。

ありがとうございます


>カゴヤ中の人乙wwww VPSといったらさくらServersManくらいし選択肢が無いのは現状当然の認識であるはずなのに!

ゴヤ人間じゃないですよー。広告してるつもりもないんですが、ベータ版だからかもしれませんけど、すごい快適ですよ。今は。

何よりタダなので。

本当に月額900円のまま本公開になったら、環境構築もめんどくさいのでそのまま契約しちゃうかもです


>カゴヤはOpenVZだからなあ。俺としてはより自由度の高いさくらVPSお薦めしたい。

そうなんですか。2週間のお試し期間はつかったのですが、正直どっちがいいとかわかりません。

どんな風に自由度が高いんですかね?あと、アダルトOKなんですっけ?


>組み立てるプログラミングは本当に簡単だよ。 みんなで入り口を隠しているだけだよ。 #組み立てるだけじゃなくて、アルゴリズムを練ることが真のプログラミングかもしれない

そう思います。感覚的にはジグソーパズルに似てました

ただ、ピースを探すのに時間がかかりましたけど。

私の場合は、アルゴリズムとかその辺はできなそうですね-。

サンプルプログラムの組み合わせで作ったようなサービスですので、プログラムソースとかぐっちゃぐちゃです

一応、公開前に見える所はきれいにしたつもりですが・・・




もともと、作ろうと思ったきっかけなんですけど、

椎名舞さんがですね、すでに引退しちゃってるんですよ。ずいぶん前に。

で、彼女プロポーションが大好きなんですね。私。

それで、検索エンジン検索したです。でも、なかなか出ないんですね。

欲望のままにやってたら、次から次に壁にぶち当たって、そしたらいつの間にかできました

結果、このシステム使って椎名舞さんのプロポーションに似たAV女優を探すと、

雛乃つばめさんとか、果梨さんとか、佐伯さきさんとか既にDVD持っている女優さんばっかりヒットしちゃうんですね。確かに似てるんですスタイル

当たっててうれしいやら、既に持っていて悲しいやら。


あと、スタイルが似てる女優って顔も似てる気がする。

とくに最近の細い子は。



あ。デザインは、某企業をパk、じゃないリスペクトさせてもらいました


-------------------------------------

23:55追記

アクセス過多でサーバーが落ちました

寝てたらサーバーからアラートメール携帯に飛んできておこされました

こんな瞬発的なアクセスを考えていなかったので、とりあえず再起動しました

が、また重くなってます。。。どうしたらいいんだろう。

しい方居たらアドバイスいただけるとうれしいです


-------------------------------------

12/4 01:45追記

何度再起動してもサーバーが反応しなくなるので、うぎゃーってなってたのですが、

親切な方が「MySQLサーバーが原因じゃね?デフォルトだろ?query_cache_sizeを設定したらいいよ。」とわざわざお問い合わせからアドバイスくれました

設定してみたら驚くほどつながりやすくなりました

同じSQLクエリーを保持してくれるらしく、実際にデータ検索を行わないので高速になるそうです。こんなの知らなかった。ありがとうごいました

プログラムはサンプルがあるからどうにかなるんですが、サーバー周りの事が全然わかりません。。。。ぐうぅぅ。。。。

おやすみなさい。

-------------------------------------

12/6 23:30追記

ブックマークコメントもらっていた事を別の日記で説明しました

http://anond.hatelabo.jp/20101206224349

-------------------------------------

1/12 10:00追記

最終報告を書きました想定外ばかり。

http://anond.hatelabo.jp/20110112095450

2010-07-28

データhtmlよりjsonで返した方が世の中のためになると思う

y/Y

2009-12-28

ttp://d.hatena.ne.jp/nitoyon/20091228/as3corelib_lazy_json

普通の人だったらどうやったら簡単に JSON を吐き出すクラスを作るか、だと思うんだけれども…正直何をしたいのか理解に苦しむ。AS系、とひとくくりにはしたくないけれど、ActionScriptソースをいくつかいじった経験でいくつかこういうの、見てきた気がする。

2009-06-17

やっぱLLだな

SQL + Perl/Python/Ruby/Java + JavaScript + CSS + HTML、おまけにXMLとかJSONとかYAMLとか。DOMとかXPathとか。

2009-04-20

Perl, JSON, Ajax誰か助けて

PerlJSONやろうと思ったら、ものすごく簡単なところでハマっているっぽい。とにかく、JSONとか言う前に、Perlで生成したJavaScriptコードSCRIPTタグSRCに指定して、読み込ませるところで失敗している。

test.pl

#!/usr/bin/perl

use strict;

use CGI;

my $q = new CGI;

print $q->header( -type=>'text/plain', -charset=>'UTF-8');

print "alert(\"Perl passed\");\n";

test.html

<html&gt;

<script type="text/javascript" src="test.pl"&gt; </script&gt;

</html&gt;

何でこれで動かないんだろう?すごい簡単なことしかやっていないのに。誰か教えて。

2009-03-17

はてなフォトライフデスクトップ入れてみた

パラパラ変わる美人時計とかカナ速の壁紙沢山のやつとか見て、使ってみようかと。

しかし、Y!Pipesで遊ぼうと思ったら、RSSで出る項目決まってるんだ。残念だ。

というわけで標準状態なのだが、冴えないね。時折非常に冴えない。残念だ。

そんなわけで、何処かにHTMLJSON元に任意のRSSに変換してくれるサービスないかな。

それより自分で書いたほうが早いかな。flickrでも使ったほうが早いかな?それともおすすめタグある?

2009-03-05

http://anond.hatelabo.jp/20090303184207

http://anchor18.blog77.fc2.com/blog-entry-195.html

http://ameblo.jp/omisedayori/entry-10184130642.html

両方ブクマしてる人のコメント抜き出してみた。

あんまおもしろくないな。もっと面白いURL募集

teruyastar

["", "まいったね。 やはりWiiウェアなんかのDL方式か、ネトゲに移行せざるをえないか。"]

complex_cat

["何処をどう描いても完璧デッサン力。", "これについてはWiki記述は,確かに使う側を助けてしまっているかも。いや,ここを読むようなユーザーとの重複度は小さい

とは思うが。"]

silverscythe

["昔のはホントすごいよなあ", ""]

yachimon

["", ""]

shoku-in

["", ""]

mike_n

["", ""]

y_arim

["Dr.スランプの時点ですでに完成されていて、そこからさらに進化しているのはすごい。", "違法性の認識とか関係なく、便利なものがあるから使うってだけだろうな。たいていのひとは物事を深く考えないし自分の欲求には逆らわない。/そしてid:utd_sn3781の主張は増田に書かれた http://anond.hatelabo.jp/20081231201301"]

A410

["", ""]

tailtame

["やっぱり手書きの頃がいいなぁ…塗りが…塗りが…。&gt;&gt;66の赤いトカゲドラゴン?の艶とかたまらんなー。画集っていくつあるんだろ…", "蔓延しすぎているってのも あるんだろうな…本屋に行けば目に付く本は……。子供の頃なんてゲームは半年に一本でいいよ。クリエイターも割れしてたしな…"]

dododod

["", "おやおや"]

retlet

["", ""]

kashmir108

["", ""]

tokuhirom

["", ""]

ocha39

["", ""]

whirl

["", ""]

kalmalogy

["「ビッチズ・ライフ」買おう。", "親が買い与えるのは絶対だめ。『公認意識』が子供に芽生える。自腹で買って『本当はよくないけど次善の策』という意識でないと。/ マジコン使うならせめて『良いと思ったモノに投資する』意識教育すべきでは。"]

came8244

["", ""]

fujii_isana

["", ""]

lizy

["クロノトリガーの絵を見るだけで、頭にあのBGMが流れてくる", "モラルでの解決方法案としては、ゲーム作りの過程を理解してもらう、とかかな。どれだけ多くの人がどれだけ苦労してコストをかけて作っているのかを理解してもらうしか|それでもやる奴はもうダメですね"]

mochige

["", ""]

aohige9610

["中鶴の絵がまじってるので注意", ""]

gm333

["", ""]

posinega

["", ""]

pikayan

["", ""]

asitaki

["マジコンは叩くのに画像コピペブログは叩かないクソはてなーども↓。マジコンと同様にブログ規制すべき。", ""]

#!ruby -Ku

require "rubygems"
require "json"
require 'open-uri'

url = ["http://anchor18.blog77.fc2.com/blog-entry-195.html",
       "http://ameblo.jp/omisedayori/entry-10184130642.html"]

bookmarks = url.map{|u|
  open("http://b.hatena.ne.jp/entry/json/"+u){|f|
    JSON.parse(f.read.gsub(/\A\(|\)\Z/, ""))
  }
}

user_comment_map = bookmarks.map{|bm|
  h = {}
  bm["bookmarks"].each{|b|
    h[b["user"]] = b["comment"]
  }
  h
}

overlaped_users = user_comment_map.map{|bm|
  bm.keys
}.inject{|a,b|
  a&amp;amp;b
}

overlaped_users.each{|user|
  puts user
  p user_comment_map.map{|b| b[user]}
  puts
}

2009-02-01

深淵データストレージ世界

ちょこっとしたデータプログラムとやり取りするのに楽な方法にはsqliteとかJSONとかxmlとか・・・他にはあるでしょうか?

2009-01-20

d:id:naoyaさま

このたびは、御社はてラボ内のサービスの一つ「はてな匿名ダイアリー」において、正式サービスではないにもかかわらず、迅速な荒らし対策を行っていただき、誠に有難う御座いました。

心より御礼申し上げます。

さて、上記対策により、一部閲覧し難いエントリーも正常に表示されるようになりましたが、しかし、その対策による影響とは断定できるわけではありませんが、一部機能に不具合が生じております。

具体的には、エントリーパーマリンクのページに付加されます、トラックバックツリーにおきまして、本文の一部表示を全文表示に展開できる「その場で全文機能*1」が動作しなくなっております。

原因としましては、modeパラメータに対しjsonを指定した際の応答が、本来jsonまたはjsonp形式となるはずが、HTMLエスケープされた上で幾つかのHTMLタグが付加されているためです。

多忙の中、このようなお願いが甚だ失礼とも存じますが、すばらしい機能である「その場で全文機能*1」が正常に動作しない現状は非常に残念であり、また荒らし行為への対策を完璧にするためにも、ご対応をして頂けないものかと希望しております。

御社のますますのご発展をお祈りいたしております。

*1 誠に勝手ではありますが、呼称が定まっておりませんでしたので、僭越ながら命名させていただきました。

おやおや

対策されたのは良いのだけれど、まさかこうくるとは。

まじですか

って言いたい。

あと、mode=jsonjsonであってhtmlじゃないからよろしくお願いします>はてなさん、つかd:id:jkondo社長、かな?d:id:naoyaだった。

http://b.hatena.ne.jp/naoya/20090120#bookmark-11744130

時間的には

http://anond.hatelabo.jp/20090120185655

かな?

2008-12-31

うごメモはてな - ゆうたさんの作品のスター/コメントを集計してみた。スターの方はentry.json適当に処理して、コメントの方はHTML適当に処理した。

スター

  • otsune, 268492個
  • pi8027, 162445個
  • at_yasu, 98700個
  • hogelog, 54051個
  • hajimehoshi, 47608個
  • elim, 46433個
  • itkz, 46080個
  • hageatama-, 45507個
  • debedebe, 44846個
  • gamil, 40308個
  • kfujii, 31521個
  • utiliti, 29791個
  • tenkoma, 26116個
  • twainy, 25052個
  • IwamotoTakashi, 24912個
  • nakanohito1, 23075個
  • TKSK, 21982個
  • fellows, 20965個
  • kei-os2007, 20246個
  • mimimu8, 18117個
  • storz, 15364個
  • sumyun34, 15113個
  • yachimon, 13160個
  • takano32, 12600個
  • gebet, 11167個
  • YU-TA, 10113個
  • burenka, 9166個
  • shunirr, 8400個
  • nonki, 7777個
  • nishiohirokazu, 7359個
  • nonasu, 6877個
  • nvsofts, 6590個
  • mgkiller, 5813個
  • showyou, 5610個
  • VoQn, 5264個
  • kabawo, 3980個
  • tomoko-am, 3949個
  • suztomo, 3594個
  • z0rac, 2560個
  • Jug, 2222個
  • mainichiomoroh/@livedoor, 2083個
  • eigokun, 2043個
  • abeckham22, 1947個
  • solailo, 1893個
  • sakuragaoka, 1864個
  • everydaysongs, 1839個
  • wang-zhi, 1729個
  • n-styles, 1500個
  • notoriousBIG, 1281個
  • magicalhat, 1272個
  • lautenwerke, 1203個
  • Chaborin, 1193個
  • hubito, 1008個
  • Iketaki, 1000個
  • SUS430, 934個
  • rgfx, 887個
  • kudzu_naoki, 844個
  • mimizumo, 794個
  • BLOG15, 790個
  • Constellation, 765個
  • ykht, 755個
  • janus_wel, 672個
  • konekonekoneko, 663個
  • WiiAreTheWorld, 623
  • KoshianX, 608個
  • s68, 602個
  • fishingsenyou, 586個
  • shitu, 574個
  • hmori, 566
  • himeatball, 523個
  • Manaphy, 518個
  • lokivip, 512個
  • monato, 501
  • saitamanodoruji, 500個
  • mitz_777, 477個
  • shingo-07_05, 453個
  • v44c, 451個
  • kotorikotoriko, 406
  • hatayasan, 400個
  • asc_gamefreak, 395個
  • murakami_tak, 389個
  • takimo, 388個
  • hitagism, 380個
  • clownboy-zuzu, 380個
  • tsugo-tsugo, 374個
  • ssig33, 368個
  • mindblinds, 353個
  • tiiyuka0239, 351個
  • bootaro, 341個
  • coin0136, 339個
  • Blue-Period, 333個
  • jacoby, 323個
  • ayako666, 311
  • yoshitetsu, 301個
  • diary7, 294個
  • aznacc, 283個
  • AKIY, 280個
  • hayamiz, 279個
  • heiwaboke, 276個
  • snaka72, 272個
  • Sore_0, 271個
  • a_halka, 270個
  • wanderingdj, 266個
  • komatz, 261個
  • kaiteki61, 260個
  • Dokukinoko, 256個
  • s00516, 253個
  • chroju, 250個
  • kana-kana_ceo, 247個
  • izm_11, 246個
  • h_uchida, 239個
  • kawango, 236個
  • kikuchige, 232個
  • kiria25, 229個
  • sextremely, 223個
  • ngmkz, 221個
  • goinger, 210個
  • jingi469, 208個
  • jumboly, 202個
  • ktomotaka2, 201個
  • hkn, 200個
  • moeprj, 197個
  • u1koba, 193個
  • tks_period, 189個
  • kazuki253, 186個
  • Mizuhin, 185個
  • Trapezoid, 184個
  • kiku-chan, 184個
  • nolifeking, 183個
  • at_yuko, 180個
  • closer, 176個
  • Nijiko, 174個
  • disable, 169個
  • S0R5, 160個
  • re_niladmirari, 158個
  • minelayer, 150個
  • hirokidaichi, 148個
  • mochige, 148個
  • maritama, 146個
  • yocchan20, 144個
  • Dynagon, 140個
  • osicoman, 140個
  • jionsehu, 138個
  • cielomajin, 137個
  • fura, 136個
  • mind_of_siva, 133個
  • Marnier, 133個
  • kentahirai, 128個
  • mura-taiken, 128個
  • azurestone, 127個
  • okome_chan, 127個
  • hinemosu_notari, 126個
  • akira_srce, 126個
  • kurenaiiro, 125個
  • yoira, 120個
  • antipop, 120個
  • yuyarin, 120個
  • yaketa, 120個
  • to-r, 120個
  • MASSY, 120個
  • r_da, 119個
  • methane, 118個
  • wataken44, 116個
  • skatsuyama, 114個
  • fqu, 114個
  • sugar0101, 112個
  • hayashi311, 111個
  • wozozo, 110個
  • sidestepism, 106個
  • noreply, 101個
  • Horiuchi_H, 100個
  • digm, 99個
  • navecin, 99個
  • muumuuyoshi, 98個
  • cawther, 95個
  • pokutuna, 95個
  • siztsb, 94個
  • camelmasa, 94個
  • hagino_3000, 92個
  • kiichi55, 92個
  • moi5, 92個
  • yoshi1207, 92個
  • Lif, 91個
  • Trou, 89個
  • rui_rui_723, 88個
  • jinonjy, 86個
  • keitaro79, 85個
  • calbeecalbee, 84個
  • hiroaki777, 83個
  • nagakura_eil, 82個
  • emosei, 82個
  • teruyastar, 81個
  • papiko-07, 79個
  • gotorush, 78個
  • yoh596, 77個
  • abekameri, 77個
  • t-tanaka, 76個
  • yaa-at, 76個
  • ta-01, 75個
  • takmck, 74個
  • nori0620, 73個
  • Quecy, 73個
  • gimbuee, 72個
  • shin722, 69個
  • mnz, 68個
  • goyoki, 67個
  • okoppe8, 67個
  • pha, 65個
  • ArappoCaro, 63個
  • wonder_wonder, 63個
  • tsukamoto-hiroshi, 61個
  • kwkn555, 60個
  • ug_okada, 59個
  • ku-suke, 57個
  • dainamo1, 57個
  • wild-man, 57個
  • tacticslife, 57個
  • ksk-si, 56個
  • mitsukurina, 55個
  • Aroundight, 53個
  • sfujiwara, 53個
  • nekogusa9, 52個
  • nowokay, 52個
  • lliorzill, 52個
  • Luhi, 50個
  • terau, 50個
  • yusuke_f, 50個
  • mochikoAsTech, 48個
  • hashillaneye, 48個
  • RocRoc36, 46個
  • ppop, 45個
  • keroro-ex_99, 45個
  • makiton, 42個
  • platoronical, 42個
  • itochan, 42個
  • puyop, 40個
  • sagi-usagi, 39個
  • ocs, 39個
  • hachiuma, 38個
  • blurman, 38個
  • ken1-t, 36個
  • orangestar, 36個
  • so-cru, 34個
  • fuldagap, 34個
  • sekreto, 33個
  • veiros, 33個
  • kazuyacco, 31個
  • sibazyou, 30個
  • takatana-ka, 30個
  • oya03, 30個
  • duanxak, 29個
  • takeim, 29個
  • sansonO7, 28個
  • ShigeakiYazaki, 27個
  • killclock, 27個
  • sakoto00, 26個
  • menaguro, 26個
  • Metaphone, 25個
  • Pasta-K, 25個
  • parumo, 24個
  • zu2, 23個
  • ryo-chan1214, 23個
  • gouzou, 22個
  • k-ryoya, 22個
  • lbuki, 22個
  • javascripter, 22個
  • hitode909, 21個
  • andalusia, 19個
  • the48, 19個
  • uniuniko, 19個
  • frsh_mt, 18個
  • utrillo, 18個
  • nka, 18個
  • gandmu, 18個
  • upu, 17個
  • neoyta, 17個
  • yosshi-t, 17個
  • badending, 17個
  • hk1226, 17個
  • ihojin, 16個
  • kskmrmt, 16個
  • h_kenan, 15個
  • gaomu-rongli, 15個
  • FRAIL, 15個
  • ululun, 15個
  • kimzo, 15個
  • brick, 15個
  • xyobu, 14個
  • mykichi, 14個
  • nonasige, 14個
  • tomioka_hiroshi, 14個
  • emirichi, 13個
  • tsukitaro, 13個
  • minimum_sho, 13個
  • kouta77, 13個
  • rider-2, 13個
  • crazyup, 12個
  • ahiru-123, 12個
  • Haruka1976, 11個
  • martyan, 11個
  • bob3, 10個
  • t-murachi, 10個
  • yuu665, 10個
  • hiroshiykw, 10個
  • hashikyon, 10個
  • yama-28, 10個
  • Frunk, 10個
  • katsu_w, 10個
  • sky-graph, 10個
  • woremacx, 10個
  • tajimaboard, 10個
  • hsymd, 9個
  • tek_koc, 9個
  • yamifuu, 9個
  • ki-sa, 9個
  • cmizuna, 8個
  • hokoyo, 8個
  • FC6, 8個
  • konotomoko@vox, 7個
  • Urume, 7個
  • tai2, 7個
  • narima001, 6個
  • a2c, 6個
  • analogdevicez, 6個
  • shin個

コメント

  • Dokukinoko, 20回
  • taka-T, 9回
  • mozizin, 6回
  • kakusi-96, 5回
  • soukon1500, 4回
  • a_halka, 2回
  • hogelog, 2回
  • diary7, 2回
  • Snakun, 2回
  • nao5531, 2回
  • hajimehoshi, 2回
  • riberura1107, 2回
  • keroro-ex_99, 2回
  • uho-iiotoko, 1回
  • inouoe, 1回
  • tsukitaro, 1回
  • toguo, 1回
  • daiti7, 1回
  • itochan, 1回
  • rgfx, 1回
  • mochikun, 1回
  • morinoGororin, 1回
  • Chaborin, 1回
  • Akimbo, 1回
  • SaffK, 1回
  • supersex, 1回
  • YaMiNeKo, 1回
  • cawther, 1回
  • legend31, 1回
  • papuriko2009, 1回
  • Aroundight, 1回
  • kazuki253, 1回
  • tekitouotoko, 1回
  • aintai, 1回
  • name-0913, 1回
  • yukky2001, 1回
  • Manaphy, 1回
  • gandmu, 1回
  • nekogusa9, 1回
  • H4416, 1回
  • Sirangana, 1回
  • suztomo, 1回
  • hmori, 1回
  • n-pls, 1回
  • ugokumemotyou, 1回
  • peroon, 1回
  • kikuhime_rx, 1回
  • NERU, 1回
  • colombo, 1回
  • ryo-chan1214, 1回
  • abebetaro, 1回
  • abeckham22, 1回
  • hayamiz, 1回
  • salt_tea, 1回
  • Trapezoid, 1回
  • AKIY, 1回
  • ZUNDA, 1回
  • yoidoretensi, 1回
  • ngmkz, 1回
  • kururu1, 1回
  • ugomemonokami, 1回
  • rui_rui_723, 1回
  • futaba-mirin, 1回
  • takimo, 1回
  • monato, 1回
  • gamil, 1回
  • rwas, 1回
  • myhanabi0912, 1回
  • hosine, 1回
  • shom5w, 1回
  • moons, 1回
  • eigokun, 1回
  • altana11, 1回
  • tiiyuka0239, 1回
  • hihi01, 1回

スクリプト

スクリプトはこんな感じ。

コメント

HTML正規表現ごにょごにょして['hoge', 'piyo', 'fuga', 'piyo']みたいな配列にして、でもこれだけだとコメントした回数がカウントできずに何回も同じユーザーが出てきてしまうから

a = ['hoge', 'piyo', 'fuga', 'piyo']
l = []
for i in a:
  l.append([i, a.count(i)])
l = list(set(map(lambda x: tuple(x), l)))
l.sort(lambda x, y: cmp(y[1], x[1]))
for i in l:
  print i[0], i[1]

とする。

スター
import urllib, json
h = urllib.urlopen('http://s.hatena.ne.jp/entry.json?uri=http%3A%2F%2Fugomemo.hatena.ne.jp%2F0B3D19604CE04B2F%40DSi%2Fmovie%2FE04B2F_08720FF94B42A_002')
a = json.load(h)
f=open('stars.txt', 'w')
sys.stdout=f
for i in a[u'entries'][0][u'stars']:
  print i[u'name'], i[u'count']

2008-12-30

[][]うごメモ関連付けセレクトボックス

javascript:(function(j){j.src='http://jqueryjs.googlecode.com/files/jquery-1.2.6.min.js';document.body.appendChild(j);setTimeout(function(){$.ready();$('<script src=%22http://query.yahooapis.com/v1/public/yql?q=select%2520content%2520from%2520html%2520where%2520url%253D%2522http%253A%252F%252Fugomemo.hatena.ne.jp%252Fchannels%2522%2520and%2520xpath%253D\'%252F%252Fh3%252Fa\'&amp;format=json&amp;callback=makeSelect%22>').appendTo('body')},1000)})(document.createElement('script'));function makeSelect(o){var s=$('<select name=%22channel-name%22>');$.each(o.query.results.a,function(i,v){$('<option>').text(v).appendTo(s);});$('input.txt').replaceWith(s)}

これでいいのかな。エンコード関係どうなんかね。

2008-12-01

糞FC2BLOGは氏ね

callback_FC2Blog([
<!--topentry-->{
	"title":"<%topentry_title>",
	"date":"<%topentry_year>-<%topentry_month>-<%topentry_day> <%topentry_hour>:<%topentry_minute>:<%topentry_second&gt;",
	"uri":"<%topentry_link>"
},<!--/topentry-->
]);
//<%ad> <%ad2>

っていうテンプレート書いてプレビューJSON出力ウッホホーイと思ったら何故かプレビューだとデフォテンプレが出てくる!

何考えてんだFC2BLOGは

2008-04-19

?mode=jsonがいつの間にか使えなくなってた。(´・ω・`)ショボーン

2008-04-09

JSON根本的な脆弱性

この間どっかの記事で見つけて(これ自体は数年前から海外では言われていたことだが)ターゲットを探そうと頑張ってたんだけどいろいろと理由があってここで公開することにする。

javascriptは以下のようにコンストラクタを再定義できる。

function Array() {
    var obj = this;
    var ind = 0;
    var getNext = function(x) {
        obj[ind++] setter = getNext;
        if (x) {
            console.log(x.toString());//細工。
        }
    };
    this[ind++] setter = getNext;
}

実はこれが問題で例えば、配列を含むオブジェクトJSONで渡す際に必ずArrayコンストラクタが呼び出される。

上記のように少し細工をしておけばcallbackとかついてなくても内容を処理できるのでクロスドメイン対策がされている場合でもJSONからデータを読み取ることが出来る。

余談だが、Gmailなどではこれを回避するためにwhile 1を先頭につけることで無限ループさせている。

ちなみに公開する理由
  • Firefox3はこれを仕掛けることが出来ない
  • はまちちゃんのようにいいターゲットを見つけることが出来なかった
    • いや、ひとつ見つけたんだけどあまりにも(ry
  • これとかを見る限り知っている人が少なそうだったから

2008-03-27

http://anond.hatelabo.jp/20080327022633

というわけでまとめはwikiで。

という話が過去にあったなと、検索したらもうすぐ2年です><

http://www.goodpic.com/mt/archives2/2005/06/blogwiki.html

http://mojix.org/2005/06/28/232219

でみんなワイワイ。ブックマークでワイワイ。

ま、ブログも専用カテゴリ過去日記トラックバックで幾らか似たような事が出来るし、はてなグループキーワードwiki的だけれど、結局はまとめる手間が面倒臭いと。

トラックバックがそのあたりを少しだけ補完してくれるのだけれど、もう少しハンドリングが良くなるといいな。XMLとかJSONとかで配ってくれるとツリー構築とかしやすくなると思う。

あと、閲覧側が手軽に記事同士を関連付けていける仕組みってのがほしいかも。その辺はブックマークが担う部分か。現状、はてなブックマークはそのあたりが幾らか備わってるな。wiki的に誰でも編集できるとか、含む日記とか。そのあたり、対人間も含めもう少しハンドリングよくしたり、ユーザーのページで役に立つようにしたらよいかも。

だからあれか、フローストックの中間的な部分をフローからストックへ向けてデータを再利用しながら改良していきやすいシステムでつなげていけばいいのか。

はてなブックマークポチポチ選択していったら、ブログからトラックバックコメントを取ってきて、それをまたポチポチ選択していくと関連する話のツリーやまとめが出来てきて、それでポチっと押すとはてな記法でそれが吐き出されて、とか。

そういや、今度作り直すんだって?そんな風にならないかな。

2007-10-02

1回でいいよ

mixiのview_diary.plで読み込んでるjs群。

<script language="javascript" type="text/javascript" src="/static/js/prototype.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/swfobject.js?1186571328"></script>

<script language="javascript" type="text/javascript" src="/static/js/video.js?1188959159"></script>

<script language="javascript" type="text/javascript" src="/static/js/youtube.js?1186571328"></script>

<script language="javascript" type="text/javascript" src="/static/js/prototype.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/Jemplate.js?1186571328"></script>

<script language="javascript" type="text/javascript" src="/static/js/json.js?1186571328"></script>

<script language="javascript" type="text/javascript" src="/static/js/decolink/decolink.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/prototype.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/effects.js?1186571328"></script>

<script language="javascript" type="text/javascript" src="/static/js/windowstate.js?1186571328"></script>

<script language="javascript" type="text/javascript" src="/static/js/overlay.js?1186571328"></script>

<script language="javascript" type="text/javascript" src="/static/js/popup.js?1186571328"></script>

<script language="javascript" type="text/javascript" src="/static/js/emoji_palette_base.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/emoji_palette_1.js?1188367218"></script>

<script language="javascript" type="text/javascript" src="/static/js/mixi.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/prototype.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/mixi/util.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/mixi/form.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/mixi/searchbox.js?1191207813"></script>

<script language="javascript" type="text/javascript" src="/static/js/mixi/navigation.js?1191207813"></script>

2007-09-25

がんばるあなたを応援したい

上二つはガジェット自体の構造について。要はXMLを書けばよいらしい。

で3つ目が本題で、内容javascriptRSSJSONデータを取得して表示する方法。表示はこれを参考にすればよいと思う。後は、読んだ後、リストからはずす処理をどういったUIでやるかだよね。Javaやったんならオブジェクトの扱いは大体わかると思うから、後はDOMを理解すればOKだと思う。

がんばれ。

2007-09-02

[][][Web::Scraper][API][JSON][JavaScript]Web::Scraperを使ってみたくてニフティクリップJSONを作ってみた

最近perl勉強してて、naoyaのはてなダイアリー - Web::ScraperWeb::Scraperを知り、試しにはてブのAPIを真似してニフティクリップコメントを吐くJSONを作った。

#!/usr/local/bin/perl -T
#
#
use strict;
use warnings;

use URI;
use Web::Scraper;
use JSON::XS;
use CGI;
use Encode;

my $q = new CGI;
print $q->header( -type=>'text/plain', -charset=>'UTF-8');

my $path_info = $q->path_info;
my $path = $path_info =~ m{^/?(nobracket/)?(http\w?)://?(.*)$}xms ? $2.'://'.$3
         :                                                          undef
         ;
exit if ! $path;
my $is_nobracket = 'true' if $1;

if ($q->query_string) {
    my $query_string = $q->query_string;
    $query_string =~ s/;/&amp;/g;
    $path = $path.'?'.$query_string
}

$path =~ s/%23/#/;

$path =~ s/([^\w ])/'%' . unpack('H2', $1)/eg;
$path =~ tr/ /+/;

my $entry_url = "http://clip.nifty.com/entry/?url=" . $path;

my $bookmarks = scraper {
    process 'h4>a', 'user' => 'TEXT';
    process 'li.dateAndTime', 'timestamp' => 'TEXT';
    process 'a.tagtag', 'tags[]' => sub {
        my $text = $_->as_text or return;
        my $left = decode_utf8('??~P');
        my $right = decode_utf8('??~Q');
        return $text =~ /$left (.*?) $right/xms;
    };
    process 'p.comment', 'comment' => 'TEXT';
    result 'user', 'timestamp', 'tags', 'comment';
};

my $niftyclip_entry_info = scraper {
    process 'div.clipTitle>h3>a', 'title' => 'TEXT';
    process 'div.clipTitle>p.url>a', 'url' => '@href';
    process 'div.comments>div.commentsDetails',
        'bookmarks[]' => $bookmarks;
    result 'title','url','bookmarks';
};

my $niftyclip = scraper {
    process 'div#content',
        'niftyclip_entry' => $niftyclip_entry_info;
    result 'niftyclip_entry';
}->scrape(URI->new($entry_url));

exit if ! ($niftyclip->{'url'});

$niftyclip->{'entry_url'} = $entry_url;
$niftyclip->{'count'} = @{$niftyclip->{'bookmarks'}};

my $json = JSON::XS->new->utf8->encode($niftyclip);

$json = '('. $json. ')' if ! $is_nobracket;
print $json;

取得方法は

http://monm.on.coocan.jp/niftyclip/json/entry/<取得したいURL

ってすればいい。「#」は「%23」にエスケープしないとダメ

ニフティクリップのトップならこんな感じ

作りながら「取得したいURLURLエンコードするのは面倒だな」って思い、はてブAPIみたいにpath_infoでアクセスできるようにしたわけだけど、その取得したいURLquery_stringが付いてた場合にどうやってやって良いかわからず結構悩んだ。

結局、path_info+'&'+query_stringってやることで無理やり作ったけど、普通どうやるもんなんだろ?cpanに何か良いモジュールがあったりするのかな。

それと、はてブに合わせて出力の際に()を付けるようにしたけど、これだとYahoo!Pipesで使えなかったから、

http://monm.on.coocan.jp/niftyclip/json/entry/nobracket/<取得したいURL

みたいに「nobracket」付きでアクセスした場合には()を付けないようにした。

コレ使うとニフティクリップとlivedoor クリプのコメント取ってくるAPIみたいなのが作れる。

RSSで取得する場合は

http://pipes.yahoo.com/pipes/pipe.run?_id=zECBJ_VY3BGtBw6B8ivLAg&_render=rss&URL=URLエンコードしたURL

で取得できるし、jsonで取得する場合は

http://pipes.yahoo.com/pipes/pipe.run?_id=zECBJ_VY3BGtBw6B8ivLAg&_render=json&URL=URLエンコードしたURL

ってなる。

こんな感じ

とりあえずサクッと作ってみたけど、わざわざページからJSON作ってるからちょっと重い。

デザインリニューアルされたら使えなくなるし。

その頃にはJSON吐いてくれるようになるんじゃないかなと期待はしてるけど。

参考URL:

http://d.hatena.ne.jp/naoya/20070509/1178686816

http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%D6%A5%C3%A5%AF%A5%DE%A1%BC%A5%AF%A5%A8%A5%F3%A5%C8%A5%EA%A1%BC%BE%F0%CA%F3%BC%E8%C6%C0API?kid=184075

2007-07-19

/* Ten */
if (typeof(Ten) == 'undefined') {
    Ten = {};
}
Ten.NAME = 'Ten';
Ten.VERSION = 0.06;

/* Ten.Class */
Ten.Class = function(klass, prototype) {
    if (klass && klass.initialize) {
	var c = klass.initialize;
    } else if(klass && klass.base) {
        var c = function() { return klass.base[0].apply(this, arguments) };
    } else {
	var c = function() {};
    }
    c.prototype = prototype || {};
    c.prototype.constructor = c;
    Ten.Class.inherit(c, klass);
    if (klass && klass.base) {
        for (var i = 0;  i < klass.base.length; i++) {
	    var parent = klass.base[i];
            if (i == 0) {
                c.SUPER = parent;
                c.prototype.SUPER = parent.prototype;
            }
            Ten.Class.inherit(c, parent);
            Ten.Class.inherit(c.prototype, parent.prototype);
        }
    }
    return c;
}
Ten.Class.inherit = function(child,parent) {
    for (var prop in parent) {
        if (typeof(child[prop]) != 'undefined' || prop == 'initialize') continue;
        child[prop] = parent[prop];
    }
}

/*
// Basic Ten Classes
**/

/* Ten.JSONP */
Ten.JSONP = new Ten.Class({
    initialize: function(uri,obj,method) {
        if (Ten.JSONP.Callbacks.length) {
            setTimeout(function() {new Ten.JSONP(uri,obj,method)}, 500);
            return;
        }
        var del = uri.match(/\?/) ? '&' : '?';
        uri += del + 'callback=Ten.JSONP.callback';
        if (!uri.match(/timestamp=/)) {
            uri += '&' + encodeURI(new Date());
        }
        if (obj && method) Ten.JSONP.addCallback(obj,method);
        this.script = document.createElement('script');
        this.script.src = uri;
        this.script.type = 'text/javascript';
        document.getElementsByTagName('head')[0].appendChild(this.script);
    },
    addCallback: function(obj,method) {
        Ten.JSONP.Callbacks.push({object: obj, method: method});
    },
    callback: function(args) {
        // alert('callback called');
        var cbs = Ten.JSONP.Callbacks;
        for (var i = 0; i < cbs.length; i++) {
            var cb = cbs[i];
            cb.object[cb.method].call(cb.object, args);
        }
        Ten.JSONP.Callbacks = [];
    },
    MaxBytes: 8000,
    Callbacks: []
});

/* Ten.XHR */
Ten.XHR = new Ten.Class({
    initialize: function(uri,opts,obj,method) {
        if (!uri) return;
        this.request = Ten.XHR.getXMLHttpRequest();
        this.callback = {object: obj, method: method};
        var xhr = this;
        var prc = this.processReqChange;
        this.request.onreadystatechange = function() {
            prc.apply(xhr, arguments);
        }
        var method = opts.method || 'GET';
        this.request.open(method, uri, true);
        if (method == 'POST') {
            this.request.setRequestHeader('Content-Type',
                                          'application/x-www-form-urlencoded');
        }
        var data = opts.data ? Ten.XHR.makePostData(opts.data) : null;
        this.request.send(data);
    },
    getXMLHttpRequest: function() {
        var xhr;
        var tryThese = [
            function () { return new XMLHttpRequest(); },
            function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
            function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
            function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
        ];
        for (var i = 0; i < tryThese.length; i++) {
            var func = tryThese[i];
            try {
                xhr = func;
                return func();
            } catch (e) {
                //alert(e);
            }
        }
        return xhr;
    },
    makePostData: function(data) {
        var pairs = [];
        var regexp = /%20/g;
        for (var k in data) {
            var v = data[k].toString();
            var pair = encodeURIComponent(k).replace(regexp,'+') + '=' +
                encodeURIComponent(v).replace(regexp,'+');
            pairs.push(pair);
        }
        return pairs.join('&');
    }
},{
    processReqChange: function() {
        var req = this.request;
        if (req.readyState == 4) {
            if (req.status == 200) {
                var cb = this.callback;
                cb.object[cb.method].call(cb.object, req);
            } else {
                alert("There was a problem retrieving the XML data:\n" +
                      req.statusText);
            }
        }
    }
});

/* Ten.Observer */
Ten.Observer = new Ten.Class({
    initialize: function(element,event,obj,method) {
        var func = obj;
        if (typeof(method) == 'string') {
            func = obj[method];
        }
        this.element = element;
        this.event = event;
        this.listener = function(event) {
            return func.call(obj, new Ten.Event(event || window.event));
        }
        if (this.element.addEventListener) {
            if (this.event.match(/^on(.+)$/)) {
                this.event = RegExp.$1;
            }
            this.element.addEventListener(this.event, this.listener, false);
        } else if (this.element.attachEvent) {
            this.element.attachEvent(this.event, this.listener);
        }
    }
},{
    stop: function() {
        if (this.element.removeEventListener) {
            this.element.removeEventListener(this.event,this.listener,false);
        } else if (this.element.detachEvent) {
            this.element.detachEvent(this.event,this.listener);
        }
    }
});

/* Ten.Event */
Ten.Event = new Ten.Class({
    initialize: function(event) {
        this.event = event;
    },
    keyMap: {
        8:"backspace", 9:"tab", 13:"enter", 19:"pause", 27:"escape", 32:"space",
        33:"pageup", 34:"pagedown", 35:"end", 36:"home", 37:"left", 38:"up",
        39:"right", 40:"down", 44:"printscreen", 45:"insert", 46:"delete",
        112:"f1", 113:"f2", 114:"f3", 115:"f4", 116:"f5", 117:"f6", 118:"f7",
        119:"f8", 120:"f9", 121:"f10", 122:"f11", 123:"f12",
        144:"numlock", 145:"scrolllock"
    }
},{
    mousePosition: function() {
        if (!this.event.clientX) return;
        return Ten.Geometry.getMousePosition(this.event);
    },
    isKey: function(name) {
        var ecode = this.event.keyCode;
        if (!ecode) return;
        var ename = Ten.Event.keyMap[ecode];
        if (!ename) return;
        return (ename == name);
    },
    targetIsFormElements: function() {
        var target = this.event.target;
        if (!target) return;
        var T = (target.tagName || '').toUpperCase();
        return (T == 'INPUT' || T == 'SELECT' || T == 'OPTION' ||
                T == 'BUTTON' || T == 'TEXTAREA');
    },
    stop: function() {
        var e = this.event;
        if (e.stopPropagation) {
            e.stopPropagation();
            e.preventDefault();
        } else {
            e.cancelBubble = true;
            e.returnValue = false;
        }
    }
});

/* Ten.DOM */
Ten.DOM = new Ten.Class({
    getElementsByTagAndClassName: function(tagName, className, parent) {
        if (typeof(parent) == 'undefined') {
            parent = document;
        }
        var children = parent.getElementsByTagName(tagName);
        if (className) { 
            var elements = [];
            for (var i = 0; i < children.length; i++) {
                var child = children[i];
                var cls = child.className;
                if (!cls) {
                    continue;
                }
                var classNames = cls.split(' ');
                for (var j = 0; j < classNames.length; j++) {
                    if (classNames[j] == className) {
                        elements.push(child);
                        break;
                    }
                }
            }
            return elements;
        } else {
            return children;
        }
    },
    removeEmptyTextNodes: function(element) {
        var nodes = element.childNodes;
        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i];
            if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) {
                node.parentNode.removeChild(node);
            }
        }
    },
    nextElement: function(elem) {
        do {
            elem = elem.nextSibling;
        } while (elem && elem.nodeType != 1);
        return elem;
    },
    prevElement: function(elem) {
        do {
            elem = elem.previousSibling;
        } while (elem && elem.nodeType != 1);
        return elem;
    },
    scrapeText: function(node) {
        var rval = [];
        (function (node) {
            var cn = node.childNodes;
            if (cn) {
                for (var i = 0; i < cn.length; i++) {
                    arguments.callee.call(this, cn[i]);
                }
            }
            var nodeValue = node.nodeValue;
            if (typeof(nodeValue) == 'string') {
                rval.push(nodeValue);
            }
        })(node);
        return rval.join('');
    },
    onLoadFunctions: [],
    loaded: false,
    timer: null,
    addEventListener: function(event,func) {
        if (event != 'load') return;
        Ten.DOM.onLoadFunctions.push(func);
        Ten.DOM.checkLoaded();
    },
    checkLoaded: function() {
        var c = Ten.DOM;
        if (c.loaded) return true;
        if (document && document.getElementsByTagName &&
            document.getElementById && document.body) {
            if (c.timer) {
                clearInterval(c.timer);
                c.timer = null;
            }
            for (var i = 0; i < c.onLoadFunctions.length; i++) {
                    c.onLoadFunctions[i]();
            }
            c.onLoadFunctions = [];
            c.loaded = true;
        } else {
            c.timer = setInterval(c.checkLoaded, 13);
        }
    }
});

/* Ten.Style */
Ten.Style = new Ten.Class({
    applyStyle: function(elem, style) {
        for (prop in style) {
            elem.style[prop] = style[prop];
        }
    }
});

/* Ten.Geometry */
Ten.Geometry = new Ten.Class({
    initialize: function() {
        if (Ten.Geometry._initialized) return;
        var func = Ten.Geometry._functions;
        var de = document.documentElement;
        if (window.innerWidth) {
            func.getWindowWidth = function() { return window.innerWidth; }
            func.getWindowHeight = function() { return window.innerHeight; }
            func.getXScroll = function() { return window.pageXOffset; }
            func.getYScroll = function() { return window.pageYOffset; }
        } else if (de && de.clientWidth) {
            func.getWindowWidth = function() { return de.clientWidth; }
            func.getWindowHeight = function() { return de.clientHeight; }
            func.getXScroll = function() { return de.scrollLeft; }
            func.getYScroll = function() { return de.scrollTop; }
        } else if (document.body.clientWidth) {
            func.getWindowWidth = function() { return document.body.clientWidth; }
            func.getWindowHeight = function() { return document.body.clientHeight; }
            func.getXScroll = function() { return document.body.scrollLeft; }
            func.getYScroll = function() { return document.body.scrollTop; }
        }
        Ten.Geometry._initialized = true;
    },
    _initialized: false,
    _functions: {},
    getScroll: function() {
        if (!Ten.Geometry._initialized) new Ten.Geometry;
        return {
            x: Ten.Geometry._functions.getXScroll(),
            y: Ten.Geometry._functions.getYScroll()
        };
    },
    getMousePosition: function(pos) {
        // pos should have clientX, clientY same as mouse event
        if ((navigator.userAgent.indexOf('Safari') > -1) &&
            (navigator.userAgent.indexOf('Version/') < 0)) {
            return {
                x: pos.clientX,
                y: pos.clientY
            };
        } else {
            var scroll = Ten.Geometry.getScroll();
            return {
                x: pos.clientX + scroll.x,
                y: pos.clientY + scroll.y
            };
        }
    },
    getElementPosition: function(e) {
        return {
            x: e.offsetLeft,
            y: e.offsetTop
        };
    },
    getWindowSize: function() {
        if (!Ten.Geometry._initialized) new Ten.Geometry;
        return {
            w: Ten.Geometry._functions.getWindowWidth(),
            h: Ten.Geometry._functions.getWindowHeight()
        };
    }
});

/* Ten.Position */
Ten.Position = new Ten.Class({
    initialize: function(x,y) {
        this.x = x;
        this.y = y;
    },
    subtract: function(a,b) {
        return new Ten.Position(a.x - b.x, a.y - b.y);
    }
});

/*
// require Ten.js
**/

/* Ten.SubWindow */
Ten.SubWindow = new Ten.Class({
    initialize: function() {
        var c = this.constructor;
        if (c.singleton && c._cache) {
            return c._cache;
        }
        var div = document.createElement('div');
        Ten.Style.applyStyle(div, Ten.SubWindow._baseStyle);
        Ten.Style.applyStyle(div, c.style);
        this.window = div;
        this.addContainerAndCloseButton();
        document.body.appendChild(div);
        if (c.draggable) {
            this._draggable = new Ten.Draggable(div, this.handle);
        }
        if (c.singleton) c._cache = this;
        return this;
    },
    _baseStyle: {
        color: '#000',
        position: 'absolute',
        display: 'none',
        zIndex: 2,
        left: 0,
        top: 0,
        backgroundColor: '#fff',
        border: '1px solid #bbb'
    },
    style: {
        padding: '2px',
        textAlign: 'center',
        borderRadius: '6px',
        MozBorderRadius: '6px',
        width: '100px',
        height: '100px'
    },
    handleStyle: {
        position: 'absolute',
        top: '0px',
        left: '0px',
        backgroundColor: '#f3f3f3',
        borderBottom: '1px solid #bbb',
        width: '100%',
        height: '30px'
    },
    containerStyle: {
        margin: '32px 0 0 0',
        padding: '0 10px'
    },
    // closeButton: 'close.gif',
    closeButton: 'http://s.hatena.com/images/close.gif',
    closeButtonStyle: {
        position: 'absolute',
        top: '8px',
        right: '10px',
        cursor: 'pointer'
    },
    _baseScreenStyle: {
        position: 'absolute',
        top: '0px',
        left: '0px',
        display: 'none',
        zIndex: 1,
        overflow: 'hidden',
        width: '100%',
        height: '100%'
    },
    screenStyle: {},
    showScreen: true,
    singleton: true,
    draggable: true,
    _cache: null
},{
    screen: null,
    windowObserver: null,
    visible: false,
    addContainerAndCloseButton: function() {
        var win = this.window;
        var c = this.constructor;
        var div = document.createElement('div');
        win.appendChild(div);
        Ten.Style.applyStyle(div, c.containerStyle);
        this.container = div;
        if (c.handleStyle) {
            var handle = document.createElement('div');
            Ten.Style.applyStyle(handle, c.handleStyle);
            win.appendChild(handle);
            this.handle = handle;
        }
        if (c.closeButton) {
	    var btn = document.createElement('img');
            btn.src = c.closeButton;
            btn.alt = 'close';
            Ten.Style.applyStyle(btn, c.closeButtonStyle);
            win.appendChild(btn);
            new Ten.Observer(btn, 'onclick', this, 'hide');
            this.closeButton = btn;
        }
        if (c.showScreen) {
            var screen = document.createElement('div');
            Ten.Style.applyStyle(screen, Ten.SubWindow._baseScreenStyle);
            Ten.Style.applyStyle(screen, c.screenStyle);
            document.body.appendChild(screen);
            this.screen = screen;
            new Ten.Observer(screen, 'onclick', this, 'hide');
        }
    },
    show: function(pos) {
        pos = (pos.x && pos.y) ? pos : {x:0, y:0};
        with (this.window.style) {
            display = 'block';
            left = pos.x + 'px';
            top = pos.y + 'px';
        }
        if (this.screen) {
            with (this.screen.style) {
                display = 'block';
                left = Ten.Geometry.getScroll().x + 'px';
                top = Ten.Geometry.getScroll().y + 'px';
            }
        }
        this.windowObserver = new Ten.Observer(document.body, 'onkeypress', this, 'handleEscape');
        this.visible = true;
    },
    handleEscape: function(e) {
        if (!e.isKey('escape')) return;
        this.hide();
    },
    hide: function() {
        if (this._draggable) this._draggable.endDrag();
        this.window.style.display = 'none';
        if (this.screen) this.screen.style.display = 'none';
        if (this.windowObserver) this.windowObserver.stop();
        this.visible = false;
    }
});

/* Ten.Draggable */
Ten.Draggable = new Ten.Class({
    initialize: function(element,handle) {
        this.element = element;
        this.handle = handle || element;
        this.startObserver = new Ten.Observer(this.handle, 'onmousedown', this, 'startDrag');
        this.handlers = [];
    }
},{
    startDrag: function(e) {
        if (e.targetIsFormElements()) return;
        this.delta = Ten.Position.subtract(
            e.mousePosition(),
            Ten.Geometry.getElementPosition(this.element)
        );
        this.handlers = [
            new Ten.Observer(document, 'onmousemove', this, 'drag'),
            new Ten.Observer(document, 'onmouseup', this, 'endDrag'),
            new Ten.Observer(this.element, 'onlosecapture', this, 'endDrag')
        ];
        e.stop();
    },
    drag: function(e) {
        var pos = Ten.Position.subtract(e.mousePosition(), this.delta);
        Ten.Style.applyStyle(this.element, {
            left: pos.x + 'px',
            top: pos.y + 'px'
        });
        e.stop();
    },
    endDrag: function(e) {
        for (var i = 0; i < this.handlers.length; i++) {
            this.handlers[i].stop();
        }
        if(e) e.stop();
    }
});

/* Hatena */
if (typeof(Hatena) == 'undefined') {
    Hatena = {};
}

/* Hatena.User */
Hatena.User = new Ten.Class({
    initialize: function(name) {
        this.name = name;
    },
    getProfileIcon: function(name) {
        if (!name) name = 'user';
        var pre = name.match(/^[\w-]{2}/)[0];
        var img = document.createElement('img');
        img.src = 'http://www.hatena.ne.jp/users/' + pre + '/' + name + '/profile_s.gif';
        img.alt = name;
        img.setAttribute('class', 'profile-icon');
        img.setAttribute('width','16px');
        img.setAttribute('height','16px');
        with (img.style) {
            margin = '0 3px';
            border = 'none';
            verticalAlign = 'middle';
        }
        return img;
    }
}, {
    profileIcon: function() {
        return Hatena.User.getProfileIcon(this.name);
    }
});

/* Hatena.Star */
if (typeof(Hatena.Star) == 'undefined') {
    Hatena.Star = {};
}

/*
// Hatena.Star.* classes //
**/
if (window.location && window.location.host.match(/hatena\.com/)) {
    Hatena.Star.BaseURL = 'http://s.hatena.com/';
} else {
    Hatena.Star.BaseURL = 'http://s.hatena.ne.jp/';
}
Hatena.Star.Token = null;

/* Hatena.Star.User */
Hatena.Star.User = new Ten.Class({
    base: [Hatena.User],
    initialize: function(name) {
        if (Hatena.Star.User._cache[name]) {
            return Hatena.Star.User._cache[name];
        } else {
            this.name = name;
            Hatena.Star.User._cache[name] = this;
            return this;
        }
    },
    _cache: {}
},{
    userPage: function() {
        return Hatena.Star.BaseURL + this.name + '/';
    }
});

/* Hatena.Star.Entry */
Hatena.Star.Entry = new Ten.Class({
    initialize: function(e) {
        this.entry = e;
        this.uri = e.uri;
        this.title = e.title;
        this.star_container = e.star_container;
        this.comment_container = e.comment_container;
        this.stars = [];
        this.comments = [];
    },
    maxStarCount: 11
},{
    flushStars: function() {
        this.stars = [];
        this.star_container.innerHTML = '';
    },
    bindStarEntry: function(se) {
        this.starEntry = se;
        for (var i = 0; i < se.stars.length; i++) {
            if (typeof(se.stars[i]) == 'number') {
                this.stars.push(new Hatena.Star.InnerCount(se.stars[i],this));
            } else {
                this.stars.push(new Hatena.Star.Star(se.stars[i]));
            }
        }
        if (se.comments && !this.comments.length) {
            for (var i = 0; i < se.comments.length; i++) {
                this.comments.push(new Hatena.Star.Comment(se.comments[i]));
            }
        }
        this.can_comment = se.can_comment;
    },
    setCanComment: function(v) {
        this.can_comment = v;
    },
    showButtons: function() {
        this.addAddButton();
        this.addCommentButton();
    },
    addAddButton: function() {
        if (this.star_container) {
            this.addButton = new Hatena.Star.AddButton(this);
            this.star_container.appendChild(this.addButton);
        }
    },
    addCommentButton: function() {
        if (this.comment_container) {
            this.commentButton = new Hatena.Star.CommentButton(this);
            this.comment_container.appendChild(this.commentButton.img);
        }
    },
    showStars: function() {
        var klass = this.constructor;
        // if (this.stars.length > klass.maxStarCount) {
        //     var ic = new Hatena.Star.InnerCount(this.stars.slice(1,this.stars.length));
        //     this.star_container.appendChild(this.stars[0]);
        //     this.star_container.appendChild(ic);
        //     this.star_container.appendChild(this.stars[this.stars.length - 1]);
        // } else {
        for (var i = 0; i < this.stars.length; i++) {
            this.star_container.appendChild(this.stars[i]);
        }
    },
    showCommentButton: function() {
        if (this.can_comment) {
            this.commentButton.show();
            if (this.comments.length) this.commentButton.activate();
        } else {
            // this.commentButton.hide();
        }
    },
    addStar: function(star) {
        this.stars.push(star);
        this.star_container.appendChild(star);
    },
    addComment: function(com) {
        if (!this.comments) this.comments = [];
        if (this.comments.length == 0) {
            this.commentButton.activate();
        }
        this.comments.push(com);
    },
    showCommentCount: function() {
        this.comment_container.innerHTML += this.comments.length;
    }
});

/* Hatena.Star.Button */
Hatena.Star.Button = new Ten.Class({
    createButton: function(args) {
        var img = document.createElement('img');
        img.src = args.src;
        img.alt = img.title = args.alt;
        with (img.style) {
	    cursor = 'pointer';
	    margin = '0 3px';
            padding = '0';
            border = 'none';
            verticalAlign = 'middle';
        }
        return img;
    }
});

/* Hatena.Star.AddButton */
Hatena.Star.AddButton = new Ten.Class({
    base: ['Hatena.Star.Button'],
    initialize: function(entry) {
        this.entry = entry;
        this.lastPosition = null;
        var img = Hatena.Star.Button.createButton({
            src: Hatena.Star.AddButton.ImgSrc,
            alt: 'Add Star'
        });
        this.observer = new Ten.Observer(img,'onclick',this,'addStar');
        this.img = img;
        return img;
    },
    ImgSrc: Hatena.Star.BaseURL + 'images/add.gif'
},{
    addStar: function(e) {
        this.lastPosition = e.mousePosition();
        var uri = Hatena.Star.BaseURL + 'star.add.json?uri=' + encodeURIComponent(this.entry.uri) +
            '&title=' + encodeURIComponent(this.entry.title);
        if (Hatena.Star.Token) {
            uri += '&token=' + Hatena.Star.Token;
        }
        new Ten.JSONP(uri, this, 'receiveResult');
    },
    receiveResult: function(args) {
        var name = args ? args.name : null;
        if (name) {
            this.entry.addStar(new Hatena.Star.Star({name: name}));
            //alert('Succeeded in Adding Star ' + args);
        } else if (args.errors) {
            var pos = this.lastPosition;
            pos.x -= 10;
            pos.y += 25;
            var scroll = Ten.Geometry.getScroll();
            var scr = new Hatena.Star.AlertScreen();
            var alert = args.errors[0];
            scr.showAlert(alert, pos);
        }
    }
});

/* Hatena.Star.CommentButton */
Hatena.Star.CommentButton = new Ten.Class({
    base: ['Hatena.Star.Button'],
    initialize: function(entry) {
        this.entry = entry;
        this.lastPosition = null;
        var img = Hatena.Star.Button.createButton({
            src: Hatena.Star.CommentButton.ImgSrc,
            alt: 'Comments'
        });
        img.style.display = 'none';
        this.observer = new Ten.Observer(img,'onclick',this,'showComments');
        this.img = img;
    },
    ImgSrc: Hatena.Star.BaseURL + 'images/comment.gif',
    ImgSrcActive: Hatena.Star.BaseURL + 'images/comment_active.gif'
},{
    showComments: function(e) {
        if (!this.screen) this.screen = new Hatena.Star.CommentScreen();
        this.screen.bindEntry(this.entry);
        var pos = e.mousePosition();
        pos.y += 25;
        this.screen.showComments(this.entry, pos);
    },
    hide: function() {
        this.img.style.display = 'none';
    },
    show: function() {
        this.img.style.display = 'inline';
    },
    activate: function() {
        this.show();
        this.img.src = Hatena.Star.CommentButton.ImgSrcActive;
    }
});

/* Hatena.Star.Star */
Hatena.Star.Star = new Ten.Class({
    initialize: function(args) {
        if (args.img) {
            this.img = args.img;
            this.name = this.img.getAttribute('alt');
        } else {
            this.name = args.name;
            var img = document.createElement('img');
            img.src = Hatena.Star.Star.ImgSrc;
            img.alt = this.name;
            with (img.style) {
                padding = '0';
                border = 'none';
            }
            this.img = img;
        }
	new Ten.Observer(this.img,'onmouseover',this,'showName');
	new Ten.Observer(this.img,'onmouseout',this,'hideName');
	if (this.name) {
            this.user = new Hatena.Star.User(this.name);
            this.img.style.cursor = 'pointer';
            new Ten.Observer(this.img,'onclick',this,'goToUserPage');
        }
        if (args.count && args.count > 1) {
            var c = document.createElement('span');
            c.setAttribute('class', 'hatena-star-inner-count');
            Ten.Style.applyStyle(c, Hatena.Star.InnerCount.style);
            c.innerHTML = args.count;
            var s = document.createElement('span');
            s.appendChild(img);
            s.appendChild(c);
            return s;
        } else {
            return this.img;
        }
    },
    ImgSrc: Hatena.Star.BaseURL + 'images/star.gif'
},{
    showName: function(e) {
        if (!this.screen) this.screen = new Hatena.Star.NameScreen();
        var pos = e.mousePosition();
        pos.x += 10;
        pos.y += 25;
        this.screen.showName(this.name, pos);
    },
    hideName: function() {
        if (!this.screen) return;
        this.screen.hide();
    },
    goToUserPage: function() {
        window.location = this.user.userPage();
    }
});

/* Hatena.Star.InnerCount */
Hatena.Star.InnerCount = new Ten.Class({
    initialize: function(count, e) {
        this.count = count;
        this.entry = e;
        var c = document.createElement('span');
        c.setAttribute('class', 'hatena-star-inner-count');
        Ten.Style.applyStyle(c, Hatena.Star.InnerCount.style);
        c.style.cursor = 'pointer';
        c.innerHTML = count;
        new Ten.Observer(c,'onclick',this,'showInnerStars');
        this.container = c;
        return c;
    },
    style: {
        color: '#f4b128',
        fontWeight: 'bold',
        fontSize: '80%',
        fontFamily: '"arial", sans-serif',
        margin: '0 2px'
    }
},{
    showInnerStars: function() {
        var url = Hatena.Star.BaseURL + 'entry.json?uri=' +
        encodeURIComponent(this.entry.uri);
        new Ten.JSONP(url, this, 'receiveStarEntry');
    },
    receiveStarEntry: function(res) {
        var se = res.entries[0];
        var e = this.entry;
        if (encodeURIComponent(se.uri) != encodeURIComponent(e.uri)) return;
        e.flushStars();
        e.bindStarEntry(se);
        e.addAddButton();
        e.showStars();
    }
});

/* Hatena.Star.Comment */
Hatena.Star.Comment = new Ten.Class({
    initialize: function(args) {
        this.name = args.name;
        this.body = args.body;
    }
},{
    asElement: function() {
        var div = document.createElement('div');
        with (div.style) {
            margin = '0px 0';
            padding = '5px 0';
            borderBottom = '1px solid #ddd';
        }
        var ico = Hatena.User.getProfileIcon(this.name);
        div.appendChild(ico);
        var span = document.createElement('span');
        with(span.style) {
            fontSize = '90%';
        }
        span.innerHTML = this.body;
        div.appendChild(span);
        return div;
    }
});

/* Hatena.Star.NameScreen */
Hatena.Star.NameScreen = new Ten.Class({
    base: [Ten.SubWindow],
    style: {
        padding: '2px',
        textAlign: 'center'
    },
    containerStyle: {
        margin: 0,
        padding: 0
    },
    handleStyle: null,
    showScreen: false,
    closeButton: null,
    draggable: false
},{
    showName: function(name, pos) {
        this.container.innerHTML = '';
        this.container.appendChild(Hatena.User.getProfileIcon(name));
        this.container.appendChild(document.createTextNode(name));
        this.show(pos);
    }
});

/* Hatena.Star.AlertScreen */
Hatena.Star.AlertScreen = new Ten.Class({
    base: [Ten.SubWindow],
    style: {
        padding: '2px',
        textAlign: 'center',
        borderRadius: '6px',
        MozBorderRadius: '6px',
        width: '240px',
        height: '120px'
    },
    handleStyle: {
        position: 'absolute',
        top: '0px',
        left: '0px',
        backgroundColor: '#f3f3f3',
        borderBottom: '1px solid #bbb',
        width: '100%',
        height: '30px',
        borderRadius: '6px 6px 0 0',
        MozBorderRadius: '6px 6px 0 0'
    }
},{
    showAlert: function(msg, pos) {
        this.container.innerHTML = msg;
        var win = Ten.Geometry.getWindowSize();
        var scr = Ten.Geometry.getScroll();
        var w = parseInt(this.constructor.style.width) + 20;
        if (pos.x + w > scr.x + win.w) pos.x = win.w + scr.x - w;
        this.show(pos);
    }
});

/* Hatena.Star.CommentScreen */
Hatena.Star.CommentScreen = new Ten.Class({
    base: [Ten.SubWindow],
    initialize: function() {
        var self = this.constructor.SUPER.call(this);
        if (!self.commentsContainer) self.addCommentsContainer();
        return self;
    },
    style: {
        width: '280px',
        height: '280px',
        overflowY: 'auto',
        padding: '2px',
        textAlign: 'center',
        borderRadius: '6px',
        MozBorderRadius: '6px'
    },
    handleStyle: {
        position: 'absolute',
        top: '0px',
        left: '0px',
        backgroundColor: '#f3f3f3',
        borderBottom: '1px solid #bbb',
        width: '100%',
        height: '30px',
        borderRadius: '6px 6px 0 0',
        MozBorderRadius: '6px 6px 0 0'
    },
    containerStyle: {
        margin: '32px 0 0 0',
        textAlign: 'left',
        padding: '0 10px'
    },
    getLoadImage: function() {
        var img = document.createElement('img');
        img.src = Hatena.Star.BaseURL + 'images/load.gif';
        img.setAttribute('alt', 'Loading');
        with (img.style) {
            verticalAlign = 'middle';
            margin = '0 2px';
        }
        return img;
    }
},{
    addCommentsContainer: function() {
        var div = document.createElement('div');
        with (div.style) {
            marginTop = '-3px';
        }
        this.container.appendChild(div);
        this.commentsContainer = div;
    },
    showComments: function(e, pos) {
        var comments = e.comments;
        if (!comments) comments = [];
        this.commentsContainer.innerHTML = '';
        for (var i=0; i<comments.length; i++) {
            this.commentsContainer.appendChild(comments[i].asElement());
        }
        if (e.starEntry && !e.can_comment) {
            this.hideCommentForm();
        } else {
            this.addCommentForm();
        }
        var win = Ten.Geometry.getWindowSize();
        var scr = Ten.Geometry.getScroll();
        var w = parseInt(this.constructor.style.width) + 20;
        if (pos.x + w > scr.x + win.w) pos.x = win.w + scr.x - w;
        this.show(pos);
    },
    bindEntry: function(e) {
        this.entry = e;
    },
    sendComment: function(e) {
        if (!e.isKey('enter')) return;
        var body = this.commentInput.value;
        if (!body) return;
        this.commentInput.disabled = 'true';
        this.showLoadImage();
        var url = Hatena.Star.BaseURL + 'comment.add.json?body=' + encodeURIComponent(body) +
            '&uri=' + encodeURIComponent(this.entry.uri) +
            '&title=' + encodeURIComponent(this.entry.title);
        new Ten.JSONP(url, this, 'receiveResult');
    },
    receiveResult: function(args) {
        if (!args.name || !args.body) return;
        this.commentInput.value = ''; 
        this.commentInput.disabled = '';
        this.hideLoadImage();
        var com = new Hatena.Star.Comment(args);
        this.entry.addComment(com);
        this.commentsContainer.appendChild(com.asElement());
    },
    showLoadImage: function() {
        if (!this.loadImage) return; 
        this.loadImage.style.display = 'inline';
    },
    hideLoadImage: function() {
        if (!this.loadImage) return; 
        this.loadImage.style.display = 'none';
    },
    hideCommentForm: function() {
        if (!this.commentForm) return;
        this.commentForm.style.display = 'none';
    },
    addCommentForm: function() {
        if (this.commentForm) {
            this.commentForm.style.display = 'block';
            return;
        }
        var form = document.createElement('div');
        this.container.appendChild(form);
        this.commentForm = form;
        with (form.style) {
            margin = '0px 0';
            padding = '5px 0';
            // borderTop = '1px solid #ddd';
        }
        //if (Hatena.Visitor) {
        //    form.appendChild(Hatena.Visitor.profileIcon());
        //} else {
        //    form.appendChild(Hatena.User.getProfileIcon());
        //}
        var input = document.createElement('input');
        input.type = 'text';
        with (input.style) {
            width = '215px';
	    border = '1px solid #bbb';
            padding = '3px';
        }
        form.appendChild(input);
        this.commentInput = input;
        var img = this.constructor.getLoadImage();
        this.loadImage = img;
        this.hideLoadImage();
        form.appendChild(img);
        new Ten.Observer(input,'onkeypress',this,'sendComment');
    }
});

/* Hatena.Star.EntryLoader */
Hatena.Star.EntryLoader = new Ten.Class({
    initialize: function() {
        var entries = Hatena.Star.EntryLoader.loadEntries();
        this.entries = [];
        for (var i = 0; i < entries.length; i++) {
            var e = new Hatena.Star.Entry(entries[i]);
            e.showButtons();
            this.entries.push(e);
        }
        this.getStarEntries();
    },
    createStarContainer: function() {
        var sc = document.createElement('span');
        sc.setAttribute('class', 'hatena-star-star-container');
        sc.style.marginLeft = '1px';
        return sc;
    },
    createCommentContainer: function() {
        var cc = document.createElement('span');
        cc.setAttribute('class', 'hatena-star-comment-container');
        cc.style.marginLeft = '1px';
        return cc;
    },
    scrapeTitle: function(node) {
        var rval = [];
        (function (node) {
            if (node.tagName == 'SPAN' &&
                (node.className == 'sanchor' ||
                 node.className == 'timestamp')) {
                     return;
            } else if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) {
                return;
            }
            var cn = node.childNodes;
            if (cn) {
                for (var i = 0; i < cn.length; i++) {
                    arguments.callee.call(this, cn[i]);
                }
            }
            var nodeValue = node.nodeValue;
            if (typeof(nodeValue) == 'string') {
                rval.push(nodeValue);
            }
        })(node);
        return rval.join('');
    },
    headerTagAndClassName: ['h3',null],
    getHeaders: function() {
        var t = Hatena.Star.EntryLoader.headerTagAndClassName;
        return Ten.DOM.getElementsByTagAndClassName(t[0],t[1],document);
    },
    loadEntries: function() {
        var entries = [];
        //var headers = document.getElementsByTagName('h3');
        var c = Hatena.Star.EntryLoader;
        var headers = c.getHeaders();
        for (var i = 0; i < headers.length; i++) {
            var header = headers[i];
            var a = header.getElementsByTagName('a')[0];
            if (!a) continue;
            var uri = a.href;
            var title = '';
            // Ten.DOM.removeEmptyTextNodes(header);
            var cns = header.childNodes;
            title = c.scrapeTitle(header);
            var cc = c.createCommentContainer();
            header.appendChild(cc);
            var sc = c.createStarContainer();
            header.appendChild(sc);
            entries.push({
                uri: uri,
                title: title,
                star_container: sc,
                comment_container: cc
            });
        }
        return entries;
    }
},{
    getStarEntries: function() {
        var url = Hatena.Star.BaseURL + 'entries.json?';
        for (var i = 0; i < this.entries.length; i++) {
            if (url.length > Ten.JSONP.MaxBytes) {
                new Ten.JSONP(url, this, 'receiveStarEntries');
                url = Hatena.Star.BaseURL + 'entries.json?';
            }
            url += 'uri=' + encodeURIComponent(this.entries[i].uri) + '&';
        }
        new Ten.JSONP(url, this, 'receiveStarEntries');
    },
    receiveStarEntries: function(res) {
        var entries = res.entries;
        if (!entries) entries = [];
        for (var i = 0; i < this.entries.length; i++) {
            var e = this.entries[i];
            for (var j = 0; j < entries.length; j++) {
                var se = entries[j];
                if (!se.uri) continue;
                if (encodeURIComponent(se.uri) == encodeURIComponent(e.uri)) {
                    e.bindStarEntry(se);
                    entries.splice(j,1);
                    break;
                }
            }
            if (typeof(e.can_comment) == 'undefined') {
                e.setCanComment(res.can_comment);
            }
            e.showStars();
            e.showCommentButton();
        }
    }
});

/* Hatena.Star.WindowObserver */
Hatena.Star.WindowObserver = new Ten.Class({
    initialize: funct


  

2007-07-13

はてなスター」で他人に罵詈雑言を浴びせる方法

基本的に他人を褒めることしかできない「はてなスター」で他人に罵詈雑言を浴びせる方法です。

 

ttp://s.hatena.ne.jp/star.add.json?uri=http%3A%2F%2Fd.hatena.ne.jp%2F【憎いあん畜生ID】%2F0000&title=【届けたい罵詈雑言UTF-8パーセントエンコード)】&callback=Ten.JSONP.callback

 

これで

ttp://s.hatena.ne.jp/【憎いあん畜生ID】/blogs

メッセージが登録されるよ!

 

ttp://d.hatena.ne.jp/【憎いあん畜生ID】/0000

なんてページは作れないから、誰が★を送ってきたのかすらわからないよ!

仕様が変わったり、僕の勘違いだったら大変だから、ご利用は計画的に

 

1URIにつき1つしか登録されないから、複数のメッセージを送りたい場合は「0000」の部分を1回1回適当に変えようね!

 

まー、問題は、自分の

ttp://s.hatena.ne.jp/【ID】/blogs

なんて、ほとんどの人が見ないだろうってことだな。

ログイン ユーザー登録
ようこそ ゲスト さん