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.