メイン



マッシュアップ! アーカイブ

2007年09月15日

RSSティッカー + yahoo pipes + 変な検索 =

yahoopipes.JPG

ナビゲーションバーのRSSティッカーでなんか面白いこと出来ないかなーなんて思ってたのですが、リンクをRSSティッカーつきで貼るとか、ニュースを貼るとかそのくらいのことしか思いつきませんでした。

で、RSSについていろいろ探してるうちにyahoo pipesってのが面白いらしいってことがわかったんです。
4月頃にたくさんレビューされているようでしたので、そのころに人気が出たものみたいですね。

使い方なんかはレビューされてるサイトにお任せしますが、簡単に説明すると、いろんなRSSを好きなように組み合わせたり、並び替えたり、切ったり貼ったりとっかえたりと、なんでもしてください!っていうサービスみたいです。
しかもグリグリとマウスでいじって作れるってんだからすごいですね!
そういうわけで、使ってみたんです。

RSSティッカーで公開する以上、ニュースサイトとかまとめてもそんなに面白くなさそうだし、個人的なお気に入りをまとめても、個人的に使え!公開するな!てことになりそうだしで、思いついたのが検索結果をRSSティッカーにしたら面白いかもと思いやってみました。

↓こんな感じに仕上がったよ!
-------------------------

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

ホントはグーグルで検索したかったんですけど、ちょっとうまく行かなかったのでヤフーで検索してみました。

  • "どうもおかしい"
    • なんか妙なことがあった人を探してみたよ
  • "言われた" "言い返した"
    • 我慢できなかった人を探してみたよ
  • "やっぱり" "orz"
    • 期待はしてなかったけど。。。って言う人を探してみたよ
  • "けど許す"
    • 心の広い人を探してみたよ
  • "どうしても" "ない"
    • がんばっても報われない人を探してみたよ

もっと面白そうな検索ワードで作ればひとりで遊べるね! http://pipes.yahoo.com/pipes/pipe.run?_id=Pu33popj3BG84OvnAcqenA&_render=rss

★このコンテンツに目的の情報はありませんでしたか?


[ スポンサードリンク ]

2007年09月23日

みんなのフィードビューアをつくってみました。

mojaloglabs.JPG

みんなのフィードビューアを作ってみました。

そのサービスの性質上、宣伝なんかにも使われそうですが、基本的にはyahoo pipesなどを使って面白いRSSを編集し、投稿していただけることを期待したサービスです。

使い方は、特に難しいことはありません。紹介したいRSSを用意して、タイトル・名前・コメントを入力して投稿するだけです。

※このRSSビューアはGoogle Ajax Feed APIを利用しています。まれにRSSフィードが正しく認識できないことがありますので、ご用意いただいたRSSフィードにFeed Burneryahoo pipesなどを噛ませてから投稿してみてください。

ちなみにちょっと昔の記事ですが、GIGAZINEさんのとこで紹介していました、Search & RSSなんかも絡ませてみると面白いことになるかもしれませんね。
現在のところイメージファイルは表示できないので、コレが何とかなればいいかなーなんて思います。

★このコンテンツに目的の情報はありませんでしたか?


2007年09月25日

リファラをスライスするウェブサービスとajaxでサイト内再検索ウェブパーツ

refslice.JPG

本エントリについての訂正記事


なんだかわかりにくいエントリタイトルをつけてみましたが、google ajax search apiと検索で飛んできたリファラを使って、非同期でサイト内の再検索結果を得られるブログパーツを実装してみました。
(ソフトウェアなどの)技術系の検索を行う場合って、以下のように検索ワードが複数に渡ることがよくあるのですが、このようなユーザ検索を補助してあげるパーツです。例えばgoogleで、mojalog ajax feed apiという検索で飛んできた場合、これをリストにして

  • mojalog
  • ajax
  • feed
  • api

を非同期に再検索行えるようにしました。ためしにこんなワードで飛んできていただければ判ると思います。
エントリ毎に★このコンテンツに目的の情報はありませんでしたか?というリンクを用意しましたので、クリックしてみてください。ページ下部のリファラ再検索パーツに遷移します。
これは、検索して飛んできたページにあまり有用な内容がなかった場合、googleに戻って再検索するより手っ取り早く再検索が出来るようにしたかったためのものです。
ただ、mojalogはまだコンテンツが溢れているわけではありませんので、その効果はもしかしたら乏しいものかもしれません。
やっつけで作ったものなので、あまりオプショナルな機能(例えばサイト外検索したいなど)はありませんが、近いうちにスクリプトとその実装内容を報告したいと思います。たいしたことやってないけど。

あとリファラをスライスしてxmlで返すサービスはこんな感じです。
'http://mojalog.com/mojascript/gsajax/refrxml.php?refr=' + escape( top.document.referrer )
スクリプトでリファラ拾ってエスケープして上記のphpにrefr=で投げます。そしたらスライスして返してきます。実際にはこんな感じ。また今度解説します。

続きを読む "リファラをスライスするウェブサービスとajaxでサイト内再検索ウェブパーツ" »

★このコンテンツに目的の情報はありませんでしたか?


2007年10月06日

mojaラボ 気になるキーワード検索を作ったよ!

mojaSearch.png

mojaラボ気になるキーワード検索はコチラ
ブログエントリなど、文章を入力すると単語の利用頻度でフォントの大きさを変えて、話題のキーワードだと、フォントカラーを変えてタグクラウド見たいなものを表示します。
そっから、気になるキーワードを選ぶと、グーグルと兆しで検索がかかります。たまに兆しのほうはヒットしないことがあるようです。
ぐりぐりいじっていただければどういうものなのかわかるかと思います。

<どんなときに使うの?>

どんなときに使っていただいてもいいのですが、ワタシはエントリを書いてみて、他のブロガーが関連エントリを書いているかどうか見たり、トラックバックを送ったりするときに使おうかなと思っています。
あと、スパムトラックバックはやめましょう。ホント消すのめんどくさいアレ。

続きを読む "mojaラボ 気になるキーワード検索を作ったよ!" »

★このコンテンツに目的の情報はありませんでしたか?


2007年10月09日

mojaラボ気になるキーワード検索の解説

out.PNG

mojaラボ気になるキーワード検索について簡単に技術解説します。flickrぽいロゴは別に意味はありません。ココで作れますよ。
と言うわけで、たいしたことはやってないんですけど、いくつかのウェブapiを使っていますので、その紹介も含めて解説したいと思います。
すみません。今、firefoxだとちゃんと動かない模様です。えとinnerHTMLがfirefoxだとダメみたいですね。てことはこのページ下部のサイト内検索もうまく見れていないんですね。orz

<やりたかったこと>

・文章から特徴的な単語を抜き出して、主要な検索エンジンやウェブサービスの結果を返す。
→ここでいう文章は、主にブログエントリなんかを期待してます。
※ちなみに入力されたエントリをログにとったりはしてません。

<実現できていないこと>

・tf-idfという、まさに文章の特徴的な単語の抽出が出来ていません。
→代わりにkizAPIの、きざしランキングウェブサービスを利用しました。

それでは、どういう仕組みになっているのがを簡単に説明します。

  1. "きざしランキングウェブサービス"から、今日話題になったキーワードとその間連語300語くらいを取得する
  2. yahoo developer networkの"形態素解析ウェブサービス"を使って入力した文章を解析する。
  3. 解析結果の単語が1.のランキングに載っていれば、出力したタグクラウドのフォントカラーをいじる。
  4. 出力したタグクラウドのワードをオンクリックで"google search ajax api"による検索とkizAPIの"KWIC(KeyWord In Context)検索"を行う。

<ajaxとクロスドメインの問題>

まず、各々のウェブサービスの呼び出しについてです。
ajaxで使うXMLHttpRequestというインスタンスは、今そのページが表示されているドメインから、それ以外のドメインに対してリクエストを送ることが出来ませんので、(IEだと「書き込みできません」というスクリプトエラーになります)自分のドメインでプロキシっぽい動きをするサービスを用意してみました。
つまり、

javascript 自分のプロキシウェブサービス yahoo とか kizAPI ウェブサービス
javascript 自分のプロキシウェブサービス

のようにして呼び出して戻りのxmlを得ている訳です。

このやり取りを抜粋すると、こういうことになります。

・プロキシ側

    # $sendURLはkizAPIなどのウェブサービス呼び出し先。$_GETなどを判定して適宜変更する。
    $req =& new HTTP_Request( $sendURL );
    $req->setMethod( HTTP_REQUEST_METHOD_GET );
    if ( !PEAR::isError( $req->sendRequest() ) ) {
        # ヘッダーをContent-Type: text/xmlで返してあげないとスクリプトが理解してくれない
        header('Content-Type: text/xml');
        echo $req->getResponseBody();
    }

・呼び出し側

    // crtXmlHttpReqはファクトリみたいなもの。XMLHttpRequestインスタンスを返します。
    httpInst = crtXmlHttpReq( getWordRankData );
    if ( httpInst ){
        httpInst.open( "GET", "proxy.php?qry=1", false );
        httpInst.send( "" );
    }

・XMLを取得する

function getWordRankData()
{
    if ( ( httpInst.readyState == 4 ) && ( httpInst.status == 200 ) ){
        var xmlData = httpInst.responseXML;

スクリプトがproxy.phpにパラメータを渡して、proxy.phpがそのパラメータによって呼び出し先を適宜変更してあげる感じです。
ちなみにこういうプロキシサービスを作らなくてもJSONP(JSON with padding)という方法をとれば、プロキシなくても出来るようです。ふへー。throw lifeさんに判りやすい説明がありました。

<Prototype.jsの利用>

きざしランキングウェブサービスで返ってくるxmlのCDataのリンクタグを削除したいのでstripTags()を使いました。つまりローカルでデータを整形しています。コレはサーバでやらんとだめですね。。。
via:http://www.prototypejs.org/

<形態素解析>

文章に上限がないとなかなか結果が返ってこないので、送信できる文章は2048文字以内の制限を掛けています。 yahooの形態素解析は1度に送れるバイト数の上限を定めていますので、ブラウザからもらったテキストは適宜分割して形態素解析を行っているわけです。
※本当は解析を行った結果をさらに、tf-idfで重要語句を判定したかった。

・wordanalysis.phpの抜粋

    # $curMsgListは入力した文章を適当なデリミタでarrayにしています。
    # -----------------------------------------------------------------------------
    # センテンス毎に形態素解析
    # -----------------------------------------------------------------------------
    require_once( 'HTTP/Request.php' );
    $req =& new HTTP_Request( "http://api.jlp.yahoo.co.jp/MAService/V1/parse" );
    $arySize = count( $curMsgList );
    for( $idx = 0; $idx < $arySize; $idx++ ){
        $req->setMethod( HTTP_REQUEST_METHOD_POST );
        $req->addPostData( "appid", "**********" );
        $req->addPostData( "results", "uniq" );
        $req->addPostData( "response", "surface" );
        $req->addPostData( "filter", "1|2|9" );
        $req->addPostData( "sentence", $curMsgList[ $idx ] );

        if ( !PEAR::isError( $req->sendRequest() ) ) {
            $res = $req->getResponseBody();
            $xml_parser = new xml();
            $xml_parser->parse( $res );
            # ループ中のxmlパーサなのでunsetしておく。
            unset( $xml_parser );
        }
        $req->setURL( "http://api.jlp.yahoo.co.jp/MAService/V1/parse" );
        $req->clearPostData();
    }

<XMLパーサ>

xmlパーサは以下のようなクラスを作ってパースしています。"SURFACE"タグに目的のワードが入っているわけですね。
ちなみにこのクラスのインスタンスはパースが終了するごとに、unset( $xml_parser );のように開放しないとうまく動きませんでした。1つのインスタンスで複数のxmlをパース出来ないんでしょうね。多分。
尚、$retAryは返却するxmlの実データになるのですが、C++のSTLで言うpairのfirstに解析後の単語、secondにその嵩みが入っています。

・wordanalysis.phpの抜粋(xmlパーサクラス)

    class xml {
        var $parser;
        function xml(){
            $this->parser = xml_parser_create( 'UTF-8' );
            xml_set_object( $this->parser, $this );
            xml_set_element_handler( $this->parser, "TagOpen", "TagClose" );
            xml_set_character_data_handler( $this->parser, "CData" );
        }
        
        function parse( $data ){
            xml_parse( $this->parser, $data );
        }

        function TagOpen( $parser, $tag, $attributes ){
            global $refPrs;
            if( $tag == "SURFACE" ){
                $refPrs = $tag;
            }
        }

        function CData( $parser, $cdata ){
            global $refPrs;
            global $retAry;
            if( $refPrs == "SURFACE" ){
                if( isset( $retAry[ $cdata ] ) ){
                    $retAry[ $cdata ] += 1;
                }
                else {
                    $retAry[ $cdata ] = 1;
                }
            }
        }

        function TagClose( $parser, $tag ){
            global $refPrs;
            $refPrs = "";
        }
    }

<タグクラウドの生成>

後は、スクリプトのgetWordRankDataメソッドで取得したワードランク(refAryに格納されています)と解析後のワードをつき合わせてタグクラウドを生成しています。
wordanalysis.phpが返すXMLはcountタグにワードの嵩みが入っていて、コレがフォントサイズになります。itemタグには実際のワードが入っています。

function getElement()
{
    if ( ( txHttpInst.readyState == 4 ) && ( txHttpInst.status == 200 ) ){
        var xmlData = txHttpInst.responseXML;
        var elemListTags = xmlData.getElementsByTagName( "elem" );
        var countListTags = xmlData.getElementsByTagName( "count" );
        var itemListTags = xmlData.getElementsByTagName( "item" );
        var elemSize = elemListTags.length;

        resultText = "";
        for( i = 0; i < elemSize; i++ ){
            var wordCnt  = countListTags[i].childNodes[0].nodeValue;
            var itemName = itemListTags[i].childNodes[0].nodeValue;
            var color = "";
            
            // refAryの中身は↓な感じで格納されています
            // "オカザイル イケ EXILE 最高 岡村 岡村さん 見た 岡村隆史 面白かった のめちゃ ダンス" ... 
            // indexOfでヒットすればフォントカラーをいじります。
            for( ref = 0; ref < refAry.length; ref++ ){
                if( refAry[ ref ].indexOf( itemName ) > 0 ){
                    color = "style=\"color:#f93\"";
                    break;
                }
            }
            resultText = resultText + " <a href=\"#\" onclick=\"getReferWord( '" + itemName +  "' );OnLoad( '" + itemName + "' );\"><font size=\"" + ( wordCnt * 2 ) + "\" " + color + ">" + itemName + "</font></a> ";
        }
        $("result").innerHTML = resultText + "<ul><li>よく使われるキーワードを大きく表示しています</li><li>話題のキーワードをオレンジ色に表示しています。</li></ul><hr size=0 noshade />";
    }
    else {
        $("result").innerHTML = "解析中です。。。";
    }
}

タグクラウドのリンクタグにonclickでスクリプトを埋め込んでいます。これで、クリック時に検索キーワードを渡してgoogle検索と兆し検索が行われるという動きになります。
ここら辺は如何様にでもアレンジが出来そうですね。
via: throw life kizAPI yahoo developer network google search ajax api prototypejs

★このコンテンツに目的の情報はありませんでしたか?


2007年10月22日

mojaラボ 気になる成分解析をつくったよ!

elemanalysis.PNG

昨日、受け攻め度チェックをやってみたのですが、こういう占いみたいのって結構おもしろいなぁと思ってmojaラボ気になる成分解析を作ってみました!

成分解析って、「××」が「○○」パーセントの、「××」の部分の列挙が結構しんどいんですね。で、グッと来るワードってなんだろうなぁなんて考えていたのですが、途中でめんどくさくなって、これを連想検索エンジンに任せることにしました。
連想検索エンジンとは、あるワードに対して、(検索エンジンの上でかな?)連想されるワードを50個返してくれるもので、適当な言葉をたくさん列挙したい場合にはとても便利です。
というわけで、成分解析の「××」が「○○」パーセントの「××」の部分を連想検索エンジンに任せて成分解析apiに通すと、自分も予期していなかった変な言葉が返ってきて意外と面白かったです。
今日、10/22にmojalogを解析してみましたら

  • word press:65.3%
  • スポ根:21.3%
  • さきっちょ:8%
  • 溜息:5.3%

ってでました。なにこれ。どっちかっていうとword pressより、movable typeが65.3%なんだけど。しかもさきっちょて。なんのだ。

ちなみに、成分解析結果は日ごとに変わるように出来ています。
デフォルトで連想検索に飛ばすワードがなんとなく恣意的なのは、面白い解析を得たかったからです。へへへ。

尚、ψ(プサイ)の興味関心空間さんでロジックを参考にさせていただきました。また、gigazineさんのエントリでamchartsの適用を参考にさせていただきました。(amchartsすげー)
via :amCharts, ψ(プサイ)の興味関心空間, gigazine

★このコンテンツに目的の情報はありませんでしたか?


2007年10月23日

検索ワードの有効利用エントリのお詫びと訂正

リファラをスライスするサービス作ったよ!なんてココココで鼻息を荒くしながら紹介したのですが、あのままですと他のドメイン(mojalog.com以外)からjavascriptで呼ぶことが出来ないじゃんかってことが調べていたら判ったので(ブフー)呼べるようにしました。みて下さった方にお詫びいたします。すみませんでした。

特に当該エントリに引用いただいたコマネタ帳のネタ帳さん。申し訳ございません。
コマネタ帳:コチラがメインブログでしょうか

このクロスドメインの問題はウェブサービスをいくつか作っているうちに判っていたことなのですが、放置していました。重ねてすみません。

というわけで、テスト用のhtml(エントリ下部参照)と、javascriptのライブラリをご自身のサイトにアップロードして、正しく動けばいろいろいじくれるかと思います。一応、labs.mojalog.comのサブドメインで試してみたので大丈夫かと思います。
javascriptのライブラリは、
JSON and the Dynamic Script Tag: Easy, XML-less Web Services for JavaScript
こちらからダウンロードしてきてください。「1.My JSONscriptRequest class」ってとこにアンカされてます。

このクロスドメイン版は、JSONPという技術で実現しているのですが、JSONのデータフォーマットにコールバック関数なる記述を加えることによってクロスドメインの制約を解決しているわけです。判りにくいですね。
判りやすい説明がありましたので引用させていただきます。といいますか、ほとんどこちらの戯言++さんでいろいろ調べさせていただきました。

via : 戯言++さん
JavaScript でリモートのデータを呼び込む際に2通りの方法があると説明しました。ひとつは XMLHttpRequest クラスを使う方法,もうひとつは JavaScript ソースとしてインクルードする方法です。「クロスドメインの制約」からXMLHttpRequest を使う方法では読込先が他ドメインの場合は使えません。そこでデータを JSONP の形式にして JavaScript のソースとしてインクルードしてしまうわけです。また JSONP は関数呼び出しの形式になっているので,その関数の処理を別に記述することでコールバック関数のように機能します。

リモートのデータをjavascriptのソースとしてインクルードするということみたいですね。すごいね。
リファラのスライスサービス・サーバ側は、json([{"idx":"0","item","hoge"}]);というコールバックをしますので、script側の実装はお好きな方法でどうぞ。
サンプルソースはこんな感じです。上記のjsr_class.jsというライブラリを使用させていただいています。

<html>
<head>
<script type="text/javascript" src="jsr_class.js"></script>
<script type="text/javascript">
var jsrInst;
function getItems(){
    // google でjavascript jsonという検索を行ったと仮定します。
    jsrInst = new JSONscriptRequest('http://mojalog.com/mojascript/gsajax/refrxml.php?refr=http%3A//www.google.co.jp/search%3Fhl%3Dja%26q%3Djavascript+json%26lr%3D&fmt=json');
    // 実際にはこう使う
    // var jsrInst = new JSONscriptRequest('http://mojalog.com/mojascript/gsajax/refrxml.php?refr=' + escape( top.document.referrer ) + '&fmt=json');
    jsrInst.buildScriptTag();
    jsrInst.addScriptTag();
}

// コールバックがjsonなのでjsonというメソッド。変えちゃだめっぽい
function json( data ) {
    var html = "";
    for( idx = 0; idx < data.length; idx++ ){
        alert( data[ idx ].item );
        alert( data[ idx ].idx );
        html = html + data[ idx ].item + ",";
    }
    // スライスしたリファラはご自身の検索フォームにおくなど、ご自由に使ってください
    document.getElementById('result').innerHTML = html;
    jsrInst.removeScriptTag();
}
</script>
</head>
<body>
<input type="button" onclick="getItems()" value="send">
<div id="result"></div>
</body>
</html>

★このコンテンツに目的の情報はありませんでしたか?


2007年10月24日

mojaラボ成分解析もJSONP対応であなたのページに使えます!

mojaラボ成分解析もJSONPでクロスドメインで使えるようにしました!

GET URLの構築

送信するURLはコチラです。
http://labs.mojalog.com/mojascript/elemanalysis/crtPie.php

リクエストURL例

http://labs.mojalog.com/mojascript/elemanalysis/crtPie.php?item=%E3%81%AA%E3%81%BE%E3%81%88&userdict=%E3%83%96%E3%83%AD%E3%82%B0%0D%0A%E7%94%9F%E6%B4%BB&fmt=callback

リクエストパラメータ例

item=%E3%81%AA%E3%81%BE%E3%81%88【なまえ】
userdict=%E3%83%96%E3%83%AD%E3%82%B0%0D%0A%E7%94%9F%E6%B4%BB【ブログ(改行)生活】
fmt=callback【callbackというメソッド名で実装する】

パラメータ説明
itemstring(URLエンコード)解析対象テキストです
userdictstring(URLエンコード)※解析マッピングシードです。改行で区切って複数のシードが設定可能です。(最大20個)
fmtstring(コールバックメソッド名)実装するスクリプトのコールバック関数名を与えます

※解析マッピングシードとは、「××」が○パーセントという結果の、「××」を生成するワードです。
このワード1つ1つを連想検索エンジンに送信し、受信に成功した場合、1件当たり50個の関連ワードを受信します。
(例:「ブログ」を送信した結果)
http://labs.preferred.jp/reflexa/api.php?q=%E3%83%96%E3%83%AD%E3%82%B0&format=xml
この結果の総数(最大1000個)から成分解析を行います。

レスポンスフィールド例

callback ([{"title" : "ブロガー", "value" : "93"},{"title" : "申し入れ", "value" : "6"},{"title" : "Blogger", "value" : "1"}]);

パラメータのfmtを省略した場合RESTで結果を返却します。

<?xml version="1.0" encoding="UTF-8" ?>
<pie>
    <slice title="ブロガー">93</slice>
    <slice title="申し入れ">6</slice>
    <slice title="Blogger">1</slice>
</pie>

HTMLサンプル

サンプルhtmlはコチラになります。このhtmlサンプルはJSONscriptRequestというライブラリを使用しています。
javascriptのライブラリは、
JSON and the Dynamic Script Tag: Easy, XML-less Web Services for JavaScript
こちらからダウンロードしてきてください。「1.My JSONscriptRequest class」ってとこにアンカされてます。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=uft-8" />
<title>sample html</title>
<script type="text/javascript" src="jsr_class.js"></script>
<script type="text/javascript">
var jsrInst;
function getItems(){
    jsrInst = new JSONscriptRequest('http://labs.mojalog.com/mojascript/elemanalysis/crtPie.php?item=' + encodeURI( document.getElementById( 'item' ).value ) + '&userdict=' + encodeURI( document.getElementById( 'userdict' ).value ) + '&fmt=json');
    jsrInst.buildScriptTag();
    jsrInst.addScriptTag();
}
// コールバックがjsonなのでjsonというメソッド。
function json( data ) {
    var html = "";
    for( idx = 0; idx < data.length; idx++ ){
        html = html + data[ idx ].title + ":" + data[ idx ].value + "%<br />";
    }
    // スライスしたリファラはご自由に使ってください
    document.getElementById('result').innerHTML = document.getElementById( 'item' ).value + "を構成する成分の分析結果<br />" + html;
    jsrInst.removeScriptTag();
}
</script>
</head>
<body>
<input type="text" id="item">
<!--ユーザに解析マッピングシードを変更させたくない場合は非表示にする-->
<span style="display:none">
<textarea id="userdict">成分
原料
</textarea>
</span>
<input type="button" onclick="getItems()" value="解析">
<div id="result"></div>
</body>
</html>

HTMLサンプルamCharts適用版

ampiedir.PNG

コチラのサンプルは、amChartsのPIE & DONUT CHARTを使用したものです。ダウンロードページにリンクを張っておきます。

  1. まず、(現在のバージョン)ampie_1.2.5.zipというアーカイブをダウンロードして解凍します。
  2. 解凍したディレクトリを開くと、「examples」というディレクトリがあるかと思います。そこに、コチラからダウンロードしたexamples.zipを解凍し、そのままかぶせてください。
  3. さらに、JSON and the Dynamic Script Tag: Easy, XML-less Web Services for JavaScriptをダウンロードして解凍します
  4. jsr_class.jsというファイルをexamplesディレクトリの中に放り込みます

ampiedir2.PNG

それぞれのファイルをかぶせると、ディレクトリはこんな感じの状態(画像参照)になるかと思います。test.htmlを開いてデータを送信し、受信した内容がレンダリングされることを確認してください。ブラウザのフラッシュのバージョンによっては、グラフが正しく表示されない可能性があります。
一応、受信したJSON形式のデータをフラッシュに食わせて表示されるところまで確認が取れています。

  • IE
  • fire fox
  • sleipnir
  • bugbrowser

ちなみに、examples.zip内のampie_data.xmlは初期化データが入っています。これは、initPieメソッドで表示されるデータが空であって欲しいためです。もっといい方法があれば適宜変更するなどしてください。
ampie_settings.xmlはフラッシュのコンフィグファイルです。グラフのカラーや大きさ、タイトルヘッダなどはここで変更を加えます。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=uft-8" />
<title>sample</title>
<script type="text/javascript" src="../ampie/swfobject.js"></script>
<script type="text/javascript" src="jsr_class.js"></script>
<script type="text/javascript">
function initPie(){
    var so = new SWFObject("../ampie/ampie.swf", "ampie", "520", "400", "7", "#FFFFFF");
    so.addVariable("path", "../ampie/");
    so.addVariable("chart_id", "ampie");
    // グラフのコンフィグセッティング
    so.addVariable("settings_file", escape("ampie_settings.xml"));
    // 空のグラフデータ
    so.addVariable("data_file", escape("ampie_data.xml"));
    so.addVariable("preloader_color", "#999999");
    so.write("flashcontent");
}

var jsrInst;
function getItems(){
    jsrInst = new JSONscriptRequest( 'http://labs.mojalog.com/mojascript/elemanalysis/crtPie.php?item=' + encodeURI( document.getElementById( 'item' ).value ) + '&userdict=' + encodeURI( document.getElementById( 'userdict' ).value ) + '&fmt=json' );
    jsrInst.buildScriptTag();
    jsrInst.addScriptTag();
}
// コールバックがjsonなのでjsonというメソッド。
function json( data ) {
    var html = "";
    
    // 0件返却時のampie.swfエラー抑止
    var xml = "<slice title=\"\">0</slice>";
    for( idx = 0; idx < data.length; idx++ ){
        html = html + data[ idx ].title + ":" + data[ idx ].value + "%<br />";
        xml = xml + "<slice title=\"" + data[ idx ].title + "\">" + data[ idx ].value + "</slice>";
    }
    // 分析結果のレンダリングはご自由に
    document.getElementById( 'result' ).innerHTML = document.getElementById( 'item' ).value + "を構成する成分の分析結果<br />" + html;
    jsrInst.removeScriptTag();
    var flashMovie = document.getElementById("ampie");
    flashMovie.setData('<pie>' + xml + '</pie>');
}
</script>
</head>
<body>
<input type="text" id="item">
<!--ユーザに解析マッピングシードを変更させたくない場合は非表示にする-->
<span style="display:none">
<textarea id="userdict"></textarea>
</span>
<input type="button" onclick="initPie();getItems()" value="解析">
<div id="result"></div>
<div id="flashcontent"></div>
</body>
</html>

ざっくりですが、こんな感じです。これでご自身のページにも簡単(では無いかもしれませんが)に成分解析ページがつくれますね!適宜、ファイルのアップロードパスなどを調整してご自身のブログやホームページに使ってみてください。ほいでわ

★このコンテンツに目的の情報はありませんでしたか?


2007年10月27日

ニコウゴコメントフォームをあなたのブログやサイトに設置する方法

nicougo.PNG

mojaラボ ニコウゴコメントフォームをJSONPでクロスドメイン対応にしました!
トップページにフォームがおいてあります

GET URLの構築

送信するURLはコチラです。
http://labs.mojalog.com/mojascript/nicougo/nicougo.php

リクエストURL例

http://labs.mojalog.com/mojascript/nicougo/nicougo.php?url=http%3a%2f%2fmojalog%2ecom%2f&msg=&fmt=nicougoload

リクエストパラメータ例

url=http%3a%2f%2fmojalog%2ecom%2f【URL】(document.URLをencodeURIしたもの)
msg=【投稿メッセージ】(空だと投稿は行いません)
fmt=nicougoload【nicougoloadというメソッド名で実装する】

パラメータ説明
urlstring(URLエンコード)利用者のURLです。URL(http/https)形式かどうかをサーバ側で判定しています。
msgstring(URLエンコード)送信するメッセージです。空だと投稿はされません。
fmtstring(コールバックメソッド名)実装するスクリプトのコールバック関数名を与えます

レスポンスフィールド例

nicougoload ([{"datetime" : "2007/10/27 22:50:25", "item" : "キタ――――(゚∀゚)――――"},{"datetime" : "2007/10/27 22:00:53", "item" : "32文字以上ですとサーバ側で切り落とします。"},{"datetime" : "2007/10/27 22:00:25", "item" : "投稿できる文字数は32文字になっています。32文字以上ですとサー…"},{"datetime" : "2007/10/27 21:58:31", "item" : "こんにちは"},{"datetime" : "2007/10/27 13:16:06", "item" : "firefoxだと妙に重い"},{"datetime" : "2007/10/27 13:06:12", "item" : "ニコウゴニコウゴ"},{"datetime" : "2007/10/27 13:04:30", "item" : "ニッコウゴにしてやんよ"},{"datetime" : "2007/10/27 13:04:10", "item" : "(゚∀゚)にこうご"},{"datetime" : "2007/10/27 13:03:35", "item" : "firefoxでも一応動くように…"},{"datetime" : "", "item" : "END"}]);

パラメータのfmtを省略した場合RESTで結果を返却します。

<?xml version="1.0" encoding="UTF-8" ?> 
<elems>
  <elem datetime="2007/10/27 22:50:25">キタ――――(゚∀゚)――――</elem> 
  <elem datetime="2007/10/27 22:00:53">32文字以上ですとサーバ側で切り落とします。</elem> 
  <elem datetime="2007/10/27 22:00:25">投稿できる文字数は32文字になっています。32文字以上ですとサー…</elem> 
  <elem datetime="2007/10/27 21:58:31">こんにちは</elem> 
  <elem datetime="2007/10/27 13:16:06">firefoxだと妙に重い</elem> 
  <elem datetime="2007/10/27 13:06:12">ニコウゴニコウゴ</elem> 
  <elem datetime="2007/10/27 13:04:30">ニッコウゴにしてやんよ</elem> 
  <elem datetime="2007/10/27 13:04:10">(゚∀゚)にこうご</elem> 
  <elem datetime="2007/10/27 13:03:35">firefoxでも一応動くように…</elem> 
</elems>

注意点

  • mojaラボ ニコウゴウェブサービスは、パラメータのurlが正しく解決できれば、サーバ側で持っているメッセージをすべて閲覧することが可能です。ある掲示板に、URL名でスレッドがたっているだけというイメージでご利用ください。
  • つまり、ニコウゴフォームを設置したサイトからでなくとも投稿や閲覧が可能です。何らかのパスワードや機密情報、個人情報などの投稿はお控えください。
  • ブログやサイトに設置する方は、エンドユーザに上記内容をお知らせすることをオススメします
  • 投稿されたメッセージはURL毎に30件保管します。それ以上のメッセージが送られると古いものから削除されます。(サーバの負荷などを見て変更されることがあります)
  • 投稿されたすべてのメッセージは、ご利用される方に通知連絡なしで削除されることがあります。
  • mojalog管理人は、ユーザが本サービスを利用して、損害や不利益を被った場合でも、一切の責任を負わず、一切の損害賠償の義務を負わないものとします

HTMLサンプル

サンプルhtmlはエントリ下部に記します。このhtmlサンプルは3つのjavascriptライブラリを利用しています。
1.JSONscriptRequestというライブラリを使用しています。
JSON and the Dynamic Script Tag: Easy, XML-less Web Services for JavaScript
こちらからダウンロードしてきてください。「1.My JSONscriptRequest class」ってとこにアンカされてます。

2.firefoxブラウザを判定するためのスクリプト
as flash as flexさん■[AJAX/JavaScript] UserAgentからブラウザを判定するオールマイティなJavaScript(browsercheck.js)を利用させてもらっています。

3.ニコニコでウゴウゴな表示をするためのスクリプト
nicougo.zip
dynamicdriveにあったドラッグ&ドロップスクリプトを利用しています。 via : dynamicdrive
nicougo.jsの中身は表示方法などを記述したスクリプトです。ご自由にいじってくださっても構いません。といいますか、いい感じな見せ方ができたら教えて欲しいです。ワタシscriptいじるの下手なんで。

htmlに必要なのは以下のコードです。タグのidがご自身のページで競合した場合は、スクリプトのほうも合わせて変更してください。
最後のdivタグ(id=nicougowrap)はbodyタグを閉じる直前あたりに書いたほうがいいかもしれません。

<input type="text" id="nicougosendmsg">
<input type="button" onclick='initJsr();' value="ニコウゴする!" id="nicougobtn">
<div id="nicougowrap"></div>

nicougo.jsはご自由にいじってください。一応 user setting に適当なコンフィグを用意しておきました。

// user setting.
var NICOUGOWIDTH    = 360;       // display width.
var NICOUGOHEIGHT   = 240;       // display height.
var NICOUGOBGCOLOR  = "#FFFFFF"; // display color.
var NICOUGOVPOS     = "240px";   // display vertical position.
var NICOUGOHPOS     = "120px";   // display horizonal position.
var NICOUGOFONTSIZE = 14;        // font size.
var NICOUGOOPC      = 80;        // opacity.
var NICOUGOSPD      = 16;        // speed.
var NICOUGOSHAKE    = 8;         // ugougo shakes.
var NICOUGOEMGSPAN  = 80;        // emerge span.
// user setting.

var itemList;
var topBlurList;
var leftBlurList;
var jsrInst;
var dataSize     = 0;
var nowRendering = false;

var Drag = {

    obj : null,
    init : function( o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper )
    {
        o.onmousedown = Drag.start;
        o.onmouseover = Drag.over;
        o.onmouseout  = Drag.out;
        o.hmode       = bSwapHorzRef ? false : true ;
        o.vmode       = bSwapVertRef ? false : true ;
        o.root = oRoot && oRoot != null ? oRoot : o ;
        if ( o.hmode  && isNaN( parseInt( o.root.style.left   ) ) ) o.root.style.left   = "0px";
        if ( o.vmode  && isNaN( parseInt( o.root.style.top    ) ) ) o.root.style.top    = "0px";
        if ( !o.hmode && isNaN( parseInt( o.root.style.right  ) ) ) o.root.style.right  = "0px";
        if ( !o.vmode && isNaN( parseInt( o.root.style.bottom ) ) ) o.root.style.bottom = "0px";
        o.minX    = typeof minX != 'undefined' ? minX : null;
        o.minY    = typeof minY != 'undefined' ? minY : null;
        o.maxX    = typeof maxX != 'undefined' ? maxX : null;
        o.maxY    = typeof maxY != 'undefined' ? maxY : null;
        o.xMapper = fXMapper ? fXMapper : null;
        o.yMapper = fYMapper ? fYMapper : null;
        o.root.onDragStart = new Function();
        o.root.onDragEnd   = new Function();
        o.root.onDrag      = new Function();
    },

    start : function( e )
    {
        var o = Drag.obj = this;
        e = Drag.fixE( e );
        var y = parseInt( o.vmode ? o.root.style.top  : o.root.style.bottom );
        var x = parseInt( o.hmode ? o.root.style.left : o.root.style.right  );
        o.root.onDragStart( x, y );

        o.lastMouseX = e.clientX;
        o.lastMouseY = e.clientY;

        if ( o.hmode ) {
            if ( o.minX != null ) o.minMouseX = e.clientX - x + o.minX;
            if ( o.maxX != null ) o.maxMouseX = o.minMouseX + o.maxX - o.minX;
        } 
        else {
            if ( o.minX != null ) o.maxMouseX = -o.minX + e.clientX + x;
            if ( o.maxX != null ) o.minMouseX = -o.maxX + e.clientX + x;
        }

        if ( o.vmode ) {
            if ( o.minY != null ) o.minMouseY = e.clientY - y + o.minY;
            if ( o.maxY != null ) o.maxMouseY = o.minMouseY + o.maxY - o.minY;
        } 
        else {
            if ( o.minY != null ) o.maxMouseY = -o.minY + e.clientY + y;
            if ( o.maxY != null ) o.minMouseY = -o.maxY + e.clientY + y;
        }

        document.onmousemove = Drag.drag;
        document.onmouseup   = Drag.end;

        return false;
    },

    drag : function( e )
    {
        e = Drag.fixE( e );
        var o = Drag.obj;

        var ey = e.clientY;
        var ex = e.clientX;
        var y = parseInt( o.vmode ? o.root.style.top  : o.root.style.bottom );
        var x = parseInt( o.hmode ? o.root.style.left : o.root.style.right  );
        var nx, ny;

        if ( o.minX != null ) ex = o.hmode ? Math.max( ex, o.minMouseX ) : Math.min( ex, o.maxMouseX );
        if ( o.maxX != null ) ex = o.hmode ? Math.min( ex, o.maxMouseX ) : Math.max( ex, o.minMouseX );
        if ( o.minY != null ) ey = o.vmode ? Math.max( ey, o.minMouseY ) : Math.min( ey, o.maxMouseY );
        if ( o.maxY != null ) ey = o.vmode ? Math.min( ey, o.maxMouseY ) : Math.max( ey, o.minMouseY );

        nx = x + ( ( ex - o.lastMouseX ) * ( o.hmode ? 1 : -1 ) );
        ny = y + ( ( ey - o.lastMouseY ) * ( o.vmode ? 1 : -1 ) );

        if ( o.xMapper )      nx = o.xMapper( y )
        else if ( o.yMapper ) ny = o.yMapper( x )

        Drag.obj.root.style[ o.hmode ? "left" : "right" ] = nx + "px";
        Drag.obj.root.style[ o.vmode ? "top" : "bottom" ] = ny + "px";
        Drag.obj.lastMouseX = ex;
        Drag.obj.lastMouseY = ey;

        Drag.obj.root.onDrag( nx, ny );
        return false;
    },

    end : function()
    {
        document.onmousemove = null;
        document.onmouseup   = null;
        Drag.obj.root.onDragEnd( parseInt( Drag.obj.root.style[ Drag.obj.hmode ? "left" : "right" ] ), 
                                 parseInt( Drag.obj.root.style[ Drag.obj.vmode ? "top" : "bottom" ] ) );
        Drag.obj = null;
    },

    over : function( e )
    {
        nowRendering = true;
        return false;
    },

    out : function( e )
    {
        nowRendering = false;
        rendering();
        return false;
    },

    fixE : function( e )
    {
        if ( typeof e        == 'undefined' ) e = window.event;
        if ( typeof e.layerX == 'undefined' ) e.layerX = e.offsetX;
        if ( typeof e.layerY == 'undefined' ) e.layerY = e.offsetY;
        return e;
    }
};

// setup json script request.
function initJsr()
{
    // ローカルでテストする場合は encodeURI( document.URL ) を'http%3a%2f%2fexample%2ecom%2f'など実URLに変更する必要があります
    jsrInst = new JSONscriptRequest( 'http://labs.mojalog.com/mojascript/nicougo/nicougo.php?url=' 
                                    + encodeURI( document.URL ) + '&msg=' 
                                    + encodeURI( document.getElementById( "nicougosendmsg" ).value ) 
                                    + '&fmt=nicougoload' );
    jsrInst.buildScriptTag();
    jsrInst.addScriptTag();
}

function nicougoload( param ){
	var nicougoItem = "";
	dataSize = param.length;
    for( idx = 0; idx < dataSize; idx++ ){
        nicougoItem = nicougoItem + '<div id="nicougomsg' + idx 
                                  + '" style="position:absolute; top:0px; left:' + NICOUGOWIDTH 
                                  + 'px; font-size:' + NICOUGOFONTSIZE 
                                  + 'px;"><Nobr><ruby><rb>' + param[ idx ].item 
                                  + '</rb><rp>(</rp><rt>' + param[ idx ].datetime 
                                  + '</rt><rp>)</rp></ruby></Nobr></div>';
    }
    isRender = true;

    var browserInf = new BrowserInfo();
    var inf = 'filter:Alpha( opacity=' + NICOUGOOPC + ' );';
    if( browserInf.firefox ){
        inf = '-moz-opacity:' + NICOUGOOPC / 100 + ';';
    }
    else if( browserInf.opera ){
        inf = 'opacity:' + NICOUGOOPC / 100 + ';';
    }

    document.getElementById( "nicougowrap" ).innerHTML = '<div id="nicougo" style="position:absolute; top:' + NICOUGOVPOS
                                                       + '; left:' + NICOUGOHPOS
                                                       + '; width:' + NICOUGOWIDTH
                                                       + 'px; height:' + NICOUGOHEIGHT
                                                       + 'px; padding: 5px; cursor:move; background-color:#ffffff;' 
                                                       + inf
                                                       + '"><span style="cursor:pointer;" onclick="unload()">'
                                                       + '<img alt="閉じる" src="close.PNG" /></span>' 
                                                       + nicougoItem + '</div>';

    jsrInst.removeScriptTag();
    Drag.init( document.getElementById( "nicougo" ) );
    setupItem();
    rendering();
}

function setupItem()
{
    itemList     = new Array();
    topBlurList  = new Array();
    leftBlurList = new Array();

    for( idx = 0; idx < dataSize; idx++ ){
        itemList.push( document.getElementById( "nicougomsg" + idx ) );
        itemList[ idx ].style.top = 20 + ( Math.random() * ( NICOUGOHEIGHT - 60 ) );
        topBlurList.push( parseInt( itemList[ idx ].style.top ) );
        leftBlurList.push( parseInt( itemList[ idx ].style.left ) + ( idx * NICOUGOEMGSPAN ) );
    }

    nowRendering = false;
}

function rendering(){
    document.getElementById( "nicougobtn" ).disabled = true;
    if( nowRendering ) return false;

    for( idx = 0; idx < dataSize; idx++ ){
        itemList[ idx ].style.position = 'absolute';
        leftBlurList[ idx ] = leftBlurList[ idx ] - NICOUGOSPD;

        if( leftBlurList[ idx ] <= NICOUGOWIDTH ){
            itemList[ idx ].style.display = "";
            itemList[ idx ].style.left = parseInt( itemList[ idx ].style.left ) - NICOUGOSPD + "px";
        }
        else {
            itemList[ idx ].style.display = "none";
        }
        var browserInf = new BrowserInfo();
        var vanishPoint = 0;
        if( browserInf.firefox ){
            vanishPoint = 0;
        }
        else {
            vanishPoint = itemList[ idx ].innerText.length * parseInt( itemList[ idx ].style.fontSize );
        }

        if( leftBlurList[ idx ] + ( vanishPoint ) <= 0 ){
            itemList[ idx ].innerHTML = "";
        }
        itemList[ idx ].style.top = topBlurList[ idx ] + ( Math.random() * NICOUGOSHAKE ) + "px";
    }
    if( itemList[ dataSize -1 ].innerHTML == "" ){
        unload();
    }
    else {
        setTimeout( 'rendering()', 100 );
    }
}

function unload(){
    nowRendering = false;
    document.getElementById( "nicougobtn" ).disabled = false;
    document.getElementById( "nicougowrap" ).innerHTML = "";
}

★このコンテンツに目的の情報はありませんでしたか?


2007年10月29日

ニコウゴコメントフォームを簡単設置!ブログパーツにしてみたよ

ニコウゴコメントフォームを簡単に設置できるようにしたよ!
もちろんニコニコ風なただ流れるコメントにもできます。いろいろカスタマイズしたい場合は、コッチを参照して、スクリプトをカスタマイズしてください。

<script src="http://labs.mojalog.com/mojascript/nicougo/nicougo.js" type=text/javascript></script>
<input type="text" id="nicougosendmsg">
<input type="button" onclick='initJsr();' value="ニコウゴする!" id="nicougobtn">
<!-- ユーザセッティングタグがない場合はデフォルトの値(以下のとおり)になります。環境に合わせてご利用ください //-->
<!-- ポップするウィンドウの幅指定 //-->
<input type="hidden" id="nicougowidth" value="360">
<!-- ポップするウィンドウの高さ指定 //-->
<input type="hidden" id="nicougoheight" value="240">
<!-- ポップするウィンドウの色指定 //-->
<input type="hidden" id="nicougobgcolor" value="#FFFFFF">
<!-- ポップするウィンドウの縦位置指定 //-->
<input type="hidden" id="nicougovpos" value="240">
<!-- ポップするウィンドウの横位置指定 //-->
<input type="hidden" id="nicougohpos" value="120">
<!-- メッセージのフォントサイズ指定 //-->
<input type="hidden" id="nicougofontsize" value="14">
<!-- ポップするウィンドウの透明度指定 //-->
<input type="hidden" id="nicougoopc" value="80">
<!-- コメントの流れるスピード指定 //-->
<input type="hidden" id="nicougospd" value="8">
<!-- コメントの揺れ幅指定 //-->
<input type="hidden" id="nicougoshake" value="8">
<!-- コメントの表示間隔指定 //-->
<input type="hidden" id="nicougoemgspan" value="80">
<!-- コメントの揺れ表示指定 0 無効 / 1 有効 //-->
<input type="hidden" id="isugougo" value="0">
<!--以下のdivタグはbodyタグを閉じる直前あたりに入れてください //-->
<div id="nicougowrap"></div>

★このコンテンツに目的の情報はありませんでしたか?


2008年04月10日

SharePointウェブサービスとExtJS連携でmoss2007 viewer for developers

moss2007viewer.PNG SharePointのビューアを作ってみました。
ExtJSの使い方を勉強しながらだったのでソースは汚いです。
使ったウィジェットは
  • Ext.grid.EditorGridPanel(エクセルみたいなパネル)
  • Ext.TabPanel(タブパネル)
  • Ext.form.ComboBox(ドロップダウンリスト)
  • Ext.DataView(データビュー。アイテムのソートやフィルタなんかができる)
  • Ext.Window(ウィンドウがポップする)
  • Ext.Panel(普通のパネル)
などです。これだけ使ったのでなんとなくExtJSをどうやって使うのか理解できてきましたが、基本的にはサンプルソースを見ながらいろいろといじくってできたものです。あまり参考にならないかもしれませんし、正しい実装ではないかもしれません。
今回の実装は、基本的にはウェブサービスが返すXMLのビューアみたいな使い方をしています。使っているうちに、そういう風に使うことが効果的なのかなと思いました。
ほいでは、以下にnoticeを記します。アーカイブのダウンロードもできるようにしておきますが、免責事項として、ご利用いただいたスクリプトにて生じるいかなる損害についても責任を負いません。

<moss2007 viewer for developersについて>

moss2007 viewer for developers は、サイト・ライブラリ・フィールドの要素や属性などのビューアです。ホストに対してサイト・ライブラリ・フィールドの変更要求は投げていません。SharePointに関係する開発や管理者向けのビューアです。

moss2007のドキュメントライブラリなどにディレクトリごとアップロードしてください
ローカルの端末から使う場合は、moss.jsの71行目

httpInst.open( "POST", "/_vti_bin/SiteData.asmx", false );

の箇所に以下のようにホストヘッダを追加することで使えます。

httpInst.open( "POST", "http://hogehoge/_vti_bin/SiteData.asmx", false );

<使い方>

「▼サイトURLを選んでください」ドロップダウンより、サイトを選んでエンターキーを押下すると、サイトが保持するライブラリ一覧がタブ(LibDef:)で追加されます。また、「▼ライブラリを選んでください」ドロップダウンに一覧が設定されます。
「▼ライブラリを選んでください」ドロップダウンより、ライブラリを選んでエンターキーを押下すると、ライブラリのフィールド一覧と定期情報タブ(FldDef:)が追加されます。FldDef:タブ内の左ペインのフィールド情報をクリックすると、右ペインに詳細な情報が表示されます。ダブルクリックすると、該当のフィールドを加えて、ライブラリアイテム一覧がタブが追加されます。

ダウンロード
String.prototype.ellipse = function(maxLength){
    if(this.length > maxLength){
        return this.substr(0, maxLength-3) + '...';
    }
    return this;
};


// ----------------------------------------------------------------------------
// tabCloseFunc.
// ----------------------------------------------------------------------------
Ext.ux.TabCloseMenu = function(){
    var tabs, menu, ctxItem;
    this.init = function(tp){
        tabs = tp;
        tabs.on('contextmenu', onContextMenu);
    }

    function onContextMenu(ts, item, e){
        if(!menu){
            menu = new Ext.menu.Menu([{
                id: tabs.id + '-close',
                text: 'Close Tab',
                handler : function(){
                    tabs.remove(ctxItem);
                }
            },{
                id: tabs.id + '-close-others',
                text: 'Close Other Tabs',
                handler : function(){
                    tabs.items.each(function(item){
                        if(item.closable && item != ctxItem){
                            tabs.remove(item);
                        }
                    });
                }
            }]);
        }
        ctxItem = item;
        var items = menu.items;
        items.get(tabs.id + '-close').setDisabled(!item.closable);
        var disableOthers = true;
        tabs.items.each(function(){
            if(this != item && this.closable){
                disableOthers = false;
                return false;
            }
        });
        items.get(tabs.id + '-close-others').setDisabled(disableOthers);
        menu.showAt(e.getPoint());
    }
};

// ----------------------------------------------------------------------------
// XMLHttpRequest Func.
// ----------------------------------------------------------------------------
function getHttpReq(){
    return window.XMLHttpRequest ? new XMLHttpRequest() : ( function() {
        try        { return new ActiveXObject( "Msxml2.XMLHTTP" );    }
        catch( e ) { return new ActiveXObject( "Microsoft.XMLHTTP" ); }
    }());
}

// ----------------------------------------------------------------------------
// SiteData.asmx::GetSite Func.
// ----------------------------------------------------------------------------
function getSiteHttpInst()
{
    var httpInst = getHttpReq();
    var retHttpInst;
    httpInst.open( "POST", "/_vti_bin/SiteData.asmx", false );
    httpInst.setRequestHeader( 'Content-Type','text/xml; charset=utf-8' );
    httpInst.setRequestHeader( 'SOAPAction','http://schemas.microsoft.com/sharepoint/soap/GetSite' );
    var postBody = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetSite xmlns="http://schemas.microsoft.com/sharepoint/soap/" /></soap:Body></soap:Envelope>';

    httpInst.onreadystatechange = function () 
    {
        if ( httpInst.readyState == 4 ){
            retHttpInst  = httpInst;
        }
    }
    httpInst.send( postBody );
    return retHttpInst;
}

// ----------------------------------------------------------------------------
// Parse get SingleNode list Func.
// ----------------------------------------------------------------------------
function getSingleTagList( xml, tagNm )
{
    var urlList = new Array();
    var urlElem  = xml.getElementsByTagName( tagNm );
    var listSize = urlElem.length;
    for( idx = 0; idx < listSize; idx++ ){
       var tempList = [ urlElem[ idx ].firstChild.nodeValue ];
       urlList.push( tempList );
    }
    return urlList;
}

// ----------------------------------------------------------------------------
// SiteData.asmx::GetListCollection Func.
// ----------------------------------------------------------------------------
function getListCollectionHttpInst( url )
{
    var httpInst = getHttpReq();
    httpInst.open( "POST",  url + "/_vti_bin/SiteData.asmx", false );
    httpInst.setRequestHeader( 'Content-Type','text/xml; charset=utf-8' );
    httpInst.setRequestHeader( 'SOAPAction','http://schemas.microsoft.com/sharepoint/soap/GetListCollection' );
    var postBody = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetListCollection xmlns="http://schemas.microsoft.com/sharepoint/soap/" /></soap:Body></soap:Envelope>';
    var retHttpInst;
    httpInst.onreadystatechange = function () 
    {
        if ( httpInst.readyState == 4 ){
            retHttpInst = httpInst;
        }
    }
    httpInst.send( postBody );
    return retHttpInst;
}

// ----------------------------------------------------------------------------
// Lists.asmx::GetList Func.
// ----------------------------------------------------------------------------
function getLibHttpInst( url, listNm )
{
    var httpInst = getHttpReq();
    var retHttpInst;
    httpInst.open( "POST", url + "/_vti_bin/Lists.asmx", false );
    httpInst.setRequestHeader( 'Content-Type','text/xml; charset=utf-8' );
    httpInst.setRequestHeader( 'SOAPAction','http://schemas.microsoft.com/sharepoint/soap/GetList' );
    var postBody = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetList xmlns="http://schemas.microsoft.com/sharepoint/soap/"><listName>' + listNm + '</listName></GetList></soap:Body></soap:Envelope>';

    httpInst.onreadystatechange = function () 
    {
        if ( httpInst.readyState == 4 ){
            retHttpInst  = httpInst;
        }
    }
    httpInst.send( postBody );
    return retHttpInst;
}

// ----------------------------------------------------------------------------
// get Grid Func.
// ----------------------------------------------------------------------------
function getSiteDataGrid( xml )
{
    
    var tagList = [ 
        "InternalName", 
        "Title", 
        "BaseTemplate", 
        "DefaultViewUrl", 
        "LastModified", 
        "InheritedSecurity", 
        "AllowAnonymousAccess", 
        "AnonymousViewListItems", 
        "ReadSecurity" 
    ];
    var dataAry = new Array();
    var headList = new Array();
    var clmnList = new Array();
    for( var idx = 0; idx < tagList.length; idx++ ){
        headList.push( { name: tagList[ idx ] } );
        clmnList.push( {header:tagList[ idx ],width:120,sortable:true,dataIndex:tagList[ idx ],editor:new Ext.form.TextField()} );
    }

    var listElem  = xml.getElementsByTagName( "_sList" );
    var listSize = listElem.length;
    for( var idx = 0; idx < listSize; idx+