zenbackのスクリプトを解析してみた(1/3)


ブログパーツを作成してみたいのですが、手始めに勉強としてzenbackの動作原理を解析してみました。


まず、入り口です。
サイトにて提示されているスクリプトは以下の通りです。xxxの部分にはサイトに応じた値が入ります。
(実際は1行ですが見辛いので適当に改行してます。)

<!-- X:S ZenBackWidget -->
<script type="text/javascript">
	document.write( unescape("%3Cscript") + 
                    " src='http://widget.zenback.jp/?base_uri=http%3A//xxx.com/blog&nsid=xxx%3A%3Axxx&rand=" + 
                    Math.ceil((new Date()*1)*Math.random()) + 
                    "' type='text/javascript'"+unescape("%3E%3C/script%3E") );
</script>
<!-- X:E ZenBackWidget -->



処理の内容は、以下のURLで取得できるjavascriptを実行しているだけです。

http://widget.zenback.jp/?base_uri=http://nanoappli.com/blog&nsid=101368938457261269::A1013689596
63646496&rand=1





ですので、次は、ブラウザから上記ページにアクセスして、スクリプトの中身を取得します。

document.write( "\n<div id=\"zenback_loady\" style=\"clear:both;background:#ddf2c3;color:#565656;m
argin:0 0 10px 0;padding:5px 0;text-align:center;font-size:13px;display:block !important;\">zenbac
k\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059\u3002</div>\n<script type=\"text/javascript\">\n  wind
ow['zb_$'] = window['$'] || 0;\n</script>\n<script type=\"text/javascript\" src=\"http://ajax.goog
leapis.com/ajax/libs/jquery/1.4.2/jquery.min.js\"></script>\n<script type=\"text/javascript\">\nva
r bootLoaded = 0;\nfunction bootLoader() {\n  if (!window.jQuery) {\n    setTimeout(function() {\n
      bootLoader();\n    }, 200);\n    return;\n  }\n  if (bootLoaded) return;\n  bootLoaded = 1;\
n  window.zb_jq = jQuery.noConflict(true);\n  if (window['zb_$']) { window['$'] = window['zb_$']; }
\n  \n  window.zb_canonical = zb_jq('head link[rel=\"canonical\"]');\n  window.zb_jq(function(){\n
    window.zb_ping = function() {\n      window.zb_loady_zone_width = zb_jq('#zenback_loady').widt
h();\n      zb_jq.ajax({\n        url: \"http://widget.zenback.jp/\"\n        ,dataType:'jsonp'\n
        ,data: zb_jq.extend({\"base_uri\": \"http://nanoappli.com/blog\", \"rand\": \"1\", \"nsid\":
 \"101368938457261269::A101368959663646496\", \"stage\": \"1\"}, {'motouri':document.referrer}, (wi
ndow.zb_canonical.length ? {'canonical_uri':window.zb_canonical.attr('href')} : undefined))\n      
  ,success: function(data) {\n          zb_jq('#zenback_loady').replaceWith(data);\n          zb_jq
('.zenback').fadeIn(200)\n        }\n      });\n    };\n    var $window = zb_jq(window);\n    var d
ummypos = zb_jq('#zenback_loady').position().top;\n    window.zb_pcc = 0;\n    window.zb_sf = funct
ion(){\n      if (window.zb_pcc || ($window.scrollTop()+$window.height()) < dummypos) return;\n    
  window.zb_pcc = 1;\n      window.zb_ping();\n    };\n    zb_jq(window).scroll(window.zb_sf);\n   
 window.zb_sf();\n  });\n};bootLoader();\n</script>\n<iframe frameborder=\"0\" noresize=\"noresize\
" width=\"0\" height=\"0\" src=\"http://widget.zenback.jp/_p/tracking.html?v=cf1d3\"></iframe>\n");


…長すぎて理解できない。
最初のdocument.writeをalertに変更し、スクリプトの実行結果だけ頂くことにします。

取得した結果は以下の通り。

<div id="zenback_loady" style="clear:both;background:#ddf2c3;color:#565656;margin:0 0 10px 0;padding:5px 0;text-align:center;font-size:13px;display:block !important;">zenback読み込み中です。</div>
<script type="text/javascript">
  window['zb_$'] = window['$'] || 0;
</script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
var bootLoaded = 0;
function bootLoader() {
  if (!window.jQuery) {
    setTimeout(function() {
      bootLoader();
    }, 200);
    return;
  }
  if (bootLoaded) return;
  bootLoaded = 1;
  window.zb_jq = jQuery.noConflict(true);
  if (window['zb_$']) { window['$'] = window['zb_$']; }
 
  window.zb_canonical = zb_jq('head link[rel="canonical"]');
  window.zb_jq(function(){
    window.zb_ping = function() {
      window.zb_loady_zone_width = zb_jq('#zenback_loady').width();
      zb_jq.ajax({
        url: "http://widget.zenback.jp/"
        ,dataType:'jsonp'
        ,data: zb_jq.extend({"base_uri": "http://nanoappli.com/blog", "rand": "1", "nsid": "101368938457261269::A101368959663646496", "stage": "1"}, {'motouri':document.referrer}, (window.zb_canonical.length ? {'canonical_uri':window.zb_canonical.attr('href')} : undefined))
        ,success: function(data) {
          zb_jq('#zenback_loady').replaceWith(data);
          zb_jq('.zenback').fadeIn(200)
        }
      });
    };
    var $window = zb_jq(window);
    var dummypos = zb_jq('#zenback_loady').position().top;
    window.zb_pcc = 0;
    window.zb_sf = function(){
      if (window.zb_pcc || ($window.scrollTop()+$window.height()) < dummypos) return;
      window.zb_pcc = 1;
      window.zb_ping();
    };
    zb_jq(window).scroll(window.zb_sf);
    window.zb_sf();
  });
};bootLoader();
</script>
<iframe frameborder="0" noresize="noresize" width="0" height="0" src="http://widget.zenback.jp/_p/tracking.html?v=cf1d3"></iframe>



エスケープされているのが処理されたのと、改行が入ったのでだいぶ見やすくなりましたが、処理がちょっと長いのでコメントを入れつつ個々の記述を理解していきます(一部、説明のために改行を入れてます)。

//----------------------------------------------------------
// ブログパーツが作られるエリアのdivタグを定義
//----------------------------------------------------------
<div id="zenback_loady" style="clear:both;
                               background:#ddf2c3;
                               color:#565656;
                               margin:0 0 10px 0;
                               padding:5px 0;
                               text-align:center;
                               font-size:13px;
                               display:block !important;">
zenback読み込み中です。
</div>
 
<script type="text/javascript">
  //----------------------------------------------------------
  // 既にjQueryのオブジェクトがある場合は、zb_$に退避しておく
  //----------------------------------------------------------
  window['zb_$'] = window['$'] || 0;
</script>
 
<!-- jQueryのライブラリをロードする -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
 
<script type="text/javascript">
//---------------------------------------
// グローバル変数のbootLoadedを定義する
//---------------------------------------
var bootLoaded = 0;
 
//---------------------------------------
// zenbackのロード処理
//---------------------------------------
function bootLoader() {
 
  //---------------------------------------------------------------
  // まだjQueryのロードが終わってなければ、0.2秒待ってからリトライ
  //---------------------------------------------------------------
  if (!window.jQuery) {
    setTimeout(function() {
      bootLoader();
    }, 200);
    return;
  }
 
  //---------------------------------------------------------------------
  // 多重実行防止を行う(zenbackのブログパーツが複数存在したときの対策??)
  //---------------------------------------------------------------------
  if (bootLoaded) return;
  bootLoaded = 1;
 
  //-------------------------------------------------------------------------------------
  // グローバル変数「zb_jq」にjQueryオブジェクトを格納し,zb_$に退避していた場合は元に戻す
  //-------------------------------------------------------------------------------------
  window.zb_jq = jQuery.noConflict(true);
  if (window['zb_$']) { window['$'] = window['zb_$']; }
 
  //-------------------------------------------------------------------------------------
  // グローバル変数「zb_canonical」を用意し...
  // headタグ内に、<link rel="canonical">なタグがある場合は、そのタグを保存する
  //-------------------------------------------------------------------------------------
  window.zb_canonical = zb_jq('head link[rel="canonical"]');
 
  //-------------------------------------------------
  // ページのロードが完了したとき、以下の処理を行う
  // (処理内容は後述)
  //-------------------------------------------------
  window.zb_jq(function(){
    window.zb_ping = function() {
      window.zb_loady_zone_width = zb_jq('#zenback_loady').width();
      zb_jq.ajax({
        url: "http://widget.zenback.jp/"
        ,dataType:'jsonp'
        ,data: zb_jq.extend({"base_uri": "http://nanoappli.com/blog", "rand": "1", "nsid": "101368938457261269::A101368959663646496", "stage": "1"}, {'motouri':document.referrer}, (window.zb_canonical.length ? {'canonical_uri':window.zb_canonical.attr('href')} : undefined))
        ,success: function(data) {
          zb_jq('#zenback_loady').replaceWith(data);
          zb_jq('.zenback').fadeIn(200)
        }
      });
    };
    var $window = zb_jq(window);
    var dummypos = zb_jq('#zenback_loady').position().top;
    window.zb_pcc = 0;
    window.zb_sf = function(){
      if (window.zb_pcc || ($window.scrollTop()+$window.height()) < dummypos) return;
      window.zb_pcc = 1;
      window.zb_ping();
    };
    zb_jq(window).scroll(window.zb_sf);
    window.zb_sf();
  });
};
//-------------------------------------
// 上で定義したロード処理をコールする
//-------------------------------------
bootLoader();
</script>
 
 
//-------------------------------------------------
// Google Analysticsを使用して、アクセスログを取る
// (下記のURLにアクセスすると分かります)
//-------------------------------------------------
<iframe frameborder="0" noresize="noresize" width="0" height="0" src="http://widget.zenback.jp/_p/tracking.html?v=cf1d3"></iframe>


なんだかグローバルな変数が多いなぁ…という印象。
でも、大きな流れは理解できました。

次は、本編(?)であるwindow.zb_jq関数の中身に入って行きます。zb_jqにはjQueryのオブジェクトが入っているので、これは、普通のスクリプトだといわゆる “$.ready(“に相当するものです。

で、中身です。こっちもコメントを入れていきます。

window.zb_jq(function(){
  //----------------------------------------
  // 関数の定義 : zb_ping()
  // ブログパーツのhtmlをロードする
  // これもグローバルですか...
  //----------------------------------------
  window.zb_ping = function() {
    //---------------------------------------------------------------
    // パーツの横幅を取得し、グローバル変数zb_loady_zone_widthに保存
    //---------------------------------------------------------------
    window.zb_loady_zone_width = zb_jq('#zenback_loady').width();
    //------------------------------------------
    // 非同期で,パーツ内のhtmlを取得する
    // ちょっと複雑なので、詳細は後述します...
    //------------------------------------------
    zb_jq.ajax({
      url: "http://widget.zenback.jp/"
      ,dataType:'jsonp'
      ,data: zb_jq.extend({"base_uri": "http://nanoappli.com/blog", "rand": "1", "nsid": "101368938457261269::A101368959663646496", "stage": "1"}, {'motouri':document.referrer}, (window.zb_canonical.length ? {'canonical_uri':window.zb_canonical.attr('href')} : undefined))
      ,success: function(data) {
        zb_jq('#zenback_loady').replaceWith(data);
        zb_jq('.zenback').fadeIn(200)
      }
    });
  };
 
  // windowオブジェクトを取得する
  var $window = zb_jq(window);
  // パーツのある場所(ページ上部からの高さ)を求める
  var dummypos = zb_jq('#zenback_loady').position().top;
  // パーツのhtmlを読み込んだかflgを、"0:未読み込み"で初期化
  window.zb_pcc = 0;
 
  //------------------------------------------------------------------------
  // 関数の定義: zb_sf()
  // ブログパーツがある所までページがスクロールされてたらパーツをロードする
  //------------------------------------------------------------------------
  window.zb_sf = function(){
  // 既にロード済みだったり,画面のスクロール位置がパーツのある所まで来てない時は何もしない
    if (window.zb_pcc || ($window.scrollTop()+$window.height()) < dummypos) return;
    window.zb_pcc = 1;
 
    // パーツのhtmlを読み込む
    window.zb_ping();
  };
 
  //-----------------------------------------------------------
  // ブラウザでページがスクロールした時のイベントハンドラとして
 // zb_sf() を登録する(ブログパーツ本体の読込処理)
  //-----------------------------------------------------------
  zb_jq(window).scroll(window.zb_sf);
 
  //-----------------------------------------------------------
  // ハンドラに登録した上で、直ぐに一回だけコールする
  // ページ表示のタイミングでロードが必要な場合もあるので
  //-----------------------------------------------------------
  window.zb_sf();
});



最後に、ajaxによるページの非同期読み込み処理を調査します。
1行が長くて読み辛いので、かなり改行を入れてます。

zb_jq.ajax({
  url: "http://widget.zenback.jp/"
  ,dataType:'jsonp'
  ,data: zb_jq.extend({  "base_uri": "http://nanoappli.com/blog", 
                         "rand"    : "1", 
                         "nsid"    : "101368938457261269::A101368959663646496", 
                         "stage"   : "1"}, 
                      {  'motouri':document.referrer }, 
                      (window.zb_canonical.length ? {'canonical_uri':window.zb_canonical.attr('href')} : undefined))
  ,success: function(data) {
    zb_jq('#zenback_loady').replaceWith(data);
    zb_jq('.zenback').fadeIn(200)
  }




$.ajax()関数で、http://widget.zenback.jp/ へ非同期アクセスを行います。dataTypeは”jsonp”なので、jsonpの呼び出しですがcallback指定が無いので,適当なメソッド名でコールされるようです。(この部分自信なし…)
また、アクセスはGETで行われ、クエリストリングdataで指定されています。具体的には以下のような感じになります。

key           value
------------- --------------------------------------------------------------------
base_uri      http://nanoappli.com/blog
callback    jsonp1328873042994
canonical_uri ページのheadタグ内にあるrel="canonical"タグの中身(=正規化されたURL)
motouri       本ページのアクセス元リファラ
nsid	      101368938457261269::101368959663646496
rand          738535305094
stage         1



アクセスが成功したら、successに記載されている関数がコールされます。
関数内ではid=”zenback_loady”なdivタグの内容を置き換えた上で、その内容を200msecかけてフェードインさせます。



次は、このajax呼び出しに対するレスポンス内容の解析になるのですが…、ここまで来るのにだいぶ掛かったので、続きは次回にします。

関連記事

コメントを残す

メールアドレスが公開されることはありません。