Merge branch 'about-page'
This commit is contained in:
commit
b25e10dd2c
1
Gemfile
1
Gemfile
|
@ -12,6 +12,7 @@ gem 'uglifier', '>= 1.3.0'
|
|||
gem 'jquery-rails'
|
||||
gem 'foundation-rails', '~> 5.2.1'
|
||||
gem 'foundation-icons-sass-rails'
|
||||
gem 'font-awesome-sass'
|
||||
|
||||
gem 'mongoid'
|
||||
gem 'mongoid-pagination'
|
||||
|
|
|
@ -73,6 +73,8 @@ GEM
|
|||
ffi (1.9.5)
|
||||
figaro (1.0.0)
|
||||
thor (~> 0.14)
|
||||
font-awesome-sass (4.0.2)
|
||||
sass-rails (>= 3.1.1)
|
||||
formatador (0.2.5)
|
||||
foundation-icons-sass-rails (3.0.0)
|
||||
railties (>= 3.1.1)
|
||||
|
@ -285,6 +287,7 @@ DEPENDENCIES
|
|||
database_cleaner
|
||||
factory_girl_rails
|
||||
figaro
|
||||
font-awesome-sass
|
||||
foundation-icons-sass-rails
|
||||
foundation-rails (~> 5.2.1)
|
||||
guard
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="图形" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<path fill="#219AB3" d="M926.917973 774.19392C959.65184 774.19392 986.19392 747.65184 986.19392 714.917973L986.19392-114.917973C986.19392-147.65184 959.65184-174.19392 926.917973-174.19392L97.082027-174.19392C64.34816-174.19392 37.80608-147.65184 37.80608-114.917973L37.80608 714.917973C37.80608 747.65184 64.34816 774.19392 97.082027 774.19392zM176.653653 635.80032 176.653653 559.321173 825.658027 559.321173 825.658027 635.80032zM217.719467 495.853653 217.719467 183.91936 273.524053 183.91936 341.292373 41.60384 157.259093 41.60384 157.259093-33.417813 842.949973-33.417813 842.949973 41.60384 654.226773 41.60384 722.899627 183.91936 783.67744 183.91936 783.67744 495.853653zM684.885333 419.108267 684.885333 258.012587 312.576 258.012587 312.576 419.108267zM570.770773 41.60384 426.653013 41.60384 359.621973 183.91936 639.443627 183.91936z" transform="translate(0, 812) scale(1, -1)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
|
@ -0,0 +1,589 @@
|
|||
/**
|
||||
* x is a value between 0 and 1, indicating where in the animation you are.
|
||||
*/
|
||||
var duScrollDefaultEasing = function (x) {
|
||||
'use strict';
|
||||
|
||||
if(x < 0.5) {
|
||||
return Math.pow(x*2, 2)/2;
|
||||
}
|
||||
return 1-Math.pow((1-x)*2, 2)/2;
|
||||
};
|
||||
|
||||
angular.module('duScroll', [
|
||||
'duScroll.scrollspy',
|
||||
'duScroll.smoothScroll',
|
||||
'duScroll.scrollContainer',
|
||||
'duScroll.spyContext',
|
||||
'duScroll.scrollHelpers'
|
||||
])
|
||||
//Default animation duration for smoothScroll directive
|
||||
.value('duScrollDuration', 350)
|
||||
//Scrollspy debounce interval, set to 0 to disable
|
||||
.value('duScrollSpyWait', 100)
|
||||
//Wether or not multiple scrollspies can be active at once
|
||||
.value('duScrollGreedy', false)
|
||||
//Default offset for smoothScroll directive
|
||||
.value('duScrollOffset', 0)
|
||||
//Default easing function for scroll animation
|
||||
.value('duScrollEasing', duScrollDefaultEasing);
|
||||
|
||||
|
||||
angular.module('duScroll.scrollHelpers', ['duScroll.requestAnimation'])
|
||||
.run(["$window", "$q", "cancelAnimation", "requestAnimation", "duScrollEasing", "duScrollDuration", "duScrollOffset", function($window, $q, cancelAnimation, requestAnimation, duScrollEasing, duScrollDuration, duScrollOffset) {
|
||||
'use strict';
|
||||
|
||||
var proto = angular.element.prototype;
|
||||
|
||||
var isDocument = function(el) {
|
||||
return (typeof HTMLDocument !== 'undefined' && el instanceof HTMLDocument) || (el.nodeType && el.nodeType === el.DOCUMENT_NODE);
|
||||
};
|
||||
|
||||
var isElement = function(el) {
|
||||
return (typeof HTMLElement !== 'undefined' && el instanceof HTMLElement) || (el.nodeType && el.nodeType === el.ELEMENT_NODE);
|
||||
};
|
||||
|
||||
var unwrap = function(el) {
|
||||
return isElement(el) || isDocument(el) ? el : el[0];
|
||||
};
|
||||
|
||||
proto.scrollTo = function(left, top, duration, easing) {
|
||||
var aliasFn;
|
||||
if(angular.isElement(left)) {
|
||||
aliasFn = this.scrollToElement;
|
||||
} else if(duration) {
|
||||
aliasFn = this.scrollToAnimated;
|
||||
}
|
||||
if(aliasFn) {
|
||||
return aliasFn.apply(this, arguments);
|
||||
}
|
||||
var el = unwrap(this);
|
||||
if(isDocument(el)) {
|
||||
return $window.scrollTo(left, top);
|
||||
}
|
||||
el.scrollLeft = left;
|
||||
el.scrollTop = top;
|
||||
};
|
||||
|
||||
var scrollAnimation, deferred;
|
||||
proto.scrollToAnimated = function(left, top, duration, easing) {
|
||||
if(duration && !easing) {
|
||||
easing = duScrollEasing;
|
||||
}
|
||||
var startLeft = this.scrollLeft(),
|
||||
startTop = this.scrollTop(),
|
||||
deltaLeft = Math.round(left - startLeft),
|
||||
deltaTop = Math.round(top - startTop);
|
||||
|
||||
var startTime = null;
|
||||
var el = this;
|
||||
|
||||
var cancelOnEvents = 'scroll mousedown mousewheel touchmove keydown';
|
||||
var cancelScrollAnimation = function($event) {
|
||||
if (!$event || $event.which > 0) {
|
||||
el.unbind(cancelOnEvents, cancelScrollAnimation);
|
||||
cancelAnimation(scrollAnimation);
|
||||
deferred.reject();
|
||||
scrollAnimation = null;
|
||||
}
|
||||
};
|
||||
|
||||
if(scrollAnimation) {
|
||||
cancelScrollAnimation();
|
||||
}
|
||||
deferred = $q.defer();
|
||||
|
||||
if(!deltaLeft && !deltaTop) {
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
var animationStep = function(timestamp) {
|
||||
if (startTime === null) {
|
||||
startTime = timestamp;
|
||||
}
|
||||
|
||||
var progress = timestamp - startTime;
|
||||
var percent = (progress >= duration ? 1 : easing(progress/duration));
|
||||
|
||||
el.scrollTo(
|
||||
startLeft + Math.ceil(deltaLeft * percent),
|
||||
startTop + Math.ceil(deltaTop * percent)
|
||||
);
|
||||
if(percent < 1) {
|
||||
scrollAnimation = requestAnimation(animationStep);
|
||||
} else {
|
||||
el.unbind(cancelOnEvents, cancelScrollAnimation);
|
||||
scrollAnimation = null;
|
||||
deferred.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
//Fix random mobile safari bug when scrolling to top by hitting status bar
|
||||
el.scrollTo(startLeft, startTop);
|
||||
|
||||
el.bind(cancelOnEvents, cancelScrollAnimation);
|
||||
|
||||
scrollAnimation = requestAnimation(animationStep);
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
proto.scrollToElement = function(target, offset, duration, easing) {
|
||||
var el = unwrap(this);
|
||||
if(!angular.isNumber(offset) || isNaN(offset)) {
|
||||
offset = duScrollOffset;
|
||||
}
|
||||
var top = this.scrollTop() + unwrap(target).getBoundingClientRect().top - offset;
|
||||
if(isElement(el)) {
|
||||
top -= el.getBoundingClientRect().top;
|
||||
}
|
||||
return this.scrollTo(0, top, duration, easing);
|
||||
};
|
||||
|
||||
var overloaders = {
|
||||
scrollLeft: function(value, duration, easing) {
|
||||
if(angular.isNumber(value)) {
|
||||
return this.scrollTo(value, this.scrollTop(), duration, easing);
|
||||
}
|
||||
var el = unwrap(this);
|
||||
if(isDocument(el)) {
|
||||
return $window.scrollX || document.documentElement.scrollLeft || document.body.scrollLeft;
|
||||
}
|
||||
return el.scrollLeft;
|
||||
},
|
||||
scrollTop: function(value, duration, easing) {
|
||||
if(angular.isNumber(value)) {
|
||||
return this.scrollTo(this.scrollTop(), value, duration, easing);
|
||||
}
|
||||
var el = unwrap(this);
|
||||
if(isDocument(el)) {
|
||||
return $window.scrollY || document.documentElement.scrollTop || document.body.scrollTop;
|
||||
}
|
||||
return el.scrollTop;
|
||||
}
|
||||
};
|
||||
|
||||
proto.scrollToElementAnimated = function(target, offset, duration, easing) {
|
||||
return this.scrollToElement(target, offset, duration || duScrollDuration, easing);
|
||||
};
|
||||
|
||||
proto.scrollTopAnimated = function(top, duration, easing) {
|
||||
return this.scrollTop(top, duration || duScrollDuration, easing);
|
||||
};
|
||||
|
||||
proto.scrollLeftAnimated = function(left, duration, easing) {
|
||||
return this.scrollLeft(left, duration || duScrollDuration, easing);
|
||||
};
|
||||
|
||||
//Add duration and easing functionality to existing jQuery getter/setters
|
||||
var overloadScrollPos = function(superFn, overloadFn) {
|
||||
return function(value, duration, easing) {
|
||||
if(duration) {
|
||||
return overloadFn.apply(this, arguments);
|
||||
}
|
||||
return superFn.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
for(var methodName in overloaders) {
|
||||
proto[methodName] = (proto[methodName] ? overloadScrollPos(proto[methodName], overloaders[methodName]) : overloaders[methodName]);
|
||||
}
|
||||
}]);
|
||||
|
||||
|
||||
//Adapted from https://gist.github.com/paulirish/1579671
|
||||
angular.module('duScroll.polyfill', [])
|
||||
.factory('polyfill', ["$window", function($window) {
|
||||
'use strict';
|
||||
|
||||
var vendors = ['webkit', 'moz', 'o', 'ms'];
|
||||
|
||||
return function(fnName, fallback) {
|
||||
if($window[fnName]) {
|
||||
return $window[fnName];
|
||||
}
|
||||
var suffix = fnName.substr(0, 1).toUpperCase() + fnName.substr(1);
|
||||
for(var key, i = 0; i < vendors.length; i++) {
|
||||
key = vendors[i]+suffix;
|
||||
if($window[key]) {
|
||||
return $window[key];
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
}]);
|
||||
|
||||
angular.module('duScroll.requestAnimation', ['duScroll.polyfill'])
|
||||
.factory('requestAnimation', ["polyfill", "$timeout", function(polyfill, $timeout) {
|
||||
'use strict';
|
||||
|
||||
var lastTime = 0;
|
||||
var fallback = function(callback, element) {
|
||||
var currTime = new Date().getTime();
|
||||
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
||||
var id = $timeout(function() { callback(currTime + timeToCall); },
|
||||
timeToCall);
|
||||
lastTime = currTime + timeToCall;
|
||||
return id;
|
||||
};
|
||||
|
||||
return polyfill('requestAnimationFrame', fallback);
|
||||
}])
|
||||
.factory('cancelAnimation', ["polyfill", "$timeout", function(polyfill, $timeout) {
|
||||
'use strict';
|
||||
|
||||
var fallback = function(promise) {
|
||||
$timeout.cancel(promise);
|
||||
};
|
||||
|
||||
return polyfill('cancelAnimationFrame', fallback);
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI'])
|
||||
.factory('spyAPI', ["$rootScope", "$timeout", "scrollContainerAPI", "duScrollGreedy", "duScrollSpyWait", function($rootScope, $timeout, scrollContainerAPI, duScrollGreedy, duScrollSpyWait) {
|
||||
'use strict';
|
||||
|
||||
var createScrollHandler = function(context) {
|
||||
var timer = false, queued = false;
|
||||
var handler = function() {
|
||||
queued = false;
|
||||
var container = context.container,
|
||||
containerEl = container[0],
|
||||
containerOffset = 0;
|
||||
|
||||
if (typeof HTMLElement !== 'undefined' && containerEl instanceof HTMLElement || containerEl.nodeType && containerEl.nodeType === containerEl.ELEMENT_NODE) {
|
||||
containerOffset = containerEl.getBoundingClientRect().top;
|
||||
}
|
||||
|
||||
var i, currentlyActive, toBeActive, spies, spy, pos;
|
||||
spies = context.spies;
|
||||
currentlyActive = context.currentlyActive;
|
||||
toBeActive = undefined;
|
||||
|
||||
for(i = 0; i < spies.length; i++) {
|
||||
spy = spies[i];
|
||||
pos = spy.getTargetPosition();
|
||||
if (!pos) continue;
|
||||
|
||||
if(pos.top + spy.offset - containerOffset < 20 && (pos.top*-1 + containerOffset) < pos.height) {
|
||||
if(!toBeActive || toBeActive.top < pos.top) {
|
||||
toBeActive = {
|
||||
top: pos.top,
|
||||
spy: spy
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if(toBeActive) {
|
||||
toBeActive = toBeActive.spy;
|
||||
}
|
||||
if(currentlyActive === toBeActive || (duScrollGreedy && !toBeActive)) return;
|
||||
if(currentlyActive) {
|
||||
currentlyActive.$element.removeClass('active');
|
||||
$rootScope.$broadcast('duScrollspy:becameInactive', currentlyActive.$element);
|
||||
}
|
||||
if(toBeActive) {
|
||||
toBeActive.$element.addClass('active');
|
||||
$rootScope.$broadcast('duScrollspy:becameActive', toBeActive.$element);
|
||||
}
|
||||
context.currentlyActive = toBeActive;
|
||||
};
|
||||
|
||||
if(!duScrollSpyWait) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
//Debounce for potential performance savings
|
||||
return function() {
|
||||
if(!timer) {
|
||||
handler();
|
||||
timer = $timeout(function() {
|
||||
timer = false;
|
||||
if(queued) {
|
||||
handler();
|
||||
}
|
||||
}, duScrollSpyWait, false);
|
||||
} else {
|
||||
queued = true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var contexts = {};
|
||||
|
||||
var createContext = function($scope) {
|
||||
var id = $scope.$id;
|
||||
var context = {
|
||||
spies: []
|
||||
};
|
||||
|
||||
context.handler = createScrollHandler(context);
|
||||
contexts[id] = context;
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
destroyContext($scope);
|
||||
});
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
var destroyContext = function($scope) {
|
||||
var id = $scope.$id;
|
||||
var context = contexts[id], container = context.container;
|
||||
if(container) {
|
||||
container.off('scroll', context.handler);
|
||||
}
|
||||
delete contexts[id];
|
||||
};
|
||||
|
||||
var defaultContextId = createContext($rootScope);
|
||||
|
||||
var getContextForScope = function(scope) {
|
||||
if(contexts[scope.$id]) {
|
||||
return contexts[scope.$id];
|
||||
}
|
||||
if(scope.$parent) {
|
||||
return getContextForScope(scope.$parent);
|
||||
}
|
||||
return contexts[defaultContextId];
|
||||
};
|
||||
|
||||
var getContextForSpy = function(spy) {
|
||||
var context, contextId, scope = spy.$element.scope();
|
||||
if(scope) {
|
||||
return getContextForScope(scope);
|
||||
}
|
||||
//No scope, most likely destroyed
|
||||
for(contextId in contexts) {
|
||||
context = contexts[contextId];
|
||||
if(context.spies.indexOf(spy) !== -1) {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var isElementInDocument = function(element) {
|
||||
while (element.parentNode) {
|
||||
element = element.parentNode;
|
||||
if (element === document) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var addSpy = function(spy) {
|
||||
var context = getContextForSpy(spy);
|
||||
if (!context) return;
|
||||
context.spies.push(spy);
|
||||
if (!context.container || !isElementInDocument(context.container)) {
|
||||
if(context.container) {
|
||||
context.container.off('scroll', context.handler);
|
||||
}
|
||||
context.container = scrollContainerAPI.getContainer(spy.$element.scope());
|
||||
context.container.on('scroll', context.handler).triggerHandler('scroll');
|
||||
}
|
||||
};
|
||||
|
||||
var removeSpy = function(spy) {
|
||||
var context = getContextForSpy(spy);
|
||||
if(spy === context.currentlyActive) {
|
||||
context.currentlyActive = null;
|
||||
}
|
||||
var i = context.spies.indexOf(spy);
|
||||
if(i !== -1) {
|
||||
context.spies.splice(i, 1);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
addSpy: addSpy,
|
||||
removeSpy: removeSpy,
|
||||
createContext: createContext,
|
||||
destroyContext: destroyContext,
|
||||
getContextForScope: getContextForScope
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.scrollContainerAPI', [])
|
||||
.factory('scrollContainerAPI', ["$document", function($document) {
|
||||
'use strict';
|
||||
|
||||
var containers = {};
|
||||
|
||||
var setContainer = function(scope, element) {
|
||||
var id = scope.$id;
|
||||
containers[id] = element;
|
||||
return id;
|
||||
};
|
||||
|
||||
var getContainerId = function(scope) {
|
||||
if(containers[scope.$id]) {
|
||||
return scope.$id;
|
||||
}
|
||||
if(scope.$parent) {
|
||||
return getContainerId(scope.$parent);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
var getContainer = function(scope) {
|
||||
var id = getContainerId(scope);
|
||||
return id ? containers[id] : $document;
|
||||
};
|
||||
|
||||
var removeContainer = function(scope) {
|
||||
var id = getContainerId(scope);
|
||||
if(id) {
|
||||
delete containers[id];
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
getContainerId: getContainerId,
|
||||
getContainer: getContainer,
|
||||
setContainer: setContainer,
|
||||
removeContainer: removeContainer
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.smoothScroll', ['duScroll.scrollHelpers', 'duScroll.scrollContainerAPI'])
|
||||
.directive('duSmoothScroll', ["duScrollDuration", "duScrollOffset", "scrollContainerAPI", function(duScrollDuration, duScrollOffset, scrollContainerAPI) {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
link : function($scope, $element, $attr) {
|
||||
$element.on('click', function(e) {
|
||||
if(!$attr.href || $attr.href.indexOf('#') === -1) return;
|
||||
|
||||
var target = document.getElementById($attr.href.replace(/.*(?=#[^\s]+$)/, '').substring(1));
|
||||
if(!target || !target.getBoundingClientRect) return;
|
||||
|
||||
if (e.stopPropagation) e.stopPropagation();
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
|
||||
var offset = $attr.offset ? parseInt($attr.offset, 10) : duScrollOffset;
|
||||
var duration = $attr.duration ? parseInt($attr.duration, 10) : duScrollDuration;
|
||||
var container = scrollContainerAPI.getContainer($scope);
|
||||
|
||||
container.scrollToElement(
|
||||
angular.element(target),
|
||||
isNaN(offset) ? 0 : offset,
|
||||
isNaN(duration) ? 0 : duration
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.spyContext', ['duScroll.spyAPI'])
|
||||
.directive('duSpyContext', ["spyAPI", function(spyAPI) {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: true,
|
||||
compile: function compile(tElement, tAttrs, transclude) {
|
||||
return {
|
||||
pre: function preLink($scope, iElement, iAttrs, controller) {
|
||||
spyAPI.createContext($scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.scrollContainer', ['duScroll.scrollContainerAPI'])
|
||||
.directive('duScrollContainer', ["scrollContainerAPI", function(scrollContainerAPI){
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: true,
|
||||
compile: function compile(tElement, tAttrs, transclude) {
|
||||
return {
|
||||
pre: function preLink($scope, iElement, iAttrs, controller) {
|
||||
iAttrs.$observe('duScrollContainer', function(element) {
|
||||
if(angular.isString(element)) {
|
||||
element = document.getElementById(element);
|
||||
}
|
||||
|
||||
element = (angular.isElement(element) ? angular.element(element) : iElement);
|
||||
scrollContainerAPI.setContainer($scope, element);
|
||||
$scope.$on('$destroy', function() {
|
||||
scrollContainerAPI.removeContainer($scope);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
angular.module('duScroll.scrollspy', ['duScroll.spyAPI'])
|
||||
.directive('duScrollspy', ["spyAPI", "duScrollOffset", "$timeout", "$rootScope", function(spyAPI, duScrollOffset, $timeout, $rootScope) {
|
||||
'use strict';
|
||||
|
||||
var Spy = function(targetElementOrId, $element, offset) {
|
||||
if(angular.isElement(targetElementOrId)) {
|
||||
this.target = targetElementOrId;
|
||||
} else if(angular.isString(targetElementOrId)) {
|
||||
this.targetId = targetElementOrId;
|
||||
}
|
||||
this.$element = $element;
|
||||
this.offset = offset;
|
||||
};
|
||||
|
||||
Spy.prototype.getTargetElement = function() {
|
||||
if (!this.target && this.targetId) {
|
||||
this.target = document.getElementById(this.targetId);
|
||||
}
|
||||
return this.target;
|
||||
};
|
||||
|
||||
Spy.prototype.getTargetPosition = function() {
|
||||
var target = this.getTargetElement();
|
||||
if(target) {
|
||||
return target.getBoundingClientRect();
|
||||
}
|
||||
};
|
||||
|
||||
Spy.prototype.flushTargetCache = function() {
|
||||
if(this.targetId) {
|
||||
this.target = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
link: function ($scope, $element, $attr) {
|
||||
var href = $attr.ngHref || $attr.href;
|
||||
var targetId;
|
||||
|
||||
if (href && href.indexOf('#') !== -1) {
|
||||
targetId = href.replace(/.*(?=#[^\s]+$)/, '').substring(1);
|
||||
} else if($attr.duScrollspy) {
|
||||
targetId = $attr.duScrollspy;
|
||||
}
|
||||
if(!targetId) return;
|
||||
|
||||
// Run this in the next execution loop so that the scroll context has a chance
|
||||
// to initialize
|
||||
$timeout(function() {
|
||||
var spy = new Spy(targetId, $element, -($attr.offset ? parseInt($attr.offset, 10) : duScrollOffset));
|
||||
spyAPI.addSpy(spy);
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
spyAPI.removeSpy(spy);
|
||||
});
|
||||
$scope.$on('$locationChangeSuccess', spy.flushTargetCache.bind(spy));
|
||||
$rootScope.$on('$stateChangeSuccess', spy.flushTargetCache.bind(spy));
|
||||
}, 0, false);
|
||||
}
|
||||
};
|
||||
}]);
|
|
@ -2,10 +2,12 @@
|
|||
#= require angular-cookies
|
||||
#= require angular-resource
|
||||
#= require angular-sanitize
|
||||
#= require angular-scroll
|
||||
#= require_self
|
||||
#= require_tree ./angularjs
|
||||
|
||||
@app = angular.module('app', ['ngCookies', 'ngSanitize'])
|
||||
@app = angular.module('app', ['ngCookies', 'ngSanitize', 'duScroll'])
|
||||
@app.value('duScrollOffset', 30)
|
||||
|
||||
@app.config(["$httpProvider", (provider) ->
|
||||
provider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
@app.controller 'AboutScrollController', [ '$scope', '$document', ($scope, $document)->
|
||||
|
||||
about_id = angular.element(document.getElementById('about'))
|
||||
|
||||
$document.on 'scroll', ()=>
|
||||
$scope.$apply()
|
||||
|
||||
$scope.is_top = ()->
|
||||
$document.scrollTop() <= 0
|
||||
|
||||
|
||||
$scope.to_about = ()->
|
||||
$document.scrollToElementAnimated(about_id)
|
||||
]
|
|
@ -3,6 +3,7 @@
|
|||
//= require foundation/foundation
|
||||
//= require foundation/foundation.topbar
|
||||
//= require foundation/foundation.offcanvas
|
||||
//= require foundation/foundation.magellan
|
||||
//= require angularjs
|
||||
//= require 'jquery.html5-fileupload'
|
||||
//= require_tree .
|
||||
|
|
|
@ -20,3 +20,216 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.about-page {
|
||||
@media only screen and (min-width: 40.063em) {
|
||||
.top-bar-wrapper {
|
||||
background: 0 0;
|
||||
transition: background .5s ease-in-out,padding .5s ease-in-out;
|
||||
&.active {
|
||||
background: #000;
|
||||
border-bottom: 1px solid #666;
|
||||
|
||||
.top-bar {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
background: 0 0;
|
||||
margin: 1.5rem 0;
|
||||
.name h1 {
|
||||
font-size: 1.325rem;
|
||||
}
|
||||
}
|
||||
|
||||
.top-bar-section ul{
|
||||
li, li a {
|
||||
background: 0 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
li a:hover {
|
||||
background: #666;
|
||||
}
|
||||
|
||||
li a.active {
|
||||
background: #4C4C4C;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.275rem;
|
||||
}
|
||||
|
||||
.intro {
|
||||
//background-color: #7A7A7A;
|
||||
background: image-url('intro-bg.jpg') no-repeat bottom center scroll;
|
||||
background-size: cover;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: table;
|
||||
|
||||
.intro-heading {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
|
||||
.heading, .sub-heading {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sub-heading {
|
||||
margin-top: 2rem;
|
||||
p {
|
||||
line-height: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.circle {
|
||||
color: #fff;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
font-size: 3rem;
|
||||
border: 2px solid #eee;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
padding: 0.5rem 0;
|
||||
margin-top: 2rem;
|
||||
&:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#about {
|
||||
background-color: #000;
|
||||
color: #eee;
|
||||
padding: 10rem 0;
|
||||
h1, h2 {
|
||||
color: #eee;
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
#work {
|
||||
background: image-url('download-bg.jpg') no-repeat bottom center scroll;
|
||||
background-size: cover;
|
||||
padding: 10rem 0;
|
||||
h1, h2 {
|
||||
color: #eee;
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #eee;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.works {
|
||||
color: #eee;
|
||||
background-color: rgba(63, 63, 63, 0.6);
|
||||
list-style: none;
|
||||
padding: 2rem 1rem;
|
||||
li {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-right: 0.5rem;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
&:after {
|
||||
margin-left: 0.5rem;
|
||||
font-weight: 400;
|
||||
content: '--';
|
||||
}
|
||||
}
|
||||
|
||||
.brief {
|
||||
margin-right: 1rem;
|
||||
color: #dfdfdf;
|
||||
}
|
||||
|
||||
.link {
|
||||
margin-right: 1rem;
|
||||
color: #dfdfdf;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #aaa;
|
||||
font-size: 95%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#contact {
|
||||
background: #000;
|
||||
background-size: cover;
|
||||
padding: 10rem 0;
|
||||
padding-bottom: 20rem;
|
||||
h1, h2 {
|
||||
color: #eee;
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #eee;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mail_to {
|
||||
margin-bottom: 3rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.contact-ul {
|
||||
text-align: center;
|
||||
li {
|
||||
display: inline-block;
|
||||
}
|
||||
li a {
|
||||
display: inline-block;
|
||||
padding: 0.5rem 2rem;
|
||||
border: 1px solid #219AB3;
|
||||
border-radius: 0.5rem;
|
||||
color: #219AB3;
|
||||
margin: 0 1rem;
|
||||
transition: all .3s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
outline: 0;
|
||||
color: #000;
|
||||
background-color: #219ab3;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 0.5rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
i.douban {
|
||||
font-style: normal;
|
||||
font-size: 95%;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
border: none;
|
||||
padding: 1.5rem;
|
||||
background-color: #353535;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1190,3 +1190,4 @@ $base-font-size: 100%;
|
|||
|
||||
@import 'foundation';
|
||||
@import 'foundation-icons';
|
||||
@import 'font-awesome';
|
||||
|
|
|
@ -1,5 +1,97 @@
|
|||
- content_for(:title) do
|
||||
| 关于我
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns.self-introduce
|
||||
= render 'common/aboutme'
|
||||
- content_for(:main) do
|
||||
/! 导航
|
||||
.about-page ng-app='app' ng-controller='AboutScrollController'
|
||||
.top-bar-wrapper.contain-to-grid.fixed ng-class="{ active: ! is_top() }"
|
||||
.row
|
||||
.small-12.columns
|
||||
nav.top-bar data-topbar='' role='navigation'
|
||||
ul.title-area
|
||||
li.name
|
||||
h1
|
||||
a href='/' WinDy's Blog
|
||||
section.top-bar-section
|
||||
ul.right
|
||||
li
|
||||
a href='#about' du-smooth-scroll='' du-scrollspy='' 关于
|
||||
li
|
||||
a href='#work' du-smooth-scroll='' du-scrollspy='' 作品
|
||||
li
|
||||
a href='#contact' du-smooth-scroll='' du-scrollspy='' 联系
|
||||
#intro
|
||||
header.intro
|
||||
.intro-heading
|
||||
.row
|
||||
.small-12.columns
|
||||
h1.heading 李亚飞
|
||||
.sub-heading
|
||||
p
|
||||
| 懒, 是人类进步的动力
|
||||
br
|
||||
| 所谓技术, 就是让你的生活越来越懒
|
||||
a.circle href='#about' du-smooth-scroll=''
|
||||
i.fa.fa-angle-double-down
|
||||
section#about
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns
|
||||
h1.title 关于我
|
||||
p 我是李亚飞, 一个在中国深圳的网页开发工程师( Web Developer ).
|
||||
p
|
||||
| 曾经在
|
||||
a href='http://sangfor.com' 深信服
|
||||
| 工作大约 5 年. 在那里, 从一个菜鸟成长为一个资深工程师, 还有幸带领一个很酷的团队帮助公司进行自动化测试方向的研究与推进. 掌握的技术大约有: Linux, Ruby, Ruby on Rails, Testing Automation
|
||||
p
|
||||
| 在今年 3 月份, 为了那份创业梦, 出山开发了 cywin.cn, 这是一个股权众筹平台, 帮助创业团队更好的融资. 借此, 也将自己的 Ruby on Rails 技术全部注入在内
|
||||
p
|
||||
| 现在, 想先休息一段时间, 接下来做一个自由职业者, 接手一些关于 Web 开发的外包工作
|
||||
section#work
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns
|
||||
h1.title 作品
|
||||
ul.works
|
||||
li
|
||||
span.name ATM/ATT
|
||||
span.brief 测试业界领先的关键字驱动的自动化测试平台
|
||||
span.link 公司所属
|
||||
span.time #{time_ago_in_words(DateTime.new(2013))} 前
|
||||
li
|
||||
span.name WBlog
|
||||
span.brief Rails 社区正缺了一个独立博客建站系统
|
||||
span.link
|
||||
a href='https://github.com/windy/wblog' target='_blank' https://github.com/windy/wblog
|
||||
span.time #{time_ago_in_words(DateTime.new(2014,3,1))} 前
|
||||
li
|
||||
span.name Cywin
|
||||
span.brief 一个股权众筹的商业平台
|
||||
span.link
|
||||
a href='http://cywin.cn' target='_blank' http://cywin.cn
|
||||
span.time #{time_ago_in_words(1.month.ago)} 前
|
||||
section#contact
|
||||
.row
|
||||
.small-12.large-9.large-centered.columns
|
||||
h1.title 联系我
|
||||
p
|
||||
| 我目前处于自由职业状态, 如果你想给你的公司或个人制作一个精美简洁的网站, 或许我可以帮到你
|
||||
p
|
||||
| 不要紧张, 请随时联系我
|
||||
p.mail_to
|
||||
= mail_to 'lyfi2003@gmail.com'
|
||||
ul.contact-ul
|
||||
li
|
||||
a href='https://github.com/windy' target='_blank'
|
||||
i.fa.fa-github
|
||||
| Github
|
||||
li
|
||||
a href='http://www.douban.com/people/41759170/' target='_blank'
|
||||
i.douban
|
||||
| 豆
|
||||
| Douban
|
||||
li
|
||||
a href='http://www.linkedin.com/pub/yafei-lee/77/3b/505' target='_blank'
|
||||
i.fa.fa-linkedin-square
|
||||
| LinkedIn
|
||||
.footer
|
||||
.row
|
||||
.small-12.columns
|
||||
div Copyright © yafeilee.me
|
||||
|
|
|
@ -13,53 +13,56 @@ html
|
|||
/ 增加 favico 图标
|
||||
link href="/favicon.png" type='image/png' rel="icon"
|
||||
body
|
||||
.off-canvas-wrap data-offcanvas=''
|
||||
.inner-wrap
|
||||
nav.tab-bar
|
||||
section.left-small
|
||||
a.left-off-canvas-toggle.menu-icon href="#"
|
||||
span
|
||||
h1.title.middle-text
|
||||
= link_to ENV['SITE_NAME'], root_path
|
||||
aslide.left-off-canvas-menu
|
||||
ul.off-canvas-list
|
||||
li
|
||||
label Menu
|
||||
li
|
||||
= link_to root_path do
|
||||
i.fi-home
|
||||
| 首页
|
||||
li
|
||||
= link_to '/tech' do
|
||||
i.fi-social-evernote
|
||||
| 技术
|
||||
li
|
||||
= link_to '/life' do
|
||||
i.fi-torsos-male-female
|
||||
| 生活
|
||||
li
|
||||
= link_to '/creator' do
|
||||
i.fi-lightbulb
|
||||
| 创业
|
||||
li
|
||||
label Archive
|
||||
li
|
||||
= link_to archives_path do
|
||||
i.fi-align-justify
|
||||
| 时间线
|
||||
li
|
||||
label About
|
||||
= link_to about_path do
|
||||
i.fi-torso
|
||||
| 关于我
|
||||
section.main-section ng-app="app"
|
||||
- flash.each do |name, msg|
|
||||
- if msg.is_a?(String)
|
||||
div class=("alert-box #{name == :notice ? "success" : "alert"}") data-alert=""
|
||||
= content_tag :div, msg
|
||||
a.close href="#" ×
|
||||
= yield
|
||||
= render "layouts/footer"
|
||||
a.exit-off-canvas
|
||||
- if content_for?(:main)
|
||||
= yield(:main)
|
||||
- else
|
||||
.off-canvas-wrap data-offcanvas=''
|
||||
.inner-wrap
|
||||
nav.tab-bar
|
||||
section.left-small
|
||||
a.left-off-canvas-toggle.menu-icon href="#"
|
||||
span
|
||||
h1.title.middle-text
|
||||
= link_to ENV['SITE_NAME'], root_path
|
||||
aslide.left-off-canvas-menu
|
||||
ul.off-canvas-list
|
||||
li
|
||||
label Menu
|
||||
li
|
||||
= link_to root_path do
|
||||
i.fi-home
|
||||
| 首页
|
||||
li
|
||||
= link_to '/tech' do
|
||||
i.fi-social-evernote
|
||||
| 技术
|
||||
li
|
||||
= link_to '/life' do
|
||||
i.fi-torsos-male-female
|
||||
| 生活
|
||||
li
|
||||
= link_to '/creator' do
|
||||
i.fi-lightbulb
|
||||
| 创业
|
||||
li
|
||||
label Archive
|
||||
li
|
||||
= link_to archives_path do
|
||||
i.fi-align-justify
|
||||
| 时间线
|
||||
li
|
||||
label About
|
||||
= link_to about_path do
|
||||
i.fi-torso
|
||||
| 关于我
|
||||
section.main-section ng-app="app"
|
||||
- flash.each do |name, msg|
|
||||
- if msg.is_a?(String)
|
||||
div class=("alert-box #{name == :notice ? "success" : "alert"}") data-alert=""
|
||||
= content_tag :div, msg
|
||||
a.close href="#" ×
|
||||
= yield
|
||||
= render "layouts/footer"
|
||||
a.exit-off-canvas
|
||||
|
||||
= render 'layouts/google_analytics'
|
||||
|
|
Loading…
Reference in New Issue