PageSourceSearch

https://www.mot.be/assets/blocks/imports/search/scripts/results/main.js?etag=LygHa0MtgEu

js mot.be collected 2026-09-24 22:35:19 UTC 14,331 bytes, 300 lines download raw bytes

1/*
2 * Copyright (c) 2024 Republic of Reinvention BV <[email protected]>. All Rights Reserved.
3 * This file is part of project "cinematek-filmout-site" and can not be copied and/or distributed without
4 * the express permission and written consent of the legal responsible of Republic of Reinvention BV.
5 */
6
7/**
8 * Created by wouter on 25/08/15.
9 */
10// WATCH OUT: this name is referenced from /templates/blocks/imports/search/results/results.html
11// KEEP THEM IN SYNC
12base.plugin("blocks.imports.SearchResults", ["base.core.Commons", "base.core.Security", "blocks.core.Broadcaster", "blocks.core.Notification", "blocks.core.Commons", "blocks.core.Async", "constants.blocks.core", "messages.blocks.core", "constants.blocks.imports.search", "messages.blocks.imports.search", "blocks.imports.SearchCommons", "blocks.imports.Dtable", "constants.blocks.imports.dtable", function (Commons, Security, Broadcaster, Notification, BlocksCommons, Async, BlocksConstants, BlocksMessages, SearchConstants, SearchMessages, SearchCommons, Dtable, DtableConstants)
13{
14    var SearchResults = this;
15
16    var THEAD_BASE_URL_KEY = 'base-url';
17    var THEAD_SORT_CONFIGS_KEY = 'sort-configs';
18
19    // catch the events from the search box to reload the search results
20    $(document).on(Broadcaster.EVENTS.BLOCK.ASYNC.CHANGE, function (event, eventData)
21    {
22        // note that we don't check the type, for now, we'll reload on all async changes
23        if (eventData && eventData.source && SearchConstants.TAG_NAME_SEARCH_BOX === eventData.source.nodeName) {
24            // needs some explaining: the server side need access to the search box filter configs
25            // during the same request where the results are rendered, so we need to reload the search box
26            // together with the search results or else SearchBox.loadMeta() won't be called.
27            Async.reload($(SearchConstants.TAG_NAME_SEARCH_BOX + ',' + SearchConstants.TAG_NAME_SEARCH_RESULTS),
28                undefined,
29                // filter out the search results from the returning box+results
30                SearchConstants.TAG_NAME_SEARCH_RESULTS
31            );
32        }
33    });
34
35    /**
36     * This is called directly from the results DOM via a <script> tag tp sync the meta with the search box
37     */
38    this.setResultsMeta = function (resultsMeta)
39    {
40        // this will be undefined when the server didn't have meta
41        if (resultsMeta && resultsMeta.length > 0) {
42            var SearchBox = base.getPlugin("blocks.imports.SearchBox");
43            if (SearchBox) {
44                // note: let's also do this when resultsMeta is undefined,
45                // so existing filterconfigs get cleared during async reload
46                SearchBox.loadMeta(resultsMeta);
47            }
48        }
49    };
50
51    this.initExportBtn = function (parent, btnSelector)
52    {
53        var exportBtn = parent.find(btnSelector);
54
55        if (exportBtn && exportBtn.length) {
56
57            var pageSize = parseInt(exportBtn.attr(SearchConstants.SEARCH_RESULTS_EXPORT_SIZE_ARG));
58            var totalHits = parseInt(exportBtn.attr(SearchConstants.SEARCH_RESULTS_EXPORT_HITS_ARG));
59
60            var buttons = [
61                {
62                    label: BlocksMessages.close,
63                    action: function (dialogRef)
64                    {
65                        dialogRef.close();
66                    }
67                }
68            ];
69
70            if (totalHits <= SearchConstants.SEARCH_MAX_EXPORT_SIZE || Security.isPermitted(SearchConstants.PAGE_SEARCH_EXPORT_UNLIMITED_ALL_PERM)) {
71                buttons.push({
72                    label: SearchMessages.exportResultsDialogSubmit,
73                    cssClass: 'btn-primary',
74                    icon: 'fa fa-check',
75                    action: function (dialogRef)
76                    {
77                        var btn = $(this);
78                        var form = dialogRef.getModalBody().find('form');
79
80                        // let's not disable the download button, but hide it instead, see below
81                        //btn.disable();
82                        //btn.addClass(BlocksConstants.BTN_BUSY_CLASS);
83
84                        btn.hide();
85                        // watch out: first submit, then clear, obviously
86                        form.submit();
87                        form.html(SearchMessages.exportResultsDialogFeedback);
88                    }
89                });
90            }
91
92            // Note that we need to use event propagation because the block contents will be replaced
93            // eg. in case of sorting/filtering tabled results (and reverse results)
94            parent.on('click', btnSelector, function (e)
95            {
96                var dialog = BootstrapDialog.show({
97                    title: SearchMessages.exportResultsDialogTitle,
98                    type: BootstrapDialog.TYPE_DEFAULT,
99                    // prevent the user from closing by clicking in the background since this is quite an important dialog
100                    closeByBackdrop: false,
101                    // let's allow hitting esc though, especially for the "downloading" dialog
102                    closeByKeyboard: true,
103                    buttons: buttons,
104                    message: $('<div>' + BlocksMessages.loadingData + '</div>').load(SearchConstants.GET_EXPORT_ENDPOINT,
105                        // Note that we'll transfer all data arguments to the endpoint
106                        // if this is a string, a GET is performed, otherwise a POST
107                        //$.param(Commons.getAttributes(exportBtn, "data-")),
108                        // update 070525 switched to POST for better SPARQL query support
109                        Commons.getAttributes(exportBtn, "data-"),
110                        function (data, status, xhr)
111                        {
112                            if (status === "success") {
113                                if (jQuery().perfectScrollbar) {
114                                    dialog.getModalBody().perfectScrollbar();
115                                }
116                            }
117                            else {
118                                if (dialog) {
119                                    dialog.close();
120                                }
121                                Notification.jsonError(BlocksMessages.generalServerDataError, xhr, data);
122                            }
123                        }),
124                });
125            })
126        }
127    };
128
129    this.initDtable = function (dtableEl)
130    {
131        Dtable.init(dtableEl,
132            function initCallback(parentBlock, columns)
133            {
134                // Use the init callback (fired when all filtering was installed correctly) to ajax-ize the pagination
135                // Note that we need to use event propagation because the block contents will be replaced
136                parentBlock.on('click', 'nav a', function (event)
137                {
138                    // don't navigate away when the user clicks the page nav buttons
139                    event.preventDefault();
140
141                    var linkEl = $(this);
142                    // note: don't use the table
143                    var theadEl = parentBlock.find('table thead');
144                    // store the target uri as the base referrer URI in the thead, it will be picked up during reload
145                    theadEl.data(THEAD_BASE_URL_KEY, linkEl.attr('href'));
146
147                    reloadTable(parentBlock, undefined, undefined);
148                });
149            },
150            function filterCallback(parentBlock, columnData, inputEl, query)
151            {
152                var columnField = columnData[Dtable.COL_DATA_FIELD];
153
154                // create a filter and attach it to the input control
155                // to allow multiple filters on multiple columns
156                var filterAttr = '';
157                if (Commons.isSet(query) && query !== '') {
158                    filterAttr = Commons.toJson64(
159                        // note that making it an array turns it into a filterGroup
160                        [
161                            SearchCommons.createFieldFilter(columnField,
162                                undefined,
163                                query,
164                                undefined,
165                                SearchConstants.SEARCH_PARAM_FILTER_TYPE_INFIX,
166                                SearchConstants.SEARCH_PARAM_FILTER_BOOL_NONE
167                            )
168                        ]
169                    );
170                }
171                inputEl.attr(SearchConstants.SEARCH_RESULTS_DFILTER_VAL_ARG, filterAttr);
172
173                reloadTable(parentBlock, columnData, inputEl);
174            },
175            function sortCallback(parentBlock, columnData, clickedEl, sortDir)
176            {
177                var columnField = columnData[Dtable.COL_DATA_FIELD];
178                var columnTh = columnData[Dtable.COL_DATA_TH];
179                var columnThSortEl = columnTh.find('.' + DtableConstants.DTABLE_TH_SORT_CLASS);
180                // note that we'll be using the <thead> element as the uppermost element we can use to attach data to
181                // because it's the element that's kept alive during ajax calls
182                var theadEl = columnTh.closest('thead');
183
184                // an undefined sortDir means: no specific sorting for this columns
185                var hasValue = Commons.isSet(sortDir);
186
187                // note that we're re-using the sort dir constants as the class names
188                columnThSortEl.removeClass(DtableConstants.DTABLE_SORT_VAL_ASC);
189                columnThSortEl.removeClass(DtableConstants.DTABLE_SORT_VAL_DESC);
190                if (hasValue) {
191                    columnThSortEl.addClass(sortDir);
192                }
193
194                // we'll pass 'true' as the value when we're sorting descending, 'false' otherwise
195                var sortConfig = SearchCommons.createSortConfig(columnField, sortDir === DtableConstants.DTABLE_SORT_VAL_DESC);
196
197                // by appending the sort configs to an array, we keep their order in which they were created
198                var sortConfigs = theadEl.data(THEAD_SORT_CONFIGS_KEY) || [];
199
200                var found = false;
201                for (var i = 0; i < sortConfigs.length; i++) {
202                    var sconf = sortConfigs[i];
203                    if (sconf[SearchConstants.SEARCH_PARAM_FILTER_FIELD] === columnField) {
204                        if (hasValue) {
205                            sortConfigs[i] = sortConfig;
206                        }
207                        else {
208                            sortConfigs.splice(i, 1);
209                        }
210                        found = true;
211                    }
212                }
213                // push it to the back to keep sorting order
214                if (!found) {
215                    sortConfigs.push(sortConfig);
216                }
217
218                // make sure to save it because it might be new
219                theadEl.data(THEAD_SORT_CONFIGS_KEY, sortConfigs);
220
221                reloadTable(parentBlock, columnData, undefined);
222            }
223        );
224
225        var reloadTable = function (parentBlock, columnData, focusEl)
226        {
227            var theadEl = parentBlock.find('table thead');
228
229            // show the user we're reloading
230            parentBlock.addClass(BlocksConstants.LOADING_CLASS);
231
232            var params = Commons.keyValObj(
233                BlocksConstants.ASYNC_BLOCK_HTML_PARAM, Async.getHtml(parentBlock)
234            );
235
236            // this will be an array of (serialized) SearchFilterGroup
237            var filters = [];
238            theadEl.find('th input').each(function ()
239            {
240                var f = $(this).attr(SearchConstants.SEARCH_RESULTS_DFILTER_VAL_ARG);
241                if (f) {
242                    filters.push(f);
243                }
244            });
245            if (filters && filters.length > 0) {
246                params[SearchConstants.SEARCH_PARAM_FILTERS] = filters;
247            }
248
249            var sortConfigs = theadEl.data(THEAD_SORT_CONFIGS_KEY);
250            if (sortConfigs && sortConfigs.length > 0) {
251                params[SearchConstants.SEARCH_PARAM_SORT_CONFIG] = Commons.toJson64(sortConfigs);
252            }
253
254            var referrer = theadEl.data(THEAD_BASE_URL_KEY);
255            if (referrer) {
256                params[BlocksConstants.PAGE_URL_PARAM] = referrer;
257            }
258
259            // make sure to use traditional URL encoding so the 'filter' query param is not turned into 'filter[]'
260            $.get(SearchConstants.TABLE_ASYNC_DTABLE_ENDPOINT, $.param(params, true))
261                .done(function (newBlockHtml)
262                {
263                    var block = $(newBlockHtml);
264
265                    // watch out: this will also remove the thead from the parentBlock!
266                    block.find('table thead').replaceWith(theadEl);
267                    // we can't use replaceWith() directly because the event listeners are bound to the parentBlock
268                    // watch out: never serialize the block (eg. using block.html()) or the state of the header will change
269                    // also note that block.unwrap() didn't seem to work (it didn't unwrap), but block.children() works fine
270                    parentBlock.empty().append(block.children());
271
272                    if (focusEl) {
273                        focusEl.focus();
274                    }
275                })
276                .fail(function (xhr, textStatus, exception)
277                {
278                    Notification.jsonError(BlocksMessages.generalServerDataError, xhr, textStatus, exception);
279                })
280                .always(function ()
281                {
282                    parentBlock.removeClass(BlocksConstants.LOADING_CLASS);
283                });
284        };
285    };
286
287    $(SearchConstants.TAG_NAME_SEARCH_RESULTS).each(function (index)
288    {
289        SearchResults.initExportBtn($(this), ' .' + SearchConstants.SEARCH_RESULTS_EXPORT_CLASS);
290    });
291
292    $(SearchConstants.TAG_NAME_SEARCH_RESULTS + ' .results table').each(function (index)
293    {
294        // note that the controller re-prints the argument on the table, so make sure to check that one
295        if ($(this).attr(SearchConstants.SEARCH_RESULTS_ENABLE_DTABLE_ARG) === 'true') {
296            SearchResults.initDtable($(this));
297        }
298    });
299
300}]);

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.