PageSourceSearch

https://tto.um.si/wp-content/plugins/wedocs/assets/js/frontend.js?ver=1787865885

js um.si collected 2026-09-24 08:45:19 UTC 20,626 bytes, 557 lines download raw bytes

1+( function ( $ ) {
2  let pending_ajax = false;
3  const anchestorItem = document.querySelector( 'ul.doc-nav-list > li.page_item.current_page_ancestor' ) || null;
4
5  const weDocs = {
6    initialize() {
7      $( '.wedocs-feedback-wrap' ).on( 'click', 'a', this.feedback );
8      $( '#top-search-form .dropdown-menu' ).on( 'click', 'a', this.searchForm );
9      $( 'a.wedocs-print-article' ).on( 'click', this.printArticle );
10
11      // Sidebar toggle.
12      $( 'ul.doc-nav-list .page_item_has_children' ).on( 'click', '.wedocs-caret', this.toggleSidebar );
13
14      // Load single doc page search modal.
15      this.loadSingleDocSearchModal();
16
17      // Initialize Quick Search blocks
18      this.initializeQuickSearch();
19
20      // Handle modal actions.
21      $( 'a#wedocs-stuck-modal' ).on( 'click', this.showModal );
22      $( 'a#wedocs-modal-close' ).on( 'click', this.closeModal );
23      $( '#wedocs-modal-backdrop' ).on( 'click', this.closeModal );
24      $( 'form#wedocs-contact-modal-form' ).on( 'submit', this.contactHelp );
25      $( 'ul.wedocs-doc-sections > li svg' ).on( 'click', this.showSectionArticles );
26
27      // Single documentation page search modal actions.
28      $( '#wedocs-single-doc-search-modal' ).on( 'click', this.handleDocSearchModalBackDrop );
29      $( '.doc-search-container #doc-search-input' ).on( 'keyup', this.detectDocSearchInput.bind( this ) );
30      $( '.doc-search-container .doc-search-field .search-clean' ).on( 'click', this.cleanDocSearchInput );
31      $( '#wedocs-single-doc-search-modal .doc-search-cancel' ).on( 'click', this.closeSinglePageSearchModal );
32      $( '.wedocs-single-search-input' ).on( 'click', '.search-field, .search-submit, .short-key', this.showSinglePageSearchModal );
33    },
34
35    feedback ( e ) {
36      e.preventDefault();
37
38      // return if any request is in process already
39      if ( pending_ajax ) {
40        return;
41      }
42
43      pending_ajax = true;
44
45      const self = $( this ),
46        wrap = self.closest( '.feedback-content' ),
47        data = {
48          post_id: self.data( 'id' ),
49          type: self.data( 'type' ),
50          action: 'wedocs_ajax_feedback',
51          _wpnonce: weDocs_Vars.nonce,
52        };
53
54      $.post( weDocs_Vars.ajaxurl, data, function ( resp ) {
55        wrap.html( resp.data );
56
57        pending_ajax = false;
58      } );
59    },
60
61    searchForm ( e ) {
62      e.preventDefault();
63
64      const param = $( this ).attr( 'href' ).replace( '#', '' );
65      const concept = $( this ).text();
66
67      $( '#top-search-form span#search_concept' ).text( concept );
68      $( '.input-group #search_param' ).val( param );
69    },
70
71    printArticle ( e ) {
72      e.preventDefault();
73
74      // Try multiple selectors to find the article content
75      let article = $( this ).closest( 'article' );
76
77      // If no article found (e.g., in block themes), try common content selectors
78      if ( ! article.length ) {
79        article = $( '.wedocs-single-content article' );
80      }
81      if ( ! article.length ) {
82        article = $( '.entry-content' ).closest( 'article' );
83      }
84      if ( ! article.length ) {
85        article = $( '.entry-content' );
86      }
87
88      // If still no content found, bail out gracefully
89      if ( ! article.length ) {
90        window.print();
91        return;
92      }
93
94      const mywindow = window.open( '', 'my div', 'height=600,width=800' );
95      mywindow.document.write( '<html><head><title>Print Article</title>' );
96      mywindow.document.write(
97        '<link rel="stylesheet" href="' +
98          weDocs_Vars.style +
99          '" type="text/css" media="all" />'
100      );
101      mywindow.document.write( '</head><body >' );
102      mywindow.document.write( article.html() );
103      mywindow.document.write(
104        '<div class="powered-by">' + weDocs_Vars.powered + '</div>'
105      );
106      mywindow.document.write( '</body></html>' );
107
108      mywindow.document.close(); // necessary for IE >= 10
109      mywindow.focus(); // necessary for IE >= 10
110
111      // Event-driven print handling
112      let printCompleted = false;
113      let fallbackTimeout = null;
114      
115      // Function to handle print completion and cleanup
116      const handlePrintComplete = function() {
117        if (printCompleted) return; // Prevent multiple calls
118        printCompleted = true;
119        
120        // Clear fallback timeout if it exists
121        if (fallbackTimeout) {
122          clearTimeout(fallbackTimeout);
123          fallbackTimeout = null;
124        }
125        
126        // Remove event listeners to prevent memory leaks
127        if (mywindow.removeEventListener) {
128          mywindow.removeEventListener('afterprint', handlePrintComplete);
129        } else if (mywindow.detachEvent) {
130          mywindow.detachEvent('onafterprint', handlePrintComplete);
131        }
132        
133        // Close the window
134        mywindow.close();
135      };
136      
137      // Function to initiate printing
138      const initiatePrint = function() {
139        // Attach afterprint event listener before printing
140        if (mywindow.addEventListener) {
141          mywindow.addEventListener('afterprint', handlePrintComplete, false);
142        } else if (mywindow.attachEvent) {
143          // IE8 and older support
144          mywindow.attachEvent('onafterprint', handlePrintComplete);
145        }
146        
147        // Set up fallback timeout as last resort (for browsers that don't support afterprint)
148        fallbackTimeout = setTimeout(function() {
149          if (!printCompleted) {
150            console.warn('Print afterprint event not fired, using fallback cleanup');
151            handlePrintComplete();
152          }
153        }, 30000); // 30 second fallback timeout
154        
155        // Initiate print
156        mywindow.print();
157      };
158      
159      // Wait for window to load before printing
160      if (mywindow.addEventListener) {
161        mywindow.addEventListener('load', initiatePrint, false);
162      } else if (mywindow.attachEvent) {
163        // IE8 and older support
164        mywindow.attachEvent('onload', initiatePrint);
165      } else {
166        // Fallback for very old browsers
167        mywindow.onload = initiatePrint;
168      }
169
170      return true;
171    },
172
173    showModal ( e ) {
174      e.preventDefault();
175
176      $( '#wedocs-modal-backdrop' ).show();
177      $( '#wedocs-contact-modal' ).show();
178      $( 'body' ).addClass( 'wedocs-overflow-hidden' );
179    },
180
181    closeModal ( e ) {
182      e.preventDefault();
183
184      $( '#wedocs-modal-backdrop' ).hide();
185      $( '#wedocs-contact-modal' ).hide();
186      $( 'body' ).removeClass( 'wedocs-overflow-hidden' );
187    },
188
189    contactHelp ( e ) {
190      e.preventDefault();
191
192      const self = $( this ),
193        submit = self.find( 'input[type=submit]' ),
194        body = self.closest( '.wedocs-modal-body' ),
195        data = self.serialize() + '&_wpnonce=' + weDocs_Vars.nonce;
196
197      submit.prop( 'disabled', true );
198
199      $.post( weDocs_Vars.ajaxurl, data, function ( resp ) {
200        if ( resp.success === false ) {
201          submit.prop( 'disabled', false );
202          $( '#wedocs-modal-errors', body )
203            .empty()
204            .append(
205              '<div class="wedocs-alert wedocs-alert-danger">' +
206                resp.data +
207                '</div>'
208            );
209        } else {
210          body
211            .empty()
212            .append(
213              '<div class="wedocs-alert wedocs-alert-success">' +
214                resp.data +
215                '</div>'
216            );
217        }
218      } );
219    },
220
221    toggleSidebar ( e ) {
222      e.preventDefault();
223      const self = $( this ),
224        parent = self.closest( '.page_item' );
225
226      if ( parent.hasClass( 'wd-state-closed' ) ) {
227        parent.removeClass( 'wd-state-closed' ).addClass( 'wd-state-open' );
228      } else {
229        parent.removeClass( 'wd-state-open' ).addClass( 'wd-state-closed' );
230      }
231    },
232
233    loadSingleDocSearchModal( e ) {
234      if ( !weDocs_Vars.isSingleDoc && !weDocs_Vars.isVendorDashboard ) {
235        return;
236      }
237
238      const data = {
239        action   : weDocs_Vars.isVendorDashboard ? 'wedocs_get_vendor_docs' : 'wedocs_get_docs',
240        _wpnonce : weDocs_Vars.nonce,
241      };
242
243      $.ajax({
244        data,
245        url      : weDocs_Vars.ajaxurl,
246        type     : 'POST',
247        error    : ( e ) => console.log( e ),
248        complete : ( response ) => {
249          if ( response?.responseJSON?.data ) {
250            this.docs = response?.responseJSON?.data;
251          }
252        },
253      });
254
255      // Mount doc single page search modal.
256      if ( weDocs_Vars.searchModal ) {
257        const mountDiv = document.createElement( 'div' );
258        mountDiv.setAttribute( 'id', 'wedocs-single-doc-search-modal' );
259        mountDiv.innerHTML = weDocs_Vars.searchModal;
260        document.body.appendChild( mountDiv );
261      }
262
263      document.addEventListener( 'keydown', ( event ) => {
264        // Bind single page search modal with (ctrl/command + k).
265        if ( ( event.ctrlKey || event.metaKey ) && event.key === 'k' ) {
266          this.showSinglePageSearchModal();
267        }
268
269        // Bind single page search modal hiding option with escape button.
270        if ( event.keyCode === 27 && event.key === 'Escape' ) {
271          this.closeSinglePageSearchModal();
272        }
273      } );
274    },
275
276    showSectionArticles ( e ) {
277      $( this ).toggleClass( 'active' ).closest( 'li' ).next( '.children' ).toggleClass( 'active' );
278    },
279
280    cleanDocSearchInput ( e ) {
281      $( this ).hide();
282      $( this ).prev( 'input[type="text"]' ).val( null ).focus();
283      $( '.doc-search-dropdown-container .doc-search-hits' )
284        .html( `<div class='doc-empty-search'>${ weDocs_Vars.searchBlankMsg }</div>` );
285    },
286
287    detectDocSearchInput ( e ) {
288      const searchValue = e.target.value,
289        searchDocs = this.getSearchDocs( searchValue ),
290        searchEmpty = `<div class='doc-empty-search'>${ weDocs_Vars.searchEmptyMsg }</div>`,
291        searchKeyEmpty = `<div class='doc-empty-search'>${ weDocs_Vars.searchBlankMsg }</div>`;
292
293      // Searched docs list.
294      const $ulNode = this.getListTemplate( searchDocs );
295
296      // Render search cleaner icon.
297      $( e.target ).next( '.search-clean' )[0].style.display = `${ searchValue ? 'block' : 'none' }`;
298
299      // Render search list or necessary message.
300      $( '.doc-search-dropdown-container .doc-search-hits' ).html(
301        ( searchValue ? ( searchDocs.length ? $ulNode : searchEmpty ) : searchKeyEmpty )
302      );
303    },
304
305    getListTemplate ( searchDocs ) {
306      const ulNode = document.createElement( 'ul' );
307      ulNode.setAttribute( 'id', 'doc-search-list' );
308
309      // Build a URL for a doc, using dashboard-scoped links when on the vendor dashboard.
310      const buildDocUrl = ( doc ) => {
311        if ( ! doc ) {
312          return '#';
313        }
314        if ( weDocs_Vars.isVendorDashboard && weDocs_Vars.vendorDocsBaseUrl ) {
315          return weDocs_Vars.vendorDocsBaseUrl + '?doc_id=' + doc.ID;
316        }
317        return doc?.permalink || doc?.guid;
318      };
319
320      searchDocs.forEach( ( data, index ) => {
321        const docItem = data?.article || data?.section || data?.parent;
322        let url = buildDocUrl( docItem ),
323          title = docItem?.post_title,
324          parentNavigation = data?.section ?
325            `<div class='parent-doc-nav'>
326              ${ weDocs_Vars.docNavLabel }
327              <span data-url="${ buildDocUrl( data?.parent ) }" class="doc-search-hit-path">
328                ${ this.extractedTitle( data?.parent?.post_title, 35 ) }
329              </span>
330            </div>` : '',
331          sectionNavigation = data?.article ?
332            `<div class='section-doc-nav'>
333              ${ weDocs_Vars.sectionNavLabel }
334              <span data-url="${ buildDocUrl( data?.section ) }" class="doc-search-hit-path">
335                ${ this.extractedTitle( data?.section?.post_title, 35 ) }
336              </span>
337            </div>` : '';
338
339        title = this.extractedTitle( title );
340        const bootstrapTemplate = $( '.wedocs-single-wrap .doc-nav-list a' ).hasClass( 'bootstrap' ) ? 'bootstrap ' : '',
341          tailwindTemplate = $( '.wedocs-single-wrap .doc-nav-list a' ).hasClass( 'tailwind' ) ? 'tailwind ' : '';
342
343        // Make search results as list node.
344        const liNode = document.createElement( 'li' );
345        liNode.setAttribute( 'class', 'doc-search-hit' );
346        liNode.setAttribute( 'id', `doc-search-item-${index}` );
347        liNode.setAttribute( 'role', 'option' );
348        liNode.setAttribute( 'aria-selected', 'false' );
349        const linkTarget = weDocs_Vars.isVendorDashboard ? '_self' : '_blank';
350        liNode.innerHTML = `
351          <a
352            href="${ url }"
353            target="${ linkTarget }"
354            class="${ bootstrapTemplate + tailwindTemplate }doc-search-hit-result"
355          >
356            <div class="doc-search-hit-container">
357              <div class="doc-search-hit-icon">
358                <svg width="12" height="18" viewBox="0 0 14 18" fill="none">
359                  <path
360                    stroke="#3B82F6"
361                    stroke-width="2"
362                    stroke-linecap="round"
363                    stroke-linejoin="round"
364                    d="M4.49984 9H9.49984M4.49984 12.3333H9.49984M11.1665 16.5H2.83317C1.9127 16.5 1.1665 15.7538 1.1665 14.8333V3.16667C1.1665 2.24619 1.9127 1.5 2.83317 1.5H7.48799C7.70901 1.5 7.92097 1.5878 8.07725 1.74408L12.5891 6.25592C12.7454 6.4122 12.8332 6.62416 12.8332 6.84518V14.8333C12.8332 15.7538 12.087 16.5 11.1665 16.5
364Z"
365                  />
366                </svg>
367              </div>
368              <div class="doc-search-hit-content-wrapper">
369                <div class="doc-search-hit-title">${ title }</div>
370                ${ ( parentNavigation || sectionNavigation ) ? '<hr />' : '' }
371                <div class='wd-docs-navigation'>
372                  ${ parentNavigation + sectionNavigation }
373                </div>
374              </div>
375            </div>
376          </a>`;
377
378        // Handle navigation redirection manually.
379        liNode.querySelectorAll( '.doc-search-hit-path' ).forEach(
380          searchPath => searchPath?.addEventListener( 'click', function ( event ) {
381            event.preventDefault();
382            window.open( $( this ).data( 'url' ), '_blank' );
383          })
384        );
385
386        try {
387          liNode.querySelectorAll( '.doc-search-hit-result' ).forEach(
388            list => {
389              // Update list icon background & color.
390              const hitIcon = list?.querySelector( '.doc-search-hit-icon' );
391              const hitIconPath = hitIcon?.querySelector( 'path' );
392              
393              if ( hitIcon ) {
394                hitIcon.style.background = weDocs_Vars?.searchModalColors?.active_shade_color;
395              }
396              
397              if ( hitIconPath ) {
398                hitIconPath.style.stroke = weDocs_Vars?.searchModalColors?.active_primary_color;
399              }
400
401              const parentDocNav = list?.querySelector( '.parent-doc-nav' ),
402                sectionDocNav = list?.querySelector( '.section-doc-nav' ),
403                parentNavSearchHitPath = parentDocNav?.querySelector( '.doc-search-hit-path' ),
404                sectionNavSearchHitPath = sectionDocNav?.querySelector( '.doc-search-hit-path' );
405
406            if ( parentNavSearchHitPath ) {
407              // Update parent nav shade color.
408              parentNavSearchHitPath.style.background = weDocs_Vars?.searchModalColors?.active_shade_color;
409            }
410
411            if ( sectionNavSearchHitPath ) {
412              // Update section nav shade color.
413              sectionNavSearchHitPath.style.background = weDocs_Vars?.searchModalColors?.active_shade_color;
414            }
415
416            if ( parentDocNav ) {
417              // Update parent text color on mouse over.
418              parentDocNav.addEventListener( 'mouseover', function() {
419                parentNavSearchHitPath.style.color = weDocs_Vars?.searchModalColors?.active_primary_color;
420              } );
421
422              // Update parent text color on mouse out.
423              parentDocNav.addEventListener( 'mouseout', function() {
424                parentNavSearchHitPath.style.color = '#6B7280';
425              } );
426            }
427
428            if ( sectionDocNav ) {
429              // Update section text color on mouse over.
430              sectionDocNav.addEventListener( 'mouseover', function() {
431                sectionNavSearchHitPath.style.color = weDocs_Vars?.searchModalColors?.active_primary_color;
432              } );
433
434              // Update section text color on mouse over.
435              sectionDocNav.addEventListener( 'mouseout', function() {
436                sectionNavSearchHitPath.style.color = '#6B7280';
437              } );
438            }
439
440            // Update doc lists background color on mouse over.
441            list.addEventListener( 'mouseover', function() {
442              this.style.background = weDocs_Vars?.searchModalColors?.active_primary_color;
443            } );
444
445            // Update doc lists background color on mouse out.
446            list.addEventListener( 'mouseout', function() {
447              this.style.background = '#fff';
448            } );
449          }
450          );
451        } catch ( error ) {
452          console.warn( 'Error processing search results:', error );
453        }
454
455        ulNode.appendChild(liNode);
456      });
457
458      return $( ulNode );
459    },
460
461    getSearchDocs( searchValue ) {
462      const searchDocs = [];
463      const searchResult = this?.docs?.all_docs?.filter( doc => {
464        if ( doc?.post_status !== 'publish' ) return;
465        return doc?.post_title?.toLowerCase().includes( searchValue?.toLowerCase() );
466      } );
467
468      searchResult?.map( doc => {
469        const docObj = {};
470        if ( doc?.post_parent === 0 ) {
471          docObj.parent = doc;
472        }
473
474        if ( this?.docs?.sections?.find( sectionDoc => sectionDoc.ID === doc?.ID ) ) {
475          docObj.section = doc;
476          docObj.parent = this?.docs?.parents?.find( parentDoc => parentDoc?.ID === doc?.post_parent );
477        }
478
479        if ( this?.docs?.articles?.find( articleDoc => articleDoc.ID === doc?.ID ) ) {
480          docObj.article = doc;
481          docObj.section = this?.docs?.sections?.find( sectionDoc => sectionDoc?.ID === doc?.post_parent );
482          docObj.parent = this?.docs?.parents?.find( parentDoc => parentDoc?.ID === docObj.section?.post_parent );
483        }
484
485        searchDocs.push( docObj );
486      } );
487
488      return searchDocs;
489    },
490
491    showSinglePageSearchModal ( e ) {
492      const modal = $( '#wedocs-single-doc-search-modal' );
493      if ( modal.length ) {
494        modal.addClass( 'active' );
495        modal.find( '#doc-search-input' ).focus();
496      }
497    },
498
499    closeSinglePageSearchModal ( e ) {
500      const modal = $( '#wedocs-single-doc-search-modal' );
501      if ( modal.length ) {
502        modal.removeClass( 'active' );
503      }
504    },
505
506    extractedTitle ( title, length = 190 ) {
507      const extractedString = title?.substr( 0, length );
508      return extractedString?.length >= length ? `${ extractedString }...` : extractedString;
509    },
510
511    handleDocSearchModalBackDrop ( e ) {
512      if ( !e.target.closest( '.doc-search-modal' ) ) {
513        $( this ).removeClass( 'active' );
514      }
515    },
516
517    initializeQuickSearch() {
518      // Initialize Quick Search blocks functionality
519      // The Quick Search blocks have their own JavaScript built into their render.php
520      // This method is called to ensure compatibility but the actual functionality
521      // is handled by the individual Quick Search block instances
522      
523      // Check if there are any Quick Search blocks on the page
524      const quickSearchBlocks = document.querySelectorAll('.wedocs-quick-search-block');
525      
526      if (quickSearchBlocks.length > 0) {
527        // The Quick Search blocks are self-contained with their own JavaScript
528        // No additional initialization is needed here as each block handles its own functionality
529        // Both search systems can coexist without conflicts
530      }
531    },
532  };
533
534  $( function () {
535    // Handle hash router.
536      // This is commented out because it causes the page to reload when the hash is present.
537    // if ( window.location.hash ) {
538    //   window.location = window.location.pathname;
539    // }
540
541    // Handle navigation caret.
542    if ( anchestorItem && ! anchestorItem.classList.contains( 'wd-state-open' ) ) {
543      anchestorItem.classList.add( 'wd-state-open' );
544      anchestorItem.classList.remove( 'wd-state-closed' );
545    }
546
547    weDocs.initialize();
548  } );
549
550  // initialize anchor.js
551  anchors.options = {
552    icon: '#',
553  };
554  anchors.add(
555    '.wedocs-single-content .entry-content > h2, .wedocs-single-content .entry-content > h3'
556  );
557} )( jQuery );

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.