1var batcheditInterface = (function () {
2    function debounce(callback, timeout) {
3        var timeoutId = null;
4
5        var wrapper = function() {
6            var self = this;
7            var args = arguments;
8
9            if (timeoutId) {
10                clearTimeout(timeoutId);
11            }
12
13            timeoutId = setTimeout(function () {
14                callback.apply(self, args);
15
16                timeoutId = null;
17            }, timeout);
18        };
19
20        return wrapper;
21    }
22
23    function escapeId(id) {
24        return id.replace(/([:.])/g, '\\$1');
25    }
26
27    function observeStyleMutations($element, callback) {
28        return new MutationObserver(debounce(callback, 500)).observe($element.get(0), {
29            attributes: true, attributeFilter: ['style']
30        });
31    }
32
33    function getLang(id) {
34        if (id in batcheditLang) {
35            return batcheditLang[id];
36        }
37
38        return 'undefined';
39    }
40
41    function updateConfig(id, value) {
42        var config = Cookies.getJSON('BatchEditConfig');
43
44        if (typeof config != 'undefined') {
45            config[id] = value;
46
47            Cookies.set('BatchEditConfig', config, { path: '', sameSite: 'Strict' });
48        }
49    }
50
51    function initializeTooltip() {
52        jQuery.widget.bridge('uitooltip', jQuery.ui.tooltip);
53        jQuery('#batchedit').uitooltip({
54            tooltipClass: 'be-tooltip',
55            show: {delay: 1000},
56            track: true
57        });
58    }
59
60    function initializeApplyCheckboxes() {
61        jQuery('#be-applyall').click(function() {
62            jQuery('.be-file input').prop('checked', this.checked);
63        });
64
65        jQuery('.be-file .be-stats input').click(function() {
66            jQuery('.be-match input[id^=' + escapeId(this.id) + ']').prop('checked', this.checked);
67        });
68
69        // When all single matches of a file have been checked, mark the appropriate file box as checked, too.
70        jQuery('.be-match input').click(function() {
71            var pageId = escapeId(this.id.substr(0, this.id.indexOf('#')));
72            var pageMatches = jQuery('.be-match input[id^=' + pageId + ']').get();
73
74            jQuery('#' + pageId).prop('checked', pageMatches.reduce(function(checked, input) {
75                return checked && input.checked;
76            }, true));
77        });
78
79        // Consolidate the list of all checked match ids into single hidden input field as
80        // a JSON-encoded array string. This avoids problems for huge replacement sets
81        // exceeding `max_input_vars` in `php.ini`.
82        jQuery('#batchedit form').submit(function() {
83            // Consolidate checked matches into a single string variable to be posted to the backend.
84            jQuery('input[name=apply]').val(JSON.stringify(jQuery('.be-match input:checked').map(function() {
85                return this.id;
86            }).get()));
87        });
88    }
89
90    function initializeTotalStatsFloater() {
91        var $anchor = jQuery('#be-totalstats');
92        var $floater = $anchor.children('div');
93
94        var updateFloater = function() {
95            if ($anchor.length == 0) {
96                return;
97            }
98
99            if (window.pageYOffset > $anchor.offset().top) {
100                $floater.addClass('be-floater').width($anchor.css('width'));
101            }
102            else {
103                $floater.removeClass('be-floater');
104            }
105        };
106
107        jQuery(window).scroll(updateFloater);
108
109        updateFloater();
110    }
111
112    function initializeSearchMode() {
113        var $searchMode = jQuery('input[name=searchmode]');
114        var $advancedRegexp = jQuery('input[name=advregexp]');
115        var $searchInputs = jQuery('#be-searchedit, #be-searcharea');
116        var $replaceInputs = jQuery('#be-replaceedit, #be-replacearea');
117
118        function updatePlaceholders(searchMode, advancedRegexp) {
119            var replaceMode = searchMode;
120
121            if (searchMode == 'regexp' && advancedRegexp) {
122                searchMode = 'advregexp';
123            }
124
125            $searchInputs.prop('placeholder', getLang('hnt_' + searchMode + 'search'));
126            $replaceInputs.prop('placeholder', getLang('hnt_' + replaceMode + 'replace'));
127        }
128
129        $searchMode.click(function() {
130            updatePlaceholders(this.value, $advancedRegexp.prop('checked'));
131            updateConfig('searchmode', this.value);
132        });
133
134        $advancedRegexp.click(function() {
135            updatePlaceholders($searchMode.filter(':checked').val(), this.checked);
136            updateConfig('advregexp', this.checked);
137        });
138    }
139
140    function initializeMatchCase() {
141        jQuery('input[name=matchcase]').click(function() {
142            updateConfig('matchcase', this.checked);
143        });
144    }
145
146    function initializeMultiline() {
147        var $multiline = jQuery('input[name=multiline]');
148        var $searchEdit = jQuery('#be-searchedit');
149        var $searchArea = jQuery('#be-searcharea');
150        var $replaceEdit = jQuery('#be-replaceedit');
151        var $replaceArea = jQuery('#be-replacearea');
152
153        $multiline.click(function() {
154            if (this.checked) {
155                $searchEdit.hide();
156                $replaceEdit.hide();
157                $searchArea.val($searchEdit.val()).show();
158                $replaceArea.val($replaceEdit.val()).show();
159            }
160            else {
161                $searchArea.hide();
162                $replaceArea.hide();
163                $searchEdit.val($searchArea.val().replace(/\n/g, '\\n')).show();
164                $replaceEdit.val($replaceArea.val().replace(/\n/g, '\\n')).show();
165            }
166
167            updateConfig('multiline', this.checked);
168        });
169
170        observeStyleMutations($searchArea, function() {
171            // Avoid using $searchArea.outerHeight() as it will have to modify display style
172            // of the element when it's hidden in order to get its height. This style mutation
173            // will trigger the observer again, causing an infintite loop.
174            if ($searchArea.get(0).offsetHeight > 0) {
175                updateConfig('searchheight', $searchArea.get(0).offsetHeight);
176            }
177        });
178
179        observeStyleMutations($replaceArea, function() {
180            if ($replaceArea.get(0).offsetHeight > 0) {
181                updateConfig('replaceheight', $replaceArea.get(0).offsetHeight);
182            }
183        });
184
185        jQuery('#batchedit form').submit(function() {
186            if (!$multiline.prop('checked')) {
187                $searchArea.val($searchEdit.val());
188                $replaceArea.val($replaceEdit.val());
189            }
190        });
191    }
192
193    function initializeAdvancedOptions() {
194        $document = jQuery(document);
195        $options = jQuery('#be-extoptions');
196
197        function onClickOutside(event) {
198            if (!$options.get(0).contains(event.target)) {
199                console.log('outside');
200                close();
201            }
202        }
203
204        function open() {
205            $options.show();
206            $document.on('click', onClickOutside);
207        }
208
209        function close() {
210            $options.hide();
211            $document.off('click', onClickOutside);
212        }
213
214        jQuery('a[href="javascript:openAdvancedOptions();"]').click(function() {
215            open();
216
217            return false;
218        });
219
220        jQuery('a[href="javascript:closeAdvancedOptions();"]').click(function() {
221            close();
222
223            return false;
224        });
225    }
226
227    function initializeMatchContext() {
228        var $contextChars = jQuery('input[name=ctxchars]');
229        var $contextLines = jQuery('input[name=ctxlines]');
230
231        jQuery('input[name=matchctx]').click(function() {
232            $contextChars.prop('disabled', !this.checked);
233            $contextLines.prop('disabled', !this.checked);
234            updateConfig('matchctx', this.checked);
235        });
236
237        $contextChars.change(function() {
238            updateConfig('ctxchars', this.value);
239        });
240
241        $contextLines.change(function() {
242            updateConfig('ctxlines', this.value);
243        });
244    }
245
246    function initializeSearchLimit() {
247        var $searchMax = jQuery('input[name=searchmax]');
248
249        jQuery('input[name=searchlimit]').click(function() {
250            $searchMax.prop('disabled', !this.checked);
251            updateConfig('searchlimit', this.checked);
252        });
253
254        $searchMax.change(function() {
255            updateConfig('searchmax', this.value);
256        });
257    }
258
259    function initializeKeepMarks() {
260        var $markPolicy = jQuery('select[name=markpolicy]');
261
262        jQuery('input[name=keepmarks]').click(function() {
263            $markPolicy.prop('disabled', !this.checked);
264            updateConfig('keepmarks', this.checked);
265        });
266
267        $markPolicy.change(function() {
268            updateConfig('markpolicy', this.value);
269        });
270    }
271
272    function initializeApplyTemplatePatterns() {
273        jQuery('input[name=tplpatterns]').click(function() {
274            updateConfig('tplpatterns', this.checked);
275        });
276    }
277
278    function initializeCheckSummary() {
279        jQuery('input[name=checksummary]').click(function() {
280            updateConfig('checksummary', this.checked);
281        });
282    }
283
284    function startProgressMonitor() {
285        var hidden = true;
286        var $progress = jQuery('#be-progress');
287
288        function updateProgress(data) {
289            $progress.text(data.operation).width((data.progress / 10) + "%");
290        }
291
292        function checkProgress() {
293            setTimeout(function () {
294                batcheditServer.checkProgress(onProgressUpdate);
295            }, 500);
296        }
297
298        function onProgressUpdate(data) {
299            if (hidden && data.progress < 400) {
300                jQuery('#be-progressbar').css('display', 'flex');
301                jQuery('input[name^=cmd').prop('disabled', true);
302
303                hidden = false;
304            }
305
306            if (!hidden) {
307                updateProgress(data);
308                checkProgress();
309            }
310        }
311
312        checkProgress();
313    }
314
315    function initializePreview() {
316        jQuery('input[name=cmd\\[preview\\]]').click(function() {
317            startProgressMonitor();
318        });
319    }
320
321    function initializeApply() {
322        jQuery('input[name=cmd\\[apply\\]]').click(function() {
323            var proceed = true;
324
325            if (jQuery('input[name=checksummary]').prop('checked') &&
326                    jQuery('input[name=summary]').val().replace(/\s+/, '') == '') {
327                proceed = confirm(getLang('war_nosummary'));
328            }
329
330            if (proceed) {
331                startProgressMonitor();
332            }
333
334            return proceed;
335        });
336    }
337
338    function initializeCancel() {
339        jQuery('input[name=cancel]').click(function() {
340            batcheditServer.cancelOperation();
341        });
342    }
343
344    function initialize() {
345        initializeTooltip();
346        initializeApplyCheckboxes();
347        initializeTotalStatsFloater();
348        initializeSearchMode();
349        initializeMatchCase();
350        initializeMultiline();
351        initializeAdvancedOptions();
352        initializeMatchContext();
353        initializeSearchLimit();
354        initializeKeepMarks();
355        initializeApplyTemplatePatterns();
356        initializeCheckSummary();
357        initializePreview();
358        initializeApply();
359        initializeCancel();
360    }
361
362    return {
363        initialize : initialize
364    }
365})();
366
367jQuery(function () {
368    batcheditInterface.initialize();
369});
370