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.