Browse Source

Moved to easier page header show/hide implementation of long pages.

Martin Edenhofer 10 years ago
parent
commit
6491d18f34

+ 40 - 21
app/assets/javascripts/app/controllers/ticket_zoom.js.coffee

@@ -1,9 +1,11 @@
 class App.TicketZoom extends App.Controller
   elements:
-    '.main': 'main'
+    '.main':             'main'
+    '.ticketZoom':       'ticketZoom'
+    '.scrollPageHeader': 'scrollPageHeader'
 
   events:
-    'click .js-submit': 'submit'
+    'click .js-submit':   'submit'
     'click .js-bookmark': 'bookmark'
 
   constructor: (params) ->
@@ -59,24 +61,21 @@ class App.TicketZoom extends App.Controller
   show: =>
     App.OnlineNotification.seen( 'Ticket', @ticket_id )
     @navupdate '#'
-    if @scrollHeader
-      @scrollHeader.continue()
+    @positionPageHeaderStart()
 
   hide: =>
-    if @scrollHeader
-      @scrollHeader.pause()
+    @positionPageHeaderStop()
 
   changed: =>
     formCurrent = @formParam( @el.find('.edit') )
-    ticket = App.Ticket.find(@ticket_id).attributes()
-    modelDiff  = @getDiff( ticket, formCurrent )
+    ticket      = App.Ticket.find(@ticket_id).attributes()
+    modelDiff   = @getDiff( ticket, formCurrent )
     return false if !modelDiff || _.isEmpty( modelDiff )
     return true
 
   release: =>
-    # nothing
     @autosaveStop()
-    @scrollHeader.destroy() if @scrollHeader
+    @positionPageHeaderStop()
 
   fetch: (ticket_id, force) ->
 
@@ -149,6 +148,36 @@ class App.TicketZoom extends App.Controller
     # render page
     @render(force)
 
+  positionPageHeaderStart: =>
+
+    # scroll is also fired on window resize, if element scroll is changed
+    @main.bind(
+      'scroll'
+      @positionPageHeaderUpdate
+    )
+
+  positionPageHeaderStop: =>
+    @main.unbind('scroll', @positionPageHeaderUpdate)
+
+  positionPageHeaderUpdate: =>
+    pageHeader       = @scrollPageHeader.height()
+    mainScrollHeigth = @main.prop('scrollHeight')
+    mainHeigth       = @main.height()
+
+    # if page header is possible to use, show page header
+    top = 0
+    if mainScrollHeigth > mainHeigth + pageHeader
+      offset = @ticketZoom.offset()
+      if offset.top >= 0
+        top = offset.top
+
+    # if page header is not possible to use - mainScrollHeigth to low - hide page header
+    else
+      top = pageHeader
+
+    #console.log('TOP', top, @ticket.id, new Date)
+    @scrollPageHeader.css('transform', "translateY(-#{top}px)")
+
   render: (force) =>
 
     # update taskbar with new meta data
@@ -404,21 +433,11 @@ class App.TicketZoom extends App.Controller
 
     @scrollToBottom()
 
-    @bindScrollPageHeader()
+    @positionPageHeaderStart()
 
   scrollToBottom: =>
     @main.scrollTop( @main.prop('scrollHeight') )
 
-  bindScrollPageHeader: ->
-    pageHeader = @$('.page-header')
-    scrollBody = @main.prop('scrollHeight') - @main.height()
-
-    if scrollBody > pageHeader.height()
-      # TODO: recalculate the distance when adding a comment
-      @scrollHeader = skrollr.init
-        forceHeight: false
-        holder: @main.get(0)
-
   autosaveStop: =>
     @autosaveLast = {}
     @clearInterval( 'autosave' )

+ 0 - 1789
app/assets/javascripts/app/lib/animations/skrollr.js

@@ -1,1789 +0,0 @@
-/*!
- * skrollr core
- *
- * Alexander Prinzhorn - https://github.com/Prinzhorn/skrollr
- *
- * Modified: Felix Niklas – add 'holder' option for scrolling inside a div
- *
- * Free to use under terms of MIT license
- */
-window.id = 0;
-(function(window, document, undefined) {
-	'use strict';
-
-	/*
-	 * Global api.
-	 */
-	var skrollr = {
-		get: function() {
-			return _instance;
-		},
-		//Main entry point.
-		init: function(options) {
-			new Skrollr(options);
-		},
-		VERSION: '0.6.26'
-	};
-
-	//Minify optimization.
-	var hasProp = Object.prototype.hasOwnProperty;
-	var Math = window.Math;
-	var getStyle = window.getComputedStyle;
-
-	//They will be filled when skrollr gets initialized.
-	var documentElement;
-	var body;
-
-	var EVENT_TOUCHSTART = 'touchstart';
-	var EVENT_TOUCHMOVE = 'touchmove';
-	var EVENT_TOUCHCANCEL = 'touchcancel';
-	var EVENT_TOUCHEND = 'touchend';
-
-	var SKROLLABLE_CLASS = 'skrollable';
-	var SKROLLABLE_BEFORE_CLASS = SKROLLABLE_CLASS + '-before';
-	var SKROLLABLE_BETWEEN_CLASS = SKROLLABLE_CLASS + '-between';
-	var SKROLLABLE_AFTER_CLASS = SKROLLABLE_CLASS + '-after';
-
-	var SKROLLR_CLASS = 'skrollr';
-	var NO_SKROLLR_CLASS = 'no-' + SKROLLR_CLASS;
-	var SKROLLR_DESKTOP_CLASS = SKROLLR_CLASS + '-desktop';
-	var SKROLLR_MOBILE_CLASS = SKROLLR_CLASS + '-mobile';
-
-	var DEFAULT_EASING = 'linear';
-	var DEFAULT_DURATION = 1000;//ms
-	var DEFAULT_MOBILE_DECELERATION = 0.004;//pixel/ms²
-
-	var DEFAULT_SMOOTH_SCROLLING_DURATION = 200;//ms
-
-	var ANCHOR_START = 'start';
-	var ANCHOR_END = 'end';
-	var ANCHOR_CENTER = 'center';
-	var ANCHOR_BOTTOM = 'bottom';
-
-	//The property which will be added to the DOM element to hold the ID of the skrollable.
-	var SKROLLABLE_ID_DOM_PROPERTY = '___skrollable_id';
-
-	var rxTouchIgnoreTags = /^(?:input|textarea|button|select)$/i;
-
-	var rxTrim = /^\s+|\s+$/g;
-
-	//Find all data-attributes. data-[_constant]-[offset]-[anchor]-[anchor].
-	var rxKeyframeAttribute = /^data(?:-(_\w+))?(?:-?(-?\d*\.?\d+p?))?(?:-?(start|end|top|center|bottom))?(?:-?(top|center|bottom))?$/;
-
-	var rxPropValue = /\s*(@?[\w\-\[\]]+)\s*:\s*(.+?)\s*(?:;|$)/gi;
-
-	//Easing function names follow the property in square brackets.
-	var rxPropEasing = /^(@?[a-z\-]+)\[(\w+)\]$/;
-
-	var rxCamelCase = /-([a-z0-9_])/g;
-	var rxCamelCaseFn = function(str, letter) {
-		return letter.toUpperCase();
-	};
-
-	//Numeric values with optional sign.
-	var rxNumericValue = /[\-+]?[\d]*\.?[\d]+/g;
-
-	//Used to replace occurences of {?} with a number.
-	var rxInterpolateString = /\{\?\}/g;
-
-	//Finds rgb(a) colors, which don't use the percentage notation.
-	var rxRGBAIntegerColor = /rgba?\(\s*-?\d+\s*,\s*-?\d+\s*,\s*-?\d+/g;
-
-	//Finds all gradients.
-	var rxGradient = /[a-z\-]+-gradient/g;
-
-	//Vendor prefix. Will be set once skrollr gets initialized.
-	var theCSSPrefix = '';
-	var theDashedCSSPrefix = '';
-
-	//Will be called once (when skrollr gets initialized).
-	var detectCSSPrefix = function() {
-		//Only relevant prefixes. May be extended.
-		//Could be dangerous if there will ever be a CSS property which actually starts with "ms". Don't hope so.
-		var rxPrefixes = /^(?:O|Moz|webkit|ms)|(?:-(?:o|moz|webkit|ms)-)/;
-
-		//Detect prefix for current browser by finding the first property using a prefix.
-		if(!getStyle) {
-			return;
-		}
-
-		var style = getStyle(body, null);
-
-		for(var k in style) {
-			//We check the key and if the key is a number, we check the value as well, because safari's getComputedStyle returns some weird array-like thingy.
-			theCSSPrefix = (k.match(rxPrefixes) || (+k == k && style[k].match(rxPrefixes)));
-
-			if(theCSSPrefix) {
-				break;
-			}
-		}
-
-		//Did we even detect a prefix?
-		if(!theCSSPrefix) {
-			theCSSPrefix = theDashedCSSPrefix = '';
-
-			return;
-		}
-
-		theCSSPrefix = theCSSPrefix[0];
-
-		//We could have detected either a dashed prefix or this camelCaseish-inconsistent stuff.
-		if(theCSSPrefix.slice(0,1) === '-') {
-			theDashedCSSPrefix = theCSSPrefix;
-
-			//There's no logic behind these. Need a look up.
-			theCSSPrefix = ({
-				'-webkit-': 'webkit',
-				'-moz-': 'Moz',
-				'-ms-': 'ms',
-				'-o-': 'O'
-			})[theCSSPrefix];
-		} else {
-			theDashedCSSPrefix = '-' + theCSSPrefix.toLowerCase() + '-';
-		}
-	};
-
-	var polyfillRAF = function() {
-		var requestAnimFrame = window.requestAnimationFrame || window[theCSSPrefix.toLowerCase() + 'RequestAnimationFrame'];
-
-		var lastTime = _now();
-
-		if(_isMobile || !requestAnimFrame) {
-			requestAnimFrame = function(callback) {
-				//How long did it take to render?
-				var deltaTime = _now() - lastTime;
-				var delay = Math.max(0, 1000 / 60 - deltaTime);
-
-				return window.setTimeout(function() {
-					lastTime = _now();
-					callback();
-				}, delay);
-			};
-		}
-
-		return requestAnimFrame;
-	};
-
-	var polyfillCAF = function() {
-		var cancelAnimFrame = window.cancelAnimationFrame || window[theCSSPrefix.toLowerCase() + 'CancelAnimationFrame'];
-
-		if(_isMobile || !cancelAnimFrame) {
-			cancelAnimFrame = function(timeout) {
-				return window.clearTimeout(timeout);
-			};
-		}
-
-		return cancelAnimFrame;
-	};
-
-	//Built-in easing functions.
-	var easings = {
-		begin: function() {
-			return 0;
-		},
-		end: function() {
-			return 1;
-		},
-		linear: function(p) {
-			return p;
-		},
-		quadratic: function(p) {
-			return p * p;
-		},
-		cubic: function(p) {
-			return p * p * p;
-		},
-		swing: function(p) {
-			return (-Math.cos(p * Math.PI) / 2) + 0.5;
-		},
-		sqrt: function(p) {
-			return Math.sqrt(p);
-		},
-		outCubic: function(p) {
-			return (Math.pow((p - 1), 3) + 1);
-		},
-		//see https://www.desmos.com/calculator/tbr20s8vd2 for how I did this
-		bounce: function(p) {
-			var a;
-
-			if(p <= 0.5083) {
-				a = 3;
-			} else if(p <= 0.8489) {
-				a = 9;
-			} else if(p <= 0.96208) {
-				a = 27;
-			} else if(p <= 0.99981) {
-				a = 91;
-			} else {
-				return 1;
-			}
-
-			return 1 - Math.abs(3 * Math.cos(p * a * 1.028) / a);
-		}
-	};
-
-	/**
-	 * Constructor.
-	 */
-	function Skrollr(options) {
-		documentElement = options.holder || document.documentElement;
-		body = document.body;
-
-		detectCSSPrefix();
-
-		_instance = this;
-
-		this.id = window.id++;
-
-		options = options || {};
-
-		_constants = options.constants || {};
-
-		//We allow defining custom easings or overwrite existing.
-		if(options.easing) {
-			for(var e in options.easing) {
-				easings[e] = options.easing[e];
-			}
-		}
-
-		_edgeStrategy = options.edgeStrategy || 'set';
-
-		_listeners = {
-			//Function to be called right before rendering.
-			beforerender: options.beforerender,
-
-			//Function to be called right after finishing rendering.
-			render: options.render,
-
-			//Function to be called whenever an element with the `data-emit-events` attribute passes a keyframe.
-			keyframe: options.keyframe
-		};
-
-		//forceHeight is true by default
-		_forceHeight = options.forceHeight !== false;
-
-		if(_forceHeight) {
-			_scale = options.scale || 1;
-		}
-
-		_mobileDeceleration = options.mobileDeceleration || DEFAULT_MOBILE_DECELERATION;
-
-		_smoothScrollingEnabled = options.smoothScrolling !== false;
-		_smoothScrollingDuration = options.smoothScrollingDuration || DEFAULT_SMOOTH_SCROLLING_DURATION;
-
-		//Dummy object. Will be overwritten in the _render method when smooth scrolling is calculated.
-		_smoothScrolling = {
-			targetTop: _instance.getScrollTop()
-		};
-
-		//A custom check function may be passed.
-		_isMobile = ((options.mobileCheck || function() {
-			return (/Android|iPhone|iPad|iPod|BlackBerry/i).test(navigator.userAgent || navigator.vendor || window.opera);
-		})());
-
-		if(_isMobile) {
-			_skrollrBody = document.getElementById('skrollr-body');
-
-			//Detect 3d transform if there's a skrollr-body (only needed for #skrollr-body).
-			if(_skrollrBody) {
-				_detect3DTransforms();
-			}
-
-			_initMobile();
-			_updateClass(documentElement, [SKROLLR_CLASS, SKROLLR_MOBILE_CLASS], [NO_SKROLLR_CLASS]);
-		} else {
-			_updateClass(documentElement, [SKROLLR_CLASS, SKROLLR_DESKTOP_CLASS], [NO_SKROLLR_CLASS]);
-		}
-
-		//Triggers parsing of elements and a first reflow.
-		_instance.refresh();
-
-		_addEvent(window, 'resize orientationchange', function() {
-			var width = documentElement.clientWidth;
-			var height = documentElement.clientHeight;
-
-			//Only reflow if the size actually changed (#271).
-			if(height !== _lastViewportHeight || width !== _lastViewportWidth) {
-				_lastViewportHeight = height;
-				_lastViewportWidth = width;
-
-				_requestReflow = true;
-			}
-		});
-
-		//Let's go.
-		this.animloop();
-
-		return _instance;
-	}
-
-	Skrollr.prototype.animloop = function() {
-		var requestAnimFrame = polyfillRAF();
-		_render();
-		// console.log("rendering", this.id);
-		if(!this.paused)
-			_animFrame = requestAnimFrame(_instance.animloop.bind(this));
-	}
-
-	Skrollr.prototype.pause = function() {
-		cancelAnimFrame(_animFrame);
-		this.paused = true;
-	}
-
-	Skrollr.prototype.continue = function() {
-		this.paused = false;
-		this.animloop();
-	}
-
-	/**
-	 * (Re)parses some or all elements.
-	 */
-	Skrollr.prototype.refresh = function(elements) {
-		var elementIndex;
-		var elementsLength;
-		var ignoreID = false;
-
-		//Completely reparse anything without argument.
-		if(elements === undefined) {
-			//Ignore that some elements may already have a skrollable ID.
-			ignoreID = true;
-
-			_skrollables = [];
-			_skrollableIdCounter = 0;
-
-			elements = document.getElementsByTagName('*');
-		} else if(elements.length === undefined) {
-			//We also accept a single element as parameter.
-			elements = [elements];
-		}
-
-		elementIndex = 0;
-		elementsLength = elements.length;
-
-		for(; elementIndex < elementsLength; elementIndex++) {
-			var el = elements[elementIndex];
-			var anchorTarget = el;
-			var keyFrames = [];
-
-			//If this particular element should be smooth scrolled.
-			var smoothScrollThis = _smoothScrollingEnabled;
-
-			//The edge strategy for this particular element.
-			var edgeStrategy = _edgeStrategy;
-
-			//If this particular element should emit keyframe events.
-			var emitEvents = false;
-
-			//If we're reseting the counter, remove any old element ids that may be hanging around.
-			if(ignoreID && SKROLLABLE_ID_DOM_PROPERTY in el) {
-				delete el[SKROLLABLE_ID_DOM_PROPERTY];
-			}
-
-			if(!el.attributes) {
-				continue;
-			}
-
-			//Iterate over all attributes and search for key frame attributes.
-			var attributeIndex = 0;
-			var attributesLength = el.attributes.length;
-
-			for (; attributeIndex < attributesLength; attributeIndex++) {
-				var attr = el.attributes[attributeIndex];
-
-				if(attr.name === 'data-anchor-target') {
-					anchorTarget = document.querySelector(attr.value);
-
-					if(anchorTarget === null) {
-						throw 'Unable to find anchor target "' + attr.value + '"';
-					}
-
-					continue;
-				}
-
-				//Global smooth scrolling can be overridden by the element attribute.
-				if(attr.name === 'data-smooth-scrolling') {
-					smoothScrollThis = attr.value !== 'off';
-
-					continue;
-				}
-
-				//Global edge strategy can be overridden by the element attribute.
-				if(attr.name === 'data-edge-strategy') {
-					edgeStrategy = attr.value;
-
-					continue;
-				}
-
-				//Is this element tagged with the `data-emit-events` attribute?
-				if(attr.name === 'data-emit-events') {
-					emitEvents = true;
-
-					continue;
-				}
-
-				var match = attr.name.match(rxKeyframeAttribute);
-
-				if(match === null) {
-					continue;
-				}
-
-				var kf = {
-					props: attr.value,
-					//Point back to the element as well.
-					element: el,
-					//The name of the event which this keyframe will fire, if emitEvents is
-					eventType: attr.name.replace(rxCamelCase, rxCamelCaseFn)
-				};
-
-				keyFrames.push(kf);
-
-				var constant = match[1];
-
-				if(constant) {
-					//Strip the underscore prefix.
-					kf.constant = constant.substr(1);
-				}
-
-				//Get the key frame offset.
-				var offset = match[2];
-
-				//Is it a percentage offset?
-				if(/p$/.test(offset)) {
-					kf.isPercentage = true;
-					kf.offset = (offset.slice(0, -1) | 0) / 100;
-				} else {
-					kf.offset = (offset | 0);
-				}
-
-				var anchor1 = match[3];
-
-				//If second anchor is not set, the first will be taken for both.
-				var anchor2 = match[4] || anchor1;
-
-				//"absolute" (or "classic") mode, where numbers mean absolute scroll offset.
-				if(!anchor1 || anchor1 === ANCHOR_START || anchor1 === ANCHOR_END) {
-					kf.mode = 'absolute';
-
-					//data-end needs to be calculated after all key frames are known.
-					if(anchor1 === ANCHOR_END) {
-						kf.isEnd = true;
-					} else if(!kf.isPercentage) {
-						//For data-start we can already set the key frame w/o calculations.
-						//#59: "scale" options should only affect absolute mode.
-						kf.offset = kf.offset * _scale;
-					}
-				}
-				//"relative" mode, where numbers are relative to anchors.
-				else {
-					kf.mode = 'relative';
-					kf.anchors = [anchor1, anchor2];
-				}
-			}
-
-			//Does this element have key frames?
-			if(!keyFrames.length) {
-				continue;
-			}
-
-			//Will hold the original style and class attributes before we controlled the element (see #80).
-			var styleAttr, classAttr;
-
-			var id;
-
-			if(!ignoreID && SKROLLABLE_ID_DOM_PROPERTY in el) {
-				//We already have this element under control. Grab the corresponding skrollable id.
-				id = el[SKROLLABLE_ID_DOM_PROPERTY];
-				styleAttr = _skrollables[id].styleAttr;
-				classAttr = _skrollables[id].classAttr;
-			} else {
-				//It's an unknown element. Asign it a new skrollable id.
-				id = (el[SKROLLABLE_ID_DOM_PROPERTY] = _skrollableIdCounter++);
-				styleAttr = el.style.cssText;
-				classAttr = _getClass(el);
-			}
-
-			_skrollables[id] = {
-				element: el,
-				styleAttr: styleAttr,
-				classAttr: classAttr,
-				anchorTarget: anchorTarget,
-				keyFrames: keyFrames,
-				smoothScrolling: smoothScrollThis,
-				edgeStrategy: edgeStrategy,
-				emitEvents: emitEvents,
-				lastFrameIndex: -1
-			};
-
-			_updateClass(el, [SKROLLABLE_CLASS], []);
-		}
-
-		//Reflow for the first time.
-		_reflow();
-
-		//Now that we got all key frame numbers right, actually parse the properties.
-		elementIndex = 0;
-		elementsLength = elements.length;
-
-		for(; elementIndex < elementsLength; elementIndex++) {
-			var sk = _skrollables[elements[elementIndex][SKROLLABLE_ID_DOM_PROPERTY]];
-
-			if(sk === undefined) {
-				continue;
-			}
-
-			//Parse the property string to objects
-			_parseProps(sk);
-
-			//Fill key frames with missing properties from left and right
-			_fillProps(sk);
-		}
-
-		return _instance;
-	};
-
-	/**
-	 * Transform "relative" mode to "absolute" mode.
-	 * That is, calculate anchor position and offset of element.
-	 */
-	Skrollr.prototype.relativeToAbsolute = function(element, viewportAnchor, elementAnchor) {
-		var viewportHeight = documentElement.clientHeight;
-		var box = element.getBoundingClientRect();
-		var absolute = box.top;
-
-		//#100: IE doesn't supply "height" with getBoundingClientRect.
-		var boxHeight = box.bottom - box.top;
-
-		if(viewportAnchor === ANCHOR_BOTTOM) {
-			absolute -= viewportHeight;
-		} else if(viewportAnchor === ANCHOR_CENTER) {
-			absolute -= viewportHeight / 2;
-		}
-
-		if(elementAnchor === ANCHOR_BOTTOM) {
-			absolute += boxHeight;
-		} else if(elementAnchor === ANCHOR_CENTER) {
-			absolute += boxHeight / 2;
-		}
-
-		//Compensate scrolling since getBoundingClientRect is relative to viewport.
-		absolute += _instance.getScrollTop();
-
-		return (absolute + 0.5) | 0;
-	};
-
-	/**
-	 * Animates scroll top to new position.
-	 */
-	Skrollr.prototype.animateTo = function(top, options) {
-		options = options || {};
-
-		var now = _now();
-		var scrollTop = _instance.getScrollTop();
-
-		//Setting this to a new value will automatically cause the current animation to stop, if any.
-		_scrollAnimation = {
-			startTop: scrollTop,
-			topDiff: top - scrollTop,
-			targetTop: top,
-			duration: options.duration || DEFAULT_DURATION,
-			startTime: now,
-			endTime: now + (options.duration || DEFAULT_DURATION),
-			easing: easings[options.easing || DEFAULT_EASING],
-			done: options.done
-		};
-
-		//Don't queue the animation if there's nothing to animate.
-		if(!_scrollAnimation.topDiff) {
-			if(_scrollAnimation.done) {
-				_scrollAnimation.done.call(_instance, false);
-			}
-
-			_scrollAnimation = undefined;
-		}
-
-		return _instance;
-	};
-
-	/**
-	 * Stops animateTo animation.
-	 */
-	Skrollr.prototype.stopAnimateTo = function() {
-		if(_scrollAnimation && _scrollAnimation.done) {
-			_scrollAnimation.done.call(_instance, true);
-		}
-
-		_scrollAnimation = undefined;
-	};
-
-	/**
-	 * Returns if an animation caused by animateTo is currently running.
-	 */
-	Skrollr.prototype.isAnimatingTo = function() {
-		return !!_scrollAnimation;
-	};
-
-	Skrollr.prototype.isMobile = function() {
-		return _isMobile;
-	};
-
-	Skrollr.prototype.setScrollTop = function(top, force) {
-		_forceRender = (force === true);
-
-		if(_isMobile) {
-			_mobileOffset = Math.min(Math.max(top, 0), _maxKeyFrame);
-		} else {
-			documentElement.scrollTop = top;
-		}
-
-		return _instance;
-	};
-
-	Skrollr.prototype.getScrollTop = function() {
-		if(_isMobile) {
-			return _mobileOffset;
-		} else {
-			return documentElement.scrollTop || 0;
-		}
-	};
-
-	Skrollr.prototype.getMaxScrollTop = function() {
-		return _maxKeyFrame;
-	};
-
-	Skrollr.prototype.on = function(name, fn) {
-		_listeners[name] = fn;
-
-		return _instance;
-	};
-
-	Skrollr.prototype.off = function(name) {
-		delete _listeners[name];
-
-		return _instance;
-	};
-
-	Skrollr.prototype.destroy = function() {
-		var cancelAnimFrame = polyfillCAF();
-		cancelAnimFrame(_animFrame);
-		_removeAllEvents();
-
-		_updateClass(documentElement, [NO_SKROLLR_CLASS], [SKROLLR_CLASS, SKROLLR_DESKTOP_CLASS, SKROLLR_MOBILE_CLASS]);
-
-		var skrollableIndex = 0;
-		var skrollablesLength = _skrollables.length;
-
-		for(; skrollableIndex < skrollablesLength; skrollableIndex++) {
-			_reset(_skrollables[skrollableIndex].element);
-		}
-
-		documentElement.style.overflow = body.style.overflow = '';
-		documentElement.style.height = body.style.height = '';
-
-		if(_skrollrBody) {
-			skrollr.setStyle(_skrollrBody, 'transform', 'none');
-		}
-
-		_instance = undefined;
-		_skrollrBody = undefined;
-		_listeners = undefined;
-		_forceHeight = undefined;
-		_maxKeyFrame = 0;
-		_scale = 1;
-		_constants = undefined;
-		_mobileDeceleration = undefined;
-		_direction = 'down';
-		_lastTop = -1;
-		_lastViewportWidth = 0;
-		_lastViewportHeight = 0;
-		_requestReflow = false;
-		_scrollAnimation = undefined;
-		_smoothScrollingEnabled = undefined;
-		_smoothScrollingDuration = undefined;
-		_smoothScrolling = undefined;
-		_forceRender = undefined;
-		_skrollableIdCounter = 0;
-		_edgeStrategy = undefined;
-		_isMobile = false;
-		_mobileOffset = 0;
-		_translateZ = undefined;
-	};
-
-	/*
-		Private methods.
-	*/
-
-	var _initMobile = function() {
-		var initialElement;
-		var initialTouchY;
-		var initialTouchX;
-		var currentElement;
-		var currentTouchY;
-		var currentTouchX;
-		var lastTouchY;
-		var deltaY;
-
-		var initialTouchTime;
-		var currentTouchTime;
-		var lastTouchTime;
-		var deltaTime;
-
-		_addEvent(documentElement, [EVENT_TOUCHSTART, EVENT_TOUCHMOVE, EVENT_TOUCHCANCEL, EVENT_TOUCHEND].join(' '), function(e) {
-			var touch = e.changedTouches[0];
-
-			currentElement = e.target;
-
-			//We don't want text nodes.
-			while(currentElement.nodeType === 3) {
-				currentElement = currentElement.parentNode;
-			}
-
-			currentTouchY = touch.clientY;
-			currentTouchX = touch.clientX;
-			currentTouchTime = e.timeStamp;
-
-			if(!rxTouchIgnoreTags.test(currentElement.tagName)) {
-				e.preventDefault();
-			}
-
-			switch(e.type) {
-				case EVENT_TOUCHSTART:
-					//The last element we tapped on.
-					if(initialElement) {
-						initialElement.blur();
-					}
-
-					_instance.stopAnimateTo();
-
-					initialElement = currentElement;
-
-					initialTouchY = lastTouchY = currentTouchY;
-					initialTouchX = currentTouchX;
-					initialTouchTime = currentTouchTime;
-
-					break;
-				case EVENT_TOUCHMOVE:
-					//Prevent default event on touchIgnore elements in case they don't have focus yet.
-					if(rxTouchIgnoreTags.test(currentElement.tagName) && document.activeElement !== currentElement) {
-						e.preventDefault();
-					}
-
-					deltaY = currentTouchY - lastTouchY;
-					deltaTime = currentTouchTime - lastTouchTime;
-
-					_instance.setScrollTop(_mobileOffset - deltaY, true);
-
-					lastTouchY = currentTouchY;
-					lastTouchTime = currentTouchTime;
-					break;
-				default:
-				case EVENT_TOUCHCANCEL:
-				case EVENT_TOUCHEND:
-					var distanceY = initialTouchY - currentTouchY;
-					var distanceX = initialTouchX - currentTouchX;
-					var distance2 = distanceX * distanceX + distanceY * distanceY;
-
-					//Check if it was more like a tap (moved less than 7px).
-					if(distance2 < 49) {
-						if(!rxTouchIgnoreTags.test(initialElement.tagName)) {
-							initialElement.focus();
-
-							//It was a tap, click the element.
-							var clickEvent = document.createEvent('MouseEvents');
-							clickEvent.initMouseEvent('click', true, true, e.view, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 0, null);
-							initialElement.dispatchEvent(clickEvent);
-						}
-
-						return;
-					}
-
-					initialElement = undefined;
-
-					var speed = deltaY / deltaTime;
-
-					//Cap speed at 3 pixel/ms.
-					speed = Math.max(Math.min(speed, 3), -3);
-
-					var duration = Math.abs(speed / _mobileDeceleration);
-					var targetOffset = speed * duration + 0.5 * _mobileDeceleration * duration * duration;
-					var targetTop = _instance.getScrollTop() - targetOffset;
-
-					//Relative duration change for when scrolling above bounds.
-					var targetRatio = 0;
-
-					//Change duration proportionally when scrolling would leave bounds.
-					if(targetTop > _maxKeyFrame) {
-						targetRatio = (_maxKeyFrame - targetTop) / targetOffset;
-
-						targetTop = _maxKeyFrame;
-					} else if(targetTop < 0) {
-						targetRatio = -targetTop / targetOffset;
-
-						targetTop = 0;
-					}
-
-					duration = duration * (1 - targetRatio);
-
-					_instance.animateTo((targetTop + 0.5) | 0, {easing: 'outCubic', duration: duration});
-					break;
-			}
-		});
-
-		//Just in case there has already been some native scrolling, reset it.
-		window.scrollTo(0, 0);
-		documentElement.style.overflow = body.style.overflow = 'hidden';
-	};
-
-	/**
-	 * Updates key frames which depend on others / need to be updated on resize.
-	 * That is "end" in "absolute" mode and all key frames in "relative" mode.
-	 * Also handles constants, because they may change on resize.
-	 */
-	var _updateDependentKeyFrames = function() {
-		var viewportHeight = documentElement.clientHeight;
-		var processedConstants = _processConstants();
-		var skrollable;
-		var element;
-		var anchorTarget;
-		var keyFrames;
-		var keyFrameIndex;
-		var keyFramesLength;
-		var kf;
-		var skrollableIndex;
-		var skrollablesLength;
-		var offset;
-		var constantValue;
-
-		//First process all relative-mode elements and find the max key frame.
-		skrollableIndex = 0;
-		skrollablesLength = _skrollables.length;
-
-		for(; skrollableIndex < skrollablesLength; skrollableIndex++) {
-			skrollable = _skrollables[skrollableIndex];
-			element = skrollable.element;
-			anchorTarget = skrollable.anchorTarget;
-			keyFrames = skrollable.keyFrames;
-
-			keyFrameIndex = 0;
-			keyFramesLength = keyFrames.length;
-
-			for(; keyFrameIndex < keyFramesLength; keyFrameIndex++) {
-				kf = keyFrames[keyFrameIndex];
-
-				offset = kf.offset;
-				constantValue = processedConstants[kf.constant] || 0;
-
-				kf.frame = offset;
-
-				if(kf.isPercentage) {
-					//Convert the offset to percentage of the viewport height.
-					offset = offset * viewportHeight;
-
-					//Absolute + percentage mode.
-					kf.frame = offset;
-				}
-
-				if(kf.mode === 'relative') {
-					_reset(element);
-
-					kf.frame = _instance.relativeToAbsolute(anchorTarget, kf.anchors[0], kf.anchors[1]) - offset;
-
-					_reset(element, true);
-				}
-
-				kf.frame += constantValue;
-
-				//Only search for max key frame when forceHeight is enabled.
-				if(_forceHeight) {
-					//Find the max key frame, but don't use one of the data-end ones for comparison.
-					if(!kf.isEnd && kf.frame > _maxKeyFrame) {
-						_maxKeyFrame = kf.frame;
-					}
-				}
-			}
-		}
-
-		//#133: The document can be larger than the maxKeyFrame we found.
-		_maxKeyFrame = Math.max(_maxKeyFrame, _getDocumentHeight());
-
-		//Now process all data-end keyframes.
-		skrollableIndex = 0;
-		skrollablesLength = _skrollables.length;
-
-		for(; skrollableIndex < skrollablesLength; skrollableIndex++) {
-			skrollable = _skrollables[skrollableIndex];
-			keyFrames = skrollable.keyFrames;
-
-			keyFrameIndex = 0;
-			keyFramesLength = keyFrames.length;
-
-			for(; keyFrameIndex < keyFramesLength; keyFrameIndex++) {
-				kf = keyFrames[keyFrameIndex];
-
-				constantValue = processedConstants[kf.constant] || 0;
-
-				if(kf.isEnd) {
-					kf.frame = _maxKeyFrame - kf.offset + constantValue;
-				}
-			}
-
-			skrollable.keyFrames.sort(_keyFrameComparator);
-		}
-	};
-
-	/**
-	 * Calculates and sets the style properties for the element at the given frame.
-	 * @param fakeFrame The frame to render at when smooth scrolling is enabled.
-	 * @param actualFrame The actual frame we are at.
-	 */
-	var _calcSteps = function(fakeFrame, actualFrame) {
-		//Iterate over all skrollables.
-		var skrollableIndex = 0;
-		var skrollablesLength = _skrollables.length;
-
-		for(; skrollableIndex < skrollablesLength; skrollableIndex++) {
-			var skrollable = _skrollables[skrollableIndex];
-			var element = skrollable.element;
-			var frame = skrollable.smoothScrolling ? fakeFrame : actualFrame;
-			var frames = skrollable.keyFrames;
-			var framesLength = frames.length;
-			var firstFrame = frames[0];
-			var lastFrame = frames[frames.length - 1];
-			var beforeFirst = frame < firstFrame.frame;
-			var afterLast = frame > lastFrame.frame;
-			var firstOrLastFrame = beforeFirst ? firstFrame : lastFrame;
-			var emitEvents = skrollable.emitEvents;
-			var lastFrameIndex = skrollable.lastFrameIndex;
-			var key;
-			var value;
-
-			//If we are before/after the first/last frame, set the styles according to the given edge strategy.
-			if(beforeFirst || afterLast) {
-				//Check if we already handled this edge case last time.
-				//Note: using setScrollTop it's possible that we jumped from one edge to the other.
-				if(beforeFirst && skrollable.edge === -1 || afterLast && skrollable.edge === 1) {
-					continue;
-				}
-
-				//Add the skrollr-before or -after class.
-				if(beforeFirst) {
-					_updateClass(element, [SKROLLABLE_BEFORE_CLASS], [SKROLLABLE_AFTER_CLASS, SKROLLABLE_BETWEEN_CLASS]);
-
-					//This handles the special case where we exit the first keyframe.
-					if(emitEvents && lastFrameIndex > -1) {
-						_emitEvent(element, firstFrame.eventType, _direction);
-						skrollable.lastFrameIndex = -1;
-					}
-				} else {
-					_updateClass(element, [SKROLLABLE_AFTER_CLASS], [SKROLLABLE_BEFORE_CLASS, SKROLLABLE_BETWEEN_CLASS]);
-
-					//This handles the special case where we exit the last keyframe.
-					if(emitEvents && lastFrameIndex < framesLength) {
-						_emitEvent(element, lastFrame.eventType, _direction);
-						skrollable.lastFrameIndex = framesLength;
-					}
-				}
-
-				//Remember that we handled the edge case (before/after the first/last keyframe).
-				skrollable.edge = beforeFirst ? -1 : 1;
-
-				switch(skrollable.edgeStrategy) {
-					case 'reset':
-						_reset(element);
-						continue;
-					case 'ease':
-						//Handle this case like it would be exactly at first/last keyframe and just pass it on.
-						frame = firstOrLastFrame.frame;
-						break;
-					default:
-					case 'set':
-						var props = firstOrLastFrame.props;
-
-						for(key in props) {
-							if(hasProp.call(props, key)) {
-								value = _interpolateString(props[key].value);
-
-								//Set style or attribute.
-								if(key.indexOf('@') === 0) {
-									element.setAttribute(key.substr(1), value);
-								} else {
-									skrollr.setStyle(element, key, value);
-								}
-							}
-						}
-
-						continue;
-				}
-			} else {
-				//Did we handle an edge last time?
-				if(skrollable.edge !== 0) {
-					_updateClass(element, [SKROLLABLE_CLASS, SKROLLABLE_BETWEEN_CLASS], [SKROLLABLE_BEFORE_CLASS, SKROLLABLE_AFTER_CLASS]);
-					skrollable.edge = 0;
-				}
-			}
-
-			//Find out between which two key frames we are right now.
-			var keyFrameIndex = 0;
-
-			for(; keyFrameIndex < framesLength - 1; keyFrameIndex++) {
-				if(frame >= frames[keyFrameIndex].frame && frame <= frames[keyFrameIndex + 1].frame) {
-					var left = frames[keyFrameIndex];
-					var right = frames[keyFrameIndex + 1];
-
-					for(key in left.props) {
-						if(hasProp.call(left.props, key)) {
-							var progress = (frame - left.frame) / (right.frame - left.frame);
-
-							//Transform the current progress using the given easing function.
-							progress = left.props[key].easing(progress);
-
-							//Interpolate between the two values
-							value = _calcInterpolation(left.props[key].value, right.props[key].value, progress);
-
-							value = _interpolateString(value);
-
-							//Set style or attribute.
-							if(key.indexOf('@') === 0) {
-								element.setAttribute(key.substr(1), value);
-							} else {
-								skrollr.setStyle(element, key, value);
-							}
-						}
-					}
-
-					//Are events enabled on this element?
-					//This code handles the usual cases of scrolling through different keyframes.
-					//The special cases of before first and after last keyframe are handled above.
-					if(emitEvents) {
-						//Did we pass a new keyframe?
-						if(lastFrameIndex !== keyFrameIndex) {
-							if(_direction === 'down') {
-								_emitEvent(element, left.eventType, _direction);
-							} else {
-								_emitEvent(element, right.eventType, _direction);
-							}
-
-							skrollable.lastFrameIndex = keyFrameIndex;
-						}
-					}
-
-					break;
-				}
-			}
-		}
-	};
-
-	/**
-	 * Renders all elements.
-	 */
-	var _render = function() {
-		if(_requestReflow) {
-			_requestReflow = false;
-			_reflow();
-		}
-
-		//We may render something else than the actual scrollbar position.
-		var renderTop = _instance.getScrollTop();
-
-		//If there's an animation, which ends in current render call, call the callback after rendering.
-		var afterAnimationCallback;
-		var now = _now();
-		var progress;
-
-		//Before actually rendering handle the scroll animation, if any.
-		if(_scrollAnimation) {
-			//It's over
-			if(now >= _scrollAnimation.endTime) {
-				renderTop = _scrollAnimation.targetTop;
-				afterAnimationCallback = _scrollAnimation.done;
-				_scrollAnimation = undefined;
-			} else {
-				//Map the current progress to the new progress using given easing function.
-				progress = _scrollAnimation.easing((now - _scrollAnimation.startTime) / _scrollAnimation.duration);
-
-				renderTop = (_scrollAnimation.startTop + progress * _scrollAnimation.topDiff) | 0;
-			}
-
-			_instance.setScrollTop(renderTop, true);
-		}
-		//Smooth scrolling only if there's no animation running and if we're not forcing the rendering.
-		else if(!_forceRender) {
-			var smoothScrollingDiff = _smoothScrolling.targetTop - renderTop;
-
-			//The user scrolled, start new smooth scrolling.
-			if(smoothScrollingDiff) {
-				_smoothScrolling = {
-					startTop: _lastTop,
-					topDiff: renderTop - _lastTop,
-					targetTop: renderTop,
-					startTime: _lastRenderCall,
-					endTime: _lastRenderCall + _smoothScrollingDuration
-				};
-			}
-
-			//Interpolate the internal scroll position (not the actual scrollbar).
-			if(now <= _smoothScrolling.endTime) {
-				//Map the current progress to the new progress using easing function.
-				progress = easings.sqrt((now - _smoothScrolling.startTime) / _smoothScrollingDuration);
-
-				renderTop = (_smoothScrolling.startTop + progress * _smoothScrolling.topDiff) | 0;
-			}
-		}
-
-		//That's were we actually "scroll" on mobile.
-		if(_isMobile && _skrollrBody) {
-			//Set the transform ("scroll it").
-			skrollr.setStyle(_skrollrBody, 'transform', 'translate(0, ' + -(_mobileOffset) + 'px) ' + _translateZ);
-		}
-
-		//Did the scroll position even change?
-		if(_forceRender || _lastTop !== renderTop) {
-			//Remember in which direction are we scrolling?
-			_direction = (renderTop > _lastTop) ? 'down' : (renderTop < _lastTop ? 'up' : _direction);
-
-			_forceRender = false;
-
-			var listenerParams = {
-				curTop: renderTop,
-				lastTop: _lastTop,
-				maxTop: _maxKeyFrame,
-				direction: _direction
-			};
-
-			//Tell the listener we are about to render.
-			var continueRendering = _listeners.beforerender && _listeners.beforerender.call(_instance, listenerParams);
-
-			//The beforerender listener function is able the cancel rendering.
-			if(continueRendering !== false) {
-				//Now actually interpolate all the styles.
-				_calcSteps(renderTop, _instance.getScrollTop());
-
-				//Remember when we last rendered.
-				_lastTop = renderTop;
-
-				if(_listeners.render) {
-					_listeners.render.call(_instance, listenerParams);
-				}
-			}
-
-			if(afterAnimationCallback) {
-				afterAnimationCallback.call(_instance, false);
-			}
-		}
-
-		_lastRenderCall = now;
-	};
-
-	/**
-	 * Parses the properties for each key frame of the given skrollable.
-	 */
-	var _parseProps = function(skrollable) {
-		//Iterate over all key frames
-		var keyFrameIndex = 0;
-		var keyFramesLength = skrollable.keyFrames.length;
-
-		for(; keyFrameIndex < keyFramesLength; keyFrameIndex++) {
-			var frame = skrollable.keyFrames[keyFrameIndex];
-			var easing;
-			var value;
-			var prop;
-			var props = {};
-
-			var match;
-
-			while((match = rxPropValue.exec(frame.props)) !== null) {
-				prop = match[1];
-				value = match[2];
-
-				easing = prop.match(rxPropEasing);
-
-				//Is there an easing specified for this prop?
-				if(easing !== null) {
-					prop = easing[1];
-					easing = easing[2];
-				} else {
-					easing = DEFAULT_EASING;
-				}
-
-				//Exclamation point at first position forces the value to be taken literal.
-				value = value.indexOf('!') ? _parseProp(value) : [value.slice(1)];
-
-				//Save the prop for this key frame with his value and easing function
-				props[prop] = {
-					value: value,
-					easing: easings[easing]
-				};
-			}
-
-			frame.props = props;
-		}
-	};
-
-	/**
-	 * Parses a value extracting numeric values and generating a format string
-	 * for later interpolation of the new values in old string.
-	 *
-	 * @param val The CSS value to be parsed.
-	 * @return Something like ["rgba(?%,?%, ?%,?)", 100, 50, 0, .7]
-	 * where the first element is the format string later used
-	 * and all following elements are the numeric value.
-	 */
-	var _parseProp = function(val) {
-		var numbers = [];
-
-		//One special case, where floats don't work.
-		//We replace all occurences of rgba colors
-		//which don't use percentage notation with the percentage notation.
-		rxRGBAIntegerColor.lastIndex = 0;
-		val = val.replace(rxRGBAIntegerColor, function(rgba) {
-			return rgba.replace(rxNumericValue, function(n) {
-				return n / 255 * 100 + '%';
-			});
-		});
-
-		//Handle prefixing of "gradient" values.
-		//For now only the prefixed value will be set. Unprefixed isn't supported anyway.
-		if(theDashedCSSPrefix) {
-			rxGradient.lastIndex = 0;
-			val = val.replace(rxGradient, function(s) {
-				return theDashedCSSPrefix + s;
-			});
-		}
-
-		//Now parse ANY number inside this string and create a format string.
-		val = val.replace(rxNumericValue, function(n) {
-			numbers.push(+n);
-			return '{?}';
-		});
-
-		//Add the formatstring as first value.
-		numbers.unshift(val);
-
-		return numbers;
-	};
-
-	/**
-	 * Fills the key frames with missing left and right hand properties.
-	 * If key frame 1 has property X and key frame 2 is missing X,
-	 * but key frame 3 has X again, then we need to assign X to key frame 2 too.
-	 *
-	 * @param sk A skrollable.
-	 */
-	var _fillProps = function(sk) {
-		//Will collect the properties key frame by key frame
-		var propList = {};
-		var keyFrameIndex;
-		var keyFramesLength;
-
-		//Iterate over all key frames from left to right
-		keyFrameIndex = 0;
-		keyFramesLength = sk.keyFrames.length;
-
-		for(; keyFrameIndex < keyFramesLength; keyFrameIndex++) {
-			_fillPropForFrame(sk.keyFrames[keyFrameIndex], propList);
-		}
-
-		//Now do the same from right to fill the last gaps
-
-		propList = {};
-
-		//Iterate over all key frames from right to left
-		keyFrameIndex = sk.keyFrames.length - 1;
-
-		for(; keyFrameIndex >= 0; keyFrameIndex--) {
-			_fillPropForFrame(sk.keyFrames[keyFrameIndex], propList);
-		}
-	};
-
-	var _fillPropForFrame = function(frame, propList) {
-		var key;
-
-		//For each key frame iterate over all right hand properties and assign them,
-		//but only if the current key frame doesn't have the property by itself
-		for(key in propList) {
-			//The current frame misses this property, so assign it.
-			if(!hasProp.call(frame.props, key)) {
-				frame.props[key] = propList[key];
-			}
-		}
-
-		//Iterate over all props of the current frame and collect them
-		for(key in frame.props) {
-			propList[key] = frame.props[key];
-		}
-	};
-
-	/**
-	 * Calculates the new values for two given values array.
-	 */
-	var _calcInterpolation = function(val1, val2, progress) {
-		var valueIndex;
-		var val1Length = val1.length;
-
-		//They both need to have the same length
-		if(val1Length !== val2.length) {
-			throw 'Can\'t interpolate between "' + val1[0] + '" and "' + val2[0] + '"';
-		}
-
-		//Add the format string as first element.
-		var interpolated = [val1[0]];
-
-		valueIndex = 1;
-
-		for(; valueIndex < val1Length; valueIndex++) {
-			//That's the line where the two numbers are actually interpolated.
-			interpolated[valueIndex] = val1[valueIndex] + ((val2[valueIndex] - val1[valueIndex]) * progress);
-		}
-
-		return interpolated;
-	};
-
-	/**
-	 * Interpolates the numeric values into the format string.
-	 */
-	var _interpolateString = function(val) {
-		var valueIndex = 1;
-
-		rxInterpolateString.lastIndex = 0;
-
-		return val[0].replace(rxInterpolateString, function() {
-			return val[valueIndex++];
-		});
-	};
-
-	/**
-	 * Resets the class and style attribute to what it was before skrollr manipulated the element.
-	 * Also remembers the values it had before reseting, in order to undo the reset.
-	 */
-	var _reset = function(elements, undo) {
-		//We accept a single element or an array of elements.
-		elements = [].concat(elements);
-
-		var skrollable;
-		var element;
-		var elementsIndex = 0;
-		var elementsLength = elements.length;
-
-		for(; elementsIndex < elementsLength; elementsIndex++) {
-			element = elements[elementsIndex];
-			skrollable = _skrollables[element[SKROLLABLE_ID_DOM_PROPERTY]];
-
-			//Couldn't find the skrollable for this DOM element.
-			if(!skrollable) {
-				continue;
-			}
-
-			if(undo) {
-				//Reset class and style to the "dirty" (set by skrollr) values.
-				element.style.cssText = skrollable.dirtyStyleAttr;
-				_updateClass(element, skrollable.dirtyClassAttr);
-			} else {
-				//Remember the "dirty" (set by skrollr) class and style.
-				skrollable.dirtyStyleAttr = element.style.cssText;
-				skrollable.dirtyClassAttr = _getClass(element);
-
-				//Reset class and style to what it originally was.
-				element.style.cssText = skrollable.styleAttr;
-				_updateClass(element, skrollable.classAttr);
-			}
-		}
-	};
-
-	/**
-	 * Detects support for 3d transforms by applying it to the skrollr-body.
-	 */
-	var _detect3DTransforms = function() {
-		_translateZ = 'translateZ(0)';
-		skrollr.setStyle(_skrollrBody, 'transform', _translateZ);
-
-		var computedStyle = getStyle(_skrollrBody);
-		var computedTransform = computedStyle.getPropertyValue('transform');
-		var computedTransformWithPrefix = computedStyle.getPropertyValue(theDashedCSSPrefix + 'transform');
-		var has3D = (computedTransform && computedTransform !== 'none') || (computedTransformWithPrefix && computedTransformWithPrefix !== 'none');
-
-		if(!has3D) {
-			_translateZ = '';
-		}
-	};
-
-	/**
-	 * Set the CSS property on the given element. Sets prefixed properties as well.
-	 */
-	skrollr.setStyle = function(el, prop, val) {
-		var style = el.style;
-
-		//Camel case.
-		prop = prop.replace(rxCamelCase, rxCamelCaseFn).replace('-', '');
-
-		//Make sure z-index gets a <integer>.
-		//This is the only <integer> case we need to handle.
-		if(prop === 'zIndex') {
-			if(isNaN(val)) {
-				//If it's not a number, don't touch it.
-				//It could for example be "auto" (#351).
-				style[prop] = val;
-			} else {
-				//Floor the number.
-				style[prop] = '' + (val | 0);
-			}
-		}
-		//#64: "float" can't be set across browsers. Needs to use "cssFloat" for all except IE.
-		else if(prop === 'float') {
-			style.styleFloat = style.cssFloat = val;
-		}
-		else {
-			//Need try-catch for old IE.
-			try {
-				//Set prefixed property if there's a prefix.
-				if(theCSSPrefix) {
-					style[theCSSPrefix + prop.slice(0,1).toUpperCase() + prop.slice(1)] = val;
-				}
-
-				//Set unprefixed.
-				style[prop] = val;
-			} catch(ignore) {}
-		}
-	};
-
-	/**
-	 * Cross browser event handling.
-	 */
-	var _addEvent = skrollr.addEvent = function(element, names, callback) {
-		var intermediate = function(e) {
-			//Normalize IE event stuff.
-			e = e || window.event;
-
-			if(!e.target) {
-				e.target = e.srcElement;
-			}
-
-			if(!e.preventDefault) {
-				e.preventDefault = function() {
-					e.returnValue = false;
-					e.defaultPrevented = true;
-				};
-			}
-
-			return callback.call(this, e);
-		};
-
-		names = names.split(' ');
-
-		var name;
-		var nameCounter = 0;
-		var namesLength = names.length;
-
-		for(; nameCounter < namesLength; nameCounter++) {
-			name = names[nameCounter];
-
-			if(element.addEventListener) {
-				element.addEventListener(name, callback, false);
-			} else {
-				element.attachEvent('on' + name, intermediate);
-			}
-
-			//Remember the events to be able to flush them later.
-			_registeredEvents.push({
-				element: element,
-				name: name,
-				listener: callback
-			});
-		}
-	};
-
-	var _removeEvent = skrollr.removeEvent = function(element, names, callback) {
-		names = names.split(' ');
-
-		var nameCounter = 0;
-		var namesLength = names.length;
-
-		for(; nameCounter < namesLength; nameCounter++) {
-			if(element.removeEventListener) {
-				element.removeEventListener(names[nameCounter], callback, false);
-			} else {
-				element.detachEvent('on' + names[nameCounter], callback);
-			}
-		}
-	};
-
-	var _removeAllEvents = function() {
-		var eventData;
-		var eventCounter = 0;
-		var eventsLength = _registeredEvents.length;
-
-		for(; eventCounter < eventsLength; eventCounter++) {
-			eventData = _registeredEvents[eventCounter];
-
-			_removeEvent(eventData.element, eventData.name, eventData.listener);
-		}
-
-		_registeredEvents = [];
-	};
-
-	var _emitEvent = function(element, name, direction) {
-		if(_listeners.keyframe) {
-			_listeners.keyframe.call(_instance, element, name, direction);
-		}
-	};
-
-	var _reflow = function() {
-		var pos = _instance.getScrollTop();
-
-		//Will be recalculated by _updateDependentKeyFrames.
-		_maxKeyFrame = 0;
-
-		if(_forceHeight && !_isMobile) {
-			//un-"force" the height to not mess with the calculations in _updateDependentKeyFrames (#216).
-			body.style.height = '';
-		}
-
-		_updateDependentKeyFrames();
-
-		if(_forceHeight && !_isMobile) {
-			//"force" the height.
-			body.style.height = (_maxKeyFrame + documentElement.clientHeight) + 'px';
-		}
-
-		//The scroll offset may now be larger than needed (on desktop the browser/os prevents scrolling farther than the bottom).
-		if(_isMobile) {
-			_instance.setScrollTop(Math.min(_instance.getScrollTop(), _maxKeyFrame));
-		} else {
-			//Remember and reset the scroll pos (#217).
-			_instance.setScrollTop(pos, true);
-		}
-
-		_forceRender = true;
-	};
-
-	/*
-	 * Returns a copy of the constants object where all functions and strings have been evaluated.
-	 */
-	var _processConstants = function() {
-		var viewportHeight = documentElement.clientHeight;
-		var copy = {};
-		var prop;
-		var value;
-
-		for(prop in _constants) {
-			value = _constants[prop];
-
-			if(typeof value === 'function') {
-				value = value.call(_instance);
-			}
-			//Percentage offset.
-			else if((/p$/).test(value)) {
-				value = (value.slice(0, -1) / 100) * viewportHeight;
-			}
-
-			copy[prop] = value;
-		}
-
-		return copy;
-	};
-
-	/*
-	 * Returns the height of the document.
-	 */
-	var _getDocumentHeight = function() {
-		var skrollrBodyHeight = (_skrollrBody && _skrollrBody.offsetHeight || 0);
-		var bodyHeight = Math.max(skrollrBodyHeight, body.scrollHeight, body.offsetHeight, documentElement.scrollHeight, documentElement.offsetHeight, documentElement.clientHeight);
-
-		return bodyHeight - documentElement.clientHeight;
-	};
-
-	/**
-	 * Returns a string of space separated classnames for the current element.
-	 * Works with SVG as well.
-	 */
-	var _getClass = function(element) {
-		var prop = 'className';
-
-		//SVG support by using className.baseVal instead of just className.
-		if(window.SVGElement && element instanceof window.SVGElement) {
-			element = element[prop];
-			prop = 'baseVal';
-		}
-
-		return element[prop];
-	};
-
-	/**
-	 * Adds and removes a CSS classes.
-	 * Works with SVG as well.
-	 * add and remove are arrays of strings,
-	 * or if remove is ommited add is a string and overwrites all classes.
-	 */
-	var _updateClass = function(element, add, remove) {
-		var prop = 'className';
-
-		//SVG support by using className.baseVal instead of just className.
-		if(window.SVGElement && element instanceof window.SVGElement) {
-			element = element[prop];
-			prop = 'baseVal';
-		}
-
-		//When remove is ommited, we want to overwrite/set the classes.
-		if(remove === undefined) {
-			element[prop] = add;
-			return;
-		}
-
-		//Cache current classes. We will work on a string before passing back to DOM.
-		var val = element[prop];
-
-		//All classes to be removed.
-		var classRemoveIndex = 0;
-		var removeLength = remove.length;
-
-		for(; classRemoveIndex < removeLength; classRemoveIndex++) {
-			val = _untrim(val).replace(_untrim(remove[classRemoveIndex]), ' ');
-		}
-
-		val = _trim(val);
-
-		//All classes to be added.
-		var classAddIndex = 0;
-		var addLength = add.length;
-
-		for(; classAddIndex < addLength; classAddIndex++) {
-			//Only add if el not already has class.
-			if(_untrim(val).indexOf(_untrim(add[classAddIndex])) === -1) {
-				val += ' ' + add[classAddIndex];
-			}
-		}
-
-		element[prop] = _trim(val);
-	};
-
-	var _trim = function(a) {
-		return a.replace(rxTrim, '');
-	};
-
-	/**
-	 * Adds a space before and after the string.
-	 */
-	var _untrim = function(a) {
-		return ' ' + a + ' ';
-	};
-
-	var _now = Date.now || function() {
-		return +new Date();
-	};
-
-	var _keyFrameComparator = function(a, b) {
-		return a.frame - b.frame;
-	};
-
-	/*
-	 * Private variables.
-	 */
-
-	//Singleton
-	var _instance;
-
-	/*
-		A list of all elements which should be animated associated with their the metadata.
-		Exmaple skrollable with two key frames animating from 100px width to 20px:
-
-		skrollable = {
-			element: <the DOM element>,
-			styleAttr: <style attribute of the element before skrollr>,
-			classAttr: <class attribute of the element before skrollr>,
-			keyFrames: [
-				{
-					frame: 100,
-					props: {
-						width: {
-							value: ['{?}px', 100],
-							easing: <reference to easing function>
-						}
-					},
-					mode: "absolute"
-				},
-				{
-					frame: 200,
-					props: {
-						width: {
-							value: ['{?}px', 20],
-							easing: <reference to easing function>
-						}
-					},
-					mode: "absolute"
-				}
-			]
-		};
-	*/
-	var _skrollables;
-
-	var _skrollrBody;
-
-	var _listeners;
-	var _forceHeight;
-	var _maxKeyFrame = 0;
-
-	var _scale = 1;
-	var _constants;
-
-	var _mobileDeceleration;
-
-	//Current direction (up/down).
-	var _direction = 'down';
-
-	//The last top offset value. Needed to determine direction.
-	var _lastTop = -1;
-
-	//The last time we called the render method (doesn't mean we rendered!).
-	var _lastRenderCall = _now();
-
-	//For detecting if it actually resized (#271).
-	var _lastViewportWidth = 0;
-	var _lastViewportHeight = 0;
-
-	var _requestReflow = false;
-
-	//Will contain data about a running scrollbar animation, if any.
-	var _scrollAnimation;
-
-	var _smoothScrollingEnabled;
-
-	var _smoothScrollingDuration;
-
-	//Will contain settins for smooth scrolling if enabled.
-	var _smoothScrolling;
-
-	//Can be set by any operation/event to force rendering even if the scrollbar didn't move.
-	var _forceRender;
-
-	//Each skrollable gets an unique ID incremented for each skrollable.
-	//The ID is the index in the _skrollables array.
-	var _skrollableIdCounter = 0;
-
-	var _edgeStrategy;
-
-
-	//Mobile specific vars. Will be stripped by UglifyJS when not in use.
-	var _isMobile = false;
-
-	//The virtual scroll offset when using mobile scrolling.
-	var _mobileOffset = 0;
-
-	//If the browser supports 3d transforms, this will be filled with 'translateZ(0)' (empty string otherwise).
-	var _translateZ;
-
-	//Will contain data about registered events by skrollr.
-	var _registeredEvents = [];
-
-	//Animation frame id returned by RequestAnimationFrame (or timeout when RAF is not supported).
-	var _animFrame;
-
-	//Expose skrollr as either a global variable or a require.js module.
-	if(typeof define === 'function' && define.amd) {
-		define([], function () {
-			return skrollr;
-		});
-	} else if (typeof module !== 'undefined' && module.exports) {
-		module.exports = skrollr;
-	} else {
-		window.skrollr = skrollr;
-	}
-
-}(window, document));

+ 1 - 5
app/assets/javascripts/app/views/ticket_zoom.jst.eco

@@ -1,11 +1,7 @@
 <div class="tabsSidebar-holder">
   <div class="main no-padding flex tabsSidebar-sidebarSpacer tabsSidebar-tabsSpacer">
     <div class="ticketZoom">
-      <div class="scrollPageHeader tabsSidebar-sidebarSpacer zIndex-3 horizontal center"
-        data-anchor-target=".ticketZoom .page-header"
-        data-128-top-bottom="transform: translateY(-64px);"
-        data-64-top-bottom="transform: translateY(0px);"
-      >
+      <div class="scrollPageHeader tabsSidebar-sidebarSpacer zIndex-3 horizontal center">
         <small><%- @C('ticket_hook') %> <span class="ticket-number"><%- @ticket.number %></span></small>
         <div class="ticket-title flex"></div>
         <div class="pagination-counter">

+ 0 - 1
app/assets/javascripts/application.js

@@ -11,7 +11,6 @@
 
 //= require ./app/lib/animations/velocity.min.js
 //= require ./app/lib/animations/velocity.ui.js
-//= require ./app/lib/animations/skrollr.js
 
 //not_used= require_tree ./app/lib/spine
 //= require ./app/lib/spine/spine.coffee