PageSourceSearch

https://tanhao.me/js/src/utils.js?v=5.1.0

js tanhao.me collected 2026-09-26 10:02:57 UTC 5,756 bytes, 207 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').not('.group-picture img, .post-gallery img').each(function () {
9
10      var $image = $(this);
11      var imageTitle = $image.attr('title');
12      var $imageWrapLink = $image.parent('a');
13
14      if ($imageWrapLink.size() < 1) {
15        $imageWrapLink = $image.wrap('<a href="' + this.getAttribute('src') + '"></a>').parent('a');
16      }
17
18      $imageWrapLink.addClass('fancybox');
19      $imageWrapLink.attr('rel', 'group');
20
21      if (imageTitle) {
22        $imageWrapLink.append('<p class="image-caption">' + imageTitle + '</p>');
23        $imageWrapLink.attr('title', imageTitle); //make sure img title tag will show correctly in fancybox
24      }
25    });
26
27    $('.fancybox').fancybox({
28      helpers: {
29        overlay: {
30          locked: false
31        }
32      }
33    });
34  },
35
36  lazyLoadPostsImages: function () {
37    $('#posts').find('img').lazyload({
38      placeholder: '/images/loading.gif',
39      effect: 'fadeIn'
40    });
41  },
42
43  registerBackToTop: function () {
44    var THRESHOLD = 50;
45    var $top = $('.back-to-top');
46
47    $(window).on('scroll', function () {
48      $top.toggleClass('back-to-top-on', window.pageYOffset > THRESHOLD);
49    });
50
51    $top.on('click', function () {
52      $('body').velocity('scroll');
53    });
54  },
55
56  /**
57   * Transform embedded video to support responsive layout.
58   * @see http://toddmotto.com/fluid-and-responsive-youtube-and-vimeo-videos-with-fluidvids-js/
59   */
60  embeddedVideoTransformer: function () {
61    var $iframes = $('iframe');
62
63    // Supported Players. Extend this if you need more players.
64    var SUPPORTED_PLAYERS = [
65      'www.youtube.com',
66      'player.vimeo.com',
67      'player.youku.com',
68      'music.163.com',
69      'www.tudou.com'
70    ];
71    var pattern = new RegExp( SUPPORTED_PLAYERS.join('|') );
72
73    $iframes.each(function () {
74      var iframe = this;
75      var $iframe = $(this);
76      var oldDimension = getDimension($iframe);
77      var newDimension;
78
79      if (this.src.search(pattern) > 0) {
80
81        // Calculate the video ratio based on the iframe's w/h dimensions
82        var videoRatio = getAspectRadio(oldDimension.width, oldDimension.height);
83
84        // Replace the iframe's dimensions and position the iframe absolute
85        // This is the trick to emulate the video ratio
86        $iframe.width('100%').height('100%')
87          .css({
88            position: 'absolute',
89            top: '0',
90            left: '0'
91          });
92
93
94        // Wrap the iframe in a new <div> which uses a dynamically fetched padding-top property
95        // based on the video's w/h dimensions
96        var wrap = document.createElement('div');
97        wrap.className = 'fluid-vids';
98        wrap.style.position = 'relative';
99        wrap.style.marginBottom = '20px';
100        wrap.style.width = '100%';
101        wrap.style.paddingTop = videoRatio + '%';
102
103        // Add the iframe inside our newly created <div>
104        var iframeParent = iframe.parentNode;
105        iframeParent.insertBefore(wrap, iframe);
106        wrap.appendChild(iframe);
107
108        // Additional adjustments for 163 Music
109        if (this.src.search('music.163.com') > 0) {
110          newDimension = getDimension($iframe);
111          var shouldRecalculateAspect = newDimension.width > oldDimension.width ||
112                                        newDimension.height < oldDimension.height;
113
114          // 163 Music Player has a fixed height, so we need to reset the aspect radio
115          if (shouldRecalculateAspect) {
116            wrap.style.paddingTop = getAspectRadio(newDimension.width, oldDimension.height) + '%';
117          }
118        }
119      }
120    });
121
122    function getDimension($element) {
123      return {
124        width: $element.width(),
125        height: $element.height()
126      };
127    }
128
129    function getAspectRadio(width, height) {
130      return height / width * 100;
131    }
132  },
133
134  /**
135   * Add `menu-item-active` class name to menu item
136   * via comparing location.path with menu item's href.
137   */
138  addActiveClassToMenuItem: function () {
139    var path = window.location.pathname;
140    path = path === '/' ? path : path.substring(0, path.length - 1);
141    $('.menu-item a[href="' + path + '"]').parent().addClass('menu-item-active');
142  },
143
144  hasMobileUA: function () {
145    var nav = window.navigator;
146    var ua = nav.userAgent;
147    var pa = /iPad|iPhone|Android|Opera Mini|BlackBerry|webOS|UCWEB|Blazer|PSP|IEMobile|Symbian/g;
148
149    return pa.test(ua);
150  },
151
152  isTablet: function () {
153    return window.screen.width < 992 && window.screen.width > 767 && this.hasMobileUA();
154  },
155
156  isMobile: function () {
157    return window.screen.width < 767 && this.hasMobileUA();
158  },
159
160  isDesktop: function () {
161    return !this.isTablet() && !this.isMobile();
162  },
163
164  /**
165   * Escape meta symbols in jQuery selectors.
166   *
167   * @param selector
168   * @returns {string|void|XML|*}
169   */
170  escapeSelector: function (selector) {
171    return selector.replace(/[!"$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, '\\$&');
172  },
173
174  displaySidebar: function () {
175    if (!this.isDesktop() || this.isPisces()) {
176      return;
177    }
178    $('.sidebar-toggle').trigger('click');
179  },
180
181  isMist: function () {
182    return CONFIG.scheme === 'Mist';
183  },
184
185  isPisces: function () {
186    return CONFIG.scheme === 'Pisces';
187  },
188
189  getScrollbarWidth: function () {
190    var $div = $('<div />').addClass('scrollbar-measure').prependTo('body');
191    var div = $div[0];
192    var scrollbarWidth = div.offsetWidth - div.clientWidth;
193
194    $div.remove();
195
196    return scrollbarWidth;
197  },
198
199  /**
200   * Affix behaviour for Sidebar.
201   *
202   * @returns {Boolean}
203   */
204  needAffix: function () {
205    return this.isPisces();
206  }
207};

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.