/*! mjl.js
 * MITSUE-LINKS JavaScript Library
 * Version 2.0.3
 * Copyright (C) 2008 MITSUE-LINKS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
// ----------------------------------------------------------------------------
// 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(",");})()); @*/


// ----------------------------------------------------------------------------
// MJL Namespace
// ----------------------------------------------------------------------------
var MJL = {};


// ----------------------------------------------------------------------------
// version: バージョン情報
// ----------------------------------------------------------------------------
MJL.version = "2.0.3";


// ----------------------------------------------------------------------------
// inherit: 継承の実現
// ----------------------------------------------------------------------------
MJL.inherit = function(target, parent) {
    // __proto__ を用いた方法
    if (undefined !== target.__proto__ &&
        undefined !== target.prototype.__proto__) {
        // __proto__ を用いてプロトタイプチェーンへ直接接続する
        target.prototype.__proto__ = parent.prototype;
        target.__proto__ = parent;
        // 同じプロトタイプチェーンに接続されているなら成功とみなす
        if (parent.prototype.isPrototypeOf &&
            parent.prototype.isPrototypeOf(target.prototype)) {
            return target;
        }
        // 念のため、余分に付与された __proto__ は delete しない
        // ReadOnly で上書き不可の場合があったらマズいため
    }
    // __proto__ が使えない場合（= 失敗した場合）
    // new による方法
    target.prototype = new parent();
    try {
        // parent の constructor になったままなので元に戻す
        target.prototype.constructor = target;
    } catch (e) {
        throw Error("can't substitution 'constructor': "+e);
    }
    return target;
};


// ----------------------------------------------------------------------------
// convArray: 与オブジェクトを配列に変換
// ----------------------------------------------------------------------------
MJL.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: 文字列の不要部分を削除
// ----------------------------------------------------------------------------
MJL.sanitize = (function() {
    var sol = /^[\s\t]+/; // 行頭空白
    var eol = /[\s\t]+$/; // 行末空白
    return function(str) {
        return str.replace(sol, "").replace(eol, "");
    };
})();


// ----------------------------------------------------------------------------
// ua: User Agent 情報
// ----------------------------------------------------------------------------
MJL.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;
})();


// ----------------------------------------------------------------------------
// convNode: 与テキストを DOM Node に変換
// ----------------------------------------------------------------------------
MJL.convNode = function(text) {
    // 生成される要素数に応じて返値が変化
    //  1: ノード自身
    // 他: Document Fragment に格納された並列ノード
    var tmp = document.createElement("div");
    tmp.innerHTML = text; // 現状は innerHTML を利用
    var childs = tmp.childNodes;
    var nchilds = childs.length;
    var ret = null;
    if (nchilds <= 1) {
        ret = childs[0];
    } else {
        ret = document.createDocumentFragment();
        for (var c = 0; c < nchilds; c++) {
            ret.appendChild(childs[0]);
        }
    }
    return ret;
};


// ----------------------------------------------------------------------------
// getClassNameRegExp: class 属性値 単一値取得用 RegExp オブジェクト生成
// ----------------------------------------------------------------------------
MJL.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 属性値を持っているか
// ----------------------------------------------------------------------------
MJL.hasClassName = function(elem, name) {
    if ("string" != typeof name) { return false; }
    return MJL.getClassNameRegExp(name).test(elem.className);
};


// ----------------------------------------------------------------------------
// addClassName: 要素の class 属性値に指定値を追加
// ----------------------------------------------------------------------------
MJL.addClassName = function(elem, name) {
    if (MJL.hasClassName(elem, name)) { return; }
    elem.className = elem.className ? elem.className+" "+name : name;
};


// ----------------------------------------------------------------------------
// removeClassName: 要素の class 属性値から指定値を削除
// ----------------------------------------------------------------------------
MJL.removeClassName = function(elem, name) {
    if (!MJL.hasClassName(elem, name)) { return; }
    var className = MJL.sanitize(
        elem.className.replace(MJL.getClassNameRegExp(name), " ")
    );
    if (className) {
        elem.className = className;
    } else {
        // 値がなければ属性ごと削除
        elem.removeAttribute("class");
        // BUG IE6,7: "className" を操作しないと class 属性が変化しない
        elem.removeAttribute("className");
    }
};


// ----------------------------------------------------------------------------
// getElementsByClassName: class 属性値による要素収集
// ----------------------------------------------------------------------------
// Note:
//   速度向上のため NodeList オブジェクトなら Array に変換しておく
// ----------------------------------------------------------------------------
MJL.getElementsByClassName = (function() {
    // 実装されている場合: MJL へのラッピングのみ
    if (document.getElementsByClassName) {
         return function(parent, name) {
            return MJL.convArray(parent.getElementsByClassName(name));
         };
    // Selectors API が利用できる
    } else if (document.querySelectorAll) {
        return function(parent, name) {
            return MJL.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 (MJL.hasClassName(nodes[n], name)) {
                    elems.push(nodes[n]);
                }
            }
            return elems;
        };
    }
})();


// ----------------------------------------------------------------------------
// getElementsByChildNodes: 子要素収集
// ----------------------------------------------------------------------------
MJL.getElementsByChildNodes = (function() {
    // XPath が利用できる
    if (document.evaluate) {
        return function(parent, nodeName, eq) {
            var query = document.evaluate( // XPath で収集
                !nodeName      ? './*'                          :
                (false === eq) ? './*[not(self::'+nodeName+')]' : './'+nodeName,
                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, nodeName, eq) {
            // childNodes のうち、要素 (ELEMENT_NODE) のみを収集
            var nodes  = parent.childNodes;
            var nnodes = nodes.length;
            var elems = [];
            if (false !== eq) { eq = true; }
            for (var n = 0; n < nnodes; n++) {
                if (1 == nodes[n].nodeType &&
                    (!nodeName ||
                     eq == (nodeName == nodes[n].nodeName.toLowerCase()))
                ) {
                    elems.push(nodes[n]);
                }
            }
            return elems;
        };
    }
})();


// ----------------------------------------------------------------------------
// vp: ViewPort 操作インタフェイス
// ----------------------------------------------------------------------------
MJL.vp = (function() {
    // ViewPort としてどの要素を利用しているか
    var elem = MJL.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 (MJL.ua.webkit && MJL.ua.version < 3) {
                // Safari 2.x before
                w = window.innerWidth;
                h = window.innerHeight;
            } else if (MJL.ua.opera && MJL.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
// ----------------------------------------------------------------------------
MJL.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
                }
            }
        };
    }
};


// ------------------------------------
// メモリリーク防止
// ------------------------------------
MJL.event.add(window, "unload", function() {
    // add した全イベントに対して remove を実行
    var types = MJL.event._types;
    for (var type in types) {
        var list = types[type];
        var nlist = list.length;
        for (var l = 0; l < nlist; l++) {
            MJL.event.remove(list[l].node,
                             type,
                             list[l].listener,
                             list[l].useCapture);
        }
    }
}, false, true); // このイベントだけは _nowrap


// ------------------------------------
// MJL.event.windowLoaded 処理
// ------------------------------------
MJL.event.add(window, "load", function() {
    MJL.event.windowLoaded = true;
}, false);


// ------------------------------------
// 独自イベント: フォントリサイズ
// ------------------------------------
// trident では resize イベントを利用
//   -> setInterval ではかなり CPU リソースを消費するため
// ------------------------------------
MJL.event._origins.fontresize = {
    add : (function() {
        if (MJL.ua.trident) {
            return function(node, listener, useCapture) {
                return MJL.event.add(MJL.style._getUnitElem(),
                                     "resize",
                                     listener,
                                     useCapture);
            };
        } else {
            var run = false; // 実行フラグ
            return function(node, listener, useCapture) {
                var id = MJL.event._addStack("fontresize", listener);
                if (!run) {
                    run = true;
                    setInterval(function() { // 疑似イベント
                        if (MJL.style.isZoomed()) {
                            MJL.event._runStack("fontresize");
                        }
                    }, 1000); // チェックインターバル (ms)
                }
                return id;
            };
        }
    })(),
    remove : (function() {
        if (MJL.ua.trident) {
            return function(node, listener, useCapture) {
                return MJL.event.remove(MJL.style._getUnitElem(),
                                        "resize",
                                        listener,
                                        useCapture);
            };
        } else {
            return function(node, listener, useCapture) {
                return MJL.event._removeStack("fontresize", listener);
            };
        }
    })()
};


// ----------------------------------------------------------------------------
// style: stylesheet オブジェクトリスト 操作インタフェイス
// ----------------------------------------------------------------------------
MJL.style = {
    // --------------------------------
    // Public
    // --------------------------------
    // title が付されたシートの連想配列を取得
    // return: {title 文字列 : [CSSStyleSheet オブジェクト, ...]
    getWithTitles : function() {
        // WebKit: Alternate Stylesheets を認識しない
        //         document.styleSheets を用いず別関数で対応
        var sheets = MJL.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 : (MJL.ua.trident ||
                          (MJL.ua.opera && MJL.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;
                // clientXXX は padding を含むため除算
                return (elem.clientHeight-p1-p2)+"px";
                }
            },
            fontSize : {
                enable : (MJL.ua.trident ||
                          (MJL.ua.webkit && MJL.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 (MJL.ua.opera) {
            var innserSize = 0; // ViewPort サイズ
            var fontSize   = 0; // font-size の Computed Style
            // ViewPort サイズがなぜか増減することを併用
            // 但し、何らかの DOM スタイル操作で ViewPort のスクロールバーが
            // 縦横同時に出現した場合、ズームされなくとも true を返す
            // font-size の Computed Style はスタイルスイッチ & 直接変更
            MJL.event.add(window, "load", function() {
                innerSize = MJL.vp.getInnerSize();
                fontSize  = MJL.style.getComputed(document.body, "fontSize");
            }, false);
            return function() {
                var nowInnerSize = MJL.vp.getInnerSize();
                var nowFontSize  = MJL.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;
            MJL.event.add(window, "load", function() {
                size = MJL.style.getComputed(document.body, "fontSize");
            }, false);
            return function() {
                var nowsize = MJL.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: スタイルスイッチャ
// ----------------------------------------------------------------------------
MJL.style.Switcher = function(/* arguments */) {
    this.parent = null; // 基点要素 (親要素)
    this.targets = [    // 対象要素群
        // {node:対象要素, title:スタイルタイトル}, ...
    ];
    var obj = this;
    this.options = {    // オプション
        collect : obj._collectDefault // 対象要素群 収集関数
    };

    // MJL.Cookie オブジェクト
    this._cookie = new MJL.Cookie(this._COOKIE_STATUS.name,
                                  this._COOKIE_STATUS.optional);

    this.setOptions.apply(this, arguments);
};

MJL.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) {
        MJL.style.switchAlt(title);
        this._setCookie(title);
    },

    // ------------------------------------
    // Private
    // ------------------------------------
    // Cookie 設定値
    _COOKIE_STATUS : {
        name     : "MJL.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++) {
            MJL.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 MJL.style.Switcher.prototype


// ----------------------------------------------------------------------------
// Cookie: クッキー制御
// ----------------------------------------------------------------------------
MJL.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);
};

MJL.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 MJL.Cookie.prototype


// ----------------------------------------------------------------------------
// Rollover: ロールオーバー
// ----------------------------------------------------------------------------
MJL.Rollover = function(/* arguments */) {
    // 対象要素集合
    this.targets = [
        // {element:要素, events:{イベント名:イベントリスナ ...}} ...
    ];
    // 有効にするクラス名
    this.enable = "";
    // オプション
    this.options = {
        // 無効にするクラス名
        disable : "",
        // 属性値変換対応
        switchers : {
            on  : {cond : /(\.[^\.]+)$/g, replace : "_on$1"},
            off : {cond : "",             replace : ""}
        }
    };

    this.setOptions.apply(this, arguments);
};

MJL.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 (MJL.hasClassName(node, this.options.disable)) { return false; }
            if (MJL.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 = MJL.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) {
                MJL.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 MJL.Rollover.prototype


// ----------------------------------------------------------------------------
// Flash: Flash プラグイン インタフェイス
// ----------------------------------------------------------------------------
MJL.Flash = function(/* arguments */) {
    this.node    = null;   // DOM ノード
    this.alt     = null;   // 代替要素
    this.options = {       // オプション
        activate  : false, // IE 用アクティブ化是非 (是: true, 非: false)
        version   : 0,     // 最小対応バージョン
        minVerMsg : null   // 指定未満バージョン時メッセージ
    };
    this.params  = {     // param 要素 name/value 属性値対
        // name : value
    };
    // 正常な object 要素の出力是非 (是: true, 非: false)
    this.validCreated = false;
    this.setOptions.apply(this, arguments);
};

MJL.Flash.prototype = {
    // ------------------------------------
    // Public
    // ------------------------------------
    // MIME Type
    type : "application/x-shockwave-flash",
    // Plugin URL
    pluginurl : "http://www.adobe.com/go/getflashplayer",
    // Class ID (ActiveX)
    classid : "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000",
    // Code Base (ActiveX)
    codebase : "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab",

    // バージョン
    // 形式：{major:Number, minor:Number, revision:Number, debug:Number}
    version : (function() {
        var str = "";
        try {         // NPAPI 互換
            str = navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin.description;
            str.match(/(\d+)\.(\d+)(?:\s*[r\.](\d+))?(?:\s*[bd](\d+))?$/); // バージョン抽出
        } catch (e) { // ActiveX
            try {
                // 7 以降
                str = (new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7")).GetVariable("$version");
            } catch (e) {
                try {
                    // 6.x
                    var plugin = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
                    str = "6,0,21,0"; // 6.x first
                    // 6.0.22 - 6.0.29 では GetVariable("$version") でクラッシュ
                    // 6,0,47 以上なら AllowScriptAccess が利用可能
                    // AllowScriptAccess で例外を投げさせて回避
                    plugin.AllowScriptAccess = "always";
                    str = plugin.GetVariable("$version");
                } catch (e) {
                    try {
                        // 5.x, 4.x
                        // 3.x は GetVariale() で例外を投げる
                        str = (new ActiveXObject("ShockwaveFlash.ShockwaveFlash")).GetVariable("$version");
                    } catch (e) {
                        // 3.x 以前はサポートしない
                        str = ""; // "6,0,21,0" をリセット
                    }
                }
            }
            str.match(/(\d+),(\d+),(\d+),(\d+)/); // バージョン抽出
        }
        var version = {major:0, minor:0, revision:0, debug:0};
        if (str) {
            // 数値変換
            version.major = parseInt(RegExp.$1) || 0;
            version.minor = parseInt(RegExp.$2) || 0;
            version.revision = parseInt(RegExp.$3) || 0;
            version.debug    = parseInt(RegExp.$4) || 0;
        }
        return version;
    })(),

    // Plugin 有効/無効 (有効: true, 無効: false)
    enable : (function() {
        try {
            return !!navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin;
        } catch (e) {
            try {
                return !!(new ActiveXObject("ShockwaveFlash.ShockwaveFlash"));
            } catch (e) {
            }
        }
        return false;
    })(),

    // バージョン比較
    compVersion : function(major, minor, revision, debug) {
        // 小数点以下切捨
        major    = parseInt(major);
        minor    = parseInt(minor);
        revision = parseInt(revision);
        debug    = parseInt(debug);
        // プラグインバージョン < 比較バージョン ->  1
        // プラグインバージョン = 比較バージョン ->  0
        // プラグインバージョン > 比較バージョン -> -1
        return ((this.version.major < major)       ?  1 :
                (this.version.major > major)       ? -1 :
                isNaN(minor)                       ?  0 :
                (this.version.minor < minor)       ?  1 :
                (this.version.minor > minor)       ? -1 :
                isNaN(revision)                    ?  0 :
                (this.version.revision < revision) ?  1 :
                (this.version.revision > revision) ? -1 :
                isNaN(debug)                       ?  0 :
                (this.version.debug < debug)       ?  1 :
                (this.version.debug > debug)       ? -1 : 0);
    },

    // オプション設定
    setOptions : function(node, optional) {
        if (arguments.length < 1) { return; }
        if (1 != node.nodeType || "object" != node.nodeName.toLowerCase()) {
            // object 要素でなければ不正
            throw Error("invalid 'object' element node: "+elem);
        }
        this.node = node;
        this.validCreated = false;
        if (null !== optional && "object" == typeof optional) {
            for (var o in this.options) { // オプション抽出
                if (undefined === optional[o]) { continue; }
                this.options[o] = optional[o];
            }
        }
        this._setParams();
        this._setOptionsByParams(); // optional 引数に優先する
    },

    // 要素生成
    create : function(/* arguments */) {
        this.setOptions.apply(this, arguments);
        this._switchNode();
        this._activate();
        return this.node;
    },

    // ------------------------------------
    // Private
    // ------------------------------------
    // param 要素を抽出し、name/value 属性値を this.params に設定
    _setParams : function() {
        var params = MJL.getElementsByChildNodes(this.node, "param");
        var nparams = params.length;
        for (var p = 0; p < nparams; p++) {
            this.params[params[p].getAttribute("name")] =
                params[p].getAttribute("value");
        }
    },

    // param 要素 name/value からオプションを抽出
    _setOptionsByParams : function() {
        for (var o in this.options) {
            if (undefined === this.params[o]) { continue; }
            this.options[o] = this.params[o];
        }
    },

    // ノード切替
    _switchNode : function() {
        // 条件分岐による切替ノード分岐
        if (this.enable) {
            if (this.compVersion(this.options.version) <= 0) {
                this.validCreated = true; // 正常な object 要素を出力
                return;
            }
            // 代替コンテンツ表示ケース
            this.alt = (this.options.minVerMsg) ? this._createMinVerMsg()
                                                : this._createAlt();
            var alt = this.alt;
            var node = this.node;
            // ノード切替
            this.node = this.alt;
            // 一番時間がかかる処理を並列に
            setTimeout(function() {
                // object 要素内の代替要素と object 要素自身を置換
                node.parentNode.replaceChild(alt, node);
            }, 0);
        } // plugin 無効なら何もしない
    },

    // 特別ノード生成
    _createMinVerMsg : function() {
        // String -> DOM 文字列
        // その他 -> DOM ノード
        return ("string" == typeof this.options.minVerMsg) ?
            MJL.convNode(this.options.minVerMsg) : this.options.minVerMsg
    },

    // 代替コンテンツ取得
    _createAlt : (function() {
        if (MJL.ua.trident) { // Trident
            return function() {
                // BUG IE6,7: childNodes で代替コンテンツが取得不能
                // BUG IE7: 条件コメント <![endif]--> はテキストノードとして
                //          レンダリングされてしまう（未解決）
                // innerHTML を使うと代替コンテンツのみ取得可能
                return MJL.convNode(this.node.innerHTML);
            };
        } else {              // Others
            return function() {
                var df = document.createDocumentFragment();
                var elems = MJL.getElementsByChildNodes(this.node,
                                                        "param",
                                                        false);
                var nelems = elems.length;
                for (var e = 0; e < nelems; e++) {
                    df.appendChild(elems[e].cloneNode(true));
                }
                return df;
            };
        }
    })(),

    // IE ONLY: ActiveX アクティブ化
    _activate : function() {
        // ActiveX アクティブ化は、KB945007 累積パッチ群適用コンピュータには
        // 不要となった
        // see also: http://support.microsoft.com/kb/945007
        // BUG IE6,7: object 要素を DOM ツリーへ追加後に ActiveX をアクティブ
        //            にしないとレンダリングしない
        // see also: http://www.microsoft.com/japan/msdn/workshop/author/dhtml/overview/activating_activex.aspx#loading
        if (MJL.ua.activex && this.options.activate && this.validCreated) {
            this._setCopyObject();
            // ActiveX アクティブ化スイッチは classid 属性値
            // （ないし type 属性値）
            this.node.setAttribute("classid", this.classid);
       }
    },

    // IE ONLY: object 要素のコピーを設定
    _setCopyObject : function() {
        // clone ノードでは ActiveX アクティブ化ができない
        var obj = document.createElement("object");
        // 属性の全移植
        var attrs = this.node.attributes;
        var nattrs = attrs.length;
        for (var a = 0; a < nattrs; a++) {
            // 空文字列と "null" の場合は何もしない
            if ("" == attrs[a].value || "null" === attrs[a].value ||
                // ActiveX アクティブ化スイッチは入れないようにする
                "type" == attrs[a].name || "classid" == attrs[a].name) {
                continue;
            }
            obj.setAttribute(attrs[a].name, attrs[a].value);
        }
        // 子ノードの全移植
        var childs = this.node.childNodes;
        var nchilds = childs.length;
        for (var c = 0; c < nchilds; c++) {
            obj.appendChild(childs[c].cloneNode(true));
        }
        // DOM ツリー上にある object 要素と置換
        this.node.parentNode.replaceChild(obj, this.node);
        this.node = obj;
    }
}; // END MJL.Flash.prototype

// ユーティリティ
// コンストラクタ外にあるため new 時にコピーされない
MJL.Flash.version = MJL.Flash.prototype.version;
MJL.Flash.enable = MJL.Flash.prototype.enable;
MJL.Flash.compVersion = MJL.Flash.prototype.compVersion;


// ----------------------------------------------------------------------------
// Window: 新規ウインドウ生成
// ----------------------------------------------------------------------------
MJL.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);
};

MJL.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++) {
            MJL.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);
    }
};

// ユーティリティ
MJL.Window.collectDefault = MJL.Window.prototype._collectDefault;
MJL.Window.collectHTTP = MJL.Window.prototype._collectHTTP;


// ----------------------------------------------------------------------------
// Tab: タブインタフェイス生成
// ----------------------------------------------------------------------------
MJL.Tab = function(/* arguments */) {
    this.container = null;  // コンテンツコンテナノード
    this.content   = null;  // パネルを包含するノード (メインドキュメント)
    this.list      = null;  // タブリストノード (タブになる部分)
    this.id        = "";    // this.content ノードに付与された id 属性値
    this.activeId  = "";    // 現在アクティブなパネルの ID
    this.stat      = false; // 静的モード是非 (是: true, 非: false)
    // class 属性値対応
    this.classes = {
        container : "tabContainer", // コンテンツコンテナ
        list      : "tabList",      // タブリスト
        panel     : "tabPanel",     // パネル
        title     : "tabTitle",     // タイトル
        active    : "active",       // アクティブ状態
        stat      : "static"        // 静的モード
    };
    // オプション
    var obj = this;
    this.options = {
        // Cookie による状態保存有無 & Cookie オプション
        //   保存なし: null, 保存あり: 連想配列（MJL.Cookie 第2引数）
        cookie  : {},
        collect : obj._collectDefault // 内容要素群 収集関数
    };
    // id - アイテム集合対応
    this.items = {
        // id : { // 内容要素 id 属性値
        //    panel : パネルノード
        //    title   : タイトルノード (dynamic ONLY)
        //    list    : タブリストの該当ノード
        //    event   : イベントのトリガにするノード
        // }, ...
    };
    this.nitems      = 0;         // 内容要素数
    this._cookie     = null;      // MJL.Cookie オブジェクト
    this._cookieName = "MJL.Tab"; // Cookie 項目名

    this.setOptions.apply(this, arguments);
};

MJL.Tab.prototype = {
    // ------------------------------------
    // Public
    // ------------------------------------
    // オプション設定
    setOptions : function(content, optional) {
        if (arguments.length < 1) { return; }
        this.content = content;
        this.id = content.getAttribute("id");
        // 各種オプション値設定
        if (null !== optional && "object" == typeof optional) {
            for (var o in this.options) {
                if (undefined === optional[o]) { continue; }
                this.options[o] = optional[o];
            }
        }
        // id がない場合は cookie 制御ができないため強制 false
        if (!this.id) {
            this.options.cookie = null;
        }
    },

    // DOM ノード生成
    create : function(/* arguments */) {
        this.setOptions.apply(this, arguments);
        this._distStatic();
        this._getContents();
        this._createContainer();
        this._createList();
        this._setEvents();
        this._createCookie();
        // 対象要素 (this.content) との置換はまだ行わない
    },

    // 内容ノードとコンテナノードを置換
    replace : function() {
        var pivot = document.createComment(""); // ノード位置保存用ピボット
        var root  = this.content.parentNode;    // replaceChild の基点
        // 描画回数を2回にするため、一度 DOM ツリーから外す
        root.replaceChild(pivot, this.content);
        // パネルに class 属性値を付与
        for (var i in this.items) {
            MJL.addClassName(this.items[i].panel, this.classes.panel);
        }
        if (this.stat) {
            // 静的モード：戻す
            root.replaceChild(this.content, pivot);
        } else {
            // 動的モード：生成ノードを追加し、コンテンツコンテナノードを戻す
            this.container.appendChild(this.list);
            this.container.appendChild(this.content);
            root.replaceChild(this.container, pivot);
        }
        this.active();
    },

    // アクティブタブ＆コンテンツ切替
    active : function(id) {
        var aid = this._getActiveId();
        var c = this.classes.active;
        if (id == aid) {                   // アクティブ ID と同一
            return;   // 何もしない
        } else if (!this._isValidId(id)) { // 対象 ID が不正
            id = aid; // 現在のアクティブ ID を使用
        }
        // アクティブ ID のタブとパネルを非アクティブに
        MJL.removeClassName(this.items[aid].list, c);
        MJL.removeClassName(this.items[aid].panel, c);
        // 対象 ID のタブとパネルをアクティブに
        MJL.addClassName(this.items[id].list, c);
        MJL.addClassName(this.items[id].panel, c);
        // アクティブ ID を対象 ID に変更
        this._setActiveId(id);
        // Cookie 値設定
        this._setCookie();
    },

    // ------------------------------------
    // Private
    // ------------------------------------
    // 自動生成 id 属性値接頭語
    _ID_PREFIX : "MJL_TAB_ITEM_",

    // URI 文字列から id 属性値を抽出する際の置換用正規表現
    _URI2ID : /^[^#]*#/,

    // 内容要素群 収集関数（既定）
    _collectDefault : function(parent) {
        // parent の子要素を収集
        return MJL.getElementsByChildNodes(parent);
    },

    // 静的モード判定
    _distStatic : function() {
        this.stat = MJL.hasClassName(this.content, this.classes.stat);
        if (this.stat) {
            // 静的モードなら class 属性値削除
            // CSS ON, JS OFF の時、情報は伝達するように
            // ex) .tabContainer .tabs.static {position:static;}
            MJL.removeClassName(this.content, this.classes.stat);
        }
    },

    // 適正な ID 値の是非 (是: true, 非: false)
    _isValidId : function(id) {
        return ("" != id && undefined !== this.items[id]);
    },

    // アクティブ ID 取得
    _getActiveId : function() {
        //
        // 以降の処理は初回のみ実行される
        // 2回目以降は、末尾で上書きする2回目以降用関数が利用される
        //
        // マークアップから取得したアクティブ ID
        var activeIdByMarkup = this._getActiveIdByMarkup();
        //
        // アクティブ ID 優先順位
        //   Cookie > URI > 静的マークアップ > 先頭アイテム
        //
        // Cookie から取得
        var activeId = this._getCookie();
        if (!this._isValidId(activeId)) {         // Cookie からの取得値が不正
            // URI から取得
            var hash = window.location.hash;
            activeId = hash ? hash.replace(this._URI2ID, "") : "";
            if (!this._isValidId(activeId)) {     // URI からの取得値が不正
                // 静的マークアップから取得
                activeId = activeIdByMarkup;
                if (!this._isValidId(activeId)) { // 静的マークアップからの
                                                  // 取得値が不正
                    // 先頭アイテムの ID を取得
                    for (var id in this.items) {
                        activeId = id;
                        break;
                    }
                }
            }
        }
        this._setActiveId(activeId);
        // 自己を2回目以降用メソッドで上書き
        this._getActiveId = this._getActiveIdBySelf;
        return activeId;
    },

    // マークアップからアクティブ ID を取得
    _getActiveIdByMarkup : function() {
        var activeId = "";
        for (var id in this.items) {
            if (MJL.hasClassName(this.items[id].panel,
                                 this.classes.active)) {
                activeId = id;
                break;
            }
        }
        // アクティブ ID がある場合：アクティブアイテム クラス名削除
        if (activeId) {
            MJL.removeClassName(this.items[id].panel, this.classes.active);
            if (this.stat) {
                // 静的モードの時だけリストの該当要素からクラス名削除
                MJL.removeClassName(this.items[id].list, this.classes.active);
            }
        }
        return activeId;
    },

    // _getActiveId 2回目以降の実行用
    _getActiveIdBySelf : function() {
        return this.activeId;
    },

    // アクティブ ID 設定
    _setActiveId : function(id) {
        if (this._isValidId(id)) {
            this.activeId = id;
        } // id が不正なら何もしない
    },

    // ID 取得
    _getId : (function() {
        // id 属性値用インデックス値
        // 同一文書内での同一 ID 使用は禁止 (id 属性値の仕様)
        //   -> idIndex のインクリメントで対応
        var idIndex = 0;
        return function(elem) {
            var id = "";
            // 静的コンテンツ: id 属性値取得
            if (this.stat) {
                id = elem.getAttribute("id");
                if (!id) {
                    // id 属性値の不在はありえない
                    throw Error("invalid id attribute value '"+id+"'");
                }
            // 動的コンテンツ: 生成
            } else {
                id = this._ID_PREFIX+idIndex;
                idIndex++;
            }
            return id;
        };
    })(),

    // a 要素 href 属性値から ID を取得
    _getIdByHref : function(elem) {
        var href = elem.getAttribute("href");
        if (!href) {
            // href の値がないのはありえない
            throw Error("invalid href attribure value '"+href+"'");
        }
        var id = href.replace(this._URI2ID, "");
        if (!this._isValidId(id)) {
            // ID の不正はありえない
            throw Error("invalid reference ID '"+id+"' in '"+href+"'");
        }
        return id;
    },

    // 内容ノードから id - アイテム集合を取得
    _getContents : function() {
        var targets = this.options.collect.call(this, this.content);
        var ntargets = targets.length;
        for (var t = 0; t < ntargets; t++) {
            // アイテム生成
            var id = this._getId(targets[t]);
            if (undefined === this.items[id]) {
                this.items[id] = {
                    panel : targets[t],
                    title : this._getTitle(targets[t]),
                    list  : null,
                    event : null
                };
            } else {
                throw Error("overlapping id value '"+id+"'");
            }
        }
        this.nitems = ntargets;      // 個数も保存
    },

    // タブタイトル要素取得
    _getTitle : function(elem) {
        var title = "";
        if (this.stat) {
            // 何もしない
        } else {
            var titles = MJL.getElementsByClassName(elem, this.classes.title);
            if (titles.length < 1) {
                // タブタイトルの不在はありえない
                throw Error("not found title-use element");
            }
            title = titles[0];
        }
        return title;
    },

    // タブタイトル要素の子孫ノードを全クローン
    _cloneTitle : function(id) {
        var df = document.createDocumentFragment();
        var childs = this.items[id].title.childNodes;
        var nchilds = childs.length;
        for (var c = 0; c < nchilds; c++) {
            df.appendChild(childs[c].cloneNode(true));
        }
        return df; // Document Fragment をそのまま使う
    },

    // コンテナノード生成
    _createContainer : function() {
        var cont = null;
        if (this.stat) {
            cont = this.content.parentNode;
            // 最大で root まで辿る
            while (cont && !MJL.hasClassName(cont, this.classes.container)) {
                cont = cont.parentNode;
            }
            if (!cont) {
                // コンテナノードの不在はありえない
                throw Error("not found tab container element");
            }
        } else {
            cont = document.createElement("div");
            MJL.addClassName(cont, this.classes.container);
        }
        this.container = cont;
    },

    // タブリストノード生成
    _createList : function() {
        var list = null;
        if (this.stat) {
            list = MJL.getElementsByClassName(this.container,
                                              this.classes.list)[0];
            if (!list) {
                // タブリストノードの不在はありえない
                throw Error("not found tab list element");
            }
            var childs = MJL.getElementsByChildNodes(list);
            var nchilds = childs.length;
            if (nchilds != this.nitems) {
                // タブリストノード項目数と内容ノード項目数が異なってはならない
                throw Error("not equal tab list items ("+this.nitems+") and contents ("+nchilds+")");
            }
            for (var c = 0; c < nchilds; c++) {
                var a = this._getEventElement(childs[c]);
                // href 属性値から ID を抽出し
                // 対応する id - アイテム集合対応のアイテムに値を与える
                var id = this._getIdByHref(a);
                this.items[id].list = childs[c];
                this.items[id].event = a;
            }
        } else {
            list = document.createElement("ul");
            MJL.addClassName(list, this.classes.list);
            for (var id in this.items) {
                var li = document.createElement("li");
                var a = document.createElement("a");
                a.setAttribute("href", '#'+id); // ページ内リンク生成
                list.appendChild(li)
                    .appendChild(a)
                    .appendChild(this._cloneTitle(id));
                this.items[id].list = li;
                this.items[id].event = a;
            }
        }
        this.list = list;
    },

    // イベントトリガ用要素取得 (static ONLY)
    _getEventElement : function(parent) {
        var elem = parent.getElementsByTagName("a")[0];
        if (!elem) {
            // イベント設定要素の不在はありえない
            throw Error("not found valid event element");
        }
        return elem;
    },

    // 全イベントトリガ用要素にイベントを設定
    _setEvents : function() {
        for (var i in this.items) {
            MJL.event.add(this.items[i].event,
                          "click",
                          this._getEventListener(i),
                          false);
        }
    },

    // イベントリスナ用クロージャを取得
    _getEventListener : function(id) {
        var obj = this;
        return function() {
            obj.active(id);
            return false; // ページ内遷移防止
        };
    },

    //
    // Cookie
    //
    // Cookie 生成
    _createCookie : function() {
        if (null === this.options.cookie || null !== this._cookie) { return; }
        this._cookie = new MJL.Cookie(this._cookieName, this.options.cookie);
    },

    // Cookie 値設定
    _setCookie : function() {
        if (null === this.options.cookie) { return; }
        this._cookie.set(this.id, this._getActiveId());
    },

    // Cookie 値取得
    _getCookie : function() {
        return (
            (null === this.options.cookie) ? "" : this._cookie.get(this.id)
        );
    }
}; // END MJL.Tab.prototype


// ----------------------------------------------------------------------------
// HeightEqualizer: 指定要素等高
// ----------------------------------------------------------------------------
MJL.HeightEqualizer = function(/* arguments */) {
    this.parent  = null;               // 起点要素 (親要素)
    this.targets = [];                 // 対象要素群
    var obj = this;
    this.options = {                   // オプション
        groupBy : 0,                   // グルーピング要素数
        collect : obj._collectDefault, // 対象要素群 収集関数
        resize  : true                 // 自動リサイズ (ON: true, OFF: false)
    };
    this._listeners = {                // イベントリスナ
        // イベント名 : MJL.event.add 返値
        resize     : null,
        fontresize : null
    };

    this.setOptions.apply(this, arguments);
};

MJL.HeightEqualizer.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.targets = this.options.collect.call(this, this.parent);
        this.set();
        this._setAutoResize();
    },

    // スタイル設定
    set : function() {
        // 一度スタイル開放しないと前回の設定値により通常フロー時の値がとれない
        this.release();
        var heights = this._getHeights();
        var ntargets = this.targets.length;
        for (var t = 0; t < ntargets; t++) {
            this.targets[t].style.height = heights[t];
        }
    },

    // スタイル開放
    release : function() {
        var ntargets = this.targets.length;
        for (var t = 0; t < ntargets; t++) {
            this.targets[t].style.height = "";
        }
    },

    // ------------------------------------
    // Private
    // ------------------------------------
    // 設定する単位
    _UNIT : "px",

    // 対象要素群 収集関数（既定）
    _collectDefault : function(parent) {
        // parent の子要素を収集
        return MJL.getElementsByChildNodes(parent);
    },

    // 各要素のレンダリング後 height 最大値取得
    _getHeights : function(elem) {
        var ntargets = this.targets.length;
        var heights = new Array(ntargets); // 対象要素の height 集合
        // あらかじめ height を全計算させる
        for (var t = 0; t < ntargets; t++) {
            // レンダリング後 height 値を取得 (単位 px, 数値のみ)
            heights[t] = parseInt(
                MJL.style.getComputed(this.targets[t], "height")
            );
        }
        var groupBy = this.options.groupBy;
        var max = 0; // height 最大値
        // groupBy 無効範囲
        // groupBy == 1 の場合は意味喪失するため groupBy 無効と判断
        if (groupBy < 2 || ntargets <= groupBy) {
            // 全対象要素に対する height 最大値
            max = Math.max.apply(Math, heights) + this._UNIT;
            for (var t = 0; t < ntargets; t++) {
                heights[t] = max;
            }
        // groupBy 有効範囲
        // 2 <= groupBy && groupBy < ntargets
        } else {
            for (var t = 0; t < ntargets; t++) {
                // groupBy 個ずつの height 最大値
                if (0 == t % groupBy) {
                    max = Math.max.apply(Math, heights.slice(t, t+groupBy))
                        + this._UNIT;
                }
                heights[t] = max;
            }
        }
        return heights;
    },

    // 自動リサイズ設定
    _setAutoResize : function() {
        if (this.options.resize) {
            var obj = this;
            for (var l in this._listeners) {
                this._listeners[l] = MJL.event.add(window, l, function() {
                    obj.set();
                }, false);
            }
        }
    }
}; // END MJL.HeightEqualizer.prototype


// ----------------------------------------------------------------------------
// enable: 機能許可インタフェイス (簡易実行ラッパー)
// ----------------------------------------------------------------------------
MJL.enable = {
    // MJL.Rollover
    rollover : function(enable, optional) {
        var ret = new MJL.Rollover(enable, optional);
        ret.create();
        return ret;
    },

    // MJL.Flash
    flash : function(className, optional) {
        var elems = MJL.getElementsByClassName(document, className);
        var nelems = elems.length;
        var ret = new Array(nelems);
        for (var e = 0; e < nelems; e++) {
            var obj = new MJL.Flash(elems[e], optional);
            obj.create();
            ret.push(obj);
        }
        return ret;
    },

    // MJL.Window
    window : function(className, optional) {
        var elems = MJL.getElementsByClassName(document, className);
        var nelems = elems.length;
        var ret = new Array(nelems);
        for (var e = 0; e < nelems; e++) {
            var obj = new MJL.Window(elems[e], optional);
            obj.create();
            ret[e] = obj;
        }
        return ret;
    },

    // MJL.Tab
    tab : function(className, optional) {
        var elems = MJL.getElementsByClassName(document, className);
        var nelems = elems.length;
        var ret = new Array(nelems);
        for (var e = 0; e < nelems; e++) {
            var obj = new MJL.Tab(elems[e], optional);
            obj.create();
            obj.replace();
            ret[e] = obj;
        }
        return ret;
    },

    // MJL.style.Switcher
    styleSwitcher : function(className, optional) {
        var elems = MJL.getElementsByClassName(document, className);
        var nelems = elems.length;
        var ret = new Array(nelems);
        for (var e = 0; e < nelems; e++) {
            var obj = new MJL.style.Switcher(elems[e], optional);
            obj.create();
            ret[e] = obj;
        }
        return ret;
    },

    // MJL.HeightEqualizer
    heightEqualizer : function(className, optional) {
        var elems = MJL.getElementsByClassName(document, className);
        var nelems = elems.length;
        var ret = new Array(nelems);
        for (var e = 0; e < nelems; e++) {
            var obj = new MJL.HeightEqualizer(elems[e], optional);
            obj.create();
            ret[e] = obj;
        }
        return ret;
    }
};


//popUp
function Open_the_Window(url){
var winop = window.open(url,"subwindow_sample03","status=yes");
winop.window.focus();
}
