PageSourceSearch

https://www.accea.co.jp/js/fess.js

js accea.co.jp collected 2026-09-24 10:25:40 UTC 12,902 bytes, 454 lines download raw bytes

1/**
2 * ナビゲーション定数
3 */
4const FESS_NAVIGATION_PREV = -1;
5const FESS_NAVIGATION_NEXT = 1;
6const FESS_NAVIGATION_FIRST = 0;
7const FESS_NAVIGATION_EXACT = "e";
8
9/**
10 * FESS URL
11 */
12const BASE_URL = "https://www.accea.co.jp/search/api/v1/documents?q=";
13
14$(function(){
15    $('#container')
16        // 前ページリンクが押されたときの処理
17        .on('click', '#prevPageLink', { navi: FESS_NAVIGATION_PREV }, doSearch)
18        // 次ページリンクが押されたときの処理
19        .on('click', '#nextPageLink', { navi: FESS_NAVIGATION_NEXT }, doSearch)
20        // 指定ページリンクが押されたときの処理
21        .on('click', '.pagerLink', { navi: FESS_NAVIGATION_EXACT }, doSearch);
22    // クエリパラメータの解析
23    const search = location.search;
24    const params = new URLSearchParams(search);
25    const query = params.get('query') || '';
26    if (query !== null && query.length > 0) {
27        // 検索クエリが存在する場合、ページ読み込み時に検索を実行
28        $('#searchQuery').val(query);
29        doSearch({data:{navi: FESS_NAVIGATION_FIRST}});
30    }
31});
32
33/**
34 * ページネーションの開始位置を計算
35 * 
36 * @param {number} start 現在の開始位置
37 * @param {number} num 1ページあたりの件数
38 * @param {number|string} navi ページ移動タイプ (-1: 前, 1: 次, "e": 指定ページ, 0: 最初)
39 * @param {number|undefined} targetPageNum 指定ページ番号 (naviが"e"の場合に使用)
40 * 
41 * @returns {number} 新しい開始位置
42 */
43function getPageOffset(start, num, navi, targetPageNum) {
44    switch (navi) {
45        case FESS_NAVIGATION_PREV:
46            return Math.max(0, start - num);
47        case FESS_NAVIGATION_NEXT:
48            return start + num;
49        case FESS_NAVIGATION_EXACT:
50            if (targetPageNum !== undefined &&  targetPageNum > 0) {
51                return num * (targetPageNum - 1);
52            }
53            return start;
54        case FESS_NAVIGATION_FIRST:
55        default:
56            return 0;
57    }
58}
59
60/**
61 * URLに対して検索リクエストを送信
62 * 
63 * @param {String} url リクエスト先URL
64 * @param {Function} successHandler 成功時ハンドラ
65 * @param {Function} errorHandler 失敗時ハンドラ
66 * @param {Function} completeHandler 完了時ハンドラ(成功・失敗問わず呼び出す)
67 * @returns {void}
68 */
69function sendSearchRequest(url, successHandler, errorHandler, completeHandler) {
70    $.ajax({
71        url: url,
72        complete: completeHandler,
73    }).then(
74        successHandler,
75        errorHandler,
76    );
77}
78
79/**
80 * 検索処理
81 * 
82 * @param {*} event イベントオブジェクト
83 * @returns {void}
84 */
85function doSearch(event) {
86    // 検索ボタンのjQueryオブジェクト
87    const $searchButton = $('#searchButton');
88    // 表示開始位置、表示件数の取得
89    let start = parseInt($('#searchStart').val())
90    let num = parseInt($('#searchNum').val());
91
92    // 表示開始位置のチェック
93    if(start < 0) {
94        start = 0;
95    }
96    // 表示件数のチェック
97    if(num < 1 || num > 100) {
98        num = 20;
99    }
100
101    // 指定ページ番号の取得
102    let targetPageNum = parseInt($(event.target).data("page"));
103    targetPageNum = isNaN(targetPageNum) ? undefined : targetPageNum;
104    start = getPageOffset(start, num, event.data.navi, targetPageNum);
105
106    // 検索フィールドの値をトリムして格納
107    const searchQuery = $('#searchQuery').val().trim();
108
109    // ブラウザの履歴を更新
110    const historyURL = new URL(location.pathname, location.origin);
111    historyURL.searchParams.set('query', searchQuery);
112    historyURL.searchParams.set('start', start);
113    window.history.pushState(null,null,historyURL.toString());
114
115    // 検索フォームが空文字チェック
116    if (searchQuery.length === 0) {
117        // サブミットしないので false を返す
118        return false;
119    }
120
121    // 検索ボタンを無効にする
122    $searchButton.attr('disabled', true);
123
124    // 検索成功時ハンドラ
125    const searchSuccessHandler = (dataResponse) => {
126        const $subheader = $('#subheader');
127        const $result = $('#result');
128
129        const recordCount = dataResponse.record_count;
130        let offset = 0;
131
132        // 検索結果がない場合
133        if(recordCount === 0) { // 検索結果がない場合
134            $subheader.empty();
135
136            // 結果領域に出力
137            const $b = $('<b>').text(unescapeLucene(dataResponse.q));
138            $result.empty().append($b).append(' に一致する情報は見つかりませんでした。');
139
140            updatePageInfomation(offset, num);
141            $(document).scrollTop(0);
142            return;
143        }
144
145        // 検索結果がある場合
146
147        const pageNumber = dataResponse.page_number;
148        const pageSize = dataResponse.page_size;
149        const pageCount = dataResponse.page_count;
150        const startRange = (pageNumber - 1) * pageSize + 1;
151        const endRange = Math.min(pageNumber * pageSize, recordCount);
152
153        offset = startRange - 1;
154
155        const $b = $('<b>').text(unescapeLucene(dataResponse.q));
156        $subheader.empty().append($b).append(' の検索結果 ' + recordCount + ' 件中 ' + startRange + ' - ' + endRange + ' 件目 (' + dataResponse.exec_time + ' 秒)');
157
158        // 検索結果領域のクリア
159        $result.empty();
160
161        // 検索結果の出力
162        const $resultBody = $("<ol>", {
163            id: 'searchResultList',
164            class: 'searchResultList',
165        });
166
167        const results = dataResponse.data;
168
169        for(let i = 0; i < results.length; i++) {
170            const $searchResultItem = createResultItem(results[i]);
171            $resultBody.append($searchResultItem);
172        }
173
174        $result.append($resultBody);
175
176        // ページ番号情報の出力
177
178        const $pageInfo = $('<div>', {
179            id: 'pageInfo',
180            class: 'mb40',
181        });
182
183        // 前のページへのリンク
184        if(pageNumber > 1) {
185            const $prevPageLink = createPrevPageLink();
186            $pageInfo.append($prevPageLink);
187        }
188
189        let omissionPt1 = false; // 現在より前のページ番号の省略の点々を表示したかのフラグ
190        let omissionPt2 = false; // 現在より後のページ番号の省略の点々を表示したかのフラグ
191
192        // 各ページへのリンク
193        for (let page = 1; page <= pageCount; page++) {
194            const pageItem = createPageItem(page, pageNumber, pageCount, omissionPt1, omissionPt2);
195            $pageInfo.append(pageItem.$dom);
196            omissionPt1 = pageItem.omissionPt1;
197            omissionPt2 = pageItem.omissionPt2;
198        }
199
200        // 次のページへのリンク
201        if(pageNumber < pageCount) {
202            const $nextPageLink = createNextPageLink();
203            $pageInfo.append($nextPageLink);
204        }
205
206        $result.append($pageInfo);
207
208        // ページ情報の更新
209        updatePageInfomation(offset, num);
210        // ページ表示を上部に移動
211        $(document).scrollTop(0);
212    };
213
214    // 検索失敗時ハンドラ
215    const searchErrorHandler = (error) => {
216        alert("検索中に問題が発生しました。管理者にご相談ください。");
217        console.error('Search API ERROR:', error);
218    };
219
220    // 検索完了時ハンドラ
221    const searchCompleteHandler = () => {
222        // 検索ボタンを有効にする
223        $searchButton.attr('disabled', false);
224    }
225
226    const searchUrl = BASE_URL + encodeURIComponent(escapeLuceneSpecialChars(searchQuery)) + '&start=' + start + '&num=' + num;
227
228    // 検索リクエスト送信
229    sendSearchRequest(searchUrl,
230        // 成功時ハンドラ
231        searchSuccessHandler,
232        // 失敗時ハンドラ
233        searchErrorHandler,
234        // 完了時ハンドラ
235        searchCompleteHandler
236    );
237
238    // サブミットしないので false を返す
239    return false;
240};
241
242/**
243 * ページ情報の更新
244 * 
245 * @param {number} start 開始位置
246 * @param {number} num 件数
247 */
248function updatePageInfomation(start, num) {
249    $('#searchStart').val(start);
250    $('#searchNum').val(num);
251}
252
253/**
254 * 前のページに戻るリンクの作成
255 * 
256 * @returns {jQuery<HTMLAnchorElement>}
257 */
258function createPrevPageLink() {
259    const $prevPageLink = $('<a>', {
260        id: 'prevPageLink',
261        class: 'now_result_page',
262        href: '#',
263    });
264
265    const $acIcon = $('<i>', {
266        class: 'acIcon acIcon--arrow_p_left',
267    });
268    $prevPageLink.append($acIcon);
269
270    return $prevPageLink;
271}
272
273/**
274 * 次のページに進むリンクの作成
275 * 
276 * @returns {jQuery<HTMLAnchorElement>}
277 */
278function createNextPageLink() {
279    const $nextPageLink = $('<a>', {
280        id: 'nextPageLink',
281        class: 'now_result_page',
282        href: '#',
283    });
284
285    const $acIcon = $('<i>', {
286        class: 'acIcon acIcon--arrow_p_right',
287    });
288    $nextPageLink.append($acIcon);
289
290    return $nextPageLink;
291}
292
293/**
294 * 指定ページリンクの作成
295 * 
296 * @param {number} page ページ番号
297 * @returns {jQuery<HTMLAnchorElement>}
298 */
299function createPageLink(page) {
300    return $('<a>', {
301        href: '#',
302        class: 'now_result_page pagerLink',
303        'data-page': page,
304        text: page,
305    });
306}
307
308/**
309 * 現在のページアイテムの作成
310 * 
311 * @param {number} page ページ番号
312 * @returns {jQuery<HTMLSpanElement>}
313 */
314function createCurrentPage(page) {
315    return $('<span>', {
316        class: 'now_result_page',
317        text: page,
318    });
319}
320
321/**
322 * 省略点の作成
323 * 
324 * @returns {jQuery<HTMLSpanElement>}
325 */
326function createEllipsis() {
327    return $('<span>', {
328        class: 'omissionPt',
329        text: '...',
330    });
331}
332
333/**
334 * 検索結果アイテムの作成
335 * 
336 * @param {{
337 *  url_link: string,
338 *  title: string,
339 *  content_description: string,
340 *  site: string,
341 * }} result
342 * @returns {jQuery<HTMLLIElement>}
343 */
344function createResultItem(result) {
345    const $searchResultItem = $('<li>', {
346        class: 'searchResultItem',
347    });
348
349    const $link = $('<a>', {
350        href: result.url_link,
351        class: 'searchResultItem__link',
352    });
353
354    const $title = $('<h3>', {
355        class: 'searchResultItem__title',
356        text: result.title,
357    });
358
359    $link.append($title);
360
361    const $description = $('<div>', {
362        class: 'searchResultItem__body',
363        html: result.content_description // FESSのハイライトタグが含まれるため text ではなく html を使用
364    });
365
366    const $site = $('<cite>', {
367        class: 'searchResultItem__url',
368        text: result.site
369    });
370
371    $description
372        .append('<br/>')
373        .append($site);
374
375    $link.append($description);
376
377    $searchResultItem
378        .append($link);
379
380    return $searchResultItem;
381}
382
383/**
384 * ページ移動アイテムの作成
385 * 
386 * @param {number} page ページ番号
387 * @param {number} pageNumber 現在のページ番号
388 * @param {number} pageCount 総ページ数
389 * @param {boolean} omissionPt1
390 * @param {boolean} omissionPt2
391 * @returns {{ $dom: jQuery<HTMLElement>, omissionPt1: boolean, omissionPt2: boolean }}
392 */
393function createPageItem(page, pageNumber, pageCount, omissionPt1, omissionPt2) {
394    let $dom;
395    // 現在のページ
396    if (page === pageNumber) {
397        $dom = createCurrentPage(page);
398    }
399
400    // 先頭と末尾のページ
401    else if (page === 1 || page === pageCount) {
402        $dom = createPageLink(page);
403    }
404
405    // 現在のページが4以下の場合、最初の6ページを表示
406    else if (pageNumber < 4 && page < 6) {
407        $dom = createPageLink(page);
408    }
409
410    // 現在ページ-2よりも前のページ
411    // 現在が5ページのとき、2ページ以前
412    else if (page < (pageNumber - 2)) {
413        // 省略点を表示
414        if (!omissionPt1) {
415            omissionPt1 = true;
416            $dom = createEllipsis();
417        }
418    }
419
420    // 現在ページ + 2よりも後のページ
421    // 現在が5ページのとき、8ページ以降
422    else if (page > (pageNumber + 2)) {
423        // 省略点を表示
424        if (!omissionPt2) {
425            omissionPt2 = true;
426            $dom = createEllipsis();
427        }
428    }
429
430    else {
431        $dom = createPageLink(page);
432    }
433
434    return {
435        $dom: $dom,
436        omissionPt1: omissionPt1,
437        omissionPt2: omissionPt2,
438    };
439}
440
441/**
442 * FESS内部で使われるのLucene特殊文字をエスケープする
443 * 
444 * @param {string} str 
445 * @returns {string}
446 */
447function escapeLuceneSpecialChars(str) {
448    return str.replace(/([+\-!(){}\[\]^"~*?:\\/])/g, "\\$1");
449}
450
451function unescapeLucene(str) {
452    // バックスラッシュでエスケープされている文字を元に戻す
453    return str.replace(/\\([+\-!(){}\[\]^"~*?:\\/])/g, '$1');
454}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.