674 lines
20 KiB
JavaScript
674 lines
20 KiB
JavaScript
/**
|
|
* @author Alexander Farkas
|
|
* v. 1.22
|
|
*/
|
|
(function ($)
|
|
{
|
|
if (!document.defaultView || !document.defaultView.getComputedStyle)
|
|
{ // IE6-IE8
|
|
var oldCurCSS = $.curCSS;
|
|
$.curCSS = function (elem, name, force)
|
|
{
|
|
if (name === 'background-position')
|
|
{
|
|
name = 'backgroundPosition';
|
|
}
|
|
if (name !== 'backgroundPosition' || !elem.currentStyle || elem.currentStyle[name])
|
|
{
|
|
return oldCurCSS.apply(this, arguments);
|
|
}
|
|
var style = elem.style;
|
|
if (!force && style && style[name])
|
|
{
|
|
return style[name];
|
|
}
|
|
return oldCurCSS(elem, 'backgroundPositionX', force) + ' ' + oldCurCSS(elem, 'backgroundPositionY', force);
|
|
};
|
|
}
|
|
|
|
var oldAnim = $.fn.animate;
|
|
$.fn.animate = function (prop)
|
|
{
|
|
if ('background-position' in prop)
|
|
{
|
|
prop.backgroundPosition = prop['background-position'];
|
|
delete prop['background-position'];
|
|
}
|
|
if ('backgroundPosition' in prop)
|
|
{
|
|
prop.backgroundPosition = '(' + prop.backgroundPosition;
|
|
}
|
|
return oldAnim.apply(this, arguments);
|
|
};
|
|
|
|
function toArray(strg)
|
|
{
|
|
strg = strg.replace(/left|top/g, '0px');
|
|
strg = strg.replace(/right|bottom/g, '100%');
|
|
strg = strg.replace(/([0-9\.]+)(\s|\)|$)/g, "$1px$2");
|
|
var res = strg.match(/(-?[0-9\.]+)(px|\%|em|pt)\s(-?[0-9\.]+)(px|\%|em|pt)/);
|
|
return [parseFloat(res[1], 10), res[2], parseFloat(res[3], 10), res[3]];
|
|
}
|
|
|
|
$.fx.step.backgroundPosition = function (fx)
|
|
{
|
|
if (!fx.bgPosReady)
|
|
{
|
|
var start = $.curCSS(fx.elem, 'backgroundPosition');
|
|
if (!start)
|
|
{//FF2 no inline-style fallback
|
|
start = '0px 0px';
|
|
}
|
|
|
|
start = toArray(start);
|
|
fx.start = [start[0], start[2]];
|
|
var end = toArray(fx.end);
|
|
fx.end = [end[0], end[2]];
|
|
|
|
fx.unit = [end[1], end[3]];
|
|
fx.bgPosReady = true;
|
|
}
|
|
//return;
|
|
var nowPosX = [];
|
|
nowPosX[0] = ((fx.end[0] - fx.start[0]) * fx.pos) + fx.start[0] + fx.unit[0];
|
|
nowPosX[1] = ((fx.end[1] - fx.start[1]) * fx.pos) + fx.start[1] + fx.unit[1];
|
|
fx.elem.style.backgroundPosition = nowPosX[0] + ' ' + nowPosX[1];
|
|
|
|
};
|
|
})(jQuery);
|
|
|
|
|
|
|
|
/*
|
|
tlrkSlider
|
|
|
|
example usage:
|
|
|
|
$("#slider").tlrkSlider({
|
|
autoStart: false,
|
|
elements: {
|
|
"img": {delay: 10},
|
|
"h2": {delay: 500},
|
|
".copy": {delay: 800},
|
|
".button": {delay: 1000}
|
|
}
|
|
});
|
|
|
|
to go to a specific frame:
|
|
$("#slider").tlrkSlider("go", position);
|
|
"position" can have one of the following values:
|
|
"next", "prev", "first", "last", "+1", "-1" or a numeric value
|
|
|
|
to start/stop the automatic loop:
|
|
$("#slider").tlrkSlider("start");
|
|
$("#slider").tlrkSlider("stop");
|
|
|
|
to change the delay between automatic transitions:
|
|
$("#slider").tlrkSlider("option", "delayAnimation", 1000);
|
|
|
|
to change any option:
|
|
$("#slider").tlrkSlider("option", option_name, option_value);
|
|
|
|
Changing the "elements" object is not tested.
|
|
|
|
Changing the following options: "navigation", "navigationClass", "framesSelector", "autoStart" won't have any effect for now.
|
|
They are used only during the initialization.
|
|
|
|
$("#slider").data("tlrkSlider") will return the plugin instance and the methods/properties can be accessed from there.
|
|
|
|
The plugin contructor defaults are accessable through TlrkSlider.defaults
|
|
|
|
The function that actually sweep the elements in/out can be overriden from
|
|
TlrkSlider.prototype._animationIn and TlrkSlider.prototype._animationOut
|
|
|
|
See sweepIn/sweepOut
|
|
|
|
*/
|
|
|
|
|
|
|
|
;(function( $, window, document, undefined ){
|
|
|
|
// utility function that generates the "dots" navigation
|
|
function generateNavigation($el, count, config) {
|
|
var i, html = "",
|
|
width = count * 24;
|
|
|
|
html += "<ol class='" + config.navigationClass + "' style='margin-left: -" + width/2 + "px; width: " + width + "px'>";
|
|
for (i = 0; i < count; i++) {
|
|
html += "<li><a " + (i === 0 ? "class='selected'" : "" ) + " href='#" + (i) + "'>slide</a></li>";
|
|
}
|
|
html += "</ol>";
|
|
|
|
$el.append(html);
|
|
}
|
|
|
|
function sweepOut($el, windowWidth) {
|
|
var dfr = $.Deferred(),
|
|
pos = $el.position(),
|
|
width = $el.width(),
|
|
delta, final,
|
|
options = $el.data("tlrkAnimOptions");
|
|
|
|
windowWidth = windowWidth || $(window).width(); // check if the windowWidth is passed, if not - get it
|
|
|
|
delta = windowWidth - pos.left;
|
|
final = -(delta);
|
|
|
|
setTimeout(function(){
|
|
$el.animate({left: final, opacity: "toggle"}, options.speed, options.easing, function(){
|
|
dfr.resolve();
|
|
});
|
|
}, options.delay);
|
|
|
|
return dfr.promise();
|
|
}
|
|
|
|
function sweepIn($el, windowWidth, frameLeft) {
|
|
var dfr = $.Deferred(),
|
|
options = $el.data("tlrkAnimOptions"),
|
|
positionData = $el.data("tlrkOriginalPos"),
|
|
final = positionData.position.left,
|
|
rightEdge;
|
|
|
|
windowWidth = windowWidth || $(window).width(); // check if the windowWidth is passed, if not - get it
|
|
|
|
$el.css({opacity: 0, display: "block"}); // move it outside the right edge of the screen
|
|
$el.css("left", function(current){
|
|
return current + windowWidth - frameLeft;
|
|
});
|
|
|
|
setTimeout(function(){
|
|
$el.animate({left: final, opacity: 1}, options.speed, options.easing, function(){
|
|
dfr.resolve();
|
|
});
|
|
}, options.delay);
|
|
|
|
return dfr.promise();
|
|
}
|
|
|
|
|
|
// two pass function that first iterates all the elements and gets their position/width/height
|
|
// and then sets their position to absolute
|
|
function absolutize($elements) {
|
|
|
|
// todo - move it to separate function and do it just once
|
|
// gather the original position/dimension data for all elements
|
|
$elements.each(function(){
|
|
var $t = $(this);
|
|
|
|
if ($t.data("tlrkOriginalPos")) return
|
|
|
|
$t.data("tlrkOriginalPos", {
|
|
position: $t.position(),
|
|
width: $t.width(),
|
|
height: $t.height(),
|
|
css_pos: $t.css("position"),
|
|
css_left: $t.css("left"),
|
|
css_top: $t.css("top"),
|
|
css_width: $t.css("width") || "auto",
|
|
css_height: $t.css("height") || "auto"
|
|
});
|
|
|
|
});
|
|
|
|
// set the absolute position
|
|
$elements.each(function(){
|
|
var $t = $(this),
|
|
opos = $t.data("tlrkOriginalPos");
|
|
|
|
$t.css({
|
|
position: "absolute",
|
|
left: opos.position.left,
|
|
top: opos.position.top,
|
|
width: opos.width,
|
|
height: opos.height
|
|
});
|
|
});
|
|
}
|
|
|
|
function restoreFrameElements($elements) {
|
|
$elements.each(function(){
|
|
var $t = $(this),
|
|
opos = $t.data("tlrkOriginalPos");
|
|
|
|
if (!opos) return
|
|
|
|
$t.css({
|
|
position: opos.css_pos,
|
|
left: opos.css_left,
|
|
top: opos.css_top,
|
|
width: opos.css_width,
|
|
height: opos.css_height
|
|
});
|
|
});
|
|
|
|
}
|
|
|
|
var TlrkSlider = function( elem, options ){
|
|
this.elem = elem;
|
|
this.$elem = $(elem);
|
|
this.options = options;
|
|
};
|
|
|
|
// the plugin prototype
|
|
TlrkSlider.prototype = {
|
|
defaults: {
|
|
|
|
|
|
defaultElementOptions: {
|
|
speed: 1200,
|
|
easing: "easeInOutBack",
|
|
// interval before the element starts moving when the fadeIn/Out functions are called
|
|
// it's a good idea to give different delays for the different elements
|
|
// if all have the same delay they'll start moving all together
|
|
delay: 100
|
|
},
|
|
|
|
// dispose elements are these that are not included in the elements object
|
|
// but affect the document flow and will be fadedIn/Out
|
|
disposeDelay: 100, // delay for the dispose elements
|
|
disposeSpeed: 1000, // how quickly they'll fadeOut/In
|
|
|
|
delayBetweenTransition: 1000, // time between starting fadeOut and fadeIn
|
|
delayAnimation: 7000, // time between auto changing the current frame
|
|
|
|
loop: true, // if true when clicking next on the last frame the slider jumps to the first one
|
|
|
|
autoStart: true, // start the automatic looping through the frames on init
|
|
|
|
framesSelector: "section", // selector for the frames inside the slider
|
|
|
|
elements: {
|
|
"p": {delay: 100, speed: 1000, easing: "easeInOutBack"}
|
|
},
|
|
|
|
navigation: true, // the dots navigation on the bottom
|
|
navigationClass: "slider-nav",
|
|
|
|
// callbacks
|
|
// another way to "catch" these events is with
|
|
// $(-slider-element-).bind("animationStart")
|
|
animationStart: null,
|
|
animationEnd: null
|
|
},
|
|
|
|
init: function() {
|
|
var c, e, element, $element,
|
|
that = this,
|
|
$firstFrame;
|
|
|
|
c = this.config = $.extend({}, this.defaults, this.options);
|
|
|
|
this.elem.style.position = "relative"; // make the wrapping element relative
|
|
|
|
// basics
|
|
this.$frames = this.$elem.find(c.framesSelector);
|
|
this.framesCount = this.$frames.length;
|
|
this.currentFrame = 0;
|
|
this.queue = [];
|
|
|
|
this._$elementsByFrame = {};
|
|
this._$disposeElementsByFrame = {};
|
|
|
|
for (i = 0; i < this.framesCount; i++) {
|
|
this._$elementsByFrame[i] = this._getFrameElements(i); // cache the $elements by frame
|
|
this._$disposeElementsByFrame[i] = this._getDisposeFrameElements(i); // cache the rest of the tree for each frame
|
|
}
|
|
|
|
if (c.navigation) {
|
|
generateNavigation(this.$elem, this.framesCount, c);
|
|
this.$navigation = this.$elem.find("."+c.navigationClass);
|
|
}
|
|
|
|
// bindings
|
|
this.$elem.find(".slider-nav").delegate("a", "click", function(e){
|
|
var frame = this.getAttribute("href").split("#")[1];
|
|
that.go.call(that, frame);
|
|
return false;
|
|
});
|
|
|
|
this.$elem // internal bindings for the callbacks
|
|
.bind("animationStart", function(){
|
|
if ($.isFunction(c.animationStart)) {c.animationStart.apply(that, arguments);}
|
|
})
|
|
.bind("animationEnd", function(){
|
|
if ($.isFunction(c.animationEnd)) {c.animationEnd.apply(that, arguments);}
|
|
})
|
|
;
|
|
|
|
// start animation?
|
|
if (c.autoStart) {
|
|
this.start();
|
|
} else {
|
|
this.running = false;
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
start: function(instant) {
|
|
var that = this;
|
|
|
|
if (this.timer) { // we'll clear the current timer
|
|
window.clearTimeout(this.timer);
|
|
}
|
|
|
|
this.running = true;
|
|
|
|
if (instant) {
|
|
that.nextFrame();
|
|
} else {
|
|
this.timer = window.setTimeout(function(){ that.nextFrame.call(that) }, that.config.delayAnimation);
|
|
}
|
|
},
|
|
|
|
stop: function() {
|
|
if (!this.running) return; // we are not running
|
|
|
|
this.running = false;
|
|
window.clearTimeout(this.timer);
|
|
},
|
|
|
|
// main function for changing frames
|
|
selectFrame: function(frame, dfr) {
|
|
var c = this.config, // shorthand for the config
|
|
that = this,
|
|
dfr = dfr || $.Deferred(),
|
|
dFadeIn = $.Deferred(),
|
|
dFadeOut = $.Deferred();
|
|
|
|
if (isNaN(frame) || frame < 0 || frame > this.framesCount || frame === this.currentFrame) {
|
|
dfr.reject();
|
|
return dfr.promise();
|
|
}
|
|
|
|
// clear the animation loop interval if the animation is running
|
|
if (this.running && this.timer) {
|
|
window.clearTimeout(this.timer);
|
|
}
|
|
|
|
// check if we are currenly running an animation.
|
|
if (this.animated && this.queue.length > 0) {
|
|
// wait for the last item in the queue to finish
|
|
this.queue[this.queue.length-1].done(function(){
|
|
that.selectFrame(frame, dfr); // and call again the selectFrame
|
|
})
|
|
return dfr.promise();
|
|
}
|
|
|
|
this.animated = true;
|
|
this.$elem.trigger("animationStart", [this, frame]);
|
|
|
|
this.queue.push(dfr);
|
|
|
|
// fade the frames
|
|
dFadeOut = this._fadeOutFrame(this.currentFrame);
|
|
|
|
// hide the fadetout frame
|
|
dFadeOut.done(function(){
|
|
that.$frames.eq(that.currentFrame).hide();
|
|
});
|
|
|
|
window.setTimeout(function(){ // then wait delayBetweenTransition and fadeIn the new frame
|
|
dFadeIn = that._fadeInFrame.call(that, frame).done(function(){
|
|
// when both the fadeIn and fadeOut are done we'll resolve the selectFrame promise
|
|
$.when(dFadeOut, dFadeIn).done(function(){
|
|
that.animated = false;
|
|
that.queue.shift();
|
|
that.$elem.trigger("animationEnd", [that]);
|
|
that.currentFrame = frame;
|
|
dfr.resolve();
|
|
});
|
|
});
|
|
}, c.delayBetweenTransition);
|
|
|
|
// navigation html change
|
|
if (this.config.navigation) {
|
|
this.$navigation.find(".selected").removeClass("selected").end()
|
|
.find("a").eq(frame).addClass("selected");
|
|
}
|
|
|
|
dfr.done(function(){ // we'll resume the loop animation after the transitions are done
|
|
if (that.running) {
|
|
that.start();
|
|
}
|
|
});
|
|
|
|
return dfr.promise();
|
|
},
|
|
|
|
_fadeFrame: function(frame, callback, direction) {
|
|
var dfr = $.Deferred(),
|
|
$frame = this.$frames.eq(frame),
|
|
$elements = this._$elementsByFrame[frame],
|
|
windowWidth = $(window).width(), // cache it before the animations, so we don't have to check it for each element
|
|
i, len,
|
|
that = this,
|
|
elementAnimations = [],
|
|
$disposeElements = this._$disposeElementsByFrame[frame],
|
|
$affectedElements,
|
|
frameLeft = $frame.offset().left;
|
|
|
|
direction = direction || "out";
|
|
|
|
if (!$.isFunction(callback)) return; // do nothing if there's no callback passed
|
|
|
|
$affectedElements = $elements.add($disposeElements);
|
|
|
|
// position absolute the animation and dispose elements
|
|
absolutize($affectedElements);
|
|
|
|
// toggle the dispose elements
|
|
if ($disposeElements.length > 0) {
|
|
window.setTimeout(function(){
|
|
$disposeElements[direction === "out" ? "fadeOut" : "fadeIn"](that.config.disposeSpeed);
|
|
}, this.config.disposeDelay);
|
|
}
|
|
|
|
// invoke the callback for each element
|
|
// the callback must return a promise
|
|
$elements.each(function(){
|
|
elementAnimations.push( callback.call(that, $(this), windowWidth, frameLeft) );
|
|
});
|
|
|
|
// wait for all the elements to finish their animation
|
|
$.when.apply(this, elementAnimations).done(function(){
|
|
//restoreFrameElements($affectedElements); // and restore the elements' position
|
|
dfr.resolve(); // resolve the fade function
|
|
});
|
|
|
|
return dfr.promise();
|
|
},
|
|
|
|
_fadeOutFrame: function(frame) {
|
|
var dfr = $.Deferred(),
|
|
$frame = this.$frames.eq(frame),
|
|
$disposeElements = this._$disposeElementsByFrame[frame];
|
|
|
|
this._fadeFrame(frame, this._animationOut, "out").done(function(){
|
|
dfr.resolve();
|
|
})
|
|
|
|
return dfr.promise();
|
|
},
|
|
|
|
_fadeInFrame: function(frame) {
|
|
var dfr = $.Deferred(),
|
|
$frame = this.$frames.eq(frame),
|
|
$elements = this._$elementsByFrame[frame];
|
|
|
|
this._restoreFrame(frame);
|
|
|
|
$frame.show();
|
|
|
|
this._fadeFrame(frame, this._animationIn, "in").done(function(){
|
|
dfr.resolve();
|
|
});
|
|
|
|
return dfr.promise();
|
|
},
|
|
|
|
_restoreFrame: function(frame){
|
|
if (!frame) return
|
|
restoreFrameElements( this._$elementsByFrame[frame].add(this._$disposeElementsByFrame[frame]) );
|
|
},
|
|
|
|
nextFrame: function() {
|
|
var frame = this.currentFrame+1,
|
|
dfr = $.Deferred();
|
|
|
|
if (frame > this.framesCount-1) {
|
|
if (this.config.loop) {
|
|
frame = 0;
|
|
} else {
|
|
dfr.reject();
|
|
}
|
|
};
|
|
|
|
this.selectFrame(frame).done(function(){
|
|
dfr.resolve();
|
|
});
|
|
|
|
return dfr.promise();
|
|
},
|
|
|
|
prevFrame: function() {
|
|
var frame = this.currentFrame-1,
|
|
dfr = $.Deferred();
|
|
|
|
if (frame < 0) {
|
|
if (this.config.loop) {
|
|
frame = this.framesCount-1;
|
|
} else {
|
|
dfr.reject();
|
|
return dfr.promise();
|
|
}
|
|
}
|
|
|
|
this.selectFrame(frame).done(function(){
|
|
dfr.resolve();
|
|
});
|
|
|
|
return dfr.promise();
|
|
},
|
|
|
|
go: function(str) { // shorthand
|
|
switch (str) {
|
|
case "next":
|
|
case "+1":
|
|
this.nextFrame();
|
|
break;
|
|
|
|
case "prev":
|
|
case "-1":
|
|
this.prevFrame();
|
|
break;
|
|
|
|
case "first":
|
|
this.selectFrame(0);
|
|
break;
|
|
|
|
case "last":
|
|
this.selectFrame(this.framesCount-1);
|
|
break;
|
|
|
|
default:
|
|
if (isNaN(str)) return;
|
|
this.selectFrame(Number(str));
|
|
}
|
|
},
|
|
|
|
// returns jquery collection of animation elements
|
|
_getFrameElements: function(frame) {
|
|
var $frame = this.$frames.eq(frame),
|
|
elements = this.config.elements,
|
|
e, elementOptions,
|
|
$found, $frameElements = $([]);
|
|
|
|
for (e in elements) {
|
|
elementOptions = elements[e];
|
|
$found = $frame.find(e);
|
|
$found.addClass("t-frame-element").data("tlrkAnimOptions", $.extend({}, this.defaults.defaultElementOptions, elementOptions ));
|
|
$frameElements = $frameElements.add($found);
|
|
}
|
|
|
|
return $frameElements;
|
|
},
|
|
|
|
// returns jquery collection of elements that have to be faded out
|
|
// i.e. elements on the same level as the animation elements
|
|
// that doesn't contain other animation elements
|
|
_getDisposeFrameElements: function(frame) {
|
|
var $disposeElements = $([]),
|
|
$frame = this.$frames.eq(frame),
|
|
$elements = this._$elementsByFrame[frame];
|
|
|
|
$elements.each(function(){
|
|
var $t = $(this),
|
|
$siblings = $t.siblings().not(".t-frame-element");
|
|
|
|
$siblings.each(function(){
|
|
var $t = $(this);
|
|
// check if the node is not already marked and doesn't contains other frame elements
|
|
if (!$t.hasClass("t-frame-dispose") && $t.find(".t-frame-element").length === 0) {
|
|
$t.addClass("t-frame-dispose");
|
|
$disposeElements = $disposeElements.add($t);
|
|
}
|
|
});
|
|
|
|
});
|
|
return $disposeElements;
|
|
},
|
|
|
|
|
|
// expose the internal animationIn/Out functions that are called for each element in the frame
|
|
// two arguments are passed - the $element which have to be animated and the window width
|
|
_animationIn: sweepIn,
|
|
_animationOut: sweepOut
|
|
|
|
}
|
|
|
|
TlrkSlider.defaults = TlrkSlider.prototype.defaults;
|
|
|
|
$.fn.tlrkSlider = function(options) {
|
|
var otherArgs = Array.prototype.slice.call(arguments, 1);
|
|
|
|
return this.each(function() {
|
|
var $el = $(this),
|
|
pluginData = $el.data("tlrkSlider");
|
|
|
|
if (!pluginData) { // check if the slider is already attached
|
|
pluginData = new TlrkSlider(this, options).init();
|
|
$el.data("tlrkSlider", pluginData);
|
|
return;
|
|
}
|
|
|
|
//change the options or call a method
|
|
if (typeof options === "string") {
|
|
|
|
// setting / getting option(s)
|
|
if (options === "option") {
|
|
|
|
if (typeof otherArgs[0] === "string" && typeof otherArgs[1] !== "undefined") { // set an option value
|
|
pluginData.config[otherArgs[0]] = otherArgs[1];
|
|
}
|
|
|
|
if (typeof otherArgs[0] === "object") { // extend the config with new options
|
|
pluginData.config = $.extend(pluginData.config, otherArgs[0]);
|
|
}
|
|
|
|
} else { // call a method?
|
|
try {
|
|
pluginData[options].apply(pluginData, otherArgs);
|
|
} catch(ex) {
|
|
throw "Error calling a plugin method (" + ex + ")";
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
window.TlrkSlider = TlrkSlider;
|
|
|
|
})( jQuery, window , document ); |