「DDD」を含む日記 RSS

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

2017-09-06

anond:20170906164245

申し訳ないけどどこ推してるかは伏せておくね。

2.5次元って言われる舞台には行く方だったから、アプリやってこうだろうなと想像してるキャラクターと実際に俳優さんが演じるキャラクターの演技や表現が一致するとはあんまり思ってないし、そういう違いを楽しめない性質じゃないと自分では思ってたんだけどあんステはそれ以前の問題だった。

北斗の弄りも本当に酷かったし、あとアドニス颯馬あたりも元のキャラクター性を汲まれずにすっかり場面転換の間をつなぐためのギャグ要員扱いされてて酷いなと思った。ほかにもいるけど言い出したらきりがない気がする。

俳優の演技が解釈違いとか以前の問題で、キャラクター舞台装置としてしか扱わない脚本なり演出なりの問題なんじゃないかなあ。俳優さんたちは別に率先してキャラクター蔑ろにはしてなかったと思うし。

TYMはとくにS1からDDDまでの間を引き延ばしに伸ばしてほぼ進展のない内容の公演だったから、適当にやっててもキャラクターの格好した俳優ライブすればそれでウケると思って作ってるんじゃないかと感じてしまったよ。

原作物語とかキャラクターとかを俳優だけじゃなくて制作スタッフももちょっと大事にしてくれたらよかったのにって思うから、行けないのも悪いことばっかりじゃないんじゃないかな。勝手な言い分だし、それも見てから判断たかったって思うと悔しいだろうけど。

追記:当日券の詳細出てるみたいだから、元記事増田チャレンジできるなら当たるように祈ってるよ。

2017-07-10

Webアプリを作るときにどの言語/WAFで書くべきか

使ったことあるモノもないモノもごちゃまぜにして経験雰囲気で書いてる。

PHP

Laravelは結構好き。DSL過ぎず、それなりにフルスタック生産性もいい。

何よりLaravel本体ソースコードが読みやすいのがいい。

まともな日本語情報が少ないのは弱点だけど、気になったところは本体コードを読めばすぐに分かる。

最大の欠点PHPってことだ。他のLL言語に比べてPHP自体生産性は低い。セキュリティ面の不安も大きい。それに安心して後を任せられるようなPHPerは一握りしかいない。

Perl

Mojolicious結構好き。これもDSL過ぎず分かりやすい。CPAN豊富ライブラリ群もある。

Perlは可読性が悪いなんて言うけど、ちゃんとしたライブラリ普通に読みやすいよ。

最大の欠点Perlってことだ。長期的に開発者を集めることを考えたら茨の道だろ?

Python

今でこそ機械学習Pythonが人気になっているけど、Web系はまだまだマイナーだ。

Djangoプロジェクト/アプリケーションという構成単位の考え方が好きじゃない。理論的な利点は分かるけど、現実問題それが必要になるケースが浮かばん。

Django以外でフルスタックのWAFが出てくればいいんだけど。Tornadoはフルスタックじゃないのでちょっと違う。

Python3で安心して開発できるならアリだと思うけど今はどうなの?使いたいライブラリが3系に対応していないとかで躓きたくないよ。

あと単純に速度が遅いよね。いや書き方を気をつければマシにはなるんだけど、書き方を気をつけなければいけない時点でつらい。

Ruby

Railsは便利だ。周辺ライブラリの充実度もすごい。情報玉石混交だけどまともな情報もたくさんある。

ただあまりにもDSL過ぎる。Railsプログラミングではなく、一つの巨大なDSLだ。

Railsプログラマの何割が、少しでもいいかRails本体ソースコードを読んだことがあるのか。めっちゃ読みにくいんだけど。Rubyは可読性が高いなんて嘘だろう。Perlと一緒でちゃんとしたコードは読みやすいけどそれはプログラマ依存する話で、言語自体に可読性の高さはない。言語思想の通り書くのは楽しいよ。でも読むのがつらい。

Rails自体DSLみたいなもんなのに、RSpecやらRakeやら周辺ツールDSL意識高すぎる。

問題があった時にググらずにコード読んで解決できるRailsエンジニアはどれだけいるのか。情報量が多いからググれば解決すると答えるやつは、底辺PHPerと大差ないからな。

あとバージョンアップ追従するのが面倒過ぎる。でも放置したら負債になるし。意識高くRailsで開発したやつの大半はバージョンアップやらの保守に入る頃にはもうそプロジェクトはいないんだろ?だからそのつらさを知らないんだろ?

散々罵ったけど、このDSLを覚えれば生産性が高いのは事実だ。だから結局ついていく確率が高い。モテ男なんだよ結局こいつは。

Java

SIerさんに敬礼

Scala

Playが王道だけど最新バージョンになるほど情報が少ない。このあたりがRailsと違う。公式(英語)とか本体コードを読める人じゃないとつらい。

そもそもJava、というかJVM周りの知識がないと本番運用はつらいだろう。LL言語運用経験しかない人は特につらい。LL言語でいうhot deployみたいなことがしたい時のやりかた分かってる?

コンパイルの遅さに耐えて開発し、運用時のGC問題を乗り越え、黒魔術を味方につけてライブラリコードリーディングが出来るならいいんじゃないか

動作は早いし、言語のものは強力だ。

Scalaを好むプログラマ関数型やらDDDやら意識高い人が多い。別にScala自体にそれらは必須ではないけど、そこら辺を意識しないならJava8でいいんじゃないかとも思う。

Node.js

非同期処理で開発することの難しさに耐えられるの?

ベストプラクティスがなく、移り変わり激しいJS界隈に流されてオレオレで書いたコード保守する自信があるならいいんじゃない。俺はない。

Go

API単体ならともかく、画面も担う普通Webアプリを書くような言語じゃない。少なくとも今は。

正確に言うと書けないことはないけど、Webアプリに関する周辺ライブラリの不足を乗り越えてまで書くメリットほとんどない。

ClojureとかElixirとか

運用実績ノウハウが少ない中で、自分で乗り越えていく気概があればいいんじゃない

結論

完璧選択などない。

2017-05-22

#このライトノベルの続きが読みたい2017

http://anond.hatelabo.jp/20140501214714

これの話題が出ていたので、今思い出せるものを書き連ねてみる。

下に行くほど絶望度がたかくなっている、かもしれない。

これ以外にも「読みたい」ってやつで、続き出てねーなーと思い出したものがあったら教えてくんちぇ。

近年ではなろう発の小説バンバン打ち切られてるんで拾いきれません。


甘城ブリリアントパーク

2016/06……8巻

賀東案件アニメ終わってから鈍化。

六花の勇者

2015/7……6巻

アニメは1巻を12話に引き伸ばすというスーパープレイ短編集を間に挟んだけど、本編は2年お預け。

世界居酒屋「のぶ」

2015/12……4巻

アニメ化決まってるけど、作者が療養中で更新もなし。はよ。

やはり俺の青春ラブコメはまちがっている。

2015/06……11

えっ……もう2年出てないの。クライマックス間近なのに。

辺境の老騎士

2015/05……3巻

コミカライズ始まったし、それがうまく行けばなんとかなるのかな。

新本格魔法少女りすか

2007/03……3巻

太田が悪い。『傷物語』の特典小説ちょっと出てきたらしい。希望はある。

世界シリーズ

2008/12……『不気味で素朴な囲われたきみとぼくの壊れた世界

メフィストに載ってたっけ? こちらも特典小説に出てきたらしい。希望はある。

ウィザーズ・ブレイン

2014/04…… (9) 破滅の星 (上)

最近出たと思ったけど、もう3年前……。

R.O.D

2016/08……12

11から10年の空き。果たして次巻はちゃんと出るのか。

アルカディア=ガーデン

2016/03……1巻

http://over-lap.co.jp/bunko/arcadiagarden/

DDD

2007/08……2巻

大丈夫FGOがなんとかしてくれる。希望はある。

DADDYFACE

2005/01……メドゥーサ4巻

「メドゥーサ編」までは完結しているけど、続きがあってほしい。

アニソン神様

2013/07……2巻

「つづく」で続かない。このラノ文庫は虫の息。

カナエの星

2015/09……3巻

水着回やって続きは未定? 『A/Bエクストリーム』って何年前?

ささみさん@がんばらない

2013/06……11

あんスタで忙しいんだよきっと。

ブラック・ブレット

2014/04…… (7) 世界変革の銃弾

音沙汰なし。絶望的。

アイドライジング!

2012/03……4巻

まだ時折目にする惜しまタイトル。もう出ないだろう。

ゆらゆらと揺れる海の彼方

2008/11……10

一応区切りの部分だが、不完全燃焼。著者はもう何も書いてない?

我が家お稲荷様

2007/10……7巻

まー、もう書かんのだろうね。アニメ化後に刊行止まるケース多すぎ。

悪魔のミカタ

2009/2……666 6巻

It編で終わったんだ。そう思うしかない。

涼宮ハルヒシリーズ

2011/6……涼宮ハルヒの驚愕(後)

もはや完結したと思え。

学校を出よう!

2004/10……6巻

完結したんだ。そうなんだ。

秋山瑞人作品

2012/01……龍盤七朝 DRAGONBUSTER 02

2005/01……ミナミノミナミノ

1999/07……E.G.コンバット 3rd

待つ、という行為をしてはいけない。

佐藤大輔作品

合掌。

番外 復活

気象精霊

https://www.amazon.co.jp/dp/B00NI96I9W/

ゼロの使い魔

2017-05-09

月姫きのこにハマり、Fateが合わずDDD歓喜してまほよも楽しめた。FGOは逆にきのこ担当分以外ならまっさらに楽しめた。

現状型月で動いてるのがFate周りだけでとてもつらい。

2017-04-02

ヒドイコードを書く奴ら

今の37〜40歳ぐらいのベンチャーやらスタートアップやらWeb系やらの技術者の書くコードやばい気がする。

それが外注とかフリーとかじゃなく、役職付いている人とか役員だったりする。

それをメンテする若手の気持ちといったらもう。

コピペしたり入り組んだ関数メソッド)を書いたりとか・・・

抽象的な考え方とか、参照透過性とか、100%適応できないまでにしても少し考えてかけないものなのだろうか・・・

そういう基礎的な部分の技術をまとめた技術書籍がない(もしくは流行っていない)のも原因なのかな。

Javaオブジェクト指向からRuby on Rails時代に移行していった時代に生きていた人たちなのかな?

色々と今まで学んできたことの基盤が崩れ一気にレガシー化した感覚が強く、新しいものに手を出したがるくせがあるんじゃないかと疑ってしまう。

新しい技術を学んでも、思考を変えずに使っていたら何も変わらねぇっすよ。ゴミを今の技術で再構築するだけ。

GRASPとかSOLID原則とか、できればDDDとか関数型の考えとかを勉強して欲しい・・・

まあ別に上記知らなくても、構造プログラミングとか、クラスとはなんぞやとか、関心事をなるべく分離していくようなコーディングとか・・・。そういうのが欲しいです。

リーダブルコード局所的な事を書いている気がするし、もっと大局的な技術リテラシー・・・

結構やばいやついるので経営陣気をつけろよ、とも思う。

てかコレが技術負債かー。ちょっと直すだけでもすげーめんどくさいし、やる気全然起きないし、なんならリファクタしてから修正していきたい気持ちになる。。。

DB設計もなーーー、なんでNOT NULL制約付けないかなー。もちろんテストなんて書くわけもなく。

創業期なら頑張るって感じだけど、中規模になってきたベンチャーとかだと気合い入れて全て変えてやるぜぐらいのポジションで入らないと環境変わらないだろうし、いわいるプログラマーで入るなら面接時に匂いを察知して回避したほうが良い気がするお。まぁでも入社時のやる気なら変えれるのかな・・・。もはや業務量増えてマンネリ化した状態の今は全くやる気しねぇ。すべての開発を止めて、テストコードを書きながらリファクタしていく、ってならやる。


そしたら今後何か頼まれても工数減るので「いっすよやりますよー」といいやすいし「もっと短く出来ないの?」みたいな地味なストレスがなくなるのでまぁ色々ハッピーなんじゃないのかな。(てめえのゴミ直すのに工数かかるんだよといいたくなる)


的な、ゴミと一緒に働けねぇ、みたいな問題解決するためにマイクロサービスで開発するのありかもね。ゴミレガシーラップして、臭いものに蓋をしてまあ許せるインターフェースだけ公開して使わせて欲しい。(驚くほど上から目線コードレビューですり合わせられるようなレベル感ではなく、でかいゴミ山があり、そいつゴミ山を整理する気がないならそうするのが折衷案なんじゃないでしょうか。

まぁゴミを一つずつ整理していったほうが結果幸せだろうねぇ。


サービスを作ってきたコードから感謝して」って実際その現場になるとあまり思えないものなんだな。サービススケールさせられないこの足元作ったやつまじ、考え直してもらわんと対して売上立たず終わるから期待持たせるだけだぞ、みたいな。これがクソ稼いでいるサービスとかだったら別なんだろうなぁ・・・







うだうだ言わずハッカーならハックしろ!!!!!!!!!!!!!!!!!!ハッカーになろ。

2017-02-11

http://anond.hatelabo.jp/20170211022822

あくま個人的感想みたいなものなのですが、次のものがあると思っています

レビュー体制を作る

 ・ミスは誰にでもあるのでちゃんとレビューする体制を作って責任分散する

  ・個人攻撃は誰も得しないばかりか、批判隠蔽体質を作る

 ・レビュー時にちゃんとテストパターン漏れがないか確認する

  ・逆にいうとレビュアーテストパターン漏れがないことと他に発生し得るテストパターンがないかレビューすればいい

ドキュメント化する

 ・仕様KKD(経験,勘,度胸)で決定していいものではないのでちゃんと仕様としてまとめておく必要があります

  ・特に専門用語はちゃんと共通認識としてできるだけ正確に記述するほうがよい

   ・これをしておかないとどこかで新しい機能を追加すると違うところの整合性が取れなくなる

    ・ドキュメント化していないとそういうことすら気がつかない

リリース毎日する

 ・リリース毎日するというより小さい単位リリースすることが目的

 ・大きい単位修正するとレビュアー負担にもなるしテストも大変

  ・その結果、テスト漏れが発生する

プロとして取り組む

 ・社内勉強会とかしたりして継続的勉強する

  ・ある程度継続しないと人は成長しない

   ・逆に一定期間負荷をかけると最初は大変でも徐々に慣れてくる

今の職場改善案みたいなものなのでモダンな開発スタイルとはまた違うかも。

モダンな開発スタイルアジャイル開発、スクラムDDD勉強してみるといいかもしれません。

2016-10-17

そういやアニメ星のカービィ」でアニメーター地獄の回あったな

デデデ大王という何でも思いつきで行動するとっつぁん坊やが一応国王

ワドルディという兵隊を使って人海戦術デデデ大王を礼賛するプロパガンダアニメを作らせる話なんだが

劇中アニメ製作

チャンネルDDD

DEN-2          ←

デデデプロダクション

で笑った

いや今思えば笑えないんだが

2014-11-10

題名をここに書く

hosted-by.leaseweb.comが127.0.0.1ってことは、

aaa@hosted-by.leaseweb.com

bbb@hosted-by.leaseweb.com

ccc@hosted-by.leaseweb.com

ddd@hosted-by.leaseweb.com

って書いとけばスパマーはひっかかりますか?

2014-10-11

アプリ屋がRailsを初めて触ってみて感じた事

Qiitaに書こうかと思ったけど、言いたいことも言えない、こんな世の中じゃ。

発想が古臭い

モバイルファーストAPIファースト文脈ハイブリッドWebをやってきた目からすると、サーバーサイドでHTMLを生成してページ遷移させるなどという90年代調のクラシカルな発想を基本に据えるフレームワークはとても斬新に思えました。HTMLゴリゴリ生成するなんてよほど特殊最適化をしようとするのでなければそもそも発想として出てこないです。それでいてDSLメタプログラミング等のテクニカル技法宝石のように鏤められている様はまるでエジプト時代骨董品を見るかのような趣がありました。turbolinkなどは、かつて表計算ソフトに出しゃばっていたイルカを思い起こさせる味があります。かつて慣れ親しんできたSPAが星のように遠い存在になりました。

モデルMVC

Web界隈の人々がモデルだとかアクティブレコードだとか"MVC"だとかを非常に具象的に話す様を見るにつけ、お前らどんだけPofEAA読み込んでるんだよと畏怖していた時期が僕にもありましたが、どうやら彼等はRailsクラスディレクトリという特定実装について話していただけだったようです。Modelという概念もこれだけ肥大化してしまったら、オリジナル概念で彼等と会話するのは諦めるべきかなと思いましたし、Railsの"MVC"をアンクォートして語るのはもはや害悪であるとすら感じました。

レールから外れる辛さ

Rails界隈の人がよく「Rails流儀」や「正しい"MVC"」というのを口角泡を飛ばし議論しているのを目にするのですが、おそらく外に広がる不条理で火傷を負って快適なRails世界に引き篭もった結果としての一種のストックホルム症候群なのだなと思いました。いまやAjaxとかWebsocketとかWebRTCとかを組み込もうとする至極真っ当な方法論がとてつもない高難度に見えてきます。設定よりも規約、というのも一つの方向性だと思いますが、ドメインサービスレイヤ名前空間を構築しようとしたりコードジェネレーションしようとしたりしただけで地獄のようなCircular Dependency罰を受けてしまったので、自分がとても間違った事をしているような気がしてしまいました。とはいえConcernsに特別名前役割を与えられても正直しんどいので、皆が皆libゴミを放り込んでいく様子にも納得がいきました。

レイヤ?何それおいしいの

RailsAPIサーバーとして使おうとするとまずビューが無くなってMとCだけになりますが、いわゆる"MVC"の文脈で育ったエンジニアがなぜ息を吸うようにFat ControllerやFat Modelを作ってしまうのかという事が良く分かりました。多くのRailsリファクタ手法と称されているものクラスを書くファイルを分割する事以上のものでは無いように思えたので、Rails使いを大きめなAPIサーバー案件に回すときセットポジションDDD青本を投げつける必要が有るなと思いました。

TDDやれんのか

ビューとコントローラを結合させた場合結合テストはCapybaraとかのBDDマークアップサイドとの干渉を恐れながら強い気持ちでメンテしていくしか無いのかなと思いました。おそらく脳に電極を埋め込んでいるか緑色のランプを見るだけでハイになれる特殊な人にしか生き抜けない闇が垣間見えました。コントローラを薄くしてサービスレイヤを挟めばその辺りもうまくいけそうな気がしましたが、ビューからヘルパーモデルがいくらでも透けて見えてしまうという状況では裏側の完全性に自信を持つ事は難しそうでした。

分業とか出来るんだろうか

ビューがRubyを叩いて永続化レイヤと直接コミュニケーション出来るというのはとても生産性が高いのだろうとは思いましたが、こうして出来たパーシャルやら何やらをデザイナーとどうやって共有するかを考えると頭痛が痛くなりました。おそらく適当に切り出して綺麗な空間をassets以下に構築した上でpublicにRPCのような窓口を備えたゴミを量産していくのかなと思いましたが、もっと綺麗な方法はあるのかもしれません。でもきっとRails案件に関われるデザイナーRubyバリバリ書けるに違いないはずなので、ここが問題になる事は無いのだろうなと思いました。

RESTとかきついです

RESTはとても美しいパラダイムではありますが、そもそもHTTPがさほど美しくないので歪んだ空間には目を背けるか勝手解釈を与える事で人は初めてRESTfulを名乗る事が出来るのだと思いますGETbodyを(公式には)持たないという事について美しい説明を与える事は出来ないでしょう。サーチAPIはどうしますか。ステータスコード足りなくないですか。401エラーはどうしますか。そしてRESTあくまリソース抽象化する美しい概念なので、アクション副作用については貧弱です。動詞が足りないですし、一般動詞に狭義の意味を与えてドキュメントするのは二度手間しか見えません。PUTには冪等性があるべきみたいなこだわりは家の猫にでも説教してればいいと思います。というわけで、REST的な設計拝借することはよしとしても、「○○はRESTでは無い故云々~」みたいな注文はやめて頂きたいものです。

そんなに嫌なら他に行けば

とか言わないで欲しいです。こういう時にセットでPHPをディスって悦に浸るのは知る限りRubiestとPythonistaと中学生だけです。それにこれはあくまサーバーサイド初心者感想なので、想像するにこれ系のFWは多かれ少なかれ似たような不満を抱えるものなのかなと思います。というわけで、おそらくこれから選択肢がある限りはRailsを使い続けると思います

だってRuby楽しいんだもの

2013-12-29

http://anond.hatelabo.jp/20131229014324

ああ、ごめん。ドメイン駆動設計中2病に風に説明しただけで伝わりにくくてごめん。

関数型言語副作用排除は、DDDでも不変オブジェクトという副作用関数排除の根に繋がってたりして面白いよね。

数学面白いけど、どちらかというとコード世界の延長も面白くなるんだぜ。と言いたかっただけだった。

2013-10-05

道具に拘るエンジニアはだいたい無能

http://d.hatena.ne.jp/Yamashiro0217/20131004/1380855545

プログラマー仕事は、

ほぼ、考えることだ。

これは正しい、正しいがゆえに

場合によっては駒の重さが30kgぐらいある。

どんだけ優れた将棋指しでも、30kgの駒を100回とか動かしたら、

疲れて頭回らなくて素人にも負けてしまうかもしれない。

30kgもある駒を動かすのは大変だ。

からプログラマーエディタ工夫したり、

開発環境工夫したり、色々して駒を軽くする。

この下りは、大嘘または大間違いだ



俺の周りにいる国際学会で発表するような研究をしてきた同僚や

Googleなどの世界的な企業活躍する、畏敬する人々は得意な道具こそあれ

道具の差や環境の差で、パフォーマンスが落ちたりしない

なぜならは

プログラマー仕事は、

ほぼ、考えることだ。

ということだから

研究であれば重要仕事殆どは調査や新しいアルゴリズムの考案だし、

システム開発であれば設計こそが最も重要な作業ということになる

手を動かす段階になれば、時間がかかる仕事重要な部分の殆どは終えている。

その段階で慣れた道具か?そうでないかの差で生まれるパフォーマンスの違いは

それほどの割合にはならない。

出来るエンジニアが最も求めるのは、深く考える事が出来る環境と、

周到な議論を繰り返せる優れた同僚に他ならない。

道具などは、プレゼン用や出張用に貸与されるようなノートPCに、そこらのエディタですら困らない訳だ

もちろん慣れた道具があれば越したことはないけど、それが全てでも最重要でも全くない

手を動かす作業が早くなることが能力証明所詮コーダーのドカタさんorただの社二病

プログラマになりたてのお子ちゃまが、無駄テンプレートデザインタンを使いたがって

パフォーマンスの悪い糞コードを乱発するくらいに質が悪く、筋が悪いことだ。

ということで

道具に拘るのは無能の証で、恥ずかしいからやめた方がいいよw

聞く人が聞けば、鼻で笑われて、相手にされなくなる



雨の土曜だし、人気のブコメコメント返しとくか

id:ybc

煽りタイトル。中身は釣り。もう見飽きました。

認めたら自分が壊れる意見は、釣りにしといた方が心は楽だな

id:zz_sexy

はあ。メモ帳コマンドプロンプトだけでIDEと変わらない効率コード書けるってことですか。そりゃ優秀ですなあ。/酷い道具でも効率落ちないのって無能過ぎて既に効率が0だからじゃないの?

DDD位は使わせてくれるんなら余裕でできるよ。

プリントデバッグのみじゃ、さすがに苦しいけど、その環境に叩きこまれたらバグ出さないように手を動かす前に考えぬけばいいだけ

そもそもエラーハンドリングなんて、IDEなくたって幾らでもやりようあるだろ

id:grandao

おめえエクセルテキストボックスひとつ動かすのに5秒掛かるPCガントチャート作ったことあんのか?地獄だぞありゃ。/経営者の皆様、社員の方々にはちゃんとした道具を使わせてあげてください。

その段階になったら、普通プログラム書いてエクセルコントロールしてチャートを作るんだよ

エクセルGUI使って直接作ることにこだわってんだ?だからエクセルサクサク動く環境でなければダメなんて奴は無能なんだよ

id:hedachi

パンチカードWebサービス作ったらその主張を認めてやるよ

id:tnishimu

バカな人がエンジニアに低スペックPC海賊版ソフト仕事させるのを正当化するのによく使う言葉

合理的な極論に飛びつく奴は例外なく低能

id:hylom

ところが実装してみなければ分からない問題ってのが往々にしてあるわけで、だからPDCAサイクルといった手法があるのよね。設計だけしかやってないSE(笑)だったら道具にこだわらなくてもいいのかもしれないけど。

設計が簡単だと思ってる時点で笑える。しかも実装しなければ、分からないって、手を動かす前に考えろよ無能

id:STAR_ZERO

考えた結果、効率上げるために環境を工夫するんだよ。

からそう言ってるだろ、あればあるに越したことはない、慣れた道具で作業すれば楽にはなる、が大事なことじゃない

id:m-matsuoka

良い道具を知らない人ほど、道具に拘るなと主張する。

色んな道具を知ってるから拘らないんだよ

もちろん、慣れた道具で作業できればそれに越したことはない


ま、こんなところか。

2011-12-29

はてダHTMLコメントスーパーpre記法の中に含まれていると出力される結果がおかしくなるバグ

発見した。

バグるテキストhttp://pastebin.com/1NkAdVVhに置いた。

これを詳細編集モードで貼り付けて更新すると見出しdddしか現れない。

誰か暇な人検証よろしく。

2011-10-19

docomoがIMEIを送出!のどこが問題なのか。素人が考えてみた。

今年冬以降に発売されるandroid端末のメディアプレイヤーHTTPヘッダのUser-Agentおよび拡張ヘッダにIMEI番号が含まれるということが分かり、騒動となった。

カレログapplogに続きNTTドコモが参戦? - http://togetter.com/li/202490

NTT docomo IMEI垂れ流し問題 http://togetter.com/li/202536

まず、

(1)IMEI送出のソース

音楽動画 | サービス機能 | NTTドコモ

http://www.nttdocomo.co.jp/service/developer/smart_phone/service_lineup/music_movie/index.html

魚拓http://megalodon.jp/2011-1019-1834-56/www.nttdocomo.co.jp/service/developer/smart_phone/service_lineup/music_movie/index.html

こう記述されている。

Android端末の一部機種では、音楽動画コンテンツ再生するためのメディアプレイヤードコモプリインストールします。

メディアプレイヤープリインストールされる機種は2011年度下期モデル以降の主なAndroid端末となります

ユーザエージェント

メディアプレイヤーHTTP通信を行う際のUser-Agentヘッダは以下となります

User-Agent:<SPDOCOMO/2.0SP>[AAA](MP;[BBB];Android;[CCC];[DDD]);imei:[xxxxxxxxxxxxxxx];networkoperator:[yyyzz]<CR><LF

SP>:半角スペース

CR><LF>:改行コード

[]以外は固定値

AAA:機種名

BBB:メディアプレイヤーバージョン

CCCOSバージョン

DDDビルド番号

xxxxxxxxxxxxxxx[15桁]:IMEI

yyy[3桁]:Mobile Country Code

zz[2桁]:Mobile Network Code

HTTP通信時の拡張ヘッダ付加情報

メディアプレイヤーHTTP通信を行う際は、以下の拡張ヘッダが付与されます

x-dcmstore-imei:<SP>xxxxxxxxxxxxxxx<CR><LF

SP>:半角スペース

CR><LF>:改行コード

xxxxxxxxxxxxxxx[15桁]:IMEI


(2)IMEI番号とは何か

ケータイ用語の基礎知識http://k-tai.impress.co.jp/cda/article/keyword/43518.html

によれば、

携帯電話データ通信カードが1台ずつ持っている識別番号です。原則として、各端末は1台1台、異なる番号になっています

IMEIは、1台ずつに違う番号が割り振られていて、USIMカードなどを差し替えても変わることはありません。

とある

端末に固有の番号であるという認識でよいだろう。パソコンで言うMACアドレスのようなものだ。

問題の切り分け

重複する部分もあるが、いくつかの問題が含まれているように思える。

twitterで見られた反応をいくつか整理してみた。

(a)共通・不変のIDであること

これが最も大きい問題。twitterでIMEIってつぶやいている人の大半はこれを問題視している。

IMEI番号をそのまま送信している。

すなわち、コンテンツプロバイダ(CP)Aにも、CP-Bにも同じ番号が送信されている。

まり空間的に広く使われている。

CP-AとCP-BでIMEI番号を突き合せて、収集した情報リンクさせることができてしまう。

IMEI番号は端末に紐付けられている。

したがって端末を買い替えない限り番号は変わらない。

まり長い時間、追跡され続けるということだ。

時間軸にも空間軸にも広く共通のIDが使われ続ける。

これが問題。

(a-1)過去の事例

10年以上前から同じことが繰り返されているため、空間時間的に広い共通IDを使うことの「何が問題か」知りたいなら過去の事例を参照するとよい。

高木浩光氏による行動トラッキング歴史と境界線についての備忘録 http://togetter.com/li/197732

インターネットにおけるIDトレーサビリティ(2003年)高木浩光http://www.nic.ad.jp/ja/materials/iw/2003/main/ipmeeting/panel-takagi.pdf

Tracking Cookie - Symantec http://www.symantec.com/ja/jp/security_response/writeup.jsp?docid=2006-080217-3524-99

(b)機種変更中古端末での不安

まだIMEI送信機能付きandroid端末が発売されていないので、どういう実装か不明のため、これは想像上の懸念だ。

このIMEI番号送信機能は、おそらくDRMに利用することが目的の一つだろう。

特定の機種でのみ購入した音楽再生できる機能が組み込まれている可能性がある。

その場合機種変更をするとIMEI番号が変わるために購入したコンテンツを利用できなくなる可能性がある。

あるいは一人で二台以上の端末を所有する場合、どちらか一方の端末でしかコンテンツが利用できない可能性もある。

実際、iPhoneにおいて似た事例が発生していた。認証にUDID(端末固有ID)を用いていたアプリ機種変更ののちに使えなくなる事例があった。

また、それまで利用していた端末をオークション等で販売する可能性もある。

その場合、端末の新しい所有者が、古い所有者の購入したコンテンツを利用できてしまう可能性もある。

ただしこれらは想像上の懸念だ。

(c)セキュリティの問題

UserAgentまたは拡張ヘッダに記述されたIMEI番号をもとに認証を行うサイトの出現が懸念される。

参考:かんたんログインの事例 http://www.atmarkit.co.jp/fsecurity/rensai/keitaiweb02/keitaiweb01.html

そもそもIMEI番号の取得はかんたんだ。

友達不用意にその辺に放置した端末で「*#06#」と入力すれば取得できてしまう。

あるいは、動画の置いた罠サイトを用意して

ユーザアクセスさせればドコモがIMEIを送信してくれる。

送信される番号はどのサイトに対しても共通なので、他のサイトで使うことができる。

そしてなりすましも容易だ。

とか。

ここまでしてIMEI番号を送りたいドコモの狙い

恐らくDRMへの活用と広告への活用ではないか

ぼくよくわかんない><

終わりに。

プライバシに関する専門家でもないので、補足訂正おねがいしまーす。

あと、この問題を扱うに当たって、何を「個人情報」だとするのかとらえ方が人によって違うことに注意したほうが余計な労力を使わなくて済む、と思いまーす。

# 増田でははてな記法が一部使えますって。

# 非対応大杉イライラするわ♡

# 来年インターンシップ生で改善してよ。

2009-11-28

7日間有効の20%割引チケットプレゼント!◆ $usernameさま、お誕生日おめでとうございます!

from ZOZORESORT <contact_member@zozo.jp&gt;

to

date DDD, MMM DD, YYYY at HH:MM

subject ◆7日間有効の20%割引チケットプレゼント!◆ $usernameさま、お誕生日おめでとうございます!

mailed-by zozo.jp

ZOZORESORT 送料無料宣言

HAPPY BIRTHDAY! 20%OFF TICKET PRESENT

$usernameさま、お誕生日おめでとうございます!

いつもZOZORESORTをご利用いただき、ありがとうございます。

誕生日のお祝いに、ZOZOTOWNでのお買い物にご利用いただける

20%割引チケットプレゼントさせていただきました。

20%割引チケットは、本日のお誕生日よりYYYY年MMDD日までの7日間、

ZOZOTOWNでのお買い物の際、ご利用いただくことができます。

$usernameさまにとって、素晴らしい1年になりますように。

これからもZOZORESORTをよろしくお願い致します。

HAPPY BIRTHDAY! 20%OFF TICKET PRESENT

HAPPY BIRTHDAY! 20%OFF TICKET PRESENT

(※1)チケットは、本日よりYYYY年MMDD日23時59分までの期間内に確定したご注文にてご利用いただけます。(カート内にてチケットのご利用可否を選択することができます。)

(※2)チケットのご利用は、ご購入のアイテム数に関わらず、1回のご注文に限らせて頂きます。

(※3)有効期限を過ぎた時点で、付与されたチケット自動的に失効いたします。あらかじめご了承ください。

(※4)予約商品・スペシャルプライスアイテムは、チケットご利用対象外となります。

HAPPY BIRTHDAY! 20%OFF TICKET PRESENT

Attention

● このメールは、YYYY年MMDD日にお誕生日を迎えられたお客様に配信しています。

● お問い合わせはこちらから(ZOZORESORTカスタマーサポートセンター

● このメールHTML形式で配信されています。画像が表示されない場合がございますので、オンラインでの購読を推奨いたします。

Microsoft Outlook2003をお使いの皆様

Outlook 2003は、初期設定のままですとHTML形式のメール画像が表示されません。ZOZORESORTからのHTML形式メールを今後も継続してご購読される場合はOutlook 2003の差出人セーフリストに contact_member@zozo.jp を追加して下さい。なお、送信者を[差出人セーフリスト]に追加する方法につきましては 「Microsoft Outlook 2003」のヘルプ説明書をご参照ください。

● このメールは、送信専用メールアドレスから配信されています。ご返信いただいてもお答えできませんので、ご了承ください。

個人情報の取扱いについては個人情報保護方針をご覧ください。

ZOZORESORT AND STARTTODAY CO.,LTD. ALL RIGHTS RESERVED.

こんなメールがきた。気持ちが悪い。

2009-09-25

化物語が面白くて、刀語が詰まらない理由

パクったもとの違い

化物語奈須きのこDDD

刀語 :川原正敏修羅の門

比較にならんわな

2009-07-30

アンタッチャブル山崎は全盛期のビートたけしより凄い

ロンドンハーツ実名アンケート! 芸人リアル好感度調査」(前編,後編)は今が旬のブラマヨアンタッチャブル山崎が活躍して非常に面白かった。

番組はこんな感じ : http://www.tv-asahi.co.jp/londonhearts/contents/backnumber/cur/img/1_6.JPG

カメラは「アメトーーク」と同じロケ手法を使ったのかな。同じテレ朝の加持さんの番組だし。芸人のガヤや動きが漏らさず収められているので、実力のある芸人にとっては非常にやりやすそうだった。

その中で山崎がすっごいカブせまくってて、、すごい、もの凄い才能があるわ。。

まず他の芸人カブせ方はいろいろあって、こんな感じ。

ダウンタウン明石家、紳助

AAA → BBB、って何でやねん」 (やや乗り突っ込み気味)


とんねるず

AAAでBBB、みたいな」 (バブル期は「AAA、みたいな」のみ)


ホリケン

(前に出てくる)「AAA! → BBB! → CCC! → DDD! → EEE!」(後ろに下がる)


ビートたけしの全盛期

AAA → BBB → CCCだ、この野郎!(肩をヒクっとさせる) / DDD → EEE → FFFだって、ふざけんな馬鹿!(肩をヒクっとさせる)/


■今回のロンハーでのアンタッチャブル山崎

AAA(笑) → BBB(爆笑) → CCC(淳振る) → DDD(笑) → EEE(DDDの被せ) → FFF(笑) → GGGHHH(爆笑) → III → JJJ(笑) → KKK(笑) → LLL(笑) → MMM(LLLの被せ)(笑)


司会のロンブー淳がCCC以降に絡みながら話を引き出していく感じ。(笑)は笑いが起こり、(爆笑)は爆笑が起こった地点。

全盛期のたけしでも/の部分で一息入れてる感じがあるんだけど、山崎は「ブルドーザーみたいに持ってっちゃう(by東野)」。

東野は「さんまさんタイプ」って言ってたけど、こういうカブせ方を見てるとたけしタイプなのかも。

最近までのレッドカーペット的な短いお笑い流行りが終わった今このタイミング山崎に注目が集まってるってのが非常に面白い。そんでそれを迷わずすくい上げて放送してくれる番組もありがたい。

2008-04-05

さくらの旧(?)専用サーバ CentOS 4.6 を Linux-Vserver に入れ替えてみた

前置き

契約直後の初期状態

  • ホスト名をsakura2とした。
  • sshd と vsftpd が動いていた。
[admin@sakura2 ~]$ uname -a
Linux sakura2 2.6.9-67.0.4.ELsmp #1 SMP Sun Feb 3 07:08:57 EST 2008 i686 athlon i386 GNU/Linux
[admin@sakura2 ~]$ cat /etc/issue.net
CentOS release 4.6 (Final)
Kernel \r on an \m

CentOS 5 へのアップグレード(?)

中身の整理
  • なるべく最小構成に近い最新のCentOS4.6にしてみる。
# yum update
# yum remove emacs emacspeak emacs-leim emacs-common
# yum remove NetworkManager
# yum remove bluez-bluefw bluez-hcidump bluez-libs bluez-utils
# yum remove cups cups-libs
# yum remove irda-utils isdn4k-utils pcmcia-cs wireless-tools
# yum remove wpa_supplicant gpm xinetd
# yum remove ppp nfs-utils lksctp-tools autofs
# yum remove xorg-x11-libs
# yum remove selinux-policy-targeted
# yum remove vsftpd
# yum clean all
CentOS 5 にアップグレード(?)してみる
[admin@sakura2 ~]$ uname -a
Linux sakura2 2.6.9-67.0.7.ELsmp #1 SMP Sat Mar 15 06:54:55 EDT 2008 i686 athlon i386 GNU/Linux
# rpm -Uvh http://mirror.centos.org/centos/5/os/i386/CentOS/centos-release-notes-5.1.0-2.i386.rpm \
http://mirror.centos.org/centos/5/os/i386/CentOS/centos-release-5-1.0.el5.centos.1.i386.rpm
[root@sakura2 admin]# cat /etc/issue.net
CentOS release 5 (Final)
Kernel \r on an \m

# yum update glib procps udev iptables
# rpm -Uvh --nodeps http://mirror.centos.org/centos/5/os/i386/CentOS/initscripts-8.45.17.EL-1.el5.centos.1.i386.rpm \
http://mirror.centos.org/centos/5/os/i386/CentOS/mkinitrd-5.1.19.6-19.i386.rpm
# rpm -Uvh http://mirror.centos.org/centos/5/os/i386/CentOS/e2fsprogs-1.39-10.el5.i386.rpm \
http://mirror.centos.org/centos/5/os/i386/CentOS/e2fsprogs-libs-1.39-10.el5.i386.rpm \
http://mirror.centos.org/centos/5/os/i386/CentOS/e2fsprogs-devel-1.39-10.el5.i386.rpm
# rpm -Uvh http://mirror.centos.org/centos/5/os/i386/CentOS/kernel-2.6.18-53.el5.i686.rpm
[admin@sakura2 ~]$ uname -a
Linux sakura2 2.6.18-53.el5 #1 SMP Mon Nov 12 02:22:48 EST 2007 i686 athlon i386 GNU/Linux

# yum clean all

[root@sakura2 admin]# yum --version
Loading "fastestmirror" plugin
2.4.3
[root@sakura2 admin]# rpm --version
RPM version 4.3.3
# yum update
Error: Missing Dependency: python-abi = 2.3 is needed by package python-elementtree
  • 解決法がわからないので放置

Linux-Vserver の導入

# vi /etc/ssh/sshd_config
# /etc/init.d/sshd restart
# vi /etc/yum.repos.d/dhozac-vserver.repo
# yum update kernel
# yum install util-vserver{,-core,-lib,-sysv,-build}
[admin@sakura2 ~]$ uname -a
Linux sakura2 2.6.22.19-vs2.3.0.34.1 #1 SMP Mon Mar 17 05:32:04 EDT 2008 i686 athlon i386 GNU/Linux

ホストOS環境の整備

# yum update bash screen rsync
# /etc/init.d/iptables save
# /etc/init.d/iptables start
# cat /etc/sysconfig/iptables

# Generated by iptables-save v1.3.5 on Mon Mar 24 19:57:07 2008
**filter
:INPUT ACCEPT [1067:96557]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [63680:6208436]
:e0 - [0:0]
-A INPUT -i eth0 -j e0
-A e0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A e0 -p icmp -m icmp --icmp-type 8 -j ACCEPT
-A e0 -p tcp -m tcp --dport 22 -j ACCEPT
-A e0 -p tcp -m tcp --dport 80 -j ACCEPT
-A e0 -p tcp -m tcp --dport 443 -j ACCEPT
-A e0 -j REJECT --reject-with icmp-port-unreachable
COMMIT
# Completed on Mon Mar 24 19:57:07 2008
# Generated by iptables-save v1.3.5 on Mon Mar 24 19:57:07 2008
**nat
:PREROUTING ACCEPT [179:10642]
:POSTROUTING ACCEPT [213:13895]
:OUTPUT ACCEPT [251:16220]
-A PREROUTING -s ! 10.0.0.0/255.255.255.0 -p tcp -m tcp --dport NNNN -j DNAT --to-destination 10.0.0.1:22
-A POSTROUTING -s 10.0.0.0/255.255.255.0 -d ! 10.0.0.0/255.255.255.0 -j SNAT --to-source AAA.BBB.CCC.DDD
COMMIT
# Completed on Mon Mar 24 19:57:07 2008

ゲストOSの設定

[root@sakura2 ~]# /usr/sbin/vserver-stat
CTX   PROC    VSZ    RSS  userTIME   sysTIME    UPTIME NAME
40013   27 301.5M  73.8M  30m36s31  56m38s90  11d06h56 one
40015    5  10.8M   4.4M   0m28s33   0m12s68   9d04h23 two
40016    2   4.2M   1.3M   0m00s47   0m00s64   0m01s97 three

追記

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-01-14

奈須きのこDDD感想

アリストテレスが固定した自然界の万物を構成する4つの基本要素、「地、水、火、風」の4大エレメントの思想は、やがて錬金術を生み出した。

地が暖まると火となり、地が湿ると水となる。寒く乾いたものが地。暖かさと湿り気を兼ね備えたものが風。

全ての物質はこの4大エレメントから構成されて、その比率を人為的に変更することで、卑金属を貴金属に変えようというのが錬金術の基本思想

物質世界は正方形をしていて、その4つの頂点にはそれぞれのエレメントが立っている。

正方形世界で重要な物質は、すべて「エッジ」に集まっている。3つ以上のエレメントが混じってしまったり、対立しないエレメントが混じった夾雑物は、全部「面」の上。

世界のほとんどは夾雑物からできていて、錬金術師はそこから純粋な物質だけを取り出して「エッジ」を目指し、そこから「頂点」にいたる道筋を追い求める。

幻想文学というのは、錬金術師が書いた文学、あるいは純粋な物質だけで作った物語だ。

幻想文学世界観の中では、世界を構成する基本要素は「鉱物人形、屍体、少女」。

少女少年とは交換可能なもの。この世界では、「大人」は夾雑物として取り除かれるか、あるいは矮小な「畸形」としてのみ存在を許される。

奈須きのこの新作「DDD」は、幻想文学世界観を正しく受け継いだ物語

狂言回しは畸形。主人公少年人形ハイブリッド。他にも鉱物属性を持った公安官とか、人形属性を持った敵役とか。

通常世界での「大人」は、物語世界では存在意義を持たないか、あるいは意義を持った存在に処理されるためだけに存在を許される。過剰な装飾とグロテスクさに満ちた病院や、主人公の住む部屋の描写。とても正しい幻想文学世界。

できればもっと追求してほしかったのが、登場人物の無目的性。

幻想文学世界の登場人物は、歪みがあるゆえに美しさが際立つ。

純粋な物質だけでできた登場人物が、その能力の全てを挙げて混沌に打ち込む、賛成と反対とを同時に望み、重力によって下降すると同時に飛翔したいと望んで叫び続けるようなバロック的な精神の世界が、幻想物語を駆動する原動力になる。

あれだけきれいな世界観物語をはじめてくれたんだから、やっぱり全ての登場人物にはどこか狂っていてほしいな、というのが読者としての願い。物語の筋なんてどうでもいいから、もっと狂ってほしい。美しい被造物には、理性なんて似つかわしくない。

以前見にいった「恋月姫人形」の展覧会には、純化するのに失敗した生き物多数。

ああいう人形趣味というのは、生活に疲れた50代後半の男性、「屍体属性を持った畸形」のためのもの。

魂吸われそうなぐらいにきれいなビスクドールと、すでに吸われてしまったのか、何時間人形みたいに固まってしまった観客の人、何人か。この世界に存在を許されるのは、ここまで。

会場の中には、ゴスロリの格好をした成人女性がいっぱい。

なんだか黒くてギラギラした服を着て、横方向にやたらと成長が進んだ、煮豚に韓国ノリを巻きつけたようなその生き物の群れは、4つのエレメントの混和をどう失敗したらこんなになるんだか。あれでみんなの目が虚ろだったりしたら、モンスターとしての存在意義もありそうなんだけれど。

 
アーカイブ ヘルプ
ログイン ユーザー登録
ようこそ ゲスト さん