File: /home/buildfft/public_html/wp-content/plugins/pixfort-likes/scripts/numberAnimate.js
/*global define:false, WebKitCSSMatrix:false */
/*jshint forin:true, noarg:true, noempty:true, eqeqeq:true, evil:true,
laxbreak:true, bitwise:true, strict:true, undef:true, unused:true, browser:true,
jquery:true, indent:4, curly:false, maxerr:50 */
//Set the plugin up so that it'll work as a AMD module or regular import
//See: https://github.com/umdjs/umd/blob/master/jqueryPlugin.js..
(function (factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(jQuery);
}
}(function ($) {
"use strict";
//first figure out which CSS3 properties to set..
var prefixes = ["", "O", "ms", "Webkit", "Moz"];
//using same idea as jquery transform plugin..
var testDivStyle = document.createElement('div').style;
var css3Prefix = null;
for (var i = 0, len = prefixes.length; i < len; i++) {
if (prefixes[i] + "Transition" in testDivStyle &&
prefixes[i] + "Transform" in testDivStyle) {
css3Prefix = prefixes[i];
break;
}
}
var animationSupported = css3Prefix !== null;
//get the transition ended event name for the css3Prefix..
var transitionEndEvent;
switch (css3Prefix) {
case "O":
transitionEndEvent = "otransitionend";
break;
case "ms":
transitionEndEvent = "msTransitionEnd";
break;
case "Webkit":
transitionEndEvent = "webkitTransitionEnd";
break;
default:
transitionEndEvent = "transitionend";
}
//allow the use of hardware accellerated transforms for older webkit browsers, adapted from:
//http://www.appmobi.com/documentation/content/Articles/Article_UsingBestPractices/index.html?r=8684
var translateOpen = window.WebKitCSSMatrix
&& 'm11' in new WebKitCSSMatrix() ? "translate3d(0, " : "translate(0, ";
var translateClose = window.WebKitCSSMatrix
&& 'm11' in new WebKitCSSMatrix() ? "px ,0)" : "px)";
/**
* Binds the given function onto the given jQuery array $el on the transitionEndEvent and unbinds it after execution.
* Also handles the case where the event doesn't fire, in which case a timeout is used to ensure execution, which runs
* after the given number of milliseconds plus an additional 100ms grace period.
*/
var bindToTransitionEndForSingleRun = function ($el, funcToExec, maxMSTillTransitionEnd) {
var firedFunc = false;
var wrappedFunc = function () {
funcToExec();
firedFunc = true;
$el.unbind(transitionEndEvent, wrappedFunc);
};
$el.bind(transitionEndEvent, wrappedFunc);
setTimeout(function () {
if (!firedFunc) wrappedFunc();
}, maxMSTillTransitionEnd + 100);
};
//all allowed characters (note: you get a bizzare error in Opera and IE
//if the non-digit characters are at the end for some reason)..
var allChars = ', . - + 0 1 2 3 4 5 6 7 8 9';
//checks that the given value makes sense to use..
var checkValue = function (str) {
//check there are no odd chars first..
for (var i = 0, len = str.length; i < len; i++) {
if (allChars.indexOf(str.charAt(i)) < 0) {
$.error("numberAnimate plugin requires that value used " +
"only contain character in: \"" + allChars + "\"");
return false;
}
}
return true;
};
//Given a div which holder a character, it shift it to the required character,
//note, the givenholder div should be attached prior to calling this for the animation
//to take effect..
var shiftToChar = function ($holderDiv, character, shiftTime) {
var innerStyle = $holderDiv.children()[0].style;
innerStyle[css3Prefix + 'Transition'] = "all " + shiftTime + "ms ease-in-out";
var indexOfChar = allChars.indexOf(character);
var transformY;
if (indexOfChar < 0 || /\s/.test(character)) {
transformY = $holderDiv.height();
} else {
transformY = 0 - (indexOfChar / 2) * $holderDiv.height();
}
innerStyle[css3Prefix + 'Transform'] = translateOpen + transformY + translateClose;
};
//Function to create a new character wrapper div to wrap the given character
//setting the holding div to have the given dimension and given "position".
//You should attach the element returned by this function to the DOM straight
//away in order for the animation to take effect..
//The animationTimes is an array of milliseconds which defines: creation,
//shift and remove times..
var createDivForChar = function (character, height, width, position, animationTimes) {
var creationTime = animationTimes[0];
var shiftTime = animationTimes[1];
var holderDiv = $(document.createElement('div')).css({
width: (creationTime ? 0 : width) + 'px',
height: height + 'px',
overflow: 'hidden',
display: 'inline-block'
}).attr("data-numberAnimate-pos", position);
var innerDiv = $(document.createElement('div')).html(allChars);
//fix annoying flickering for older webkit browsers..
if (css3Prefix === 'Webkit')
innerDiv[0].style['-webkit-backface-visibility'] = 'hidden';
//initially show blank..
innerDiv[0].style[css3Prefix + 'Transform'] = translateOpen + height + translateClose;
holderDiv.append(innerDiv);
//animate to the correct character when finished animating creation if necessary..
var shiftToCorrectChar = function () {
shiftToChar(holderDiv, character, shiftTime);
};
//shift if after creation and after attachment if animating..
if (creationTime) {
//bit of a hack - transition will only work if the element is attached to the DOM
//so use a timeout to make this possible (no onattached event)..
setTimeout(function () {
bindToTransitionEndForSingleRun(holderDiv, shiftToCorrectChar, creationTime);
var holderStyle = holderDiv[0].style;
holderStyle[css3Prefix + 'Transition'] = "all " + creationTime + "ms ease-in-out";
holderStyle.width = width + "px";
}, 20);
} else if (shiftTime) {
setTimeout(shiftToCorrectChar, 20);
} else {
shiftToCorrectChar();
}
return holderDiv[0];
};
//Removes the elements in thegiven jQuery collection using animation..
var removeDivsForChars = function ($divs, animationTimes) {
var shiftTime = animationTimes[1];
var removeTime = animationTimes[2];
$divs.removeAttr("data-numberAnimate-pos");
$divs.each(function (i, div) {
var $div = $(div);
var style = div.style;
//then remove it..
var animateRemoval = function () {
style[css3Prefix + 'Transition'] = "all " + removeTime + "ms ease-in-out";
style.width = "1px";
bindToTransitionEndForSingleRun($div, function () {
$div.remove();
}, removeTime);
};
if (shiftTime) {
bindToTransitionEndForSingleRun($div, animateRemoval, shiftTime);
} else {
animateRemoval();
}
//first move it so that the no break space is showing..
shiftToChar($div, 'not there', shiftTime);
});
};
var methods = {
init: function (options) {
var settings = $.extend({}, {
animationTimes: [500, 500, 500] //creation, animation, removal ms
}, options);
this.css('display', 'inline-block'); //otherwise height/width calculated incorrectly..
$.each(this, function () {
var $this = $(this);
//get initial value and set it as data..
var valueStr = this.innerHTML;
if (!checkValue(valueStr)) return;
$this.attr("data-numberAnimate-value", valueStr);
if (!animationSupported) return; //do nothing..
//get width of a single character (assume mono-spaced font)..
$this.html("1");
var characterWidth = $this.width();
var characterHeight = $this.height();
$this.attr("data-numberAnimate-characterHeight", characterHeight);
$this.attr("data-numberAnimate-characterWidth", characterWidth);
$this.html("");
//required to get things to line up..
$this.css({
"vertical-align": "top",
"display": "inline-block",
"height": characterHeight + "px"
});
$this.attr("data-numberAnimate-animationTimes", "[" + settings.animationTimes + "]");
//we positionthings relative to the dot, so store it's position..
var indexOfPoint = valueStr.indexOf(".");
if (indexOfPoint < 0) indexOfPoint = valueStr.length;
//add divs representing each character..
var docFrag = document.createDocumentFragment();
for (var i = 0, len = valueStr.length; i < len; i++) {
var character = valueStr.charAt(i);
//create the divs with zero animation time..
docFrag.appendChild(
createDivForChar(character, characterHeight,
characterWidth, indexOfPoint - i, [0, 0, 0])
);
}
$this.append(docFrag); //add in one go.
});
return this;
},
/**
* Obtains the string value that is being animating for the first matched element.
*/
val: function () {
return this.attr("data-numberAnimate-value");
},
/**
* Sets the value to the new given one, using the given animationTimes if provided.
* If animationTimes are not provided the ones associated with this object are used.
*/
set: function (newValue, animationTimes) {
if (typeof newValue === 'number') //normalize to a string..
newValue = "" + newValue;
if (!animationTimes)
animationTimes = $.parseJSON(this.attr('data-numberAnimate-animationTimes'));
//get the number value and update the stored value..
if (!checkValue(newValue)) return;
this.attr("data-numberAnimate-value", newValue);
//if not animating just change the value..
if (!animationSupported) {
this.html(newValue);
return;
}
//work out which characters are required relative to the dot..
var indexOfPoint = newValue.indexOf(".");
if (indexOfPoint < 0) indexOfPoint = newValue.length;
$.each(this, function () {
var $this = $(this);
var numberHolderDivs = $this.find("[data-numberAnimate-pos]");
var characterHeight = $this.attr('data-numberAnimate-characterHeight') * 1;
var characterWidth = $this.attr('data-numberAnimate-characterWidth') * 1;
//if new characters are required, this will be set to one of the newly created ones..
var newlyCreatedHoldingDiv;
//add/remove those at the start..
var largestCurrentPos = numberHolderDivs.attr('data-numberAnimate-pos') * 1;
if (isNaN(largestCurrentPos)) largestCurrentPos = 0;
var largestRequiredPos = indexOfPoint;
var docFragment, pos, character, index;
if (largestCurrentPos < largestRequiredPos) {
docFragment = document.createDocumentFragment();
for (pos = largestRequiredPos, index = 0;
pos >= largestCurrentPos + 1; pos--, index++) {
character = newValue.charAt(index);
docFragment.appendChild(
createDivForChar(character, characterHeight,
characterWidth, pos, animationTimes)
);
}
newlyCreatedHoldingDiv = docFragment.firstChild;
$this.prepend(docFragment);
} else if (largestCurrentPos > largestRequiredPos) {
removeDivsForChars(
numberHolderDivs.slice(0, largestCurrentPos - largestRequiredPos),
animationTimes
);
}
//add/remove at the end of the list..
var smallestCurrentPos = numberHolderDivs.last()
.attr('data-numberAnimate-pos') * 1;
if (isNaN(smallestCurrentPos)) smallestCurrentPos = 1;
var smallestRequiredPos = indexOfPoint - newValue.length + 1;
if (smallestRequiredPos < smallestCurrentPos) {
docFragment = document.createDocumentFragment();
for (pos = smallestCurrentPos - 1,
index = newValue.length - (smallestCurrentPos - smallestRequiredPos);
pos >= smallestRequiredPos; pos--, index++) {
character = newValue.charAt(index);
docFragment.appendChild(
createDivForChar(character, characterHeight,
characterWidth, pos, animationTimes)
);
}
newlyCreatedHoldingDiv = docFragment.firstChild;
$this.append(docFragment);
} else if (smallestRequiredPos > smallestCurrentPos) {
removeDivsForChars(
numberHolderDivs.slice(
numberHolderDivs.length - (smallestRequiredPos - smallestCurrentPos)
),
animationTimes
);
}
//performs the animation of the characters that are already there..
var shiftPresentCharacters = function () {
var shiftTime = animationTimes[1];
pos = Math.min(largestRequiredPos, largestCurrentPos);
var endPos = Math.max(smallestRequiredPos, smallestCurrentPos);
index = indexOfPoint - pos;
for (; pos >= endPos; pos--, index++) {
character = newValue.charAt(index);
var holdingDiv = $this.find("[data-numberAnimate-pos=" + pos + "]");
shiftToChar(holdingDiv, character, shiftTime);
}
};
//execute above function straight away or once the newly created holding div has finished animating..
if (newlyCreatedHoldingDiv) {
bindToTransitionEndForSingleRun(
$(newlyCreatedHoldingDiv), shiftPresentCharacters, animationTimes[0] + 100);
} else {
shiftPresentCharacters();
}
});
return this;
},
/**
* Undoes the changes made by this plugin to the selected elements.
*/
destroy: function () {
$.each(this, function () {
var $this = $(this);
var value = $this.numberAnimate('val');
if (value === null) return; //continue
$this.html(value);
//remove attributes that may have been added - code adapted from:
//cletus's answer for: http://stackoverflow.com/questions/1870441/remove-all-attributes
var attributesToRemove = $.map(this.attributes, function (attr) {
var name = attr.name;
return name.indexOf('data-numberanimate') === 0 ? name : null;
});
$this.removeAttr(attributesToRemove.join(' '));
});
return this;
}
};
$.fn.numberAnimate = function (method) {
// Method calling logic (adapted from http://docs.jquery.com/Plugins/Authoring)..
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.numberAnimate');
}
};
}));