PageSourceSearch

https://www.rheumatology.org.uk/DesktopModules/Cantarus/Listing/Components/listing.js?cdv=3047

js rheumatology.org.uk collected 2026-10-02 04:56:00 UTC 20,308 bytes, 584 lines download raw bytes

1var SearchData = {};
2var paginationNext = '#paginationNext';
3var paginationPrev = '#paginationPrev';
4var dataModId = 'data-mod-id';
5var defaultInstances = [];
6var defaultCategories = [];
7var defaultTags = [];
8// Init dispatch var
9var dispatch = window.dispatch;
10if (typeof dispatch === 'undefined') {
11    dispatch = {};
12}
13
14(function () {
15    // Pagination 'next' handler
16    $(document).on('click', paginationNext, function (event, element) {
17        var moduleId = getModuleId($(this));
18        // Check we're not at the last page
19        if (dispatch[moduleId].searchParams.PageNumber < dispatch[moduleId].searchParams.PageCount - 1) {
20            dispatch[moduleId].searchParams.PageNumber++;
21            getListingData(SearchData, moduleId);
22        }
23    });
24
25    // Pagination 'prev' handler
26    $(document).on('click', paginationPrev, function (event, element) {
27        var moduleId = getModuleId($(this));
28
29        // Check we're not at the first page
30        if (dispatch[moduleId].searchParams.PageNumber > 0) {
31            dispatch[moduleId].searchParams.PageNumber--;
32            getListingData(SearchData, moduleId);
33        }
34    });
35
36    // Listen for search event
37    $(document).on('DispatchListing:PerformSearch', function (event) {
38        // Remove any keys that are now 'undefined'
39        var moduleId = event.listingModuleId || getModuleId(null);
40        // Clear previous filters
41        ClearSearchFilters(moduleId);
42        // Copy of current base parameters
43        var filteredSearchParams = Object.assign({}, dispatch[moduleId].searchParams);
44
45        // Override existing categoryIds, instanceIds with new ones if new ones provided
46        filteredSearchParams.CategoryIds = OverrideExistingParameter(event.categories, filteredSearchParams.CategoryIds);
47        filteredSearchParams.TagIds = OverrideExistingParameter(event.tags, filteredSearchParams.TagIds);
48        filteredSearchParams.InstanceIds = OverrideExistingParameter(event.instances, filteredSearchParams.InstanceIds);
49
50        // Get the keywords
51        if (event.text !== null && event.text !== "") {
52            filteredSearchParams.Keywords = event.text.split(' ');
53        }
54
55        if (event.minPubDate !== null && event.minPubDate !== "") {
56            var startOfMinPubDate = new Date(event.minPubDate);
57            startOfMinPubDate.setHours(0, 0, 0, 0);
58            filteredSearchParams.MinPublishDate = startOfMinPubDate.toISOString();
59        }
60
61        if (event.maxPubDate !== null && event.maxPubDate !== "") {
62            // Returned articles inclusive of max date picked
63            var endOfMaxPubDate = new Date(event.maxPubDate);
64            endOfMaxPubDate.setHours(23, 59, 59, 999);
65
66            if (endOfMaxPubDate > new Date()) {
67                endOfMaxPubDate = new Date();
68            }
69
70            filteredSearchParams.MaxPublishDate = endOfMaxPubDate.toISOString();
71        }
72
73        // Set the search flag and reset pagination
74        filteredSearchParams.Search = true;
75        filteredSearchParams.PageNumber = 0;
76
77        dispatch[moduleId].LoadedArticles = [];
78        getListingData(filteredSearchParams, moduleId);
79    });
80}());
81
82function ClearSearchFilters(moduleId) {
83    // Reset search filters to listing defaults
84    dispatch[moduleId].searchParams.CategoryIds = defaultCategories;
85    dispatch[moduleId].searchParams.InstanceIds = defaultInstances;
86    dispatch[moduleId].searchParams.TagIds = defaultTags;
87    dispatch[moduleId].searchParams.Keywords = [];
88
89    delete dispatch[moduleId].searchParams.MinPublishDate;
90    delete dispatch[moduleId].searchParams.MaxPublishDate;
91}
92
93function OverrideExistingParameter(newParam, existingParam) {
94    if (newParam.split(',').length > 0 && newParam.split(',')[0] !== "") {
95        return newParam.split(',').map(function (item) {
96            return parseInt(item);
97        });
98    }
99
100    return existingParam;
101}
102
103/*
104 * Initialise listing module with given settings
105 */
106function initListing(moduleId, settings, searchParams) {
107    defaultCategories = searchParams.CategoryIds;
108    defaultInstances = searchParams.InstanceIds;
109
110    if (!settings.DisableRouting) {
111        // push initial state - needed so we can go back
112        history.replaceState({ pageNo: 0 }, null, window.location.href);
113    }
114
115    dispatch[moduleId] = dispatch[moduleId] || {};
116    dispatch[moduleId].LoadedArticles = settings.Articles;
117
118    // Remove unnecessary settings
119    delete settings.TotalCount;
120    delete settings.Articles;
121
122    dispatch[moduleId].settings = settings;
123    dispatch[moduleId].searchParams = searchParams;
124
125    // Init the module view
126    processTemplates(moduleId);
127    registerHelpers();
128
129    // Fire 'ArticlesLoaded' event
130    $(document).trigger('DispatchListing:ArticlesLoaded');
131    setIsLoading(false, moduleId);
132
133    // add listener for html5 popstate - re-fetch the data for the previous page
134    if (!dispatch[moduleId].settings.DisableRouting) {
135        window.addEventListener('popstate', function (e) {
136            if (e.state) {
137                // Update page number (ensure it is a positive integer)
138                var pageNo = e.state.pageNo - 1;
139                pageNo = pageNo < 0 ? 0 : pageNo;
140                dispatch[moduleId].searchParams.pageNumber = pageNo;
141
142                var data = {
143                    fromPopState: true
144                };
145
146                getListingData(data, moduleId);
147            }
148        });
149    }
150}
151
152/*
153 * Process template
154 */
155function processTemplates(moduleId) {
156    var settings = dispatch[moduleId].settings;
157
158    // Get template
159    $.get(settings.Template, function (data) {
160        dispatch[moduleId].Template = data;
161
162        var source = data;
163        dispatch[moduleId].template = Handlebars.compile(source);
164    });
165}
166
167/*
168 * Render the handlebars template with the given data
169 */
170function renderHandlebarsTemplate(givenData, moduleId) {
171    var paginationMode = dispatch[moduleId].settings.PaginationMode;
172
173    var newArticles = givenData.Articles;
174
175    if (dispatch[moduleId].hasOwnProperty("LoadedArticles") && paginationMode.toLowerCase() === "infinite") {
176        // Append the new articles
177        var existingArticles = dispatch[moduleId].LoadedArticles;
178        dispatch[moduleId].LoadedArticles = existingArticles.concat(newArticles);
179    } else {
180        // Normal pagination mode (default) or have not been initialised
181        dispatch[moduleId].LoadedArticles = newArticles;
182    }
183
184    var module = getModule(moduleId);
185
186    var pageCount = parseInt(givenData.TotalCount / dispatch[moduleId].searchParams.PageSize);
187    if (givenData.TotalCount % dispatch[moduleId].searchParams.PageSize !== 0) {
188        pageCount++;
189    }
190
191    dispatch[moduleId].searchParams.PageCount = pageCount;
192
193    // Build data and compile with handlebars
194    var context = {
195        Articles: dispatch[moduleId].LoadedArticles,
196        TotalCount: givenData.TotalCount,
197        DetailsPage: dispatch[moduleId].settings.DetailsPage,
198        PageCount: pageCount,
199        PageNumber: parseInt(dispatch[moduleId].searchParams.PageNumber) + 1,
200        DefaultImage: dispatch[moduleId].settings.DefaultImage,
201        Featured: givenData.Featured
202    };
203
204    var html = dispatch[moduleId].template(context);
205    $(module).html(html);
206
207    setIsLoading(false, moduleId);
208
209    setupPaginationClasses(moduleId);
210}
211
212/*
213 * Get the latest listing data based on the search parameters
214 */
215function getListingData(searchData, moduleId) {
216    setIsLoading(true, moduleId);
217    // Add and overwrite base parameters with new SearchData
218    jQuery.extend(dispatch[moduleId].searchParams, searchData);
219
220    // Get correct site root
221    var sfSiteRoot = dnn.getVar('sf_siteRoot');
222    sfSiteRoot = sfSiteRoot ? sfSiteRoot : '/';
223
224    // Setup services framework headers
225    var headers = getHeaders(moduleId);
226    $.ajaxSetup({
227        headers: headers
228    });
229
230    var json = JSON.stringify(dispatch[moduleId].searchParams);
231    $.ajax({
232        type: "POST",
233        url: sfSiteRoot + 'DesktopModules/Listing/API/ListingApi/GetListing',
234        data: json,
235        contentType: "application/json; charset=utf-8",
236        dataType: "json",
237        success: function (response) {
238            renderHandlebarsTemplate(response, moduleId);
239
240            var data = {
241                SearchInfo: null,
242                Settings: null,
243                Articles: {
244                    Articles: response.Articles,
245                    DefaultImage: response.DefaultImage,
246                    TotalCount: response.TotalCount,
247                    PageNumber: response.PageNumber,
248                    PageCount: response.PageCount,
249                    Featured: null,
250                    PaginationMode: response.PaginationMode,
251                    IncludeNonFeaturedArticles: response.IncludeNonFeaturedArticles,
252                    IncludeFeaturedArticles: response.IncludeFeaturedArticles,
253                    Template: response.Template
254                },
255                Featured: response.Featured
256            };
257            $(document).trigger('DispatchListing:ArticlesLoaded', [data]);
258            setIsLoading(false, moduleId);
259            
260        },
261        error: function (err) {
262            console.log(err);
263        }
264    });
265}
266
267/*
268 * Set the required headers to validate requests
269 */
270function getHeaders(moduleId) {
271    if ($.ServicesFramework) {
272        // Set up services framework.
273        var sf = $.ServicesFramework(moduleId);
274        var headers = {
275            ModuleId: sf.getModuleId(),
276            TabId: sf.getTabId()
277        };
278
279        // Set the anti-forgery token header.
280        var antiForgeryKey = sf.getAntiForgeryKey();
281        var antiForgeryValue = sf.getAntiForgeryValue();
282        headers[antiForgeryKey] = antiForgeryValue;
283
284        // Fallback in case getAntiForgeryKey() does not return the correct key.
285        if (antiForgeryKey !== 'RequestVerificationToken') {
286            headers["RequestVerificationToken"] = antiForgeryValue;
287        }
288
289        return headers;
290    }
291
292    return null;
293}
294
295/*
296 * Register all handlebars helpers (should match C# implementations)
297 */
298function registerHelpers() {
299    // Check if list is not empty of not
300    Handlebars.registerHelper('ifLength', function (param1, options) {
301        return param1.length ? options.fn(this) : options.inverse(this);
302    });
303
304    // Check if list length equals certain length
305    Handlebars.registerHelper('ifLengthEquals', function (param1, param2, options) {
306        return param1.length === param2 ? options.fn(this) : options.inverse(this);
307    });
308
309    // Check if the passed value is even number
310    Handlebars.registerHelper('ifEven', function (param1, options) {
311        return param1 % 2 === 0 ? options.fn(this) : options.inverse(this);
312    });
313
314    // Check if the passed value is odd number
315    Handlebars.registerHelper('ifOdd', function (param1, options) {
316        return param1 % 2 === 0 ? options.inverse(this) : options.fn(this);
317    });
318
319    // Check if two values are equal
320    Handlebars.registerHelper('ifEqual', function (param1, param2, options) {
321        var fnTrue = options.fn,
322            fnFalse = options.inverse;
323
324        return param1 === param2 ? fnTrue(this) : fnFalse(this);
325    });
326
327    // Check if two values are not equal
328    Handlebars.registerHelper('ifNotEqual', function (param1, param2, options) {
329        var fnTrue = options.fn,
330            fnFalse = options.inverse;
331
332        return param1 !== param2 ? fnTrue(this) : fnFalse(this);
333    });
334
335    Handlebars.registerHelper('ifLessThan', function (param1, param2, options) {
336        var fnTrue = options.fn,
337            fnFalse = options.inverse;
338
339        return param1 < param2 ? fnTrue(this) : fnFalse(this);
340    });
341
342    // Format the date to the given format
343    Handlebars.registerHelper('formatDate', function (dateVar, format) {
344        var date = moment(dateVar);
345        return date.format(format.toUpperCase());
346    });
347
348    // Check if given field exists
349    Handlebars.registerHelper('customFieldExists', function (customFields, fieldName, options) {
350        var fnTrue = options.fn,
351            fnFalse = options.inverse;
352        if (!customFields || !customFields[fieldName]) {
353            return fnFalse(this);
354        }
355        return fnTrue(this);
356    });
357
358    Handlebars.registerHelper('ifCustomFieldExists', function (fieldName, options) {
359        var articleEachIndex = options.data.index;
360        var article = options.data.root.Articles[articleEachIndex];
361        var articleCustomFields = article.CustomFields || [];
362        var customFields = articleCustomFields.filter(function (i) {
363            return i.FieldName === fieldName;
364        });
365        if (customFields[0] != null && customFields[0].FieldValue != null) {
366            return options.fn(customFields[0]);
367        }
368        else {
369            return options.inverse(this);
370        }
371    });
372
373    // Format the date, given as a jobject to the given format
374    Handlebars.registerHelper('formatCustomFieldDate', function (customFields, format, fieldName, useLowerCaseDateFormat) {
375        if (!customFields || !customFields[fieldName]) {
376            return '';
377        }
378        var date = moment(customFields[fieldName]);
379        if (useLowerCaseDateFormat) {
380            return date.format(format);
381        }
382        return date.format(format.toUpperCase());
383    });
384
385    // Convert string to lowercase
386    Handlebars.registerHelper('lowercase', function (text) {
387        if (text !== undefined) {
388            return text.toLowerCase();
389        }
390        else {
391            return "";
392        }
393    });
394
395    // Escape single quotes in given URI component
396    Handlebars.registerHelper('escape', function (url) {
397        return encodeURI(url).replace(/'/g, '\\\'');
398    });
399
400    // Return the passed value, or the default if one does not exist
401    Handlebars.registerHelper('valOrDefault', function (val, defaultVal) {
402        return val ? val : defaultVal;
403    });
404
405    //Textonly filter
406    Handlebars.registerHelper("textOnly", function (content, textlimit) {
407        var source = `<div> ${content} </div>`; // Make sure context is wrappped around by html element.
408        var text = $(source).find("script,noscript,style").remove().end() // Remove non-text html nodes
409            .text().replace(/(\r\n|\n|\r)/gm, "").replace(/\s\s+/g, ' '); // Replace new lines with spaces
410
411        if (textlimit !== undefined) {
412            var limit = parseInt(textlimit);
413            text = text.substring(0, limit);
414        }
415
416        return text;
417    });
418
419    // Given a integer list and an index, return the item stored at the list index
420    Handlebars.registerHelper("getIntListItemByIndex", function (list, index) {
421        var item = -1;
422
423        if (index <= list.length) {
424            item = list[index];
425        }
426
427        return item;
428    });
429
430    // Block helper to check whether string length is larger than a certain number
431    Handlebars.registerHelper('ifStringLongerThan', function (text, number, options) {
432        var fnTrue = options.fn,
433            fnFalse = options.inverse;
434
435        return text.length > number ? fnTrue(this) : fnFalse(this);
436    });
437
438    // Parse the given attribute from the object, or provide the fallback
439    Handlebars.registerHelper('parseAttr', function (input, attr, fallback) {
440        if (input) {
441            var obj = JSON.parse(input);
442            var attrVal = obj[attr];
443
444            // If the object does not have the available attribute, use the fallback (if provided).
445            if (!attrVal && fallback) {
446                attrVal = fallback;
447            }
448            return attrVal;
449        }
450        return "";
451    });
452
453    // Check if the first string contains the second
454    Handlebars.registerHelper('ifContains', function (input, subString, options) {
455        var fnTrue = options.fn,
456            fnFalse = options.inverse;
457
458        return input.indexOf(subString) > -1 ? fnTrue(this) : fnFalse(this);
459    });
460
461    // Strip HTML from the given tags
462    Handlebars.registerHelper('stripHtml', function (input) {
463        return $(input).text();
464    });
465
466    // Calculate the time to read the given content based on an
467    // average reading speed of 250 words per minute
468    Handlebars.registerHelper('timeToRead', function (content) {
469        var text = $(content).text();
470        var words = text.split(' ');
471
472        var avgReadSpeed = 250; // words per minute
473        var timeToRead = Math.ceil(words.length / avgReadSpeed);
474
475        return `${timeToRead} min read`;
476    });
477
478    // concaternate tags
479    Handlebars.registerHelper('tagConcatenator', function (tagList) {
480        return tagList.map(function (elem) {
481            return elem.Name;
482        }).join(', ');
483    });
484
485    Handlebars.registerHelper('pageNumbers', function (numbersEitherSide, options) {
486        var currentPage = options.data.root.PageNumber;
487        numbersEitherSide = parseInt(numbersEitherSide);
488        var start = Math.max(1, Math.min(currentPage - 1, Math.max(currentPage - numbersEitherSide, 1)));
489        var end = Math.min(options.data.root.PageCount, currentPage + numbersEitherSide);
490        var out = '';
491        for (var i = start; i <= end; i++) {
492            out += options.fn({ index: i, data: this });
493        }
494        return out;
495    });
496
497}
498
499/*
500 * Setup the pagination classes depending on whether CTA is actionable
501 */
502function setupPaginationClasses(moduleId) {
503    var module = getModule(moduleId);
504
505    if (!module.length) {
506        return;
507    }
508
509    // Disable the prev button if current page is the first page
510    if (parseInt(dispatch[moduleId].searchParams.pageNumber) === 0) {
511        $(paginationPrev, module).addClass('disabled').removeClass('secondary');
512    }
513    else {
514        $(paginationPrev, module).removeClass('disabled').addClass('secondary');
515    }
516
517    // Disable the next button if current page is the last page
518    if (dispatch[moduleId].searchParams.pageNumber === dispatch[moduleId].searchParams.PageCount - 1) {
519        $(paginationNext, module).addClass('disabled').removeClass('secondary');
520    }
521    else {
522        $(paginationNext, module).removeClass('disabled').addClass('secondary');
523    }
524}
525
526/*
527 * Get the module container element from the given moduleId
528 */
529function getModule(moduleId) {
530    return $("[data-mod-id=" + moduleId + "]");
531}
532
533/*
534 * Parse module id from attribute on parent of given element
535 */
536function getModuleId(element) {
537    var moduleId;
538
539    if (element && element.length) {
540        // Get parent module id
541        var container = element.parents("[data-mod-id]");
542        moduleId = parseInt(container.attr(dataModId));
543    } else {
544        // Get module id - used if separate search module is present
545        moduleId = parseInt($(".listing-container[data-mod-id]").attr(dataModId));
546    }
547
548    return moduleId;
549}
550
551/*
552 * Set whether the loading spinner appears
553 */
554function setIsLoading(isLoading, moduleId) {
555    var container = getModule(moduleId);
556    if (container.length) {
557        container.attr('data-loading', isLoading);
558    }
559
560    if (container.children().children(".listing-loading") !== null) {
561        if (isLoading) {
562            container.children().children(".listing-loading").show();
563        }
564        else {
565            container.children().children(".listing-loading").hide();
566        }
567    }
568}
569
570/*
571 * Emit an event to share the article
572 */
573function shareArticle(event) {
574    var target = $(event.target);
575    var moduleId = getModuleId(target);
576
577    var article = {
578        DetailsPage: dispatch[moduleId].settings.DetailsPage,
579        Title: target.attr('data-title'),
580        FriendlyUrl: target.attr('data-url')
581    };
582
583    $(document).trigger('Dispatch:ShareArticle', [target, article]);
vendor: 3 bytes, line 584
584}

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.