PageSourceSearch

https://www.rstone-jp.com/rstone/wp-content/plugins/rstone-freewor…/js/advanced-autocomplete.js?ver=1.1

js rstone-jp.com collected 2026-10-05 01:26:35 UTC 6,835 bytes, 174 lines download raw bytes

1jQuery(function($) {
2    // Initialize data
3    const query_categories = autocompleteData.query_categories;
4    const query_words = autocompleteData.query_words;
5
6    // Prepare searchable data
7    const searchableData = query_categories.reduce((acc, category) => {
8        query_words[category].forEach(([word, ...readings]) => {
9            const searchTerms = [word.toLowerCase(), ...readings.map(r => r.toLowerCase())];
10            searchTerms.forEach(term => {
11                if (!acc[term]) acc[term] = [];
12                acc[term].push({ word, category });
13            });
14        });
15        return acc;
16    }, {});
17
18    // Cache for AJAX results
19    const ajaxCache = new Map();
20    let ajaxCallCount = 0;
21    const MAX_AJAX_CALLS = 20;  // Maximum number of queries to the AJAX API
22    const $target = $("#si-q");
23    let composition = false;
24
25    $target.autocomplete({
26        minLength: 1,
27        search: function(event, ui) {
28            return !composition;
29        },
30        source: function(request, response) {
31            const terms = zenNormalizeString(request.term).split(/\s+/);
32            const lastTerm = terms[terms.length - 1].toLowerCase();
33            const existingCategories = new Set();
34
35            // Check for existing words and their categories
36            const existingWords = [];
37            for (let i = 0; i < terms.length - 1; i++) {
38                const word = terms[i].toLowerCase();
39                existingWords.push(terms[i]);
40                for (const category of query_categories) {
41                    if (query_words[category].some(([w]) => w.toLowerCase() === word)) {
42                        existingCategories.add(category);
43                        break;
44                    }
45                }
46            }
47
48            // let candidates = [];
49            let candidate_words = [];
50
51            if (false) {    // B
52                for (const category of query_categories) {
53                    if (!existingCategories.has(category)) {
54                        const results = Object.keys(searchableData)
55                            .filter(key => key.startsWith(lastTerm))
56                            .flatMap(key => searchableData[key])
57                            .filter(item => item.category === category);
58                        if (results.length > 0) {
59                            // candidates = results;
60                            candidate_words = results.map(c => c.word);
61                            candidate_words = [...new Set(candidate_words)];    // make unique
62                            candidate_words = candidate_words.slice(0, 10);     // only the first N cases
63                            break;
64                        }
65                    }
66                }
67            } else {    // A
68                for (const category of query_categories) {
69                    if (!existingCategories.has(category)) {
70                        const results = Object.keys(searchableData)
71                            .filter(key => key.startsWith(lastTerm))
72                            .flatMap(key => searchableData[key])
73                            .filter(item => item.category === category);
74
75                        if (results.length > 0) {
76                            candidate_words.push(...new Set(results.slice(0, 1).map(c => c.word)));
77                        }
78                    }
79                }
80            }
81
82            if (ajaxCallCount >= MAX_AJAX_CALLS) {
83                // response(candidates.map(c => c.word));
84                response(candidate_words);
85                return;
86            }
87
88            if (! candidate_words.length) {
89                response([]);
90                return;
91            }
92
93            // Prepare query for AJAX
94            const ajaxQuery = {
95                existingWords: existingWords,
96                candidates: candidate_words
97            };
98
99            const cacheKey = JSON.stringify(ajaxQuery);
100
101            if (ajaxCache.has(cacheKey)) {
102                filterAndRespond(ajaxCache.get(cacheKey));
103            } else if (existingWords.length < 1) {  // Leading words are not queried in AJAX.
104                filterAndRespond({
105                    validWords: candidate_words
106                });
107            } else {
108                console.log("acr", existingWords.length, candidate_words.length, request.term);
109
110                $.ajax({
111                    url: autocompleteData.root + 'rstone/v1/autocomplete',
112                    method: 'POST',
113                    data: {
114                        action: 'autocomplete_query',
115                        ex: existingWords.join(" "),
116                        ca: candidate_words.join(" ")
117                    },
118                    beforeSend: function(xhr) {
119                        xhr.setRequestHeader('X-WP-Nonce', autocompleteData.nonce);
120                    }
121                })
122                .done(function(data, textStatus, jqXHR) {
123                    ajaxCallCount++;
124                    ajaxCache.set(cacheKey, data);
125                    filterAndRespond(data);
126                })
127                .fail(function(jqXHR, textStatus, errorThrown) {
128                    console.error('AJAX error:', textStatus, errorThrown);
129                    response([]);
130                });
131            }
132
133            function filterAndRespond(data) {
134                const validWords = new Set(data.validWords.map(w => w.toLowerCase()));
135                console.log("filterAndRespond", validWords);
136                response(Array.from(validWords));
137            }
138        },
139        select: function(event, ui) {
140            const terms = this.value.split(/\s+/);
141            terms.pop();
142            terms.push(ui.item.value);
143            this.value = terms.join(' ') + ' ';
144
145            // Displays suggestions for the next word after selection.
146            setTimeout(() => {
147                $(this).autocomplete('search', this.value);
148            }, 0);
149
150            return false;
151        }
152    })
153    .on('compositionstart', function(event) {
154        composition = true;
155        $target.autocomplete('disable');
156    })
157    .on('compositionend', function(event) {
158        composition = false;
159        $target.autocomplete('enable').autocomplete('search');
160    });
161    ;
162
163    function zenNormalizeString(str) {
164        // 全角スペースなどの空白文字を半角スペース1つに置換
165        str = str.replace(/\s/g, ' ');
166
167        // 全角英数字を半角英数字に置換
168        str = str.replace(/[A-Za-z0-9]/g, function(s) {
169          return String.fromCharCode(s.charCodeAt(0) - 0xFEE0);
170        });
171
172        return str;
173    }
174});

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.