sly1.js 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224
  1. /*!
  2. * sly 1.6.1 - 8th Aug 2015
  3. * https://github.com/darsain/sly
  4. *
  5. * Licensed under the MIT license.
  6. * http://opensource.org/licenses/MIT
  7. */
  8. ;(function ($, w, undefined) {
  9. 'use strict';
  10. var pluginName = 'sly';
  11. var className = 'Sly';
  12. var namespace = pluginName;
  13. // Local WindowAnimationTiming interface
  14. var cAF = w.cancelAnimationFrame || w.cancelRequestAnimationFrame;
  15. var rAF = w.requestAnimationFrame;
  16. // Support indicators
  17. var transform, gpuAcceleration;
  18. // Other global values
  19. var $doc = $(document);
  20. var dragInitEvents = 'touchstart.' + namespace + ' mousedown.' + namespace;
  21. var dragMouseEvents = 'mousemove.' + namespace + ' mouseup.' + namespace;
  22. var dragTouchEvents = 'touchmove.' + namespace + ' touchend.' + namespace;
  23. var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel.' : 'mousewheel.') + namespace;
  24. var clickEvent = 'click.' + namespace;
  25. var mouseDownEvent = 'mousedown.' + namespace;
  26. var interactiveElements = ['INPUT', 'SELECT', 'BUTTON', 'TEXTAREA'];
  27. var tmpArray = [];
  28. var time;
  29. // Math shorthands
  30. var abs = Math.abs;
  31. var sqrt = Math.sqrt;
  32. var pow = Math.pow;
  33. var round = Math.round;
  34. var max = Math.max;
  35. var min = Math.min;
  36. // Keep track of last fired global wheel event
  37. var lastGlobalWheel = 0;
  38. $doc.on(wheelEvent, function (event) {
  39. var sly = event.originalEvent[namespace];
  40. var time = +new Date();
  41. // Update last global wheel time, but only when event didn't originate
  42. // in Sly frame, or the origin was less than scrollHijack time ago
  43. if (!sly || sly.options.scrollHijack < time - lastGlobalWheel) lastGlobalWheel = time;
  44. });
  45. /**
  46. * Sly.
  47. *
  48. * @class
  49. *
  50. * @param {Element} frame DOM element of sly container.
  51. * @param {Object} options Object with options.
  52. * @param {Object} callbackMap Callbacks map.
  53. */
  54. function Sly(frame, options, callbackMap) {
  55. if (!(this instanceof Sly)) return new Sly(frame, options, callbackMap);
  56. // Extend options
  57. var o = $.extend({}, Sly.defaults, options);
  58. // Private variables
  59. var self = this;
  60. var parallax = isNumber(frame);
  61. // Frame
  62. var $frame = $(frame);
  63. var $slidee = o.slidee ? $(o.slidee).eq(0) : $frame.children().eq(0);
  64. var frameSize = 0;
  65. var slideeSize = 0;
  66. var pos = {
  67. start: 0,
  68. center: 0,
  69. end: 0,
  70. cur: 0,
  71. dest: 0
  72. };
  73. // Scrollbar
  74. var $sb = $(o.scrollBar).eq(0);
  75. var $handle = $sb.children().eq(0);
  76. var sbSize = 0;
  77. var handleSize = 0;
  78. var hPos = {
  79. start: 0,
  80. end: 0,
  81. cur: 0
  82. };
  83. // Pagesbar
  84. var $pb = $(o.pagesBar);
  85. var $pages = 0;
  86. var pages = [];
  87. // Items
  88. var $items = 0;
  89. var items = [];
  90. var rel = {
  91. firstItem: 0,
  92. lastItem: 0,
  93. centerItem: 0,
  94. activeItem: null,
  95. activePage: 0
  96. };
  97. // Styles
  98. var frameStyles = new StyleRestorer($frame[0]);
  99. var slideeStyles = new StyleRestorer($slidee[0]);
  100. var sbStyles = new StyleRestorer($sb[0]);
  101. var handleStyles = new StyleRestorer($handle[0]);
  102. // Navigation type booleans
  103. var basicNav = o.itemNav === 'basic';
  104. var forceCenteredNav = o.itemNav === 'forceCentered';
  105. var centeredNav = o.itemNav === 'centered' || forceCenteredNav;
  106. var itemNav = !parallax && (basicNav || centeredNav || forceCenteredNav);
  107. // Miscellaneous
  108. var $scrollSource = o.scrollSource ? $(o.scrollSource) : $frame;
  109. var $dragSource = o.dragSource ? $(o.dragSource) : $frame;
  110. var $forwardButton = $(o.forward);
  111. var $backwardButton = $(o.backward);
  112. var $prevButton = $(o.prev);
  113. var $nextButton = $(o.next);
  114. var $prevPageButton = $(o.prevPage);
  115. var $nextPageButton = $(o.nextPage);
  116. var callbacks = {};
  117. var last = {};
  118. var animation = {};
  119. var move = {};
  120. var dragging = {
  121. released: 1
  122. };
  123. var scrolling = {
  124. last: 0,
  125. delta: 0,
  126. resetTime: 200
  127. };
  128. var renderID = 0;
  129. var historyID = 0;
  130. var cycleID = 0;
  131. var continuousID = 0;
  132. var i, l;
  133. // Normalizing frame
  134. if (!parallax) {
  135. frame = $frame[0];
  136. }
  137. // Expose properties
  138. self.initialized = 0;
  139. self.frame = frame;
  140. self.slidee = $slidee[0];
  141. self.pos = pos;
  142. self.rel = rel;
  143. self.items = items;
  144. self.pages = pages;
  145. self.isPaused = 0;
  146. self.options = o;
  147. self.dragging = dragging;
  148. /**
  149. * Loading function.
  150. *
  151. * Populate arrays, set sizes, bind events, ...
  152. *
  153. * @param {Boolean} [isInit] Whether load is called from within self.init().
  154. * @return {Void}
  155. */
  156. function load(isInit) {
  157. // Local variables
  158. var lastItemsCount = 0;
  159. var lastPagesCount = pages.length;
  160. // Save old position
  161. pos.old = $.extend({}, pos);
  162. // Reset global variables
  163. frameSize = parallax ? 0 : $frame[o.horizontal ? 'width' : 'height']();
  164. sbSize = $sb[o.horizontal ? 'width' : 'height']();
  165. slideeSize = parallax ? frame : $slidee[o.horizontal ? 'outerWidth' : 'outerHeight']();
  166. pages.length = 0;
  167. //frameSize /=2;
  168. //slideeSize /=2;
  169. //console.log(frameSize);
  170. //console.log(slideeSize);
  171. // Set position limits & relatives
  172. pos.start = 0;
  173. pos.end = max(slideeSize - frameSize, 0);
  174. // Sizes & offsets for item based navigations
  175. if (itemNav) {
  176. // Save the number of current items
  177. lastItemsCount = items.length;
  178. // Reset itemNav related variables
  179. $items = $slidee.children(o.itemSelector);
  180. items.length = 0;
  181. // Needed variables
  182. var paddingStart = getPx($slidee, o.horizontal ? 'paddingLeft' : 'paddingTop');
  183. var paddingEnd = getPx($slidee, o.horizontal ? 'paddingRight' : 'paddingBottom');
  184. var borderBox = $($items).css('boxSizing') === 'border-box';
  185. var areFloated = $items.css('float') !== 'none';
  186. var ignoredMargin = 0;
  187. var lastItemIndex = $items.length - 1;
  188. var lastItem;
  189. // Reset slideeSize
  190. slideeSize = 0;
  191. // Iterate through items
  192. $items.each(function (i, element) {
  193. //console.log(slideeSize);
  194. // Item
  195. var $item = $(element);
  196. var rect = element.getBoundingClientRect();
  197. //console.log(rect);
  198. var itemSize = round(o.horizontal ? rect.width || rect.right - rect.left : rect.height || rect.bottom - rect.top);
  199. if($('#outBody').attr('scale')){
  200. var scalebody = $('#outBody').attr('scale');
  201. itemSize =(o.horizontal ? rect.width || rect.right - rect.left : rect.height || rect.bottom - rect.top)*scalebody;
  202. }
  203. //console.log(itemSize)
  204. var itemMarginStart = getPx($item, o.horizontal ? 'marginLeft' : 'marginTop');
  205. var itemMarginEnd = getPx($item, o.horizontal ? 'marginRight' : 'marginBottom');
  206. var itemSizeFull = itemSize + itemMarginStart + itemMarginEnd;
  207. var singleSpaced = !itemMarginStart || !itemMarginEnd;
  208. var item = {};
  209. item.el = element;
  210. item.size = singleSpaced ? itemSize : itemSizeFull;
  211. item.half = item.size / 2;
  212. item.start = slideeSize + (singleSpaced ? itemMarginStart : 0);
  213. item.center = item.start - round(frameSize / 2 - item.size / 2);
  214. item.end = item.start - frameSize + item.size;
  215. //console.log(item.size)
  216. //console.log(item.start)
  217. // Account for slidee padding
  218. if (!i) {
  219. slideeSize += paddingStart;
  220. }
  221. // Increment slidee size for size of the active element
  222. slideeSize += itemSizeFull;
  223. // Try to account for vertical margin collapsing in vertical mode
  224. // It's not bulletproof, but should work in 99% of cases
  225. if (!o.horizontal && !areFloated) {
  226. // Subtract smaller margin, but only when top margin is not 0, and this is not the first element
  227. if (itemMarginEnd && itemMarginStart && i > 0) {
  228. slideeSize -= min(itemMarginStart, itemMarginEnd);
  229. }
  230. }
  231. // Things to be done on last item
  232. if (i === lastItemIndex) {
  233. item.end += paddingEnd;
  234. slideeSize += paddingEnd;
  235. ignoredMargin = singleSpaced ? itemMarginEnd : 0;
  236. }
  237. // Add item object to items array
  238. items.push(item);
  239. lastItem = item;
  240. });
  241. // Resize SLIDEE to fit all items
  242. $slidee[0].style[o.horizontal ? 'width' : 'height'] = (borderBox ? slideeSize: slideeSize - paddingStart - paddingEnd) + 'px';
  243. // Adjust internal SLIDEE size for last margin
  244. slideeSize -= ignoredMargin;
  245. // Set limits
  246. if (items.length) {
  247. pos.start = items[0][forceCenteredNav ? 'center' : 'start'];
  248. pos.end = forceCenteredNav ? lastItem.center : frameSize < slideeSize ? lastItem.end : pos.start;
  249. } else {
  250. pos.start = pos.end = 0;
  251. }
  252. }
  253. // Calculate SLIDEE center position
  254. pos.center = round(pos.end / 2 + pos.start / 2);
  255. // Update relative positions
  256. updateRelatives();
  257. // Scrollbar
  258. if ($handle.length && sbSize > 0) {
  259. // Stretch scrollbar handle to represent the visible area
  260. if (o.dynamicHandle) {
  261. handleSize = pos.start === pos.end ? sbSize : round(sbSize * frameSize / slideeSize);
  262. handleSize = within(handleSize, o.minHandleSize, sbSize);
  263. $handle[0].style[o.horizontal ? 'width' : 'height'] = handleSize + 'px';
  264. } else {
  265. handleSize = $handle[o.horizontal ? 'outerWidth' : 'outerHeight']();
  266. }
  267. hPos.end = sbSize - handleSize;
  268. if (!renderID) {
  269. syncScrollbar();
  270. }
  271. }
  272. // Pages
  273. if (!parallax && frameSize > 0) {
  274. var tempPagePos = pos.start;
  275. var pagesHtml = '';
  276. // Populate pages array
  277. if (itemNav) {
  278. $.each(items, function (i, item) {
  279. if (forceCenteredNav) {
  280. pages.push(item.center);
  281. } else if (item.start + item.size > tempPagePos && tempPagePos <= pos.end) {
  282. tempPagePos = item.start;
  283. pages.push(tempPagePos);
  284. tempPagePos += frameSize;
  285. if (tempPagePos > pos.end && tempPagePos < pos.end + frameSize) {
  286. pages.push(pos.end);
  287. }
  288. }
  289. });
  290. } else {
  291. while (tempPagePos - frameSize < pos.end) {
  292. pages.push(tempPagePos);
  293. tempPagePos += frameSize;
  294. }
  295. }
  296. // Pages bar
  297. if ($pb[0] && lastPagesCount !== pages.length) {
  298. for (var i = 0; i < pages.length; i++) {
  299. pagesHtml += o.pageBuilder.call(self, i);
  300. }
  301. $pages = $pb.html(pagesHtml).children();
  302. $pages.eq(rel.activePage).addClass(o.activeClass);
  303. }
  304. }
  305. // Extend relative variables object with some useful info
  306. rel.slideeSize = slideeSize;
  307. rel.frameSize = frameSize;
  308. rel.sbSize = sbSize;
  309. rel.handleSize = handleSize;
  310. // Activate requested position
  311. if (itemNav) {
  312. if (isInit && o.startAt != null) {
  313. activate(o.startAt);
  314. self[centeredNav ? 'toCenter' : 'toStart'](o.startAt);
  315. }
  316. // Fix possible overflowing
  317. var activeItem = items[rel.activeItem];
  318. slideTo(centeredNav && activeItem ? activeItem.center : within(pos.dest, pos.start, pos.end));
  319. } else {
  320. if (isInit) {
  321. if (o.startAt != null) slideTo(o.startAt, 1);
  322. } else {
  323. // Fix possible overflowing
  324. slideTo(within(pos.dest, pos.start, pos.end));
  325. }
  326. }
  327. // Trigger load event
  328. trigger('load');
  329. }
  330. self.reload = function () { load(); };
  331. /**
  332. * Animate to a position.
  333. *
  334. * @param {Int} newPos New position.
  335. * @param {Bool} immediate Reposition immediately without an animation.
  336. * @param {Bool} dontAlign Do not align items, use the raw position passed in first argument.
  337. *
  338. * @return {Void}
  339. */
  340. function slideTo(newPos, immediate, dontAlign) {
  341. // Align items
  342. if (itemNav && dragging.released && !dontAlign) {
  343. var tempRel = getRelatives(newPos);
  344. var isNotBordering = newPos > pos.start && newPos < pos.end;
  345. if (centeredNav) {
  346. if (isNotBordering) {
  347. newPos = items[tempRel.centerItem].center;
  348. }
  349. if (forceCenteredNav && o.activateMiddle) {
  350. activate(tempRel.centerItem);
  351. }
  352. } else if (isNotBordering) {
  353. newPos = items[tempRel.firstItem].start;
  354. }
  355. }
  356. // Handle overflowing position limits
  357. if (dragging.init && dragging.slidee && o.elasticBounds) {
  358. if (newPos > pos.end) {
  359. newPos = pos.end + (newPos - pos.end) / 6;
  360. } else if (newPos < pos.start) {
  361. newPos = pos.start + (newPos - pos.start) / 6;
  362. }
  363. } else {
  364. newPos = within(newPos, pos.start, pos.end);
  365. }
  366. // Update the animation object
  367. animation.start = +new Date();
  368. animation.time = 0;
  369. animation.from = pos.cur;
  370. animation.to = newPos;
  371. animation.delta = newPos - pos.cur;
  372. animation.tweesing = dragging.tweese || dragging.init && !dragging.slidee;
  373. animation.immediate = !animation.tweesing && (immediate || dragging.init && dragging.slidee || !o.speed);
  374. // Reset dragging tweesing request
  375. dragging.tweese = 0;
  376. // Start animation rendering
  377. if (newPos !== pos.dest) {
  378. pos.dest = newPos;
  379. trigger('change');
  380. if (!renderID) {
  381. render();
  382. }
  383. }
  384. // Reset next cycle timeout
  385. resetCycle();
  386. // Synchronize states
  387. updateRelatives();
  388. updateButtonsState();
  389. syncPagesbar();
  390. }
  391. /**
  392. * Render animation frame.
  393. *
  394. * @return {Void}
  395. */
  396. function render() {
  397. if (!self.initialized) {
  398. return;
  399. }
  400. // If first render call, wait for next animationFrame
  401. if (!renderID) {
  402. renderID = rAF(render);
  403. if (dragging.released) {
  404. trigger('moveStart');
  405. }
  406. return;
  407. }
  408. // If immediate repositioning is requested, don't animate.
  409. if (animation.immediate) {
  410. pos.cur = animation.to;
  411. }
  412. // Use tweesing for animations without known end point
  413. else if (animation.tweesing) {
  414. animation.tweeseDelta = animation.to - pos.cur;
  415. // Fuck Zeno's paradox
  416. if (abs(animation.tweeseDelta) < 0.1) {
  417. pos.cur = animation.to;
  418. } else {
  419. pos.cur += animation.tweeseDelta * (dragging.released ? o.swingSpeed : o.syncSpeed);
  420. }
  421. }
  422. // Use tweening for basic animations with known end point
  423. else {
  424. animation.time = min(+new Date() - animation.start, o.speed);
  425. pos.cur = animation.from + animation.delta * $.easing[o.easing](animation.time/o.speed, animation.time, 0, 1, o.speed);
  426. }
  427. // If there is nothing more to render break the rendering loop, otherwise request new animation frame.
  428. if (animation.to === pos.cur) {
  429. pos.cur = animation.to;
  430. dragging.tweese = renderID = 0;
  431. } else {
  432. renderID = rAF(render);
  433. }
  434. trigger('move');
  435. // Update SLIDEE position
  436. if (!parallax) {
  437. if (transform) {
  438. $slidee[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + (-pos.cur) + 'px)';
  439. } else {
  440. $slidee[0].style[o.horizontal ? 'left' : 'top'] = -round(pos.cur) + 'px';
  441. }
  442. }
  443. // When animation reached the end, and dragging is not active, trigger moveEnd
  444. if (!renderID && dragging.released) {
  445. trigger('moveEnd');
  446. }
  447. syncScrollbar();
  448. }
  449. /**
  450. * Synchronizes scrollbar with the SLIDEE.
  451. *
  452. * @return {Void}
  453. */
  454. function syncScrollbar() {
  455. if ($handle.length) {
  456. hPos.cur = pos.start === pos.end ? 0 : (((dragging.init && !dragging.slidee) ? pos.dest : pos.cur) - pos.start) / (pos.end - pos.start) * hPos.end;
  457. hPos.cur = within(round(hPos.cur), hPos.start, hPos.end);
  458. if (last.hPos !== hPos.cur) {
  459. last.hPos = hPos.cur;
  460. if (transform) {
  461. $handle[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + hPos.cur + 'px)';
  462. } else {
  463. $handle[0].style[o.horizontal ? 'left' : 'top'] = hPos.cur + 'px';
  464. }
  465. }
  466. }
  467. }
  468. /**
  469. * Synchronizes pagesbar with SLIDEE.
  470. *
  471. * @return {Void}
  472. */
  473. function syncPagesbar() {
  474. if ($pages[0] && last.page !== rel.activePage) {
  475. last.page = rel.activePage;
  476. $pages.removeClass(o.activeClass).eq(rel.activePage).addClass(o.activeClass);
  477. trigger('activePage', last.page);
  478. }
  479. }
  480. /**
  481. * Returns the position object.
  482. *
  483. * @param {Mixed} item
  484. *
  485. * @return {Object}
  486. */
  487. self.getPos = function (item) {
  488. if (itemNav) {
  489. var index = getIndex(item);
  490. return index !== -1 ? items[index] : false;
  491. } else {
  492. var $item = $slidee.find(item).eq(0);
  493. if ($item[0]) {
  494. var offset = o.horizontal ? $item.offset().left - $slidee.offset().left : $item.offset().top - $slidee.offset().top;
  495. var size = $item[o.horizontal ? 'outerWidth' : 'outerHeight']();
  496. return {
  497. start: offset,
  498. center: offset - frameSize / 2 + size / 2,
  499. end: offset - frameSize + size,
  500. size: size
  501. };
  502. } else {
  503. return false;
  504. }
  505. }
  506. };
  507. /**
  508. * Continuous move in a specified direction.
  509. *
  510. * @param {Bool} forward True for forward movement, otherwise it'll go backwards.
  511. * @param {Int} speed Movement speed in pixels per frame. Overrides options.moveBy value.
  512. *
  513. * @return {Void}
  514. */
  515. self.moveBy = function (speed) {
  516. move.speed = speed;
  517. // If already initiated, or there is nowhere to move, abort
  518. if (dragging.init || !move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) {
  519. return;
  520. }
  521. // Initiate move object
  522. move.lastTime = +new Date();
  523. move.startPos = pos.cur;
  524. // Set dragging as initiated
  525. continuousInit('button');
  526. dragging.init = 1;
  527. // Start movement
  528. trigger('moveStart');
  529. cAF(continuousID);
  530. moveLoop();
  531. };
  532. /**
  533. * Continuous movement loop.
  534. *
  535. * @return {Void}
  536. */
  537. function moveLoop() {
  538. // If there is nowhere to move anymore, stop
  539. if (!move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) {
  540. self.stop();
  541. }
  542. // Request new move loop if it hasn't been stopped
  543. continuousID = dragging.init ? rAF(moveLoop) : 0;
  544. // Update move object
  545. move.now = +new Date();
  546. move.pos = pos.cur + (move.now - move.lastTime) / 1000 * move.speed;
  547. // Slide
  548. slideTo(dragging.init ? move.pos : round(move.pos));
  549. // Normally, this is triggered in render(), but if there
  550. // is nothing to render, we have to do it manually here.
  551. if (!dragging.init && pos.cur === pos.dest) {
  552. trigger('moveEnd');
  553. }
  554. // Update times for future iteration
  555. move.lastTime = move.now;
  556. }
  557. /**
  558. * Stops continuous movement.
  559. *
  560. * @return {Void}
  561. */
  562. self.stop = function () {
  563. if (dragging.source === 'button') {
  564. dragging.init = 0;
  565. dragging.released = 1;
  566. }
  567. };
  568. /**
  569. * Activate previous item.
  570. *
  571. * @return {Void}
  572. */
  573. self.prev = function () {
  574. self.activate(rel.activeItem == null ? 0 : rel.activeItem - 1);
  575. };
  576. /**
  577. * Activate next item.
  578. *
  579. * @return {Void}
  580. */
  581. self.next = function () {
  582. self.activate(rel.activeItem == null ? 0 : rel.activeItem + 1);
  583. };
  584. /**
  585. * Activate previous page.
  586. *
  587. * @return {Void}
  588. */
  589. self.prevPage = function () {
  590. self.activatePage(rel.activePage - 1);
  591. };
  592. /**
  593. * Activate next page.
  594. *
  595. * @return {Void}
  596. */
  597. self.nextPage = function () {
  598. self.activatePage(rel.activePage + 1);
  599. };
  600. /**
  601. * Slide SLIDEE by amount of pixels.
  602. *
  603. * @param {Int} delta Pixels/Items. Positive means forward, negative means backward.
  604. * @param {Bool} immediate Reposition immediately without an animation.
  605. *
  606. * @return {Void}
  607. */
  608. self.slideBy = function (delta, immediate) {
  609. if (!delta) {
  610. return;
  611. }
  612. if (itemNav) {
  613. self[centeredNav ? 'toCenter' : 'toStart'](
  614. within((centeredNav ? rel.centerItem : rel.firstItem) + o.scrollBy * delta, 0, items.length)
  615. );
  616. } else {
  617. slideTo(pos.dest + delta, immediate);
  618. }
  619. };
  620. /**
  621. * Animate SLIDEE to a specific position.
  622. *
  623. * @param {Int} pos New position.
  624. * @param {Bool} immediate Reposition immediately without an animation.
  625. *
  626. * @return {Void}
  627. */
  628. self.slideTo = function (pos, immediate) {
  629. slideTo(pos, immediate);
  630. };
  631. /**
  632. * Core method for handling `toLocation` methods.
  633. *
  634. * @param {String} location
  635. * @param {Mixed} item
  636. * @param {Bool} immediate
  637. *
  638. * @return {Void}
  639. */
  640. function to(location, item, immediate) {
  641. // Optional arguments logic
  642. if (type(item) === 'boolean') {
  643. immediate = item;
  644. item = undefined;
  645. }
  646. if (item === undefined) {
  647. slideTo(pos[location], immediate);
  648. } else {
  649. // You can't align items to sides of the frame
  650. // when centered navigation type is enabled
  651. if (centeredNav && location !== 'center') {
  652. return;
  653. }
  654. var itemPos = self.getPos(item);
  655. if (itemPos) {
  656. slideTo(itemPos[location], immediate, !centeredNav);
  657. }
  658. }
  659. }
  660. /**
  661. * Animate element or the whole SLIDEE to the start of the frame.
  662. *
  663. * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
  664. * @param {Bool} immediate Reposition immediately without an animation.
  665. *
  666. * @return {Void}
  667. */
  668. self.toStart = function (item, immediate) {
  669. to('start', item, immediate);
  670. };
  671. /**
  672. * Animate element or the whole SLIDEE to the end of the frame.
  673. *
  674. * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
  675. * @param {Bool} immediate Reposition immediately without an animation.
  676. *
  677. * @return {Void}
  678. */
  679. self.toEnd = function (item, immediate) {
  680. to('end', item, immediate);
  681. };
  682. /**
  683. * Animate element or the whole SLIDEE to the center of the frame.
  684. *
  685. * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
  686. * @param {Bool} immediate Reposition immediately without an animation.
  687. *
  688. * @return {Void}
  689. */
  690. self.toCenter = function (item, immediate) {
  691. to('center', item, immediate);
  692. };
  693. /**
  694. * Get the index of an item in SLIDEE.
  695. *
  696. * @param {Mixed} item Item DOM element.
  697. *
  698. * @return {Int} Item index, or -1 if not found.
  699. */
  700. function getIndex(item) {
  701. return item != null ?
  702. isNumber(item) ?
  703. item >= 0 && item < items.length ? item : -1 :
  704. $items.index(item) :
  705. -1;
  706. }
  707. // Expose getIndex without lowering the compressibility of it,
  708. // as it is used quite often throughout Sly.
  709. self.getIndex = getIndex;
  710. /**
  711. * Get index of an item in SLIDEE based on a variety of input types.
  712. *
  713. * @param {Mixed} item DOM element, positive or negative integer.
  714. *
  715. * @return {Int} Item index, or -1 if not found.
  716. */
  717. function getRelativeIndex(item) {
  718. return getIndex(isNumber(item) && item < 0 ? item + items.length : item);
  719. }
  720. /**
  721. * Activates an item.
  722. *
  723. * @param {Mixed} item Item DOM element, or index starting at 0.
  724. *
  725. * @return {Mixed} Activated item index or false on fail.
  726. */
  727. function activate(item, force) {
  728. var index = getIndex(item);
  729. if (!itemNav || index < 0) {
  730. return false;
  731. }
  732. // Update classes, last active index, and trigger active event only when there
  733. // has been a change. Otherwise just return the current active index.
  734. if (last.active !== index || force) {
  735. // Update classes
  736. $items.eq(rel.activeItem).removeClass(o.activeClass);
  737. $items.eq(index).addClass(o.activeClass);
  738. last.active = rel.activeItem = index;
  739. updateButtonsState();
  740. trigger('active', index);
  741. }
  742. return index;
  743. }
  744. /**
  745. * Activates an item and helps with further navigation when o.smart is enabled.
  746. *
  747. * @param {Mixed} item Item DOM element, or index starting at 0.
  748. * @param {Bool} immediate Whether to reposition immediately in smart navigation.
  749. *
  750. * @return {Void}
  751. */
  752. self.activate = function (item, immediate) {
  753. var index = activate(item);
  754. // Smart navigation
  755. if (o.smart && index !== false) {
  756. // When centeredNav is enabled, center the element.
  757. // Otherwise, determine where to position the element based on its current position.
  758. // If the element is currently on the far end side of the frame, assume that user is
  759. // moving forward and animate it to the start of the visible frame, and vice versa.
  760. if (centeredNav) {
  761. self.toCenter(index, immediate);
  762. } else if (index >= rel.lastItem) {
  763. self.toStart(index, immediate);
  764. } else if (index <= rel.firstItem) {
  765. self.toEnd(index, immediate);
  766. } else {
  767. resetCycle();
  768. }
  769. }
  770. };
  771. /**
  772. * Activates a page.
  773. *
  774. * @param {Int} index Page index, starting from 0.
  775. * @param {Bool} immediate Whether to reposition immediately without animation.
  776. *
  777. * @return {Void}
  778. */
  779. self.activatePage = function (index, immediate) {
  780. if (isNumber(index)) {
  781. slideTo(pages[within(index, 0, pages.length - 1)], immediate);
  782. }
  783. };
  784. /**
  785. * Return relative positions of items based on their visibility within FRAME.
  786. *
  787. * @param {Int} slideePos Position of SLIDEE.
  788. *
  789. * @return {Void}
  790. */
  791. function getRelatives(slideePos) {
  792. slideePos = within(isNumber(slideePos) ? slideePos : pos.dest, pos.start, pos.end);
  793. var relatives = {};
  794. var centerOffset = forceCenteredNav ? 0 : frameSize / 2;
  795. // Determine active page
  796. if (!parallax) {
  797. for (var p = 0, pl = pages.length; p < pl; p++) {
  798. if (slideePos >= pos.end || p === pages.length - 1) {
  799. relatives.activePage = pages.length - 1;
  800. break;
  801. }
  802. if (slideePos <= pages[p] + centerOffset) {
  803. relatives.activePage = p;
  804. break;
  805. }
  806. }
  807. }
  808. // Relative item indexes
  809. if (itemNav) {
  810. var first = false;
  811. var last = false;
  812. var center = false;
  813. // From start
  814. for (var i = 0, il = items.length; i < il; i++) {
  815. // First item
  816. if (first === false && slideePos <= items[i].start + items[i].half) {
  817. first = i;
  818. }
  819. // Center item
  820. if (center === false && slideePos <= items[i].center + items[i].half) {
  821. center = i;
  822. }
  823. // Last item
  824. if (i === il - 1 || slideePos <= items[i].end + items[i].half) {
  825. last = i;
  826. break;
  827. }
  828. }
  829. // Safe assignment, just to be sure the false won't be returned
  830. relatives.firstItem = isNumber(first) ? first : 0;
  831. relatives.centerItem = isNumber(center) ? center : relatives.firstItem;
  832. relatives.lastItem = isNumber(last) ? last : relatives.centerItem;
  833. }
  834. return relatives;
  835. }
  836. /**
  837. * Update object with relative positions.
  838. *
  839. * @param {Int} newPos
  840. *
  841. * @return {Void}
  842. */
  843. function updateRelatives(newPos) {
  844. $.extend(rel, getRelatives(newPos));
  845. }
  846. /**
  847. * Disable navigation buttons when needed.
  848. *
  849. * Adds disabledClass, and when the button is <button> or <input>, activates :disabled state.
  850. *
  851. * @return {Void}
  852. */
  853. function updateButtonsState() {
  854. var isStart = pos.dest <= pos.start;
  855. var isEnd = pos.dest >= pos.end;
  856. var slideePosState = (isStart ? 1 : 0) | (isEnd ? 2 : 0);
  857. // Update paging buttons only if there has been a change in SLIDEE position
  858. if (last.slideePosState !== slideePosState) {
  859. last.slideePosState = slideePosState;
  860. if ($prevPageButton.is('button,input')) {
  861. $prevPageButton.prop('disabled', isStart);
  862. }
  863. if ($nextPageButton.is('button,input')) {
  864. $nextPageButton.prop('disabled', isEnd);
  865. }
  866. $prevPageButton.add($backwardButton)[isStart ? 'addClass' : 'removeClass'](o.disabledClass);
  867. $nextPageButton.add($forwardButton)[isEnd ? 'addClass' : 'removeClass'](o.disabledClass);
  868. }
  869. // Forward & Backward buttons need a separate state caching because we cannot "property disable"
  870. // them while they are being used, as disabled buttons stop emitting mouse events.
  871. if (last.fwdbwdState !== slideePosState && dragging.released) {
  872. last.fwdbwdState = slideePosState;
  873. if ($backwardButton.is('button,input')) {
  874. $backwardButton.prop('disabled', isStart);
  875. }
  876. if ($forwardButton.is('button,input')) {
  877. $forwardButton.prop('disabled', isEnd);
  878. }
  879. }
  880. // Item navigation
  881. if (itemNav && rel.activeItem != null) {
  882. var isFirst = rel.activeItem === 0;
  883. var isLast = rel.activeItem >= items.length - 1;
  884. var itemsButtonState = (isFirst ? 1 : 0) | (isLast ? 2 : 0);
  885. if (last.itemsButtonState !== itemsButtonState) {
  886. last.itemsButtonState = itemsButtonState;
  887. if ($prevButton.is('button,input')) {
  888. $prevButton.prop('disabled', isFirst);
  889. }
  890. if ($nextButton.is('button,input')) {
  891. $nextButton.prop('disabled', isLast);
  892. }
  893. $prevButton[isFirst ? 'addClass' : 'removeClass'](o.disabledClass);
  894. $nextButton[isLast ? 'addClass' : 'removeClass'](o.disabledClass);
  895. }
  896. }
  897. }
  898. /**
  899. * Resume cycling.
  900. *
  901. * @param {Int} priority Resume pause with priority lower or equal than this. Used internally for pauseOnHover.
  902. *
  903. * @return {Void}
  904. */
  905. self.resume = function (priority) {
  906. if (!o.cycleBy || !o.cycleInterval || o.cycleBy === 'items' && (!items[0] || rel.activeItem == null) || priority < self.isPaused) {
  907. return;
  908. }
  909. self.isPaused = 0;
  910. if (cycleID) {
  911. cycleID = clearTimeout(cycleID);
  912. } else {
  913. trigger('resume');
  914. }
  915. cycleID = setTimeout(function () {
  916. trigger('cycle');
  917. switch (o.cycleBy) {
  918. case 'items':
  919. self.activate(rel.activeItem >= items.length - 1 ? 0 : rel.activeItem + 1);
  920. break;
  921. case 'pages':
  922. self.activatePage(rel.activePage >= pages.length - 1 ? 0 : rel.activePage + 1);
  923. break;
  924. }
  925. }, o.cycleInterval);
  926. };
  927. /**
  928. * Pause cycling.
  929. *
  930. * @param {Int} priority Pause priority. 100 is default. Used internally for pauseOnHover.
  931. *
  932. * @return {Void}
  933. */
  934. self.pause = function (priority) {
  935. if (priority < self.isPaused) {
  936. return;
  937. }
  938. self.isPaused = priority || 100;
  939. if (cycleID) {
  940. cycleID = clearTimeout(cycleID);
  941. trigger('pause');
  942. }
  943. };
  944. /**
  945. * Toggle cycling.
  946. *
  947. * @return {Void}
  948. */
  949. self.toggle = function () {
  950. self[cycleID ? 'pause' : 'resume']();
  951. };
  952. /**
  953. * Updates a signle or multiple option values.
  954. *
  955. * @param {Mixed} name Name of the option that should be updated, or object that will extend the options.
  956. * @param {Mixed} value New option value.
  957. *
  958. * @return {Void}
  959. */
  960. self.set = function (name, value) {
  961. if ($.isPlainObject(name)) {
  962. $.extend(o, name);
  963. } else if (o.hasOwnProperty(name)) {
  964. o[name] = value;
  965. }
  966. };
  967. /**
  968. * Add one or multiple items to the SLIDEE end, or a specified position index.
  969. *
  970. * @param {Mixed} element Node element, or HTML string.
  971. * @param {Int} index Index of a new item position. By default item is appended at the end.
  972. *
  973. * @return {Void}
  974. */
  975. self.add = function (element, index) {
  976. var $element = $(element);
  977. if (itemNav) {
  978. // Insert the element(s)
  979. if (index == null || !items[0] || index >= items.length) {
  980. $element.appendTo($slidee);
  981. } else if (items.length) {
  982. $element.insertBefore(items[index].el);
  983. }
  984. // Adjust the activeItem index
  985. if (rel.activeItem != null && index <= rel.activeItem) {
  986. last.active = rel.activeItem += $element.length;
  987. }
  988. } else {
  989. $slidee.append($element);
  990. }
  991. // Reload
  992. load();
  993. };
  994. /**
  995. * Remove an item from SLIDEE.
  996. *
  997. * @param {Mixed} element Item index, or DOM element.
  998. * @param {Int} index Index of a new item position. By default item is appended at the end.
  999. *
  1000. * @return {Void}
  1001. */
  1002. self.remove = function (element) {
  1003. if (itemNav) {
  1004. var index = getRelativeIndex(element);
  1005. if (index > -1) {
  1006. // Remove the element
  1007. $items.eq(index).remove();
  1008. // If the current item is being removed, activate new one after reload
  1009. var reactivate = index === rel.activeItem;
  1010. // Adjust the activeItem index
  1011. if (rel.activeItem != null && index < rel.activeItem) {
  1012. last.active = --rel.activeItem;
  1013. }
  1014. // Reload
  1015. load();
  1016. // Activate new item at the removed position
  1017. if (reactivate) {
  1018. last.active = null;
  1019. self.activate(rel.activeItem);
  1020. }
  1021. }
  1022. } else {
  1023. $(element).remove();
  1024. load();
  1025. }
  1026. };
  1027. /**
  1028. * Helps re-arranging items.
  1029. *
  1030. * @param {Mixed} item Item DOM element, or index starting at 0. Use negative numbers to select items from the end.
  1031. * @param {Mixed} position Item insertion anchor. Accepts same input types as item argument.
  1032. * @param {Bool} after Insert after instead of before the anchor.
  1033. *
  1034. * @return {Void}
  1035. */
  1036. function moveItem(item, position, after) {
  1037. item = getRelativeIndex(item);
  1038. position = getRelativeIndex(position);
  1039. // Move only if there is an actual change requested
  1040. if (item > -1 && position > -1 && item !== position && (!after || position !== item - 1) && (after || position !== item + 1)) {
  1041. $items.eq(item)[after ? 'insertAfter' : 'insertBefore'](items[position].el);
  1042. var shiftStart = item < position ? item : (after ? position : position - 1);
  1043. var shiftEnd = item > position ? item : (after ? position + 1 : position);
  1044. var shiftsUp = item > position;
  1045. // Update activeItem index
  1046. if (rel.activeItem != null) {
  1047. if (item === rel.activeItem) {
  1048. last.active = rel.activeItem = after ? (shiftsUp ? position + 1 : position) : (shiftsUp ? position : position - 1);
  1049. } else if (rel.activeItem > shiftStart && rel.activeItem < shiftEnd) {
  1050. last.active = rel.activeItem += shiftsUp ? 1 : -1;
  1051. }
  1052. }
  1053. // Reload
  1054. load();
  1055. }
  1056. }
  1057. /**
  1058. * Move item after the target anchor.
  1059. *
  1060. * @param {Mixed} item Item to be moved. Can be DOM element or item index.
  1061. * @param {Mixed} position Target position anchor. Can be DOM element or item index.
  1062. *
  1063. * @return {Void}
  1064. */
  1065. self.moveAfter = function (item, position) {
  1066. moveItem(item, position, 1);
  1067. };
  1068. /**
  1069. * Move item before the target anchor.
  1070. *
  1071. * @param {Mixed} item Item to be moved. Can be DOM element or item index.
  1072. * @param {Mixed} position Target position anchor. Can be DOM element or item index.
  1073. *
  1074. * @return {Void}
  1075. */
  1076. self.moveBefore = function (item, position) {
  1077. moveItem(item, position);
  1078. };
  1079. /**
  1080. * Registers callbacks.
  1081. *
  1082. * @param {Mixed} name Event name, or callbacks map.
  1083. * @param {Mixed} fn Callback, or an array of callback functions.
  1084. *
  1085. * @return {Void}
  1086. */
  1087. self.on = function (name, fn) {
  1088. // Callbacks map
  1089. if (type(name) === 'object') {
  1090. for (var key in name) {
  1091. if (name.hasOwnProperty(key)) {
  1092. self.on(key, name[key]);
  1093. }
  1094. }
  1095. // Callback
  1096. } else if (type(fn) === 'function') {
  1097. var names = name.split(' ');
  1098. for (var n = 0, nl = names.length; n < nl; n++) {
  1099. callbacks[names[n]] = callbacks[names[n]] || [];
  1100. if (callbackIndex(names[n], fn) === -1) {
  1101. callbacks[names[n]].push(fn);
  1102. }
  1103. }
  1104. // Callbacks array
  1105. } else if (type(fn) === 'array') {
  1106. for (var f = 0, fl = fn.length; f < fl; f++) {
  1107. self.on(name, fn[f]);
  1108. }
  1109. }
  1110. };
  1111. /**
  1112. * Registers callbacks to be executed only once.
  1113. *
  1114. * @param {Mixed} name Event name, or callbacks map.
  1115. * @param {Mixed} fn Callback, or an array of callback functions.
  1116. *
  1117. * @return {Void}
  1118. */
  1119. self.one = function (name, fn) {
  1120. function proxy() {
  1121. fn.apply(self, arguments);
  1122. self.off(name, proxy);
  1123. }
  1124. self.on(name, proxy);
  1125. };
  1126. /**
  1127. * Remove one or all callbacks.
  1128. *
  1129. * @param {String} name Event name.
  1130. * @param {Mixed} fn Callback, or an array of callback functions. Omit to remove all callbacks.
  1131. *
  1132. * @return {Void}
  1133. */
  1134. self.off = function (name, fn) {
  1135. if (fn instanceof Array) {
  1136. for (var f = 0, fl = fn.length; f < fl; f++) {
  1137. self.off(name, fn[f]);
  1138. }
  1139. } else {
  1140. var names = name.split(' ');
  1141. for (var n = 0, nl = names.length; n < nl; n++) {
  1142. callbacks[names[n]] = callbacks[names[n]] || [];
  1143. if (fn == null) {
  1144. callbacks[names[n]].length = 0;
  1145. } else {
  1146. var index = callbackIndex(names[n], fn);
  1147. if (index !== -1) {
  1148. callbacks[names[n]].splice(index, 1);
  1149. }
  1150. }
  1151. }
  1152. }
  1153. };
  1154. /**
  1155. * Returns callback array index.
  1156. *
  1157. * @param {String} name Event name.
  1158. * @param {Function} fn Function
  1159. *
  1160. * @return {Int} Callback array index, or -1 if isn't registered.
  1161. */
  1162. function callbackIndex(name, fn) {
  1163. for (var i = 0, l = callbacks[name].length; i < l; i++) {
  1164. if (callbacks[name][i] === fn) {
  1165. return i;
  1166. }
  1167. }
  1168. return -1;
  1169. }
  1170. /**
  1171. * Reset next cycle timeout.
  1172. *
  1173. * @return {Void}
  1174. */
  1175. function resetCycle() {
  1176. if (dragging.released && !self.isPaused) {
  1177. self.resume();
  1178. }
  1179. }
  1180. /**
  1181. * Calculate SLIDEE representation of handle position.
  1182. *
  1183. * @param {Int} handlePos
  1184. *
  1185. * @return {Int}
  1186. */
  1187. function handleToSlidee(handlePos) {
  1188. return round(within(handlePos, hPos.start, hPos.end) / hPos.end * (pos.end - pos.start)) + pos.start;
  1189. }
  1190. /**
  1191. * Keeps track of a dragging delta history.
  1192. *
  1193. * @return {Void}
  1194. */
  1195. function draggingHistoryTick() {
  1196. // Looking at this, I know what you're thinking :) But as we need only 4 history states, doing it this way
  1197. // as opposed to a proper loop is ~25 bytes smaller (when minified with GCC), a lot faster, and doesn't
  1198. // generate garbage. The loop version would create 2 new variables on every tick. Unexaptable!
  1199. dragging.history[0] = dragging.history[1];
  1200. dragging.history[1] = dragging.history[2];
  1201. dragging.history[2] = dragging.history[3];
  1202. dragging.history[3] = dragging.delta;
  1203. }
  1204. /**
  1205. * Initialize continuous movement.
  1206. *
  1207. * @return {Void}
  1208. */
  1209. function continuousInit(source) {
  1210. dragging.released = 0;
  1211. dragging.source = source;
  1212. dragging.slidee = source === 'slidee';
  1213. }
  1214. /**
  1215. * Dragging initiator.
  1216. *
  1217. * @param {Event} event
  1218. *
  1219. * @return {Void}
  1220. */
  1221. function dragInit(event) {
  1222. var isTouch = event.type === 'touchstart';
  1223. var source = event.data.source;
  1224. var isSlidee = source === 'slidee';
  1225. // Ignore when already in progress, or interactive element in non-touch navivagion
  1226. if (dragging.init || !isTouch && isInteractive(event.target)) {
  1227. return;
  1228. }
  1229. // Handle dragging conditions
  1230. if (source === 'handle' && (!o.dragHandle || hPos.start === hPos.end)) {
  1231. return;
  1232. }
  1233. // SLIDEE dragging conditions
  1234. if (isSlidee && !(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) {
  1235. return;
  1236. }
  1237. if (!isTouch) {
  1238. // prevents native image dragging in Firefox
  1239. stopDefault(event);
  1240. }
  1241. // Reset dragging object
  1242. continuousInit(source);
  1243. // Properties used in dragHandler
  1244. dragging.init = 0;
  1245. dragging.$source = $(event.target);
  1246. dragging.touch = isTouch;
  1247. dragging.pointer = isTouch ? event.originalEvent.touches[0] : event;
  1248. dragging.initX = dragging.pointer.pageX;
  1249. dragging.initY = dragging.pointer.pageY;
  1250. dragging.initPos = isSlidee ? pos.cur : hPos.cur;
  1251. dragging.start = +new Date();
  1252. dragging.time = 0;
  1253. dragging.path = 0;
  1254. dragging.delta = 0;
  1255. dragging.locked = 0;
  1256. dragging.history = [0, 0, 0, 0];
  1257. dragging.pathToLock = isSlidee ? isTouch ? 30 : 10 : 0;
  1258. // Bind dragging events
  1259. $doc.on(isTouch ? dragTouchEvents : dragMouseEvents, dragHandler);
  1260. // Pause ongoing cycle
  1261. self.pause(1);
  1262. // Add dragging class
  1263. (isSlidee ? $slidee : $handle).addClass(o.draggedClass);
  1264. // Trigger moveStart event
  1265. trigger('moveStart');
  1266. // Keep track of a dragging path history. This is later used in the
  1267. // dragging release swing calculation when dragging SLIDEE.
  1268. if (isSlidee) {
  1269. historyID = setInterval(draggingHistoryTick, 10);
  1270. }
  1271. }
  1272. /**
  1273. * Handler for dragging scrollbar handle or SLIDEE.
  1274. *
  1275. * @param {Event} event
  1276. *
  1277. * @return {Void}
  1278. */
  1279. function dragHandler(event) {
  1280. dragging.released = event.type === 'mouseup' || event.type === 'touchend';
  1281. dragging.pointer = dragging.touch ? event.originalEvent[dragging.released ? 'changedTouches' : 'touches'][0] : event;
  1282. dragging.pathX = dragging.pointer.pageX - dragging.initX;
  1283. dragging.pathY = dragging.pointer.pageY - dragging.initY;
  1284. dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2));
  1285. dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY;
  1286. if (!dragging.released && dragging.path < 1) return;
  1287. // We haven't decided whether this is a drag or not...
  1288. if (!dragging.init) {
  1289. // If the drag path was very short, maybe it's not a drag?
  1290. if (dragging.path < o.dragThreshold) {
  1291. // If the pointer was released, the path will not become longer and it's
  1292. // definitely not a drag. If not released yet, decide on next iteration
  1293. return dragging.released ? dragEnd() : undefined;
  1294. }
  1295. else {
  1296. // If dragging path is sufficiently long we can confidently start a drag
  1297. // if drag is in different direction than scroll, ignore it
  1298. if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) {
  1299. dragging.init = 1;
  1300. } else {
  1301. return dragEnd();
  1302. }
  1303. }
  1304. }
  1305. stopDefault(event);
  1306. // Disable click on a source element, as it is unwelcome when dragging
  1307. if (!dragging.locked && dragging.path > dragging.pathToLock && dragging.slidee) {
  1308. dragging.locked = 1;
  1309. dragging.$source.on(clickEvent, disableOneEvent);
  1310. }
  1311. // Cancel dragging on release
  1312. if (dragging.released) {
  1313. dragEnd();
  1314. // Adjust path with a swing on mouse release
  1315. if (o.releaseSwing && dragging.slidee) {
  1316. dragging.swing = (dragging.delta - dragging.history[0]) / 40 * 300;
  1317. dragging.delta += dragging.swing;
  1318. dragging.tweese = abs(dragging.swing) > 10;
  1319. }
  1320. }
  1321. slideTo(dragging.slidee ? round(dragging.initPos - dragging.delta) : handleToSlidee(dragging.initPos + dragging.delta));
  1322. }
  1323. /**
  1324. * Stops dragging and cleans up after it.
  1325. *
  1326. * @return {Void}
  1327. */
  1328. function dragEnd() {
  1329. clearInterval(historyID);
  1330. dragging.released = true;
  1331. $doc.off(dragging.touch ? dragTouchEvents : dragMouseEvents, dragHandler);
  1332. (dragging.slidee ? $slidee : $handle).removeClass(o.draggedClass);
  1333. // Make sure that disableOneEvent is not active in next tick.
  1334. setTimeout(function () {
  1335. dragging.$source.off(clickEvent, disableOneEvent);
  1336. });
  1337. // Normally, this is triggered in render(), but if there
  1338. // is nothing to render, we have to do it manually here.
  1339. if (pos.cur === pos.dest && dragging.init) {
  1340. trigger('moveEnd');
  1341. }
  1342. // Resume ongoing cycle
  1343. self.resume(1);
  1344. dragging.init = 0;
  1345. }
  1346. /**
  1347. * Check whether element is interactive.
  1348. *
  1349. * @return {Boolean}
  1350. */
  1351. function isInteractive(element) {
  1352. return ~$.inArray(element.nodeName, interactiveElements) || $(element).is(o.interactive);
  1353. }
  1354. /**
  1355. * Continuous movement cleanup on mouseup.
  1356. *
  1357. * @return {Void}
  1358. */
  1359. function movementReleaseHandler() {
  1360. self.stop();
  1361. $doc.off('mouseup', movementReleaseHandler);
  1362. }
  1363. /**
  1364. * Buttons navigation handler.
  1365. *
  1366. * @param {Event} event
  1367. *
  1368. * @return {Void}
  1369. */
  1370. function buttonsHandler(event) {
  1371. /*jshint validthis:true */
  1372. stopDefault(event);
  1373. switch (this) {
  1374. case $forwardButton[0]:
  1375. case $backwardButton[0]:
  1376. self.moveBy($forwardButton.is(this) ? o.moveBy : -o.moveBy);
  1377. $doc.on('mouseup', movementReleaseHandler);
  1378. break;
  1379. case $prevButton[0]:
  1380. self.prev();
  1381. break;
  1382. case $nextButton[0]:
  1383. self.next();
  1384. break;
  1385. case $prevPageButton[0]:
  1386. self.prevPage();
  1387. break;
  1388. case $nextPageButton[0]:
  1389. self.nextPage();
  1390. break;
  1391. }
  1392. }
  1393. /**
  1394. * Mouse wheel delta normalization.
  1395. *
  1396. * @param {Event} event
  1397. *
  1398. * @return {Int}
  1399. */
  1400. function normalizeWheelDelta(event) {
  1401. // wheelDelta needed only for IE8-
  1402. scrolling.curDelta = ((o.horizontal ? event.deltaY || event.deltaX : event.deltaY) || -event.wheelDelta);
  1403. scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100;
  1404. if (!itemNav) {
  1405. return scrolling.curDelta;
  1406. }
  1407. time = +new Date();
  1408. if (scrolling.last < time - scrolling.resetTime) {
  1409. scrolling.delta = 0;
  1410. }
  1411. scrolling.last = time;
  1412. scrolling.delta += scrolling.curDelta;
  1413. if (abs(scrolling.delta) < 1) {
  1414. scrolling.finalDelta = 0;
  1415. } else {
  1416. scrolling.finalDelta = round(scrolling.delta / 1);
  1417. scrolling.delta %= 1;
  1418. }
  1419. return scrolling.finalDelta;
  1420. }
  1421. /**
  1422. * Mouse scrolling handler.
  1423. *
  1424. * @param {Event} event
  1425. *
  1426. * @return {Void}
  1427. */
  1428. function scrollHandler(event) {
  1429. // Mark event as originating in a Sly instance
  1430. event.originalEvent[namespace] = self;
  1431. // Don't hijack global scrolling
  1432. var time = +new Date();
  1433. if (lastGlobalWheel + o.scrollHijack > time && $scrollSource[0] !== document && $scrollSource[0] !== window) {
  1434. lastGlobalWheel = time;
  1435. return;
  1436. }
  1437. // Ignore if there is no scrolling to be done
  1438. if (!o.scrollBy || pos.start === pos.end) {
  1439. return;
  1440. }
  1441. var delta = normalizeWheelDelta(event.originalEvent);
  1442. // Trap scrolling only when necessary and/or requested
  1443. if (o.scrollTrap || delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) {
  1444. stopDefault(event, 1);
  1445. }
  1446. self.slideBy(o.scrollBy * delta);
  1447. }
  1448. /**
  1449. * Scrollbar click handler.
  1450. *
  1451. * @param {Event} event
  1452. *
  1453. * @return {Void}
  1454. */
  1455. function scrollbarHandler(event) {
  1456. // Only clicks on scroll bar. Ignore the handle.
  1457. if (o.clickBar && event.target === $sb[0]) {
  1458. stopDefault(event);
  1459. // Calculate new handle position and sync SLIDEE to it
  1460. slideTo(handleToSlidee((o.horizontal ? event.pageX - $sb.offset().left : event.pageY - $sb.offset().top) - handleSize / 2));
  1461. }
  1462. }
  1463. /**
  1464. * Keyboard input handler.
  1465. *
  1466. * @param {Event} event
  1467. *
  1468. * @return {Void}
  1469. */
  1470. function keyboardHandler(event) {
  1471. if (!o.keyboardNavBy) {
  1472. return;
  1473. }
  1474. switch (event.which) {
  1475. // Left or Up
  1476. case o.horizontal ? 37 : 38:
  1477. stopDefault(event);
  1478. self[o.keyboardNavBy === 'pages' ? 'prevPage' : 'prev']();
  1479. break;
  1480. // Right or Down
  1481. case o.horizontal ? 39 : 40:
  1482. stopDefault(event);
  1483. self[o.keyboardNavBy === 'pages' ? 'nextPage' : 'next']();
  1484. break;
  1485. }
  1486. }
  1487. /**
  1488. * Click on item activation handler.
  1489. *
  1490. * @param {Event} event
  1491. *
  1492. * @return {Void}
  1493. */
  1494. function activateHandler(event) {
  1495. /*jshint validthis:true */
  1496. // Ignore clicks on interactive elements.
  1497. if (isInteractive(this)) {
  1498. event.originalEvent[namespace + 'ignore'] = true;
  1499. return;
  1500. }
  1501. // Ignore events that:
  1502. // - are not originating from direct SLIDEE children
  1503. // - originated from interactive elements
  1504. if (this.parentNode !== $slidee[0] || event.originalEvent[namespace + 'ignore']) return;
  1505. self.activate(this);
  1506. }
  1507. /**
  1508. * Click on page button handler.
  1509. *
  1510. * @param {Event} event
  1511. *
  1512. * @return {Void}
  1513. */
  1514. function activatePageHandler() {
  1515. /*jshint validthis:true */
  1516. // Accept only events from direct pages bar children.
  1517. if (this.parentNode === $pb[0]) {
  1518. self.activatePage($pages.index(this));
  1519. }
  1520. }
  1521. /**
  1522. * Pause on hover handler.
  1523. *
  1524. * @param {Event} event
  1525. *
  1526. * @return {Void}
  1527. */
  1528. function pauseOnHoverHandler(event) {
  1529. if (o.pauseOnHover) {
  1530. self[event.type === 'mouseenter' ? 'pause' : 'resume'](2);
  1531. }
  1532. }
  1533. /**
  1534. * Trigger callbacks for event.
  1535. *
  1536. * @param {String} name Event name.
  1537. * @param {Mixed} argX Arguments passed to callbacks.
  1538. *
  1539. * @return {Void}
  1540. */
  1541. function trigger(name, arg1) {
  1542. if (callbacks[name]) {
  1543. l = callbacks[name].length;
  1544. // Callbacks will be stored and executed from a temporary array to not
  1545. // break the execution queue when one of the callbacks unbinds itself.
  1546. tmpArray.length = 0;
  1547. for (i = 0; i < l; i++) {
  1548. tmpArray.push(callbacks[name][i]);
  1549. }
  1550. // Execute the callbacks
  1551. for (i = 0; i < l; i++) {
  1552. tmpArray[i].call(self, name, arg1);
  1553. }
  1554. }
  1555. }
  1556. /**
  1557. * Destroys instance and everything it created.
  1558. *
  1559. * @return {Void}
  1560. */
  1561. self.destroy = function () {
  1562. // Remove the reference to itself
  1563. Sly.removeInstance(frame);
  1564. // Unbind all events
  1565. $scrollSource
  1566. .add($handle)
  1567. .add($sb)
  1568. .add($pb)
  1569. .add($forwardButton)
  1570. .add($backwardButton)
  1571. .add($prevButton)
  1572. .add($nextButton)
  1573. .add($prevPageButton)
  1574. .add($nextPageButton)
  1575. .off('.' + namespace);
  1576. // Unbinding specifically as to not nuke out other instances
  1577. $doc.off('keydown', keyboardHandler);
  1578. // Remove classes
  1579. $prevButton
  1580. .add($nextButton)
  1581. .add($prevPageButton)
  1582. .add($nextPageButton)
  1583. .removeClass(o.disabledClass);
  1584. if ($items && rel.activeItem != null) {
  1585. $items.eq(rel.activeItem).removeClass(o.activeClass);
  1586. }
  1587. // Remove page items
  1588. $pb.empty();
  1589. if (!parallax) {
  1590. // Unbind events from frame
  1591. $frame.off('.' + namespace);
  1592. // Restore original styles
  1593. frameStyles.restore();
  1594. slideeStyles.restore();
  1595. sbStyles.restore();
  1596. handleStyles.restore();
  1597. // Remove the instance from element data storage
  1598. $.removeData(frame, namespace);
  1599. }
  1600. // Clean up collections
  1601. items.length = pages.length = 0;
  1602. last = {};
  1603. // Reset initialized status and return the instance
  1604. self.initialized = 0;
  1605. return self;
  1606. };
  1607. /**
  1608. * Initialize.
  1609. *
  1610. * @return {Object}
  1611. */
  1612. self.init = function () {
  1613. if (self.initialized) {
  1614. return;
  1615. }
  1616. // Disallow multiple instances on the same element
  1617. if (Sly.getInstance(frame)) throw new Error('There is already a Sly instance on this element');
  1618. // Store the reference to itself
  1619. Sly.storeInstance(frame, self);
  1620. // Register callbacks map
  1621. self.on(callbackMap);
  1622. // Save styles
  1623. var holderProps = ['overflow', 'position'];
  1624. var movableProps = ['position', 'webkitTransform', 'msTransform', 'transform', 'left', 'top', 'width', 'height'];
  1625. frameStyles.save.apply(frameStyles, holderProps);
  1626. sbStyles.save.apply(sbStyles, holderProps);
  1627. slideeStyles.save.apply(slideeStyles, movableProps);
  1628. handleStyles.save.apply(handleStyles, movableProps);
  1629. // Set required styles
  1630. var $movables = $handle;
  1631. if (!parallax) {
  1632. $movables = $movables.add($slidee);
  1633. $frame.css('overflow', 'hidden');
  1634. if (!transform && $frame.css('position') === 'static') {
  1635. $frame.css('position', 'relative');
  1636. }
  1637. }
  1638. if (transform) {
  1639. if (gpuAcceleration) {
  1640. $movables.css(transform, gpuAcceleration);
  1641. }
  1642. } else {
  1643. if ($sb.css('position') === 'static') {
  1644. $sb.css('position', 'relative');
  1645. }
  1646. $movables.css({ position: 'absolute' });
  1647. }
  1648. // Navigation buttons
  1649. if (o.forward) {
  1650. $forwardButton.on(mouseDownEvent, buttonsHandler);
  1651. }
  1652. if (o.backward) {
  1653. $backwardButton.on(mouseDownEvent, buttonsHandler);
  1654. }
  1655. if (o.prev) {
  1656. $prevButton.on(clickEvent, buttonsHandler);
  1657. }
  1658. if (o.next) {
  1659. $nextButton.on(clickEvent, buttonsHandler);
  1660. }
  1661. if (o.prevPage) {
  1662. $prevPageButton.on(clickEvent, buttonsHandler);
  1663. }
  1664. if (o.nextPage) {
  1665. $nextPageButton.on(clickEvent, buttonsHandler);
  1666. }
  1667. // Scrolling navigation
  1668. $scrollSource.on(wheelEvent, scrollHandler);
  1669. // Clicking on scrollbar navigation
  1670. if ($sb[0]) {
  1671. $sb.on(clickEvent, scrollbarHandler);
  1672. }
  1673. // Click on items navigation
  1674. if (itemNav && o.activateOn) {
  1675. $frame.on(o.activateOn + '.' + namespace, '*', activateHandler);
  1676. }
  1677. // Pages navigation
  1678. if ($pb[0] && o.activatePageOn) {
  1679. $pb.on(o.activatePageOn + '.' + namespace, '*', activatePageHandler);
  1680. }
  1681. // Dragging navigation
  1682. $dragSource.on(dragInitEvents, { source: 'slidee' }, dragInit);
  1683. // Scrollbar dragging navigation
  1684. if ($handle) {
  1685. $handle.on(dragInitEvents, { source: 'handle' }, dragInit);
  1686. }
  1687. // Keyboard navigation
  1688. $doc.on('keydown', keyboardHandler);
  1689. if (!parallax) {
  1690. // Pause on hover
  1691. $frame.on('mouseenter.' + namespace + ' mouseleave.' + namespace, pauseOnHoverHandler);
  1692. // Reset native FRAME element scroll
  1693. $frame.on('scroll.' + namespace, resetScroll);
  1694. }
  1695. // Mark instance as initialized
  1696. self.initialized = 1;
  1697. // Load
  1698. load(true);
  1699. // Initiate automatic cycling
  1700. if (o.cycleBy && !parallax) {
  1701. self[o.startPaused ? 'pause' : 'resume']();
  1702. }
  1703. // Return instance
  1704. return self;
  1705. };
  1706. }
  1707. Sly.getInstance = function (element) {
  1708. return $.data(element, namespace);
  1709. };
  1710. Sly.storeInstance = function (element, sly) {
  1711. return $.data(element, namespace, sly);
  1712. };
  1713. Sly.removeInstance = function (element) {
  1714. return $.removeData(element, namespace);
  1715. };
  1716. /**
  1717. * Return type of the value.
  1718. *
  1719. * @param {Mixed} value
  1720. *
  1721. * @return {String}
  1722. */
  1723. function type(value) {
  1724. if (value == null) {
  1725. return String(value);
  1726. }
  1727. if (typeof value === 'object' || typeof value === 'function') {
  1728. return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object';
  1729. }
  1730. return typeof value;
  1731. }
  1732. /**
  1733. * Event preventDefault & stopPropagation helper.
  1734. *
  1735. * @param {Event} event Event object.
  1736. * @param {Bool} noBubbles Cancel event bubbling.
  1737. *
  1738. * @return {Void}
  1739. */
  1740. function stopDefault(event, noBubbles) {
  1741. event.preventDefault();
  1742. if (noBubbles) {
  1743. event.stopPropagation();
  1744. }
  1745. }
  1746. /**
  1747. * Disables an event it was triggered on and unbinds itself.
  1748. *
  1749. * @param {Event} event
  1750. *
  1751. * @return {Void}
  1752. */
  1753. function disableOneEvent(event) {
  1754. /*jshint validthis:true */
  1755. stopDefault(event, 1);
  1756. $(this).off(event.type, disableOneEvent);
  1757. }
  1758. /**
  1759. * Resets native element scroll values to 0.
  1760. *
  1761. * @return {Void}
  1762. */
  1763. function resetScroll() {
  1764. /*jshint validthis:true */
  1765. this.scrollLeft = 0;
  1766. this.scrollTop = 0;
  1767. }
  1768. /**
  1769. * Check if variable is a number.
  1770. *
  1771. * @param {Mixed} value
  1772. *
  1773. * @return {Boolean}
  1774. */
  1775. function isNumber(value) {
  1776. return !isNaN(parseFloat(value)) && isFinite(value);
  1777. }
  1778. /**
  1779. * Parse style to pixels.
  1780. *
  1781. * @param {Object} $item jQuery object with element.
  1782. * @param {Property} property CSS property to get the pixels from.
  1783. *
  1784. * @return {Int}
  1785. */
  1786. function getPx($item, property) {
  1787. return 0 | round(String($item.css(property)).replace(/[^\-0-9.]/g, ''));
  1788. }
  1789. /**
  1790. * Make sure that number is within the limits.
  1791. *
  1792. * @param {Number} number
  1793. * @param {Number} min
  1794. * @param {Number} max
  1795. *
  1796. * @return {Number}
  1797. */
  1798. function within(number, min, max) {
  1799. return number < min ? min : number > max ? max : number;
  1800. }
  1801. /**
  1802. * Saves element styles for later restoration.
  1803. *
  1804. * Example:
  1805. * var styles = new StyleRestorer(frame);
  1806. * styles.save('position');
  1807. * element.style.position = 'absolute';
  1808. * styles.restore(); // restores to state before the assignment above
  1809. *
  1810. * @param {Element} element
  1811. */
  1812. function StyleRestorer(element) {
  1813. var self = {};
  1814. self.style = {};
  1815. self.save = function () {
  1816. if (!element || !element.nodeType) return;
  1817. for (var i = 0; i < arguments.length; i++) {
  1818. self.style[arguments[i]] = element.style[arguments[i]];
  1819. }
  1820. return self;
  1821. };
  1822. self.restore = function () {
  1823. if (!element || !element.nodeType) return;
  1824. for (var prop in self.style) {
  1825. if (self.style.hasOwnProperty(prop)) element.style[prop] = self.style[prop];
  1826. }
  1827. return self;
  1828. };
  1829. return self;
  1830. }
  1831. // Local WindowAnimationTiming interface polyfill
  1832. (function (w) {
  1833. rAF = w.requestAnimationFrame
  1834. || w.webkitRequestAnimationFrame
  1835. || fallback;
  1836. /**
  1837. * Fallback implementation.
  1838. */
  1839. var prev = new Date().getTime();
  1840. function fallback(fn) {
  1841. var curr = new Date().getTime();
  1842. var ms = Math.max(0, 16 - (curr - prev));
  1843. var req = setTimeout(fn, ms);
  1844. prev = curr;
  1845. return req;
  1846. }
  1847. /**
  1848. * Cancel.
  1849. */
  1850. var cancel = w.cancelAnimationFrame
  1851. || w.webkitCancelAnimationFrame
  1852. || w.clearTimeout;
  1853. cAF = function(id){
  1854. cancel.call(w, id);
  1855. };
  1856. }(window));
  1857. // Feature detects
  1858. (function () {
  1859. var prefixes = ['', 'Webkit', 'Moz', 'ms', 'O'];
  1860. var el = document.createElement('div');
  1861. function testProp(prop) {
  1862. for (var p = 0, pl = prefixes.length; p < pl; p++) {
  1863. var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop;
  1864. if (el.style[prefixedProp] != null) {
  1865. return prefixedProp;
  1866. }
  1867. }
  1868. }
  1869. // Global support indicators
  1870. transform = testProp('transform');
  1871. gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : '';
  1872. }());
  1873. // Expose class globally
  1874. w[className] = Sly;
  1875. // jQuery proxy
  1876. $.fn[pluginName] = function (options, callbackMap) {
  1877. var method, methodArgs;
  1878. // Attributes logic
  1879. if (!$.isPlainObject(options)) {
  1880. if (type(options) === 'string' || options === false) {
  1881. method = options === false ? 'destroy' : options;
  1882. methodArgs = Array.prototype.slice.call(arguments, 1);
  1883. }
  1884. options = {};
  1885. }
  1886. // Apply to all elements
  1887. return this.each(function (i, element) {
  1888. // Call with prevention against multiple instantiations
  1889. var plugin = Sly.getInstance(element);
  1890. if (!plugin && !method) {
  1891. // Create a new object if it doesn't exist yet
  1892. plugin = new Sly(element, options, callbackMap).init();
  1893. } else if (plugin && method) {
  1894. // Call method
  1895. if (plugin[method]) {
  1896. plugin[method].apply(plugin, methodArgs);
  1897. }
  1898. }
  1899. });
  1900. };
  1901. // Default options
  1902. Sly.defaults = {
  1903. slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE.
  1904. horizontal: false, // Switch to horizontal mode.
  1905. // Item based navigation
  1906. itemNav: null, // Item navigation type. Can be: 'basic', 'centered', 'forceCentered'.
  1907. itemSelector: null, // Select only items that match this selector.
  1908. smart: false, // Repositions the activated item to help with further navigation.
  1909. activateOn: null, // Activate an item on this event. Can be: 'click', 'mouseenter', ...
  1910. activateMiddle: false, // Always activate the item in the middle of the FRAME. forceCentered only.
  1911. // Scrolling
  1912. scrollSource: null, // Element for catching the mouse wheel scrolling. Default is FRAME.
  1913. scrollBy: 0, // Pixels or items to move per one mouse scroll. 0 to disable scrolling.
  1914. scrollHijack: 300, // Milliseconds since last wheel event after which it is acceptable to hijack global scroll.
  1915. scrollTrap: false, // Don't bubble scrolling when hitting scrolling limits.
  1916. // Dragging
  1917. dragSource: null, // Selector or DOM element for catching dragging events. Default is FRAME.
  1918. mouseDragging: false, // Enable navigation by dragging the SLIDEE with mouse cursor.
  1919. touchDragging: false, // Enable navigation by dragging the SLIDEE with touch events.
  1920. releaseSwing: false, // Ease out on dragging swing release.
  1921. swingSpeed: 0.2, // Swing synchronization speed, where: 1 = instant, 0 = infinite.
  1922. elasticBounds: false, // Stretch SLIDEE position limits when dragging past FRAME boundaries.
  1923. dragThreshold: 3, // Distance in pixels before Sly recognizes dragging.
  1924. interactive: null, // Selector for special interactive elements.
  1925. // Scrollbar
  1926. scrollBar: null, // Selector or DOM element for scrollbar container.
  1927. dragHandle: false, // Whether the scrollbar handle should be draggable.
  1928. dynamicHandle: false, // Scrollbar handle represents the ratio between hidden and visible content.
  1929. minHandleSize: 50, // Minimal height or width (depends on sly direction) of a handle in pixels.
  1930. clickBar: false, // Enable navigation by clicking on scrollbar.
  1931. syncSpeed: 0.5, // Handle => SLIDEE synchronization speed, where: 1 = instant, 0 = infinite.
  1932. // Pagesbar
  1933. pagesBar: null, // Selector or DOM element for pages bar container.
  1934. activatePageOn: null, // Event used to activate page. Can be: click, mouseenter, ...
  1935. pageBuilder: // Page item generator.
  1936. function (index) {
  1937. return '<li>' + (index + 1) + '</li>';
  1938. },
  1939. // Navigation buttons
  1940. forward: null, // Selector or DOM element for "forward movement" button.
  1941. backward: null, // Selector or DOM element for "backward movement" button.
  1942. prev: null, // Selector or DOM element for "previous item" button.
  1943. next: null, // Selector or DOM element for "next item" button.
  1944. prevPage: null, // Selector or DOM element for "previous page" button.
  1945. nextPage: null, // Selector or DOM element for "next page" button.
  1946. // Automated cycling
  1947. cycleBy: null, // Enable automatic cycling by 'items' or 'pages'.
  1948. cycleInterval: 5000, // Delay between cycles in milliseconds.
  1949. pauseOnHover: false, // Pause cycling when mouse hovers over the FRAME.
  1950. startPaused: false, // Whether to start in paused sate.
  1951. // Mixed options
  1952. moveBy: 300, // Speed in pixels per second used by forward and backward buttons.
  1953. speed: 0, // Animations speed in milliseconds. 0 to disable animations.
  1954. easing: 'swing', // Easing for duration based (tweening) animations.
  1955. startAt: null, // Starting offset in pixels or items.
  1956. keyboardNavBy: null, // Enable keyboard navigation by 'items' or 'pages'.
  1957. // Classes
  1958. draggedClass: 'dragged', // Class for dragged elements (like SLIDEE or scrollbar handle).
  1959. activeClass: 'active', // Class for active items and pages.
  1960. disabledClass: 'disabled' // Class for disabled navigation elements.
  1961. };
  1962. }(jQuery, window));