|
@@ -1,5 +1,6 @@
|
|
|
-// The MIT License (MIT)
|
|
|
-// Typed.js | Copyright (c) 2014 Matt Boldt | www.mattboldt.com
|
|
|
+// ------------- JQUERY TYPED.JS -------------------------------------------- //
|
|
|
+// ============ https://github.com/mattboldt/typed.js/ ====================== //
|
|
|
+// -------------------------------------------------------------------------- //
|
|
|
|
|
|
|
|
|
!function($){
|
|
@@ -315,6 +316,11 @@
|
|
|
}(window.jQuery);
|
|
|
|
|
|
|
|
|
+
|
|
|
+// ------------- JQUERY SCROLLTO--------------------------------------------- //
|
|
|
+// ============ https://github.com/balupton/jquery-scrollto ================= //
|
|
|
+// -------------------------------------------------------------------------- //
|
|
|
+
|
|
|
/*global define:false require:false */
|
|
|
(function (name, context, definition) {
|
|
|
if (typeof module != 'undefined' && module.exports) module.exports = definition();
|
|
@@ -576,3 +582,1030 @@
|
|
|
// Export
|
|
|
return ScrollTo;
|
|
|
});
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// ------------- WOW ANIMATE ------------------------------------------------ //
|
|
|
+// ==============https://github.com/matthieua/WOW =========================== //
|
|
|
+// -------------------------------------------------------------------------- //
|
|
|
+
|
|
|
+(function() {
|
|
|
+ var MutationObserver, Util, WeakMap, getComputedStyle, getComputedStyleRX,
|
|
|
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
|
|
|
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
|
|
+
|
|
|
+ Util = (function() {
|
|
|
+ function Util() {}
|
|
|
+
|
|
|
+ Util.prototype.extend = function(custom, defaults) {
|
|
|
+ var key, value;
|
|
|
+ for (key in defaults) {
|
|
|
+ value = defaults[key];
|
|
|
+ if (custom[key] == null) {
|
|
|
+ custom[key] = value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return custom;
|
|
|
+ };
|
|
|
+
|
|
|
+ Util.prototype.isMobile = function(agent) {
|
|
|
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(agent);
|
|
|
+ };
|
|
|
+
|
|
|
+ Util.prototype.addEvent = function(elem, event, fn) {
|
|
|
+ if (elem.addEventListener != null) {
|
|
|
+ return elem.addEventListener(event, fn, false);
|
|
|
+ } else if (elem.attachEvent != null) {
|
|
|
+ return elem.attachEvent("on" + event, fn);
|
|
|
+ } else {
|
|
|
+ return elem[event] = fn;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ Util.prototype.removeEvent = function(elem, event, fn) {
|
|
|
+ if (elem.removeEventListener != null) {
|
|
|
+ return elem.removeEventListener(event, fn, false);
|
|
|
+ } else if (elem.detachEvent != null) {
|
|
|
+ return elem.detachEvent("on" + event, fn);
|
|
|
+ } else {
|
|
|
+ return delete elem[event];
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ Util.prototype.innerHeight = function() {
|
|
|
+ if ('innerHeight' in window) {
|
|
|
+ return window.innerHeight;
|
|
|
+ } else {
|
|
|
+ return document.documentElement.clientHeight;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return Util;
|
|
|
+
|
|
|
+ })();
|
|
|
+
|
|
|
+ WeakMap = this.WeakMap || this.MozWeakMap || (WeakMap = (function() {
|
|
|
+ function WeakMap() {
|
|
|
+ this.keys = [];
|
|
|
+ this.values = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ WeakMap.prototype.get = function(key) {
|
|
|
+ var i, item, _i, _len, _ref;
|
|
|
+ _ref = this.keys;
|
|
|
+ for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
|
|
+ item = _ref[i];
|
|
|
+ if (item === key) {
|
|
|
+ return this.values[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WeakMap.prototype.set = function(key, value) {
|
|
|
+ var i, item, _i, _len, _ref;
|
|
|
+ _ref = this.keys;
|
|
|
+ for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
|
|
+ item = _ref[i];
|
|
|
+ if (item === key) {
|
|
|
+ this.values[i] = value;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.keys.push(key);
|
|
|
+ return this.values.push(value);
|
|
|
+ };
|
|
|
+
|
|
|
+ return WeakMap;
|
|
|
+
|
|
|
+ })());
|
|
|
+
|
|
|
+ MutationObserver = this.MutationObserver || this.WebkitMutationObserver || this.MozMutationObserver || (MutationObserver = (function() {
|
|
|
+ function MutationObserver() {
|
|
|
+ if (typeof console !== "undefined" && console !== null) {
|
|
|
+ console.warn('MutationObserver is not supported by your browser.');
|
|
|
+ }
|
|
|
+ if (typeof console !== "undefined" && console !== null) {
|
|
|
+ console.warn('WOW.js cannot detect dom mutations, please call .sync() after loading new content.');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MutationObserver.notSupported = true;
|
|
|
+
|
|
|
+ MutationObserver.prototype.observe = function() {};
|
|
|
+
|
|
|
+ return MutationObserver;
|
|
|
+
|
|
|
+ })());
|
|
|
+
|
|
|
+ getComputedStyle = this.getComputedStyle || function(el, pseudo) {
|
|
|
+ this.getPropertyValue = function(prop) {
|
|
|
+ var _ref;
|
|
|
+ if (prop === 'float') {
|
|
|
+ prop = 'styleFloat';
|
|
|
+ }
|
|
|
+ if (getComputedStyleRX.test(prop)) {
|
|
|
+ prop.replace(getComputedStyleRX, function(_, char) {
|
|
|
+ return char.toUpperCase();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return ((_ref = el.currentStyle) != null ? _ref[prop] : void 0) || null;
|
|
|
+ };
|
|
|
+ return this;
|
|
|
+ };
|
|
|
+
|
|
|
+ getComputedStyleRX = /(\-([a-z]){1})/g;
|
|
|
+
|
|
|
+ this.WOW = (function() {
|
|
|
+ WOW.prototype.defaults = {
|
|
|
+ boxClass: 'wow',
|
|
|
+ animateClass: 'animated',
|
|
|
+ offset: 0,
|
|
|
+ mobile: true,
|
|
|
+ live: true
|
|
|
+ };
|
|
|
+
|
|
|
+ function WOW(options) {
|
|
|
+ if (options == null) {
|
|
|
+ options = {};
|
|
|
+ }
|
|
|
+ this.scrollCallback = __bind(this.scrollCallback, this);
|
|
|
+ this.scrollHandler = __bind(this.scrollHandler, this);
|
|
|
+ this.start = __bind(this.start, this);
|
|
|
+ this.scrolled = true;
|
|
|
+ this.config = this.util().extend(options, this.defaults);
|
|
|
+ this.animationNameCache = new WeakMap();
|
|
|
+ }
|
|
|
+
|
|
|
+ WOW.prototype.init = function() {
|
|
|
+ var _ref;
|
|
|
+ this.element = window.document.documentElement;
|
|
|
+ if ((_ref = document.readyState) === "interactive" || _ref === "complete") {
|
|
|
+ this.start();
|
|
|
+ } else {
|
|
|
+ this.util().addEvent(document, 'DOMContentLoaded', this.start);
|
|
|
+ }
|
|
|
+ return this.finished = [];
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.start = function() {
|
|
|
+ var box, _i, _len, _ref;
|
|
|
+ this.stopped = false;
|
|
|
+ this.boxes = (function() {
|
|
|
+ var _i, _len, _ref, _results;
|
|
|
+ _ref = this.element.querySelectorAll("." + this.config.boxClass);
|
|
|
+ _results = [];
|
|
|
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
|
+ box = _ref[_i];
|
|
|
+ _results.push(box);
|
|
|
+ }
|
|
|
+ return _results;
|
|
|
+ }).call(this);
|
|
|
+ this.all = (function() {
|
|
|
+ var _i, _len, _ref, _results;
|
|
|
+ _ref = this.boxes;
|
|
|
+ _results = [];
|
|
|
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
|
+ box = _ref[_i];
|
|
|
+ _results.push(box);
|
|
|
+ }
|
|
|
+ return _results;
|
|
|
+ }).call(this);
|
|
|
+ if (this.boxes.length) {
|
|
|
+ if (this.disabled()) {
|
|
|
+ this.resetStyle();
|
|
|
+ } else {
|
|
|
+ _ref = this.boxes;
|
|
|
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
|
+ box = _ref[_i];
|
|
|
+ this.applyStyle(box, true);
|
|
|
+ }
|
|
|
+ this.util().addEvent(window, 'scroll', this.scrollHandler);
|
|
|
+ this.util().addEvent(window, 'resize', this.scrollHandler);
|
|
|
+ this.interval = setInterval(this.scrollCallback, 50);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (this.config.live) {
|
|
|
+ return new MutationObserver((function(_this) {
|
|
|
+ return function(records) {
|
|
|
+ var node, record, _j, _len1, _results;
|
|
|
+ _results = [];
|
|
|
+ for (_j = 0, _len1 = records.length; _j < _len1; _j++) {
|
|
|
+ record = records[_j];
|
|
|
+ _results.push((function() {
|
|
|
+ var _k, _len2, _ref1, _results1;
|
|
|
+ _ref1 = record.addedNodes || [];
|
|
|
+ _results1 = [];
|
|
|
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
|
|
|
+ node = _ref1[_k];
|
|
|
+ _results1.push(this.doSync(node));
|
|
|
+ }
|
|
|
+ return _results1;
|
|
|
+ }).call(_this));
|
|
|
+ }
|
|
|
+ return _results;
|
|
|
+ };
|
|
|
+ })(this)).observe(document.body, {
|
|
|
+ childList: true,
|
|
|
+ subtree: true
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.stop = function() {
|
|
|
+ this.stopped = true;
|
|
|
+ this.util().removeEvent(window, 'scroll', this.scrollHandler);
|
|
|
+ this.util().removeEvent(window, 'resize', this.scrollHandler);
|
|
|
+ if (this.interval != null) {
|
|
|
+ return clearInterval(this.interval);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.sync = function(element) {
|
|
|
+ if (MutationObserver.notSupported) {
|
|
|
+ return this.doSync(this.element);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.doSync = function(element) {
|
|
|
+ var box, _i, _len, _ref, _results;
|
|
|
+ if (element == null) {
|
|
|
+ element = this.element;
|
|
|
+ }
|
|
|
+ if (element.nodeType !== 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ element = element.parentNode || element;
|
|
|
+ _ref = element.querySelectorAll("." + this.config.boxClass);
|
|
|
+ _results = [];
|
|
|
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
|
+ box = _ref[_i];
|
|
|
+ if (__indexOf.call(this.all, box) < 0) {
|
|
|
+ this.boxes.push(box);
|
|
|
+ this.all.push(box);
|
|
|
+ if (this.stopped || this.disabled()) {
|
|
|
+ this.resetStyle();
|
|
|
+ } else {
|
|
|
+ this.applyStyle(box, true);
|
|
|
+ }
|
|
|
+ _results.push(this.scrolled = true);
|
|
|
+ } else {
|
|
|
+ _results.push(void 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return _results;
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.show = function(box) {
|
|
|
+ this.applyStyle(box);
|
|
|
+ return box.className = "" + box.className + " " + this.config.animateClass;
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.applyStyle = function(box, hidden) {
|
|
|
+ var delay, duration, iteration;
|
|
|
+ duration = box.getAttribute('data-wow-duration');
|
|
|
+ delay = box.getAttribute('data-wow-delay');
|
|
|
+ iteration = box.getAttribute('data-wow-iteration');
|
|
|
+ return this.animate((function(_this) {
|
|
|
+ return function() {
|
|
|
+ return _this.customStyle(box, hidden, duration, delay, iteration);
|
|
|
+ };
|
|
|
+ })(this));
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.animate = (function() {
|
|
|
+ if ('requestAnimationFrame' in window) {
|
|
|
+ return function(callback) {
|
|
|
+ return window.requestAnimationFrame(callback);
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ return function(callback) {
|
|
|
+ return callback();
|
|
|
+ };
|
|
|
+ }
|
|
|
+ })();
|
|
|
+
|
|
|
+ WOW.prototype.resetStyle = function() {
|
|
|
+ var box, _i, _len, _ref, _results;
|
|
|
+ _ref = this.boxes;
|
|
|
+ _results = [];
|
|
|
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
|
+ box = _ref[_i];
|
|
|
+ _results.push(box.style.visibility = 'visible');
|
|
|
+ }
|
|
|
+ return _results;
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.customStyle = function(box, hidden, duration, delay, iteration) {
|
|
|
+ if (hidden) {
|
|
|
+ this.cacheAnimationName(box);
|
|
|
+ }
|
|
|
+ box.style.visibility = hidden ? 'hidden' : 'visible';
|
|
|
+ if (duration) {
|
|
|
+ this.vendorSet(box.style, {
|
|
|
+ animationDuration: duration
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (delay) {
|
|
|
+ this.vendorSet(box.style, {
|
|
|
+ animationDelay: delay
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (iteration) {
|
|
|
+ this.vendorSet(box.style, {
|
|
|
+ animationIterationCount: iteration
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.vendorSet(box.style, {
|
|
|
+ animationName: hidden ? 'none' : this.cachedAnimationName(box)
|
|
|
+ });
|
|
|
+ return box;
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.vendors = ["moz", "webkit"];
|
|
|
+
|
|
|
+ WOW.prototype.vendorSet = function(elem, properties) {
|
|
|
+ var name, value, vendor, _results;
|
|
|
+ _results = [];
|
|
|
+ for (name in properties) {
|
|
|
+ value = properties[name];
|
|
|
+ elem["" + name] = value;
|
|
|
+ _results.push((function() {
|
|
|
+ var _i, _len, _ref, _results1;
|
|
|
+ _ref = this.vendors;
|
|
|
+ _results1 = [];
|
|
|
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
|
+ vendor = _ref[_i];
|
|
|
+ _results1.push(elem["" + vendor + (name.charAt(0).toUpperCase()) + (name.substr(1))] = value);
|
|
|
+ }
|
|
|
+ return _results1;
|
|
|
+ }).call(this));
|
|
|
+ }
|
|
|
+ return _results;
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.vendorCSS = function(elem, property) {
|
|
|
+ var result, style, vendor, _i, _len, _ref;
|
|
|
+ style = getComputedStyle(elem);
|
|
|
+ result = style.getPropertyCSSValue(property);
|
|
|
+ _ref = this.vendors;
|
|
|
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
|
+ vendor = _ref[_i];
|
|
|
+ result = result || style.getPropertyCSSValue("-" + vendor + "-" + property);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.animationName = function(box) {
|
|
|
+ var animationName;
|
|
|
+ try {
|
|
|
+ animationName = this.vendorCSS(box, 'animation-name').cssText;
|
|
|
+ } catch (_error) {
|
|
|
+ animationName = getComputedStyle(box).getPropertyValue('animation-name');
|
|
|
+ }
|
|
|
+ if (animationName === 'none') {
|
|
|
+ return '';
|
|
|
+ } else {
|
|
|
+ return animationName;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.cacheAnimationName = function(box) {
|
|
|
+ return this.animationNameCache.set(box, this.animationName(box));
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.cachedAnimationName = function(box) {
|
|
|
+ return this.animationNameCache.get(box);
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.scrollHandler = function() {
|
|
|
+ return this.scrolled = true;
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.scrollCallback = function() {
|
|
|
+ var box;
|
|
|
+ if (this.scrolled) {
|
|
|
+ this.scrolled = false;
|
|
|
+ this.boxes = (function() {
|
|
|
+ var _i, _len, _ref, _results;
|
|
|
+ _ref = this.boxes;
|
|
|
+ _results = [];
|
|
|
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
|
+ box = _ref[_i];
|
|
|
+ if (!(box)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (this.isVisible(box)) {
|
|
|
+ this.show(box);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ _results.push(box);
|
|
|
+ }
|
|
|
+ return _results;
|
|
|
+ }).call(this);
|
|
|
+ if (!(this.boxes.length || this.config.live)) {
|
|
|
+ return this.stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.offsetTop = function(element) {
|
|
|
+ var top;
|
|
|
+ while (element.offsetTop === void 0) {
|
|
|
+ element = element.parentNode;
|
|
|
+ }
|
|
|
+ top = element.offsetTop;
|
|
|
+ while (element = element.offsetParent) {
|
|
|
+ top += element.offsetTop;
|
|
|
+ }
|
|
|
+ return top;
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.isVisible = function(box) {
|
|
|
+ var bottom, offset, top, viewBottom, viewTop;
|
|
|
+ offset = box.getAttribute('data-wow-offset') || this.config.offset;
|
|
|
+ viewTop = window.pageYOffset;
|
|
|
+ viewBottom = viewTop + Math.min(this.element.clientHeight, this.util().innerHeight()) - offset;
|
|
|
+ top = this.offsetTop(box);
|
|
|
+ bottom = top + box.clientHeight;
|
|
|
+ return top <= viewBottom && bottom >= viewTop;
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.util = function() {
|
|
|
+ return this._util != null ? this._util : this._util = new Util();
|
|
|
+ };
|
|
|
+
|
|
|
+ WOW.prototype.disabled = function() {
|
|
|
+ return !this.config.mobile && this.util().isMobile(navigator.userAgent);
|
|
|
+ };
|
|
|
+
|
|
|
+ return WOW;
|
|
|
+
|
|
|
+ })();
|
|
|
+
|
|
|
+}).call(this);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// ------------- JQUERY SMOOTHSTATE ----------------------------------------- //
|
|
|
+// ============ https://github.com/miguel-perez/jquery.smoothState.js ======= //
|
|
|
+// -------------------------------------------------------------------------- //
|
|
|
+
|
|
|
+;(function ( $, window, document, undefined ) {
|
|
|
+ "use strict";
|
|
|
+
|
|
|
+ var
|
|
|
+ /** Used later to scroll page to the top */
|
|
|
+ $body = $("html, body"),
|
|
|
+
|
|
|
+ /** Used in development mode to console out useful warnings */
|
|
|
+ consl = (window.console || false),
|
|
|
+
|
|
|
+ /** Plugin default options */
|
|
|
+ defaults = {
|
|
|
+
|
|
|
+ /** jquery element string to specify which anchors smoothstate should bind to */
|
|
|
+ anchors : "a",
|
|
|
+
|
|
|
+ /** If set to true, smoothState will prefetch a link's contents on hover */
|
|
|
+ prefetch : false,
|
|
|
+
|
|
|
+ /** A selecor that deinfes with links should be ignored by smoothState */
|
|
|
+ blacklist : ".no-smoothstate, [target]",
|
|
|
+
|
|
|
+ /** If set to true, smoothState will log useful debug information instead of aborting */
|
|
|
+ development : false,
|
|
|
+
|
|
|
+ /** The number of pages smoothState will try to store in memory and not request again */
|
|
|
+ pageCacheSize : 0,
|
|
|
+
|
|
|
+ /** A function that can be used to alter urls before they are used to request content */
|
|
|
+ alterRequestUrl : function (url) {
|
|
|
+ return url;
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Run when a link has been activated */
|
|
|
+ onStart : {
|
|
|
+ duration: 0,
|
|
|
+ render: function (url, $container) {
|
|
|
+ $body.scrollTop(0);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Run if the page request is still pending and onStart has finished animating */
|
|
|
+ onProgress : {
|
|
|
+ duration: 0,
|
|
|
+ render: function (url, $container) {
|
|
|
+ $body.css("cursor", "wait");
|
|
|
+ $body.find("a").css("cursor", "wait");
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Run when requested content is ready to be injected into the page */
|
|
|
+ onEnd : {
|
|
|
+ duration: 0,
|
|
|
+ render: function (url, $container, $content) {
|
|
|
+ $body.css("cursor", "auto");
|
|
|
+ $body.find("a").css("cursor", "auto");
|
|
|
+ $container.html($content);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Run when content has been injected and all animations are complete */
|
|
|
+ callback : function(url, $container, $content) {
|
|
|
+
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Utility functions that are decoupled from SmoothState */
|
|
|
+ utility = {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Checks to see if the url is external
|
|
|
+ * @param {string} url - url being evaluated
|
|
|
+ * @see http://stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls
|
|
|
+ *
|
|
|
+ */
|
|
|
+ isExternal: function (url) {
|
|
|
+ var match = url.match(/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/);
|
|
|
+ if (typeof match[1] === "string" && match[1].length > 0 && match[1].toLowerCase() !== window.location.protocol) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (typeof match[2] === "string" && match[2].length > 0 && match[2].replace(new RegExp(":(" + {"http:": 80, "https:": 443}[window.location.protocol] + ")?$"), "") !== window.location.host) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Checks to see if the url is an internal hash
|
|
|
+ * @param {string} url - url being evaluated
|
|
|
+ *
|
|
|
+ */
|
|
|
+ isHash: function (url) {
|
|
|
+ var hasPathname = (url.indexOf(window.location.pathname) > 0) ? true : false,
|
|
|
+ hasHash = (url.indexOf("#") > 0) ? true : false;
|
|
|
+ return (hasPathname && hasHash) ? true : false;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Checks to see if we should be loading this URL
|
|
|
+ * @param {string} url - url being evaluated
|
|
|
+ * @param {string} blacklist - jquery selector
|
|
|
+ *
|
|
|
+ */
|
|
|
+ shouldLoad: function ($anchor, blacklist) {
|
|
|
+ var url = $anchor.prop("href");
|
|
|
+ // URL will only be loaded if it"s not an external link, hash, or blacklisted
|
|
|
+ return (!utility.isExternal(url) && !utility.isHash(url) && !$anchor.is(blacklist));
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Prevents jQuery from stripping elements from $(html)
|
|
|
+ * @param {string} url - url being evaluated
|
|
|
+ * @author Ben Alman http://benalman.com/
|
|
|
+ * @see https://gist.github.com/cowboy/742952
|
|
|
+ *
|
|
|
+ */
|
|
|
+ htmlDoc: function (html) {
|
|
|
+ var parent,
|
|
|
+ elems = $(),
|
|
|
+ matchTag = /<(\/?)(html|head|body|title|base|meta)(\s+[^>]*)?>/ig,
|
|
|
+ prefix = "ss" + Math.round(Math.random() * 100000),
|
|
|
+ htmlParsed = html.replace(matchTag, function(tag, slash, name, attrs) {
|
|
|
+ var obj = {};
|
|
|
+ if (!slash) {
|
|
|
+ elems = elems.add("<" + name + "/>");
|
|
|
+ if (attrs) {
|
|
|
+ $.each($("<div" + attrs + "/>")[0].attributes, function(i, attr) {
|
|
|
+ obj[attr.name] = attr.value;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ elems.eq(-1).attr(obj);
|
|
|
+ }
|
|
|
+ return "<" + slash + "div" + (slash ? "" : " id='" + prefix + (elems.length - 1) + "'") + ">";
|
|
|
+ });
|
|
|
+
|
|
|
+ // If no placeholder elements were necessary, just return normal
|
|
|
+ // jQuery-parsed HTML.
|
|
|
+ if (!elems.length) {
|
|
|
+ return $(html);
|
|
|
+ }
|
|
|
+ // Create parent node if it hasn"t been created yet.
|
|
|
+ if (!parent) {
|
|
|
+ parent = $("<div/>");
|
|
|
+ }
|
|
|
+ // Create the parent node and append the parsed, place-held HTML.
|
|
|
+ parent.html(htmlParsed);
|
|
|
+
|
|
|
+ // Replace each placeholder element with its intended element.
|
|
|
+ $.each(elems, function(i) {
|
|
|
+ var elem = parent.find("#" + prefix + i).before(elems[i]);
|
|
|
+ elems.eq(i).html(elem.contents());
|
|
|
+ elem.remove();
|
|
|
+ });
|
|
|
+
|
|
|
+ return parent.children().unwrap();
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Resets an object if it has too many properties
|
|
|
+ *
|
|
|
+ * This is used to clear the "cache" object that stores
|
|
|
+ * all of the html. This would prevent the client from
|
|
|
+ * running out of memory and allow the user to hit the
|
|
|
+ * server for a fresh copy of the content.
|
|
|
+ *
|
|
|
+ * @param {object} obj
|
|
|
+ * @param {number} cap
|
|
|
+ *
|
|
|
+ */
|
|
|
+ clearIfOverCapacity: function (obj, cap) {
|
|
|
+ // Polyfill Object.keys if it doesn"t exist
|
|
|
+ if (!Object.keys) {
|
|
|
+ Object.keys = function (obj) {
|
|
|
+ var keys = [],
|
|
|
+ k;
|
|
|
+ for (k in obj) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
|
|
|
+ keys.push(k);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return keys;
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Object.keys(obj).length > cap) {
|
|
|
+ obj = {};
|
|
|
+ }
|
|
|
+
|
|
|
+ return obj;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Finds the inner content of an element, by an ID, from a jQuery object
|
|
|
+ * @param {string} id
|
|
|
+ * @param {object} $html
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getContentById: function (id, $html) {
|
|
|
+ $html = ($html instanceof jQuery) ? $html : utility.htmlDoc($html);
|
|
|
+ var $insideElem = $html.find(id),
|
|
|
+ updatedContainer = ($insideElem.length) ? $.trim($insideElem.html()) : $html.filter(id).html(),
|
|
|
+ newContent = (updatedContainer.length) ? $(updatedContainer) : null;
|
|
|
+ return newContent;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Stores html content as jquery object in given object
|
|
|
+ * @param {object} object - object contents will be stored into
|
|
|
+ * @param {string} url - url to be used as the prop
|
|
|
+ * @param {jquery} html - contents to store
|
|
|
+ *
|
|
|
+ */
|
|
|
+ storePageIn: function (object, url, $html) {
|
|
|
+ $html = ($html instanceof jQuery) ? $html : utility.htmlDoc($html);
|
|
|
+ object[url] = { // Content is indexed by the url
|
|
|
+ status: "loaded",
|
|
|
+ title: $html.find("title").text(), // Stores the title of the page
|
|
|
+ html: $html // Stores the contents of the page
|
|
|
+ };
|
|
|
+ return object;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Triggers an "allanimationend" event when all animations are complete
|
|
|
+ * @param {object} $element - jQuery object that should trigger event
|
|
|
+ * @param {string} resetOn - which other events to trigger allanimationend on
|
|
|
+ *
|
|
|
+ */
|
|
|
+ triggerAllAnimationEndEvent: function ($element, resetOn) {
|
|
|
+
|
|
|
+ resetOn = " " + resetOn || "";
|
|
|
+
|
|
|
+ var animationCount = 0,
|
|
|
+ animationstart = "animationstart webkitAnimationStart oanimationstart MSAnimationStart",
|
|
|
+ animationend = "animationend webkitAnimationEnd oanimationend MSAnimationEnd",
|
|
|
+ eventname = "allanimationend",
|
|
|
+ onAnimationStart = function (e) {
|
|
|
+ if ($(e.delegateTarget).is($element)) {
|
|
|
+ e.stopPropagation();
|
|
|
+ animationCount ++;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onAnimationEnd = function (e) {
|
|
|
+ if ($(e.delegateTarget).is($element)) {
|
|
|
+ e.stopPropagation();
|
|
|
+ animationCount --;
|
|
|
+ if(animationCount === 0) {
|
|
|
+ $element.trigger(eventname);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ $element.on(animationstart, onAnimationStart);
|
|
|
+ $element.on(animationend, onAnimationEnd);
|
|
|
+
|
|
|
+ $element.on("allanimationend" + resetOn, function(){
|
|
|
+ animationCount = 0;
|
|
|
+ utility.redraw($element);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Forces browser to redraw elements */
|
|
|
+ redraw: function ($element) {
|
|
|
+ $element.height(0);
|
|
|
+ setTimeout(function(){$element.height("auto");}, 0);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Handles the popstate event, like when the user hits "back" */
|
|
|
+ onPopState = function ( e ) {
|
|
|
+ if(e.state !== null) {
|
|
|
+ var url = window.location.href,
|
|
|
+ $page = $("#" + e.state.id),
|
|
|
+ page = $page.data("smoothState");
|
|
|
+
|
|
|
+ if(page.href !== url && !utility.isHash(url)) {
|
|
|
+ page.load(url, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Constructor function */
|
|
|
+ SmoothState = function ( element, options ) {
|
|
|
+ var
|
|
|
+ /** Container element smoothState is run on */
|
|
|
+ $container = $(element),
|
|
|
+
|
|
|
+ /** Variable that stores pages after they are requested */
|
|
|
+ cache = {},
|
|
|
+
|
|
|
+ /** Url of the content that is currently displayed */
|
|
|
+ currentHref = window.location.href,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Loads the contents of a url into our container
|
|
|
+ *
|
|
|
+ * @param {string} url
|
|
|
+ * @param {bool} isPopped - used to determine if whe should
|
|
|
+ * add a new item into the history object
|
|
|
+ *
|
|
|
+ */
|
|
|
+ load = function (url, isPopped) {
|
|
|
+
|
|
|
+ /** Makes this an optional variable by setting a default */
|
|
|
+ isPopped = isPopped || false;
|
|
|
+
|
|
|
+ var
|
|
|
+ /** Used to check if the onProgress function has been run */
|
|
|
+ hasRunCallback = false,
|
|
|
+
|
|
|
+ callbBackEnded = false,
|
|
|
+
|
|
|
+ /** List of responses for the states of the page request */
|
|
|
+ responses = {
|
|
|
+
|
|
|
+ /** Page is ready, update the content */
|
|
|
+ loaded: function() {
|
|
|
+ var eventName = hasRunCallback ? "ss.onProgressEnd" : "ss.onStartEnd";
|
|
|
+
|
|
|
+ if(!callbBackEnded || !hasRunCallback) {
|
|
|
+ $container.one(eventName, function(){
|
|
|
+ updateContent(url);
|
|
|
+ });
|
|
|
+ } else if(callbBackEnded) {
|
|
|
+ updateContent(url);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!isPopped) {
|
|
|
+ window.history.pushState({ id: $container.prop("id") }, cache[url].title, url);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Loading, wait 10 ms and check again */
|
|
|
+ fetching: function() {
|
|
|
+
|
|
|
+ if(!hasRunCallback) {
|
|
|
+
|
|
|
+ hasRunCallback = true;
|
|
|
+
|
|
|
+ // Run the onProgress callback and set trigger
|
|
|
+ $container.one("ss.onStartEnd", function(){
|
|
|
+ options.onProgress.render(url, $container, null);
|
|
|
+
|
|
|
+ setTimeout(function(){
|
|
|
+ $container.trigger("ss.onProgressEnd");
|
|
|
+ callbBackEnded = true;
|
|
|
+ }, options.onStart.duration);
|
|
|
+
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ setTimeout(function () {
|
|
|
+ // Might of been canceled, better check!
|
|
|
+ if(cache.hasOwnProperty(url)){
|
|
|
+ responses[cache[url].status]();
|
|
|
+ }
|
|
|
+ }, 10);
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Error, abort and redirect */
|
|
|
+ error: function(){
|
|
|
+ window.location = url;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ if (!cache.hasOwnProperty(url)) {
|
|
|
+ fetch(url);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Run the onStart callback and set trigger
|
|
|
+ options.onStart.render(url, $container, null);
|
|
|
+ setTimeout(function(){
|
|
|
+ $container.trigger("ss.onStartEnd");
|
|
|
+ }, options.onStart.duration);
|
|
|
+
|
|
|
+ // Start checking for the status of content
|
|
|
+ responses[cache[url].status]();
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Updates the contents from cache[url] */
|
|
|
+ updateContent = function (url) {
|
|
|
+ // If the content has been requested and is done:
|
|
|
+ var containerId = "#" + $container.prop("id"),
|
|
|
+ $content = cache[url] ? utility.getContentById(containerId, cache[url].html) : null;
|
|
|
+
|
|
|
+ if($content) {
|
|
|
+ document.title = cache[url].title;
|
|
|
+ $container.data("smoothState").href = url;
|
|
|
+
|
|
|
+ // Call the onEnd callback and set trigger
|
|
|
+ options.onEnd.render(url, $container, $content);
|
|
|
+
|
|
|
+ $container.one("ss.onEndEnd", function(){
|
|
|
+ options.callback(url, $container, $content);
|
|
|
+ });
|
|
|
+
|
|
|
+ setTimeout(function(){
|
|
|
+ $container.trigger("ss.onEndEnd");
|
|
|
+ }, options.onEnd.duration);
|
|
|
+
|
|
|
+ } else if (!$content && options.development && consl) {
|
|
|
+ // Throw warning to help debug in development mode
|
|
|
+ consl.warn("No element with an id of " + containerId + " in response from " + url + " in " + cache);
|
|
|
+ } else {
|
|
|
+ // No content availble to update with, aborting...
|
|
|
+ window.location = url;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Fetches the contents of a url and stores it in the "cache" varible
|
|
|
+ * @param {string} url
|
|
|
+ *
|
|
|
+ */
|
|
|
+ fetch = function (url) {
|
|
|
+
|
|
|
+ // Don"t fetch we have the content already
|
|
|
+ if(cache.hasOwnProperty(url)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ cache = utility.clearIfOverCapacity(cache, options.pageCacheSize);
|
|
|
+
|
|
|
+ cache[url] = { status: "fetching" };
|
|
|
+
|
|
|
+ var requestUrl = options.alterRequestUrl(url) || url,
|
|
|
+ request = $.ajax(requestUrl);
|
|
|
+
|
|
|
+ // Store contents in cache variable if successful
|
|
|
+ request.success(function (html) {
|
|
|
+ // Clear cache varible if it"s getting too big
|
|
|
+ utility.storePageIn(cache, url, html);
|
|
|
+ $container.data("smoothState").cache = cache;
|
|
|
+ });
|
|
|
+
|
|
|
+ // Mark as error
|
|
|
+ request.error(function () {
|
|
|
+ cache[url].status = "error";
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * Binds to the hover event of a link, used for prefetching content
|
|
|
+ *
|
|
|
+ * @param {object} event
|
|
|
+ *
|
|
|
+ */
|
|
|
+ hoverAnchor = function (event) {
|
|
|
+ var $anchor = $(event.currentTarget),
|
|
|
+ url = $anchor.prop("href");
|
|
|
+ if (utility.shouldLoad($anchor, options.blacklist)) {
|
|
|
+ event.stopPropagation();
|
|
|
+ fetch(url);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Binds to the click event of a link, used to show the content
|
|
|
+ *
|
|
|
+ * @param {object} event
|
|
|
+ *
|
|
|
+ */
|
|
|
+ clickAnchor = function (event) {
|
|
|
+ var $anchor = $(event.currentTarget),
|
|
|
+ url = $anchor.prop("href");
|
|
|
+
|
|
|
+ // Ctrl (or Cmd) + click must open a new tab
|
|
|
+ if (!event.metaKey && !event.ctrlKey && utility.shouldLoad($anchor, options.blacklist)) {
|
|
|
+ // stopPropagation so that event doesn"t fire on parent containers.
|
|
|
+ event.stopPropagation();
|
|
|
+ event.preventDefault();
|
|
|
+ load(url);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Binds all events and inits functionality
|
|
|
+ *
|
|
|
+ * @param {object} event
|
|
|
+ *
|
|
|
+ */
|
|
|
+ bindEventHandlers = function ($element) {
|
|
|
+ //@todo: Handle form submissions
|
|
|
+ $element.on("click", options.anchors, clickAnchor);
|
|
|
+
|
|
|
+ if (options.prefetch) {
|
|
|
+ $element.on("mouseover touchstart", options.anchors, hoverAnchor);
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Used to restart css animations with a class */
|
|
|
+ toggleAnimationClass = function (classname) {
|
|
|
+ var classes = $container.addClass(classname).prop("class");
|
|
|
+
|
|
|
+ $container.removeClass(classes);
|
|
|
+
|
|
|
+ setTimeout(function(){
|
|
|
+ $container.addClass(classes);
|
|
|
+ },0);
|
|
|
+
|
|
|
+ $container.one("ss.onStartEnd ss.onProgressEnd ss.onEndEnd", function(){
|
|
|
+ $container.removeClass(classname);
|
|
|
+ });
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ /** Override defaults with options passed in */
|
|
|
+ options = $.extend(defaults, options);
|
|
|
+
|
|
|
+ /** Sets a default state */
|
|
|
+ if(window.history.state === null) {
|
|
|
+ window.history.replaceState({ id: $container.prop("id") }, document.title, currentHref);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Stores the current page in cache variable */
|
|
|
+ utility.storePageIn(cache, currentHref, document.documentElement.outerHTML);
|
|
|
+
|
|
|
+ /** Bind all of the event handlers on the container, not anchors */
|
|
|
+ utility.triggerAllAnimationEndEvent($container, "ss.onStartEnd ss.onProgressEnd ss.onEndEnd");
|
|
|
+
|
|
|
+ /** Bind all of the event handlers on the container, not anchors */
|
|
|
+ bindEventHandlers($container);
|
|
|
+
|
|
|
+ /** Public methods */
|
|
|
+ return {
|
|
|
+ href: currentHref,
|
|
|
+ cache: cache,
|
|
|
+ load: load,
|
|
|
+ fetch: fetch,
|
|
|
+ toggleAnimationClass: toggleAnimationClass
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ /** Returns elements with SmoothState attached to it */
|
|
|
+ declareSmoothState = function ( options ) {
|
|
|
+ return this.each(function () {
|
|
|
+ // Checks to make sure the smoothState element has an id and isn"t already bound
|
|
|
+ if(this.id && !$.data(this, "smoothState")) {
|
|
|
+ // Makes public methods available via $("element").data("smoothState");
|
|
|
+ $.data(this, "smoothState", new SmoothState(this, options));
|
|
|
+ } else if (!this.id && consl) {
|
|
|
+ // Throw warning if in development mode
|
|
|
+ consl.warn("Every smoothState container needs an id but the following one does not have one:", this);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ /** Sets the popstate function */
|
|
|
+ window.onpopstate = onPopState;
|
|
|
+
|
|
|
+ /** Makes utility functions public for unit tests */
|
|
|
+ $.smoothStateUtility = utility;
|
|
|
+
|
|
|
+ /** Defines the smoothState plugin */
|
|
|
+ $.fn.smoothState = declareSmoothState;
|
|
|
+
|
|
|
+})(jQuery, window, document);
|