1"use strict"; 2 3riot.tag2('collapsible-content', '<div class="collapsible-wrapper {collapsed: collapsed}"><yield></yield> <div class="toggle-expand-btn" onclick="{toggle}"></div> </div> <div class="align-center" if="{is_long && !toggle_selector}"><a class="link-text" href="#" onclick="{toggle}">{collapsed ? expand_label : collapse_label}</a></div>', '', 'id="{id}"', function (opts) { 4 this.id = 'collapisble-content-' + util.uniqueId(); 5 this.is_long = false; 6 this["default"] = opts["default"] || 'collapsed'; 7 this.height = util.cssSizeInPixel(opts.height || '24rem'); 8 this.toggle_selector = opts.toggle || false; 9 this.expand_label = opts.expandLabel || 'à¸à¸¹à¸à¸±à¹à¸à¸«à¸¡à¸'; 10 this.collapse_label = opts.collapseLabel || 'ยà¹à¸à¸à¸à¸²à¸'; 11 this.toggle = function (e) { 12 if (e) e.preventDefault(); 13 this.collapsed = !this.collapsed; 14 if (this.collapsed) { 15 this.makeCollapsed(); 16 } else { 17 this.makeExpanded(); 18 } 19 this.update(); 20 }.bind(this); 21 this.checkNeedCollapsible = function () { 22 var content_height = $(this.root).outerHeight(); 23 if (content_height > this.height) { 24 this.is_long = true; 25 this.makeCollapsed(); 26 } else { 27 this.is_long = false; 28 this.makeExpanded(); 29 } 30 this.update(); 31 }.bind(this); 32 this.makeCollapsed = function () { 33 this.collapsed = true; 34 $(this.root).find('.collapsible-wrapper').css('max-height', this.height + 'px'); 35 if (this.toggle_selector) { 36 $(this.toggle_selector).text(this.expand_label); 37 } 38 }.bind(this); 39 this.makeExpanded = function () { 40 this.collapsed = false; 41 $(this.root).find('.collapsible-wrapper').css('max-height', ''); 42 if (this.toggle_selector) { 43 $(this.toggle_selector).text(this.collapse_label); 44 } 45 }.bind(this); 46 this.on('mount', function () { 47 if (this["default"] === 'collapsed') { 48 this.checkNeedCollapsible(); 49 } 50 if (this.toggle_selector) { 51 var self = this; 52 $('body').on('click.' + this.id, this.toggle_selector, function (e) { 53 self.toggle(); 54 self.update(); 55 }); 56 } 57 }); 58 this.on('unmount', function () { 59 if (this.toggle_selector) { 60 $('body').off('click.' + this.id); 61 } 62 }); 63}); 64riot.tag2('dropdown-menu', '<div class="content"> <ul class="dropdown-list"> <li each="{data}"><a href="{url}" target="{target}" onclick="{parent.handleClick}"><i class="icon" if="{icon}">{icon}</i><span>{name}</span></a></li> </ul> </div>', '', '', function (opts) { 65 this.compact = function (array) { 66 var result = []; 67 for (var i = 0; i < array.length; i++) { 68 if (array[i]) { 69 result.push(array[i]); 70 } 71 } 72 return result; 73 }; 74 this.noop = function () {}; 75 this.map = function (array, iteratee) { 76 var result = []; 77 for (var i = 0; i < array.length; i++) { 78 result.push(iteratee(array[i], i)); 79 } 80 return result; 81 }; 82 this.merge = function (target) { 83 var sources = Array.prototype.slice.call(arguments, 1); 84 for (var i = 0; i < sources.length; i++) { 85 var source = sources[i]; 86 for (var key in source) { 87 if (source.hasOwnProperty(key)) { 88 target[key] = source[key]; 89 } 90 } 91 } 92 return target; 93 }; 94 this.snakeCase = function (str) { 95 return str.replace(/\W+/g, ' ').replace(/([a-z\d])([A-Z])/g, '$1_$2').toLowerCase().replace(/\s+/g, '_'); 96 }; 97 this._target = opts.target; 98 this._content = '.content'; 99 this.classes = this.compact((opts["class"] || '').split(' ')); 100 this.position = 'bottom left'; 101 this.menu = this.noop; 102 this.data = []; 103 this.handleClick = function (e) { 104 e.preventDefault(); 105 var item = e.item; 106 if (item && typeof item.onclick === 'function') { 107 try { 108 item.onclick.call(item, e); 109 } catch (err) { 110 console.error('Dropdown click handler error:', err); 111 } 112 } 113 if (this.drop) { 114 this.drop.close(); 115 } 116 }.bind(this); 117 this.setData = function (data) { 118 var self = this; 119 this.data = this.map(data, function (item, index) { 120 return self.merge({ 121 id: self.snakeCase('dd-menu-item-' + util.uniqueId()), 122 name: 'Link', 123 url: '', 124 target: '' 125 }, item); 126 }); 127 this.update(); 128 }.bind(this); 129 this.setMenu = function (menu) { 130 if (typeof menu === 'function') { 131 this.menu = menu; 132 this.setData(this.menu()); 133 } 134 }.bind(this); 135 this.on('before-mount', function () { 136 this.setMenu(opts.menu); 137 if (opts.position) { 138 this.position = opts.position; 139 } 140 }); 141 this.on('mount', function () { 142 try { 143 if (typeof $ === 'undefined') { 144 console.error('jQuery is required for dropdown-menu'); 145 return; 146 } 147 if (typeof Drop === 'undefined') { 148 console.error('Drop.js is required for dropdown-menu'); 149 return; 150 } 151 this.$target = $(this._target); 152 this.target = this.$target.get(0); 153 this.$content = $(this.root).find(this._content); 154 this.content = this.$content.get(0); 155 if (this.target && this.content) { 156 this.drop = new Drop({ 157 target: this.target, 158 content: this.content, 159 position: this.position, 160 classes: ['bb-dropdown'].concat(this.classes).join(' '), 161 constrainToWindow: true, 162 openOn: 'click', 163 remove: 'true', 164 tetherOptions: {} 165 }); 166 } 167 } catch (e) { 168 console.error('Dropdown mount error:', e); 169 } 170 }); 171 this.on('unmount', function () { 172 try { 173 if (this.drop) { 174 this.drop.destroy(); 175 } 176 } catch (e) { 177 console.error('Dropdown unmount error:', e); 178 } 179 }); 180}); 181riot.tag2('image-slider', '<div class="slider-list" data-is="content"><yield></yield></div>', '', '', function (opts) { 182 this.item = opts.item || 'div'; 183 this.image = opts.image !== 'false'; 184 this.viewer = opts.viewer === 'true'; 185 this.arrows = !(opts.arrows === 'false'); 186 this.dots = opts.dots === 'true'; 187 this.autoplay = !(opts.autoplay === 'false'); 188 this.autoplay_speed = +(opts.autoplayspeed || 12000); 189 this.speed = +(opts.speed || 400); 190 this.fade = !(opts.fade === 'false'); 191 this.extendClass = []; 192 this.extendClass = this.extendClass.join(' '); 193 this.compact = function (array) { 194 var result = []; 195 for (var i = 0; i < array.length; i++) { 196 if (array[i]) { 197 result.push(array[i]); 198 } 199 } 200 return result; 201 }; 202 this.openPhotoSwipe = function () { 203 var pswpElement = $(this.$fullscreen).find('.pswp')[0]; 204 var self = this; 205 var items = $(this.root).find('.slider-item:not(.slick-cloned)').map(function (index, el) { 206 var $item = $(this); 207 return { 208 src: $item.attr('data-src'), 209 w: $item.attr('data-width'), 210 h: $item.attr('data-height') 211 }; 212 }); 213 var options = { 214 modal: false, 215 index: this.$slider.slick('slickCurrentSlide') 216 }; 217 var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options); 218 gallery.init(); 219 gallery.listen('afterChange', function () { 220 self.$slider.slick('slickGoTo', this.getCurrentIndex(), true); 221 }); 222 }.bind(this); 223 this.on('mount', function () { 224 var self = this; 225 this.$slider = $(this.root).find('.slider-list'); 226 this.$slider.children().show(); 227 this.$slider.on('init reinit', function (e) { 228 if (self.image) { 229 $(this).find('.slider-item').each(function () { 230 var $item = $(this); 231 var src = $item.attr('data-src') || $item.attr('data-bg-image'); 232 if (!src) { 233 try { 234 var bgImageMatch = /url\(['"]?([^'"]*)['"]?\)/gi.exec($item.css('background-image')); 235 if (bgImageMatch && bgImageMatch[1]) { 236 src = bgImageMatch[1]; 237 } 238 } catch (e) { 239 console.warn('Failed to extract background image URL:', e); 240 } 241 } 242 if (src) { 243 $item.empty(); 244 if (self.item === 'img') { 245 $item.append($('<img/>').addClass(self.extendClass).attr('data-src', src)); 246 } else { 247 $item.append($('<div class="image hide-on-error"/>').addClass(self.extendClass).attr('data-src', src).css('background-image', 'url(\'' + src + '\')')); 248 } 249 } 250 }); 251 } else { 252 $(this).find('.slider-item[data-bg-image]').each(function () { 253 var $item = $(this); 254 var bgImageUrl = $item.attr('data-bg-image'); 255 if (bgImageUrl) { 256 $item.css('background-image', 'url(\'' + bgImageUrl + '\')'); 257 } 258 }); 259 } 260 }).on('setPosition', function (e, slick) { 261 if (self.lazyload) { 262 var i = slick.currentSlide; 263 var slideIndices = [i === 0 ? slick.$slides.length - 1 : i - 1, i, i === slick.$slides.length - 1 ? 0 : i + 1]; 264 var elements = self.compact($(slick.$slides[slideIndices[0]]).add(slick.$slides[slideIndices[1]]).add(slick.$slides[slideIndices[2]]
264).find('.lazyload:not(.lazyloaded)').toArray()); 265 } 266 }).slick({ 267 infinite: true, 268 speed: this.speed, 269 fade: this.fade, 270 autoplay: this.autoplay, 271 autoplaySpeed: this.autoplay_speed, 272 arrows: this.arrows, 273 dots: this.dots, 274 cssEase: 'ease-in' 275 }); 276 if (this.viewer) {} 277 }); 278 this.on('unmount', function () { 279 if (this.$slider) { 280 try { 281 if (this.$slider.hasClass('slick-initialized')) { 282 this.$slider.slick('unslick'); 283 } 284 this.$slider.off('init reinit setPosition'); 285 } catch (e) { 286 console.warn('Error during slider cleanup:', e); 287 } 288 } 289 }); 290}); 291riot.tag2('item-list-filter', '<div class="tool-container clearfix"> <div class="left"> <div class="rec-input input-field inline tool-select"> <select data-name="rec" onchange="{updateFilter}" onclick="{handleSelectChange}"> <option each="{recOptions}" riot-value="{value}" selected="{current.rec == value}">{name}</option> </select> </div> <div class="ne-input input-field inline tool-select"> <select data-name="ne" onchange="{updateFilter}" onclick="{handleSelectChange}"> <option each="{neOptions}" riot-value="{value}" selected="{current.ne == value}">{name}</option> </select> </div> <div class="year-input input-field inline tool-select"> <select data-name="y" onchange="{updateFilter}" onclick="{handleSelectChange}"> <option each="{yearOptions}" riot-value="{value}" selected="{current.y == value}">{name}</option> </select> </div> <div class="month-input input-field inline tool-select"> <select data-name="m" onchange="{updateFilter}" onclick="{handleSelectChange}"> <option each="{monthOptions}" riot-value="{value}" selected="{current.m == value}">{name}</option> </select> </div> <div class="sort-input input-field inline tool-select"> <select data-name="sort" onchange="{updateFilter}" onclick="{handleSelectChange}"> <option each="{sortOptions}" riot-value="{value}" selected="{current.sort == value}">{name}</option> </select> </div> </div> </div>', '', '', function (opts) { 292 var now = moment(); 293 console.log('opts:', opts); 294 function parseQueryString(url) { 295 var params = {}; 296 try { 297 if (typeof util !== 'undefined' && util.parse_querystring) { 298 return util.parse_querystring(url); 299 } else { 300 var searchParams = new URLSearchParams(window.location.search); 301 searchParams.forEach(function (value, key) { 302 params[key] = value; 303 }); 304 console.log('Manually parsed query params:', params); 305 return params; 306 } 307 } catch (error) { 308 console.error('Error parsing query string:', error); 309 return {}; 310 } 311 } 312 this.base_url = util.site_url(location.origin + location.pathname); 313 this.current = _.assignIn({ 314 y: now.format('YYYY'), 315 m: now.format('M') 316 }, opts && opts.current || parseQueryString(location.href)); 317 this.filter = opts && opts.filter || {}; 318 this.monthly_archive = opts && opts.monthly_archive || false; 319 this.max_date = (opts && opts.max_date ? moment(opts.max_date) : moment()).endOf('month'); 320 this.min_date = (opts && opts.min_date ? moment(opts.min_date) : moment()).startOf('month'); 321 console.log('this.filter:', this.filter); 322 console.log('this.monthly_archive:', this.monthly_archive); 323 console.log('this.current (final):', this.current); 324 function do_filter() { 325 console.log('do_filter called'); 326 console.log('this.current:', this.current); 327 console.log('this.base_url:', this.base_url); 328 if (this.monthly_archive) { 329 var selected_date = moment(this.current.y + '-' + this.current.m, 'YYYY-MM'); 330 if (selected_date.isBefore(this.min_date)) { 331 this.current.y = this.min_date.format('YYYY'); 332 this.current.m = this.min_date.format('M'); 333 } 334 if (selected_date.isAfter(this.max_date)) { 335 this.current.y = this.max_date.format('YYYY'); 336 this.current.m = this.max_date.format('M'); 337 } 338 } 339 var queryString = ''; 340 try { 341 if (typeof util !== 'undefined' && util.format_querystring) { 342 queryString = util.format_querystring(this.current); 343 } else { 344 var params = []; 345 for (var key in this.current) {
346 if (this.current[key] != null && this.current[key] !== '') { 347 params.push(encodeURIComponent(key) + '=' + encodeURIComponent(this.current[key])); 348 } 349 } 350 queryString = params.join('&'); 351 } 352 var newUrl = this.base_url + (queryString ? '?' + queryString : ''); 353 console.log('Redirecting to:', newUrl); 354 window.location.href = newUrl; 355 } catch (error) { 356 console.error('Error in do_filter:', error); 357 console.log('Falling back to page reload with query params'); 358 var _params = new URLSearchParams(window.location.search); 359 for (var _key in this.current) { 360 if (this.current[_key] != null && this.current[_key] !== '') { 361 _params.set(_key, this.current[_key]); 362 } 363 } 364 window.location.search = _params.toString(); 365 } 366 } 367 function generateAllOptions() { 368 if (this.filter.rec && this.filter.rec.options) { 369 this.recOptions = this.filter.rec.options; 370 console.log('Rec options set:', this.recOptions.length); 371 } else { 372 this.recOptions = []; 373 } 374 if (this.filter.ne && this.filter.ne.options) { 375 this.neOptions = this.filter.ne.options; 376 console.log('Ne options set:', this.neOptions.length); 377 } else { 378 this.neOptions = []; 379 } 380 if (this.monthly_archive) { 381 var selected_year = +this.current.y || +moment().format('YYYY'); 382 var min_date = this.min_date; 383 var max_date = this.max_date; 384 console.log('Date range:', min_date.format('YYYY-MM'), 'to', max_date.format('YYYY-MM')); 385 if (this.filter.y) { 386 this.filter.y.options = _.range(+min_date.format('YYYY'), +max_date.format('YYYY') + 1).map(function (year) { 387 return { 388 name: String(year), 389 value: year 390 }; 391 }); 392 this.filter.y.options.reverse(); 393 console.log('Year options generated:', this.filter.y.options.length); 394 this.yearOptions = this.filter.y.options; 395 } else { 396 this.yearOptions = []; 397 } 398 if (this.filter.m) { 399 this.filter.m.options = _.range((selected_year === +min_date.year() ? min_date.month() : 0) + 1, (selected_year === +max_date.year() ? max_date.month() : 11) + 2).map(function (month) { 400 return { 401 name: moment(month, 'MM').format('MMMM'), 402 value: month 403 }; 404 }); 405 this.filter.m.options.reverse(); 406 console.log('Month options generated:', this.filter.m.options.length); 407 this.monthOptions = this.filter.m.options; 408 } else { 409 this.monthOptions = []; 410 } 411 } else { 412 this.yearOptions = []; 413 this.monthOptions = []; 414 } 415 if (this.filter.sort && this.filter.sort.options) { 416 this.sortOptions = this.filter.sort.options; 417 console.log('Sort options set:', this.sortOptions.length); 418 } else { 419 this.sortOptions = []; 420 } 421 } 422 function removeEmptySelects() { 423 var _this = this; 424 console.log('Removing empty select elements...'); 425 var filterChecks = [{ 426 selector: '.rec-input', 427 options: this.recOptions 428 }, { 429 selector: '.ne-input', 430 options: this.neOptions 431 }, { 432 selector: '.year-input', 433 options: this.yearOptions 434 }, { 435 selector: '.month-input', 436 options: this.monthOptions 437 }, { 438 selector: '.sort-input', 439 options: this.sortOptions 440 }]; 441 filterChecks.forEach(function (check) { 442 var $element = $(_this.root).find(check.selector); 443 if (!check.options || check.options.length === 0) { 444 console.log('Removing', check.selector, '- no options'); 445 $element.remove(); 446 } else { 447 console.log('Keeping', check.selector, '- has', check.options.length, 'options'); 448 } 449 }); 450 } 451 this.updateFilter = function (e) { 452 console.log('=== updateFilter called ==='); 453 console.log('Event:', e); 454 console.log('Event type:', e.type); 455 console.log('Target element:', e.currentTarget); 456 var $el = $(e.currentTarget); 457 console.log('jQuery element:', $el); 458 console.log('Element data-name:', $el.data('name')); 459 console.log('Element value:', $el.val()); 460 var filter_key = $el.data('name'); 461 var filter_value = $el.val(); 462 console.log('Filter key:', filter_key); 463 console.log('Filter value:', filter_value); 464 console.log('Current state before update:', this.current); 465 this.current[filter_key] = filter_value; 466 console.log('Current state after update:', this.current); 467 if (filter_key === 'y') { 468 console.log('Year changed, regenerating options...'); 469 this.isUpdatingOptions = true; 470 generateAllOptions.call(this); 471 this.update(); 472 } else { 473 console.log('Calling do_filter...'); 474 do_filter.call(this); 475 } 476 }.bind(this); 477 this.handleSelectChange = function (e) { 478 console.log('=== handleSelectChange called ==='); 479 console.log('Alternative event handler triggered'); 480 this.updateFilter(e); 481 }.bind(this); 482 this.testYearClick = function (e) { 483 console.log('=== testYearClick called ==='); 484 console.log('Riot.js event binding is working!'); 485 console.log('Year select value:', $(this.root).find('select[data-name="y"]').val()); 486 var yearSelect = $(this.root).find('select[data-name="y"]'); 487 if (yearSelect.length) { 488 console.log('Found year select, current value:', yearSelect.val()); 489 yearSelect.trigger('change'); 490 } 491 }.bind(this); 492 this.on('mount', function () { 493 console.log('Component mounted'); 494 generateAllOptions.call(this); 495 console.log('Options generated, calling update...'); 496 this.update(); 497 }); 498 this.on('updated', function () { 499 var _this2 = this; 500 console.log('Component updated'); 501 if (this.isUpdatingOptions) { 502 console.log('Skipping Material UI init during options update'); 503 this.isUpdatingOptions = false;
504 return; 505 } 506 setTimeout(function () { 507 removeEmptySelects.call(_this2); 508 var selects = $(_this2.root).find('.tool-select select'); 509 console.log('Found', selects.length, 'select elements after cleanup'); 510 if (selects.length > 0) { 511 console.log('Initializing Material UI selects...'); 512 selects.each(function () { 513 if ($(this).hasClass('initialized')) { 514 $(this).material_select('destroy'); 515 } 516 }); 517 console.log('Setting current values before Material UI init...'); 518 selects.each(function (index, select) { 519 var $select = $(select); 520 var filterKey = $select.data('name'); 521 var currentValue = _this2.current[filterKey]; 522 console.log("Setting ".concat(filterKey, " to ").concat(currentValue)); 523 if (currentValue) { 524 $select.val(currentValue); 525 $select.find('option').removeAttr('selected'); 526 $select.find("option[value=\"".concat(currentValue, "\"]")).attr('selected', 'selected'); 527 } 528 }); 529 selects.material_select(); 530 console.log('Material UI selects initialized'); 531 setTimeout(function () { 532 console.log('Updating Material UI display values...'); 533 selects.each(function (index, select) { 534 var $select = $(select); 535 var filterKey = $select.data('name'); 536 var currentValue = _this2.current[filterKey]; 537 if (currentValue) { 538 var optionText = $select.find("option[value=\"".concat(currentValue, "\"]")).text(); 539 var $input = $select.siblings('.select-dropdown'); 540 console.log("Setting display for ".concat(filterKey, ": ").concat(optionText)); 541 $input.val(optionText); 542 } 543 }); 544 }, 50); 545 console.log('Adding manual event listeners...'); 546 selects.off('change.customFilter').on('change.customFilter', function (e) { 547 console.log('Manual change event triggered on:', e.target); 548 _this2.updateFilter(e); 549 }); 550 } else { 551 console.log('No select elements remaining after cleanup'); 552 } 553 }, 100); 554 }); 555}); 556riot.tag2('kc-search-box', '<form class="clearfix" id="simple-search-tool-container" if="{!advanced || !open_advanced}" onsubmit="{onSubmitSearchForm}"> <section class="simple-search-tool"> <div class="search-tool-row"> <div class="search-type-input" id="search-type-input"> <div class="select-wrapper-default"> <select class="browser-default search-dropdown-input" ref="keyword_type" name="keyword_type" onchange="{onChangeSearchType}"> <option each="{search_type}" riot-value="{value}" selected="{selected}">{name}</option> </select> </div> </div> <div class="search-keyword-input" id="search-keyword-input"> <input class="search-text-input" ref="keyword" type="search" name="keyword" riot-value="{Array.isArray(q) ? "" : q}" placeholder="Keyword"> </div> <div class="search-submit-button" id="search-submit-button"> <button class="btn primary btn-block" onclick="{onClickSearchButton}">Search<i class="icon material-icons"></i></button> </div> <div class="search-help-col"> <div class="left"><a class="advance-search-btn" if="{advanced}" href="{util.site_url("/knowledge/search?advance=true")}" onclick="{toggleAdvancedSearch}">Advanced Search</a></div> </div> </div> </section> </form> <form class="clearfix" id="advanced-search-tool-container" if="{advanced && open_advanced}" onsubmit="{onSubmitSearchForm}"> <section class="advance-search-tool" if="{open_advanced}"> <div class="search-tool-row"> <div class="search-filter-input"> <div class="search-tool-row"> <div class="select-wrapper-default"> <select class="browser-default search-dropdown-input" ref="adv_keyword_type" name="adv[keyword_type]" onchange="{onChangeSearchType}"> <option each="{search_type}" riot-value="{value}" selected="{selected}">{name}</option> </select> </div> </div> <div class="search-tool-row"> <div class="select-wrapper-default"> <select class="browser-default search-dropdown-input" ref="adv_year_type" name="adv[year_type]" onchange="{onChangeSearchType}"> <option each="{year_type}" riot-value="{value}" selected="{selected}">{name}</option> </select> </div> </div> <div class="search-tool-row"> <div class="select-wrapper-default"> <select class="browser-default search-dropdown-input" ref="adv_lang_type" name="adv[lang_type]" onchange="{onChangeSearchType}"> <option each="{lang_type}" riot-value="{value}" selected="{selected}">{name}</option> </select> </div> </div> </div> <div class="search-compare-field-input"> <div class="search-tool-row" each="{adv_search_rows}"> <div class="search-field-input"> <div class="select-wrapper-default"> <select class="browser-default search-dropdown-input" ref="adv_q_{this}_field" name="adv[q][{this}][field]"> <option value="">Any Fields</option> <option value="title">Title</option> <option value="author">Author</option> <option value="subject">Subject</option> </select> </div> </div> <div class="search-keyword-input"> <input class="search-text-input" ref="adv_q_{this}_keyword" type="text" name="adv[q][{this}][keyword]" riot-value="{Array.isArray(q) ? (q[this] && q[this].keyword ? q[this].keyword : "") : ""}"> </div> </div> </div> </div> <div class="search-tool-row"> <div class="search-filter-input"></div> <div class="search-compare-field-input"> <div class="search-tool-row"> <div class="search-submit-button"> <div class="search-submit-button" id="advance-search-submit-button"> <button class="btn primary btn-block" onclick="{onClickSearchButton}">Search<i class="icon material-icons"></i></button> </div> </div> <div class="search-help-col"> <div class="left"><a href="{util.site_url("/knowledge/search?advanced=false")}" onclick="{toggleAdvancedSearch}">Simple Search</a></div> </div> </div> </div> </div> </section> </form>', '', '', function (opts) { 557 this.path = '/knowledge/search'; 558 this.q = opts.q || ''; 559 this.type = 'all'; 560 this.search_type = opts.search_type || []; 561 this.year_type = opts.year_type || []; 562 this.lang_type = opts.lang_type || []; 563 this.adv_query_max = 3; 564 this.open_advanced = opts.open_advanced === true || Array.isArray(this.q) || false; 565 this.advanced = typeof opts.advanced === 'boolean' ? opts.advanced : opts.advanced === 'true' || false; 566 this.adv_search_rows = []; 567 for (var i = 0; i < this.adv_query_max; i++) { 568 this.adv_search_rows.push(i); 569 } 570 this.times = function (n) { 571 var result = []; 572 for (var i = 0; i < n; i++) { 573 result.push(i); 574 } 575 return result; 576 }; 577 this.onSubmitSearchForm = function (e) { 578 e.preventDefault(); 579 this.q = e.target.value; 580 var fn = typeof opts.onsubmitsearchform === 'function' ? opts.onsubmitsearchform : this.submitSearchForm; 581 fn(this.populateParams()); 582 }.bind(this); 583 this.onClickSearchButton = function (e) { 584 e.preventDefault(); 585 this.q = e.target.value; 586 var fn = typeof opts.onclicksearchbutton === 'function' ? opts.onclicksearchbutton : this.clickSearchButton; 587 fn(this.populateParams()); 588 }.bind(this); 589 this.onChangeSearchType = function (e) { 590 e.preventDefault(); 591 this.type = e.target.value; 592 var fn = typeof opts.onchangesearchtype === 'function' ? opts.onchangesearchtype : this.changeType; 593 fn(this.populateParams()); 594 }.bind(this); 595 this.toggleAdvancedSearch = function (e) { 596 e.preventDefault(); 597 this.open_advanced = !this.open_advanced; 598 this.update(); 599 }.bind(this); 600 this.filter = function (array, predicate) { 601 var result = []; 602 for (var i = 0; i < array.length; i++) { 603 if (predicate(array[i])) { 604 result.push(array[i]); 605 } 606 } 607 return result; 608 }; 609 this.map = function (array, iteratee) { 610 var result = []; 611 for (var i = 0; i < array.length; i++) { 612 result.push(iteratee(array[i], i)); 613 } 614 return result; 615 }; 616 this.forEach = function (collection, iteratee) { 617 if (Array.isArray(collection)) { 618 for (var i = 0; i < collection.length; i++) { 619 iteratee(collection[i], i); 620 } 621 } else { 622 for (var key in collection) { 623 if (collection.hasOwnProperty(key)) { 624 iteratee(collection[key], key); 625 } 626 } 627 } 628 }; 629 this.doSearch = function () {
630 if (this.type === 'all') { 631 this.path = '/knowledge/bento'; 632 } 633 var params = this.populateParams(); 634 location.href = util.site_url(this.path, { 635 q: params.q, 636 type: params.filter.type 637 }); 638 }.bind(this); 639 this.populateParams = function () { 640 var params = {}; 641 var self = this; 642 if (this.open_advanced) { 643 var queries = []; 644 for (var index = 0; index < this.adv_query_max; index++) { 645 var keywordRef = 'adv_q_' + index + '_keyword'; 646 var fieldRef = 'adv_q_' + index + '_field'; 647 var keyword = this.refs[keywordRef] ? this.refs[keywordRef].value : ''; 648 if (keyword) { 649 queries.push({ 650 field: this.refs[fieldRef] ? this.refs[fieldRef].value : '', 651 keyword: keyword 652 }); 653 } 654 } 655 params = { 656 adv: true, 657 q: queries, 658 filter: { 659 type: this.refs.adv_keyword_type ? this.refs.adv_keyword_type.value : '', 660 language: this.refs.adv_lang_type ? this.refs.adv_lang_type.value : '', 661 year: this.refs.adv_year_type ? this.refs.adv_year_type.value : '' 662 } 663 }; 664 var filterMappings = [['keyword_type', 'type'], ['year_type', 'year'], ['lang_type', 'language']]; 665 this.forEach(filterMappings, function (pair) { 666 self.forEach(self[pair[0]], function (item) { 667 if (item.value === params.filter[pair[1]]) { 668 item.selected = true; 669 } else { 670 item.selected = false; 671 } 672 }); 673 }); 674 } else { 675 params = { 676 q: this.refs.keyword ? this.refs.keyword.value : '', 677 filter: { 678 type: this.refs.keyword_type ? this.refs.keyword_type.value : '' 679 } 680 }; 681 } 682 return params; 683 }.bind(this); 684 this.submitSearchForm = function (params) { 685 this.doSearch(); 686 }.bind(this); 687 this.clickSearchButton = function (params) { 688 this.doSearch(); 689 }.bind(this); 690 this.changeType = function (e) {}.bind(this); 691}); 692riot.tag2('preloader', '<div class="preloader-wrapper active {class}"> <div class="spinner-layer spinner-blue-only"> <div class="circle-clipper left"> <div class="circle"></div> </div> <div class="gap-patch"> <div class="circle"></div> </div> <div class="circle-clipper right"> <div class="circle"></div> </div> </div> </div>', '', '', function (opts) { 693 this["class"] = opts["class"]; 694}); 695riot.tag2('search-box', '<a class="toggle-btn" href="#" onclick="{clickToggleSearch}"><i class="icon material-icons"></i></a> <div class="search-box-wrapper" ref="wrapper"> <form onsubmit="{submitSearch}"> <input type="search" ref="q" name="q" placeholder="Title, keyword, author" onblur="{clickToggleSearch}" tabindex="-1"> </form> </div>', '', 'class="{open: open}"', function (opts) { 696 this.open = false; 697 this.path = '/search'; 698 this.clickToggleSearch = function (e) { 699 e.preventDefault(); 700 this.open = !this.open; 701 $('body')[this.open ? 'addClass' : 'removeClass']('global-search-active'); 702 this.update(); 703 }.bind(this); 704 this.submitSearch = function (e) { 705 e.preventDefault(); 706 var searchValue = this.refs.q ? this.refs.q.value : ''; 707 location.href = util.site_url(this.path, { 708 q: searchValue 709 }); 710 }.bind(this); 711 this.on('updated', function () { 712 if (this.open) { 713 if (this.refs.q) { 714 $(this.refs.q).focus(); 715 } 716 } else {} 717 }); 718}); 719riot.tag2('page-error', '<div id="page-error"> <div class="container"> <div class="row"> <div class="col s12 m6 offset-m3 l4 offset-l4"> <div class="spacing-large"></div> <div class="center"><i class="icon material-icons title-icon">{icon}</i> <h4 class="center">{title}</h4> <h5>{message}</h5> </div> <div class="spacing-large"></div> </div> </div> </div> </div>', '', '', function (opts) { 720 this.title = opts.title || 'à¹à¸à¸´à¸à¸à¸±à¸à¸«à¸²à¸à¸¶à¹à¸!'; 721 this.message = opts.message || 'ลà¸à¸à¸à¸¹à¹à¸«à¸¡à¹à¸à¸°'; 722 this.icon = opts.icon || 'info_outline'; 723}); 724riot.tag2('page-search', '<section class="full-container {show-sidebar: show_facet, loading: loading, sidebar-active: sidebar_active}" id="search-container"> <div class="search-result-page row no-spacing" id="search-box-row"> <kc-search-box search_type="{search_type}" advanced="{advanced_search}" open_advanced="{adv}" year_type="{year_type}" lang_type="{lang_type}" q="{q}" onchangesearchtype="{changeSearchType}" onclicksearchbutton="{clickSearchButton}" onsubmitsearchform="{submitSearchForm}"></kc-search-box> </div> <div class="row no-spacing" id="search-main-row"> <div class="col" id="search-sidebar" if="{show_facet}"> <div class="right"><a id="search-facet-sidebar-toggle-btn" href="#!" onclick="{toggleSidebar}"><i class="icon material-icons">close</i></a></div> <div class="search-sidebar-row" id="search-selected-filter" if="{hasSelectedFilters}"> <h4 class="sidebar-title">Current Filters <div class="right"><a id="clear-filter-btn" href="#" onclick="{clearFacetFilter}">Clear Filters</a></div> </h4> <div class="current-filter-group filter-group"> <div each="{selected_filters_by_group}"> <h5>{parent.getGroupKey(this)}</h5> <div class="filter-checkbox" each="{this}"> <input class="filled-in" type="checkbox" id="selected-{key}-{parent.kebabCase(value)}" name="{key}" riot-value="{value}" data-label="{name}" checked onchange="{parent.changeFacetCheckbox}"> <label for="selected-{key}-{parent.kebabCase(value)}">{name}</label> </div> </div> </div> </div> <div class="search-sidebar-row" id="search-facet" if="{selectable_facets.length > 0}"> <h4 class="sidebar-title">Filter by</h4> <div each="{support_filter}"> <ul class="collapsible flat" if="{parent.hasFacetBuckets(key)}" data-collapsible="expandable"> <li> <div class="collapsible-header active">{value}</div> <div class="collapsible-body"> <div class="filter-group {"show-more-item": parent.expanded_filter[key]}"> <div class="filter-checkbox {"more-item": parent.getIndex(this) > 10}" each="{parent.facet[key].buckets}"> <input class="filled-in" type="checkbox" id="{parent.getFilterId(key, key_)}" name="{key}" riot-value="{key_}" data-label="{name}" onchange="{parent.changeFacetCheckbox}"> <label for="{parent.getFilterId(key, key_)}">{name} ({doc_count})</label> </div> <div class="filter-more-group" if="{parent.shouldShowMoreButton(key)}"> <button class="toggle-show-more-btn" riot-value="{key}" onclick="{parent.toggleFacetExpander}">{parent.expanded_filter[key] ? \'hide\' : \'view more\'}</button> </div> </div> </div> </li> </ul> </div> </div> </div> <div class="col opaque-bg" id="search-result">
724 <div class="row search-result-header"> <div class="col s12"> <div class="spacing"></div> <h4 class="page-title"> <div class="left"><a id="search-sidebar-toggle-btn" href="#!" onclick="{toggleSidebar}"><i class="icon material-icons">filter_list</i></a></div><span if="{q}">Search: <div class="search-keyword-text" each="{getSearchKeywords()}">{this}</div></span><span if="{!q}">Search Results</span> </h4> </div> </div> <div class="row search-result-subheader" if="{hasResults}"> <div class="col s6"> <div class="page-subtitle search-result-meta"> <div>{getResultsRange()}</div> </div> </div> <div class="col s6 right-align"> <div class="sort-input input-field inline search-result-meta"> <select class="browser-default" onchange="{changeSearchSort}"> <option each="{sortOptions}" riot-value="{value}" selected="{value === parent.sort}">{label}</option> </select> </div> <ul class="pagination inline search-result-meta"> <li if="{pagination.prev !== false}"><a href="#page={pagination.prev}" data-page="{pagination.prev}" onclick="{clickPage}"><i class="icon material-icons">chevron_left</i></a></li> <li class="{active: this === parent.pagination.page}" each="{getPaginationRange()}"><a href="#page={this}" data-page="{this}" onclick="{parent.clickPage}">{this}</a></li> <li if="{pagination.next !== false}"><a href="#page={pagination.next}" data-page="{pagination.next}" onclick="{clickPage}"><i class="icon material-icons">chevron_right</i></a></li> </ul> </div> </div> <div class="row"> <div class="col s12"> <div id="search-result-content"> <preloader if="{loading}"></preloader> <ul class="collection collection-action" if="{items && items.length > 0 && !loading}"> <li class="collection-item" each="{items}"> <div class="collection-img"><a href="{util && util.item_url ? util.item_url(this) : "#"}"> <div class="image center-block" data-item-type="{meta_type}" data-bg-image="{util && util.site_url && thumbnail_image ? util.site_url(thumbnail_image) : ""}"></div></a></div> <div class="collection-main"> <div class="item-type item-subtitle">{parent.startCase(_locale_type || _type || meta_type || \'Unknown\')}</div><a class="item-title truncate" href="{util && util.item_url ? util.item_url(this) : "#"}">{name || main_title || meta_id || \'Untitled\'}</a> <div class="meta-content"> <div class="meta meta-author" if="{display_author && display_author.length > 0}"><span class="label">By</span>{display_author.join(\', \')}</div> </div> <p if="{summary}">{summary}</p> </div> <div class="collection-side"> <div if="{_user_action && _user_action.see_location && items && items.length > 0}"> <div class="item-subtitle">Location</div> <ul class="item-location-list"> <li each="{parent.sortBy(parent.groupBy(items, "location") || [], ["location"])}"><a class="item-location-callnumber modal-trigger" href="#shelf-location">{this[0].location_name} ({this[0].location})</a> <div class="item-location-name" data-location="{this[0].location}">{this[0].callnumber}</div> </li> </ul> <div class="spacing"></div> </div> <div class="item-action-list"> <div class="item-action" if="{_user_action && _user_action.view_content}"><a class="btn btn-flat btn-block" href="{_user_action.view_content.url}" target="_blank">View Content</a></div> <div class="item-action" if="{_user_action && _user_action.link_library}"><a class="btn btn-flat btn-block" href="{_user_action.link_library.url}" target="_blank">See Details</a></div> </div> </div> </li> </ul> <div class="empty" if="{!loading && items && items.length === 0 && !first_time}"> <div class="spacing-large"></div> <div class="center"><i class="icon material-icons large">youtube_searched_for</i> <h4 class="center">Cannot Find What You\'re Looking For</h4> <h5>Please revise your search conditions</h5> </div> <div class="spacing-large"></div> </div> </div> </div> </div> <div class="row search-result-subheader" if="{hasResults}"> <div class="col s6"> <div class="page-subtitle search-result-meta"> <div>{getResultsRange()}</div> </div> </div> <div class="col s6 right-align"> <div class="sort-input input-field inline search-result-meta"> <select class="browser-default" onchange="{changeSearchSort}"> <option each="{sortOptions}" riot-value="{value}" selected="{value === parent.sort}">{label}</option> </select> </div> <ul class="pagination inline search-result-meta"> <li if="{pagination.prev !== false}"><a href="#page={pagination.prev}" data-page="{pagination.prev}" onclick="{clickPage}"><i class="icon material-icons">chevron_left</i></a></li>
724 <li class="{active: this === parent.pagination.page}" each="{getPaginationRange()}"><a href="#page={this}" data-page="{this}" onclick="{parent.clickPage}">{this}</a></li> <li if="{pagination.next !== false}"><a href="#page={pagination.next}" data-page="{pagination.next}" onclick="{clickPage}"><i class="icon material-icons">chevron_right</i></a></li> </ul> </div> </div> </div> </div> </section>', '', '', function (opts) { 725 this.memoize = function (fn, keyGenerator) { 726 var cache = new Map(); 727 return function () { 728 var key = keyGenerator ? keyGenerator.apply(this, arguments) : JSON.stringify(arguments); 729 if (cache.has(key)) { 730 return cache.get(key); 731 } 732 var result = fn.apply(this, arguments); 733 cache.set(key, result); 734 return result; 735 }; 736 }; 737 this.isArray = Array.isArray; 738 this.keys = Object.keys; 739 this.forEach = function (collection, iteratee) { 740 if (Array.isArray(collection)) { 741 for (var i = 0; i < collection.length; i++) { 742 iteratee(collection[i], i); 743 } 744 } else { 745 for (var key in collection) { 746 if (collection.hasOwnProperty(key)) { 747 iteratee(collection[key], key); 748 } 749 } 750 } 751 }; 752 this.map = function (array, iteratee) { 753 var result = []; 754 for (var i = 0; i < array.length; i++) { 755 result.push(iteratee(array[i], i)); 756 } 757 return result; 758 }; 759 this.filterArray = function (array, predicate) { 760 return array.filter(predicate); 761 }; 762 this.compact = function (array) { 763 var result = []; 764 for (var i = 0; i < array.length; i++) { 765 if (array[i]) { 766 result.push(array[i]); 767 } 768 } 769 return result; 770 }; 771 this.groupBy = function (array, key) { 772 return array.reduce(function (result, item) { 773 var groupKey = typeof key === 'function' ? key(item) : item[key]; 774 (result[groupKey] = result[groupKey] || []).push(item); 775 return result; 776 }, {}); 777 }; 778 this.sortBy = function (array, keys) { 779 return array.slice().sort(function (a, b) { 780 for (var i = 0; i < keys.length; i++) { 781 var key = keys[i]; 782 var aVal = a[key]; 783 var bVal = b[key]; 784 if (aVal < bVal) return -1; 785 if (aVal > bVal) return 1; 786 } 787 return 0; 788 }); 789 }; 790 this.startCase = function (str) { 791 if (!str) return ''; 792 return str.replace(/\w\S*/g, function (txt) { 793 return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); 794 }); 795 }; 796 this.kebabCase = function (str) { 797 if (!str) return ''; 798 return str.replace(/\W+/g, ' ').replace(/([a-z\d])([A-Z])/g, '$1_$2').toLowerCase().replace(/\s+/g, '-'); 799 }; 800 this.get = function (obj, path, defaultValue) { 801 var keys = path.split('.'); 802 var current = obj; 803 for (var i = 0; i < keys.length; i++) { 804 if (current == null || current[keys[i]] == null) { 805 return defaultValue; 806 } 807 current = current[keys[i]]; 808 } 809 return current; 810 }; 811 this.assign = Object.assign || function (target) { 812 var sources = Array.prototype.slice.call(arguments, 1); 813 sources.forEach(function (source) { 814 if (source) { 815 for (var key in source) { 816 if (source.hasOwnProperty(key)) { 817 target[key] = source[key]; 818 } 819 } 820 } 821 }); 822 return target; 823 }; 824 this.range = function (start, stop, step) { 825 step = step || 1; 826 var result = []; 827 for (var i = start; i < stop; i += step) { 828 result.push(i); 829 } 830 return result; 831 }; 832 this.zipObject = function (props, values) { 833 var result = {}; 834 for (var i = 0; i < props.length; i++) { 835 result[props[i]] = values[i]; 836 } 837 return result; 838 }; 839 this.times = function (n, iteratee) { 840 var result = []; 841 for (var i = 0; i < n; i++) { 842 result.push(iteratee ? iteratee(i) : i); 843 } 844 return result; 845 }; 846 this.flatten = function (array) { 847 var result = []; 848 for (var i = 0; i < array.length; i++) { 849 if (Array.isArray(array[i])) { 850 result = result.concat(this.flatten(array[i])); 851 } else { 852 result.push(array[i]); 853 } 854 } 855 return result; 856 }; 857 this.uniq = function (array) { 858 var result = []; 859 for (var i = 0; i < array.length; i++) { 860 if (result.indexOf(array[i]) === -1) { 861 result.push(array[i]); 862 } 863 } 864 return result; 865 }; 866 this.pick = function (object, keys) { 867 var result = {}; 868 for (var i = 0; i < keys.length; i++) { 869 var key = keys[i]; 870 if (object.hasOwnProperty(key)) { 871 result[key] = object[key]; 872 } 873 } 874 return result; 875 }; 876 this.omit = function (object, keys) { 877 var result = {}; 878 for (var key in object) { 879 if (object.hasOwnProperty(key) && keys.indexOf(key) === -1) { 880 result[key] = object[key]; 881 } 882 } 883 return result; 884 }; 885 this.initializeState = function () { 886 this.base_url = opts.base_url || 'search'; 887 this.first_time = true; 888 this.loading = false;
889 this.sidebar_active = false; 890 this.show_facet = !!opts.facet; 891 this.page_range = 3; 892 this.items = []; 893 this.q = opts.q || ''; 894 this.adv = opts.adv === true || Array.isArray(this.q); 895 this.type = opts.type || 'all'; 896 this.filters = opts.filter || {}; 897 this.facet = {}; 898 this.sort = opts.sort || 'score'; 899 this.pagination = { 900 page: 1, 901 next: false, 902 prev: false, 903 min_page: 1, 904 max_page: 1, 905 limit: 10, 906 total: 0 907 }; 908 this.advanced_search = opts.advanced_search || false; 909 this.support_filter = opts.support_filter_name || {}; 910 this.selected_filters_by_group = {}; 911 this.selectable_facets = []; 912 this.search_type = opts.search_type || []; 913 this.year_type = opts.year_type || []; 914 this.lang_type = opts.lang_type || []; 915 var filter_keys = Object.keys(this.support_filter); 916 this.expanded_filter = this.zipObject(filter_keys, this.times(filter_keys.length, function () { 917 return false; 918 })); 919 this.sortOptions = [{ 920 value: 'score', 921 label: 'Relevance' 922 }, { 923 value: '-date', 924 label: 'Latest' 925 }, { 926 value: 'date', 927 label: 'Oldest' 928 }, { 929 value: 'name', 930 label: 'A - Z' 931 }, { 932 value: '-name', 933 label: 'Z - A' 934 }]; 935 }; 936 this.hasSelectedFilters = function () { 937 return Object.keys(this.selected_filters_by_group).length > 0; 938 }; 939 this.hasResults = function () { 940 return this.items && this.items.length > 0; 941 }; 942 this.getSearchKeywords = function () { 943 return Array.isArray(this.q) ? this.q : [this.q]; 944 }; 945 this.getResultsRange = function () { 946 var start = this.pagination.limit * (this.pagination.page - 1) + 1; 947 var end = Math.min(this.pagination.total, this.pagination.limit * this.pagination.page); 948 return start + ' - ' + end + ' of ' + this.pagination.total + ' results'; 949 }; 950 this.getPaginationRange = function () { 951 return this.range(this.pagination.min_page, this.pagination.max_page + 1); 952 }; 953 this.hasFacetBuckets = function (key) { 954 return this.get(this.facet, key + ".buckets.length", 0) > 0; 955 }; 956 this.shouldShowMoreButton = function (key) { 957 return this.facet[key] && this.facet[key].buckets && this.facet[key].buckets.length > 10; 958 }; 959 this.getGroupKey = function (group) { 960 for (var key in this.selected_filters_by_group) { 961 if (this.selected_filters_by_group[key] === group) { 962 return key; 963 } 964 } 965 return ''; 966 }; 967 this.getIndex = function (item) { 968 var parent = this.parent; 969 var array = parent.facet[parent.key].buckets; 970 return array.indexOf(item); 971 }; 972 this.getFilterId = function (key, value) { 973 return key + '-' + this.kebabCase(value); 974 }; 975 this.hasAuthors = function () { 976 return this.display_author && this.display_author.length > 0; 977 }; 978 this.hasLocationItems = function () { 979 return this._user_action && this._user_action.see_location && this.items && this.items.length > 0; 980 }; 981 this.getLocationGroups = function () { 982 if (!this.items || !this.items.length) return []; 983 return this.sortBy(this.groupBy(this.items, "location") || [], ["location"]); 984 }; 985 this.updateParams = function (params) { 986 if (typeof params.q !== 'undefined') { 987 this.q = params.q; 988 this.adv = Array.isArray(this.q); 989 } 990 if (params.filter) { 991 if (typeof params.filter.type !== 'undefined') { 992 this.type = params.filter.type; 993 } 994 if (typeof params.filter.language !== 'undefined') { 995 this.filters.language = params.filter.language; 996 } 997 if (typeof params.filter.year !== 'undefined') { 998 this.filters.year = params.filter.year; 999 } 1000 } 1001 }; 1002 this.makeSearchQuery = function () { 1003 var filter = {}; 1004 var display_filter = {}; 1005 var self = this; 1006 this.forEach(this.keys(this.support_filter), function (key) { 1007 if (self.filters[key]) { 1008 var values = self.map(self.filters[key].split(','), function (value) { 1009 var paths = value.split(':'); 1010 return paths[0]; 1011 }); 1012 filter[key] = values.join(','); 1013 display_filter[key] = self.filters[key]; 1014 } 1015 }); 1016 var searchType = this.type; 1017 if (searchType === "all") { 1018 searchType = "library"; 1019 } 1020 var params = { 1021 q: this.q, 1022 type: searchType, 1023 page: this.pagination.page, 1024 sort: this.sort || 'score', 1025 facet: this.show_facet, 1026 adv: this.adv 1027 }; 1028 console.log('makeSearchQuery params:', params); 1029 return { 1030 value: this.assign({}, params, filter), 1031 display: this.assign({}, params, display_filter) 1032 }; 1033 }; 1034 this.parseItem = function (item) { 1035 console.log('parseItem called with:', item); 1036 var parsedItem = {}; 1037 for (var key in item) { 1038 if (item.hasOwnProperty(key)) { 1039 parsedItem[key] = item[key]; 1040 } 1041 } 1042 if (item.content && !parsedItem.summary) {
1043 try { 1044 if (typeof util !== 'undefined' && util.strip_tags) { 1045 parsedItem.summary = util.strip_tags(item.content); 1046 } else { 1047 parsedItem.summary = item.content.replace(/<[^>]*>/g, ''); 1048 } 1049 if (parsedItem.summary.length > 240) { 1050 parsedItem.summary = parsedItem.summary.slice(0, 240) + '...'; 1051 } 1052 } catch (e) { 1053 console.error('Error processing summary:', e); 1054 parsedItem.summary = item.content; 1055 } 1056 } 1057 if (!parsedItem.summary && item.summary) { 1058 parsedItem.summary = item.summary; 1059 } 1060 console.log('parseItem result:', parsedItem); 1061 console.log('parseItem result keys:', Object.keys(parsedItem)); 1062 return parsedItem; 1063 }; 1064 this.doSearch = function () { 1065 var self = this; 1066 this.loading = true; 1067 this.update(); 1068 var search_query = this.makeSearchQuery(); 1069 console.log('DoSearch called with query:', search_query); 1070 riot.route(this.base_url + '?' + util.format_querystring(util.make_querystring(search_query.display))); 1071 fetch(util.site_url('/search', app.config.api_url, search_query.value)).then(function (response) { 1072 console.log('API Response status:', response.status); 1073 if (!response.ok) { 1074 throw new Error('Network response was not ok: ' + response.status); 1075 } 1076 return response.json(); 1077 }).then(function (data) { 1078 console.log('API success, data received:', data); 1079 self.processSearchData(data); 1080 })["catch"](function (err) { 1081 console.error('Search error:', err); 1082 self.handleSearchError(err); 1083 }); 1084 }; 1085 this.processSearchData = function (data) { 1086 var self = this; 1087 console.log('processSearchData called with:', data); 1088 console.log('Raw data.data:', data.data); 1089 this.items = this.map(data.data || [], function (item) { 1090 return self.parseItem(item); 1091 }); 1092 console.log('Processed items:', this.items); 1093 console.log('Items length:', this.items.length); 1094 this.q = data.search ? data.search.q : this.q; 1095 this.adv = Array.isArray(this.q); 1096 this.pagination = data.pagination || this.pagination; 1097 this.facet = data.facet || {}; 1098 console.log('State after processing:', { 1099 items: this.items.length, 1100 pagination: this.pagination, 1101 loading: this.loading, 1102 first_time: this.first_time 1103 }); 1104 this.loading = false; 1105 this.first_time = false; 1106 this.update(); 1107 console.log('Update called, final state:', { 1108 items: this.items.length, 1109 loading: this.loading, 1110 first_time: this.first_time, 1111 hasResults: this.hasResults() 1112 }); 1113 }; 1114 this.handleSearchError = function (error) { 1115 this.loading = false; 1116 this.items = []; 1117 this.pagination.total = 0; 1118 this.update(); 1119 }; 1120 this.updateData = function (data) { 1121 var self = this; 1122 var selected_filters = []; 1123 this.forEach(this.filters, function (value, key) { 1124 if (!value) return; 1125 var vals = value.split(','); 1126 self.forEach(vals, function (val) { 1127 var paths = val.split(':'); 1128 selected_filters.push({ 1129 key: key, 1130 title: self.support_filter[key], 1131 value: decodeURIComponent(paths[0]), 1132 name: decodeURIComponent(paths[1] || self.startCase(val)) 1133 }); 1134 }); 1135 }); 1136 var omitTypeFilter = function omitTypeFilter(filter) { 1137 return filter.key !== 'type'; 1138 }; 1139 this.selected_filters_by_group = this.groupBy(this.filterArray(selected_filters, omitTypeFilter), 'title'); 1140 var validFacetKeys = []; 1141 for (var key in this.facet) { 1142 if (this.facet.hasOwnProperty(key) && this.facet[key] && this.facet[key].buckets && this.facet[key].buckets.length > 0) { 1143 validFacetKeys.push(key); 1144 } 1145 } 1146 this.selectable_facets = validFacetKeys; 1147 this.pagination.min_page = Math.max(1, this.pagination.page - this.page_range); 1148 this.pagination.max_page = Math.min(Math.ceil(this.pagination.total / this.pagination.limit), this.pagination.page + this.page_range); 1149 }; 1150 this.initializeState(); 1151 this.submitSearchForm = function (params) { 1152 this.updateParams(params); 1153 this.pagination.page = 1; 1154 this.doSearch(); 1155 }.bind(this); 1156 this.clickSearchButton = function (params) { 1157 this.updateParams(params); 1158 this.pagination.page = 1; 1159 this.doSearch(); 1160 }.bind(this); 1161 this.clickPage = function (e) { 1162 e.preventDefault(); 1163 this.pagination.page = +e.currentTarget.dataset.page; 1164 this.doSearch(); 1165 }.bind(this); 1166 this.changeSearchType = function (params) { 1167 this.updateParams(params); 1168 this.pagination.page = 1; 1169 this.doSearch(); 1170 }.bind(this); 1171 this.changeSearchSort = function (e) { 1172 this.sort = e.currentTarget.value; 1173 this.pagination.page = 1; 1174 this.doSearch(); 1175 }.bind(this); 1176 this.changeFacetCheckbox = function (e) { 1177 var filter_key = e.currentTarget.name; 1178 var $checked = $('input[name="' + filter_key + '"]:checked'); 1179 var value = []; 1180 var self = this; 1181 $checked.each(function (i, el) { 1182 value.push(encodeURIComponent(el.value) + ':' + encodeURIComponent($(el).attr('data-label'))); 1183 }); 1184 if (value.length > 0) { 1185 this.filters[filter_key] = value.join(','); 1186 } else { 1187 delete this.filters[filter_key]; 1188 } 1189 this.pagination.page = 1; 1190 this.doSearch(); 1191 }.bind(this); 1192 this.clearFacetFilter = function (e) { 1193 e.preventDefault(); 1194 this.filters = {}; 1195 this.pagination.page = 1; 1196 this.doSearch(); 1197 }.bind(this); 1198 this.toggleFacetExpander = function (e) { 1199 e.preventDefault(); 1200 var filter_key = e.currentTarget.value; 1201 this.expanded_filter[filter_key] = !this.expanded_filter[filter_key]; 1202 }.bind(this); 1203 this.toggleSidebar = function (e) { 1204 e.preventDefault(); 1205 this.sidebar_active = !this.sidebar_active; 1206 }.bind(this); 1207 this.on('mount', function () { 1208 try { 1209 if (this.tags["kc-search-box"] && this.tags["kc-search-box"].populateParams) { 1210 this.updateParams(this.tags["kc-search-box"].populateParams()); 1211 }
1212 if (this.type === "all") { 1213 this.type = "library"; 1214 } 1215 this.pagination.page = 1; 1216 if (this.q || this.show_facet) { 1217 this.doSearch(); 1218 } else { 1219 console.log('No initial query, waiting for user input...'); 1220 this.first_time = false; 1221 this.loading = false; 1222 this.update(); 1223 } 1224 } catch (e) { 1225 console.error('Mount error:', e); 1226 } 1227 }.bind(this)); 1228 this.on('updated', function () { 1229 try { 1230 this.updateData(); 1231 if (typeof $ !== 'undefined') { 1232 $(this.root).find('.modal-trigger').leanModal(); 1233 $(this.root).find('.collapsible').collapsible(); 1234 } 1235 if (typeof app !== 'undefined' && app.check_image) { 1236 app.check_image(); 1237 } 1238 } catch (e) { 1239 console.error('Update error:', e); 1240 } 1241 }.bind(this)); 1242});
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.