jquery.excoloSlider.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  1. /*!
  2. * Excolo Slider - A simple jquery slider
  3. *
  4. * Examples and documentation at:
  5. * http://excolo.github.io/Excolo-Slider/
  6. *
  7. * Author: Nikolaj Dam Larsen
  8. * Version: 1.1.0 (16-MAY-2014)
  9. *
  10. * Released under the MIT license
  11. * https://github.com/Excolo/ExcoloSlider/blob/master/LICENSE
  12. */
  13. (function (factory) {
  14. if (typeof define === 'function' && define.amd) {
  15. // AMD. Register as an anonymous module.
  16. define(['jquery'], factory);
  17. } else {
  18. // Browser globals
  19. factory(jQuery);
  20. }
  21. }(function ($) {
  22. var Plugin;
  23. /* Plugin Definition
  24. **************************************************************/
  25. Plugin = (function () {
  26. function Plugin(elem, options) {
  27. this.elem = elem;
  28. this.$elem = $(elem);
  29. this.options = options;
  30. // This next line takes advantage of HTML5 data attributes
  31. // to support customization of the plugin on a per-element
  32. // basis.
  33. this.metadata = this.$elem.data('plugin-options');
  34. }
  35. return Plugin;
  36. })();
  37. /* Plugin prototype
  38. **************************************************************/
  39. Plugin.prototype = {
  40. /* Default Configuration
  41. **********************************************************/
  42. defaults: {
  43. width: 640,
  44. height: 260,
  45. autoSize: true,
  46. touchNav: true,
  47. mouseNav: true,
  48. prevnextNav: true,
  49. prevnextAutoHide: true,
  50. pagerNav: true,
  51. startSlide: 1,
  52. autoPlay: true,
  53. delay: 0,
  54. interval: 3000,
  55. repeat: true,
  56. playReverse: false,
  57. hoverPause: true,
  58. captionAutoHide: false,
  59. animationCssTransitions: true,
  60. animationDuration: 500,
  61. animationTimingFunction: "linear",
  62. prevButtonClass: "slide-prev",
  63. nextButtonClass: "slide-next",
  64. activeSlideClass: "es-active",
  65. slideCaptionClass: "es-caption",
  66. pagerClass: "es-pager",
  67. },
  68. /* Initialization function
  69. **********************************************************/
  70. init: function () {
  71. var base, maxHeight, $prev, $next, $buttons, $innerBase, caption, $wrapper, $children, $container;
  72. // Defined variable to avoid scope problems
  73. base = this;
  74. // Introduce defaults that can be extended either globally or using an object literal.
  75. base.config = $.extend({}, base.defaults, base.options, base.metadata);
  76. base.actionClick={action:false,x:0,y:0};
  77. base.currClick={x:0,y:0};
  78. // Initialize plugin data
  79. base.data = $.data(base);
  80. $.data(base, "currentSlide", base.config.playReverse && base.config.startSlide == 1 ? base.$elem.children().length-1 : base.config.startSlide - 1);
  81. $.data(base, "nextSlide", base.data.currentSlide);
  82. $.data(base, "totalslides", base.$elem.children().length);
  83. $.data(base, "browserEnginePrefix", base._getBrowserEnginePrefix());
  84. $.data(base, "isPlaying", false);
  85. $.data(base, "isAnimating", false);
  86. $.data(base, "playPaused", false);
  87. $.data(base, "justTouched", false);
  88. $.data(base, "isMoving", false);
  89. $.data(base, "width", base.config.width);
  90. // Create helper html objects
  91. base.$elem.addClass("slider");
  92. base.$elem.css({ position: "relative" });
  93. base.$elem.wrapInner("<div class='slide-wrapper'>", base.$elem).children();
  94. base.$elem.wrapInner("<div class='slide-container'>", $(".slide-wrapper", base.$elem)).children();
  95. base.$elem.wrapInner("<div class='slide-dragcontainer'>", $(".slide-container", base.$elem)).children();
  96. $(".slide-container", base.$elem).css({ position: "relative" });
  97. // Setup common jq objects
  98. $container = $(".slide-dragcontainer", base.$elem);
  99. $wrapper = $(".slide-wrapper", base.$elem);
  100. $children = $wrapper.children(); // "Saaave the children, aaaah aah ah aaaaaah"
  101. // Add prev/next nagivation
  102. if (base.config.prevnextNav)
  103. {
  104. // Add prev/next buttons
  105. $wrapper.after("<div class='" + base.config.nextButtonClass + "'>");
  106. $wrapper.after("<div class='" + base.config.prevButtonClass + "'>");
  107. $next = $("." + base.config.nextButtonClass, base.$elem);
  108. $prev = $("." + base.config.prevButtonClass, base.$elem);
  109. $buttons = $next.add($prev);
  110. // Toogle on hover
  111. if (base.config.prevnextAutoHide) {
  112. $buttons.hide();
  113. base.$elem.hover(
  114. function () { $buttons.fadeIn("fast") },
  115. function () { $buttons.fadeOut("fast") }
  116. );
  117. }
  118. // Bind click event to buttons
  119. $prev.on("click", function (e) { base.previous(); });
  120. $next.on("click", function (e) { base.next(); });
  121. $prev.on("touchstart", function (e) {e.stopPropagation(); });
  122. $next.on("touchstart", function (e) {e.stopPropagation(); });
  123. }
  124. // Add pager navigation
  125. if (base.config.pagerNav)
  126. {
  127. base.$elem.append("<ul class='" + base.config.pagerClass + "'>");
  128. // Loop through each slide
  129. $children.each(function () {
  130. $("<li />").appendTo($("." + base.config.pagerClass, base.$elem))
  131. .attr("rel", $(this).index())
  132. .on("click", function () {
  133. $.data(base, "nextSlide", parseInt($(this).attr("rel")));
  134. base._prepareslides(true);
  135. base._slide(true);
  136. base._manualInterference();
  137. });
  138. });
  139. }
  140. // Add data-attribute captions
  141. $children.each(function () {
  142. $innerBase = $(this);
  143. caption = $innerBase.data('plugin-slide-caption');
  144. if (caption === undefined)
  145. return;
  146. if (this.tagName == "IMG")
  147. {
  148. // If the slide is an image, wrap this image in a div and append the caption div.
  149. $innerBase.wrap("<div>");
  150. $innerBase.after("<div class='" + base.config.slideCaptionClass + "'>");
  151. $innerBase.next().append(caption);
  152. } else {
  153. // For any other type of slide element, just append the caption div at the end.
  154. $innerBase.append("<div class='" + base.config.slideCaptionClass + "'>");
  155. $innerBase.children().last().append(caption);
  156. }
  157. // Toogle on hover
  158. if (base.config.captionAutoHide) {
  159. $("." + base.config.slideCaptionClass, base.$elem).hide();
  160. base.$elem.hover(
  161. function () { $("." + base.config.slideCaptionClass, base.$elem).fadeIn("fast") },
  162. function () { $("." + base.config.slideCaptionClass, base.$elem).fadeOut("fast") }
  163. );
  164. }
  165. });
  166. // Add css styles
  167. $wrapper.children().addClass("slide").css({
  168. position: "absolute",
  169. top: 0,
  170. left: 0,
  171. width: base.data.width,
  172. height: base.config.height,
  173. zIndex: 0,
  174. display: "none",
  175. webkitBackfaceVisibility: "hidden"
  176. });
  177. // Set the height of the wrapper to fit the max height of the slides
  178. maxHeight = $children.height();
  179. $wrapper.css({
  180. position: "relative",
  181. left: 0,
  182. height: maxHeight
  183. });
  184. $(".slide-container", base.$elem).css({
  185. width: base.data.width,
  186. overflow: "hidden",
  187. height: maxHeight
  188. });
  189. // Setup touch event handlers
  190. if (base.config.touchNav) {
  191. $container.on("touchstart", function (e) {
  192. var eventData = e.originalEvent.touches[0];
  193. e.preventDefault();
  194. base._onMoveStart(eventData.pageX, eventData.pageY);
  195. base.actionClick.x=base.currClick.x=eventData.pageX;
  196. base.actionClick.y=base.currClick.y=eventData.pageY;
  197. base.actionClick.action=true;
  198. return e.stopPropagation();
  199. });
  200. $container.on("touchmove", function (e) {
  201. var eventData = e.originalEvent.touches[0];
  202. e.preventDefault();
  203. base.actionClick.action=false;
  204. base._onMove(eventData.pageX, eventData.pageY);
  205. base.currClick.x=eventData.pageX;
  206. base.currClick.y=eventData.pageY;
  207. return e.stopPropagation();
  208. });
  209. $container.on("touchend", function (e) {
  210. if ((base.actionClick.action==true)&&(base.actionClick.x==base.currClick.x)&&(base.actionClick.y==base.currClick.y)){
  211. var href=$container.find("."+base.config.activeSlideClass+" a, a."+base.config.activeSlideClass).attr("href");
  212. if ((href!=undefined)&&(href!="")){
  213. window.location=href;
  214. }
  215. }
  216. e.preventDefault();
  217. base._onMoveEnd();
  218. return e.stopPropagation();
  219. });
  220. }
  221. // Setup mouse event handlers
  222. if (base.config.mouseNav) {
  223. $container.css("cursor", "pointer");
  224. $container.on("dragstart", function (e) { return false; });
  225. $container.on("mousedown", function (e) {
  226. base._onMoveStart(e.clientX, e.clientY);
  227. base.actionClick.x=e.clientX;
  228. base.actionClick.y=e.clientY;
  229. base.actionClick.action=false;
  230. $(window).attr('unselectable', 'on').on('selectstart', false).css('user-select', 'none').css('UserSelect', 'none').css('MozUserSelect', 'none');
  231. return e.stopPropagation();
  232. });
  233. // The mousemove event should also work outside the slide-wrapper container
  234. $(window).on("mousemove", function (e) {
  235. base._onMove(e.clientX, e.clientY);
  236. return e.stopPropagation();
  237. });
  238. // The mouseup event should also work outside the slide-wrapper container
  239. $(window).on("mouseup", function (e) {
  240. base._onMoveEnd();
  241. if ((base.actionClick.x==e.clientX)&&(base.actionClick.y==e.clientY)){
  242. base.actionClick.action=true;
  243. }
  244. $(window).removeAttr('unselectable').unbind('selectstart').css('user-select', null).css('UserSelect', null).css('MozUserSelect', null);
  245. return e.stopPropagation();
  246. });
  247. //ie7 and ie8 support
  248. $container.on("mouseup", function (e) {
  249. if ((base.actionClick.x==e.clientX)&&(base.actionClick.y==e.clientY)){
  250. base.actionClick.action=true;
  251. }
  252. });
  253. //prevet click action on link if mouse drag
  254. $container.on("click", function (e) {
  255. if (base.actionClick.action!=true){
  256. e.stopPropagation();
  257. e.preventDefault();
  258. }
  259. });
  260. }
  261. // Auto-size before preparing slides
  262. if (base.config.autoSize)
  263. {
  264. setTimeout(function () { base._resize(); }, 50)
  265. // Setup resize event handler
  266. $(window).resize(function () {
  267. // The timeout is to let other resize events finish
  268. // e.g. if using adapt.js
  269. // This will make it flicker momentarily when resizing
  270. // large widths
  271. return setTimeout(function () { base._resize(); }, 50);
  272. });
  273. }
  274. // Well, the name of the function says it all
  275. base._prepareslides();
  276. // Go to the start slide
  277. base.gotoSlide(base.data.currentSlide);
  278. // Autoplay if so inclined
  279. if (base.config.autoPlay)
  280. {
  281. // Setup delay, if any
  282. setTimeout(function () {
  283. base.start();
  284. }, base.config.delay);
  285. }
  286. return this;
  287. },
  288. /* Move to previous slide
  289. **********************************************************/
  290. previous: function () {
  291. var base, nextSlide;
  292. // Defined variable to avoid scope problems
  293. base = this;
  294. // Store slide direction in plugin data
  295. $.data(base, "slideDirection", "previous");
  296. // Find next index
  297. nextSlide = (base.data.nextSlide - 1) % base.data.totalslides;
  298. // Stop here if we've reached past the beginning and aren't on repeat
  299. if (!base.config.repeat && (base.data.nextSlide - 1) < 0)
  300. {
  301. if (base.config.playReverse){
  302. // Stop playing
  303. $.data(base, "playPaused", true);
  304. base.stop();
  305. }
  306. return;
  307. } else if (base.data.playPaused && (base.data.nextSlide - 1) > 0) {
  308. $.data(base, "playPaused", false);
  309. base.start();
  310. }
  311. // Update data
  312. $.data(base, "nextSlide", nextSlide);
  313. // Perform sliding to the previous slide
  314. return this._slide();
  315. },
  316. /* Move to next slide
  317. **********************************************************/
  318. next: function () {
  319. var base, nextSlide;
  320. // Defined variable to avoid scope problems
  321. base = this;
  322. // Store slide direction in plugin data
  323. $.data(base, "slideDirection", "next");
  324. // Find next index
  325. nextSlide = (base.data.nextSlide + 1) % base.data.totalslides;
  326. // Stop here if we've reached past the end and aren't on repeat
  327. if (!base.config.repeat && (base.data.nextSlide + 1) > (base.data.totalslides - 1)) {
  328. if (!base.config.playReverse) {
  329. // Stop playing
  330. $.data(base, "playPaused", true);
  331. base.stop();
  332. }
  333. return;
  334. } else if (base.data.playPaused && (base.data.nextSlide + 1) < (base.data.totalslides - 1)) {
  335. $.data(base, "playPaused", false);
  336. base.start();
  337. }
  338. // Update data
  339. $.data(base, "nextSlide", nextSlide);
  340. // Perform sliding to the next slide
  341. return this._slide();
  342. },
  343. /* A method to start the slideshow
  344. **********************************************************/
  345. start: function () {
  346. var base, $preContainer, timer;
  347. // Defined variable to avoid scope problems
  348. base = this;
  349. // Jquery objects
  350. $preContainer = $(".slide-container", base.$elem);
  351. // If we're already playing, clear previous interval
  352. if (base.data.isPlaying && base.data.playTimer)
  353. clearInterval(base.data.playTimer);
  354. // Setup the play timer
  355. timer = setInterval((function () {
  356. // Well slide already
  357. if (base.config.playReverse)
  358. base.previous();
  359. else
  360. base.next();
  361. }), base.config.interval)
  362. // Store the timer for reference
  363. $.data(base, "playTimer", timer);
  364. // Setup pause when mouse hover
  365. if (base.config.hoverPause) {
  366. $preContainer.unbind();
  367. $preContainer.hover(function () {
  368. $.data(base, "playPaused", true);
  369. return base.stop();
  370. },function () {
  371. $.data(base, "playPaused", false);
  372. return base.start();
  373. });
  374. }
  375. // Woo we're playing
  376. $.data(base, "isPlaying", true);
  377. },
  378. /* A method to stop playing the slideshow
  379. **********************************************************/
  380. stop: function () {
  381. var base, $preContainer;
  382. // Defined variable to avoid scope problems
  383. base = this;
  384. // Jquery objects
  385. $preContainer = $(".slide-container", base.$elem);
  386. // Stop the interval timer
  387. clearInterval(base.data.playTimer);
  388. $.data(base, "playTimer", null);
  389. // If stop was called but and it wasn't due to a pause,
  390. // unbind container events
  391. if (base.config.hoverPause && !base.data.playPaused)
  392. $preContainer.unbind();
  393. // We've stopped
  394. $.data(base, "isPlaying", false);
  395. },
  396. /* Simply jump to a given slide without transistion
  397. **********************************************************/
  398. gotoSlide: function (slideIndex) {
  399. var base, nextSlideIndex, $container, $slides, $slide, leftPos;
  400. // Define variable to avoid scope problems
  401. base = this;
  402. // Data
  403. $.data(base, "nextSlide", (slideIndex) % base.data.totalslides);
  404. nextSlideIndex = (slideIndex) % base.data.totalslides;
  405. $.data(base, "currentSlide", nextSlideIndex);
  406. // Jquery objects
  407. $container = $(".slide-wrapper", base.$elem);
  408. $slides = $container.children();
  409. $slide = $container.children().eq(nextSlideIndex);
  410. // Get position of goal slide
  411. leftPos = $slide.position().left;
  412. base._setActive($slides, $slide);
  413. // Gogogo
  414. if (base.config.animationCssTransitions && base.data.browserEnginePrefix) {
  415. base._transition((-leftPos), 0);
  416. } else {
  417. $container.css("left",-leftPos);
  418. }
  419. // Align the slides to prepare for next transition
  420. base._alignSlides(leftPos);
  421. },
  422. /* User interacted, if we're playing, we must restart
  423. **********************************************************/
  424. _manualInterference: function () {
  425. // Define variable to avoid scope problems
  426. var base = this;
  427. if (base.data.isPlaying)
  428. {
  429. // Stop and start, to restart the timer from the beginning.
  430. base.stop();
  431. base.start();
  432. }
  433. },
  434. /* Position and align the slides to prepare for sliding
  435. **********************************************************/
  436. _prepareslides: function (onlyAhead) {
  437. var base, $container, $slides, width, half, i;
  438. // Define variable to avoid scope problems
  439. base = this;
  440. // Jquery objects
  441. $container = $(".slide-wrapper", base.$elem);
  442. $slides = $container.children();
  443. // Config
  444. width = base.data.width;
  445. half = Math.floor(base.data.totalslides / 2);
  446. i = 0;
  447. $slides.each(function () {
  448. // Move first half the slides ahead
  449. $(this).css({
  450. display: "block",
  451. left: width * i,
  452. zIndex: 10
  453. });
  454. i++;
  455. // Move the other half back in line
  456. if (!onlyAhead && base.config.repeat && i > half)
  457. i = base.data.totalslides % 2 ? -half : -(half - 1);
  458. });
  459. },
  460. /* Handling the start of the movement
  461. **********************************************************/
  462. _onMoveStart: function (x, y) {
  463. // Define variable to avoid scope problems
  464. var base = this;
  465. // Setup touchrelated data
  466. if (!base.data.isMoving) $.data(base, "touchTime", Number(new Date()));
  467. $.data(base, "touchedX", x);
  468. $.data(base, "touchedY", y);
  469. // The mouse is down.
  470. $.data(base, "isMoving", true);
  471. // Stop playing
  472. if (base.data.isPlaying)
  473. {
  474. $.data(base, "playPaused", true);
  475. base.stop();
  476. }
  477. },
  478. /* Handling the movement
  479. **********************************************************/
  480. _onMove: function (x, y) {
  481. var base, $container, $slide, leftPos, prefix, translateX, limit;
  482. // Define variable to avoid scope problems
  483. base = this;
  484. // Only move if, we're actuall "moving"
  485. if (!base.data.isMoving)
  486. return;
  487. // Jquery objects
  488. $container = $(".slide-wrapper", base.$elem);
  489. // Verify whether we're scrolling or sliding
  490. $.data(base, "scrolling", Math.abs(x - base.data.touchedX) < Math.abs(y - base.data.touchedY));
  491. // If we're not scrolling, we perform the translation
  492. // ...also - wait for any animation to finish
  493. // (we cant slide while animating)
  494. if (!base.data.scrolling && !base.data.isAnimating)
  495. {
  496. // Get the position of the slide we are heading for
  497. $slide = $container.children().eq(base.data.nextSlide);
  498. leftPos = $slide.position().left;
  499. // Get the browser engine prefix - if any
  500. prefix = base.data.browserEnginePrefix.css;
  501. // Get the delta movement to use for translation
  502. translateX = x - base.data.touchedX;
  503. // Limit if not repeating
  504. limit = base.data.width * 0.1;
  505. if (!base.config.repeat)
  506. {
  507. if (base.data.currentSlide <= 0 && -translateX < -limit)
  508. translateX = limit;
  509. else if (base.data.currentSlide >= (base.data.totalslides - 1) && -translateX > limit)
  510. translateX = -limit;
  511. }
  512. // Transformation
  513. base._transition(-leftPos + translateX, 0);
  514. }
  515. },
  516. /* Handling the end of the movement
  517. **********************************************************/
  518. _onMoveEnd: function () {
  519. var base, $container, $slide, leftPos, half, tenth, svipe;
  520. // Define variable to avoid scope problems
  521. base = this;
  522. // Only move if, we're actually "moving"
  523. if (!base.data.isMoving)
  524. return;
  525. // Jquery objects
  526. $container = $(".slide-wrapper", base.$elem);
  527. // Set that we've just touched something such that when we slide next
  528. // the sliding duration is temporary halved.
  529. $.data(base, "justTouched", true);
  530. // Get the position of the slide we are heading for
  531. $slide = $container.children().eq(base.data.nextSlide);
  532. leftPos = $slide.position().left;
  533. // If we've slided at least half the width of the slide - slide to next
  534. // Also if we've slided 10% of the width within 1/4 of a second,
  535. // we slide to the next
  536. half = base.data.width * 0.5;
  537. tenth = base.data.width * 0.1;
  538. svipe = (Number(new Date()) - base.data.touchTime < 250);
  539. if (!base.config.repeat
  540. && ($container.position().left < -(leftPos) && base.data.currentSlide >= (base.data.totalslides - 1)
  541. || $container.position().left > (-leftPos) && base.data.currentSlide <= 0)) {
  542. // We can't move move as repeat is turned off
  543. base._transition((-leftPos), 0.15);
  544. }
  545. else if ($container.position().left > (-leftPos + half)
  546. || ($container.position().left > (-leftPos + tenth) && svipe)) {
  547. this.previous();
  548. } else if ($container.position().left < -(leftPos + half)
  549. || ($container.position().left < -(leftPos + tenth) && svipe)) {
  550. this.next();
  551. } else {
  552. // Didn't slide enough to move on - bounce back into place.
  553. base._transition((-leftPos), 0.15);
  554. }
  555. // Align the slides to prepare for the next slide
  556. base._alignSlides(leftPos);
  557. // We're no longer moving and touching
  558. $.data(base, "isMoving", false);
  559. $.data(base, "justTouched", false);
  560. // Restart playing playing
  561. if(base.data.playPaused)
  562. base.start();
  563. },
  564. /* Make an "endless" line of slides
  565. **********************************************************/
  566. // ISSUE: Too slow slide duration and too fast sliding
  567. // may result in the slides not being aligned yet.
  568. // SOLUTION:...might be to duplicate all slides in init
  569. // if number of slides is low.
  570. _alignSlides : function(goalPosition)
  571. {
  572. var base, $container, $slides, $slide, half, width, bufferLength, bufferShortage, i, lowest, highest;
  573. // Define variable to avoid scope problems
  574. base = this;
  575. if (!base.config.repeat)
  576. return;
  577. // Jquery objects
  578. $container = $(".slide-wrapper", base.$elem);
  579. $slides = $container.children();
  580. // Retrieve goalPosition if undefined
  581. if (goalPosition === undefined)
  582. {
  583. $slide = $container.children().eq(base.data.nextSlide);
  584. goalPosition = $slide.position().left;
  585. }
  586. // Remove fraction
  587. goalPosition = Math.round(goalPosition,0);
  588. // Half of the total slides
  589. half = Math.ceil(base.data.totalslides / 2);
  590. // Config
  591. width = base.data.width;
  592. // Get number of $slides after/before 'goalPosition' - this is our buffer.
  593. // If our buffer is below half the total $slides, we need to increase it.
  594. bufferLength = 0;
  595. $slides.each(function () {
  596. var l = Math.round($(this).position().left);
  597. if (l > Math.round(goalPosition - width))
  598. bufferLength++;
  599. });
  600. // Calculate how much short on buffer we are
  601. bufferShortage = half - bufferLength;
  602. // We're sliding the other direction thus moving a buffer to the other side
  603. if (bufferShortage < 0)
  604. bufferShortage = base.data.totalslides % 2 == 0 ? bufferShortage + 1 : bufferShortage;
  605. // Align slides according to bufferShortage
  606. for (i = 0; i < Math.abs(bufferShortage); i++) {
  607. // Find the element with the lowest left position
  608. lowest = [].reduce.call($slides, function (sml, cur) {
  609. return $(sml).offset().left < $(cur).offset().left ? sml : cur;
  610. });
  611. // Find the element with the highest left position
  612. highest = [].reduce.call($slides, function (sml, cur) {
  613. return $(sml).offset().left > $(cur).offset().left ? sml : cur;
  614. });
  615. if(bufferShortage > 0)
  616. $(lowest).css("left", Math.round($(highest).position().left + width));
  617. else
  618. $(highest).css("left", Math.round($(lowest).position().left - width));
  619. }
  620. },
  621. /* Perform a slide
  622. **********************************************************/
  623. _slide: function (postalign) {
  624. var base, nextSlideIndex, $container, $slides, $slide, leftPos;
  625. // Define variable to avoid scope problems
  626. base = this;
  627. // Data
  628. nextSlideIndex = base.data.nextSlide;
  629. // Jquery objects
  630. $container = $(".slide-wrapper", base.$elem);
  631. $slides = $container.children();
  632. $slide = $container.children().eq(nextSlideIndex);
  633. // Get the position of the slide we are heading for
  634. leftPos = Math.round($slide.position().left);
  635. // ---
  636. base._setActive($slides, $slide);
  637. // ---
  638. // Pre-Align the slides in a line to prepare for the transition animation
  639. if (!postalign) base._alignSlides(leftPos);
  640. // Animate - css transitions are much better.
  641. $.data(base, "isAnimating", true);
  642. if (base.config.animationCssTransitions && base.data.browserEnginePrefix) {
  643. base._transition((-leftPos), (base.data.justTouched ? 0.5 : 1));
  644. // Set nextslide on end of transition
  645. $container.on("transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd", function () {
  646. $.data(base, "currentSlide", nextSlideIndex);
  647. $container.unbind("transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd");
  648. // Post-Align the slides in a line to prepare for the transition animation
  649. if (postalign) base._alignSlides(leftPos);
  650. });
  651. } else {
  652. // We must resolve to sucky animations
  653. $container.stop().animate({ left: -leftPos }, base.config.animationDuration, function () {
  654. $.data(base, "currentSlide", nextSlideIndex);
  655. $.data(base, "isAnimating", false);
  656. $.data(base, "justTouched", false);
  657. });
  658. }
  659. },
  660. /* Perform the transition
  661. **********************************************************/
  662. _transition: function (leftPos, durationModifier)
  663. {
  664. var base, $container, prefix, transform, duration, timing;
  665. // Define variable to avoid scope problems
  666. base = this;
  667. // Jquery objects
  668. $container = $(".slide-wrapper", base.$elem);
  669. // Limit duration modifier
  670. if (durationModifier === undefined || durationModifier < 0) {
  671. durationModifier = 1;
  672. }
  673. // NOTE: We add both prefixed transition and the default
  674. // for browser compatibility.
  675. // Select the css code based on browser engine
  676. prefix = base.data.browserEnginePrefix.css;
  677. transform = prefix + "Transform";
  678. duration = prefix + "TransitionDuration";
  679. timing = prefix + "TransitionTimingFunction";
  680. // Set style to activate the slide transition
  681. $container[0].style[duration] = (base.config.animationDuration * durationModifier) + "ms";
  682. $container[0].style[timing] = base.config.animationTimingFunction;
  683. $container[0].style[transform] = "translateX(" + leftPos + "px)";
  684. $container.on("transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd", function () {
  685. $.data(base, "isAnimating", false);
  686. $.data(base, "justTouched", false);
  687. $container.unbind("transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd");
  688. });
  689. },
  690. /* Update active slide
  691. **********************************************************/
  692. _setActive: function ($slides, $slide) {
  693. var base = this, activeSlideClass, pager;
  694. activeSlideClass = base.config.activeSlideClass;
  695. // Clear old active class
  696. $slides.removeClass(activeSlideClass);
  697. // Set new active class
  698. $slide.addClass(activeSlideClass);
  699. // Set active page in pager
  700. if (base.config.pagerNav)
  701. {
  702. pager = $("." + base.config.pagerClass, base.$elem);
  703. pager.children().removeClass("act");
  704. pager.find("[rel=" + $slide.index() + "]").addClass("act");
  705. }
  706. },
  707. /* Auto-size the slider
  708. **********************************************************/
  709. _resize: function () {
  710. var base, newwidth, ratio, newheight, maxHeight;
  711. // Define variable to avoid scope problems
  712. base = this;
  713. // Stop playing
  714. if (base.data.isPlaying){
  715. $.data(base, "playPaused", true);
  716. base.stop();
  717. }
  718. // Getting width from parent container
  719. newwidth = base.$elem.width();
  720. // Calculate W/H ratio
  721. ratio = base.config.height / base.config.width;
  722. // Get height from W/H ratio
  723. newheight = newwidth * ratio;
  724. // Update width
  725. $.data(base, "width", newwidth);
  726. // Add css styles
  727. $(".slide", base.$elem).css({
  728. width: newwidth,
  729. height: newheight
  730. });
  731. // Set the height of the wrapper to fit the max height of the slides
  732. maxHeight = $(".slide-wrapper", base.$elem).children().height();
  733. $(".slide-wrapper", base.$elem).css({
  734. height: maxHeight
  735. });
  736. $(".slide-container", base.$elem).css({
  737. width: newwidth,
  738. height: maxHeight
  739. });
  740. // Restart playing
  741. if (base.data.playPaused){
  742. $.data(base, "playPaused", false);
  743. base.start();
  744. }
  745. // Realign now to make it look good.
  746. base._prepareslides();
  747. base.gotoSlide(base.data.currentSlide);
  748. },
  749. /* Find out which browser engine is used
  750. **********************************************************/
  751. _getBrowserEnginePrefix: function () {
  752. var transition, vendor, i;
  753. transition = "Transition";
  754. vendor = ["Moz", "Webkit", "Khtml", "O", "ms"];
  755. i = 0;
  756. while (i < vendor.length) {
  757. if (typeof document.body.style[vendor[i] + transition] === "string") {
  758. return { css: vendor[i] };
  759. }
  760. i++;
  761. }
  762. return false;
  763. }
  764. }
  765. Plugin.defaults = Plugin.prototype.defaults;
  766. /* Add the plugin to the jquery namespace.
  767. **************************************************************/
  768. $.fn.excoloSlider = function (options) {
  769. return this.each(function () {
  770. // Instantiate and initialize
  771. new Plugin(this, options).init();
  772. });
  773. };
  774. }));