PageSourceSearch

https://luxuantao.github.io/js/src/utils.js?v=5.1.2

js luxuantao.github.io collected 2026-10-03 09:32:54 UTC 8,318 bytes, 276 lines download raw bytes

1/* global NexT: true */
2
3NexT.utils = NexT.$u = {
4  /**
5   * Wrap images with fancybox support.
6   */
7  wrapImageWithFancyBox: function () {
8    $('.content img')
9      .not('[hidden]')
10      .not('.group-picture img, .post-gallery img')
11      .each(function () {
12        var $image = $(this);
13        var imageTitle = $image.attr('title');
14        var $imageWrapLink = $image.parent('a');
15
16        if ($imageWrapLink.size() < 1) {
17	        var imageLink = ($image.attr('data-original')) ? this.getAttribute('data-original') : this.getAttribute('src');
18          $imageWrapLink = $image.wrap('<a href="' + imageLink + '"></a>').parent('a');
19        }
20
21        $imageWrapLink.addClass('fancybox fancybox.image');
22        $imageWrapLink.attr('rel', 'group');
23
24        if (imageTitle) {
25          $imageWrapLink.append('<p class="image-caption">' + imageTitle + '</p>');
26
27          //make sure img title tag will show correctly in fancybox
28          $imageWrapLink.attr('title', imageTitle);
29        }
30      });
31
32    $('.fancybox').fancybox({
33      helpers: {
34        overlay: {
35          locked: false
36        }
37      }
38    });
39  },
40
41  lazyLoadPostsImages: function () {
42    $('#posts').find('img').lazyload({
43      //placeholder: '/images/loading.gif',
44      effect: 'fadeIn',
45      threshold : 0
46    });
47  },
48
49  /**
50   * Tabs tag listener (without twitter bootstrap).
51   */
52  registerTabsTag: function () {
53    var tNav = '.tabs ul.nav-tabs ';
54
55    // Binding `nav-tabs` & `tab-content` by real time permalink changing.
56    $(function() {
57      $(window).bind('hashchange', function() {
58        var tHash = location.hash;
59        if (tHash !== '') {
60          $(tNav + 'li:has(a[href="' + tHash + '"])').addClass('active').siblings().removeClass('active');
61          $(tHash).addClass('active').siblings().removeClass('active');
62        }
63      }).trigger('hashchange');
64    });
65
66    $(tNav + '.tab').on('click', function (href) {
67      href.preventDefault();
68      // Prevent selected tab to select again.
69      if(!$(this).hasClass('active')){
70
71        // Add & Remove active class on `nav-tabs` & `tab-content`.
72        $(this).addClass('active').siblings().removeClass('active');
73        var tActive = $(this).find('a').attr('href');
74        $(tActive).addClass('active').siblings().removeClass('active');
75
76        // Clear location hash in browser if #permalink exists.
77        if (location.hash !== '') {
78          history.pushState('', document.title, window.location.pathname + window.location.search);
79        }
80      }
81    });
82
83  },
84
85  registerESCKeyEvent: function () {
86    $(document).on('keyup', function (event) {
87      var shouldDismissSearchPopup = event.which === 27 &&
88        $('.search-popup').is(':visible');
89      if (shouldDismissSearchPopup) {
90        $('.search-popup').hide();
91        $('.search-popup-overlay').remove();
92        $('body').css('overflow', '');
93      }
94    });
95  },
96
97  registerBackToTop: function () {
98    var THRESHOLD = 50;
99    var $top = $('.back-to-top');
100
101    $(window).on('scroll', function () {
102      $top.toggleClass('back-to-top-on', window.pageYOffset > THRESHOLD);
103
104      var scrollTop = $(window).scrollTop();
105      var docHeight = $('#content').height();
106      var winHeight = $(window).height();
107      var contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : ($(document).height() - winHeight);
108      var scrollPercent = (scrollTop) / (contentMath);
109      var scrollPercentRounded = Math.round(scrollPercent*100);
110      var scrollPercentMaxed = (scrollPercentRounded > 100) ? 100 : scrollPercentRounded;
111      $('#scrollpercent>span').html(scrollPercentMaxed);
112    });
113
114    $top.on('click', function () {
115      $('body').velocity('scroll');
116    });
117  },
118
119  /**
120   * Transform embedded video to support responsive layout.
121   * @see http://toddmotto.com/fluid-and-responsive-youtube-and-vimeo-videos-with-fluidvids-js/
122   */
123  embeddedVideoTransformer: function () {
124    var $iframes = $('iframe');
125
126    // Supported Players. Extend this if you need more players.
127    var SUPPORTED_PLAYERS = [
128      'www.youtube.com',
129      'player.vimeo.com',
130      'player.youku.com',
131      'music.163.com',
132      'www.tudou.com'
133    ];
134    var pattern = new RegExp( SUPPORTED_PLAYERS.join('|') );
135
136    $iframes.each(function () {
137      var iframe = this;
138      var $iframe = $(this);
139      var oldDimension = getDimension($iframe);
140      var newDimension;
141
142      if (this.src.search(pattern) > 0) {
143
144        // Calculate the video ratio based on the iframe's w/h dimensions
145        var videoRatio = getAspectRadio(oldDimension.width, oldDimension.height);
146
147        // Replace the iframe's dimensions and position the iframe absolute
148        // This is the trick to emulate the video ratio
149        $iframe.width('100%').height('100%')
150          .css({
151            position: 'absolute',
152            top: '0',
153            left: '0'
154          });
155
156
157        // Wrap the iframe in a new <div> which uses a dynamically fetched padding-top property
158        // based on the video's w/h dimensions
159        var wrap = document.createElement('div');
160        wrap.className = 'fluid-vids';
161        wrap.style.position = 'relative';
162        wrap.style.marginBottom = '20px';
163        wrap.style.width = '100%';
164        wrap.style.paddingTop = videoRatio + '%';
165        // Fix for appear inside tabs tag.
166        (wrap.style.paddingTop === '') && (wrap.style.paddingTop = '50%');
167
168        // Add the iframe inside our newly created <div>
169        var iframeParent = iframe.parentNode;
170        iframeParent.insertBefore(wrap, iframe);
171        wrap.appendChild(iframe);
172
173        // Additional adjustments for 163 Music
174        if (this.src.search('music.163.com') > 0) {
175          newDimension = getDimension($iframe);
176          var shouldRecalculateAspect = newDimension.width > oldDimension.width ||
177                                        newDimension.height < oldDimension.height;
178
179          // 163 Music Player has a fixed height, so we need to reset the aspect radio
180          if (shouldRecalculateAspect) {
181            wrap.style.paddingTop = getAspectRadio(newDimension.width, oldDimension.height) + '%';
182          }
183        }
184      }
185    });
186
187    function getDimension($element) {
188      return {
189        width: $element.width(),
190        height: $element.height()
191      };
192    }
193
194    function getAspectRadio(width, height) {
195      return height / width * 100;
196    }
197  },
198
199  /**
200   * Add `menu-item-active` class name to menu item
201   * via comparing location.path with menu item's href.
202   */
203  addActiveClassToMenuItem: function () {
204    var path = window.location.pathname;
205    path = path === '/' ? path : path.substring(0, path.length - 1);
206    $('.menu-item a[href^="' + path + '"]:first').parent().addClass('menu-item-active');
207  },
208
209  hasMobileUA: function () {
210    var nav = window.navigator;
211    var ua = nav.userAgent;
212    var pa = /iPad|iPhone|Android|Opera Mini|BlackBerry|webOS|UCWEB|Blazer|PSP|IEMobile|Symbian/g;
213
214    return pa.test(ua);
215  },
216
217  isTablet: function () {
218    return window.screen.width < 992 && window.screen.width > 767 && this.hasMobileUA();
219  },
220
221  isMobile: function () {
222    return window.screen.width < 767 && this.hasMobileUA();
223  },
224
225  isDesktop: function () {
226    return !this.isTablet() && !this.isMobile();
227  },
228
229  /**
230   * Escape meta symbols in jQuery selectors.
231   *
232   * @param selector
233   * @returns {string|void|XML|*}
234   */
235  escapeSelector: function (selector) {
236    return selector.replace(/[!"$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, '\\$&');
237  },
238
239  displaySidebar: function () {
240    if (!this.isDesktop() || this.isPisces() || this.isGemini()) {
241      return;
242    }
243    $('.sidebar-toggle').trigger('click');
244  },
245
246  isMist: function () {
247    return CONFIG.scheme === 'Mist';
248  },
249
250  isPisces: function () {
251    return CONFIG.scheme === 'Pisces';
252  },
253
254  isGemini: function () {
255    return CONFIG.scheme === 'Gemini';
256  },
257
258  getScrollbarWidth: function () {
259    var $div = $('<div />').addClass('scrollbar-measure').prependTo('body');
260    var div = $div[0];
261    var scrollbarWidth = div.offsetWidth - div.clientWidth;
262
263    $div.remove();
264
265    return scrollbarWidth;
266  },
267
268  /**
269   * Affix behaviour for Sidebar.
270   *
271   * @returns {Boolean}
272   */
273  needAffix: function () {
274    return this.isPisces() || this.isGemini();
275  }
276};

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.