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