/** * jquery roundabout - v2.4.2 * http://fredhq.com/projects/roundabout * * moves list-items of enabled ordered and unordered lists long * a chosen path. includes the default "lazysusan" path, that * moves items long a spinning turntable. * * terms of use // jquery roundabout * * open source under the bsd license * * copyright (c) 2011-2012, fred leblanc * all rights reserved. * * redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * - neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * this software is provided by the copyright holders and contributors "as is" * and any express or implied warranties, including, but not limited to, the * implied warranties of merchantability and fitness for a particular purpose * are disclaimed. in no event shall the copyright holder or contributors be * liable for any direct, indirect, incidental, special, exemplary, or * consequential damages (including, but not limited to, procurement of * substitute goods or services; loss of use, data, or profits; or business * interruption) however caused and on any theory of liability, whether in * contract, strict liability, or tort (including negligence or otherwise) * arising in any way out of the use of this software, even if advised of the * possibility of such damage. */ (function($) { "use strict"; var defaults, internaldata, methods; // add default shape $.extend({ roundaboutshapes: { def: "lazysusan", lazysusan: function (r, a, t) { return { x: math.sin(r + a), y: (math.sin(r + 3 * math.pi / 2 + a) / 8) * t, z: (math.cos(r + a) + 1) / 2, scale: (math.sin(r + math.pi / 2 + a) / 2) + 0.5 }; } } }); defaults = { bearing: 0.0, tilt: 0.0, minz: 100, maxz: 280, minopacity: 0.4, maxopacity: 1.0, minscale: 0.4, maxscale: 1.0, duration: 600, btnnext: null, btnnextcallback: function() {}, btnprev: null, btnprevcallback: function() {}, btntoggleautoplay: null, btnstartautoplay: null, btnstopautoplay: null, easing: "swing", clicktofocus: true, clicktofocuscallback: function() {}, focusbearing: 0.0, shape: "lazysusan", debug: false, childselector: "li", startingchild: null, reflect: false, floatcomparisonthreshold: 0.001, autoplay: true, autoplayduration: 3000, autoplaypauseonhover: false, autoplaycallback: function() {}, autoplayinitialdelay: 0, enabledrag: false, dropduration: 600, dropeasing: "swing", dropanimateto: "nearest", dropcallback: function() {}, dragaxis: "x", dragfactor: 4, triggerfocusevents: true, triggerblurevents: true, responsive: false }; internaldata = { autoplayinterval: null, autoplayisrunning: false, autoplaystarttimeout: null, animating: false, childinfocus: -1, touchmovestartposition: null, stopanimation: false, lastanimationstep: false }; methods = { // starters // ----------------------------------------------------------------------- // init // starts up roundabout init: function(options, callback, relayout) { var settings, now = (new date()).gettime(); options = (typeof options === "object") ? options : {}; callback = ($.isfunction(callback)) ? callback : function() {}; callback = ($.isfunction(options)) ? options : callback; settings = $.extend({}, defaults, options, internaldata); return this .each(function() { // make options var self = $(this), childcount = self.children(settings.childselector).length, period = 360.0 / childcount, startingchild = (settings.startingchild && settings.startingchild > (childcount - 1)) ? (childcount - 1) : settings.startingchild, startbearing = (settings.startingchild === null) ? settings.bearing : 360 - (startingchild * period), holdercssposition = (self.css("position") !== "static") ? self.css("position") : "relative"; self .css({ // starting styles padding: 0, position: holdercssposition }) .addclass("roundabout-holder") .data( // starting options "roundabout", $.extend( {}, settings, { startingchild: startingchild, bearing: startbearing, oppositeoffocusbearing: methods.normalize.apply(null, [settings.focusbearing - 180]), dragbearing: startbearing, period: period } ) ); // unbind any events that we set if we're relaying out if (relayout) { self .unbind(".roundabout") .children(settings.childselector) .unbind(".roundabout"); } else { // bind responsive action if (settings.responsive) { $(window).bind("resize", function() { methods.stopautoplay.apply(self); methods.relayoutchildren.apply(self); }); } } // bind click-to-focus if (settings.clicktofocus) { self .children(settings.childselector) .each(function(i) { $(this) .bind("click.roundabout", function() { var degrees = methods.getplacement.apply(self, [i]); if (!methods.isinfocus.apply(self, [degrees])) { methods.stopanimation.apply($(this)); if (!self.data("roundabout").animating) { methods.animatebearingtofocus.apply(self, [degrees, self.data("roundabout").clicktofocuscallback]); } return false; } }); }); } // bind next buttons if (settings.btnnext) { $(settings.btnnext) .bind("click.roundabout", function() { if (!self.data("roundabout").animating) { methods.animatetonextchild.apply(self, [self.data("roundabout").btnnextcallback]); } return false; }); } // bind previous buttons if (settings.btnprev) { $(settings.btnprev) .bind("click.roundabout", function() { methods.animatetopreviouschild.apply(self, [self.data("roundabout").btnprevcallback]); return false; }); } // bind toggle autoplay buttons if (settings.btntoggleautoplay) { $(settings.btntoggleautoplay) .bind("click.roundabout", function() { methods.toggleautoplay.apply(self); return false; }); } // bind start autoplay buttons if (settings.btnstartautoplay) { $(settings.btnstartautoplay) .bind("click.roundabout", function() { methods.startautoplay.apply(self); return false; }); } // bind stop autoplay buttons if (settings.btnstopautoplay) { $(settings.btnstopautoplay) .bind("click.roundabout", function() { methods.stopautoplay.apply(self); return false; }); } // autoplay pause on hover if (settings.autoplaypauseonhover) { self .bind("mouseenter.roundabout.autoplay", function() { methods.stopautoplay.apply(self, [true]); }) .bind("mouseleave.roundabout.autoplay", function() { methods.startautoplay.apply(self); }); } // drag and drop if (settings.enabledrag) { // on screen if (!$.isfunction(self.drag)) { if (settings.debug) { alert("you do not have the drag plugin loaded."); } } else if (!$.isfunction(self.drop)) { if (settings.debug) { alert("you do not have the drop plugin loaded."); } } else { self .drag(function(e, properties) { var data = self.data("roundabout"), delta = (data.dragaxis.tolowercase() === "x") ? "deltax" : "deltay"; methods.stopanimation.apply(self); methods.setbearing.apply(self, [data.dragbearing + properties[delta] / data.dragfactor]); }) .drop(function(e) { var data = self.data("roundabout"), method = methods.getanimatetomethod(data.dropanimateto); methods.allowanimation.apply(self); methods[method].apply(self, [data.dropduration, data.dropeasing, data.dropcallback]); data.dragbearing = data.period * methods.getnearestchild.apply(self); }); } // on mobile self .each(function() { var element = $(this).get(0), data = $(this).data("roundabout"), page = (data.dragaxis.tolowercase() === "x") ? "pagex" : "pagey", method = methods.getanimatetomethod(data.dropanimateto); // some versions of ie don't like this if (element.addeventlistener) { element.addeventlistener("touchstart", function(e) { data.touchmovestartposition = e.touches[0][page]; }, false); element.addeventlistener("touchmove", function(e) { var delta = (e.touches[0][page] - data.touchmovestartposition) / data.dragfactor; e.preventdefault(); methods.stopanimation.apply($(this)); methods.setbearing.apply($(this), [data.dragbearing + delta]); }, false); element.addeventlistener("touchend", function(e) { e.preventdefault(); methods.allowanimation.apply($(this)); method = methods.getanimatetomethod(data.dropanimateto); methods[method].apply($(this), [data.dropduration, data.dropeasing, data.dropcallback]); data.dragbearing = data.period * methods.getnearestchild.apply($(this)); }, false); } }); } // start children methods.initchildren.apply(self, [callback, relayout]); }); }, // initchildren // applys settings to child elements, starts roundabout initchildren: function(callback, relayout) { var self = $(this), data = self.data("roundabout"); callback = callback || function() {}; self.children(data.childselector).each(function(i) { var startwidth, startheight, startfontsize, degrees = methods.getplacement.apply(self, [i]); // on relayout, grab these values from current data if (relayout && $(this).data("roundabout")) { startwidth = $(this).data("roundabout").startwidth; startheight = $(this).data("roundabout").startheight; startfontsize = $(this).data("roundabout").startfontsize; } // apply classes and css first $(this) .addclass("roundabout-moveable-item") .css("position", "absolute"); // now measure $(this) .data( "roundabout", { startwidth: startwidth || $(this).width(), startheight: startheight || $(this).height(), startfontsize: startfontsize || parseint($(this).css("font-size"), 10), degrees: degrees, backdegrees: methods.normalize.apply(null, [degrees - 180]), childnumber: i, currentscale: 1, parent: self } ); }); methods.updatechildren.apply(self); // start autoplay if necessary if (data.autoplay) { data.autoplaystarttimeout = settimeout(function() { methods.startautoplay.apply(self); }, data.autoplayinitialdelay); } self.trigger('ready'); callback.apply(self); return self; }, // positioning // ----------------------------------------------------------------------- // updatechildren // move children elements into their proper locations updatechildren: function() { return this .each(function() { var self = $(this), data = self.data("roundabout"), infocus = -1, info = { bearing: data.bearing, tilt: data.tilt, stage: { width: math.floor($(this).width() * 0.9), height: math.floor($(this).height() * 0.9) }, animating: data.animating, infocus: data.childinfocus, focusbearingradian: methods.degtorad.apply(null, [data.focusbearing]), shape: $.roundaboutshapes[data.shape] || $.roundaboutshapes[$.roundaboutshapes.def] }; // calculations info.midstage = { width: info.stage.width / 2, height: info.stage.height / 2 }; info.nudge = { width: info.midstage.width + (info.stage.width * 0.05), height: info.midstage.height + (info.stage.height * 0.05) }; info.zvalues = { min: data.minz, max: data.maxz, diff: data.maxz - data.minz }; info.opacity = { min: data.minopacity, max: data.maxopacity, diff: data.maxopacity - data.minopacity }; info.scale = { min: data.minscale, max: data.maxscale, diff: data.maxscale - data.minscale }; // update child positions self.children(data.childselector) .each(function(i) { if (methods.updatechild.apply(self, [$(this), info, i, function() { $(this).trigger('ready'); }]) && (!info.animating || data.lastanimationstep)) { infocus = i; $(this).addclass("roundabout-in-focus"); } else { $(this).removeclass("roundabout-in-focus"); } }); if (infocus !== info.infocus) { // blur old child if (data.triggerblurevents) { self.children(data.childselector) .eq(info.infocus) .trigger("blur"); } data.childinfocus = infocus; if (data.triggerfocusevents && infocus !== -1) { // focus new child self.children(data.childselector) .eq(infocus) .trigger("focus"); } } self.trigger("childrenupdated"); }); }, // updatechild // repositions a child element into its new position updatechild: function(childelement, info, childpos, callback) { var factors, self = this, child = $(childelement), data = child.data("roundabout"), out = [], rad = methods.degtorad.apply(null, [(360.0 - data.degrees) + info.bearing]); callback = callback || function() {}; // adjust radians to be between 0 and math.pi * 2 rad = methods.normalizerad.apply(null, [rad]); // get factors from shape factors = info.shape(rad, info.focusbearingradian, info.tilt); // correct factors.scale = (factors.scale > 1) ? 1 : factors.scale; factors.adjustedscale = (info.scale.min + (info.scale.diff * factors.scale)).tofixed(4); factors.width = (factors.adjustedscale * data.startwidth).tofixed(4); factors.height = (factors.adjustedscale * data.startheight).tofixed(4); // update item child .css({ left: ((factors.x * info.midstage.width + info.nudge.width) - factors.width / 2.0).tofixed(0) + "px", top: ((factors.y * info.midstage.height + info.nudge.height) - factors.height / 2.0).tofixed(0) + "px", width: factors.width + "px", height: factors.height + "px", opacity: (info.opacity.min + (info.opacity.diff * factors.scale)).tofixed(2), zindex: math.round(info.zvalues.min + (info.zvalues.diff * factors.z)), fontsize: (factors.adjustedscale * data.startfontsize).tofixed(1) + "px" }); data.currentscale = factors.adjustedscale; // for debugging purposes if (self.data("roundabout").debug) { out.push("
"); out.push("child " + childpos + "
"); out.push("left: " + child.css("left") + "
"); out.push("top: " + child.css("top") + "
"); out.push("width: " + child.css("width") + "
"); out.push("opacity: " + child.css("opacity") + "
"); out.push("height: " + child.css("height") + "
"); out.push("z-index: " + child.css("z-index") + "
"); out.push("font-size: " + child.css("font-size") + "
"); out.push("scale: " + child.data("roundabout").currentscale); out.push("
"); child.html(out.join("")); } // trigger event child.trigger("reposition"); // callback callback.apply(self); return methods.isinfocus.apply(self, [data.degrees]); }, // manipulation // ----------------------------------------------------------------------- // setbearing // changes the bearing of the roundabout setbearing: function(bearing, callback) { callback = callback || function() {}; bearing = methods.normalize.apply(null, [bearing]); this .each(function() { var diff, lowervalue, highervalue, self = $(this), data = self.data("roundabout"), oldbearing = data.bearing; // set bearing data.bearing = bearing; self.trigger("bearingset"); methods.updatechildren.apply(self); // not animating? we're done here diff = math.abs(oldbearing - bearing); if (!data.animating || diff > 180) { return; } // check to see if any of the children went through the back diff = math.abs(oldbearing - bearing); self.children(data.childselector).each(function(i) { var eventtype; if (methods.ischildbackdegreesbetween.apply($(this), [bearing, oldbearing])) { eventtype = (oldbearing > bearing) ? "clockwise" : "counterclockwise"; $(this).trigger("move" + eventtype + "throughback"); } }); }); // call callback if one was given callback.apply(this); return this; }, // adjustbearing // change the bearing of the roundabout by a given degree adjustbearing: function(delta, callback) { callback = callback || function() {}; if (delta === 0) { return this; } this .each(function() { methods.setbearing.apply($(this), [$(this).data("roundabout").bearing + delta]); }); callback.apply(this); return this; }, // settilt // changes the tilt of the roundabout settilt: function(tilt, callback) { callback = callback || function() {}; this .each(function() { $(this).data("roundabout").tilt = tilt; methods.updatechildren.apply($(this)); }); // call callback if one was given callback.apply(this); return this; }, // adjusttilt // changes the tilt of the roundabout adjusttilt: function(delta, callback) { callback = callback || function() {}; this .each(function() { methods.settilt.apply($(this), [$(this).data("roundabout").tilt + delta]); }); callback.apply(this); return this; }, // animation // ----------------------------------------------------------------------- // animatetobearing // animates the roundabout to a given bearing, all animations come through here animatetobearing: function(bearing, duration, easing, passeddata, callback) { var now = (new date()).gettime(); callback = callback || function() {}; // find callback function in arguments if ($.isfunction(passeddata)) { callback = passeddata; passeddata = null; } else if ($.isfunction(easing)) { callback = easing; easing = null; } else if ($.isfunction(duration)) { callback = duration; duration = null; } this .each(function() { var timer, easingfn, newbearing, self = $(this), data = self.data("roundabout"), thisduration = (!duration) ? data.duration : duration, thiseasingtype = (easing) ? easing : data.easing || "swing"; // is this your first time? if (!passeddata) { passeddata = { timerstart: now, start: data.bearing, totaltime: thisduration }; } // update the timer timer = now - passeddata.timerstart; if (data.stopanimation) { methods.allowanimation.apply(self); data.animating = false; return; } // we need to animate more if (timer < thisduration) { if (!data.animating) { self.trigger("animationstart"); } data.animating = true; if (typeof $.easing.def === "string") { easingfn = $.easing[thiseasingtype] || $.easing[$.easing.def]; newbearing = easingfn(null, timer, passeddata.start, bearing - passeddata.start, passeddata.totaltime); } else { newbearing = $.easing[thiseasingtype]((timer / passeddata.totaltime), timer, passeddata.start, bearing - passeddata.start, passeddata.totaltime); } // fixes issue #24, animation changed as of jquery 1.7.2 // also addresses issue #29, using easing breaks "linear" if (methods.compareversions.apply(null, [$().jquery, "1.7.2"]) >= 0 && !($.easing["easeoutback"])) { newbearing = passeddata.start + ((bearing - passeddata.start) * newbearing); } newbearing = methods.normalize.apply(null, [newbearing]); data.dragbearing = newbearing; methods.setbearing.apply(self, [newbearing, function() { settimeout(function() { // done with a timeout so that each step is displayed methods.animatetobearing.apply(self, [bearing, thisduration, thiseasingtype, passeddata, callback]); }, 0); }]); // we're done animating } else { data.lastanimationstep = true; bearing = methods.normalize.apply(null, [bearing]); methods.setbearing.apply(self, [bearing, function() { self.trigger("animationend"); }]); data.animating = false; data.lastanimationstep = false; data.dragbearing = bearing; callback.apply(self); } }); return this; }, // animatetonearbychild // animates roundabout to a nearby child animatetonearbychild: function(passedargs, which) { var duration = passedargs[0], easing = passedargs[1], callback = passedargs[2] || function() {}; // find callback if ($.isfunction(easing)) { callback = easing; easing = null; } else if ($.isfunction(duration)) { callback = duration; duration = null; } return this .each(function() { var j, range, self = $(this), data = self.data("roundabout"), bearing = (!data.reflect) ? data.bearing % 360 : data.bearing, length = self.children(data.childselector).length; if (!data.animating) { // reflecting, not moving to previous || not reflecting, moving to next if ((data.reflect && which === "previous") || (!data.reflect && which === "next")) { // slightly adjust for rounding issues bearing = (math.abs(bearing) < data.floatcomparisonthreshold) ? 360 : bearing; // clockwise for (j = 0; j < length; j += 1) { range = { lower: (data.period * j), upper: (data.period * (j + 1)) }; range.upper = (j === length - 1) ? 360 : range.upper; if (bearing <= math.ceil(range.upper) && bearing >= math.floor(range.lower)) { if (length === 2 && bearing === 360) { methods.animatetodelta.apply(self, [-180, duration, easing, callback]); } else { methods.animatebearingtofocus.apply(self, [range.lower, duration, easing, callback]); } break; } } } else { // slightly adjust for rounding issues bearing = (math.abs(bearing) < data.floatcomparisonthreshold || 360 - math.abs(bearing) < data.floatcomparisonthreshold) ? 0 : bearing; // counterclockwise for (j = length - 1; j >= 0; j -= 1) { range = { lower: data.period * j, upper: data.period * (j + 1) }; range.upper = (j === length - 1) ? 360 : range.upper; if (bearing >= math.floor(range.lower) && bearing < math.ceil(range.upper)) { if (length === 2 && bearing === 360) { methods.animatetodelta.apply(self, [180, duration, easing, callback]); } else { methods.animatebearingtofocus.apply(self, [range.upper, duration, easing, callback]); } break; } } } } }); }, // animatetonearestchild // animates roundabout to the nearest child animatetonearestchild: function(duration, easing, callback) { callback = callback || function() {}; // find callback if ($.isfunction(easing)) { callback = easing; easing = null; } else if ($.isfunction(duration)) { callback = duration; duration = null; } return this .each(function() { var nearest = methods.getnearestchild.apply($(this)); methods.animatetochild.apply($(this), [nearest, duration, easing, callback]); }); }, // animatetochild // animates roundabout to a given child position animatetochild: function(childposition, duration, easing, callback) { callback = callback || function() {}; // find callback if ($.isfunction(easing)) { callback = easing; easing = null; } else if ($.isfunction(duration)) { callback = duration; duration = null; } return this .each(function() { var child, self = $(this), data = self.data("roundabout"); if (data.childinfocus !== childposition && !data.animating) { child = self.children(data.childselector).eq(childposition); methods.animatebearingtofocus.apply(self, [child.data("roundabout").degrees, duration, easing, callback]); } }); }, // animatetonextchild // animates roundabout to the next child animatetonextchild: function(duration, easing, callback) { return methods.animatetonearbychild.apply(this, [arguments, "next"]); }, // animatetopreviouschild // animates roundabout to the preious child animatetopreviouschild: function(duration, easing, callback) { return methods.animatetonearbychild.apply(this, [arguments, "previous"]); }, // animatetodelta // animates roundabout to a given delta (in degrees) animatetodelta: function(degrees, duration, easing, callback) { callback = callback || function() {}; // find callback if ($.isfunction(easing)) { callback = easing; easing = null; } else if ($.isfunction(duration)) { callback = duration; duration = null; } return this .each(function() { var delta = $(this).data("roundabout").bearing + degrees; methods.animatetobearing.apply($(this), [delta, duration, easing, callback]); }); }, // animatebearingtofocus // animates roundabout to bring a given angle into focus animatebearingtofocus: function(degrees, duration, easing, callback) { callback = callback || function() {}; // find callback if ($.isfunction(easing)) { callback = easing; easing = null; } else if ($.isfunction(duration)) { callback = duration; duration = null; } return this .each(function() { var delta = $(this).data("roundabout").bearing - degrees; delta = (math.abs(360 - delta) < math.abs(delta)) ? 360 - delta : -delta; delta = (delta > 180) ? -(360 - delta) : delta; if (delta !== 0) { methods.animatetodelta.apply($(this), [delta, duration, easing, callback]); } }); }, // stopanimation // if an animation is currently in progress, stop it stopanimation: function() { return this .each(function() { $(this).data("roundabout").stopanimation = true; }); }, // allowanimation // clears the stop-animation hold placed by stopanimation allowanimation: function() { return this .each(function() { $(this).data("roundabout").stopanimation = false; }); }, // autoplay // ----------------------------------------------------------------------- // startautoplay // starts autoplaying this roundabout startautoplay: function(callback) { return this .each(function() { var self = $(this), data = self.data("roundabout"); callback = callback || data.autoplaycallback || function() {}; clearinterval(data.autoplayinterval); data.autoplayinterval = setinterval(function() { methods.animatetonextchild.apply(self, [callback]); }, data.autoplayduration); data.autoplayisrunning = true; self.trigger("autoplaystart"); }); }, // stopautoplay // stops autoplaying this roundabout stopautoplay: function(keepautoplaybindings) { return this .each(function() { clearinterval($(this).data("roundabout").autoplayinterval); $(this).data("roundabout").autoplayinterval = null; $(this).data("roundabout").autoplayisrunning = false; // this will prevent autoplaypauseonhover from restarting autoplay if (!keepautoplaybindings) { $(this).unbind(".autoplay"); } $(this).trigger("autoplaystop"); }); }, // toggleautoplay // toggles autoplay pause/resume toggleautoplay: function(callback) { return this .each(function() { var self = $(this), data = self.data("roundabout"); callback = callback || data.autoplaycallback || function() {}; if (!methods.isautoplaying.apply($(this))) { methods.startautoplay.apply($(this), [callback]); } else { methods.stopautoplay.apply($(this), [callback]); } }); }, // isautoplaying // is this roundabout currently autoplaying? isautoplaying: function() { return (this.data("roundabout").autoplayisrunning); }, // changeautoplayduration // stops the autoplay, changes the duration, restarts autoplay changeautoplayduration: function(duration) { return this .each(function() { var self = $(this), data = self.data("roundabout"); data.autoplayduration = duration; if (methods.isautoplaying.apply(self)) { methods.stopautoplay.apply(self); settimeout(function() { methods.startautoplay.apply(self); }, 10); } }); }, // helpers // ----------------------------------------------------------------------- // normalize // regulates degrees to be >= 0.0 and < 360 normalize: function(degrees) { var inrange = degrees % 360.0; return (inrange < 0) ? 360 + inrange : inrange; }, // normalizerad // regulates radians to be >= 0 and < math.pi * 2 normalizerad: function(radians) { while (radians < 0) { radians += (math.pi * 2); } while (radians > (math.pi * 2)) { radians -= (math.pi * 2); } return radians; }, // ischildbackdegreesbetween // checks that a given child's backdegrees is between two values ischildbackdegreesbetween: function(value1, value2) { var backdegrees = $(this).data("roundabout").backdegrees; if (value1 > value2) { return (backdegrees >= value2 && backdegrees < value1); } else { return (backdegrees < value2 && backdegrees >= value1); } }, // getanimatetomethod // takes a user-entered option and maps it to an animation method getanimatetomethod: function(effect) { effect = effect.tolowercase(); if (effect === "next") { return "animatetonextchild"; } else if (effect === "previous") { return "animatetopreviouschild"; } // default selection return "animatetonearestchild"; }, // relayoutchildren // lays out children again with new contextual information relayoutchildren: function() { return this .each(function() { var self = $(this), settings = $.extend({}, self.data("roundabout")); settings.startingchild = self.data("roundabout").childinfocus; methods.init.apply(self, [settings, null, true]); }); }, // getnearestchild // gets the nearest child from the current bearing getnearestchild: function() { var self = $(this), data = self.data("roundabout"), length = self.children(data.childselector).length; if (!data.reflect) { return ((length) - (math.round(data.bearing / data.period) % length)) % length; } else { return (math.round(data.bearing / data.period) % length); } }, // degtorad // converts degrees to radians degtorad: function(degrees) { return methods.normalize.apply(null, [degrees]) * math.pi / 180.0; }, // getplacement // returns the starting degree for a given child getplacement: function(child) { var data = this.data("roundabout"); return (!data.reflect) ? 360.0 - (data.period * child) : data.period * child; }, // isinfocus // is this roundabout currently in focus? isinfocus: function(degrees) { var diff, self = this, data = self.data("roundabout"), bearing = methods.normalize.apply(null, [data.bearing]); degrees = methods.normalize.apply(null, [degrees]); diff = math.abs(bearing - degrees); // this calculation gives a bit of room for javascript float rounding // errors, it looks on both 0deg and 360deg ends of the spectrum return (diff <= data.floatcomparisonthreshold || diff >= 360 - data.floatcomparisonthreshold); }, // getchildinfocus // returns the current child in focus, or false if none are in focus getchildinfocus: function() { var data = $(this).data("roundabout"); return (data.childinfocus > -1) ? data.childinfocus : false; }, // compareversions // compares a given version string with another compareversions: function(baseversion, compareversion) { var i, base = baseversion.split(/\./i), compare = compareversion.split(/\./i), maxversionsegmentlength = (base.length > compare.length) ? base.length : compare.length; for (i = 0; i <= maxversionsegmentlength; i++) { if (base[i] && !compare[i] && parseint(base[i], 10) !== 0) { // base is higher return 1; } else if (compare[i] && !base[i] && parseint(compare[i], 10) !== 0) { // compare is higher return -1; } else if (base[i] === compare[i]) { // these are the same, next continue; } if (base[i] && compare[i]) { if (parseint(base[i], 10) > parseint(compare[i], 10)) { // base is higher return 1; } else { // compare is higher return -1; } } } // nothing was triggered, versions are the same return 0; } }; // start the plugin $.fn.roundabout = function(method) { if (methods[method]) { return methods[method].apply(this, array.prototype.slice.call(arguments, 1)); } else if (typeof method === "object" || $.isfunction(method) || !method) { return methods.init.apply(this, arguments); } else { $.error("method " + method + " does not exist for jquery.roundabout."); } }; })(jquery);