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 contentVisibilityHeight = NexT.utils.getContentVisibilityHeight(); 106 var scrollPercent = (scrollTop) / (contentVisibilityHeight); 107 var scrollPercentRounded = Math.round(scrollPercent*100); 108 var scrollPercentMaxed = (scrollPercentRounded > 100) ? 100 : scrollPercentRounded; 109 $('#scrollpercent>span').html(scrollPercentMaxed); 110 }); 111 112 $top.on('click', function () { 113 $('body').velocity('scroll'); 114 }); 115 }, 116 117 /** 118 * Transform embedded video to support responsive layout. 119 * @see http://toddmotto.com/fluid-and-responsive-youtube-and-vimeo-videos-with-fluidvids-js/ 120 */ 121 embeddedVideoTransformer: function () { 122 var $iframes = $('iframe'); 123 124 // Supported Players. Extend this if you need more players. 125 var SUPPORTED_PLAYERS = [ 126 'www.youtube.com', 127 'player.vimeo.com', 128 'player.youku.com', 129 'music.163.com', 130 'www.tudou.com' 131 ]; 132 var pattern = new RegExp( SUPPORTED_PLAYERS.join('|') ); 133 134 $iframes.each(function () { 135 var iframe = this; 136 var $iframe = $(this); 137 var oldDimension = getDimension($iframe); 138 var newDimension; 139 140 if (this.src.search(pattern) > 0) { 141 142 // Calculate the video ratio based on the iframe's w/h dimensions 143 var videoRatio = getAspectRadio(oldDimension.width, oldDimension.height); 144 145 // Replace the iframe's dimensions and position the iframe absolute 146 // This is the trick to emulate the video ratio 147 $iframe.width('100%').height('100%') 148 .css({ 149 position: 'absolute', 150 top: '0', 151 left: '0' 152 }); 153 154 155 // Wrap the iframe in a new <div> which uses a dynamically fetched padding-top property 156 // based on the video's w/h dimensions 157 var wrap = document.createElement('div'); 158 wrap.className = 'fluid-vids'; 159 wrap.style.position = 'relative'; 160 wrap.style.marginBottom = '20px'; 161 wrap.style.width = '100%'; 162 wrap.style.paddingTop = videoRatio + '%'; 163 // Fix for appear inside tabs tag. 164 (wrap.style.paddingTop === '') && (wrap.style.paddingTop = '50%'); 165 166 // Add the iframe inside our newly created <div> 167 var iframeParent = iframe.parentNode; 168 iframeParent.insertBefore(wrap, iframe); 169 wrap.appendChild(iframe); 170 171 // Additional adjustments for 163 Music 172 if (this.src.search('music.163.com') > 0) { 173 newDimension = getDimension($iframe); 174 var shouldRecalculateAspect = newDimension.width > oldDimension.width || 175 newDimension.height < oldDimension.height; 176
177 // 163 Music Player has a fixed height, so we need to reset the aspect radio 178 if (shouldRecalculateAspect) { 179 wrap.style.paddingTop = getAspectRadio(newDimension.width, oldDimension.height) + '%'; 180 } 181 } 182 } 183 }); 184 185 function getDimension($element) { 186 return { 187 width: $element.width(), 188 height: $element.height() 189 }; 190 } 191 192 function getAspectRadio(width, height) { 193 return height / width * 100; 194 } 195 }, 196 197 /** 198 * Add `menu-item-active` class name to menu item 199 * via comparing location.path with menu item's href. 200 */ 201 addActiveClassToMenuItem: function () { 202 var path = window.location.pathname; 203 path = path === '/' ? path : path.substring(0, path.length - 1); 204 $('.menu-item a[href^="' + path + '"]:first').parent().addClass('menu-item-active'); 205 }, 206 207 hasMobileUA: function () { 208 var nav = window.navigator; 209 var ua = nav.userAgent; 210 var pa = /iPad|iPhone|Android|Opera Mini|BlackBerry|webOS|UCWEB|Blazer|PSP|IEMobile|Symbian/g; 211 212 return pa.test(ua); 213 }, 214 215 isTablet: function () { 216 return window.screen.width < 992 && window.screen.width > 767 && this.hasMobileUA(); 217 }, 218 219 isMobile: function () { 220 return window.screen.width < 767 && this.hasMobileUA(); 221 }, 222 223 isDesktop: function () { 224 return !this.isTablet() && !this.isMobile(); 225 }, 226 227 /** 228 * Escape meta symbols in jQuery selectors. 229 * 230 * @param selector 231 * @returns {string|void|XML|*} 232 */ 233 escapeSelector: function (selector) { 234 return selector.replace(/[!"$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, '\\$&'); 235 }, 236 237 displaySidebar: function () { 238 if (!this.isDesktop() || this.isPisces() || this.isGemini()) { 239 return; 240 } 241 $('.sidebar-toggle').trigger('click'); 242 }, 243 244 isMist: function () { 245 return CONFIG.scheme === 'Mist'; 246 }, 247 248 isPisces: function () { 249 return CONFIG.scheme === 'Pisces'; 250 }, 251 252 isGemini: function () { 253 return CONFIG.scheme === 'Gemini'; 254 }, 255 256 getScrollbarWidth: function () { 257 var $div = $('<div />').addClass('scrollbar-measure').prependTo('body'); 258 var div = $div[0]; 259 var scrollbarWidth = div.offsetWidth - div.clientWidth; 260 261 $div.remove(); 262 263 return scrollbarWidth; 264 }, 265 266 getContentVisibilityHeight: function () { 267 var docHeight = $('#content').height(), 268 winHeight = $(window).height(), 269 contentVisibilityHeight = (docHeight > winHeight) ? (docHeight - winHeight) : ($(document).height() - winHeight); 270 return contentVisibilityHeight; 271 }, 272 273 getSidebarb2tHeight: function () { 274 //var sidebarb2tHeight = (CONFIG.sidebar.b2t) ? document.getElementsByClassName('back-to-top')[0].clientHeight : 0; 275 var sidebarb2tHeight = (CONFIG.sidebar.b2t) ? $('.back-to-top').height() : 0; 276 //var sidebarb2tHeight = (CONFIG.sidebar.b2t) ? 24 : 0; 277 return sidebarb2tHeight; 278 }, 279 280 getSidebarSchemePadding: function () { 281 var sidebarNavHeight = ($('.sidebar-nav').css('display') == 'block') ? $('.sidebar-nav').outerHeight(true) : 0, 282 sidebarInner = $('.sidebar-inner'), 283 sidebarPadding = sidebarInner.innerWidth() - sidebarInner.width(), 284 sidebarSchemePadding = this.isPisces() || this.isGemini() ? 285 ((sidebarPadding * 2) + sidebarNavHeight + (CONFIG.sidebar.offset * 2) + this.getSidebarb2tHeight()) : 286 ((sidebarPadding * 2) + (sidebarNavHeight / 2)); 287 return sidebarSchemePadding; 288 } 289 290 /** 291 * Affix behaviour for Sidebar. 292 * 293 * @returns {Boolean} 294 */ 295// needAffix: function () { 296// return this.isPisces() || this.isGemini(); 297// } 298}; 299 300$(document).ready(function () { 301 302 initSidebarDimension(); 303 304 /** 305 * Init Sidebar & TOC inner dimensions on all pages and for all schemes. 306 * Need for Sidebar/TOC inner scrolling if content taller then viewport. 307 */ 308 function initSidebarDimension () { 309 var updateSidebarHeightTimer; 310 311 $(window).on('resize', function () { 312 updateSidebarHeightTimer && clearTimeout(updateSidebarHeightTimer); 313 314 updateSidebarHeightTimer = setTimeout(function () { 315 var sidebarWrapperHeight = document.body.clientHeight - NexT.utils.getSidebarSchemePadding(); 316 317 updateSidebarHeight(sidebarWrapperHeight); 318 }, 0); 319 }); 320
321 // Initialize Sidebar & TOC Width. 322 var scrollbarWidth = NexT.utils.getScrollbarWidth(); 323 if ($('.site-overview-wrap').height() > (document.body.clientHeight - NexT.utils.getSidebarSchemePadding())) { 324 $('.site-overview').css('width', 'calc(100% + ' + scrollbarWidth + 'px)'); 325 } 326 if ($('.post-toc-wrap').height() > (document.body.clientHeight - NexT.utils.getSidebarSchemePadding())) { 327 $('.post-toc').css('width', 'calc(100% + ' + scrollbarWidth + 'px)'); 328 } 329 330 // Initialize Sidebar & TOC Height. 331 updateSidebarHeight(document.body.clientHeight - NexT.utils.getSidebarSchemePadding()); 332 } 333 334 function updateSidebarHeight (height) { 335 height = height || 'auto'; 336 $('.site-overview, .post-toc').css('max-height', height); 337 } 338 339});
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.