// ----------------------------------------------------------------------------

// Trident window object property faster

// ----------------------------------------------------------------------------

/*@cc_on eval((function(){var ps="document external self top parent setInterval clearInterval setTimeout clearTimeout".split(" ");var c=[];for(var i=0,l=ps.length,p=null;i<l;i++){p=ps[i];window["_"+p]=window[p];c.push(p+"=_"+p);}return "var "+c.join(",");})()); @*/





// ----------------------------------------------------------------------------

// CCC Namespace

// ----------------------------------------------------------------------------

var CCC = {};





// ----------------------------------------------------------------------------

// convArray: 与オブジェクトを配列に変換

// ----------------------------------------------------------------------------

CCC.convArray = function(obj) {

    if (obj instanceof Array) { return obj; }

    try {

        return Array.prototype.slice.call(obj);

    } catch (e) {

        // BUG IE8 Beta 2: Array.prototype.slice.call でエラー

        var nobj = obj.length;

        var ret  = new Array(nobj);

        for (var o = 0; o < nobj; o++) {

            ret[o] = obj[o];

        }

        return ret;

    }

};





// ----------------------------------------------------------------------------

// sanitize: 文字列の不要部分を削除

// ----------------------------------------------------------------------------

CCC.sanitize = (function() {

    var sol = /^[\s\t]+/; // 行頭空白

    var eol = /[\s\t]+$/; // 行末空白

    return function(str) {

        return str.replace(sol, "").replace(eol, "");

    };

})();





// ----------------------------------------------------------------------------

// ua: User Agent 情報

// ----------------------------------------------------------------------------

CCC.ua = (function() {

    // Trident は条件付コンパイルによる確実な判定を利用

    /*@cc_on @if (@_jscript) var type = "trident"; @else @*/

    // サポートプロパティによる判定

    var type = (undefined !== window.opera)         ? "opera"  :

               (undefined !== window.Components)    ? "gecko"  :

               (undefined !== window.defaultstatus) ? "webkit" : "unknown";

    /*@end @*/

    var ret = {

        // レンダリングエンジン是非 (是: true, 非: false)

        gecko   : false,

        opera   : false,

        webkit  : false,

        trident : false,

        unknown : false,

        // Quirks モード是非 (是: true, 非: false)

        quirks : ("BackCompat" == document.compatMode),

        // レンダリングエンジンバージョン (Opera & WebKit & Trident)

        version : ("opera"   == type) ? Number(window.opera.version()) :

                  ("webkit"  == type) ? document.evaluate ? 3 : 2      :

                  ("trident" == type) ?

                      ("undefined" != typeof document.documentMode) ? document.documentMode :

            ("undefined" != typeof external.SqmEnabled) ? 7 : 6 : 0,

        // ActiveX 有無 (有: true, 無: false)

        activex : ("undefined" != typeof ActiveXObject)

    };

    ret[type] = true; // レンダリングエンジン是非 設定

    return ret;

})();







// ----------------------------------------------------------------------------

// getClassNameRegExp: class 属性値 単一値取得用 RegExp オブジェクト生成

// ----------------------------------------------------------------------------

CCC.getClassNameRegExp = (function() {

    var cache = {}; // RegExp オブジェクトのキャッシュ

    return function(name) {

        if (undefined === cache[name]) {

            // キャッシュがない場合のみ新規生成

            cache[name] = new RegExp("(?:^|[\\s\\t]+)"+name+"(?:[\\s\\t]+|$)");

        }

        return cache[name];

    };

})();





// ----------------------------------------------------------------------------

// hasClassName: 要素が指定 class 属性値を持っているか

// ----------------------------------------------------------------------------

CCC.hasClassName = function(elem, name) {

    if ("string" != typeof name) { return false; }

    return CCC.getClassNameRegExp(name).test(elem.className);

};





// ----------------------------------------------------------------------------

// addClassName: 要素の class 属性値に指定値を追加

// ----------------------------------------------------------------------------

CCC.addClassName = function(elem, name) {

    if (CCC.hasClassName(elem, name)) { return; }

    elem.className = elem.className ? elem.className+" "+name : name;

};





// ----------------------------------------------------------------------------

// removeClassName: 要素の class 属性値から指定値を削除

// ----------------------------------------------------------------------------

CCC.removeClassName = function(elem, name) {

    if (!CCC.hasClassName(elem, name)) { return; }

    var className = CCC.sanitize(

        elem.className.replace(CCC.getClassNameRegExp(name), " ")

    );

    if (className) {

        elem.className = className;

    } else {

        // 値がなければ属性ごと削除

        elem.removeAttribute("class");

        // BUG IE6,7: "className" を操作しないと class 属性が変化しない

        elem.removeAttribute("className");

    }

};





// ----------------------------------------------------------------------------

// getElementsByClassName: class 属性値による要素収集

// ----------------------------------------------------------------------------

// Note:

//   速度向上のため NodeList オブジェクトなら Array に変換しておく

// ----------------------------------------------------------------------------

CCC.getElementsByClassName = (function() {

    // 実装されている場合: CCC へのラッピングのみ

    if (document.getElementsByClassName) {

         return function(parent, name) {

            return CCC.convArray(parent.getElementsByClassName(name));

         };

    // Selectors API が利用できる

    } else if (document.querySelectorAll) {

        return function(parent, name) {

            return CCC.convArray(parent.querySelectorAll("."+name));

        };

    // XPath が利用できる

    } else if (document.evaluate) {

        return function(parent, name) {

            var query = document.evaluate( // XPath で収集

                './/*[contains(concat(" ",@class," ")," '+name+' ")]',

                parent,

                null,

                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,

                null

            );

            var nquery = query.snapshotLength;

            var elems = new Array(nquery);

            for (var i = 0; i < nquery; i++) { // 配列に変換

                elems[i] = query.snapshotItem(i);

            }

            return elems;

        };

    // どの代替手段も利用できない: 線形探索

    } else {

        return function(parent, name) {

            var nodes = parent.getElementsByTagName("*");

            var nnodes = nodes.length;

            var elems = [];

            for (var n = 0; n < nnodes; n++) { // 全要素を線形探索

                if (CCC.hasClassName(nodes[n], name)) {

                    elems.push(nodes[n]);

                }

            }

            return elems;

        };

    }

})();







// ----------------------------------------------------------------------------

// vp: ViewPort 操作インタフェイス

// ----------------------------------------------------------------------------

CCC.vp = (function() {

    // ViewPort としてどの要素を利用しているか

    var elem = CCC.ua.quirks ? "body" : "documentElement";



    return {

        // ViewPort サイズ (スクロールバー除外)

        // 一般:

        // 標準 document.documentElement.clientWidth

        //      document.documentElement.clientHeight

        // 互換 document.body.clientWidth

        //      document.body.clientHeight

        // Op9.2:

        //   標準 document.body.clientWidth

        //        document.body.clientHeight

        //   互換 document.body.clientWidth

        //        document.body.clientHeight

        // Sf2:

        //   標準 window.innerWidth

        //        window.innerHeight

        //   互換 window.innerWidth

        //        window.innerHeight

        getInnerSize : function() {

            var w = 0;

            var h = 0;

            if (CCC.ua.webkit && CCC.ua.version < 3) {

                // Safari 2.x before

                w = window.innerWidth;

                h = window.innerHeight;

            } else if (CCC.ua.opera && CCC.ua.version < 9.5) {

                // Opera 9.2x before

                w = document.body.clientWidth;

                h = document.body.clientHeight;

            } else {

                // Anothers

                w = document[elem].clientWidth;

                h = document[elem].clientHeight;

            }

            return {width : w, height : h};

        },



        // フルページサイズ (スクロール含)

        getFullSize : function() {

            return {

                width  : document[elem].scrollWidth,

                height : document[elem].scrollHeight

            };

        },



        // スクロールオフセット

        getScrollPosition : function() {

            return {

                x : (window.pageXOffset || document[elem].scrollLeft),

                y : (window.pageYOffset || document[elem].scrollTop)

            };

        }

    };

})();





// ----------------------------------------------------------------------------

// event: 汎用イベント処理

// ----------------------------------------------------------------------------

// Note:

// Trident のメモリリークは、DOM ノード上にある循環参照要素に対しては修正された

// （DOM ノードへ append されていない要素が循環参照している場合は未修正）

// 故に、本イベントリスナラッパーはメモリリーク対策を講じていない

// See also:

// http://support.microsoft.com/kb/929874/

// http://www.microsoft.com/japan/msdn/ie/general/ie_leak_patterns.aspx

// http://d.hatena.ne.jp/zorio/20070626/1182875782

// http://d.hatena.ne.jp/zorio/20070918/1190135017

// http://ajaxian.com/archives/ie-memory-leaks-be-gone

// http://ajaxian.com/archives/ies-memory-leak-fix-greatly-exaggerated

// ----------------------------------------------------------------------------

CCC.event = {

    // --------------------------------

    // Public

    // --------------------------------

    // 追加

    add : function(node, type, listener, useCapture, _nowrap) {

        // _nowrap: 隠し引数

        //          true: listener のラッピングとストアを実行しない

        var wrap = _nowrap ? listener : this._wrapAfterCare(listener);

        var ret = wrap;

        if (this._origins[type]) {          // オリジナル

            ret = this._origins[type].add(node, wrap, useCapture);

        } else if (node.addEventListener) { // W3C DOM

            node.addEventListener(type, wrap, useCapture);

        } else if (node.attachEvent) {      // Trident

            node.attachEvent("on"+type, wrap);

        } else {

            ret = null;

        }

        if (!_nowrap && null !== ret && "unload" != type) {

            this._store(node, type, ret, useCapture);

        }

        return ret;

    },



    // 削除

    remove : function(node, type, listener, useCapture) {

        var ret = listener;

        if (this._origins[type]) {             // オリジナル

            ret = this._origins[type].remove(node, listener, useCapture);

        } else if (node.removeEventListener) { // W3C DOM

            node.removeEventListener(type, listener, useCapture);

        } else if (node.detachEvent) {         // Trident

            node.detachEvent("on"+type, listener);

        }

        return ret;

    },



    // イベントが発生したノードを取得

    getCurrentNode : function(event) {

        return event.currentTarget     ? event.currentTarget     :

               event.srcElement        ? event.srcElement        :

               window.event.srcElement ? window.event.srcElement : null;

    },



    // window オブジェクトの load イベント終了是非 (是: true, 非: false)

    windowLoaded : false,



    // --------------------------------

    // Private

    // --------------------------------

    // 種類別イベント一覧

    _types : {/* EMPTY */},



    // イベント毎の情報を設定

    _store : function(node, type, listener, useCapture) {

        if (undefined === this._types[type]) {

            this._types[type] = [];

        }

        this._types[type].push({

            node       : node,

            listener   : listener,

            useCapture : useCapture

        });

    },



    // オリジナルイベント一覧

    _origins : {/* EMPTY */}, // 後で追加



    // スタック生成

    _createStack : function(type) {

        // this._origins に間借りする形で領域生成

        if (undefined === this._origins[type]._callStack) {

            this._origins[type]._callStack = {

                id        : 0,

                listeners : {} // Array ではなく Object を利用

            };

        }

    },



    // スタック取得

    _getStack : function(type) {

        // プロパティの情報をカプセル化しておく

        return this._origins[type]._callStack;

    },



    // スタックに listener を追加

    _addStack : function(type, listener) {

        this._createStack(type);

        var stack = this._getStack(type);

        var id = stack.id;

        stack.listeners[id] = listener;

        stack.id++;

        return id; // id を listener として返す

    },



    // スタックから listener を削除

    _removeStack : function(type, id) {

        var stack = this._getStack(type);

        // 何もしないなら false を返す

        if (undefined === stack ||

            undefined === stack.listeners[id]) { return false; }

        delete stack.listeners[id];

        return true; // 何かをしたら true を返す

    },



    // スタックにある listener を全実行

    _runStack : function(type) {

        var stack = this._getStack(type);

        if (undefined === stack) { return; }

        for (var s in stack.listeners) {

            stack.listeners[s]();

        }

    },



    // イベント実行後のアフターケア処理をラッピング

    _wrapAfterCare : function(listener) {

        return function(event) {

            var ret = listener.apply(listener, arguments);

            if (false === ret) {

                // イベントリスナの戻り値が false なら各種キャンセル実行

                if (event.preventDefault) {    // 既定の動作を防止

                    event.preventDefault();    // Others

                } else {

                    event.returnValue = false; // Trident

                }

                if (event.stopPropagation) {   // イベント伝搬を停止

                    event.stopPropagation();   // Others

                } else {

                    event.cancelBubble = true; // Trident

                }

            }

        };

    }

};





// ------------------------------------

// メモリリーク防止

// ------------------------------------

CCC.event.add(window, "unload", function() {

    // add した全イベントに対して remove を実行

    var types = CCC.event._types;

    for (var type in types) {

        var list = types[type];

        var nlist = list.length;

        for (var l = 0; l < nlist; l++) {

            CCC.event.remove(list[l].node,

                             type,

                             list[l].listener,

                             list[l].useCapture);

        }

    }

}, false, true); // このイベントだけは _nowrap





// ------------------------------------

// CCC.event.windowLoaded 処理

// ------------------------------------

CCC.event.add(window, "load", function() {

    CCC.event.windowLoaded = true;

}, false);





// ------------------------------------

// 独自イベント: リサイズ (Trident ONLY)

// ------------------------------------

// BUG IE7,8: resize イベントで無限ループが発生しやすい

// ------------------------------------

CCC.event._origins.resize = (CCC.ua.trident && 7 <= CCC.ua.version) ? {

    add : (function() {

        // 実行許可フラグ (許可: true, 不許可: false)

        var enable = true;

        // setTimeout 用実行許可

        function changeEnable() {

            enable = true;

        }

        var time = 0;

        // このリスナだけを登録する

        window.attachEvent("onresize", function() {

            // 実行許可が出ている時のみ実行

            if (enable) {

                enable = false; // 実行不許可

                // 全登録リスナを一括実行

                CCC.event._runStack("resize");

                // このタイミングで setTimeout による遅延評価を行うと

                // 全登録リスナの実行後に changeEnable を評価する

                // 登録リスナの中に resize イベントを誘発するものがある

                // 場合、次の順序で評価が行われるらしい

                //   1. 元 resize

                //   2. 登録リスナ

                //   3. 登録リスナによって誘発された resize

                //   4. changeEnable

                // setTimeout を使わないと、登録リスナによって誘発

                // された resize の前に changeEnable が実行されてしまう

                setTimeout(changeEnable, 0); // 実行許可

            }

        });

        return function(node, listener, useCapture) {

            return CCC.event._addStack("resize", listener);

        };

    })(),

    remove : function(node, listener, useCapture) {

        return CCC.event._removeStack("resize", listener)

    }

} : undefined;





// ------------------------------------

// 独自イベント: フォントリサイズ

// ------------------------------------

CCC.event._origins.fontresize = {

    add : (function() {

        var run = false; // 実行フラグ

        return function(node, listener, useCapture) {

            var id = CCC.event._addStack("fontresize", listener);

            if (!run) {

                run = true;

                setInterval(function() { // 疑似イベント

                    if (CCC.style.isZoomed()) {

                        CCC.event._runStack("fontresize");

                    }

                }, 1000); // チェックインターバル (ms)

            }

            return id;

        };

    })(),

    remove : function(node, listener, useCapture) {

        return CCC.event._removeStack("fontresize", listener);

    }

};





// ----------------------------------------------------------------------------

// style: stylesheet オブジェクトリスト 操作インタフェイス

// ----------------------------------------------------------------------------

CCC.style = {

    // --------------------------------

    // Public

    // --------------------------------

    // title が付されたシートの連想配列を取得

    // return: {title 文字列 : [CSSStyleSheet オブジェクト, ...]

    getWithTitles : function() {

        // WebKit: Alternate Stylesheets を認識しない

        //         document.styleSheets を用いず別関数で対応

        var sheets = CCC.ua.webkit ? this._getList() : document.styleSheets;

        var nsheets = sheets.length;

        var ret = {}; // return するオブジェクト

        for (var i = 0; i < nsheets; i++) {

            var title = sheets[i].title;

            if (!title) { continue; } // title 属性値がなければ無視

            // 初回追加なら Array オブジェクトを生成

            if (undefined == ret[title]) { ret[title] = []; }

            ret[title].push(sheets[i]);

        }

        return ret;

    },



    // title 属性値 title のスタイル群に切り替え

    switchAlt : function(title) {

        var title2nodes = this.getWithTitles();

        if (undefined === title2nodes[title]) { return false; }

        for (var t in title2nodes) {

            var nodes = title2nodes[t];

            var nnodes = nodes.length;

            for (var n = 0; n < nnodes; n++) {

                nodes[n].disabled = true;

            }

        }

        var targetNodes = title2nodes[title];

        var ntargetNodes = targetNodes.length;

        for (var tn = 0; tn < ntargetNodes; tn++) {

            targetNodes[tn].disabled = false;

        }

        return true;

    },



    // 計算済スタイルプロパティ値

    getComputed : (function() {

        var customs = {

            height : {

                enable : (CCC.ua.trident ||

                          (CCC.ua.opera && CCC.ua.version < 9.5)),

                calc : function(elem, prop, ret) {

                    var p1 = parseInt(

                        this.getComputed(elem, "paddingTop")

                    ) || 0;

                    var p2 = parseInt(

                        this.getComputed(elem, "paddingBottom")

                    ) || 0;

                    // BUG IE7: hasLayout が false だと無条件で clientHeight が

                    //          0 になる場合がある (例: dt, dd 要素)

                    var ch;

                    if (CCC.ua.trident      &&

                        7 <= CCC.ua.version &&

                        !elem.currentStyle.hasLayout) {

                        // 強制的に hasLayout を true 化し clientHeight を算出

                        var zoom = elem.style.zoom;

                        elem.style.zoom = 1;

                        ch = elem.clientHeight;

                        elem.style.zoom = zoom;

                    } else {

                        ch = elem.clientHeight;

                    }

                    // clientXXX は padding を含むため除算

                    return (ch <= 0) ? 0 : (ch-p1-p2)+"px";

                }

            },

            fontSize : {

                enable : (CCC.ua.trident ||

                          (CCC.ua.webkit && CCC.ua.webkit < 3)),

                calc : function(elem, prop, ret) {

                    // Unit Element による判定

                    return this._getUnitElem().offsetWidth+"px";

                }

            }

        };

        if (document.defaultView && document.defaultView.getComputedStyle) {

            var dv = document.defaultView; // キャッシュ

            return function(elem, prop) {

                var ret = dv.getComputedStyle(elem, null)[prop];

                // カスタム計算

                if (customs[prop] && customs[prop].enable) {

                    ret = customs[prop].calc.apply(this, [elem, prop, ret]);

                }

                return ret;

            };

        } else if (document.documentElement &&

                   document.documentElement.currentStyle) {

            return function(elem, prop) {

                var ret = elem.currentStyle[prop];

                // カスタム計算

                if (customs[prop] && customs[prop].enable) {

                    ret = customs[prop].calc.apply(this, [elem, prop, ret]);

                // px 以外の数値

                } else if (!this._cond.px.test(ret) &&

                           this._cond.num.test(ret)) {

                    var jsss = elem.style.left;

                    var rtss = elem.runtimeStyle.left;

                    // left プロパティに値を詰め、pixelLeft で単位変換

                    elem.runtimeStyle.left = elem.currentStyle.left;

                    elem.style.left = ret || 0;

                    ret = elem.style.pixelLeft;

                    // 元の値に戻す

                    elem.style.left = jsss;

                    elem.runtimeStyle.left = rtss;

                }

                return ret;

            };

        } else {

            throw Error("not supported getting computed style");

        }

    })(),



    // ズーム是非 (是: true, 非: false)

    // フルページズーム、テキストズーム両方に対応

    // body 要素の font-size 増減にも対応 (スタイルスイッチ、直接変更など)

    isZoomed : (function() {

        // font-size の Computed Style + ViewPort サイズを利用

        // BUG Opera: ズーム時に Computed Style 値が再計算されない

        if (CCC.ua.opera) {

            var innserSize = 0; // ViewPort サイズ

            var fontSize   = 0; // font-size の Computed Style

            // ViewPort サイズがなぜか増減することを併用

            // 但し、何らかの DOM スタイル操作で ViewPort のスクロールバーが

            // 縦横同時に出現した場合、ズームされなくとも true を返す

            // font-size の Computed Style はスタイルスイッチ & 直接変更

            CCC.event.add(window, "load", function() {

                innerSize = CCC.vp.getInnerSize();

                fontSize  = CCC.style.getComputed(document.body, "fontSize");

            }, false);

            return function() {

                var nowInnerSize = CCC.vp.getInnerSize();

                var nowFontSize  = CCC.style.getComputed(document.body,

                                                         "fontSize");

                if (innerSize.width  == nowInnerSize.width  &&

                    innerSize.height == nowInnerSize.height &&

                    fontSize          == nowFontSize) { return false; }

                innerSize = nowInnerSize;

                fontSize  = nowFontSize;

                return true;

            };

        // font-size の Computed Style を利用

        } else {

            var size = 0;

            CCC.event.add(window, "load", function() {

                size = CCC.style.getComputed(document.body, "fontSize");

            }, false);

            return function() {

                var nowsize = CCC.style.getComputed(document.body, "fontSize");

                if (size == nowsize) { return false; }

                size = nowsize;

                return true;

            }

        }

    })(),



    // --------------------------------

    // Private

    // --------------------------------

    // 判定用正規表現

    _cond : {

        px  : /\d\s*px$/i,                  // px 単位

        num : /^\d/,                        // 数値

        rel : /(?:^|\s)stylesheet(?:\s|$)/i // rel 属性 stylesheet

    },



    // 単位取得用要素

    _unitElem : null,



    // 単位取得用要素取得

    _getUnitElem : function() {

        if (null === this._unitElem) {

            var elem = document.createElement("div");

            elem.style.display  = "block";

            elem.style.width    = "1em";

            elem.style.height   = "1em";

            elem.style.position = "absolute";

            elem.style.top      = "-999em";

            elem.style.left     = "-999em";

            document.body.appendChild(elem);

            this._unitElem = elem;

        }

        return this._unitElem;

    },



    // スタイルシート読込リストを取得 (link 要素のみ)

    // Sf2,3 Alternate StyleSheets バグ対応にのみ利用すること

    _getList : function() {

        var links = document.getElementsByTagName("link");

        var nlinks = links.length;

        var ret = [];

        for (var l = 0; l < nlinks; l++) {

            if (this._cond.rel.test(links[l].getAttribute("rel"))) {

                ret.push(links[l]);

            }

        }

        return ret;

    }

};





// ----------------------------------------------------------------------------

// Switcher: スタイルスイッチャ

// ----------------------------------------------------------------------------

CCC.style.Switcher = function(/* arguments */) {

    this.parent = null; // 基点要素 (親要素)

    this.targets = [    // 対象要素群

        // {node:対象要素, title:スタイルタイトル}, ...

    ];

    var obj = this;

    this.options = {    // オプション

        collect : obj._collectDefault // 対象要素群 収集関数

    };



    // CCC.Cookie オブジェクト

    this._cookie = new CCC.Cookie(this._COOKIE_STATUS.name,

                                  this._COOKIE_STATUS.optional);



    this.setOptions.apply(this, arguments);

};



CCC.style.Switcher.prototype = {

    // ------------------------------------

    // Public

    // ------------------------------------

    // オプション設定

    setOptions : function(parent, optional) {

        if (arguments.length < 1) { return; }

        this.parent = parent;

        if (null !== optional && "object" == typeof optional) {

            for (var o in this.options) {

                if (undefined === optional[o]) { continue; }

                this.options[o] = optional[o];

            }

        }

    },



    // 生成

    create : function(/* arguments */) {

        this.setOptions.apply(this, arguments);

        this._setTargets();

        this._setEvent();

        this._getCookie();

    },



    // スタイルを title に設定

    set : function(title) {

        CCC.style.switchAlt(title);

        this._setCookie(title);

    },



    // ------------------------------------

    // Private

    // ------------------------------------

    // Cookie 設定値

    _COOKIE_STATUS : {

        name     : "CCC.style.Switcher", // 名称

        key      : "title",              // 連想配列キー

        optional : {                     // 詳細設定

            path     : "/",

            fileUnit : false

        }

    },



    // 対象要素群 収集関数（既定）

    _collectDefault : function(parent) {

        // parent の子孫要素にある a 要素を収集

        // parent 自身が a 要素なら parent のみ収集

        var anchors = ("a" == parent.nodeName.toLowerCase()) ?

            [parent] : parent.getElementsByTagName("a");

        var nanchors = anchors.length;

        var targets = [];

        // タイトルを取得できた要素を収集

        for (var a = 0; a < nanchors; a++) {

            var attr = anchors[a].getAttribute("href");

            // ページ内アンカーの ID 値をタイトルに同定

            var index = attr.lastIndexOf("#");

            var title = (-1 == index) ? "" : attr.substring(index+1);

            if (title) {

                targets.push({node:anchors[a], title:title});

            }

        }

        return targets;

    },



    // 対象要素を収集・設定

    _setTargets : function() {

        this.targets = this.options.collect.call(this, this.parent);

    },



    // 対象要素に対しイベントを設定

    _setEvent : function() {

        var ntargets = this.targets.length;

        for (var t = 0; t < ntargets; t++) {

            CCC.event.add(this.targets[t].node,

                          "click",

                          this._getEventListener(t),

                          false);

        }

    },



    // イベントリスナ用クロージャを取得

    _getEventListener : function(id) {

        var obj = this;

        return function() {

            obj.set(obj.targets[id].title);

            return false;

        };

    },



    // Cookie 値を取得

    _getCookie : function() {

        var title = this._cookie.get(this._COOKIE_STATUS.key);

        if (title) {

            this.set(title);

        }

    },



    // Cookie 値を設定

    _setCookie : function(title) {

        this._cookie.set(this._COOKIE_STATUS.key, title);

    }

}; // END CCC.style.Switcher.prototype





// ----------------------------------------------------------------------------

// Cookie: クッキー制御

// ----------------------------------------------------------------------------

CCC.Cookie = function(/* arguments */) {

    this.name = "";  // 項目名

    this.params = {  // Cookie 設定可能パラメタ

        "path"    : "",

        "domain"  : "",

        "max-age" : 31536000, // 1年 (60*60*24*365)

        "secure"  : false

    };

    this.options = { // オプション

        fileUnit : true,        // ファイル単位で管理 (是: true, 非: false)

        index    : "index.html" // インデックスファイル名

    };

    this._nameCond = { // 項目名抽出条件

        str    : "",   // 文字列

        regexp : null  // 正規表現

    };



    this.setOptions.apply(this, arguments);

};



CCC.Cookie.prototype = {

    // ------------------------------------

    // Public

    // ------------------------------------

    // オプション設定

    setOptions : function(name, optional) {

        if (null !== optional && "object" == typeof optional) {

            for (var o in this.options) {

                if (undefined === optional[o]) { continue; }

                this.options[o] = optional[o];

            }

            for (var p in this.params) {

                if (undefined === optional[p]) { continue; }

                this.params[p] = optional[p];

            }

        }

        // this.options を利用するため設定後に実行

        this.setName(name);

    },



    // 項目名設定

    setName : function(name) {

        if (!name || "string" != typeof name) {

            throw Error("invalid cookie name ("+name+")");

        }

        // ファイル単位管理有効

        if (this.options.fileUnit) {

            var path = window.location.pathname; // ファイルパス

            // ファイルパスがディレクトリで、this.options.index に指定があれば

            // this.options.index を追加してアイテムを共有させる

            if (this._DIR_COND.test(path) && this.options.index) {

                path += this.options.index;

            }

            name += "@"+path; // 名前@パス

        } // ファイル単位管理無効なら処理しないだけ

        this.name = name;

        this._nameCond.str = name+"=";

        this._nameCond.regexp = new RegExp("^"+name+"=");

    },



    // データ取得

    get : function(key) {

        var all = this._getAll();

        return ("string" == typeof key) ? all[key] : all;

    },



    // データ保存

    set : function(key, value) {

        // キーと値が片方でもなければ何もしない

        if (undefined === key || undefined === value) { return; }

        var items = this._getAll();

        var values = [];

        items[key] = value; // 値をセット (既にある場合は上書き)

        for (var i in items) {

            if (undefined === items[i]) { continue; }

            // Cookie 格納可能形式にエンコード

            values.push(encodeURIComponent(i)+":"+encodeURIComponent(items[i]));

        }

        // 値がある時のみ実行

        if (0 < values.length) {

            document.cookie = this.name+"="+values.join(",")+this._getParamStr();

        }

    },



    // データ全削除

    remove : function() {

        var tmpAge = this.params["max-age"];

        this.params["max-age"] = 0;

        document.cookie = this.name+"="+this._getParamStr();

        this.params["max-age"] = tmpAge;

    },



    // ------------------------------------

    // Private

    // ------------------------------------

    // window.location.pathname 値がディレクトリか否か

    _DIR_COND : /\/$/i,



    // 文字列 slice 用デリミタ

    _DELIMITERS : {

        // 正規表現で \s を利用しているのは余分な空白文字を削除するため

        // 空白文字が残存していると key-value 認識がうまくいかない

        item  : /\s*;\s*/,  // Cookie 項目間

        value : /\s*\,\s*/, // key-value 単位間

        hash  : /\s*:\s*/   // key-value 間

    },



    // パラメタ設定変換

    _param2 : {

        "path" : {

            cond : function(v) { return v; }, // 条件

            conv : function(v) { return v; }  // 設定値変換

        },

        "domain" : {

            cond : function(v) { return v; },

            conv : function(v) { return v; }

        },

        "max-age" : {

            cond : function(v) { return !isNaN(v); },

            conv : function(v) { return v; }

        },

        "secure" : {

            cond : function(v) { return v; },

            conv : function(v) { return (v ? "sequre" : ""); }

        }

    },



    // 全データ取得

    _getAll : function() {

        var all = document.cookie;

        if (!all) { return {}; }

        var items = {};

        var datas = all.split(this._DELIMITERS.item);

        var ndatas = datas.length;

        for (var d = 0; d < ndatas; d++) {

            if (0 == datas[d].indexOf(this._nameCond.str)) {

                var values = datas[d].replace(this._nameCond.regexp, "")

                                     .split(this._DELIMITERS.value);

                var nvalues = values.length;

                for (var v = 0; v < nvalues; v++) {

                    var tmp = values[v].split(this._DELIMITERS.hash);

                    items[decodeURIComponent(tmp[0])] = decodeURIComponent(tmp[1]);

                }

                break;

            }

        }

        return items;

    },



    // オプションマージ済文字列取得

    _getParamStr : function() {

        var compats = []; // 有効なパラメタ集合

        for (var p in this.params) {

            // 条件に適合したパラメタのみ収集

            if (this._param2[p].cond(this.params[p])) {

                compats.push(p+"="+this._param2[p].conv(this.params[p]));

            }

        }

        // BUG IE, WebKit: max-age 未対応、expires を使うしかない

        var expires = this._getExpiresStr();

        if (expires) { compats.push(expires); }

        // 収集したパラメタを単一文字列に変換

        var str = compats.join(";");

        return (("" == str) ? "" : ";"+str);

    },



    // expires 設定用文字列取得

    _getExpiresStr : function() {

        // max-age から算出する

        var maxage = this.params["max-age"];

        if (isNaN(maxage)) { return ""; }

        var date = new Date();

        date.setTime(date.getTime() + maxage);

        return "expires="+date.toGMTString();

    }

}; // END CCC.Cookie.prototype





// ----------------------------------------------------------------------------

// Rollover: ロールオーバー

// ----------------------------------------------------------------------------

CCC.Rollover = function(/* arguments */) {

    // 対象要素集合

    this.targets = [

        // {element:要素, events:{イベント名:イベントリスナ ...}} ...

    ];

    // 有効にするクラス名

    this.enable = "";

    // オプション

    this.options = {

        // 無効にするクラス名

        disable : "",

        // 属性値変換対応

        switchers : {

            on  : {cond : /(\.[^\.]+)$/g, replace : "_o$1"},

            off : {cond : "",             replace : ""}

        }

    };



    this.setOptions.apply(this, arguments);

};



CCC.Rollover.prototype = {

    // ------------------------------------

    // Public

    // ------------------------------------

    // オプション設定

    setOptions : function(enable, optional) {

        if (arguments.length < 1) { return; }

        this.enable = enable;

        // 各種オプション値設定

        if (null !== optional && "object" == typeof optional) {

            for (var o in this.options) {

                if (undefined === optional[o]) { continue; }

                this.options[o] = optional[o];

            }

        }

    },



    // 生成

    create : function(/* arguments */) {

        this.setOptions.apply(this, arguments);

        this._setTargets();

        this._setEvents();

    },



    // ------------------------------------

    // Private

    // ------------------------------------

    // 対象にする要素の種類-条件対応

    _TYPES : (function() {

        // 要素 elem に対し、種類 type の効果をかけるクロージャを生成する

        // クロージャを生成

        function getEventGetter(type) {

            // this.targets への push が行われる時に実行される

            return function(elem) {

                var value = elem.getAttribute("src").replace(

                    this.options.switchers[type].cond,

                    this.options.switchers[type].replace

                );

                // このタイミングでキャッシュをとるのがベスト

                this._addCache(value);

                return function() {

                    elem.setAttribute("src", value);

                };

            };

        }



        // 要素 elem の子孫に img 要素に対し、種類 type の効果をかける

        // クロージャを生成するクロージャを生成

        function getDescendantEventGetter(type) {

            // this.targets への push が行われる時に実行される

            return function(elem) {

                var imgs = elem.getElementsByTagName("img");

                var nimgs = imgs.length;

                var getters = getEventGetter(type);

                var events = new Array(nimgs);

                for (var i = 0; i < nimgs; i++) {

                    events[0] = getters.call(this, imgs[i]);

                }

                return function() {

                    for (var i = 0; i < nimgs; i++) {

                        events[i]();

                    }

                };

            };

        }



        // 対応

        return {

            img : {

                isTarget : function() { return true; }, // 追加判定条件

                getters : { // イベントとイベントリスナ生成用クロージャ

                    mouseover : getEventGetter("on"),

                    mouseout  : getEventGetter("off")

                }

            },

            input : {

                isTarget : function(elem) {

                    return ("image" == elem.getAttribute("type"));

                },

                getters : {

                    mouseover : getEventGetter("on"),

                    mouseout  : getEventGetter("off"),

                    focus     : getEventGetter("on"),

                    blur      : getEventGetter("off")

                }

            },

            a : {

                isTarget : function(elem) {

                    var imgs = elem.getElementsByTagName("img");

                    return (0 < imgs.length);

                },

                getters : {

                    focus : getDescendantEventGetter("on"),

                    blur  : getDescendantEventGetter("off")

                }

            }

        };

    })(),



    // 自身 or 直近祖先要素の有効是非 (enable: true, disable: false)

    _isEnable : function(node) {

        // 無効にするクラス名が未指定なら無条件で enable

        if (!this.options.disable) { return true; }

        // 無効にするクラス名が指定されている

        do {

            // 同時指定時は disable 優先

            if (CCC.hasClassName(node, this.options.disable)) { return false; }

            if (CCC.hasClassName(node, this.enable)) { return true; }

            node = node.parentNode;

        } while (node);

        // 判別不能だった場合は例外とせず disable

        return false;

    },



    // 要素 elem 対象要素是非 (是: true, 非: false)

    _isTarget : function(elem) {

        var name = elem.nodeName.toLowerCase();

        if (undefined === this._TYPES[name]) { return false; }

        if (this._TYPES[name].isTarget(elem) && this._isEnable(elem)) {

            return true;

        }

        return false;

    },



    // 対象要素取得

    _getElements : function() {

        var ret = []; // 取得した要素の集合

        var targets = CCC.getElementsByClassName(document, this.enable);

        var ntargets = targets.length;



        for (var e = 0; e < ntargets; e++) {

            // 取得した要素自身が対象要素の場合もありうる -> 追加

            if (this._isTarget(targets[e])) {

                ret.push(targets[e]);

            }

            // 取得した要素の子孫要素に対し検索をかける

            for (var n in this._TYPES) {

                var childs = targets[e].getElementsByTagName(n);

                var nchilds = childs.length;

                for (var c = 0; c < nchilds; c++) {

                    if (this._isTarget(childs[c])) {

                        ret.push(childs[c]);

                    }

                }

            }

        }

        return ret;

    },



    // 対象要素とイベントリスナのコレクションを生成

    _setTargets : function() {

        var elems = this._getElements();

        var nelems = elems.length;

        for (var e = 0; e < nelems; e++) {

            var name = elems[e].nodeName.toLowerCase();

            var item = {element : elems[e], events : {}};

            var getters = this._TYPES[name].getters;

            for (var v in getters) {

                // call しないと this が違ったままなので注意

                item.events[v] = getters[v].call(this, elems[e]);

            }

            this.targets.push(item);

        }

    },



    // イベント設定

    _setEvents : function() {

        var ntargets = this.targets.length;

        for (var t = 0; t < ntargets; t++) {

            for (var type in this.targets[t].events) {

                CCC.event.add(this.targets[t].element,

                              type,

                              this.targets[t].events[type],

                              false);

            }

        }

    },



    // キャッシュ生成

    _addCache : (function() {

        // キャッシュした内容はクロージャに保存

        var caches = {};

        return function(src) {

            // 同一ファイルが既にキャッシュ済 -> キャッシュ不要

            // 但し、同一パスのみ対応する

            // 故に、例えば次のパスが全て同一ファイル示していても全てキャッシュ

            //    /img/test.png

            //    img/test.png

            //    ./img/test.png

            //    http://foo/img/test.png

            if (caches[src]) { return; }

            // img 要素を生成し、キャッシュに格納

            caches[src] = document.createElement("img");

            caches[src].setAttribute("src", src);

        };

    })()

}; // END CCC.Rollover.prototype







// ----------------------------------------------------------------------------

// Window: 新規ウインドウ生成

// ----------------------------------------------------------------------------

CCC.Window = function(/* arguments */) {

    this.parent = null;               // 起点要素 (親要素)

    this.targets = [                  // 対象要素群

        // {

        //     node : 対象要素,

        //     uri  : オープン URI,

        //     ref  : 開いたウインドウへのリファレンス

        // }

    ];

    var obj = this;

    this.options = {                  // オプション

        name    : "_blank",           // ウインドウ名

        collect : obj._collectDefault // 対象要素群 収集関数

    };

    this.params = {                   // ウインドウに渡すパラメタ

        // null: パラメタを渡さない

        // 状態

        left   : null, // 横位置

        top    : null, // 縦位置

        height : null, // 縦幅

        width  : null, // 横幅

        // 表示切替（非推奨）

        // "yes": 表示, "no": 非表示

        menubar  : "yes", // メニューバー

        toolbar  : "yes", // ツールバー

        location : "yes", // ロケーションバー

        status   : "yes"  // ステータスバー

    };



    this.setOptions.apply(this, arguments);

};



CCC.Window.prototype = {

    // ------------------------------------

    // Public

    // ------------------------------------

    // オプション設定

    setOptions : function(parent, optional) {

        if (arguments.length < 1) { return; }

        this.parent = parent;

        // 各種オプション値設定

        if (null !== optional && "object" == typeof optional) {

            for (var o in this.options) {

                if (undefined === optional[o]) { continue; }

                this.options[o] = optional[o];

            }

            for (var p in this.params) {

                if (undefined === optional[p]) { continue; }

                this.params[p] = this._normalizeParam(optional[p]);

            }

        }

    },



    // 生成

    create : function(/* arguments */) {

        this.setOptions.apply(this, arguments);

        this._setTargets();

        this._setEvents();

    },



    // ウインドウオープン

    open : function(id) {

        var ref = window.open(this.targets[id].uri,

                              this.options.name,

                              this._getParamStr());

        this.targets[id].ref = ref ? ref : null;

    },



    // ------------------------------------

    // Private

    // ------------------------------------

    // 変更不可パラメタ

    _IMMUTABLE_PARAMS : {

        resizable  : "yes",

        scrollbars : "yes"

    },



    // 要素名 - URI 取得関数対応

    _NODE2URI : {

        // 要素名 : URI 取得関数

        "a" : function(node) {

            return node.getAttribute("href");

        }

    },



    // 対象要素群 収集関数（既定）

    _collectDefault : function(parent) {

        // parent の子孫要素にある a 要素を収集

        // parent 自身が a 要素なら parent のみ収集

        return (("a" == parent.nodeName.toLowerCase()) ?

            [parent] : parent.getElementsByTagName("a")

        );

    },



    // 対象要素群 収集関数（外部リンク）

    _collectHTTP : function(parent) {

        var elems = [];

        if ("a" == parent.nodeName.toLowerCase()) { // parent 自身

            var href = parent.getAttribute("href");

            if (href && "http" == href.substring(0, 4)) {

                elems = [parent];

            }

        } else if (document.evaluate) {             // XPath が利用可能

            var query = document.evaluate(

                './/a[starts-with(@href, "http")]',

                parent,

                null,

                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,

                null

            );

            var nquery = query.snapshotLength;

            elems.length = nquery;

            for (var i = 0; i < nquery; i++) {

                elems[i] = query.snapshotItem(i);

            }

        } else {                                    // 線形探索

            var anchors = parent.getElementsByTagName("a");

            var nanchors = anchors.length;

            for (var a = 0; a < nanchors; a++) {

                var href = anchors[a].getAttribute("href");

                if (href && "http" == href.substring(0, 4)) {

                    elems.push(anchors[a]);

                }

            }

        }

        return elems;

    },



    // 対象要素を設定

    _setTargets : function() {

        var targets = this.options.collect.call(this, this.parent);

        var ntargets = targets.length;

        for (var t = 0; t < ntargets; t++) {

            var nodeName = targets[t].nodeName.toLowerCase();

            this.targets[t] = {

                node : targets[t],

                uri  : this._NODE2URI[nodeName](targets[t]),

                ref  : null

            };

        }

    },



    // 対象要素のイベントに open メソッドを設定

    _setEvents : function() {

        var ntargets = this.targets.length;

        for (var t = 0; t < ntargets; t++) {

            CCC.event.add(this.targets[t].node,

                          "click",

                          this._getEventListener(t),

                          false);

        }

    },



    // イベントリスナ用クロージャを取得

    _getEventListener : function(id) {

        var obj = this;

        return function() {

            obj.open(id);

            return false;

        };

    },



    // 実際に window.open へ与えるパラメタ文字列取得

    _getParamStr : function() {

        var ret = [];

        for (var p in this.params) {

            if (null === this.params[p]) { continue; }

            ret.push(p+"="+this.params[p]);

        }

        for (var i in this._IMMUTABLE_PARAMS) {

            ret.push(i+"="+this._IMMUTABLE_PARAMS[i]);

        }

        return ret.join(",");

    },



    // 与パラメタ値を正規化

    _normalizeParam : function(value) {

        return ((true === value) ? "yes" : (false === value) ? "no" : value);

    }

};



// ユーティリティ

CCC.Window.collectDefault = CCC.Window.prototype._collectDefault;

CCC.Window.collectHTTP = CCC.Window.prototype._collectHTTP;







// ----------------------------------------------------------------------------

// enable: 機能許可インタフェイス (簡易実行ラッパー)

// ----------------------------------------------------------------------------

CCC.enable = {

    // CCC.Rollover

    rollover : function(enable, optional) {

        var ret = new CCC.Rollover(enable, optional);

        ret.create();

        return ret;

    },



    // CCC.Flash

    flash : function(className, optional) {

        var elems = CCC.getElementsByClassName(document, className);

        var nelems = elems.length;

        var ret = new Array(nelems);

        for (var e = 0; e < nelems; e++) {

            var obj = new CCC.Flash(elems[e], optional);

            obj.create();

            ret.push(obj);

        }

        return ret;

    },



    // CCC.Window

    window : function(className, optional) {

        var elems = CCC.getElementsByClassName(document, className);

        var nelems = elems.length;

        var ret = new Array(nelems);

        for (var e = 0; e < nelems; e++) {

            var obj = new CCC.Window(elems[e], optional);

            obj.create();

            ret[e] = obj;

        }

        return ret;

    },



    // CCC.Tab

    tab : function(className, optional) {

        var elems = CCC.getElementsByClassName(document, className);

        var nelems = elems.length;

        var ret = new Array(nelems);

        for (var e = 0; e < nelems; e++) {

            var obj = new CCC.Tab(elems[e], optional);

            obj.create();

            obj.replace();

            ret[e] = obj;

        }

        return ret;

    },



    // CCC.style.Switcher

    styleSwitcher : function(className, optional) {

        var elems = CCC.getElementsByClassName(document, className);

        var nelems = elems.length;

        var ret = new Array(nelems);

        for (var e = 0; e < nelems; e++) {

            var obj = new CCC.style.Switcher(elems[e], optional);

            obj.create();

            ret[e] = obj;

        }

        return ret;

    },



    // CCC.HeightEqualizer

    heightEqualizer : function(className, optional) {

        var elems = CCC.getElementsByClassName(document, className);

        var nelems = elems.length;

        var ret = new Array(nelems);

        for (var e = 0; e < nelems; e++) {

            var obj = new CCC.HeightEqualizer(elems[e], optional);

            obj.create();

            ret[e] = obj;

        }

        return ret;

    }

};





CCC.event.add(window, "load", function(event) {

    CCC.enable.rollover("rollOver", {disable:"unRoll"});

}, false);







