Tyntを導入した

blogstormの記事を読んで

ここで紹介されているTyntというサイトのサービスのを導入した。

自分のWebSite上で、コピペ活動を追跡しやすいようにしてくれる。

具体的には、imageとtextのコピーを閲覧者にしてもらった時、コピーに独自のリンクを生成してくれる。そして、それが他のWeb上で自分への被リンクとなったかどうかをcheckしてくる。

なんか最初、理解できなかった。順番としては

  1. サイト運営者にtyntからのjavascript-codeをサイトに貼り付ける
  2. 閲覧者が、サイト上でコピーをする(イメージ、7語以上テキスト、7語以下テキスト。 address-barのコピーはpro向け。
  3. 閲覧者のclipboardには、コピーした情報+サイトのURL(テキストアンカーは続きを読む)+CreativeCommonsの注記 が入る。
  4. ペーストされたリンクをtyntが監視する(そのリンクがcllickされたかどうか)、crawlerか何かを使うの?そんなのいらないね、、
  5. clickされて自分のサイトに来た場合は、コピペしたものが黄色くマーキングされた表示にする。

という感じ。

試しに、自分のサイトのあるページ(//abc-analytics.com/レスポンス時間短縮の為の作業)で、コピペしてみると、

当たり前だけど、読み込みの遅いweb-pageはまずい。でも、どうやって改善したらいいかわからないから放置しがち。
続きを読む //abc-analytics.com/レスポンス時間短縮の為の作業#ixzz0YEJ35c95
Under Creative Commons License: Attribution

こんな感じに、ペーストされる。で、リンク先に行くと、

image

こんな感じで、コピペした部分が黄色の表示になる。urlはと、最後に#以下にアンカーが付く。

//abc-analytics.com/レスポンス時間短縮の為の作業#ixzz0YEJ35c95

一方、サイトの管理の為のページの方はというと、

image

コピーされた回数を見てる。

被リンクとなったものは、siliverに。

clickされるとgoldに成る。

生成された被リンクをランクに分けて表示してくるという事かな。

image

普通にpage-viewなんかもある。

Pro版にすれば、もっといろいろあるんだろうけど、このままでも満足度が高い。

挿入したscriptの動作が気になったけど、具体的な動きがよく分からない。Domのlevel2のselection機能とevent-listernerを使うような気がするけど、clipboardの書き換えなんかあったかな?

下は、chromeでprofileを出して,増加した関数を出してみたところ。nodeliset, mouseevent,range,xpathresultなんかが増加してる。

これ以上、突っ込むのはやめ。

image

レスポンス時間短縮の為の作業

2009/11/25 21:00 少し追加訂正

当たり前だけど、読み込みの遅いweb-pageはまずい。でも、どうやって改善したらいいかわからないから放置しがち。

で、わからないなりに、調べたり、手を動かしたり、放置してみた。

1.ホスティング側の問題?

レンタルサーバーはdreamhostを使ってるけど、パフォーマンスに波があるという話を聞いた。

計測方法? serverでuptimeを定期的に取って確認する?

現状 cronでuptimeを計測したのを見てると、2から8くらいまでの数値になっている。5前後が結構多い。これとresponseタイムの関係が、自分の所でまとまっていない。

対策  よくわかない。サーバーの引っ越しは大変そう。情報も出てこないので、放置。

2. wordpressの問題?

wordpressは、mysqlにデータを入れておいて、それを元にphpでhtmlを作るもの。このプロセスが遅い場合は?

計測方法? わからん。1と2を合わせたものが、対象URLのdocumentの読み込み時になると考えていいのかな。

対策 wordpress supercacheを使う。(日本語URIを使うと、動かないという話で敬遠してたけど、先ほどやってみたら動いた、手元では読み込み時間が1000ms以下になった 全然違った)。お手軽に静的ファイルをcacheに置く事ができた。
毎回、cacheを作ってるようだ、、これではだめ、、

3. renderingの問題?

ハイパフォーマンスWebサイトという本を見ながら、YSlowを入れて、いろいろやってみた。

  1. httpリクエストを減らす。 (F判定なんだけど、google 絡みのが大いので削れない、css-spriteはやれてない)
  2. CDNを使う (僕には関係ない)
  3. expireヘッダを設定する
    1. ExpiresActive On
    2. <FileMatch “.\.(gif|jpg|js|css|png)$”>
      ExpiresDefault “access plus 10 years”
      </FileMatch>
    3. js,cssなどで変更のあるものは、version番号が付いてるので、これで良しとする。
  4. html, javascritp, sytlesheetなどをgzipする (.htaccessで、SetOutputFilter DEFLATEと書いた)
  5. stylesheetを先頭に置く。 (stylesheetを<head>に持って行った)
  6. javascriptは最後に置く。 (<head>で読む必要はないので、<body>の最後に移した)
  7. stylesheetとjavascriptを外部ファイル化する (外部ファイルにして、まとめるものはまとめて、一ファイルにした)
  8. DNSルックアップを減らす。 (やれることはない?余分な外部サイトへの呼び出しを外していく?)
  9. javascriiptを縮小化する (jsminを使って縮小した)
  10. redirectを避ける (いくつか、被リンクのものを引き継いでるので、その分は放置)
  11. scriptの重複を避ける (wordpressのwp_enqueue_scriptで対応できるそうなんだけど、手で調整して重複を消した)
  12. ETagの設定を変更する (ETagの必要性がなさそうだったので、.htaccess に FileETag noneと入れた)
  13. ajaxをcache可能にする (ajaxは使ってない)

とりあえず、1.2の問題は、wordpress-supercacheの導入で良しとした。(Firebugで見る限り、1000ms以下 違った。)

それで、計測方法に、google analyticsを使う。 で完成といきたかったけど、上手くいかなかった。残念。

読み込み時間を計測して、サイト改善を考えてたけど、、

できなかった記事です。(2009/11/26訂正、下の方)

Google Analyticsを使用して、ページの読み込み時間と、ユーザーの行動(滞在時間)の関係を見る。

ユーザーの体感速度をGoogle Analyticsで可視化する

という記事があって、スクリプトまで載っていたので、コピペして使わせてもらいました。

おかげで、ユーザの体感速度(読み込みのみ)が可視化されたので、それをアクショナブルなものにしようとした。

  1. ページ事の読み込み時間の差を認識して、改善対象ページを特定する
  2. 読み込み時間の差が、サイト内行動に制約を与えてないかを特定する。

ぐらいは、簡単に評価できそうだった。で、この2つをやってみた。

1.ページ毎の読み込み時間を出す

数が少ないので、なんとも言えないけど、この場合だと一つのページは、DOM構築までに時間が掛かっているのがわかる。

改善対象が1ページなら、やる気も減退しないし、日にちを区切っていけば、問題の原因をつかめるかもしれない。

イベント値が、そのままdocumentのload開始から、contentLoadできるまでのミリ秒になります。

平均が見られるのは便利。一個、異常値がある。

image

2.読み込み時間とサイト内行動の制約

ユーザがストレスを感じて、クリックなどに影響が出てないかを見る。

滞在時間とページビューの差があれば、そのストレスを感じてるとする。

これは、利用状況を見れば良い。

image

数字をもう一度。

Dom構築まで page-view time_on_site サンプル数
-999 4.33 27:11 3
1000-1999 3.22 0:09:14 9
2000-2999 2.88 0:10:17 8
3000-3999 2.80 0:10:39 5
4000-4999 2.0 0:00:03 4
5000- 3.22 0:09:14 7

何とも言えないし、因果関係は逆で、page-viewが進めば(2番目のpage-viewから?)、cacheが効いて、読み込みが早くなる気がする。

なんとも言い難い。

などと考えていると、、、

サイト全体のセッション数が15、ページビューが32で、イベント発生数が16?

下の考えは間違い。ページビューのカウントが間違いぽい。原因は不明。イベントは、上書きされる事はなく、シンプルに送られた値が記録されていると考える方がいいみたい。よくわかってないので、こう考える方が自然。

ページを読み込む事にeventが発生してるはずなのだが、、、、

イベントのトラッキングは、セッション単位での計測になって、最後のページビューの数値しか残らない?

javascriptからはデータが送られているけど、Googleのデータ管理-レポートの段階で、ゴニョゴニョされてるよう。

セカンドディメンジョンに閲覧開始ページを出してみる(と言うことは、セッションでの管理という事???)

以下は、参考画像。

image

よくわからなくなった。

砂遊びって言葉が脳内に浮かんできた、、

GAとsetAutoTrackOutbound

autoTrackOutBound

2010/01/08 この絵には間違いがある。setAutoTrackOutboundでは、javascriptによる遷移を行う関数が二個ある。ビーコンを飛ばした後に遷移処理がある。この絵では、それが書かれてない。(このトピックの最後で僕が書いたもので気づきました

google analytics には、公式documentがないけど、setAutoTrackOutboundというapiがある。

目的は、外部へのリンクを計測するためのもの。(documentが無いということは、使うなとう意味だと思う)

英語の方のhelp forumでは議論されているみたい。 で、なんでも、外部リンクの計測は二年前から言われてた事だと。

問題になっているのは、ga.jsの読み込みタイミングとか、hrefのrequestとpageTracker._trackPageviewのタイミングとかで、計測漏れなどの問題があるのかもしれないし、いろんな事情があるのかもしれない。

参考記事: Tracking Outbound Links — The Right Way

こちらは議論には関係ないけど、計測するのに参考になる記事: Google Analyticsで離脱リンクを自動計測する方法 (_trackEventを使ったもの。trackEventの方が利用状況を使って外部への離脱までの時間が計測できるので、有用かなと思います。)

利用方法(非推奨です)

2009/11/19 送ったデータは、レポートには反映されてなかった。tracking code側は動いても、レポートには反映されないよう。

ドキュメントに無いものなので、試す価値もないけど、調べたので、一応動かしてみた。

pageTracker._setAutoTrackOutbound([]);([]は、計測しないdomainを入れておく、今回はそのまま計測するので[])

した後に、

pageTracker._trackPageview();

とする。

これで、画面遷移前にビーコンが飛ぶ。

下図は、FireBugで見たビーコンのパラメータ(utmhn(ホスト名)と、utmeの中のurlのドメインが違う)

WS000002

具体的な処理は、冒頭の絵に留めて起きます。

使える設定としては、

  • 計測しないドメインの設定 (とりあえず、ビーコンが飛(ぶ/ばない)のは確かめた。)
  • onclickの処理の最後に、独自の関数を設定 (firefoxで確かめた)
  • linksを集める時の上限を設定???(ちょっと、ロジックが不明、utmbの値に関連するみたいだけど、毎回10にresetされる気がする、たぶん、1000回。試してないけど)

ぐらいができます。

*ちなみ、6はoutboundに割り当てられたkey(utmeに出てくる)だと思う。customVarやtrackEventにも使われれて、

  • 5 – trackEvent
  • 6 – outbound
  • 8 – name(customVar)
  • 9 –  value(customVar)
  • 11 – scope-level(customVar)

現状はそうなってます。

ドキュメントされてない機能は、情報もサポートもないので、おとなしくEventListenerに追加してく方が賢明なのは、言うまでもないです。

ga.jsのキャンペーン処理関連の動きを追う。

修正: 2009/11/16 16:45(いろいろ書き直しました)

前回の処理の続きです。

initData内の3つの処理を見るシリーズで、今回は3つめのutmzの処理(キャンペーンに関する部分)です。

  1. 新規セッションなのかどうか(utma, utmb, utmc)
  2. utmvの処理(setVar, setCustomVar)に関する所
  3. utmzの処理(referrerを、どうutmzに組み込むか) (この記事)

他の回と同様に、Ga.jsのコードを追います。(変数名は、私の独自定義です)

initData()の中で、

campaignManager = new Z.Camp(

b.domainhash, b.modifiedreferrer, b.time, defaultvalues);

という感じで、utmzを扱うオブジェクトを作っておいて、

b.Ta = campaignManager.cc(b.cookiemanager, b.isNewSession)

cookie全体の操作を扱うオブジェクトと,1の新規セッションかどうかを判定したフラッグ変数を引数に、メソッドを実行します。

返値は、””, utmcr=1, utmcn=1です。キャンペーンに関係なければ、””, nがnew campaignで、rがrepeart campaignだと思います。

それで、campaingManager.cc(b.cookiemanager, b.isNewSession)の動きです。

いろいろな条件分岐で、referrerとcookienのutmzの部分を見て、キャンペーンに関係のあるセッションか? 関係あるなら、新規キャンペーンか既存か?を判定しています。

最終的に、その判定を返り値で、メインのAPIを操作するオブジェクトのプロパティに返して、データ送信時に使います。

条件としては、以下の状態return “”になります。(3パラメータ条件=”id, source, gclid”のいずれかがある)

  1. pageTracker._linkのタイプで飛んだ先の場合、既にキャンペーンがあれば、return “”
  2. 3パラメータ条件を満たすけど、urlのパラメータにnooverride=1が入っていたら、return “”
  3. sourceに検索エンジンの値を入れる。このとき、無視するキーワード(自分host名とかにaddIgnoredOrganicで設定)なら、return “”
  4. 検索エンジンの値が無い+新規セッションの場合、sourceに参照サイトを入れる。無視する参照元なら、return “”
  5. 参照元もない+新規セッション+utmzの蓄えがない場合、sourceをDirectとする。
  6. 3パラメータ条件に当てはまらない場合には、return “”

で、上の条件で、return “”とならなかった場合には、

  1. (新規セッション Or utmzの値とreferrerからの値にズレ)  の時、utmcn=1を返す
  2. それ以外は、utmcr=1を返す

という作業になります。

これで、initDataの3つの処理は終わりになります。

その後、trackePageviewなら、特殊処理をして、requestを飛ばす作業に入るし、他の設定系のAPIの場合は、値を返したり、Setしたりする作業に移っています。

コードにつけたメモを付けておきます。(中身はあまり信用しないで下さい、指摘してもらえるとありがたいです)

       //initDataで呼ばれるもの
     //return "" or "&utmcn=1" or "&utmcr=1"
     //arguments g=>"cookiemanager, h=>"isNewVisit?"
     cmp.cc = function (cookiemanager, newsession) { //?, h
       var k = "",
       n = "-",
       r, a = 0,
       d, l, domainhash = cmp.c;
       if (!cookiemanager) return "";
       l = cookiemanager.getCookieStr(); //lにcookie値を入れる
       k = cmp.getSearchTerm(dfvls.a["location"]); //kにはsearchが入る
       //_linkで遷移してきて、validataに通った場合
       if (dfvls.allowLinker && cookiemanager.verify()) {
         n = cookiemanager.na(); //utmzに関する値が、cookiemanager内変数のutmzArにあるか?
         //linkで飛んだ先に、既存のcampaignがあれば、return ""
         if (!NoVl(n) && !GaIndexOf(n, ";")) { //nに値があり、;がきちんとあれば、
           cookiemanager.Ga(); //utmzの値をcookieに書き込み
           return ""; //campaignには関係なしと
         }
       }
       n = Q(l, "__utmz=" + domainhash + ".", ";"); //nには今までのcookieにあるutmzが入る
       r = cmp.setAndGetDataHolder(k); //rに$.Camp.DataHolderオブジェクトがはいる, kはlocation.searchパ\
ラメータ

       //nooverride=1の時
       if (cmp.L(r)) {
         k = Q(k, dfvls.utm_nooverride + "=", "&"); //nooverrideの値を取得
         if ("1" == k && !NoVl(n)) return ""; //1で utmzに値があれば、""
       }

       //無視する検索ワードの場合に return ""
       if (!cmp.L(r)) { //id, source, gclidのどれもない
         r = cmp.setOrganic(); //new Z.Camp.DataHolder()が入る
         if (!NoVl(n) && cmp.isIgnoredTerm(r)) return ""; //追加の無視キーワードであれば
       }

       //新規セッションなら、referrerを調査して、無視する検索キーワードならreturn ""
       if (!cmp.L(r) && newsession) {
         r = cmp.setReferral(); //referralをsetしたcmp.DataHolderが入る
         if (!NoVl(n) && cmp.isIgnoredRef(r)) return ""; //無視する参照元なら、""
       }

       //新規セッションで、cookieが""なら直接参照をset
       if (!cmp.L(r)) if (NoVl(n) && newsession) r = cmp.setDirect(); //utmzが空なら、直接参照

       //directでsetしたなら、return ""
       if (!cmp.L(r)) return "";

       //utmzに値があれば、referrerのものと比較
       if (!NoVl(n)) {
         //a [ <domainhash><birth-time><num_of_session><num_of_camp> + csr]
         a = n.split(".");
         d = new Z.Cmp.DataHolder;
         d.setValueFromStr(a.slice(4).join("."));//utmzのutmc値から、dオブジェクトに値をset
         d = ToLower(d.getStr()) == ToLower(r.getStr()); //dオブジェクトの値を文字列化して、比較
         a = a[3] * 1; //aにはキャンペーンセッション回数
       }
       if (!d || newsession) {//cookieとreferrerが違うか、newsessionなら
         //utmaの値を取得
         newsession = Q(l, "__utma=" + domainhash + ".", ";");
         //最後の"."の場所を取得
         l = newsession.lastIndexOf(".");
         //session回数を取得 l>9にならない場合があるのか? domain-hashが1でも、session-idがあるし?
         newsession = l > 9 ? GaSubstr(newsession, l + 1) * 1 : 0;
         a++; //campain-counterを++
         //0を1に。他はそのまま
         newsession = 0 == newsession ? 1 : newsession;
         //referrerからの値で、cookiemanagerに値をSet
         cookiemanager.wb([domainhash, cmp.timenow, newsession, a, r.getStr()].join("."));
         cookiemanager.Ga(); //書き込み
         return "&" + "utmcn=1";
       } else return "&" + "utmcr=1"; //repeartの場合は、utmzは変更しない
     };
   };

ga.js : utmv処理部分(initData内で)

前回の記事(ga.js: 新規セッション管理部分)で、initData内の3つの処理

  1. 新規セッションなのかどうか(utma, utmb, utmc)
  2. utmvの処理(setVar, setCustomVar)に関する所
  3. utmzの処理(referrerを、どうutmzに組み込むか)

の1のセッションの新規・既存管理部分をみました。(b.pc())

今回は、2のutmv処理部分。

そのまえに、utmv部分についての話を少し。

これは、analyticsユーザが自由にデータにラベル付けをできる部分です。

ユーザ定義という表現でレポート画面に出ています。

(カスタム変数が使えるようになっているとのアナウンスがありますが、まだ私の所には導入されてないようです)

ユーザ定義は、apiを使って、利用者が自由にユーザをラベル付けできる部分なので、能動的?な解析が可能になります。

(カスタム変数では、ユーザ、セッション、ページの各レベルでラベリングが可能になってます。ラベリングというより、タグ付けという感じですが)

今回は、utmvmanager.tc()という処理。b.pcの後の処理です。(変数名は、前回同様、私が勝手に付けたものです)

  1. cookieからutmvに関する部分を読み出して、setVarの部分を除けておく。
  2. 読み出したものを、utmvmanager.data変数にsetする。
  3. cvmanager(送信時のutmv部分の文字列作成用オブジェ)に値をsetする
  4. scopeが1 ユーザレベルでのカスタム変数部分を取り出して、setVar部分の文字列に足す。
    1. 文字列があれば、cookieにutmvとして保存する。
    2. なければ、cookieに、utmvは ”” 空文字、セッション限りのものとする。

こんな流れになってるようです。cookieから値を読んで、送信用に値を設定してるだけでした。

ちょっと長いですが、今回の流れで登場するオブジェクトのUtmvManager, CvManager(私がつけた変数名)に関するコードのコメントを貼り付けて起きます。いろいろ勘違いはあると思います。

  //utmv管理オブジェクト   initDataから呼ばれる
  Z.UtmvManager = function (f, i, b, j) {
    //qは初期化オブジェクト, pはドメインハッシュ, c.Oはcookiemanager, eは$.CvManager
    var utmvmanager = this,
    domainhash = i,
    m = "=",
    dfvls = f,
    cvmanager = j; //cvmanager

    utmvmanager.cookiemanager = b; //cookiemanager O
    utmvmanager.oldsetvar = ""; //sa
    utmvmanager.data = {}; //値がstoreされる

    //initDataから呼ばれる
    utmvmanager.tc = function () {
      var h;
      //cookieの値を読んで、domain-hashを見て、utmv値を取り出す("."以降)
      h = GaSplit(Q(utmvmanager.cookiemanager.getCookieStr(), "__utmv=" + domainhash + ".", ";"), domainhash + ".")[1];
      if (!NoVl(h)) {
        h = h.split("|");
        //1, {}, "|" ?? utmvmanager.dataに値がsetされる gの定義は下
        // {index: [key,value,scope]}の形
        setting(1, utmvmanager.data, h[1]);
        //旧式のsetvarの値
        utmvmanager.oldsetvar = h[0];
        //cvmanagerオブジェにset
        utmvmanager.setInKvoperator();
      }
    };

    //実際にcookieにutmvの値を書き込みする
    //utmvmanager.dataオブジェクトが書き込みされる
    utmvmanager.setInKvoperator = function () {
      utmvmanager.clearAndSet(); //値を設定し直す、内部でkvopertorにデータをset
      var h = utmvmanager.oldsetvar, //setvarの代入
      k, n, r = "";
      //scope-level=>1 "user-level" rに代入
      for (k in utmvmanager.data){
        if ((n = utmvmanager.data[k]) && 1 === n[2]){
          r += k + m + n[0] + m + n[1] + m + 1 + ",";
        }
      }
      //rに値があれば(user-levelがあれば)
      NoVl(r) || (h += "|" + r);

      //setvar, user-levelに値がなければ
      if (NoVl(h)){
        //utmvに、値なし期限無しのcookie書き込み,
        utmvmanager.cookiemanager.Vb();
      //あれば
      }else {
        //utmvに値をset
        utmvmanager.cookiemanager.Aa(domainhash + "." + h);
        //utmvに、実際に、cookie書き込み
        utmvmanager.cookiemanager.Fa();
      }
    };

    //setVar用の設定
    utmvmanager.setSetVar = function (h) {
      utmvmanager.oldsetvar = h;
      utmvmanager.setInKvoperator();
    };

    //setCustomVarの設定用
    //b._setCustomVarをcallした時は、initData()のあとに、これが呼ばれる
    //pageTracker内で作られたutmv
    utmvmanager.setSetCustomVar = function (h, k, n, r) { //[index, name, value, scope]
      if (1 != r && 2 != r && 3 != r) r = 3; //指定がない場合は3(page-level)
      var a = false;
      if (k && n && h > 0 && h <= dfvls.maxSlot) { // dfvls.obは indexの最大値制限 5
        k = GaEncode(k);
        n = GaEncode(n);
        if (k["length"] + n["length"] <= 64) {
          utmvmanager.data[h] = [k, n, r]; //{index, [name, value, scope]}というデータ構造
          utmvmanager.setInKvoperator();
          a = true;
        }
      }
      return a;
    };

    //getVisitorCustomVar用
    utmvmanager.mc = function (h) {
      if ((h = utmvmanager.data[h]) && 1 === h[2]) return h[1];
    };

    utmvmanager.Ub = function (h) {
      var k = utmvmanager.data;
      if (k[h]) {
        delete k[h];
        utmvmanager.setInKvoperator();
      }
    };

    //utmvの値をclearして、setし直す
    utmvmanager.clearAndSet = function () {
      cvmanager._clearKey(8); //"k"が8のvalueをclear
      cvmanager._clearKey(9); //"k"が9のvalueをclear
      cvmanager._clearKey(11); //"k"が11のvalueをclear
      var h = utmvmanager.data,
      k, n;
      //転置のイメージ、utmvmanager.dataの長さと同じ長さの配列を作る
      //cvmanagerにデータをsetしていく
      for (n in h){
        if (k = h[n]) { //=に注意、==でない
          //cvmanager._setKey(8or9or11, index, value)という形
          cvmanager._setKey(8, n, k[0]); //nameは8
          cvmanager._setKey(9, n, k[1]); //valueは9
           //3 != k => user, sessionなら書き込み scopeをset
          (k = k[2]) && 3 != k && cvmanager._setKey(11, n, "" + k); //scopeは11
        }
      }
    };

    //値のset
    function setting(h, data, n) {
      var r;
      if (!NoVl(n)) {
        n = n.split(",");
        for (var a = 0; a < n["length"]; a++) {
          r = n[a];
          if (!NoVl(r)) {
            r = r.split(m); //m=>"="
            if (r["length"] == 4) data[r[0]] = [r[1], r[2], h]; //kに値が入る
          }
        }
      }
    }
  };

  //key-value操作用 utmv用オジェ、 メインオブジェから呼ばれる
  //3階層オブジェクトを作り、読む keyは文字列、valueは数字
  //Mainオブジェクトのプロパティとして入る
  Z.CvManager = function () {
    var cvmanager = this, //f = this
    i = {}, //メインーインスタンスーオブジェクト
    //b = "k",
    //j = "v",
    c = ["k", "j"]; //"k", "v"
    //p = "(",
    //m = ")",
    /*
    q = "*",
    e = "!",
    g = "'",
    h = {};
    h[g] = "'0";
    h[m] = "'1";
    h[q] = "'2";
    h[e] = "'3";
    */
    var k = 1,
    h = {"'":"'0", ")":"'1", "*":"'2", "!":"'3"};

    //Setter
    function setter(o, u, y, B) {
      if (undefined == i[o]) i[o] = {};
      if (undefined == i[o][u]) i[o][u] = [];
      i[o][u][y] = B;
      debugger;
    }

    //Getter
    function getter(o, u, y) {
      return undefined != i[o] && undefined != i[o][u] ? i[o][u][y] : undefined;
    }

    //Deleter
    function deleter(o, u) {
      if (undefined != i[o] && undefined != i[o][u]) {
        i[o][u] = undefined;
        u = true;
        var y;
        for (y = 0; y < ["k","v"]["length"]; y++) {
          if (undefined != i[o][["k","v"][y]]) {
            u = false;
            break
          }
        }
        if (u) i[o] = undefined;
      }
    }

    function buildStrByNum(o) { //d
      var u = "",
      y = false,
      B, O; //c => ["k", "v"]
      for (B = 0; B < c["length"]; B++) {
        O = o];
        if (undefined != O) {
          if (y) u += c[B];
          u += l(O);
          y = false;
        }else{
          y = true;
        }
      }
      return u;
    }

    function l(o) {
      var u = [],
      y, B;
      for (B = 0; B < o["length"]; B++) if (undefined != o[B]) {
        y = "";
        if (B != k && undefined == o[B - 1]) y += B.toString() + "!";
        y += t(o[B]);
        GaPush(u, y);
      }
      return "(" + u.join("*") + ")";
    }

    function t(o) {
      var u = "",
      y, B, O;
      for (y = 0; y < o["length"]; y++) {
        B = o.charAt(y);
        O = h[B];
        u += undefined != O ? O : B;
      }
      return u;
    }
    cvmanager.qc = function (o) {
      return undefined != i[o];
    };

    //b.Lc(trackPageview)から呼ばれる,送信用文字列を作成
    cvmanager.getCustomVarStr = function () {
      var o = "",
      u;
      for (u in i){
        if (undefined != i[u]){
          o += u.toString() + buildStrByNum(i[u]);
        }
      }
      return o;
    };

    //b._sendXEventからcallされる,送信文字列作成用
    cvmanager.Ac = function (cvmanager_local) {
      if (cvmanager_local == x) return cvmanager.getCustomVarStr();
      //uの例 : "5(category*action)"
      var u = cvmanager_local.getCustomVarStr(),
      y;
      for (y in i){
        if (undefined != i[y] && !cvmanager_local.qc(y)){
          u += y.toString() + buildStrByNum(i[y]);
        }
      }
      return u;
    };

    //clearAndSetから呼ばれる
    cvmanager._setKey = function (either_8_9_11, i, name) {
      if (typeof name != "string") return false;
      //elementOfUtmvDataは順番i["8"]["k"] = x xが配列でnがインデクサで、nameがvalue
      setter(either_8_9_11, "k", i, name);
      return true;
    };

    //真っ当な数字であること
    cvmanager._setValue = function (o, u, y) {
      if (typeof y != "number" && (undefined == Number || !(y instanceof Number)) || Math.round(y) != y || y == NaN || y == Infinity) return false;
      //uは順番
      setter(o, "v", u, y.toString());
      return true;
    };
    cvmanager._getKey = function (o, u) {
      return getter(o, "k", u);
    };
    cvmanager._getValue = function (o, u) {
      return getter(o, "v", u);
    };
    cvmanager._clearKey = function (o) {
      deleter(o, "k"); //o は 8 or 9 0r 11
    };
    cvmanager._clearValue = function (o) {
      deleter(o, "v");
    };
  };
  //End of CvManager

ga.js: 新規セッション管理部分

コードのコメントをそのまま載せておく形式で書きます。

この処理の部分は、initDataの中のb.pcが担当なんだけど、(bとかpcは、compile化された後の変数なのでsourceで名前は不明)

* ga.jsのversionは、4.5.9

initDataの前提として、

  • そのinitDataが呼ばれるのは、
    • trackePageview, GASO, trackEvent, CustomVar関連, link, setAutoTrackBound(使われてない)、、などなど
  • 処理の中身は、3つに分けられる。
    1. 新規・既存セッションの判別・処理(b.pc)
    2. utmv(CustomVar)に関する処理(n.tc)
    3. utmz(Campaign)に関する処理(c.mm)

となっているのですが、

この記事では、1番の新規・既存セッションの判別・処理部分(b.pc)を見ていきます。

やっていることは、(コードの流れとはずれますが)

  1. pageTracker._linkなどのurl渡しのcookieデータが来た時の処理
  2. ドメインハッシュが一致しているか?(不一致で、新規セッション)
  3. urlからcookieへの値の設定
  4. utmb, utmcでの新規・既存check
  5. 新規なら、utmaをset, 既存ならutmbをsetする(utmbのpageview部分がcountされるのは、送信直前処理)
  6. 実際にcookieに書き込み

では、コードと、僕が付けたコメント

(変数名、関数名は、僕が勝手につけたので、他人が読む意味ないかもしれないけど、他に良い方法が見あたらない)

bがpageTracker変数が参照するオブジェクトになります。

 //セッションに関する処理を行う
   b.pc = function () {
     var timenow = b.r, //現在時間がb.rに入っている
     cookiemanager = b.cookiemanager,     //CookiManagerオブジェクト d
     cookievalue = cookiemanager.getCookieStr(),   //cookieの値を取得 l
     domainhash = b.domainhash + "", //t=b.domainhash ドメインハッシュ "none"なら1
     o = dfvls.e,     //dfvlsは初期値格納オブジェ、.eはwindowsオブジェになっている
     u = o ? o.gaGlobal : undefined, //たぶん、そのままwindowsオブジェクト
     y,           //アンカーで"#"を指定した場合に、アンカー以下のURIが入ってくる
     B = GaIndexOf(cookievalue, "__utma=" + domainhash + "."), //_utmaある?
     O = GaIndexOf(cookievalue, "__utmb=" + domainhash),       //_utmbある?
     ma = GaIndexOf(cookievalue, "__utmc=" + domainhash),     // _utmcある?
     G,          //_utmaの値を格納
     J = [],     //_utmbの値を配列で格納
     S = "",    //y+"&"
     ha = false; //gaGlobalの判別flag(ドメインハッシュが一致しない場合はtrue)
     //b.pcの内部変数宣言終わり

     //"-"とかを""にする
     cookievalue = NoVl(cookievalue) ? "" : cookievalue;

     //c.allowLinkerはsetAllowLinker(true)で1
     //urlから値を取得、この時に、utmkがハッシュでの改ざんcheckになる
     if (dfvls.allowLinker) {
       y = dfvls.a["location"] && dfvls.a["location"].hash ? dfvls.a["location"].href.substring(dfvls.a["location"].href.indexOf("#")) : "";
       //b.setAllowAnchorで設定, anchorでの文字列があれば、Sに代入
       if (dfvls.allowAnchor && !NoVl(y)) S = y + "&";
       //search部分も引っ付ける
       S += dfvls.a["location"].search;
       //Sに_utmaが含まれれば
       if (!NoVl(S) && GaIndexOf(S, "__utma=")) {
         debugger;
         cookiemanager.setValuesAndG(S); //Sの値をcookieに入れて、utmk値をGにSet
         //verifyで、gが未設定か、urlの値がきちんとcookieに入ったかをHashでcheck
         //失敗なら、gはundefinedに、utmA..Z設定用変数の中身もclear
         cookiemanager.verify() || cookiemanager.Sb();
         G = cookiemanager.ja(); //_utma設定用変数から値をGに Sb()を実行した場合には、G="-"となる
       }
       //utmxのsetter, getter, cookiewriter, true(Decodeする)
       //utmx をcookieに書き込み
       n(cookiemanager.ma, cookiemanager.vb, cookiemanager.Eb, true);
       //utmv をcookieに書き込み
       n(cookiemanager.la, cookiemanager.Aa, cookiemanager.Fa);
     }
     //以上でAllowLinkerがtrueの時の作業終わり

     //通常(linkでない)の場合と、上記でutmaがなかった場合
     if (NoVl(G)){
       //Bは、domainHashに対応したutmaのcheck-flag
       if (B){
         //utmb, utmcが無い場合、新規セッション
         if (!O || !ma) {
           //utmaの値を現在時間で更新して代入
           G = setUtmaArray(cookievalue, ";", timenow);
           //b.Fは、キャンペーン処理での新規セッション判定に使われる
           b.F = true;
         //継続セッションの場合
         } else {
           //utmaの値を取得
           G = Q(cookievalue, "__utma=" + domainhash + ".", ";");
           //utmbをset, 配列にいれておく
           J = GaSplit(Q(cookievalue, "__utmb=" + domainhash, ";"), ".");
         }
       //utmaのドメインハッシュが一致しない
       }else {
         //新規にutma用の配列を作る
         G = GaJoin([domainhash, b.jc(), timenow, timenow, timenow, 1], ".");
         //新規セッションflag
         ha = b.F = true; //haはGaGlobalで使う
       }
     //utmaがあるけど、utmb or utmcがない=>新規セッション
     }else if (NoVl(cookiemanager.C()) || NoVl(cookiemanager.ka())) { //utmb, utmcのどちらかが、"-"だったとき
       //値を現在時間で更新してGに入れる
       G = setUtmaArray(S, "&", timenow);
       //新規セッションflagをtrueに
       b.F = true;
     //utma, utmb, utmcあり
     } else {
       //utmbをset
       J = GaSplit(cookiemanager.C(), ".");
       //utmbの先頭要素がドメインハッシュに?
       domainhash = J[0];
     }
     //G(utma), J(utmb)の設定終わり

     G = G.split(".");

     //gaGlobalがある時
     if (o && u && u.dh == domainhash && !dfvls.namespace) {
       G[4] = u.sid ? u.sid : G[4];
       if (ha) {
         G[3] = u.sid ? u.sid : G[4];
         if (u.vid) {
           timenow = u.vid.split("."); //from string to array
           G[1] = timenow[0];
           G[2] = timenow[1];
         }
       }
     }

     //utmaの値を、クッキーマネージャーオブジェクトにset
     cookiemanager.tb(G.join("."));
     //utmbをsetしていく

     J[0] = domainhash;
     J[1] = J[1] ? J[1] : 0;
     J[2] = undefined != J[2] ? J[2] : dfvls.Jc; //dfvls.Jc = 10
     J[3] = J[3] ? J[3] : G[4]; //session開始時間

     //utmbをset
     cookiemanager.ya(J.join("."));
     //utmcをset
     cookiemanager.ub(domainhash);
     //クッキーマネージャーオブジェクトの変数gがなければ、
     //cookiemanager.setG(cookiemanager.getAllStr())でutmkの値を入れる
     NoVl(cookiemanager.lc()) || cookiemanager.setG(cookiemanager.getAllStrToHash());
     //utmaをcookieに書き込み
     cookiemanager.Cb();
     //utmbをcookieに書き込み
     cookiemanager.Ea();
     //utmcをcookieに書き込み
     cookiemanager.Db();
   };
    //End of b.pc()