// ****************************************************************
// prealporti.js - antauxsistemo de limigi vorton
// 見出し語の絞り込み処理のフロントエンド（AJAXによる実装）

// Auxtorrajto
//     (c) 2006-2008 Organizo por Zona Servo per Sinkrona Solvo
//     Cxiuj rajtoj estas rezervitaj.
// Copyright
//     (c)2006-2008 Organization for Zonal Service with Synchronous Solution
//     All rights reserved.

// 1.0.0 - 2007/04/20 - 作成
// 1.0.1 - 2007/05/19 - リファクタリング
// 1.1.0 - 2007/05/20 - バックエンドシステムへのa要素によるリンクに対応
// 1.1.1 - 2007/05/21 - 呼び出し元HTMLのname不使用リファクタリングに対応
// 1.2.0 - 2007/05/21 - 状態遷移を結果表示部に背景色等を用いて表示
// 1.2.1 - 2007/05/21 - 連続打鍵時の「通信失敗」誤表示を抑止
// 1.2.2 - 2007/05/23 - 操作感統一・説明容易性のための1文字入力時挙動変更
// 2.0.0 - 2007/07/14 - Submission Throtting Patternに準拠
// 2.0.1 - 2007/09/25 - 見出し語検索失敗時には「造語を含む」を選ぶ旨を追記
// 3.0.0 - 2008/01/17 - 辞書引きv2.0.0に合わせてクエリ組み立てを変更
// 3.1.0 - 2008/01/26 - 検索語の強調表示
// 3.1.1 - 2008/01/30 - 同条件再検索防止・カーソル位置変更・通信中明示
// 3.2.1 - 2008/02/05 - 強調表示の正規表現を再考し、強調の誤りを是正
// 4.0.0 - 2008/02/06 - 代用表記の字上符付き文字への自動変換・強調を高速化
// 4.0.1 - 2008/02/07 - 通信中の増殖防止・同条件再検索復活（進む・戻る対応）
// 4.1.0 - 2008/02/09 - 置換不実行時は検索ボックス上書きしない（カーソル保持）
// 4.2.0 - 2008/02/09 - 置換実行時もカーソル（キャレット）位置を保持
// 4.3.0 - 2008/02/09 - 上記を文章内単語訳にも対応
// 4.4.0 - 2008/06/01 - Shift+Enterで文章内単語訳をsubmit
// 4.5.0 - 2008/06/02 - onsubmitとform.submit時に入力欄の色を変える

// ****************************************************************

// ================================================================
// 大域変数
// ----------------------------------------------------------------
    var subprema_periodo        = 40;       // 実行間隔
    var laste_demanda_vorto     = "";       // 最後に検索を行った際の入力語
    var laste_demanda_akordeco  = "";       // 最後に検索を行った際の適合型
    var xmlhttp                 = faruXmlHttpPostulon();    // XMLHttpRequest
    var minimuma_longeco        = 2;        // 絞り込みを実行可能な最小文字数
    var maksimuma_nombro        = 250;      // 最大絞り込み結果数
    var programo_de_vorto       = "/vorto/";
                                            // 辞書引き表示のバックエンド
    var programo_de_kapvorto    = "/kapvorto/";
                                            // 見出し語検索表示のバックエンド
    var programo_de_limgilo     = "/limigilo/";
                                            // 絞り込みのバックエンド
    var nediviga_signo          = '__';     // 任意文字列指定（後方一致:^.*等）

    var estas_tro_multa                     // 結果の制限超過の文面
        = "<p>Estas tro multa.</p>\n"
        + "<p>" + maksimuma_nombro + "件以上の候補がありました。"
        + "結果が多すぎるので、もう少し絞り込んでください。</p>";
    var komunikata          = '<p id="komunikata"><em>通信中……</em></p>';

// ================================================================
// komenco / start-up
// 開始処理
// ----------------------------------------------------------------
function komenco () {
    // 入力欄にフォーカスを合わせる
    // 「document.konsulti.vorto.focus();」……では、不可。
    // HTML側でnameを使う必要があり、XHTML 1.0的には許容されない。以下同じ。
    var formo;

    if (document.getElementById("vorto")) {
        if (xmlhttp == null) {
            document.getElementById("limigito").innerHTML
                = "<p>辞書の見出し語の絞り込みに非対応のブラウザです。</p>";
            exit;
        }
        formo = document.getElementById("vorto");
    }
    else {
        formo = document.getElementById("frazo");
    }

    formo.focus();              // フォーカスをあわせてから
    formo.value = formo.value;  // 文字列を挿入し、末尾にカーソルを置く

    // ショートカットキー
    if (document.getElementById("frazo")) {
        document.getElementById("frazo").onkeypress = ligiSimbole;
    }

    iteracioDeDemandiVorton();

    return;
}

// ================================================================
// iteracio de demandi vorton / iteration of query word
// 見出し語の絞り込み処理：リクエストオプション組み込み・発行
// ----------------------------------------------------------------
function iteracioDeDemandiVorton () {

    var traduki             = document.getElementById("traduki");
    var konsulti            = document.getElementById("konsulti");
    var akordeco            = document.getElementById("akordeco");

    if (traduki) {
        // 字上符付き文字への変換（した場合の検索ボックスの置換）等
        var demandaFrazo = traduki.frazo.value;
        demandaFrazo = transformuAlfabeton(demandaFrazo);
        if (
            traduki.frazo.value != demandaFrazo &&
            ! window.opera  // orz
        ) {
            anstatauxiguDemandajnVicojn(traduki.frazo, demandaFrazo);
        }
    }

    if (konsulti) {
        // 検索語変更・一致条件変更時にのみ稼働
        if (
            konsulti.vorto.value    != laste_demanda_vorto      ||
            konsulti.akordeco.value != laste_demanda_akordeco
        ) {
            limiguVorton(konsulti);
        }
    }

    setTimeout(
        'iteracioDeDemandiVorton()', subprema_periodo
    );

    return;
}

// ================================================================
// limigi vorton / limit word
// 見出し語の絞り込み処理：リクエストオプション組み込み・発行
// ----------------------------------------------------------------
function limiguVorton (formo) {
    var limigito            = document.getElementById("limigito");

    if (document.getElementById("akordeco_kompleta").selected) {
        formo.transsendiVorton.value = "辞書引き";
    }
    else {
        formo.transsendiVorton.value = "絞り込み";
    }

    laste_demanda_vorto     = formo.vorto.value;
    laste_demanda_akordeco  = formo.akordeco.value;

    // HTTPリクエストを発行
    // 空なら……ではなくて、規定文字数以上でなければ、何もしない
    if (xmlhttp.readyState > 0) xmlhttp.abort();

    if (document.getElementById("vorto").value.length >= minimuma_longeco) {
        // 字上符付き文字への変換（した場合の検索ボックスの置換）等
        var demandaVorto = formo.vorto.value;
        demandaVorto = transformuAlfabeton(demandaVorto);
        if (formo.vorto.value != demandaVorto) {
            anstatauxiguDemandajnVicojn(formo.vorto, demandaVorto);
            return;
        }
        demandaVorto = seninfektiguVorton(demandaVorto);

        // マッチオプション（前置・後置・併置・非置）とクエリ
        var path_info
            = document.getElementById("akordeco_kompleta").selected   ?
                                 encodeURI(demandaVorto)
            : document.getElementById("akordeco_antauxa").selected    ?
                                 encodeURI(demandaVorto) + nediviga_signo
            : document.getElementById("akordeco_malantauxa").selected ?
                nediviga_signo + encodeURI(demandaVorto)
            : document.getElementById("akordeco_parta").selected      ?
                nediviga_signo + encodeURI(demandaVorto) + nediviga_signo
            :
                                 encodeURI(demandaVorto)
            ;
        xmlhttp.open(
            "GET",
            programo_de_limgilo + path_info,
            true
        );

        // コールバック関数（サーバからレスポンスがあったときの処理）を設定
        xmlhttp.onreadystatechange = function () { vidiguRezultanVortojn(); }

        xmlhttp.send(
            null
        );
    }
    else if (formo.vorto.value.length != 0) {
        // 説明容易性のため、1文字のみ入力しても検索するふりをする実装にする
        // サーバへの通信は省略するという割り切りである
        limigito.style.backgroundColor  = "#fff";
        limigito.innerHTML  = estas_tro_multa;
    }
    else {
        limigito.style.borderColor      = "#cec";
        limigito.style.backgroundColor  = "#fff";
        limigito.innerHTML
            = "<p>検索条件（検索語や一致条件）に応じて、"
            + "画面遷移なしに辞書の見出し語を絞り込めます。</p>";
    }

    return;
}

// ================================================================
// anstatauxigu demandajn vicojn / replace query strings
// フォームの検索語の置換（字上符付き文字へ変換した内容の反映）
// ----------------------------------------------------------------
function anstatauxiguDemandajnVicojn (formo, vorto) {
    // カーソル（キャレット）位置を保持するように配慮
    // 単純に上書きすると末尾になるためである
    var komencaPozicio;
    if (formo.createTextRange) {
        // IE, Opera
        var vortaAmplekso = document.selection.createRange();

        var kursoraAmplekso;
        if (formo.type == "text") {
            kursoraAmplekso = formo.createTextRange();
        }
        else if (formo.type == "textarea") {
            kursoraAmplekso = document.body.createTextRange();
            kursoraAmplekso.moveToElementText(formo);
            kursoraAmplekso = kursoraAmplekso.duplicate();  // 味噌
        }
        else {
            return;
        }
        kursoraAmplekso.setEndPoint('EndToStart', vortaAmplekso);
        komencaPozicio = kursoraAmplekso.text.length - 1;

        formo.value = vorto;

        vortaAmplekso.move('character', komencaPozicio);
        vortaAmplekso.select();
    }
    else if (formo.setSelectionRange) {
        // Firefox
        komencaPozicio = formo.selectionStart - 1;

        formo.value = vorto;

        formo.setSelectionRange(komencaPozicio, komencaPozicio);
    }

    return;
}

// ================================================================
// vidigi rezultatajn vortojn / display result words
// 見出し語の絞り込み処理：リクエストオプション組み込み・発行
// ----------------------------------------------------------------
function vidiguRezultanVortojn () {
    var limigito            = document.getElementById("limigito");
    var ne_estas_trovebla                   // 見出し非存在の文面
        = "<p>Ne estas trovebla.</p>\n"
        + "<p>辞書の見出し語には見つかりませんでしたが、"
        + "造語（派生語・合成語）である可能性があります。"
        + "<a href=\""
        + programo_de_vorto
        + document.getElementById("vorto").value
        + ".html\"><em>"
        + document.getElementById("vorto").value
        + "</em>の造語を含めた辞書引き"
        + "</a>をお試しください。</p>"
        ;

    // 「通信中……」
    if (! document.getElementById("komunikata")) {
        limigito.innerHTML = komunikata + limigito.innerHTML;
    }
    limigito.style.borderColor      = "#696";
    limigito.style.backgroundColor  = "#adb";

    try {
        if (xmlhttp.readyState == 4) {      // 通信自体が成功
            if (xmlhttp.status == 200) {    // 正常にデータの取得が完了
                // 取り敢えずJSONではなくて生テキストを使う
                var teksto  = xmlhttp.responseText;

                if (teksto == "") {         // ヒットしていない
                    limigito.style.backgroundColor  = "#fff";
                    limigito.innerHTML  = ne_estas_trovebla;
                }
                else if (teksto == "0") {   // 多すぎた
                    limigito.style.backgroundColor  = "#fff";
                    limigito.innerHTML  = estas_tro_multa;
                }
                else {                      // ヒットした
                    limigito.style.backgroundColor  = "#cfd";
                    limigito.innerHTML
                        = akiruRezultatanRegistron(teksto);
                }
            }
            else if (xmlhttp.status) {      // サーバエラー
            // 単にelseにすると、連続打鍵時に処理がオーバフローした場合も通る
                limigito.style.backgroundColor  = "#fff";
                limigito.innerHTML = akiruEraranMesagxon(xmlhttp.status);
            }
        }
    }
    catch(e) {                              // 通信失敗
        limigito.style.backgroundColor  = "#fff";
        limigito.innerHTML = akiruEraranMesagxon();
    }

    return;
}

// ================================================================
// akiri rezultatan registron / get result list
// 結果リストの取得
// ----------------------------------------------------------------
function akiruRezultatanRegistron (teksto) {
    var duigilo             = ";";                      // 結果の区切り文字
    var listo               = teksto.split(duigilo);    // 結果の配列化
    var listo_sen_disigilo  = listo.concat();           // 値コピー

    // 取得したデータからHTMLを生成
    var html    = "<p>辞書の見出し語を"
                + listo.length
                + "件に絞り込みました。</p>"
                + "<ol>";

    // リンク先から語根区切りを除く
    for (var i = 0; i < listo_sen_disigilo.length; i++) {
        listo_sen_disigilo[i] = listo_sen_disigilo[i].replace(/\//g, "");
    }

    // 強調表示のための正規表現構築
    var vorto
        = document.getElementById("vorto").value.toLowerCase();
    vorto = transformuAlfabeton(vorto);

    vorto = vorto.replace(/^\//g, '(?:^|\/|\\s|-)');
    vorto = vorto.replace(/\/$/g, '(?:$|\/|\\s|-)');
    vorto = "(" + vorto + ")";
    var re = new RegExp("");
    re.compile(vorto);

    for (var i = 0; i < listo.length; i++) {
        listo[i].match(re);
        listo[i]
            = RegExp.leftContext
            + "<em>" + RegExp.$1 + "</em>"
            + RegExp.rightContext;

        html    += "<li>"
                + "<a href=\""
                + programo_de_vorto
                + listo_sen_disigilo[i] + ".html"
                + "\">" + listo[i] + "</a></li>";
    }

    html        += "</ol>";

    return html;
}

// ================================================================
// transformu alfabeton / transform alphabet
// 代用表記を正書法へ変換する
// ----------------------------------------------------------------
function transformuAlfabeton (vorto) {
    vorto = vorto.replace(/\^C|C[\^HhXx]/g, String.fromCharCode(0x108));
    vorto = vorto.replace(/\^c|c[\^HhXx]/g, String.fromCharCode(0x109));
    vorto = vorto.replace(/\^G|G[\^HhXx]/g, String.fromCharCode(0x11c));
    vorto = vorto.replace(/\^g|g[\^HhXx]/g, String.fromCharCode(0x11d));
    vorto = vorto.replace(/\^H|H[\^HhXx]/g, String.fromCharCode(0x124));
    vorto = vorto.replace(/\^h|h[\^HhXx]/g, String.fromCharCode(0x125));
    vorto = vorto.replace(/\^J|J[\^HhXx]/g, String.fromCharCode(0x134));
    vorto = vorto.replace(/\^j|j[\^HhXx]/g, String.fromCharCode(0x135));
    vorto = vorto.replace(/\^S|S[\^HhXx]/g, String.fromCharCode(0x15c));
    vorto = vorto.replace(/\^s|s[\^HhXx]/g, String.fromCharCode(0x15d));
    vorto = vorto.replace(/\^U|U[\^HhXx]/g, String.fromCharCode(0x16c));
    vorto = vorto.replace(/\^u|u[\^HhXx]/g, String.fromCharCode(0x16d));

    return vorto;
}

// ================================================================
// seninfektigu vorton / sterilize word
// 正規表現インジェクションを防止するために文字列を消毒する
// ----------------------------------------------------------------
function seninfektiguVorton (vorto) {
    // サーバ側でも除いているが、
    // 見出し語インクリメンタル検索結果部分の表記を変えるために実装した
    // 代用表記の置換とは分ける（検索ボックスでは^を問答無用で消さない）
    vorto = vorto.replace(/[$()^:=<>\\]/g, '');
    vorto = vorto.replace(/([?.\+\*])/g, "\\$1");

    return vorto;
}

// ================================================================
// havus vortfaradon / has mintage
// 造語を含むなら真
// 廃用
// ----------------------------------------------------------------
function havusVortfaradon () {
    var opcio           = document.getElementsByName("havus_vortfaradon");

    for (var i = 0; i < opcio.length; i ++) {
        if (opcio[i].checked) {
            return opcio[i].value;
        }
    }

    // can't happen!
    return 0;
}

// ================================================================
// akiri eraran mesagxon / get error message
// エラー結果メッセージの取得
// ----------------------------------------------------------------
function akiruEraranMesagxon (stato) {
    var html                = "<p>Eraro.</p>\n";

    html    +=
        stato   ?   "<p>予期せぬ障害が発生しました。"
                    + "HTTPステータスコードは" + stato + "です。</p>"
                :   "<p>サーバとの通信に失敗しました。"
                    // + "shiftキー等を打鍵して、"
                    + "絞り込みを再実行してみてください。</p>"
                ;

    return html;
}

// ================================================================
// fari XML HTTP postulon / create XML HTTP request
// XML HTTPリクエストオブジェクトを作成する
// ----------------------------------------------------------------
function faruXmlHttpPostulon () {
    if (window.XMLHttpRequest) {
        // Mozilla, Firefox, Safari, IE7
        return new XMLHttpRequest();
    }
    else if (window.ActiveXObject) {
        // IE5, IE6
        try {
            // MSXML3
            return new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch(e) {
            // MSXML2まで
            return new ActiveXObject("Microsoft.XMLHTTP");
        }
    }
    else {
        return null;
    }
}

// ================================================================
// ligi simbole / link by shortcut
// ショートカットキーによる画面遷移（ShiftとEnterで文章内単語訳実行）
// ----------------------------------------------------------------
function ligiSimbole (e) {
    // textarea内でShift+Enterが押下されたら、当該要素を返す
    if (navigator.userAgent.indexOf("Firefox") != -1) { // Firefox
        if (
            e.target.tagName.toUpperCase() == 'TEXTAREA' &&
            e.shiftKey &&
            estasEnigaKlavo(akiriKodonDeKlavo(e))
        ) {
            transsendiPatranFormon
                (document.getElementById(e.target.id));
            return false;   // submit後に一時的にでも改行しない（Enterを殺す）
        }
    }
    else { // IE, Opera, etc.
        if (
            event.srcElement.tagName.toUpperCase() == 'TEXTAREA' &&
            event.shiftKey &&
            estasEnigaKlavo(akiriKodonDeKlavo(event))
        ) {
            transsendiPatranFormon
                (document.getElementById(event.srcElement.id));
            return false;
        }
    }

    return;
}

// ================================================================
// akiri kodon de klavo / get key code
// キーコードを得る
// ----------------------------------------------------------------
function akiriKodonDeKlavo (e) {
    return e.keyCode != 0 ? e.keyCode : e.charCode;
}

// ================================================================
// estas eniga klavo / is enter key
// キーコードがエンターなら真（別に括り出さなくても動くのだが）
// ----------------------------------------------------------------
function estasEnigaKlavo (klavo) {
    // OperaはShift+EnterではShift(16), Enter(13)となるが後者を拾える
    return klavo == 13;
}

// ================================================================
// transsendi patran formon / transfar parent form
// 要素の親であるform（直接の親でなくても、先祖でも良い）をsubmitする
// ----------------------------------------------------------------
function transsendiPatranFormon (targetObject) {
    var formo = targetObject;

    while (targetObject.tagName.toUpperCase() != 'FORM') {
        targetObject = targetObject.parentNode; // parentElementはIE限定
        if (targetObject.tagName.toUpperCase() == 'BODY') {
            return;
        }
    }

    // 変に気を使ってcxiTio.disabled = true;にしてはならない
    // onsubmitは無視するので、ここでは色を変える
    sxangxuKoloronEnTranssendi(formo);

    targetObject.submit();

    return;
}

// ================================================================
// sxangxu koloron en transsendi / change color in transfar
// 送信時にフォームの色を変える
// ----------------------------------------------------------------
function sxangxuKoloronEnTranssendi (formo) {
    if (typeof formo != "object") {
        formo = document.getElementById(formo);
    }
    formo.style.backgroundColor = "#060";
    formo.style.color           = "#efe";

    return;
}

// ****************************************************************

// ================================================================
// sxangxu koloron / change color
// JS分けるのもアレなので、相乗り
// :hoverだけで出来るものはCSSへ。廃用。
// ----------------------------------------------------------------
function sxangxuKoloron (linio, opcio) {
    if (opcio == 0)
    {
        linio.style.backgroundColor = '';
    }
    else if (opcio == 1)
    {
        linio.style.backgroundColor = '#dfd';
    }

    return;
}
