PageRenderTime 55ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 1ms

/scripts/jquery.sequence.js

https://github.com/smobleyii/Sequence
JavaScript | 1298 lines | 968 code | 172 blank | 158 comment | 208 complexity | 216e40c23b1e28c9f6314bd2a999f4a8 MD5 | raw file
  1. /*
  2. Sequence.js (http://www.sequencejs.com)
  3. Version: 1.0.1.1
  4. Author: Ian Lunn @IanLunn
  5. Author URL: http://www.ianlunn.co.uk/
  6. Github: https://github.com/IanLunn/Sequence
  7. This is a FREE script and is available under a MIT License:
  8. http://www.opensource.org/licenses/mit-license.php
  9. Sequence.js and its dependencies are (c) Ian Lunn Design 2012 - 2013 unless otherwise stated.
  10. Sequence also relies on the following open source scripts:
  11. - jQuery imagesLoaded 2.1.0 (http://github.com/desandro/imagesloaded)
  12. Paul Irish et al
  13. Available under a MIT License: http://www.opensource.org/licenses/mit-license.php
  14. - jQuery TouchWipe 1.1.1 (http://www.netcu.de/jquery-touchwipe-iphone-ipad-library)
  15. Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
  16. Available under a MIT License: http://www.opensource.org/licenses/mit-license.php
  17. - Modernizr 2.6.1 Custom Build (http://modernizr.com/) (Named Modernizr for Sequence to prevent conflicts)
  18. Copyright (c) Faruk Ates, Paul Irish, Alex Sexton
  19. Available under the BSD and MIT licenses: www.modernizr.com/license/
  20. */
  21. ;(function($) {
  22. var windowLoaded = false;
  23. $(window).bind("load", function() {
  24. windowLoaded = true;
  25. });
  26. function Sequence(element, options, defaults, get) {
  27. var self = this;
  28. self.container = $(element); //the Sequence containing element
  29. self.canvas = self.container.children('.sequence-canvas'); //the Sequence canvas which holds Sequence's frames (<li> elements)
  30. self.frames = self.canvas.children('li'); //the Sequence frames (top level <li> elements within the Sequence canvas)
  31. self._modernizrForSequence(); //get the custom build necessary for Sequence
  32. var prefixes = { //convert JS transition/animation names to CSS names
  33. 'WebkitTransition' : '-webkit-',
  34. 'WebkitAnimation' : '-webkit-',
  35. 'MozTransition' : '-moz-',
  36. 'MozAnimation ' : '-moz-',
  37. 'OTransition' : '-o-',
  38. 'OAnimation' : '-o-',
  39. 'msTransition' : '-ms-',
  40. 'msAnimation' : '-ms-',
  41. 'transition' : '',
  42. 'animation' : ''
  43. };
  44. var transitionsAndAnimations = { //convert JS transition names to JS transition end and animation end event names (also apply a classname of .sequence to the event)
  45. 'WebkitTransition' : 'webkitTransitionEnd.sequence',
  46. 'WebkitAnimation' : 'webkitAnimationEnd.sequence',
  47. 'MozTransition' : 'transitionend.sequence',
  48. 'MozAnimation' : 'animationend.sequence',
  49. 'OTransition' : 'otransitionend.sequence',
  50. 'OAnimation' : 'oanimationend.sequence',
  51. 'msTransition' : 'MSTransitionEnd.sequence',
  52. 'msAnimation' : 'MSAnimationEnd.sequence',
  53. 'transition' : 'transitionend.sequence',
  54. 'animation' : 'animationend.sequence'
  55. };
  56. self.transitionPrefix = prefixes[ModernizrForSequence.prefixed('transition')], //work out the CSS transition prefix for the browser being used (-webkit- for example)
  57. self.animationPrefix = prefixes[ModernizrForSequence.prefixed('animation')], //work out the CSS animation prefix for the browser being used
  58. self.transitionProperties = {},
  59. self.transitionEnd = transitionsAndAnimations[ModernizrForSequence.prefixed('transition')] + ' ' + transitionsAndAnimations[ModernizrForSequence.prefixed('animation')], //work out the JS transitionEnd name for the browser being used (webkitTransitionEnd webkitAnimationEnd for example)
  60. self.numberOfFrames = self.frames.length, //number of frames (<li>) Sequence consists of
  61. self.transitionsSupported = (self.transitionPrefix !== undefined) ? true : false, //determine if transitions are supported
  62. self.hasTouch = ("ontouchstart" in window) ? true : false, //determine if this is a touch enabled device
  63. self.isPaused = false, //whether Sequence is paused
  64. self.isBeingHoveredOver = false, //whether the Sequence canvas is currently being hovered over
  65. self.container.removeClass('sequence-destroyed'); //if Sequence is destroyed using .destroy(), it is given a clas of "destroy", remove that now if present
  66. //CALLBACKS
  67. self.paused = function() {}, //executes when Sequence is paused
  68. self.unpaused = function() {}, //executes when Sequence is unpaused
  69. self.beforeNextFrameAnimatesIn = function() {}, //executes before the next frame animates in
  70. self.afterNextFrameAnimatesIn = function() {}, //executes after the next frame animates in
  71. self.beforeCurrentFrameAnimatesOut = function() {}, //executes before the current frame animates out
  72. self.afterCurrentFrameAnimatesOut = function() {}, //executes after the current frame animates out
  73. self.afterLoaded = function() {}; //executes after Sequence is initiated
  74. self.destroyed = function() {}; //executes when Sequence is destroyed via the destory() function
  75. //INIT
  76. self.settings = $.extend({}, defaults, options); //combine default options with developer defined ones
  77. self.settings.preloader = self._renderUiElements(self.settings.preloader, '.sequence-preloader'); //set up the preloader and save it
  78. self.isStartingFrame = (self.settings.animateStartingFrameIn) ? true : false; //determine if the first frame should animate in
  79. self.settings.unpauseDelay = (self.settings.unpauseDelay === null) ? self.settings.autoPlayDelay : self.settings.unpauseDelay; //if the unpauseDelay is not specified, make it the same as the autoPlayDelay speed
  80. self.getHashTagFrom = (self.settings.hashDataAttribute) ? "data-sequence-hashtag": "id"; //get the hashtag from the ID or data attribute?
  81. self.frameHashID = []; //array that matches frames with has IDs
  82. self.direction = self.settings.autoPlayDirection;
  83. if(self.settings.hideFramesUntilPreloaded && self.settings.preloader !== undefined && self.settings.preloader !== false) { //if using a preloader and hiding frames until preloading has completed...
  84. self.frames.hide(); //hide Sequence's frames
  85. }
  86. if(self.transitionPrefix === "-o-") { //if Opera prefixes are required...
  87. self.transitionsSupported = self._operaTest(); //run a test to see if Opera correctly supports transitions (Opera 11 has bugs relating to transitions)
  88. }
  89. self.frames.removeClass("animate-in"); //remove any instance of "animate-in", which should be used incase JS is disabled
  90. //functionality to run once Sequence has preloaded
  91. function oncePreloaded() {
  92. self.afterLoaded(); //callback
  93. if(self.settings.hideFramesUntilPreloaded && self.settings.preloader !== undefined && self.settings.preloader !== false) {
  94. self.frames.show();
  95. }
  96. if(self.settings.preloader !== undefined && self.settings.preloader !== false){
  97. if(self.settings.hidePreloaderUsingCSS && self.transitionsSupported) {
  98. self.prependPreloadingCompleteTo = (self.settings.prependPreloadingComplete === true) ? self.settings.preloader : $(self.settings.prependPreloadingComplete);
  99. self.prependPreloadingCompleteTo.addClass("preloading-complete");
  100. setTimeout(init, self.settings.hidePreloaderDelay);
  101. }else{
  102. self.settings.preloader.fadeOut(self.settings.hidePreloaderDelay, function() {
  103. clearInterval(self.defaultPreloader);
  104. init();
  105. });
  106. }
  107. }else{
  108. init();
  109. }
  110. }
  111. var preloadTheseFramesLength = self.settings.preloadTheseFrames.length; //how many frames to preload?
  112. var preloadTheseImagesLength = self.settings.preloadTheseImages.length; //how many single images to load?
  113. function saveImagesToArray(length, srcOnly) {
  114. var imagesToPreload = []; //saves the images that are to be preloaded
  115. if(!srcOnly){
  116. for(var i = length; i > 0; i--){ //for each frame to be preloaded...
  117. self.frames.eq(self.settings.preloadTheseFrames[i-1]-1).find("img").each(function() { //find <img>'s in specific frames, and for each found...
  118. imagesToPreload.push($(this)[0]); //add it to the array of images to be preloaded
  119. });
  120. }
  121. }else{
  122. for(var j = length; j > 0; j--) { //for each frame to be preloaded...
  123. imagesToPreload.push($("body").find('img[src="'+self.settings.preloadTheseImages[j-1]+'"]')); //find any <img> with the given source and add it to the array of images to be preloaded
  124. }
  125. }
  126. return imagesToPreload;
  127. }
  128. //jQuery imagesLoaded plugin v2.1.0 (http://github.com/desandro/imagesloaded)
  129. function imagesLoaded(imagesToPreload, callback) {
  130. var BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
  131. var $this = imagesToPreload,
  132. deferred = $.isFunction($.Deferred) ? $.Deferred() : 0,
  133. hasNotify = $.isFunction(deferred.notify),
  134. $images = $this.find('img').add( $this.filter('img') ),
  135. loaded = [],
  136. proper = [],
  137. broken = [];
  138. //Register deferred callbacks
  139. if($.isPlainObject(callback)) {
  140. $.each(callback, function(key, value) {
  141. if(key === 'callback') {
  142. callback = value;
  143. }else if(deferred) {
  144. deferred[key](value);
  145. }
  146. });
  147. }
  148. function doneLoading() {
  149. var $proper = $(proper),
  150. $broken = $(broken);
  151. if(deferred) {
  152. if(broken.length) {
  153. deferred.reject($images, $proper, $broken);
  154. }else{
  155. deferred.resolve($images);
  156. }
  157. }
  158. if($.isFunction(callback)) {
  159. callback.call($this, $images, $proper, $broken);
  160. }
  161. }
  162. function imgLoaded( img, isBroken ) {
  163. if(img.src === BLANK || $.inArray(img, loaded) !== -1) { // don't proceed if BLANK image, or image is already loaded
  164. return;
  165. }
  166. loaded.push(img); // store element in loaded images array
  167. if(isBroken) { // keep track of broken and properly loaded images
  168. broken.push(img);
  169. }else{
  170. proper.push(img);
  171. }
  172. $.data(img, 'imagesLoaded', {isBroken: isBroken, src: img.src }); // cache image and its state for future calls
  173. if(hasNotify) { // trigger deferred progress method if present
  174. deferred.notifyWith($(img), [isBroken, $images, $(proper), $(broken)]);
  175. }
  176. if($images.length === loaded.length) { // call doneLoading and clean listeners if all images are loaded
  177. setTimeout(doneLoading);
  178. $images.unbind('.imagesLoaded');
  179. }
  180. }
  181. if(!$images.length) { // if no images, trigger immediately
  182. doneLoading();
  183. }else{
  184. $images.bind('load.imagesLoaded error.imagesLoaded', function(event) {
  185. imgLoaded(event.target, event.type === 'error'); // trigger imgLoaded
  186. }).each(function(i, el) {
  187. var src = el.src;
  188. var cached = $.data(el, 'imagesLoaded'); // find out if this image has been already checked for status if it was, and src has not changed, call imgLoaded on it
  189. if(cached && cached.src === src) {
  190. imgLoaded(el, cached.isBroken);
  191. return;
  192. }
  193. if(el.complete && el.naturalWidth !== undefined) { // if complete is true and browser supports natural sizes, try to check for image status manually
  194. imgLoaded(el, el.naturalWidth === 0 || el.naturalHeight === 0);
  195. return;
  196. }
  197. // cached images don't fire load sometimes, so we reset src, but only when dealing with IE, or image is complete (loaded) and failed manual check webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
  198. if(el.readyState || el.complete) {
  199. el.src = BLANK;
  200. el.src = src;
  201. }
  202. });
  203. }
  204. }
  205. if(self.settings.preloader !== undefined && self.settings.preloader !== false && (preloadTheseFramesLength !== 0 || preloadTheseImagesLength !== 0)) { //if using the preloader and the dev has specified some images should preload...
  206. var frameImagesToPreload = saveImagesToArray(preloadTheseFramesLength); //get images from particular Sequence frames to be preloaded
  207. var individualImagesToPreload = saveImagesToArray(preloadTheseImagesLength, true); //get images with specific source values to be preloaded
  208. var imagesToPreload = $(frameImagesToPreload.concat(individualImagesToPreload)); //combine frame images and individual images
  209. imagesLoaded(imagesToPreload, oncePreloaded);
  210. }else{ //if not using the preloader...
  211. if(windowLoaded === true) { //if the window has already loaded...
  212. oncePreloaded(); //run the init functionality when the preloader has finished
  213. $(this).unbind("load.sequence"); //unbind the load event as it's no longer needed
  214. }else{ //if the window hasn't already loaded...
  215. $(window).bind("load.sequence", function() { //when the window loads...
  216. oncePreloaded(); //run the init functionality when the preloader has finished
  217. $(this).unbind("load.sequence"); //unbind the load event as it's no longer needed
  218. });
  219. }
  220. }
  221. function init() {
  222. $(self.settings.preloader).remove(); //remove the preloader element
  223. self.nextButton = self._renderUiElements(self.settings.nextButton, ".sequence-next"); //set up the next button
  224. self.prevButton = self._renderUiElements(self.settings.prevButton, ".sequence-prev"); //set up the previous button
  225. self.pauseButton = self._renderUiElements(self.settings.pauseButton, ".sequence-pause"); //set up the pause button
  226. self.pagination = self._renderUiElements(self.settings.pagination, ".sequence-pagination"); //set up the pagination
  227. if((self.nextButton !== undefined && self.nextButton !== false) && self.settings.showNextButtonOnInit === true){self.nextButton.show();} //if using a next button, show it
  228. if((self.prevButton !== undefined && self.prevButton !== false) && self.settings.showPrevButtonOnInit === true){self.prevButton.show();} //if using a previous button, show it
  229. if((self.pauseButton !== undefined && self.pauseButton !== false) && self.settings.showPauseButtonOnInit === true){self.pauseButton.show();} //if using a pause button, show it
  230. if(self.settings.pauseIcon !== false) {
  231. self.pauseIcon = self._renderUiElements(self.settings.pauseIcon, ".sequence-pause-icon");
  232. if(self.pauseIcon !== undefined) {
  233. self.pauseIcon.hide();
  234. }
  235. }else{
  236. self.pauseIcon = undefined;
  237. }
  238. if(self.pagination !== undefined && self.pagination !== false) {
  239. self.paginationLinks = self.pagination.children(); //get each pagination link
  240. self.paginationLinks.on('click.sequence', function() { //when a pagination link is clicked...
  241. var associatedFrameNumber = $(this).index() + 1; //get the number of the frame this link is associated with
  242. self.goTo(associatedFrameNumber); //go to the associate frame
  243. });
  244. if(self.settings.showPaginationOnInit === true) {
  245. self.pagination.show();
  246. }
  247. }
  248. self.nextFrameID = self.settings.startingFrameID;
  249. if(self.settings.hashTags === true) { //if using hashtags...
  250. self.frames.each(function() { //for each frame...
  251. self.frameHashID.push($(this).prop(self.getHashTagFrom)); //add the hashtag to an array
  252. });
  253. self.currentHashTag = location.hash.replace("#", ""); //get the current hashtag
  254. if(self.currentHashTag === undefined || self.currentHashTag === "") { //if there is no hashtag...
  255. self.nextFrameID = self.settings.startingFrameID; //use the startingFrameID
  256. }else{
  257. self.frameHashIndex = $.inArray(self.currentHashTag, self.frameHashID); //get the index of the frame that matches the hashtag
  258. if(self.frameHashIndex !== -1){ //if the hashtag matches a Sequence frame ID...
  259. self.nextFrameID = self.frameHashIndex + 1; //use the frame associated to the hashtag
  260. }else{
  261. self.nextFrameID = self.settings.startingFrameID; //use the startingFrameID
  262. }
  263. }
  264. }
  265. self.nextFrame = self.frames.eq(self.nextFrameID-1); //get the next frame
  266. self.nextFrameChildren = self.nextFrame.children(); //get the elements within the next frame to be animated
  267. if(self.pagination !== undefined) { //if using pagination, make the starting frame the current one in pagination
  268. $(self.paginationLinks[self.settings.startingFrameID-1]).addClass('current'); //add the 'current' class to the current frame
  269. }
  270. if(self.transitionsSupported) { //initiate the full featured Sequence if transitions are supported...
  271. if(!self.settings.animateStartingFrameIn) { //start first frame in animated in position
  272. self.currentFrameID = self.nextFrameID;
  273. if(self.settings.moveActiveFrameToTop) {
  274. self.nextFrame.css('z-index', self.numberOfFrames);
  275. }
  276. self._resetElements(self.transitionPrefix, self.nextFrameChildren, "0s");
  277. self.nextFrame.addClass("animate-in");
  278. if(self.settings.hashTags && self.settings.hashChangesOnFirstFrame) {
  279. self.currentHashTag = self.nextFrame.prop(self.getHashTagFrom);
  280. document.location.hash = "#"+self.currentHashTag;
  281. }
  282. setTimeout(function() {
  283. self._resetElements(self.transitionPrefix, self.nextFrameChildren, "");
  284. }, 100);
  285. self._resetAutoPlay(true, self.settings.autoPlayDelay);
  286. }else if(self.settings.reverseAnimationsWhenNavigatingBackwards && self.settings.autoPlayDirection -1 && self.settings.animateStartingFrameIn) { //animate in backwards
  287. self._resetElements(self.transitionPrefix, self.nextFrameChildren, "0s");
  288. self.nextFrame.addClass("animate-out");
  289. self.goTo(self.nextFrameID, -1, true);
  290. }else{ //animate in forwards
  291. self.goTo(self.nextFrameID, 1, true);
  292. }
  293. }else{ //initiate a basic slider for browsers that don't support CSS3 transitions
  294. self.container.addClass("sequence-fallback");
  295. self.currentFrameID = self.nextFrameID;
  296. if(self.settings.hashTags && self.settings.hashChangesOnFirstFrame){
  297. self.currentHashTag = self.nextFrame.prop(self.getHashTagFrom);
  298. document.location.hash = "#"+self.currentHashTag;
  299. }
  300. self.frames.addClass("animate-in"); //move each frame into its animate-in position
  301. self.frames.not(':eq('+(self.nextFrameID-1)+')').css({"display": "none", "opacity": 0}); //set all frames (except the next one) to display: none, opacity: 0
  302. self._resetAutoPlay(true, self.settings.autoPlayDelay);
  303. }
  304. //END INIT
  305. //EVENTS
  306. if(self.nextButton !== undefined) { //if a next button is defined...
  307. self.nextButton.bind('click.sequence', function() { //when the next button is clicked...
  308. self.next(); //go to the next frame
  309. });
  310. }
  311. if(self.prevButton !== undefined) { //if a previous button is defined...
  312. self.prevButton.bind('click.sequence', function() { //when the previous button is clicked...
  313. self.prev(); //go to the previous frame
  314. });
  315. }
  316. if(self.pauseButton !== undefined) { //if a pause button is defined...
  317. self.pauseButton.bind('click.sequence', function() { //when the pause button is clicked...
  318. self.pause(true); //pause Sequence and set hardPause to true
  319. });
  320. }
  321. function keyEvents(keyPressed, keyDirections) {
  322. var keyCode;
  323. var keyCodes;
  324. for(keyCodes in keyDirections) {
  325. if(keyCodes === "left" || keyCodes === "right") {
  326. keyCode = defaultKeys[keyCodes];
  327. }else{
  328. keyCode = keyCodes;
  329. }
  330. if(keyPressed === parseFloat(keyCode)) { //if the key pressed is associated with a function...
  331. self._initCustomKeyEvent(keyDirections[keyCodes]); //initiate the function
  332. }
  333. }
  334. }
  335. if(self.settings.keyNavigation) {
  336. var defaultKeys = {
  337. 'left' : 37,
  338. 'right' : 39
  339. };
  340. $(document).bind('keydown.sequence', function(e) { //when a key is pressed...
  341. var keyCodeChar = String.fromCharCode(e.keyCode);
  342. if((keyCodeChar > 0 && keyCodeChar <= self.numberOfFrames) && (self.settings.numericKeysGoToFrames)) {
  343. self.nextFrameID = keyCodeChar;
  344. self.goTo(self.nextFrameID); //go to specified frame
  345. }
  346. keyEvents(e.keyCode, self.settings.keyEvents); //run default keyevents
  347. keyEvents(e.keyCode, self.settings.customKeyEvents); //run custom keyevents
  348. });
  349. }
  350. self.canvas.on({
  351. 'mouseenter.sequence': function() { //when the mouse enter the Sequence element...
  352. if(self.settings.pauseOnHover && self.settings.autoPlay && !self.hasTouch) { //if using pauseOnHover and autoPlay on non touch devices
  353. self.isBeingHoveredOver = true;
  354. if(!self.isHardPaused) { //if Sequence is hard paused (via a pause button)...
  355. self.pause(); //pause autoPlay
  356. }
  357. }
  358. },
  359. 'mouseleave.sequence': function() { //when the mouse leaves the Sequence element...
  360. if(self.settings.pauseOnHover && self.settings.autoPlay && !self.hasTouch) { //if using pauseOnHover and autoPlay on non touch devices
  361. self.isBeingHoveredOver = false;
  362. if(!self.isHardPaused) { //if Sequence is not hard paused (via a pause button)...
  363. self.unpause(); //unpause autoPlay
  364. }
  365. }
  366. }
  367. });
  368. if(self.settings.hashTags) { //if hashchange is enabled in the settings...
  369. $(window).bind('hashchange.sequence', function() { //when the hashtag changes...
  370. var newTag = location.hash.replace("#", ""); //grab the new hashtag
  371. if(self.currentHashTag !== newTag) { //if the last hashtag is not the same as the current one...
  372. self.currentHashTag = newTag; //save the new tag
  373. self.frameHashIndex = $.inArray(self.currentHashTag, self.frameHashID); //get the index of the frame that matches the hashtag
  374. if(self.frameHashIndex !== -1) { //if the hashtag matches a Sequence frame ID...
  375. self.nextFrameID = self.frameHashIndex + 1; //set that frame as the next one
  376. self.goTo(self.nextFrameID); //go to the next frame
  377. }
  378. }
  379. });
  380. }
  381. function cancelTouch() {
  382. self.canvas.on("touchmove.sequence", onTouchMove);
  383. startX = null;
  384. isMoving = false;
  385. }
  386. function onTouchMove(e) {
  387. if(self.settings.swipePreventsDefault) {
  388. e.preventDefault();
  389. }
  390. if(isMoving) {
  391. var x = e.originalEvent.touches[0].pageX;
  392. var y = e.originalEvent.touches[0].pageY;
  393. var dx = startX - x;
  394. var dy = startY - y;
  395. if(Math.abs(dx) >= self.settings.swipeThreshold) {
  396. cancelTouch();
  397. if(dx > 0) {
  398. self._initCustomKeyEvent(self.settings.swipeEvents.left);
  399. }else{
  400. self._initCustomKeyEvent(self.settings.swipeEvents.right);
  401. }
  402. }else if(Math.abs(dy) >= self.settings.swipeThreshold) {
  403. cancelTouch();
  404. if(dy > 0) {
  405. self._initCustomKeyEvent(self.settings.swipeEvents.down);
  406. }else{
  407. self._initCustomKeyEvent(self.settings.swipeEvents.up);
  408. }
  409. }
  410. }
  411. }
  412. function onTouchStart(e) {
  413. if(e.originalEvent.touches.length === 1) {
  414. startX = e.originalEvent.touches[0].pageX;
  415. startY = e.originalEvent.touches[0].pageY;
  416. isMoving = true;
  417. self.canvas.on("touchmove.sequence", onTouchMove);
  418. }
  419. }
  420. if(self.settings.swipeNavigation && self.hasTouch) { //if using swipeNavigation and the device has touch capabilities...
  421. //jQuery TouchWipe v1.1.1 (http://www.netcu.de/jquery-touchwipe-iphone-ipad-library)
  422. var startX;
  423. var startY;
  424. var isMoving = false;
  425. self.canvas.on("touchstart.sequence", onTouchStart);
  426. }
  427. //END EVENTS
  428. }
  429. } //END CONSTRUCTOR
  430. Sequence.prototype = {
  431. //PUBLIC METHODS
  432. /*
  433. start autoPlay -- causing Sequence to automatically change frame every x amount of milliseconds
  434. delay: a time in ms before starting the autoPlay feature (if unspecified, the default will be used)
  435. */
  436. startAutoPlay: function(delay) {
  437. var self = this;
  438. delay = (delay === undefined) ? self.settings.autoPlayDelay : delay; //if a delay isn't specified, use the default
  439. self.unpause();
  440. self._resetAutoPlay(); //stop autoPlay before starting it again
  441. self.autoPlayTimer = setTimeout(function() { //start a new autoPlay timer and...
  442. if(self.settings.autoPlayDirection === 1) { //go to either the next or previous frame
  443. self.next();
  444. }else{
  445. self.prev();
  446. }
  447. }, delay); //after a specified delay
  448. },
  449. //stop causing Sequence to automatically change frame every x amount of seconds
  450. stopAutoPlay: function() {
  451. var self = this;
  452. self.pause(true);
  453. clearTimeout(self.autoPlayTimer); //stop the autoPlay timer
  454. },
  455. /*
  456. Toggle startAutoPlay (unpausing autoPlay) and stopAutoPlay (pausing autoPlay)
  457. hardPause: if true, Sequence's pauseOnHover will not execute. Useful for pause buttons.
  458. Note: Sequence 0.7.3 and below didn't have an .unpause() function -- .pause() would pause/unpause
  459. based on the current state. .unpause() is now included for clarity but the .pause() function will
  460. still toggle between paused and unpaused states.
  461. */
  462. pause: function(hardPause) {
  463. var self = this;
  464. if(!self.isSoftPaused) { //if pausing Sequence...
  465. if(self.pauseButton !== undefined) { //if a pause button is defined...
  466. self.pauseButton.addClass("paused"); //add the class of "paused" to the pause button
  467. if(self.pauseIcon !== undefined) { //if a pause icon is defined...
  468. self.pauseIcon.show(); //show the pause icon
  469. }
  470. }
  471. self.paused(); //callback when Sequence is paused
  472. self.isSoftPaused = true;
  473. self.isHardPaused = (hardPause) ? true : false; //if hardPausing, set hardPause to true
  474. self.isPaused = true;
  475. self._resetAutoPlay(); //stop autoPlay
  476. }else{ //if unpausing Sequence...
  477. self.unpause();
  478. }
  479. },
  480. /*
  481. Start the autoPlay feature, as well as deal with any changes to pauseButtons, pauseIcons and public variables etc
  482. callback: if false, the unpause callback will not be initiated (this is because unpause is used internally during the stop and start of each frame)
  483. */
  484. unpause: function(callback) {
  485. var self = this;
  486. if(self.pauseButton !== undefined) { //if a pause button is defined...
  487. self.pauseButton.removeClass("paused"); //remove the class of "paused" from the pause button
  488. if(self.pauseIcon !== undefined) { //if a pause icon is defined...
  489. self.pauseIcon.hide(); //hide the pause icon
  490. }
  491. }
  492. self.isSoftPaused = false;
  493. self.isHardPaused = false;
  494. self.isPaused = false;
  495. if(!self.active) {
  496. if(callback !== false) {
  497. self.unpaused(); //callback when Sequence is unpaused
  498. }
  499. self._resetAutoPlay(true, self.settings.unpauseDelay); //start autoPlay after a delay specified via the unpauseDelay setting
  500. }else{
  501. self.delayUnpause = true; //Sequence is animating so delay the unpause event until the animation completes
  502. }
  503. },
  504. //Go to the frame ahead of the current one
  505. next: function() {
  506. var self = this;
  507. id = (self.currentFrameID !== self.numberOfFrames) ? self.currentFrameID + 1 : 1; //work out the next frame ID
  508. if(self.active === false || self.active === undefined) { //if Sequence isn't currently animating...
  509. self.goTo(id, 1); //go to the next frame
  510. }else{ //if Sequence is currently animating...
  511. self.goTo(id, 1, true); //go immediately to the next frame (ignoring the transition threshold)
  512. }
  513. },
  514. //Go to the frame prior to the current one
  515. prev: function() {
  516. var self = this;
  517. id = (self.currentFrameID === 1) ? self.numberOfFrames : self.currentFrameID - 1; //work out the prev frame ID
  518. if(self.active === false || self.active === undefined) { //if Sequence isn't currently animating...
  519. self.goTo(id, -1); //go to the prev frame
  520. }else{ //if Sequence is currently animating...
  521. self.goTo(id, -1, true); //go immediately to the prev frame (ignoring the transition threshold)
  522. }
  523. },
  524. /*
  525. Go to a specific frame
  526. id: number of the frame to go to
  527. direction: direction to get to that frame (1 = forward, -1 = reverse)
  528. ignoreTransitionThreshold: if true, ignore the transitionThreshold setting and immediately go to the specified frame
  529. */
  530. goTo: function(id, direction, ignoreTransitionThreshold) {
  531. var self = this;
  532. self.nextFrameID = parseFloat(id);
  533. var transitionThreshold = (ignoreTransitionThreshold === true) ? 0 : self.settings.transitionThreshold; //if transitionThreshold is to be ignored, set it to zero
  534. if((self.nextFrameID === self.currentFrameID) //if the next frame the user is trying to go to is the same as the currently active one...
  535. || (self.settings.navigationSkip && self.navigationSkipThresholdActive) //or navigationSkip is enabled and the navigationSkipThreshold is active (which prevents frame from being navigated too fast)...
  536. || (!self.settings.navigationSkip && self.active) //or navigationSkip is disbaled but Sequence is animating...
  537. || (!self.transitionsSupported && self.active) //or Sequence is in fallback mode and Sequence is animating...
  538. || (!self.settings.cycle && direction === 1 && self.currentFrameID === self.numberOfFrames) //or cycling is disabled, the user is navigating forward and this is the last frame...
  539. || (!self.settings.cycle && direction === -1 && self.currentFrameID === 1) //or cycling is disabled, the user is navigating backwards and this is the first frame...
  540. || (self.settings.preventReverseSkipping && self.direction !== direction && self.active)) { //or Sequence is animating and the user is trying to change the direction of navigation...
  541. return false; //don't go to another frame
  542. }else if(self.settings.navigationSkip && self.active) { //if navigationSkip is enabled and Sequence is animating (a frame is being skipped before it has finished animating)...
  543. self.navigationSkipThresholdActive = true; //the navigationSkipThreshold is now active
  544. if(self.settings.fadeFrameWhenSkipped) { //if a frame should fade when skipped...
  545. self.nextFrame.stop().animate({"opacity": 0}, self.settings.fadeFrameTime); //fade
  546. }
  547. clearTimeout(self.transitionThresholdTimer);
  548. setTimeout(function() { //start the navigationSkipThreshold timer to prevent being able to navigate too quickly
  549. self.navigationSkipThresholdActive = false; //once the timer is complete, navigationSkip can occur again
  550. }, self.settings.navigationSkipThreshold);
  551. }
  552. if(!self.active || self.settings.navigationSkip) { //if there are no animations running or navigationSkip is enabled...
  553. self.active = true; //Sequence is now animating
  554. self._resetAutoPlay(); //stop any autoPlay timer that may be running
  555. if(direction === undefined) { //if no direction to navigate was defined...
  556. self.direction = (self.nextFrameID > self.currentFrameID) ? 1 : -1; //work out which way to go based on what frame is currently active
  557. }else{
  558. self.direction = direction; //go to the developer defined frame
  559. }
  560. self.currentFrame = self.canvas.children(".animate-in"); //find which frame is active -- the frame currently being viewed (and about to be animated out)
  561. self.nextFrame = self.frames.eq(self.nextFrameID-1); //grab the next frame
  562. self.currentFrameChildren = self.currentFrame.children(); //save the child elements of the current frame
  563. self.nextFrameChildren = self.nextFrame.children(); //save the child elements of the next frame
  564. if(self.pagination !== undefined) { //if using pagination...
  565. self.paginationLinks.removeClass('current'); //remove the 'current' class from all pagination links
  566. $(self.paginationLinks[self.nextFrameID-1]).addClass('current'); //add the 'current' class to the current frame
  567. }
  568. if(self.transitionsSupported) { //if the browser supports CSS3 transitions...
  569. if(self.currentFrame.length !== undefined) { //if there is a current frame (one that is in it's animate-in position)...
  570. self.beforeCurrentFrameAnimatesOut(); //callback
  571. if(self.settings.moveActiveFrameToTop) { //if the active frame should move to the top...
  572. self.currentFrame.css("z-index", 1); //move this frame to the bottom as it is now inactive
  573. }
  574. self._resetElements(self.transitionPrefix, self.nextFrameChildren, "0s"); //give the next frame elements a transition-duration and transition-delay of 0s so they don't transition to their reset position
  575. if(!self.settings.reverseAnimationsWhenNavigatingBackwards || self.direction === 1) { //if user hit next button...
  576. self.nextFrame.removeClass("animate-out"); //reset the next frame back to its starting position
  577. self._resetElements(self.transitionPrefix, self.currentFrameChildren, ""); //remove any inline styles from the elements to be animated so styles via the "animate-out" class can take full effect
  578. }else if(self.settings.reverseAnimationsWhenNavigatingBackwards && self.direction === -1) { //if the user hit prev button
  579. self.nextFrame.addClass("animate-out"); //reset the next frame back to its animate-out position
  580. self._reverseTransitionProperties(); //reverse the transition-duration, transition-delay and transition-timing-function
  581. }
  582. }else{
  583. self.isStartingFrame = false; //no longer the first frame
  584. }
  585. self.active = true; //Sequence is now animating
  586. self.currentFrame.unbind(self.transitionEnd); //remove the animation end event
  587. self.nextFrame.unbind(self.transitionEnd); //remove the animation end event
  588. if(self.settings.fadeFrameWhenSkipped && self.settings.navigationSkip) { //if a frame may have faded out when it was previously skipped...
  589. self.nextFrame.css("opacity", 1); //show it again
  590. }
  591. self.beforeNextFrameAnimatesIn(); //callback
  592. if(self.settings.moveActiveFrameToTop) { //if an active frame should be moved to the top...
  593. self.nextFrame.css('z-index', self.numberOfFrames);
  594. }
  595. //modifications to the current and next frame's elements to get them ready to animate
  596. if(!self.settings.reverseAnimationsWhenNavigatingBackwards || self.direction === 1) { //if user hit next button...
  597. setTimeout(function() { //50ms timeout to give the browser a chance to modify the DOM sequentially
  598. self._resetElements(self.transitionPrefix, self.nextFrameChildren, ""); //remove any inline styles from the elements to be animated so styles via the "animate-in" class can take full effect
  599. self._waitForAnimationsToComplete(self.nextFrame, self.nextFrameChildren, "in"); //wait for the next frame to animate in
  600. if(self.afterCurrentFrameAnimatesOut !== "function () {}" || (self.settings.transitionThreshold === true && ignoreTransitionThreshold !== true)) { //if the afterCurrentFrameAnimatesOut is being used...
  601. self._waitForAnimationsToComplete(self.currentFrame, self.currentFrameChildren, "out", true, 1); //wait for the current frame to animate out as well
  602. }
  603. }, 50);
  604. //final class changes to make animations happen
  605. setTimeout(function() { //50ms timeout to give the browser a chance to modify the DOM sequentially
  606. if(self.settings.transitionThreshold === false || self.settings.transitionThreshold === 0 || ignoreTransitionThreshold === true) { //if not using a transitionThreshold...
  607. self.currentFrame.toggleClass("animate-out animate-in"); //remove the "animate-in" class and add the "animate-out" class to the current frame
  608. self.nextFrame.addClass("animate-in"); //add the "animate-in" class
  609. }else { //if using a transitionThreshold...
  610. self.currentFrame.toggleClass("animate-out animate-in"); //remove the "animate-in" class and add the "animate-out" class to the current frame
  611. if(self.settings.transitionThreshold !== true) { //if there's no transitionThreshold or the dev specified a transitionThreshold in milliseconds
  612. self.transitionThresholdTimer = setTimeout(function() { //cause the next frame to animate in after a certain period
  613. self.nextFrame.addClass("animate-in"); //add the "animate-in" class
  614. }, transitionThreshold);
  615. }
  616. }
  617. }, 50);
  618. }else if(self.settings.reverseAnimationsWhenNavigatingBackwards && self.direction === -1) { //if the user hit prev button
  619. setTimeout(function() { //50ms timeout to give the browser a chance to modify the DOM sequentially
  620. //remove any inline styles from the elements so styles via the "animate-in" and "animate-out" class can take full effect
  621. self._resetElements(self.transitionPrefix, self.currentFrameChildren, "");
  622. self._resetElements(self.transitionPrefix, self.nextFrameChildren, "");
  623. self._reverseTransitionProperties(); //reverse the transition-duration, transition-delay and transition-timing-function
  624. self._waitForAnimationsToComplete(self.nextFrame, self.nextFrameChildren, "in"); //wait for the next frame to animate in
  625. if(self.afterCurrentFrameAnimatesOut !== "function () {}" || (self.settings.transitionThreshold === true && ignoreTransitionThreshold !== true)) { //if the afterCurrentFrameAnimatesOut is being used...
  626. self._waitForAnimationsToComplete(self.currentFrame, self.currentFrameChildren, "out", true, -1); //wait for the current frame to animate out as well
  627. }
  628. }, 50);
  629. //final class changes to make animations happen
  630. setTimeout(function() { //50ms timeout to give the browser a chance to modify the DOM sequentially
  631. if(self.settings.transitionThreshold === false || self.settings.transitionThreshold === 0 || ignoreTransitionThreshold === true) { //if not using a transitionThreshold...
  632. self.currentFrame.removeClass("animate-in"); //remove the "animate-in" class from the current frame
  633. self.nextFrame.toggleClass("animate-out animate-in"); //add the "animate-out" class and remove the "animate-in" class from the next frame
  634. }else{ //if using a transitionThreshold...
  635. self.currentFrame.removeClass("animate-in");
  636. if(self.settings.transitionThreshold !== true) { //if there's no transitionThreshold or the dev specified a transitionThreshold in milliseconds
  637. self.transitionThresholdTimer = setTimeout(function() { //cause the next frame to animate in after a certain period
  638. self.nextFrame.toggleClass("animate-out animate-in"); //add the "animate-in" class and remove the "animate-out" class
  639. }, transitionThreshold);
  640. }
  641. }
  642. }, 50);
  643. }
  644. }else{ //if the browser doesn't support CSS3 transitions...
  645. function animationComplete() {
  646. self._setHashTag();
  647. self.active = false;
  648. self._resetAutoPlay(true, self.settings.autoPlayDelay);
  649. }
  650. switch(self.settings.fallback.theme) {
  651. case "fade": //if using the fade fallback theme...
  652. self.frames.css({"position": "relative"}); //this allows for fadein/out in IE
  653. self.beforeCurrentFrameAnimatesOut();
  654. self.currentFrame = self.frames.eq(self.currentFrameID-1);
  655. self.currentFrame.animate({"opacity": 0}, self.settings.fallback.speed, function() { //hide the current frame
  656. self.currentFrame.css({"display": "none", "z-index": "1"});
  657. self.afterCurrentFrameAnimatesOut();
  658. self.beforeNextFrameAnimatesIn();
  659. self.nextFrame.css({"display": "block", "z-index": self.numberOfFrames}).animate({"opacity": 1}, 500, function() {
  660. self.afterNextFrameAnimatesIn();
  661. }); //make the next frame the current one and show it
  662. animationComplete();
  663. });
  664. self.frames.css({"position": "relative"}); //this allows for fadein/out in IE
  665. break;
  666. case "slide": //if using the slide fallback theme...
  667. default:
  668. //create objects which will save the .css() and .animation() objects
  669. var animateOut = {};
  670. var animateIn = {};
  671. var moveIn = {};
  672. //construct the .css() and .animation() objects
  673. if(self.direction === 1) {
  674. animateOut.left = "-100%";
  675. animateIn.left = "100%";
  676. }else{
  677. animateOut.left = "100%";
  678. animateIn.left = "-100%";
  679. }
  680. moveIn.left = "0";
  681. moveIn.opacity = 1;
  682. self.currentFrame = self.frames.eq(self.currentFrameID-1);
  683. self.beforeCurrentFrameAnimatesOut();
  684. self.currentFrame.animate(animateOut, self.settings.fallback.speed, function() {
  685. self.currentFrame.css({"display": "none", "z-index": "1"});
  686. self.afterCurrentFrameAnimatesOut();
  687. }); //cause the current frame to animate out
  688. self.beforeNextFrameAnimatesIn(); //callback
  689. self.nextFrame.show().css(animateIn);
  690. self.nextFrame.css({"display": "block", "z-index": self.numberOfFrames}).animate(moveIn, self.settings.fallback.speed, function() { //cause the next frame to animate in
  691. animationComplete();
  692. self.afterNextFrameAnimatesIn(); //callback
  693. });
  694. break;
  695. }
  696. }
  697. self.currentFrameID = self.nextFrameID; //make the currentFrameID the same as the one that is to animate in
  698. }
  699. },
  700. /*
  701. removes Sequence from the element it's attached to
  702. callback: a callback to run once .destroy() has finished (or see the sequence.destroyed() callback)
  703. */
  704. destroy: function(callback) {
  705. var self = this;
  706. self.container.addClass('sequence-destroyed'); //add a class of "destroyed" in case the developer wants to animate opacity etc
  707. //REMOVE EVENTS
  708. if(self.nextButton !== undefined) { //remove the next button click event if a next button is defined
  709. self.nextButton.unbind('click.sequence');
  710. }
  711. if(self.prevButton !== undefined) { //remove the previous button click event if a previous button is defined
  712. self.prevButton.unbind('click.sequence');
  713. }
  714. if(self.pauseButton !== undefined) { //remove the pause button click event if a pause button is defined
  715. self.pauseButton.unbind('click.sequence');
  716. }
  717. if(self.pagination !== undefined) {
  718. self.paginationLinks.unbind('click.sequence');
  719. }
  720. $(document).unbind('keydown.sequence'); //unbind key events
  721. self.canvas.unbind('mouseenter.sequence, mouseleave.sequence, touchstart.sequence, touchmove.sequence'); //unbind mouse and touch events
  722. $(window).unbind('hashchange.sequence'); //unbind hashchange
  723. //CLEAR TIMERS
  724. self.stopAutoPlay();
  725. clearTimeout(self.transitionThresholdTimer);
  726. //TIDY UP THE DOM
  727. self.canvas.children('li').remove(); //because Sequence rearranges frames so the active one is always on top, remove them all...
  728. self.canvas.prepend(self.frames); //then add them back in, in their original order
  729. self.frames.removeClass('animate-in animate-out').removeAttr('style'); //remove classes and inline styles from all frames
  730. self.frames.eq(self.currentFrameID-1).addClass('animate-in'); //keep the current frame in it's animate-in position
  731. //HIDE UI ELEMENTS
  732. if(self.nextButton !== undefined && self.nextButton !== false) { //if a next button is defined and was shown on initiation, hide it
  733. self.nextButton.hide();
  734. }
  735. if(self.prevButton !== undefined && self.prevButton !== false) { //if a prev button is defined and was shown on initiation, hide it
  736. self.prevButton.hide();
  737. }
  738. if(self.pauseButton !== undefined && self.pauseButton !== false) { //if a pause button is defined and was shown on initiation, hide it
  739. self.pauseButton.hide();
  740. }
  741. if(self.pauseIcon !== undefined && self.pauseIcon !== false) { //if a pause icon is defined, hide it
  742. self.pauseIcon.hide();
  743. }
  744. if(self.pagination !== undefined && self.pagination !== false) { //if pagination is defined and was shown on initiation, hide it
  745. self.pagination.hide();
  746. }
  747. //CALLBACKS - a callback can either be passed into the destroy() function or by using the sequence.destroyed() publc method
  748. if(callback !== undefined) {
  749. callback(); //callback past into the function
  750. }
  751. self.destroyed(); //callback
  752. self.container.removeData(); //remove data
  753. },
  754. //END PUBLIC METHODS
  755. //PRIVATE METHODS
  756. //trigger keyEvents, customKeyEvents and swipeEvents
  757. _initCustomKeyEvent: function(event) {
  758. var self = this;
  759. switch(event) {
  760. case "next":
  761. self.next();
  762. break;
  763. case "prev":
  764. self.prev();
  765. break;
  766. case "pause":
  767. self.pause(true);
  768. break;
  769. }
  770. },
  771. /*
  772. reset the transition-duration and transition-delay properties of an element
  773. elementToReset = the element that is to have it's properties reset
  774. cssValue = the value to be given to the transition-duration and transition-delay properties
  775. */
  776. _resetElements: function(prefix, elementToReset, cssValue) {
  777. var self = this;
  778. elementToReset.css(
  779. self._prefixCSS(prefix, {
  780. "transition-duration": cssValue,
  781. "transition-delay": cssValue,
  782. "transition-timing-function": ""
  783. })
  784. );
  785. },
  786. /*
  787. when navigating backwards and reverseAnimationsWhenNavigatingBackwards is true, take the transition properties for forward animation and manipulate the animated elements to create a perfect reversal
  788. */
  789. _reverseTransitionProperties: function() {
  790. var self = this;
  791. var currentFrameChildrenDurations = []; //saves the duration for each of the current frame's element
  792. var nextFrameChildrenDurations = []; //saves the duration for each of the next frame's element
  793. self.currentFrameChildren.each(function() { //get the overall duration (including delay) for each animated element in the current frame
  794. currentFrameChildrenDurations.push(parseFloat($(this).css(self.transitionPrefix+'transition-duration').replace('s', '')) + parseFloat($(this).css(self.transitionPrefix+'transition-delay').replace('s', '')));
  795. });
  796. self.nextFrameChildren.each(function() { //get the overall duration (including delay) for each animated element in the current frame
  797. nextFrameChildrenDurations.push(parseFloat($(this).css(self.transitionPrefix+'transition-duration').replace('s', '')) + parseFloat($(this).css(self.transitionPrefix+'transition-delay').replace('s', '')));
  798. });
  799. var maximumCurrentFrameDuration = Math.max.apply(Math, currentFrameChildrenDurations); //find which transition duration is the longest
  800. var maximumNextFrameDuration = Math.max.apply(Math, nextFrameChildrenDurations); //find which transition duration is the longest
  801. var transitionDifference = maximumCurrentFrameDuration - maximumNextFrameDuration; //get the overal transition difference between the current and next frame
  802. var currentDelay = 0;
  803. var nextDelay = 0;
  804. if(transitionDifference < 0 && !self.settings.preventDelayWhenReversingAnimations) { //if the current frame has a greater duration than the next frame...
  805. /* note: because the current frame will take longer to animate out than the next to animate in, when this animation is reversed, the current frame will have a delay applied before it animates out. By default, Sequence will aim to avoid this (via the preventDelayWhenReversingAnimations option) because a delay on the current frame may confuse the user. The delay is removed, which means the reversal of animation is slightly out of sync */
  806. currentDelay = Math.abs(transitionDifference);
  807. }else if(transitionDifference > 0) { //if the next frame has a greater duration than the current frame, add the difference on as a delay
  808. nextDelay = Math.abs(transitionDifference);
  809. }
  810. var reverseEachProperty = function(transitionProperties, currentFrameChildren, maximumFrameDuration, frameDelay) {
  811. function convertTimingFunctionToCubicBezier(timingFunction) {
  812. timingFunction = timingFunction.split(',')[0]; //only use one timing function
  813. var timingFunctionToCubicBezier = {
  814. "linear" : "cubic-bezier(0.0,0.0,1.0,1.0)",
  815. "ease" : "cubic-bezier(0.25, 0.1, 0.25, 1.0)",
  816. "ease-in": "cubic-bezier(0.42, 0.0, 1.0, 1.0)",
  817. "ease-in-out": "cubic-bezier(0.42, 0.0, 0.58, 1.0)",
  818. "ease-out": "cubic-bezier(0.0, 0.0, 0.58, 1.0)"
  819. };
  820. if(timingFunction.indexOf("cubic-bezier") < 0) { //if the timing-function returned isn't a cubic-bezier()
  821. timingFunction = timingFunctionToCubicBezier[timingFunction]; //convert it to one
  822. }
  823. return timingFunction; //return a cubic-bezier()
  824. }
  825. currentFrameChildren.each(function() {
  826. var duration = parseFloat($(this).css(self.transitionPrefix+'transition-duration').replace('s', '')); //get the elements transition-duration
  827. var delay = parseFloat($(this).css(self.transitionPrefix+'transition-delay').replace('s', '')); //get the elements transition-delay
  828. var transitionFunction = $(this).css(self.transitionPrefix+'transition-timing-function'); //get the elements transiion-timing-function
  829. if(transitionFunction.indexOf('cubic') === -1) { //if the function isn't a cubic-bezier (the Blink engine returns keywords instead)...
  830. var transitionFunction = convertTimingFunctionToCubicBezier(transitionFunction); //convert the keyword to cubic-bezier()
  831. }
  832. var cubicBezier = transitionFunction.replace('cubic-bezier(', '').replace(')', '').split(','); //remove the CSS function and just get the array
  833. $.each(cubicBezier, function(index, value) { //for each point that makes up the cubic bezier...
  834. cubicBezier[index] = parseFloat(value); //turn the point into a number (rather than text)
  835. });
  836. //reverse the cubic bezier
  837. var reversedCubicBezier = [
  838. 1 - cubicBezier[2],
  839. 1 - cubicBezier[3],
  840. 1 - cubicBezier[0],
  841. 1 - cubicBezier[1]
  842. ];
  843. transitionFunction = 'cubic-bezier('+reversedCubicBezier+')'; //add the reversed cubic bezier back into a CSS function
  844. var frameDuration = duration + delay; //get the overall duration of the element
  845. transitionProperties["transition-duration"] = duration + 's'; //reapply the element's transition-duration (to override any inline styles)
  846. transitionProperties["transition-delay"] = (maximumFrameDuration - frameDuration + frameDelay) + 's'; //add a delay if required
  847. transitionProperties["transition-timing-function"] = transitionFunction; //reapply the reversed transition function
  848. $(this).css(
  849. self._prefixCSS(self.transitionPrefix, transitionProperties) //set the new transition properties
  850. );
  851. });
  852. };
  853. reverseEachProperty(self.transitionProperties, self.currentFrameChildren, maximumCurrentFrameDuration, currentDelay); //reverse properties for each of the current frame's elements
  854. reverseEachProperty(self.transitionProperties, self.nextFrameChildren, maximumNextFrameDuration, nextDelay); //reverse properties for each of the next frame's elements
  855. },
  856. /*
  857. adds the browser vendors prefix onto multiple CSS properties
  858. prefix = the prefix for the browser Sequence is being viewed in (-webkit- for example)
  859. properties = the properties to be prefixed (transition-duration for example)
  860. */
  861. _prefixCSS: function(prefix, properties) {
  862. var self = this;
  863. var css = {};
  864. for(var property in properties) { //for each property to be modified...
  865. css[prefix + property] = properties[property]; //add the prefix to the property name
  866. }
  867. return css; //return the prefixed CSS
  868. },
  869. /*
  870. internal function used to start and stop autoPlay
  871. start: if true, autoPlay will be started, else it'll be stopped
  872. delay: a time in ms before starting the autoPlay feature (if unspecified, the default will be used)
  873. */
  874. _resetAutoPlay: function(start, delay) {
  875. var self = this;
  876. if(start === true) { //if starting autoPlay
  877. if(self.settings.autoPlay && !self.isSoftPaused) { //if using autoPlay and Sequence isn't paused...
  878. clearTimeout(self.autoPlayTimer); //stop the autoPlay timer
  879. self.autoPlayTimer = setTimeout(function() { //start a new autoPlay timer and...
  880. if(self.settings.autoPlayDirection === 1) { //go to either the next or previous frame
  881. self.next();
  882. }else{
  883. self.prev();
  884. }
  885. }, delay); //after a specified delay
  886. }
  887. }else{ //if stopping autoPlay
  888. clearTimeout(self.autoPlayTimer); //stop the autoPlay timer
  889. }
  890. },
  891. /*functionality to initiate the preloader, next/previous buttons and so on
  892. devOption: true = the developer wants to use the default selector. false = don't use a uiElement. string = the developer defined selector to use for the UI element
  893. defaultOption: the default selector to use for the UI element, when the developer specifies false for devOption
  894. */
  895. _renderUiElements: function(devOption, defaultOption) {
  896. var self = this;
  897. switch(devOption) {
  898. case false: //don't set up a uiElement
  899. return undefined;
  900. case true: //use the default uiElement
  901. if(defaultOption === ".sequence-preloader") { //if setting up the preloader...
  902. self._defaultPreloader(self.container, self.transitionsSupported, self.animationPrefix); //get the default preloader
  903. }
  904. return $(defaultOption); //return the default element
  905. default: //if using a developer defined selector...
  906. return $(devOption); //return the developer defined element
  907. }
  908. },
  909. /*
  910. prevents the next frame from animating until the current frame has finished animating
  911. frame: the frame <li> which is animating
  912. self.currentFrameChildren: the animated direct child elements of the frame
  913. transitionPhase: whether the elements are animating "in" to an active position or "out" of an active position
  914. inAfterwards: whether the next frame should animate in afterwards
  915. direction: the direction of animation
  916. */
  917. _waitForAnimationsToComplete: function(frame, currentFrameChildren, transitionPhase, inAfterwards, direction) {
  918. var self = this;
  919. if(transitionPhase === "out") { //if waiting on a frame's element to animate out...
  920. var onceComplete = function() {
  921. self.afterCurrentFrameAnimatesOut(); //callback
  922. if(self.settings.transitionThreshold === true) {
  923. if(direction === 1) {
  924. self.nextFrame.addClass("animate-in"); //add the "animate-in" class
  925. }else if(direction === -1) {
  926. self.nextFrame.toggleClass("animate-out animate-in");
  927. }
  928. }
  929. };
  930. }else if(transitionPhase === "in") { //if waiting on a frame's element to animate in...
  931. var onceComplete = function() {
  932. self.afterNextFrameAnimatesIn(); //callback
  933. self._setHashTag(); //set the hashtag to represent the newly active frame
  934. self.active = false; //Sequence is not animating
  935. if(!self.isHardPaused && !self.isBeingHoveredOver) { //if Sequence isn't hard paused (via a pause button for example) or being hovered over...
  936. if(!self.delayUnpause) { //if unpausing isn't delayed (Sequence wasn't animating when unpause was invoked)...
  937. self.unpause(false); //unpause Sequence but don't run the unpause callback
  938. }else{ //if unpausing was delay because Sequence was animating when unpause was invoked...
  939. self.delayUnpause = false;
  940. self.unpause(); //unpause Sequence
  941. }
  942. }
  943. };
  944. }
  945. currentFrameChildren.data('animationEnded', false); // set the data attribute of each animated element to indicate that the animation has not yet ended
  946. frame.bind(self.transitionEnd, function(e) { //when an element finishes animating...
  947. $(e.target).data('animationEnded', true); // set the data attrbiute to indicate that the element has finished it's animation
  948. // now check if all elements have finished animating
  949. var allAnimationsEnded = true;
  950. currentFrameChildren.each(function() { //for each element being animated within a frame...
  951. if($(this).data('animationEnded') === false) { //if the animation hasn't ended...
  952. allAnimationsEnded = false; //not all animations have ended yet
  953. return false; //break out of the animationEnded check early
  954. }
  955. });
  956. if(allAnimationsEnded) { //if all animations have ended...
  957. frame.unbind(self.transitionEnd); //stop waiting for animations to end
  958. onceComplete();
  959. }
  960. });
  961. },
  962. _setHashTag: function() {
  963. var self = this;
  964. if(self.settings.hashTags) { //if hashTags is enabled...
  965. self.currentHashTag = self.nextFrame.prop(self.getHashTagFrom); //get the hashtag name
  966. self.frameHashIndex = $.inArray(self.currentHashTag, self.frameHashID); //get the index of the frame that matches the hashtag
  967. if(self.frameHashIndex !== -1 && (self.settings.hashChangesOnFirstFrame || (!self.isStartingFrame || !self.transitionsSupported))) { //if the hashtag matches a Sequence frame ID...
  968. self.nextFrameID = self.frameHashIndex + 1;
  969. document.location.hash = "#"+self.currentHashTag;
  970. }else{
  971. self.nextFrameID = self.settings.startingFrameID;
  972. self.isStartingFrame = false;
  973. }
  974. }
  975. },
  976. /* Modernizr 2.6.1 (Custom Build) | MIT & BSD
  977. * Build: http://modernizr.com/download/#-svg-prefixed-testprop-testallprops-domprefixes
  978. */
  979. _modernizrForSequence: function() {
  980. ;window.ModernizrForSequence=function(a,b,c){function x(a){i.cssText=a}function y(a,b){return x(prefixes.join(a+";")+(b||""))}function z(a,b){return typeof a===b}function A(a,b){return!!~(""+a).indexOf(b)}function B(a,b){for(var d in a){var e=a[d];if(!A(e,"-")&&i[e]!==c)return b=="pfx"?e:!0}return!1}function C(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:z(f,"function")?f.bind(d||b):f}return!1}function D(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+m.join(d+" ")+d).split(" ");return z(b,"string")||z(b,"undefined")?B(e,b):(e=(a+" "+n.join(d+" ")+d).split(" "),C(e,b,c))}var d="2.6.1",e={},f=b.documentElement,g="modernizrForSequence",h=b.createElement(g),i=h.style,j,k={}.toString,l="Webkit Moz O ms",m=l.split(" "),n=l.toLowerCase().split(" "),o={svg:"http://www.w3.org/2000/svg"},p={},q={},r={},s=[],t=s.slice,u,v={}.hasOwnProperty,w;!z(v,"undefined")&&!z(v.call,"undefined")?w=function(a,b){return v.call(a,b)}:w=function(a,b){return b in a&&z(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=self;if(typeof c!="function")throw new TypeError;var d=t.call(arguments,1),e=function(){if(self instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(t.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(t.call(arguments)))};return e}),p.svg=function(){return!!b.createElementNS&&!!b.createElementNS(o.svg,"svg").createSVGRect};for(var E in p)w(p,E)&&(u=E.toLowerCase(),e[u]=p[E](),s.push((e[u]?"":"no-")+u));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)w(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,enableClasses&&(f.className+=" "+(b?"":"no-")+a),e[a]=b}return e},x(""),h=j=null,e._version=d,e._domPrefixes=n,e._cssomPrefixes=m,e.testProp=function(a){return B([a])},e.testAllProps=D,e.prefixed=function(a,b,c){return b?D(a,b,c):D(a,"pfx")},e}(self,self.document);
  981. },
  982. /* Set up Sequence's default preloader
  983. * prependTo = which element to prepend the preloader to
  984. * transitions = whether CSS3 transitions are supported
  985. * prefix = the prefix for the browser Sequence is being viewed in
  986. */
  987. _defaultPreloader: function(prependTo, transitions, prefix) {
  988. var icon = '<div class="sequence-preloader"><svg class="preloading" xmlns="http://www.w3.org/2000/svg"><circle class="circle" cx="6" cy="6" r="6" /><circle class="circle" cx="22" cy="6" r="6" /><circle class="circle" cx="38" cy="6" r="6" /></svg></div>';
  989. $("head").append("<style>.sequence-preloader{height: 100%;position: absolute;width: 100%;z-index: 999999;}@"+prefix+"keyframes preload{0%{opacity: 1;}50%{opacity: 0;}100%{opacity: 1;}}.sequence-preloader .preloading .circle{fill: #ff9442;display: inline-block;height: 12px;position: relative;top: -50%;width: 12px;"+prefix+"animation: preload 1s infinite; animation: preload 1s infinite;}.preloading{display:block;height: 12px;margin: 0 auto;top: 50%;margin-top:-6px;position: relative;width: 48px;}.sequence-preloader .preloading .circle:nth-child(2){"+prefix+"animation-delay: .15s; animation-delay: .15s;}.sequence-preloader .preloading .circle:nth-child(3){"+prefix+"animation-delay: .3s; animation-delay: .3s;}.preloading-complete{opacity: 0;visibility: hidden;"+prefix+"transition-duration: 1s; transition-duration: 1s;}div.inline{background-color: #ff9442; margin-right: 4px; float: left;}</style>");
  990. prependTo.prepend(icon);
  991. if(!ModernizrForSequence.svg && !transitions) { //if SVG isn't supported, remain calm and add this fallback instead...
  992. $(".sequence-preloader").prepend('<div class="preloading"><div class="circle inline"></div><div class="circle inline"></div><div class="circle inline"></div></div>');
  993. setInterval(function() {
  994. $(".sequence-preloader .circle").fadeToggle(500);
  995. }, 500);
  996. }else if(!transitions) { //if transitions aren't supported, toggle the opacity instead
  997. setInterval(function() {
  998. $(".sequence-preloader").fadeToggle(500);
  999. }, 500);
  1000. }
  1001. },
  1002. //a quick test to work out if Opera supports transitions properly (to work around the fact that Opera 11 supports transitions but doesn't return a transition value properly)
  1003. _operaTest: function() {
  1004. $("body").append('<span id="sequence-opera-test"></span>');
  1005. var $operaTest = $("#sequence-opera-test");
  1006. $operaTest.css("-o-transition", "1s"); //add a transition value to the Opera test
  1007. if($operaTest.css("-o-transition") !== "1s") { //if the expected value isn't returned...
  1008. $operaTest.remove();
  1009. return false; //cause Opera to go into the fallback theme
  1010. }else{
  1011. $operaTest.remove();
  1012. return true;
  1013. }
  1014. }
  1015. //END PRIVATE METHODS
  1016. }; //END PROTOTYPE
  1017. var defaults = {
  1018. //General Settings
  1019. startingFrameID: 1, //The frame (the list item `<li>`) that should first be displayed when Sequence loads
  1020. cycle: true, //Whether Sequence should navigate to the first frame after the last frame and vice versa
  1021. animateStartingFrameIn: false, //Whether the first frame should animate in to its active position
  1022. transitionThreshold: false, //The delay between a frame animating out and the next animating in (false = no delay, true = the next frame will animate in only once the current frame has animated out)
  1023. reverseAnimationsWhenNavigatingBackwards: true, //Whether animations should be reversed when a user navigates backwards by clicking a previous button/swiping/pressing the left key
  1024. preventDelayWhenReversingAnimations: false, //Whether a delay should be removed when animations are reversed. This delay is removed by default to prevent user confusion
  1025. moveActiveFrameToTop: true, //Whether a frame should be given a higher `z-index` than other frames whilst it is active, to bring it above the others
  1026. //Autoplay Settings
  1027. autoPlay: false, //Cause Sequence to automatically change between frames over a period of time, as defined in autoPlayDelay
  1028. autoPlayDirection: 1, //The direction in which Sequence should auto play
  1029. autoPlayDelay: 5000, //The duration in milliseconds at which frames should remain on screen before animating to the next
  1030. //Frame Skipping Settings
  1031. navigationSkip: true, //Whether the user can navigate through frames before each frame has finished animating
  1032. navigationSkipThreshold: 250, //Amount of time that must pass before the next frame can be navigated to
  1033. fadeFrameWhenSkipped: true, //If a frame is skipped before it finishes animating, it will quickly fade out
  1034. fadeFrameTime: 150, //If fadeFrameWhenSkipped is true, how quickly a frame should fade out when skipped (in milliseconds)
  1035. preventReverseSkipping: false, //Whether the user can change the direction of navigation during frames animating (if navigating forward, the user can only skip forwards when other frames are animating).
  1036. //Next/Prev Button Settings
  1037. nextButton: false, //if true, Sequence will use an element with class ".sequence-next" as the next button, else specify your own selector. false = don't use an in-built next button
  1038. showNextButtonOnInit: true, //if true, Sequence will make the next button display: block; once Sequence has loaded (give the next button selector display: none; in the CSS to hide it until its usable)
  1039. prevButton: false, //if true, Sequence will use an element with class ".sequence-prev" as the previous button, else specify your own selector. false = don't use an in-built previous button
  1040. showPrevButtonOnInit: true, //if true, Sequence will make the previous button display: block; once Sequence has loaded (give the previous button selector display: none; in the CSS to hide it until its usable)
  1041. //Pause Settings
  1042. pauseButton: false, //if true, Sequence will use an element with class ".sequence-pause" as the pause button, else specify your own selector. false = don't use an in-built pause button
  1043. unpauseDelay: null, //the time to wait before navigating to the next frame when Sequence is unpaused. Note that if an unpauseDelay is not specified, the default is the same as the autoPlayDelay setting
  1044. pauseOnHover: true, //pause Sequence when the Sequence container is hovered over
  1045. pauseIcon: false, //if true, Sequence will use an element with class ".sequence-pause-icon" as the pause icon, else specify your own selector. false = don't use an in-built pause icon (the pause icon will display when Sequence is paused)
  1046. showPauseButtonOnInit: true, //if true, Sequence will make the pause button display: block; once Sequence has loaded (give the pause button selector display: none; in the CSS to hide it until its usable)
  1047. //Pagination Settings
  1048. pagination: false, //if true, Sequence will use an element with class ".sequence-pagination" as the paginated navigation, else specify your own selector. false = don't use in-built pagination
  1049. showPaginationOnInit: true, //if true, Sequence will make the pagination display: block; once Sequence has loaded (give the pagination selector display: none; in the CSS to hide it until its usable)
  1050. //Preloader Settings
  1051. preloader: false,
  1052. preloadTheseFrames: [1], //all images in these frames will load before Sequence initiates
  1053. preloadTheseImages: [ //specify particular images to load before Sequence initiates
  1054. /* Example usage
  1055. "images/catEatingSalad.jpg",
  1056. "images/meDressedAsBatman.png"
  1057. */
  1058. ],
  1059. /*Note: You can use preloadTheseFrames and preloadTheseImages together. You might want to load all images in frame 1 and just one big image from frame 2 for example*/
  1060. hideFramesUntilPreloaded: true,
  1061. prependPreloadingComplete: true,
  1062. hidePreloaderUsingCSS: true,
  1063. hidePreloaderDelay: 0,
  1064. //Keyboard settings
  1065. keyNavigation: true, //false prevents the following keyboard settings
  1066. numericKeysGoToFrames: true,
  1067. keyEvents: {
  1068. left: "prev",
  1069. right: "next"
  1070. },
  1071. customKeyEvents: {
  1072. /* Example usage
  1073. 65: "prev", //a
  1074. 68: "next", //d
  1075. 83: "prev", //s
  1076. 87: "next" //w
  1077. */
  1078. },
  1079. //Touch Swipe Settings
  1080. swipeNavigation: true,
  1081. swipeThreshold: 20,
  1082. swipePreventsDefault: false, //be careful if setting this to true
  1083. swipeEvents: {
  1084. left: "prev",
  1085. right: "next",
  1086. up: false,
  1087. down: false
  1088. },
  1089. //hashTags Settings
  1090. //when using hashTags, please include a reference to Ben Alman's jQuery HashChange plugin above your reference to Sequence.js
  1091. //info: http://benalman.com/projects/jquery-hashchange-plugin/
  1092. //plugin: https://raw.github.com/cowboy/jquery-hashchange/v1.3/jquery.ba-hashchange.min.js
  1093. //GitHub: https://github.com/cowboy/jquery-hashchange
  1094. hashTags: false, //when a frame is navigated to, change the hashtag to the frames ID
  1095. hashDataAttribute: false, //false = the hashTag is taken from a frames ID attribute | true = the hashTag is taken from the data attribute "data-sequence-hash"
  1096. hashChangesOnFirstFrame: false, //false = the hashTag won't change for the first frame but will for those after
  1097. //Fallback Theme Settings (For browsers that don't support CSS3 transitions)
  1098. fallback: {
  1099. theme: "slide",
  1100. speed: 500
  1101. }
  1102. };
  1103. $.fn.sequence = function(options) {
  1104. return this.each(function() {
  1105. if (!$.data(this, 'sequence')) { //prevent multiple initiations on the same element
  1106. $.data(this, 'sequence', new Sequence($(this), options, defaults));
  1107. }
  1108. });
  1109. };
  1110. })(jQuery);