Site updated: 2019-03-16 15:24:42
This commit is contained in:
162
js/src/affix.js
Normal file
162
js/src/affix.js
Normal file
@ -0,0 +1,162 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: affix.js v3.3.5
|
||||
* http://getbootstrap.com/javascript/#affix
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// AFFIX CLASS DEFINITION
|
||||
// ======================
|
||||
|
||||
var Affix = function (element, options) {
|
||||
this.options = $.extend({}, Affix.DEFAULTS, options)
|
||||
|
||||
this.$target = $(this.options.target)
|
||||
.on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
|
||||
.on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
|
||||
|
||||
this.$element = $(element)
|
||||
this.affixed = null
|
||||
this.unpin = null
|
||||
this.pinnedOffset = null
|
||||
|
||||
this.checkPosition()
|
||||
}
|
||||
|
||||
Affix.VERSION = '3.3.5'
|
||||
|
||||
Affix.RESET = 'affix affix-top affix-bottom'
|
||||
|
||||
Affix.DEFAULTS = {
|
||||
offset: 0,
|
||||
target: window
|
||||
}
|
||||
|
||||
Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
|
||||
var scrollTop = this.$target.scrollTop()
|
||||
var position = this.$element.offset()
|
||||
var targetHeight = this.$target.height()
|
||||
|
||||
if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
|
||||
|
||||
if (this.affixed == 'bottom') {
|
||||
if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
|
||||
return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
|
||||
}
|
||||
|
||||
var initializing = this.affixed == null
|
||||
var colliderTop = initializing ? scrollTop : position.top
|
||||
var colliderHeight = initializing ? targetHeight : height
|
||||
|
||||
if (offsetTop != null && scrollTop <= offsetTop) return 'top'
|
||||
if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
Affix.prototype.getPinnedOffset = function () {
|
||||
if (this.pinnedOffset) return this.pinnedOffset
|
||||
this.$element.removeClass(Affix.RESET).addClass('affix')
|
||||
var scrollTop = this.$target.scrollTop()
|
||||
var position = this.$element.offset()
|
||||
return (this.pinnedOffset = position.top - scrollTop)
|
||||
}
|
||||
|
||||
Affix.prototype.checkPositionWithEventLoop = function () {
|
||||
setTimeout($.proxy(this.checkPosition, this), 1)
|
||||
}
|
||||
|
||||
Affix.prototype.checkPosition = function () {
|
||||
if (!this.$element.is(':visible')) return
|
||||
|
||||
var height = this.$element.height()
|
||||
var offset = this.options.offset
|
||||
var offsetTop = offset.top
|
||||
var offsetBottom = offset.bottom
|
||||
var scrollHeight = Math.max($(document).height(), $(document.body).height())
|
||||
|
||||
if (typeof offset != 'object') offsetBottom = offsetTop = offset
|
||||
if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
|
||||
if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
|
||||
|
||||
var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
|
||||
|
||||
if (this.affixed != affix) {
|
||||
if (this.unpin != null) this.$element.css('top', '')
|
||||
|
||||
var affixType = 'affix' + (affix ? '-' + affix : '')
|
||||
var e = $.Event(affixType + '.bs.affix')
|
||||
|
||||
this.$element.trigger(e)
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
this.affixed = affix
|
||||
this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
|
||||
|
||||
this.$element
|
||||
.removeClass(Affix.RESET)
|
||||
.addClass(affixType)
|
||||
.trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
|
||||
}
|
||||
|
||||
if (affix == 'bottom') {
|
||||
this.$element.offset({
|
||||
top: scrollHeight - height - offsetBottom
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// AFFIX PLUGIN DEFINITION
|
||||
// =======================
|
||||
|
||||
function Plugin(option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.affix')
|
||||
var options = typeof option == 'object' && option
|
||||
|
||||
if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
var old = $.fn.affix
|
||||
|
||||
$.fn.affix = Plugin
|
||||
$.fn.affix.Constructor = Affix
|
||||
|
||||
|
||||
// AFFIX NO CONFLICT
|
||||
// =================
|
||||
|
||||
$.fn.affix.noConflict = function () {
|
||||
$.fn.affix = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// AFFIX DATA-API
|
||||
// ==============
|
||||
|
||||
$(window).on('load', function () {
|
||||
$('[data-spy="affix"]').each(function () {
|
||||
var $spy = $(this)
|
||||
var data = $spy.data()
|
||||
|
||||
data.offset = data.offset || {}
|
||||
|
||||
if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
|
||||
if (data.offsetTop != null) data.offset.top = data.offsetTop
|
||||
|
||||
Plugin.call($spy, data)
|
||||
})
|
||||
})
|
||||
|
||||
}(jQuery);
|
115
js/src/algolia-search.js
Normal file
115
js/src/algolia-search.js
Normal file
@ -0,0 +1,115 @@
|
||||
/* global instantsearch: true */
|
||||
/*jshint camelcase: false */
|
||||
|
||||
$(document).ready(function () {
|
||||
var algoliaSettings = CONFIG.algolia;
|
||||
var isAlgoliaSettingsValid = algoliaSettings.applicationID &&
|
||||
algoliaSettings.apiKey &&
|
||||
algoliaSettings.indexName;
|
||||
|
||||
if (!isAlgoliaSettingsValid) {
|
||||
window.console.error('Algolia Settings are invalid.');
|
||||
return;
|
||||
}
|
||||
|
||||
var search = instantsearch({
|
||||
appId: algoliaSettings.applicationID,
|
||||
apiKey: algoliaSettings.apiKey,
|
||||
indexName: algoliaSettings.indexName,
|
||||
searchFunction: function (helper) {
|
||||
var searchInput = $('#algolia-search-input').find('input');
|
||||
|
||||
if (searchInput.val()) {
|
||||
helper.search();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Registering Widgets
|
||||
[
|
||||
instantsearch.widgets.searchBox({
|
||||
container: '#algolia-search-input',
|
||||
placeholder: algoliaSettings.labels.input_placeholder
|
||||
}),
|
||||
|
||||
instantsearch.widgets.hits({
|
||||
container: '#algolia-hits',
|
||||
hitsPerPage: algoliaSettings.hits.per_page || 10,
|
||||
templates: {
|
||||
item: function (data) {
|
||||
var link = data.permalink ? data.permalink : (CONFIG.root + data.path);
|
||||
return (
|
||||
'<a href="' + link + '" class="algolia-hit-item-link">' +
|
||||
data._highlightResult.title.value +
|
||||
'</a>'
|
||||
);
|
||||
},
|
||||
empty: function (data) {
|
||||
return (
|
||||
'<div id="algolia-hits-empty">' +
|
||||
algoliaSettings.labels.hits_empty.replace(/\$\{query}/, data.query) +
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
},
|
||||
cssClasses: {
|
||||
item: 'algolia-hit-item'
|
||||
}
|
||||
}),
|
||||
|
||||
instantsearch.widgets.stats({
|
||||
container: '#algolia-stats',
|
||||
templates: {
|
||||
body: function (data) {
|
||||
var stats = algoliaSettings.labels.hits_stats
|
||||
.replace(/\$\{hits}/, data.nbHits)
|
||||
.replace(/\$\{time}/, data.processingTimeMS);
|
||||
return (
|
||||
stats +
|
||||
'<span class="algolia-powered">' +
|
||||
' <img src="' + CONFIG.root + 'images/algolia_logo.svg" alt="Algolia" />' +
|
||||
'</span>' +
|
||||
'<hr />'
|
||||
);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
instantsearch.widgets.pagination({
|
||||
container: '#algolia-pagination',
|
||||
scrollTo: false,
|
||||
showFirstLast: false,
|
||||
labels: {
|
||||
first: '<i class="fa fa-angle-double-left"></i>',
|
||||
last: '<i class="fa fa-angle-double-right"></i>',
|
||||
previous: '<i class="fa fa-angle-left"></i>',
|
||||
next: '<i class="fa fa-angle-right"></i>'
|
||||
},
|
||||
cssClasses: {
|
||||
root: 'pagination',
|
||||
item: 'pagination-item',
|
||||
link: 'page-number',
|
||||
active: 'current',
|
||||
disabled: 'disabled-item'
|
||||
}
|
||||
})
|
||||
].forEach(search.addWidget, search);
|
||||
|
||||
search.start();
|
||||
|
||||
$('.popup-trigger').on('click', function(e) {
|
||||
e.stopPropagation();
|
||||
$('body')
|
||||
.append('<div class="search-popup-overlay algolia-pop-overlay"></div>')
|
||||
.css('overflow', 'hidden');
|
||||
$('.popup').toggle();
|
||||
$('#algolia-search-input').find('input').focus();
|
||||
});
|
||||
|
||||
$('.popup-btn-close').click(function(){
|
||||
$('.popup').hide();
|
||||
$('.algolia-pop-overlay').remove();
|
||||
$('body').css('overflow', '');
|
||||
});
|
||||
|
||||
});
|
52
js/src/bootstrap.js
vendored
Normal file
52
js/src/bootstrap.js
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
/* global NexT: true */
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
$(document).trigger('bootstrap:before');
|
||||
|
||||
NexT.utils.isMobile() && window.FastClick.attach(document.body);
|
||||
|
||||
NexT.utils.lazyLoadPostsImages();
|
||||
|
||||
NexT.utils.registerESCKeyEvent();
|
||||
|
||||
NexT.utils.registerBackToTop();
|
||||
|
||||
// Mobile top menu bar.
|
||||
$('.site-nav-toggle button').on('click', function () {
|
||||
var $siteNav = $('.site-nav');
|
||||
var ON_CLASS_NAME = 'site-nav-on';
|
||||
var isSiteNavOn = $siteNav.hasClass(ON_CLASS_NAME);
|
||||
var animateAction = isSiteNavOn ? 'slideUp' : 'slideDown';
|
||||
var animateCallback = isSiteNavOn ? 'removeClass' : 'addClass';
|
||||
|
||||
$siteNav.stop()[animateAction]('fast', function () {
|
||||
$siteNav[animateCallback](ON_CLASS_NAME);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Register JS handlers by condition option.
|
||||
* Need to add config option in Front-End at 'layout/_partials/head.swig' file.
|
||||
*/
|
||||
CONFIG.fancybox && NexT.utils.wrapImageWithFancyBox();
|
||||
CONFIG.tabs && NexT.utils.registerTabsTag();
|
||||
|
||||
NexT.utils.embeddedVideoTransformer();
|
||||
NexT.utils.addActiveClassToMenuItem();
|
||||
|
||||
|
||||
// Define Motion Sequence.
|
||||
NexT.motion.integrator
|
||||
.add(NexT.motion.middleWares.logo)
|
||||
.add(NexT.motion.middleWares.menu)
|
||||
.add(NexT.motion.middleWares.postList)
|
||||
.add(NexT.motion.middleWares.sidebar);
|
||||
|
||||
$(document).trigger('motion:before');
|
||||
|
||||
// Bootstrap Motion.
|
||||
CONFIG.motion.enable && NexT.motion.integrator.bootstrap();
|
||||
|
||||
$(document).trigger('bootstrap:after');
|
||||
});
|
15
js/src/exturl.js
Normal file
15
js/src/exturl.js
Normal file
@ -0,0 +1,15 @@
|
||||
/* global NexT: true */
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
// Create Base64 Object
|
||||
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9+/=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/rn/g,"n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}};
|
||||
|
||||
$('.exturl').on('click', function () {
|
||||
var $exturl = $(this).attr('data-url');
|
||||
var $decurl = Base64.decode($exturl);
|
||||
window.open($decurl, '_blank');
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
115
js/src/hook-duoshuo.js
Normal file
115
js/src/hook-duoshuo.js
Normal file
@ -0,0 +1,115 @@
|
||||
/* global DUOSHUO: true */
|
||||
/* jshint camelcase: false */
|
||||
|
||||
typeof DUOSHUO !== 'undefined' ?
|
||||
hookTemplate() :
|
||||
($('#duoshuo-script')[0].onload = hookTemplate);
|
||||
|
||||
|
||||
function hookTemplate() {
|
||||
var post = DUOSHUO.templates.post;
|
||||
|
||||
DUOSHUO.templates.post = function (e, t) {
|
||||
var rs = post(e, t);
|
||||
var agent = e.post.agent;
|
||||
var userId = e.post.author.user_id;
|
||||
var admin = '';
|
||||
|
||||
if (userId && (userId == CONFIG.duoshuo.userId)) {
|
||||
admin = '<span class="duoshuo-ua-admin">' + CONFIG.duoshuo.author + '</span>';
|
||||
}
|
||||
|
||||
if (agent && /^Mozilla/.test(agent)) {
|
||||
rs = rs.replace(/<\/div><p>/, admin + getAgentInfo(agent) + '</div><p>');
|
||||
}
|
||||
|
||||
return rs;
|
||||
};
|
||||
}
|
||||
|
||||
function getAgentInfo(string) {
|
||||
$.ua.set(string);
|
||||
|
||||
var UNKNOWN = 'Unknown';
|
||||
var sua = $.ua;
|
||||
var separator = isMobile() ? '<br><br>' : '<span class="duoshuo-ua-separator"></span>';
|
||||
var osName = sua.os.name || UNKNOWN;
|
||||
var osVersion = sua.os.version || UNKNOWN;
|
||||
var browserName = sua.browser.name || UNKNOWN;
|
||||
var browserVersion = sua.browser.version || UNKNOWN;
|
||||
var iconMapping = {
|
||||
os: {
|
||||
android : 'android',
|
||||
linux : 'linux',
|
||||
windows : 'windows',
|
||||
ios : 'apple',
|
||||
'mac os': 'apple',
|
||||
unknown : 'desktop'
|
||||
},
|
||||
browser: {
|
||||
chrome : 'chrome',
|
||||
chromium : 'chrome',
|
||||
firefox : 'firefox',
|
||||
opera : 'opera',
|
||||
safari : 'safari',
|
||||
ie : 'internet-explorer',
|
||||
wechat : 'wechat',
|
||||
qq : 'qq',
|
||||
unknown : 'globe'
|
||||
}
|
||||
};
|
||||
var osIcon = iconMapping.os[osName.toLowerCase()];
|
||||
var browserIcon = iconMapping.browser[getBrowserKey()];
|
||||
|
||||
return separator +
|
||||
'<span class="duoshuo-ua-platform duoshuo-ua-platform-' + osName.toLowerCase() + '">' +
|
||||
'<i class="fa fa-' + osIcon + '"></i>' +
|
||||
osName + ' ' + osVersion +
|
||||
'</span>' + separator +
|
||||
'<span class="duoshuo-ua-browser duoshuo-ua-browser-' + browserName.toLowerCase() + '">' +
|
||||
'<i class="fa fa-' + browserIcon + '"></i>' +
|
||||
browserName + ' ' + browserVersion +
|
||||
'</span>';
|
||||
|
||||
function getBrowserKey () {
|
||||
var key = browserName.toLowerCase();
|
||||
|
||||
if (key.match(/WeChat/i)) {
|
||||
return 'wechat';
|
||||
}
|
||||
|
||||
if (key.match(/QQBrowser/i)) {
|
||||
return 'qq';
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
function isMobile() {
|
||||
var userAgent = window.navigator.userAgent;
|
||||
|
||||
var isiPad = userAgent.match(/iPad/i) !== null;
|
||||
var mobileUA = [
|
||||
'iphone', 'android', 'phone', 'mobile',
|
||||
'wap', 'netfront', 'x11', 'java', 'opera mobi',
|
||||
'opera mini', 'ucweb', 'windows ce', 'symbian',
|
||||
'symbianos', 'series', 'webos', 'sony',
|
||||
'blackberry', 'dopod', 'nokia', 'samsung',
|
||||
'palmsource', 'xda', 'pieplus', 'meizu',
|
||||
'midp' ,'cldc' , 'motorola', 'foma',
|
||||
'docomo', 'up.browser', 'up.link', 'blazer',
|
||||
'helio', 'hosin', 'huawei', 'novarra',
|
||||
'coolpad', 'webos', 'techfaith', 'palmsource',
|
||||
'alcatel', 'amoi', 'ktouch', 'nexian',
|
||||
'ericsson', 'philips', 'sagem', 'wellcom',
|
||||
'bunjalloo', 'maui', 'smartphone', 'iemobile',
|
||||
'spice', 'bird', 'zte-', 'longcos',
|
||||
'pantech', 'gionee', 'portalmmm', 'jig browser',
|
||||
'hiptop', 'benq', 'haier', '^lct',
|
||||
'320x320', '240x320', '176x220'
|
||||
];
|
||||
var pattern = new RegExp(mobileUA.join('|'), 'i');
|
||||
|
||||
return !isiPad && userAgent.match(pattern);
|
||||
}
|
||||
}
|
165
js/src/js.cookie.js
Normal file
165
js/src/js.cookie.js
Normal file
@ -0,0 +1,165 @@
|
||||
/*!
|
||||
* JavaScript Cookie v2.1.4
|
||||
* https://github.com/js-cookie/js-cookie
|
||||
*
|
||||
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
|
||||
* Released under the MIT license
|
||||
*/
|
||||
;(function (factory) {
|
||||
var registeredInModuleLoader = false;
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(factory);
|
||||
registeredInModuleLoader = true;
|
||||
}
|
||||
if (typeof exports === 'object') {
|
||||
module.exports = factory();
|
||||
registeredInModuleLoader = true;
|
||||
}
|
||||
if (!registeredInModuleLoader) {
|
||||
var OldCookies = window.Cookies;
|
||||
var api = window.Cookies = factory();
|
||||
api.noConflict = function () {
|
||||
window.Cookies = OldCookies;
|
||||
return api;
|
||||
};
|
||||
}
|
||||
}(function () {
|
||||
function extend () {
|
||||
var i = 0;
|
||||
var result = {};
|
||||
for (; i < arguments.length; i++) {
|
||||
var attributes = arguments[ i ];
|
||||
for (var key in attributes) {
|
||||
result[key] = attributes[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function init (converter) {
|
||||
function api (key, value, attributes) {
|
||||
var result;
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write
|
||||
|
||||
if (arguments.length > 1) {
|
||||
attributes = extend({
|
||||
path: '/'
|
||||
}, api.defaults, attributes);
|
||||
|
||||
if (typeof attributes.expires === 'number') {
|
||||
var expires = new Date();
|
||||
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
|
||||
attributes.expires = expires;
|
||||
}
|
||||
|
||||
// We're using "expires" because "max-age" is not supported by IE
|
||||
attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';
|
||||
|
||||
try {
|
||||
result = JSON.stringify(value);
|
||||
if (/^[\{\[]/.test(result)) {
|
||||
value = result;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (!converter.write) {
|
||||
value = encodeURIComponent(String(value))
|
||||
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
|
||||
} else {
|
||||
value = converter.write(value, key);
|
||||
}
|
||||
|
||||
key = encodeURIComponent(String(key));
|
||||
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
|
||||
key = key.replace(/[\(\)]/g, escape);
|
||||
|
||||
var stringifiedAttributes = '';
|
||||
|
||||
for (var attributeName in attributes) {
|
||||
if (!attributes[attributeName]) {
|
||||
continue;
|
||||
}
|
||||
stringifiedAttributes += '; ' + attributeName;
|
||||
if (attributes[attributeName] === true) {
|
||||
continue;
|
||||
}
|
||||
stringifiedAttributes += '=' + attributes[attributeName];
|
||||
}
|
||||
return (document.cookie = key + '=' + value + stringifiedAttributes);
|
||||
}
|
||||
|
||||
// Read
|
||||
|
||||
if (!key) {
|
||||
result = {};
|
||||
}
|
||||
|
||||
// To prevent the for loop in the first place assign an empty array
|
||||
// in case there are no cookies at all. Also prevents odd result when
|
||||
// calling "get()"
|
||||
var cookies = document.cookie ? document.cookie.split('; ') : [];
|
||||
var rdecode = /(%[0-9A-Z]{2})+/g;
|
||||
var i = 0;
|
||||
|
||||
for (; i < cookies.length; i++) {
|
||||
var parts = cookies[i].split('=');
|
||||
var cookie = parts.slice(1).join('=');
|
||||
|
||||
if (cookie.charAt(0) === '"') {
|
||||
cookie = cookie.slice(1, -1);
|
||||
}
|
||||
|
||||
try {
|
||||
var name = parts[0].replace(rdecode, decodeURIComponent);
|
||||
cookie = converter.read ?
|
||||
converter.read(cookie, name) : converter(cookie, name) ||
|
||||
cookie.replace(rdecode, decodeURIComponent);
|
||||
|
||||
if (this.json) {
|
||||
try {
|
||||
cookie = JSON.parse(cookie);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (key === name) {
|
||||
result = cookie;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
result[name] = cookie;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
api.set = api;
|
||||
api.get = function (key) {
|
||||
return api.call(api, key);
|
||||
};
|
||||
api.getJSON = function () {
|
||||
return api.apply({
|
||||
json: true
|
||||
}, [].slice.call(arguments));
|
||||
};
|
||||
api.defaults = {};
|
||||
|
||||
api.remove = function (key, attributes) {
|
||||
api(key, '', extend(attributes, {
|
||||
expires: -1
|
||||
}));
|
||||
};
|
||||
|
||||
api.withConverter = init;
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
return init(function () {});
|
||||
}));
|
352
js/src/motion.js
Normal file
352
js/src/motion.js
Normal file
@ -0,0 +1,352 @@
|
||||
/* global NexT: true */
|
||||
|
||||
$(document).ready(function () {
|
||||
NexT.motion = {};
|
||||
|
||||
var sidebarToggleLines = {
|
||||
lines: [],
|
||||
push: function (line) {
|
||||
this.lines.push(line);
|
||||
},
|
||||
init: function () {
|
||||
this.lines.forEach(function (line) {
|
||||
line.init();
|
||||
});
|
||||
},
|
||||
arrow: function () {
|
||||
this.lines.forEach(function (line) {
|
||||
line.arrow();
|
||||
});
|
||||
},
|
||||
close: function () {
|
||||
this.lines.forEach(function (line) {
|
||||
line.close();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function SidebarToggleLine(settings) {
|
||||
this.el = $(settings.el);
|
||||
this.status = $.extend({}, {
|
||||
init: {
|
||||
width: '100%',
|
||||
opacity: 1,
|
||||
left: 0,
|
||||
rotateZ: 0,
|
||||
top: 0
|
||||
}
|
||||
}, settings.status);
|
||||
}
|
||||
|
||||
SidebarToggleLine.prototype.init = function () {
|
||||
this.transform('init');
|
||||
};
|
||||
SidebarToggleLine.prototype.arrow = function () {
|
||||
this.transform('arrow');
|
||||
};
|
||||
SidebarToggleLine.prototype.close = function () {
|
||||
this.transform('close');
|
||||
};
|
||||
SidebarToggleLine.prototype.transform = function (status) {
|
||||
this.el.velocity('stop').velocity(this.status[status]);
|
||||
};
|
||||
|
||||
var sidebarToggleLine1st = new SidebarToggleLine({
|
||||
el: '.sidebar-toggle-line-first',
|
||||
status: {
|
||||
arrow: {width: '50%', rotateZ: '-45deg', top: '2px'},
|
||||
close: {width: '100%', rotateZ: '-45deg', top: '5px'}
|
||||
}
|
||||
});
|
||||
var sidebarToggleLine2nd = new SidebarToggleLine({
|
||||
el: '.sidebar-toggle-line-middle',
|
||||
status: {
|
||||
arrow: {width: '90%'},
|
||||
close: {opacity: 0}
|
||||
}
|
||||
});
|
||||
var sidebarToggleLine3rd = new SidebarToggleLine({
|
||||
el: '.sidebar-toggle-line-last',
|
||||
status: {
|
||||
arrow: {width: '50%', rotateZ: '45deg', top: '-2px'},
|
||||
close: {width: '100%', rotateZ: '45deg', top: '-5px'}
|
||||
}
|
||||
});
|
||||
|
||||
sidebarToggleLines.push(sidebarToggleLine1st);
|
||||
sidebarToggleLines.push(sidebarToggleLine2nd);
|
||||
sidebarToggleLines.push(sidebarToggleLine3rd);
|
||||
|
||||
var SIDEBAR_WIDTH = '320px';
|
||||
var SIDEBAR_DISPLAY_DURATION = 200;
|
||||
var xPos, yPos;
|
||||
|
||||
var sidebarToggleMotion = {
|
||||
toggleEl: $('.sidebar-toggle'),
|
||||
dimmerEl: $('#sidebar-dimmer'),
|
||||
sidebarEl: $('.sidebar'),
|
||||
isSidebarVisible: false,
|
||||
init: function () {
|
||||
this.toggleEl.on('click', this.clickHandler.bind(this));
|
||||
this.dimmerEl.on('click', this.clickHandler.bind(this));
|
||||
this.toggleEl.on('mouseenter', this.mouseEnterHandler.bind(this));
|
||||
this.toggleEl.on('mouseleave', this.mouseLeaveHandler.bind(this));
|
||||
this.sidebarEl.on('touchstart', this.touchstartHandler.bind(this));
|
||||
this.sidebarEl.on('touchend', this.touchendHandler.bind(this));
|
||||
this.sidebarEl.on('touchmove', function(e){e.preventDefault();});
|
||||
|
||||
$(document)
|
||||
.on('sidebar.isShowing', function () {
|
||||
NexT.utils.isDesktop() && $('body').velocity('stop').velocity(
|
||||
{paddingRight: SIDEBAR_WIDTH},
|
||||
SIDEBAR_DISPLAY_DURATION
|
||||
);
|
||||
})
|
||||
.on('sidebar.isHiding', function () {
|
||||
});
|
||||
},
|
||||
clickHandler: function () {
|
||||
this.isSidebarVisible ? this.hideSidebar() : this.showSidebar();
|
||||
this.isSidebarVisible = !this.isSidebarVisible;
|
||||
},
|
||||
mouseEnterHandler: function () {
|
||||
if (this.isSidebarVisible) {
|
||||
return;
|
||||
}
|
||||
sidebarToggleLines.arrow();
|
||||
},
|
||||
mouseLeaveHandler: function () {
|
||||
if (this.isSidebarVisible) {
|
||||
return;
|
||||
}
|
||||
sidebarToggleLines.init();
|
||||
},
|
||||
touchstartHandler: function(e) {
|
||||
xPos = e.originalEvent.touches[0].clientX;
|
||||
yPos = e.originalEvent.touches[0].clientY;
|
||||
},
|
||||
touchendHandler: function(e) {
|
||||
var _xPos = e.originalEvent.changedTouches[0].clientX;
|
||||
var _yPos = e.originalEvent.changedTouches[0].clientY;
|
||||
if (_xPos-xPos > 30 && Math.abs(_yPos-yPos) < 20) {
|
||||
this.clickHandler();
|
||||
}
|
||||
},
|
||||
showSidebar: function () {
|
||||
var self = this;
|
||||
|
||||
sidebarToggleLines.close();
|
||||
|
||||
this.sidebarEl.velocity('stop').velocity({
|
||||
width: SIDEBAR_WIDTH
|
||||
}, {
|
||||
display: 'block',
|
||||
duration: SIDEBAR_DISPLAY_DURATION,
|
||||
begin: function () {
|
||||
$('.sidebar .motion-element').velocity(
|
||||
'transition.slideRightIn',
|
||||
{
|
||||
stagger: 50,
|
||||
drag: true,
|
||||
complete: function () {
|
||||
self.sidebarEl.trigger('sidebar.motion.complete');
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
complete: function () {
|
||||
self.sidebarEl.addClass('sidebar-active');
|
||||
self.sidebarEl.trigger('sidebar.didShow');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.sidebarEl.trigger('sidebar.isShowing');
|
||||
},
|
||||
hideSidebar: function () {
|
||||
NexT.utils.isDesktop() && $('body').velocity('stop').velocity({paddingRight: 0});
|
||||
this.sidebarEl.find('.motion-element').velocity('stop').css('display', 'none');
|
||||
this.sidebarEl.velocity('stop').velocity({width: 0}, {display: 'none'});
|
||||
|
||||
sidebarToggleLines.init();
|
||||
|
||||
this.sidebarEl.removeClass('sidebar-active');
|
||||
this.sidebarEl.trigger('sidebar.isHiding');
|
||||
|
||||
// Prevent adding TOC to Overview if Overview was selected when close & open sidebar.
|
||||
if (!!$('.post-toc-wrap')) {
|
||||
if ($('.site-overview-wrap').css('display') === 'block') {
|
||||
$('.post-toc-wrap').removeClass('motion-element');
|
||||
} else {
|
||||
$('.post-toc-wrap').addClass('motion-element');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
sidebarToggleMotion.init();
|
||||
|
||||
NexT.motion.integrator = {
|
||||
queue: [],
|
||||
cursor: -1,
|
||||
add: function (fn) {
|
||||
this.queue.push(fn);
|
||||
return this;
|
||||
},
|
||||
next: function () {
|
||||
this.cursor++;
|
||||
var fn = this.queue[this.cursor];
|
||||
$.isFunction(fn) && fn(NexT.motion.integrator);
|
||||
},
|
||||
bootstrap: function () {
|
||||
this.next();
|
||||
}
|
||||
};
|
||||
|
||||
NexT.motion.middleWares = {
|
||||
logo: function (integrator) {
|
||||
var sequence = [];
|
||||
var $brand = $('.brand');
|
||||
var $title = $('.site-title');
|
||||
var $subtitle = $('.site-subtitle');
|
||||
var $logoLineTop = $('.logo-line-before i');
|
||||
var $logoLineBottom = $('.logo-line-after i');
|
||||
|
||||
$brand.size() > 0 && sequence.push({
|
||||
e: $brand,
|
||||
p: {opacity: 1},
|
||||
o: {duration: 200}
|
||||
});
|
||||
|
||||
NexT.utils.isMist() && hasElement([$logoLineTop, $logoLineBottom]) &&
|
||||
sequence.push(
|
||||
getMistLineSettings($logoLineTop, '100%'),
|
||||
getMistLineSettings($logoLineBottom, '-100%')
|
||||
);
|
||||
|
||||
hasElement($title) && sequence.push({
|
||||
e: $title,
|
||||
p: {opacity: 1, top: 0},
|
||||
o: { duration: 200 }
|
||||
});
|
||||
|
||||
hasElement($subtitle) && sequence.push({
|
||||
e: $subtitle,
|
||||
p: {opacity: 1, top: 0},
|
||||
o: {duration: 200}
|
||||
});
|
||||
|
||||
if (CONFIG.motion.async) {
|
||||
integrator.next();
|
||||
}
|
||||
|
||||
if (sequence.length > 0) {
|
||||
sequence[sequence.length - 1].o.complete = function () {
|
||||
integrator.next();
|
||||
};
|
||||
$.Velocity.RunSequence(sequence);
|
||||
} else {
|
||||
integrator.next();
|
||||
}
|
||||
|
||||
|
||||
function getMistLineSettings (element, translateX) {
|
||||
return {
|
||||
e: $(element),
|
||||
p: {translateX: translateX},
|
||||
o: {
|
||||
duration: 500,
|
||||
sequenceQueue: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $elements exist.
|
||||
* @param {jQuery|Array} $elements
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasElement ($elements) {
|
||||
$elements = Array.isArray($elements) ? $elements : [$elements];
|
||||
return $elements.every(function ($element) {
|
||||
return $.isFunction($element.size) && $element.size() > 0;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
menu: function (integrator) {
|
||||
|
||||
if (CONFIG.motion.async) {
|
||||
integrator.next();
|
||||
}
|
||||
|
||||
$('.menu-item').velocity('transition.slideDownIn', {
|
||||
display: null,
|
||||
duration: 200,
|
||||
complete: function () {
|
||||
integrator.next();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
postList: function (integrator) {
|
||||
//var $post = $('.post');
|
||||
var $postBlock = $('.post-block, .pagination, .comments');
|
||||
var $postBlockTransition = CONFIG.motion.transition.post_block;
|
||||
var $postHeader = $('.post-header');
|
||||
var $postHeaderTransition = CONFIG.motion.transition.post_header;
|
||||
var $postBody = $('.post-body');
|
||||
var $postBodyTransition = CONFIG.motion.transition.post_body;
|
||||
var $collHeader = $('.collection-title, .archive-year');
|
||||
var $collHeaderTransition = CONFIG.motion.transition.coll_header;
|
||||
var $sidebarAffix = $('.sidebar-inner');
|
||||
var $sidebarAffixTransition = CONFIG.motion.transition.sidebar;
|
||||
var hasPost = $postBlock.size() > 0;
|
||||
|
||||
hasPost ? postMotion() : integrator.next();
|
||||
|
||||
if (CONFIG.motion.async) {
|
||||
integrator.next();
|
||||
}
|
||||
|
||||
function postMotion () {
|
||||
var postMotionOptions = window.postMotionOptions || {
|
||||
stagger: 100,
|
||||
drag: true
|
||||
};
|
||||
postMotionOptions.complete = function () {
|
||||
// After motion complete need to remove transform from sidebar to let affix work on Pisces | Gemini.
|
||||
if (CONFIG.motion.transition.sidebar && (NexT.utils.isPisces() || NexT.utils.isGemini())) {
|
||||
$sidebarAffix.css({ 'transform': 'initial' });
|
||||
}
|
||||
integrator.next();
|
||||
};
|
||||
|
||||
//$post.velocity('transition.slideDownIn', postMotionOptions);
|
||||
if (CONFIG.motion.transition.post_block) {
|
||||
$postBlock.velocity('transition.' + $postBlockTransition, postMotionOptions);
|
||||
}
|
||||
if (CONFIG.motion.transition.post_header) {
|
||||
$postHeader.velocity('transition.' + $postHeaderTransition, postMotionOptions);
|
||||
}
|
||||
if (CONFIG.motion.transition.post_body) {
|
||||
$postBody.velocity('transition.' + $postBodyTransition, postMotionOptions);
|
||||
}
|
||||
if (CONFIG.motion.transition.coll_header) {
|
||||
$collHeader.velocity('transition.' + $collHeaderTransition, postMotionOptions);
|
||||
}
|
||||
// Only for Pisces | Gemini.
|
||||
if (CONFIG.motion.transition.sidebar && (NexT.utils.isPisces() || NexT.utils.isGemini())) {
|
||||
$sidebarAffix.velocity('transition.' + $sidebarAffixTransition, postMotionOptions);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
sidebar: function (integrator) {
|
||||
if (CONFIG.sidebar.display === 'always') {
|
||||
NexT.utils.displaySidebar();
|
||||
}
|
||||
integrator.next();
|
||||
}
|
||||
};
|
||||
|
||||
});
|
99
js/src/post-details.js
Normal file
99
js/src/post-details.js
Normal file
@ -0,0 +1,99 @@
|
||||
/* global NexT: true */
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
initScrollSpy();
|
||||
|
||||
function initScrollSpy () {
|
||||
var tocSelector = '.post-toc';
|
||||
var $tocElement = $(tocSelector);
|
||||
var activeCurrentSelector = '.active-current';
|
||||
|
||||
$tocElement
|
||||
.on('activate.bs.scrollspy', function () {
|
||||
var $currentActiveElement = $(tocSelector + ' .active').last();
|
||||
|
||||
removeCurrentActiveClass();
|
||||
$currentActiveElement.addClass('active-current');
|
||||
|
||||
// Scrolling to center active TOC element if TOC content is taller then viewport.
|
||||
$tocElement.scrollTop($currentActiveElement.offset().top - $tocElement.offset().top + $tocElement.scrollTop() - ($tocElement.height() / 2));
|
||||
})
|
||||
.on('clear.bs.scrollspy', removeCurrentActiveClass);
|
||||
|
||||
$('body').scrollspy({ target: tocSelector });
|
||||
|
||||
function removeCurrentActiveClass () {
|
||||
$(tocSelector + ' ' + activeCurrentSelector)
|
||||
.removeClass(activeCurrentSelector.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
var html = $('html');
|
||||
var TAB_ANIMATE_DURATION = 200;
|
||||
var hasVelocity = $.isFunction(html.velocity);
|
||||
|
||||
$('.sidebar-nav li').on('click', function () {
|
||||
var item = $(this);
|
||||
var activeTabClassName = 'sidebar-nav-active';
|
||||
var activePanelClassName = 'sidebar-panel-active';
|
||||
if (item.hasClass(activeTabClassName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTarget = $('.' + activePanelClassName);
|
||||
var target = $('.' + item.data('target'));
|
||||
|
||||
hasVelocity ?
|
||||
currentTarget.velocity('transition.slideUpOut', TAB_ANIMATE_DURATION, function () {
|
||||
target
|
||||
.velocity('stop')
|
||||
.velocity('transition.slideDownIn', TAB_ANIMATE_DURATION)
|
||||
.addClass(activePanelClassName);
|
||||
}) :
|
||||
currentTarget.animate({ opacity: 0 }, TAB_ANIMATE_DURATION, function () {
|
||||
currentTarget.hide();
|
||||
target
|
||||
.stop()
|
||||
.css({'opacity': 0, 'display': 'block'})
|
||||
.animate({ opacity: 1 }, TAB_ANIMATE_DURATION, function () {
|
||||
currentTarget.removeClass(activePanelClassName);
|
||||
target.addClass(activePanelClassName);
|
||||
});
|
||||
});
|
||||
|
||||
item.siblings().removeClass(activeTabClassName);
|
||||
item.addClass(activeTabClassName);
|
||||
});
|
||||
|
||||
// TOC item animation navigate & prevent #item selector in adress bar.
|
||||
$('.post-toc a').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
var targetSelector = NexT.utils.escapeSelector(this.getAttribute('href'));
|
||||
var offset = $(targetSelector).offset().top;
|
||||
|
||||
hasVelocity ?
|
||||
html.velocity('stop').velocity('scroll', {
|
||||
offset: offset + 'px',
|
||||
mobileHA: false
|
||||
}) :
|
||||
$('html, body').stop().animate({
|
||||
scrollTop: offset
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// Expand sidebar on post detail page by default, when post has a toc.
|
||||
var $tocContent = $('.post-toc-content');
|
||||
var isSidebarCouldDisplay = CONFIG.sidebar.display === 'post' ||
|
||||
CONFIG.sidebar.display === 'always';
|
||||
var hasTOC = $tocContent.length > 0 && $tocContent.html().trim().length > 0;
|
||||
if (isSidebarCouldDisplay && hasTOC) {
|
||||
CONFIG.motion.enable ?
|
||||
(NexT.motion.middleWares.sidebar = function () {
|
||||
NexT.utils.displaySidebar();
|
||||
}) : NexT.utils.displaySidebar();
|
||||
}
|
||||
});
|
57
js/src/schemes/pisces.js
Normal file
57
js/src/schemes/pisces.js
Normal file
@ -0,0 +1,57 @@
|
||||
$(document).ready(function () {
|
||||
|
||||
var sidebarInner = $('.sidebar-inner');
|
||||
|
||||
initAffix();
|
||||
resizeListener();
|
||||
|
||||
function initAffix () {
|
||||
var headerOffset = getHeaderOffset(),
|
||||
footerOffset = getFooterOffset(),
|
||||
sidebarHeight = $('#sidebar').height() + NexT.utils.getSidebarb2tHeight(),
|
||||
contentHeight = $('#content').height();
|
||||
|
||||
// Not affix if sidebar taller then content (to prevent bottom jumping).
|
||||
if (headerOffset + sidebarHeight < contentHeight) {
|
||||
sidebarInner.affix({
|
||||
offset: {
|
||||
top: headerOffset - CONFIG.sidebar.offset,
|
||||
bottom: footerOffset
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setSidebarMarginTop(headerOffset).css({ 'margin-left': 'initial' });
|
||||
}
|
||||
|
||||
function resizeListener () {
|
||||
var mql = window.matchMedia('(min-width: 991px)');
|
||||
mql.addListener(function(e){
|
||||
if(e.matches){
|
||||
recalculateAffixPosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getHeaderOffset () {
|
||||
return $('.header-inner').height() + CONFIG.sidebar.offset;
|
||||
}
|
||||
|
||||
function getFooterOffset () {
|
||||
var footerInner = $('.footer-inner'),
|
||||
footerMargin = footerInner.outerHeight(true) - footerInner.outerHeight(),
|
||||
footerOffset = footerInner.outerHeight(true) + footerMargin;
|
||||
return footerOffset;
|
||||
}
|
||||
|
||||
function setSidebarMarginTop (headerOffset) {
|
||||
return $('#sidebar').css({ 'margin-top': headerOffset });
|
||||
}
|
||||
|
||||
function recalculateAffixPosition () {
|
||||
$(window).off('.affix');
|
||||
sidebarInner.removeData('bs.affix').removeClass('affix affix-top affix-bottom');
|
||||
initAffix();
|
||||
}
|
||||
|
||||
});
|
23
js/src/scroll-cookie.js
Normal file
23
js/src/scroll-cookie.js
Normal file
@ -0,0 +1,23 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
// Set relative link path (without domain)
|
||||
var rpath = window.location.href.replace(window.location.origin, "");
|
||||
|
||||
// Write position in cookie
|
||||
var timeout;
|
||||
$(window).on("scroll", function() {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function () {
|
||||
Cookies.set("scroll-cookie", ($(window).scrollTop() + "|" + rpath), { expires: 365, path: '' });
|
||||
}, 250);
|
||||
});
|
||||
|
||||
// Read position from cookie
|
||||
if (Cookies.get("scroll-cookie") !== undefined) {
|
||||
var cvalues = Cookies.get("scroll-cookie").split('|');
|
||||
if (cvalues[1] == rpath) {
|
||||
$(window).scrollTop(cvalues[0]);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
182
js/src/scrollspy.js
Normal file
182
js/src/scrollspy.js
Normal file
@ -0,0 +1,182 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: scrollspy.js v3.3.2
|
||||
* http://getbootstrap.com/javascript/#scrollspy
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
/**
|
||||
* Custom by iissnan
|
||||
*
|
||||
* - Add a `clear.bs.scrollspy` event.
|
||||
* - Esacpe targets selector.
|
||||
*/
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// SCROLLSPY CLASS DEFINITION
|
||||
// ==========================
|
||||
|
||||
function ScrollSpy(element, options) {
|
||||
this.$body = $(document.body)
|
||||
this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
|
||||
this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
|
||||
this.selector = (this.options.target || '') + ' .nav li > a'
|
||||
this.offsets = []
|
||||
this.targets = []
|
||||
this.activeTarget = null
|
||||
this.scrollHeight = 0
|
||||
|
||||
this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
|
||||
this.refresh()
|
||||
this.process()
|
||||
}
|
||||
|
||||
ScrollSpy.VERSION = '3.3.2'
|
||||
|
||||
ScrollSpy.DEFAULTS = {
|
||||
offset: 10
|
||||
}
|
||||
|
||||
ScrollSpy.prototype.getScrollHeight = function () {
|
||||
return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
|
||||
}
|
||||
|
||||
ScrollSpy.prototype.refresh = function () {
|
||||
var that = this
|
||||
var offsetMethod = 'offset'
|
||||
var offsetBase = 0
|
||||
|
||||
this.offsets = []
|
||||
this.targets = []
|
||||
this.scrollHeight = this.getScrollHeight()
|
||||
|
||||
if (!$.isWindow(this.$scrollElement[0])) {
|
||||
offsetMethod = 'position'
|
||||
offsetBase = this.$scrollElement.scrollTop()
|
||||
}
|
||||
|
||||
this.$body
|
||||
.find(this.selector)
|
||||
.map(function () {
|
||||
var $el = $(this)
|
||||
var href = $el.data('target') || $el.attr('href')
|
||||
var $href = /^#./.test(href) && $(NexT.utils.escapeSelector(href)) // Need to escape selector.
|
||||
|
||||
return ($href
|
||||
&& $href.length
|
||||
&& $href.is(':visible')
|
||||
&& [[$href[offsetMethod]().top + offsetBase, href]]) || null
|
||||
})
|
||||
.sort(function (a, b) { return a[0] - b[0] })
|
||||
.each(function () {
|
||||
that.offsets.push(this[0])
|
||||
that.targets.push(this[1])
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
ScrollSpy.prototype.process = function () {
|
||||
var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
|
||||
var scrollHeight = this.getScrollHeight()
|
||||
var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
|
||||
var offsets = this.offsets
|
||||
var targets = this.targets
|
||||
var activeTarget = this.activeTarget
|
||||
var i
|
||||
|
||||
if (this.scrollHeight != scrollHeight) {
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
if (scrollTop >= maxScroll) {
|
||||
return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
|
||||
}
|
||||
|
||||
if (activeTarget && scrollTop < offsets[0]) {
|
||||
$(this.selector).trigger('clear.bs.scrollspy') // Add a custom event.
|
||||
this.activeTarget = null
|
||||
return this.clear()
|
||||
}
|
||||
|
||||
for (i = offsets.length; i--;) {
|
||||
activeTarget != targets[i]
|
||||
&& scrollTop >= offsets[i]
|
||||
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])
|
||||
&& this.activate(targets[i])
|
||||
}
|
||||
}
|
||||
|
||||
ScrollSpy.prototype.activate = function (target) {
|
||||
this.activeTarget = target
|
||||
|
||||
this.clear()
|
||||
|
||||
var selector = this.selector +
|
||||
'[data-target="' + target + '"],' +
|
||||
this.selector + '[href="' + target + '"]'
|
||||
|
||||
var active = $(selector)
|
||||
.parents('li')
|
||||
.addClass('active')
|
||||
|
||||
if (active.parent('.dropdown-menu').length) {
|
||||
active = active
|
||||
.closest('li.dropdown')
|
||||
.addClass('active')
|
||||
}
|
||||
|
||||
active.trigger('activate.bs.scrollspy')
|
||||
}
|
||||
|
||||
ScrollSpy.prototype.clear = function () {
|
||||
$(this.selector)
|
||||
.parentsUntil(this.options.target, '.active')
|
||||
.removeClass('active')
|
||||
}
|
||||
|
||||
|
||||
// SCROLLSPY PLUGIN DEFINITION
|
||||
// ===========================
|
||||
|
||||
function Plugin(option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.scrollspy')
|
||||
var options = typeof option == 'object' && option
|
||||
|
||||
if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
var old = $.fn.scrollspy
|
||||
|
||||
$.fn.scrollspy = Plugin
|
||||
$.fn.scrollspy.Constructor = ScrollSpy
|
||||
|
||||
|
||||
// SCROLLSPY NO CONFLICT
|
||||
// =====================
|
||||
|
||||
$.fn.scrollspy.noConflict = function () {
|
||||
$.fn.scrollspy = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// SCROLLSPY DATA-API
|
||||
// ==================
|
||||
|
||||
$(window).on('load.bs.scrollspy.data-api', function () {
|
||||
$('[data-spy="scroll"]').each(function () {
|
||||
var $spy = $(this)
|
||||
Plugin.call($spy, $spy.data())
|
||||
})
|
||||
})
|
||||
|
||||
}(jQuery);
|
339
js/src/utils.js
Normal file
339
js/src/utils.js
Normal file
@ -0,0 +1,339 @@
|
||||
/* global NexT: true */
|
||||
|
||||
NexT.utils = NexT.$u = {
|
||||
/**
|
||||
* Wrap images with fancybox support.
|
||||
*/
|
||||
wrapImageWithFancyBox: function () {
|
||||
$('.content img')
|
||||
.not('[hidden]')
|
||||
.not('.group-picture img, .post-gallery img')
|
||||
.each(function () {
|
||||
var $image = $(this);
|
||||
var imageTitle = $image.attr('title');
|
||||
var $imageWrapLink = $image.parent('a');
|
||||
|
||||
if ($imageWrapLink.size() < 1) {
|
||||
var imageLink = ($image.attr('data-original')) ? this.getAttribute('data-original') : this.getAttribute('src');
|
||||
$imageWrapLink = $image.wrap('<a href="' + imageLink + '"></a>').parent('a');
|
||||
}
|
||||
|
||||
$imageWrapLink.addClass('fancybox fancybox.image');
|
||||
$imageWrapLink.attr('rel', 'group');
|
||||
|
||||
if (imageTitle) {
|
||||
$imageWrapLink.append('<p class="image-caption">' + imageTitle + '</p>');
|
||||
|
||||
//make sure img title tag will show correctly in fancybox
|
||||
$imageWrapLink.attr('title', imageTitle);
|
||||
}
|
||||
});
|
||||
|
||||
$('.fancybox').fancybox({
|
||||
helpers: {
|
||||
overlay: {
|
||||
locked: false
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
lazyLoadPostsImages: function () {
|
||||
$('#posts').find('img').lazyload({
|
||||
//placeholder: '/images/loading.gif',
|
||||
effect: 'fadeIn',
|
||||
threshold : 0
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Tabs tag listener (without twitter bootstrap).
|
||||
*/
|
||||
registerTabsTag: function () {
|
||||
var tNav = '.tabs ul.nav-tabs ';
|
||||
|
||||
// Binding `nav-tabs` & `tab-content` by real time permalink changing.
|
||||
$(function() {
|
||||
$(window).bind('hashchange', function() {
|
||||
var tHash = location.hash;
|
||||
if (tHash !== '') {
|
||||
$(tNav + 'li:has(a[href="' + tHash + '"])').addClass('active').siblings().removeClass('active');
|
||||
$(tHash).addClass('active').siblings().removeClass('active');
|
||||
}
|
||||
}).trigger('hashchange');
|
||||
});
|
||||
|
||||
$(tNav + '.tab').on('click', function (href) {
|
||||
href.preventDefault();
|
||||
// Prevent selected tab to select again.
|
||||
if(!$(this).hasClass('active')){
|
||||
|
||||
// Add & Remove active class on `nav-tabs` & `tab-content`.
|
||||
$(this).addClass('active').siblings().removeClass('active');
|
||||
var tActive = $(this).find('a').attr('href');
|
||||
$(tActive).addClass('active').siblings().removeClass('active');
|
||||
|
||||
// Clear location hash in browser if #permalink exists.
|
||||
if (location.hash !== '') {
|
||||
history.pushState('', document.title, window.location.pathname + window.location.search);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
registerESCKeyEvent: function () {
|
||||
$(document).on('keyup', function (event) {
|
||||
var shouldDismissSearchPopup = event.which === 27 &&
|
||||
$('.search-popup').is(':visible');
|
||||
if (shouldDismissSearchPopup) {
|
||||
$('.search-popup').hide();
|
||||
$('.search-popup-overlay').remove();
|
||||
$('body').css('overflow', '');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
registerBackToTop: function () {
|
||||
var THRESHOLD = 50;
|
||||
var $top = $('.back-to-top');
|
||||
|
||||
$(window).on('scroll', function () {
|
||||
$top.toggleClass('back-to-top-on', window.pageYOffset > THRESHOLD);
|
||||
|
||||
var scrollTop = $(window).scrollTop();
|
||||
var contentVisibilityHeight = NexT.utils.getContentVisibilityHeight();
|
||||
var scrollPercent = (scrollTop) / (contentVisibilityHeight);
|
||||
var scrollPercentRounded = Math.round(scrollPercent*100);
|
||||
var scrollPercentMaxed = (scrollPercentRounded > 100) ? 100 : scrollPercentRounded;
|
||||
$('#scrollpercent>span').html(scrollPercentMaxed);
|
||||
});
|
||||
|
||||
$top.on('click', function () {
|
||||
$('body').velocity('scroll');
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Transform embedded video to support responsive layout.
|
||||
* @see http://toddmotto.com/fluid-and-responsive-youtube-and-vimeo-videos-with-fluidvids-js/
|
||||
*/
|
||||
embeddedVideoTransformer: function () {
|
||||
var $iframes = $('iframe');
|
||||
|
||||
// Supported Players. Extend this if you need more players.
|
||||
var SUPPORTED_PLAYERS = [
|
||||
'www.youtube.com',
|
||||
'player.vimeo.com',
|
||||
'player.youku.com',
|
||||
'music.163.com',
|
||||
'www.tudou.com'
|
||||
];
|
||||
var pattern = new RegExp( SUPPORTED_PLAYERS.join('|') );
|
||||
|
||||
$iframes.each(function () {
|
||||
var iframe = this;
|
||||
var $iframe = $(this);
|
||||
var oldDimension = getDimension($iframe);
|
||||
var newDimension;
|
||||
|
||||
if (this.src.search(pattern) > 0) {
|
||||
|
||||
// Calculate the video ratio based on the iframe's w/h dimensions
|
||||
var videoRatio = getAspectRadio(oldDimension.width, oldDimension.height);
|
||||
|
||||
// Replace the iframe's dimensions and position the iframe absolute
|
||||
// This is the trick to emulate the video ratio
|
||||
$iframe.width('100%').height('100%')
|
||||
.css({
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0'
|
||||
});
|
||||
|
||||
|
||||
// Wrap the iframe in a new <div> which uses a dynamically fetched padding-top property
|
||||
// based on the video's w/h dimensions
|
||||
var wrap = document.createElement('div');
|
||||
wrap.className = 'fluid-vids';
|
||||
wrap.style.position = 'relative';
|
||||
wrap.style.marginBottom = '20px';
|
||||
wrap.style.width = '100%';
|
||||
wrap.style.paddingTop = videoRatio + '%';
|
||||
// Fix for appear inside tabs tag.
|
||||
(wrap.style.paddingTop === '') && (wrap.style.paddingTop = '50%');
|
||||
|
||||
// Add the iframe inside our newly created <div>
|
||||
var iframeParent = iframe.parentNode;
|
||||
iframeParent.insertBefore(wrap, iframe);
|
||||
wrap.appendChild(iframe);
|
||||
|
||||
// Additional adjustments for 163 Music
|
||||
if (this.src.search('music.163.com') > 0) {
|
||||
newDimension = getDimension($iframe);
|
||||
var shouldRecalculateAspect = newDimension.width > oldDimension.width ||
|
||||
newDimension.height < oldDimension.height;
|
||||
|
||||
// 163 Music Player has a fixed height, so we need to reset the aspect radio
|
||||
if (shouldRecalculateAspect) {
|
||||
wrap.style.paddingTop = getAspectRadio(newDimension.width, oldDimension.height) + '%';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function getDimension($element) {
|
||||
return {
|
||||
width: $element.width(),
|
||||
height: $element.height()
|
||||
};
|
||||
}
|
||||
|
||||
function getAspectRadio(width, height) {
|
||||
return height / width * 100;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add `menu-item-active` class name to menu item
|
||||
* via comparing location.path with menu item's href.
|
||||
*/
|
||||
addActiveClassToMenuItem: function () {
|
||||
var path = window.location.pathname;
|
||||
path = path === '/' ? path : path.substring(0, path.length - 1);
|
||||
$('.menu-item a[href^="' + path + '"]:first').parent().addClass('menu-item-active');
|
||||
},
|
||||
|
||||
hasMobileUA: function () {
|
||||
var nav = window.navigator;
|
||||
var ua = nav.userAgent;
|
||||
var pa = /iPad|iPhone|Android|Opera Mini|BlackBerry|webOS|UCWEB|Blazer|PSP|IEMobile|Symbian/g;
|
||||
|
||||
return pa.test(ua);
|
||||
},
|
||||
|
||||
isTablet: function () {
|
||||
return window.screen.width < 992 && window.screen.width > 767 && this.hasMobileUA();
|
||||
},
|
||||
|
||||
isMobile: function () {
|
||||
return window.screen.width < 767 && this.hasMobileUA();
|
||||
},
|
||||
|
||||
isDesktop: function () {
|
||||
return !this.isTablet() && !this.isMobile();
|
||||
},
|
||||
|
||||
/**
|
||||
* Escape meta symbols in jQuery selectors.
|
||||
*
|
||||
* @param selector
|
||||
* @returns {string|void|XML|*}
|
||||
*/
|
||||
escapeSelector: function (selector) {
|
||||
return selector.replace(/[!"$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, '\\$&');
|
||||
},
|
||||
|
||||
displaySidebar: function () {
|
||||
if (!this.isDesktop() || this.isPisces() || this.isGemini()) {
|
||||
return;
|
||||
}
|
||||
$('.sidebar-toggle').trigger('click');
|
||||
},
|
||||
|
||||
isMist: function () {
|
||||
return CONFIG.scheme === 'Mist';
|
||||
},
|
||||
|
||||
isPisces: function () {
|
||||
return CONFIG.scheme === 'Pisces';
|
||||
},
|
||||
|
||||
isGemini: function () {
|
||||
return CONFIG.scheme === 'Gemini';
|
||||
},
|
||||
|
||||
getScrollbarWidth: function () {
|
||||
var $div = $('<div />').addClass('scrollbar-measure').prependTo('body');
|
||||
var div = $div[0];
|
||||
var scrollbarWidth = div.offsetWidth - div.clientWidth;
|
||||
|
||||
$div.remove();
|
||||
|
||||
return scrollbarWidth;
|
||||
},
|
||||
|
||||
getContentVisibilityHeight: function () {
|
||||
var docHeight = $('#content').height(),
|
||||
winHeight = $(window).height(),
|
||||
contentVisibilityHeight = (docHeight > winHeight) ? (docHeight - winHeight) : ($(document).height() - winHeight);
|
||||
return contentVisibilityHeight;
|
||||
},
|
||||
|
||||
getSidebarb2tHeight: function () {
|
||||
//var sidebarb2tHeight = (CONFIG.sidebar.b2t) ? document.getElementsByClassName('back-to-top')[0].clientHeight : 0;
|
||||
var sidebarb2tHeight = (CONFIG.sidebar.b2t) ? $('.back-to-top').height() : 0;
|
||||
//var sidebarb2tHeight = (CONFIG.sidebar.b2t) ? 24 : 0;
|
||||
return sidebarb2tHeight;
|
||||
},
|
||||
|
||||
getSidebarSchemePadding: function () {
|
||||
var sidebarNavHeight = ($('.sidebar-nav').css('display') == 'block') ? $('.sidebar-nav').outerHeight(true) : 0,
|
||||
sidebarInner = $('.sidebar-inner'),
|
||||
sidebarPadding = sidebarInner.innerWidth() - sidebarInner.width(),
|
||||
sidebarSchemePadding = this.isPisces() || this.isGemini() ?
|
||||
((sidebarPadding * 2) + sidebarNavHeight + (CONFIG.sidebar.offset * 2) + this.getSidebarb2tHeight()) :
|
||||
((sidebarPadding * 2) + (sidebarNavHeight / 2));
|
||||
return sidebarSchemePadding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affix behaviour for Sidebar.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
// needAffix: function () {
|
||||
// return this.isPisces() || this.isGemini();
|
||||
// }
|
||||
};
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
initSidebarDimension();
|
||||
|
||||
/**
|
||||
* Init Sidebar & TOC inner dimensions on all pages and for all schemes.
|
||||
* Need for Sidebar/TOC inner scrolling if content taller then viewport.
|
||||
*/
|
||||
function initSidebarDimension () {
|
||||
var updateSidebarHeightTimer;
|
||||
|
||||
$(window).on('resize', function () {
|
||||
updateSidebarHeightTimer && clearTimeout(updateSidebarHeightTimer);
|
||||
|
||||
updateSidebarHeightTimer = setTimeout(function () {
|
||||
var sidebarWrapperHeight = document.body.clientHeight - NexT.utils.getSidebarSchemePadding();
|
||||
|
||||
updateSidebarHeight(sidebarWrapperHeight);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
// Initialize Sidebar & TOC Width.
|
||||
var scrollbarWidth = NexT.utils.getScrollbarWidth();
|
||||
if ($('.site-overview-wrap').height() > (document.body.clientHeight - NexT.utils.getSidebarSchemePadding())) {
|
||||
$('.site-overview').css('width', 'calc(100% + ' + scrollbarWidth + 'px)');
|
||||
}
|
||||
if ($('.post-toc-wrap').height() > (document.body.clientHeight - NexT.utils.getSidebarSchemePadding())) {
|
||||
$('.post-toc').css('width', 'calc(100% + ' + scrollbarWidth + 'px)');
|
||||
}
|
||||
|
||||
// Initialize Sidebar & TOC Height.
|
||||
updateSidebarHeight(document.body.clientHeight - NexT.utils.getSidebarSchemePadding());
|
||||
}
|
||||
|
||||
function updateSidebarHeight (height) {
|
||||
height = height || 'auto';
|
||||
$('.site-overview, .post-toc').css('max-height', height);
|
||||
}
|
||||
|
||||
});
|
Reference in New Issue
Block a user