1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224 |
- /*!
- * sly 1.6.1 - 8th Aug 2015
- * https://github.com/darsain/sly
- *
- * Licensed under the MIT license.
- * http://opensource.org/licenses/MIT
- */
- ;(function ($, w, undefined) {
- 'use strict';
- var pluginName = 'sly';
- var className = 'Sly';
- var namespace = pluginName;
- // Local WindowAnimationTiming interface
- var cAF = w.cancelAnimationFrame || w.cancelRequestAnimationFrame;
- var rAF = w.requestAnimationFrame;
- // Support indicators
- var transform, gpuAcceleration;
- // Other global values
- var $doc = $(document);
- var dragInitEvents = 'touchstart.' + namespace + ' mousedown.' + namespace;
- var dragMouseEvents = 'mousemove.' + namespace + ' mouseup.' + namespace;
- var dragTouchEvents = 'touchmove.' + namespace + ' touchend.' + namespace;
- var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel.' : 'mousewheel.') + namespace;
- var clickEvent = 'click.' + namespace;
- var mouseDownEvent = 'mousedown.' + namespace;
- var interactiveElements = ['INPUT', 'SELECT', 'BUTTON', 'TEXTAREA'];
- var tmpArray = [];
- var time;
- // Math shorthands
- var abs = Math.abs;
- var sqrt = Math.sqrt;
- var pow = Math.pow;
- var round = Math.round;
- var max = Math.max;
- var min = Math.min;
- // Keep track of last fired global wheel event
- var lastGlobalWheel = 0;
- $doc.on(wheelEvent, function (event) {
- var sly = event.originalEvent[namespace];
- var time = +new Date();
- // Update last global wheel time, but only when event didn't originate
- // in Sly frame, or the origin was less than scrollHijack time ago
- if (!sly || sly.options.scrollHijack < time - lastGlobalWheel) lastGlobalWheel = time;
- });
- /**
- * Sly.
- *
- * @class
- *
- * @param {Element} frame DOM element of sly container.
- * @param {Object} options Object with options.
- * @param {Object} callbackMap Callbacks map.
- */
- function Sly(frame, options, callbackMap) {
- if (!(this instanceof Sly)) return new Sly(frame, options, callbackMap);
- // Extend options
- var o = $.extend({}, Sly.defaults, options);
- // Private variables
- var self = this;
- var parallax = isNumber(frame);
- // Frame
- var $frame = $(frame);
- var $slidee = o.slidee ? $(o.slidee).eq(0) : $frame.children().eq(0);
- var frameSize = 0;
- var slideeSize = 0;
- var pos = {
- start: 0,
- center: 0,
- end: 0,
- cur: 0,
- dest: 0
- };
- // Scrollbar
- var $sb = $(o.scrollBar).eq(0);
- var $handle = $sb.children().eq(0);
- var sbSize = 0;
- var handleSize = 0;
- var hPos = {
- start: 0,
- end: 0,
- cur: 0
- };
- // Pagesbar
- var $pb = $(o.pagesBar);
- var $pages = 0;
- var pages = [];
- // Items
- var $items = 0;
- var items = [];
- var rel = {
- firstItem: 0,
- lastItem: 0,
- centerItem: 0,
- activeItem: null,
- activePage: 0
- };
- // Styles
- var frameStyles = new StyleRestorer($frame[0]);
- var slideeStyles = new StyleRestorer($slidee[0]);
- var sbStyles = new StyleRestorer($sb[0]);
- var handleStyles = new StyleRestorer($handle[0]);
- // Navigation type booleans
- var basicNav = o.itemNav === 'basic';
- var forceCenteredNav = o.itemNav === 'forceCentered';
- var centeredNav = o.itemNav === 'centered' || forceCenteredNav;
- var itemNav = !parallax && (basicNav || centeredNav || forceCenteredNav);
- // Miscellaneous
- var $scrollSource = o.scrollSource ? $(o.scrollSource) : $frame;
- var $dragSource = o.dragSource ? $(o.dragSource) : $frame;
- var $forwardButton = $(o.forward);
- var $backwardButton = $(o.backward);
- var $prevButton = $(o.prev);
- var $nextButton = $(o.next);
- var $prevPageButton = $(o.prevPage);
- var $nextPageButton = $(o.nextPage);
- var callbacks = {};
- var last = {};
- var animation = {};
- var move = {};
- var dragging = {
- released: 1
- };
- var scrolling = {
- last: 0,
- delta: 0,
- resetTime: 200
- };
- var renderID = 0;
- var historyID = 0;
- var cycleID = 0;
- var continuousID = 0;
- var i, l;
- // Normalizing frame
- if (!parallax) {
- frame = $frame[0];
- }
- // Expose properties
- self.initialized = 0;
- self.frame = frame;
- self.slidee = $slidee[0];
- self.pos = pos;
- self.rel = rel;
- self.items = items;
- self.pages = pages;
- self.isPaused = 0;
- self.options = o;
- self.dragging = dragging;
- /**
- * Loading function.
- *
- * Populate arrays, set sizes, bind events, ...
- *
- * @param {Boolean} [isInit] Whether load is called from within self.init().
- * @return {Void}
- */
- function load(isInit) {
- // Local variables
- var lastItemsCount = 0;
- var lastPagesCount = pages.length;
- // Save old position
- pos.old = $.extend({}, pos);
- // Reset global variables
- frameSize = parallax ? 0 : $frame[o.horizontal ? 'width' : 'height']();
- sbSize = $sb[o.horizontal ? 'width' : 'height']();
- slideeSize = parallax ? frame : $slidee[o.horizontal ? 'outerWidth' : 'outerHeight']();
- pages.length = 0;
-
- //frameSize /=2;
- //slideeSize /=2;
- //console.log(frameSize);
- //console.log(slideeSize);
- // Set position limits & relatives
- pos.start = 0;
- pos.end = max(slideeSize - frameSize, 0);
-
- // Sizes & offsets for item based navigations
- if (itemNav) {
-
- // Save the number of current items
- lastItemsCount = items.length;
- // Reset itemNav related variables
- $items = $slidee.children(o.itemSelector);
- items.length = 0;
- // Needed variables
- var paddingStart = getPx($slidee, o.horizontal ? 'paddingLeft' : 'paddingTop');
- var paddingEnd = getPx($slidee, o.horizontal ? 'paddingRight' : 'paddingBottom');
- var borderBox = $($items).css('boxSizing') === 'border-box';
- var areFloated = $items.css('float') !== 'none';
- var ignoredMargin = 0;
- var lastItemIndex = $items.length - 1;
- var lastItem;
- // Reset slideeSize
- slideeSize = 0;
-
- // Iterate through items
- $items.each(function (i, element) {
- //console.log(slideeSize);
- // Item
- var $item = $(element);
- var rect = element.getBoundingClientRect();
- //console.log(rect);
- var itemSize = round(o.horizontal ? rect.width || rect.right - rect.left : rect.height || rect.bottom - rect.top);
- if($('#outBody').attr('scale')){
- var scalebody = $('#outBody').attr('scale');
- itemSize =(o.horizontal ? rect.width || rect.right - rect.left : rect.height || rect.bottom - rect.top)*scalebody;
- }
-
- //console.log(itemSize)
- var itemMarginStart = getPx($item, o.horizontal ? 'marginLeft' : 'marginTop');
- var itemMarginEnd = getPx($item, o.horizontal ? 'marginRight' : 'marginBottom');
- var itemSizeFull = itemSize + itemMarginStart + itemMarginEnd;
- var singleSpaced = !itemMarginStart || !itemMarginEnd;
- var item = {};
- item.el = element;
- item.size = singleSpaced ? itemSize : itemSizeFull;
- item.half = item.size / 2;
- item.start = slideeSize + (singleSpaced ? itemMarginStart : 0);
- item.center = item.start - round(frameSize / 2 - item.size / 2);
- item.end = item.start - frameSize + item.size;
- //console.log(item.size)
- //console.log(item.start)
- // Account for slidee padding
- if (!i) {
- slideeSize += paddingStart;
- }
- // Increment slidee size for size of the active element
- slideeSize += itemSizeFull;
- // Try to account for vertical margin collapsing in vertical mode
- // It's not bulletproof, but should work in 99% of cases
- if (!o.horizontal && !areFloated) {
- // Subtract smaller margin, but only when top margin is not 0, and this is not the first element
- if (itemMarginEnd && itemMarginStart && i > 0) {
- slideeSize -= min(itemMarginStart, itemMarginEnd);
- }
- }
- // Things to be done on last item
- if (i === lastItemIndex) {
- item.end += paddingEnd;
- slideeSize += paddingEnd;
- ignoredMargin = singleSpaced ? itemMarginEnd : 0;
- }
- // Add item object to items array
- items.push(item);
- lastItem = item;
- });
- // Resize SLIDEE to fit all items
- $slidee[0].style[o.horizontal ? 'width' : 'height'] = (borderBox ? slideeSize: slideeSize - paddingStart - paddingEnd) + 'px';
- // Adjust internal SLIDEE size for last margin
- slideeSize -= ignoredMargin;
- // Set limits
- if (items.length) {
- pos.start = items[0][forceCenteredNav ? 'center' : 'start'];
- pos.end = forceCenteredNav ? lastItem.center : frameSize < slideeSize ? lastItem.end : pos.start;
- } else {
- pos.start = pos.end = 0;
- }
- }
- // Calculate SLIDEE center position
- pos.center = round(pos.end / 2 + pos.start / 2);
- // Update relative positions
- updateRelatives();
- // Scrollbar
- if ($handle.length && sbSize > 0) {
- // Stretch scrollbar handle to represent the visible area
- if (o.dynamicHandle) {
- handleSize = pos.start === pos.end ? sbSize : round(sbSize * frameSize / slideeSize);
- handleSize = within(handleSize, o.minHandleSize, sbSize);
- $handle[0].style[o.horizontal ? 'width' : 'height'] = handleSize + 'px';
- } else {
- handleSize = $handle[o.horizontal ? 'outerWidth' : 'outerHeight']();
- }
- hPos.end = sbSize - handleSize;
- if (!renderID) {
- syncScrollbar();
- }
- }
- // Pages
- if (!parallax && frameSize > 0) {
- var tempPagePos = pos.start;
- var pagesHtml = '';
- // Populate pages array
- if (itemNav) {
- $.each(items, function (i, item) {
- if (forceCenteredNav) {
- pages.push(item.center);
- } else if (item.start + item.size > tempPagePos && tempPagePos <= pos.end) {
- tempPagePos = item.start;
- pages.push(tempPagePos);
- tempPagePos += frameSize;
- if (tempPagePos > pos.end && tempPagePos < pos.end + frameSize) {
- pages.push(pos.end);
- }
- }
- });
- } else {
- while (tempPagePos - frameSize < pos.end) {
- pages.push(tempPagePos);
- tempPagePos += frameSize;
- }
- }
- // Pages bar
- if ($pb[0] && lastPagesCount !== pages.length) {
- for (var i = 0; i < pages.length; i++) {
- pagesHtml += o.pageBuilder.call(self, i);
- }
- $pages = $pb.html(pagesHtml).children();
- $pages.eq(rel.activePage).addClass(o.activeClass);
- }
- }
- // Extend relative variables object with some useful info
- rel.slideeSize = slideeSize;
- rel.frameSize = frameSize;
- rel.sbSize = sbSize;
- rel.handleSize = handleSize;
- // Activate requested position
- if (itemNav) {
- if (isInit && o.startAt != null) {
- activate(o.startAt);
- self[centeredNav ? 'toCenter' : 'toStart'](o.startAt);
- }
- // Fix possible overflowing
- var activeItem = items[rel.activeItem];
- slideTo(centeredNav && activeItem ? activeItem.center : within(pos.dest, pos.start, pos.end));
- } else {
- if (isInit) {
- if (o.startAt != null) slideTo(o.startAt, 1);
- } else {
- // Fix possible overflowing
- slideTo(within(pos.dest, pos.start, pos.end));
- }
- }
- // Trigger load event
- trigger('load');
- }
- self.reload = function () { load(); };
- /**
- * Animate to a position.
- *
- * @param {Int} newPos New position.
- * @param {Bool} immediate Reposition immediately without an animation.
- * @param {Bool} dontAlign Do not align items, use the raw position passed in first argument.
- *
- * @return {Void}
- */
- function slideTo(newPos, immediate, dontAlign) {
- // Align items
- if (itemNav && dragging.released && !dontAlign) {
- var tempRel = getRelatives(newPos);
- var isNotBordering = newPos > pos.start && newPos < pos.end;
- if (centeredNav) {
- if (isNotBordering) {
- newPos = items[tempRel.centerItem].center;
- }
- if (forceCenteredNav && o.activateMiddle) {
- activate(tempRel.centerItem);
- }
- } else if (isNotBordering) {
- newPos = items[tempRel.firstItem].start;
- }
- }
- // Handle overflowing position limits
- if (dragging.init && dragging.slidee && o.elasticBounds) {
- if (newPos > pos.end) {
- newPos = pos.end + (newPos - pos.end) / 6;
- } else if (newPos < pos.start) {
- newPos = pos.start + (newPos - pos.start) / 6;
- }
- } else {
- newPos = within(newPos, pos.start, pos.end);
- }
- // Update the animation object
- animation.start = +new Date();
- animation.time = 0;
- animation.from = pos.cur;
- animation.to = newPos;
- animation.delta = newPos - pos.cur;
- animation.tweesing = dragging.tweese || dragging.init && !dragging.slidee;
- animation.immediate = !animation.tweesing && (immediate || dragging.init && dragging.slidee || !o.speed);
- // Reset dragging tweesing request
- dragging.tweese = 0;
- // Start animation rendering
- if (newPos !== pos.dest) {
- pos.dest = newPos;
- trigger('change');
- if (!renderID) {
- render();
- }
- }
- // Reset next cycle timeout
- resetCycle();
- // Synchronize states
- updateRelatives();
- updateButtonsState();
- syncPagesbar();
- }
- /**
- * Render animation frame.
- *
- * @return {Void}
- */
- function render() {
- if (!self.initialized) {
- return;
- }
- // If first render call, wait for next animationFrame
- if (!renderID) {
- renderID = rAF(render);
- if (dragging.released) {
- trigger('moveStart');
- }
- return;
- }
- // If immediate repositioning is requested, don't animate.
- if (animation.immediate) {
- pos.cur = animation.to;
- }
- // Use tweesing for animations without known end point
- else if (animation.tweesing) {
- animation.tweeseDelta = animation.to - pos.cur;
- // Fuck Zeno's paradox
- if (abs(animation.tweeseDelta) < 0.1) {
- pos.cur = animation.to;
- } else {
- pos.cur += animation.tweeseDelta * (dragging.released ? o.swingSpeed : o.syncSpeed);
- }
- }
- // Use tweening for basic animations with known end point
- else {
- animation.time = min(+new Date() - animation.start, o.speed);
- pos.cur = animation.from + animation.delta * $.easing[o.easing](animation.time/o.speed, animation.time, 0, 1, o.speed);
- }
- // If there is nothing more to render break the rendering loop, otherwise request new animation frame.
- if (animation.to === pos.cur) {
- pos.cur = animation.to;
- dragging.tweese = renderID = 0;
- } else {
- renderID = rAF(render);
- }
- trigger('move');
- // Update SLIDEE position
- if (!parallax) {
- if (transform) {
- $slidee[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + (-pos.cur) + 'px)';
- } else {
- $slidee[0].style[o.horizontal ? 'left' : 'top'] = -round(pos.cur) + 'px';
- }
- }
- // When animation reached the end, and dragging is not active, trigger moveEnd
- if (!renderID && dragging.released) {
- trigger('moveEnd');
- }
- syncScrollbar();
- }
- /**
- * Synchronizes scrollbar with the SLIDEE.
- *
- * @return {Void}
- */
- function syncScrollbar() {
- if ($handle.length) {
- hPos.cur = pos.start === pos.end ? 0 : (((dragging.init && !dragging.slidee) ? pos.dest : pos.cur) - pos.start) / (pos.end - pos.start) * hPos.end;
- hPos.cur = within(round(hPos.cur), hPos.start, hPos.end);
- if (last.hPos !== hPos.cur) {
- last.hPos = hPos.cur;
- if (transform) {
- $handle[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + hPos.cur + 'px)';
- } else {
- $handle[0].style[o.horizontal ? 'left' : 'top'] = hPos.cur + 'px';
- }
- }
- }
- }
- /**
- * Synchronizes pagesbar with SLIDEE.
- *
- * @return {Void}
- */
- function syncPagesbar() {
- if ($pages[0] && last.page !== rel.activePage) {
- last.page = rel.activePage;
- $pages.removeClass(o.activeClass).eq(rel.activePage).addClass(o.activeClass);
- trigger('activePage', last.page);
- }
- }
- /**
- * Returns the position object.
- *
- * @param {Mixed} item
- *
- * @return {Object}
- */
- self.getPos = function (item) {
- if (itemNav) {
- var index = getIndex(item);
- return index !== -1 ? items[index] : false;
- } else {
- var $item = $slidee.find(item).eq(0);
- if ($item[0]) {
- var offset = o.horizontal ? $item.offset().left - $slidee.offset().left : $item.offset().top - $slidee.offset().top;
- var size = $item[o.horizontal ? 'outerWidth' : 'outerHeight']();
- return {
- start: offset,
- center: offset - frameSize / 2 + size / 2,
- end: offset - frameSize + size,
- size: size
- };
- } else {
- return false;
- }
- }
- };
- /**
- * Continuous move in a specified direction.
- *
- * @param {Bool} forward True for forward movement, otherwise it'll go backwards.
- * @param {Int} speed Movement speed in pixels per frame. Overrides options.moveBy value.
- *
- * @return {Void}
- */
- self.moveBy = function (speed) {
- move.speed = speed;
- // If already initiated, or there is nowhere to move, abort
- if (dragging.init || !move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) {
- return;
- }
- // Initiate move object
- move.lastTime = +new Date();
- move.startPos = pos.cur;
- // Set dragging as initiated
- continuousInit('button');
- dragging.init = 1;
- // Start movement
- trigger('moveStart');
- cAF(continuousID);
- moveLoop();
- };
- /**
- * Continuous movement loop.
- *
- * @return {Void}
- */
- function moveLoop() {
- // If there is nowhere to move anymore, stop
- if (!move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) {
- self.stop();
- }
- // Request new move loop if it hasn't been stopped
- continuousID = dragging.init ? rAF(moveLoop) : 0;
- // Update move object
- move.now = +new Date();
- move.pos = pos.cur + (move.now - move.lastTime) / 1000 * move.speed;
- // Slide
- slideTo(dragging.init ? move.pos : round(move.pos));
- // Normally, this is triggered in render(), but if there
- // is nothing to render, we have to do it manually here.
- if (!dragging.init && pos.cur === pos.dest) {
- trigger('moveEnd');
- }
- // Update times for future iteration
- move.lastTime = move.now;
- }
- /**
- * Stops continuous movement.
- *
- * @return {Void}
- */
- self.stop = function () {
- if (dragging.source === 'button') {
- dragging.init = 0;
- dragging.released = 1;
- }
- };
- /**
- * Activate previous item.
- *
- * @return {Void}
- */
- self.prev = function () {
- self.activate(rel.activeItem == null ? 0 : rel.activeItem - 1);
- };
- /**
- * Activate next item.
- *
- * @return {Void}
- */
- self.next = function () {
- self.activate(rel.activeItem == null ? 0 : rel.activeItem + 1);
- };
- /**
- * Activate previous page.
- *
- * @return {Void}
- */
- self.prevPage = function () {
- self.activatePage(rel.activePage - 1);
- };
- /**
- * Activate next page.
- *
- * @return {Void}
- */
- self.nextPage = function () {
- self.activatePage(rel.activePage + 1);
- };
- /**
- * Slide SLIDEE by amount of pixels.
- *
- * @param {Int} delta Pixels/Items. Positive means forward, negative means backward.
- * @param {Bool} immediate Reposition immediately without an animation.
- *
- * @return {Void}
- */
- self.slideBy = function (delta, immediate) {
- if (!delta) {
- return;
- }
- if (itemNav) {
- self[centeredNav ? 'toCenter' : 'toStart'](
- within((centeredNav ? rel.centerItem : rel.firstItem) + o.scrollBy * delta, 0, items.length)
- );
- } else {
- slideTo(pos.dest + delta, immediate);
- }
- };
- /**
- * Animate SLIDEE to a specific position.
- *
- * @param {Int} pos New position.
- * @param {Bool} immediate Reposition immediately without an animation.
- *
- * @return {Void}
- */
- self.slideTo = function (pos, immediate) {
- slideTo(pos, immediate);
- };
- /**
- * Core method for handling `toLocation` methods.
- *
- * @param {String} location
- * @param {Mixed} item
- * @param {Bool} immediate
- *
- * @return {Void}
- */
- function to(location, item, immediate) {
- // Optional arguments logic
- if (type(item) === 'boolean') {
- immediate = item;
- item = undefined;
- }
- if (item === undefined) {
- slideTo(pos[location], immediate);
- } else {
- // You can't align items to sides of the frame
- // when centered navigation type is enabled
- if (centeredNav && location !== 'center') {
- return;
- }
- var itemPos = self.getPos(item);
- if (itemPos) {
- slideTo(itemPos[location], immediate, !centeredNav);
- }
- }
- }
- /**
- * Animate element or the whole SLIDEE to the start of the frame.
- *
- * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
- * @param {Bool} immediate Reposition immediately without an animation.
- *
- * @return {Void}
- */
- self.toStart = function (item, immediate) {
- to('start', item, immediate);
- };
- /**
- * Animate element or the whole SLIDEE to the end of the frame.
- *
- * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
- * @param {Bool} immediate Reposition immediately without an animation.
- *
- * @return {Void}
- */
- self.toEnd = function (item, immediate) {
- to('end', item, immediate);
- };
- /**
- * Animate element or the whole SLIDEE to the center of the frame.
- *
- * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
- * @param {Bool} immediate Reposition immediately without an animation.
- *
- * @return {Void}
- */
- self.toCenter = function (item, immediate) {
- to('center', item, immediate);
- };
- /**
- * Get the index of an item in SLIDEE.
- *
- * @param {Mixed} item Item DOM element.
- *
- * @return {Int} Item index, or -1 if not found.
- */
- function getIndex(item) {
- return item != null ?
- isNumber(item) ?
- item >= 0 && item < items.length ? item : -1 :
- $items.index(item) :
- -1;
- }
- // Expose getIndex without lowering the compressibility of it,
- // as it is used quite often throughout Sly.
- self.getIndex = getIndex;
- /**
- * Get index of an item in SLIDEE based on a variety of input types.
- *
- * @param {Mixed} item DOM element, positive or negative integer.
- *
- * @return {Int} Item index, or -1 if not found.
- */
- function getRelativeIndex(item) {
- return getIndex(isNumber(item) && item < 0 ? item + items.length : item);
- }
- /**
- * Activates an item.
- *
- * @param {Mixed} item Item DOM element, or index starting at 0.
- *
- * @return {Mixed} Activated item index or false on fail.
- */
- function activate(item, force) {
- var index = getIndex(item);
- if (!itemNav || index < 0) {
- return false;
- }
- // Update classes, last active index, and trigger active event only when there
- // has been a change. Otherwise just return the current active index.
- if (last.active !== index || force) {
- // Update classes
- $items.eq(rel.activeItem).removeClass(o.activeClass);
- $items.eq(index).addClass(o.activeClass);
- last.active = rel.activeItem = index;
- updateButtonsState();
- trigger('active', index);
- }
- return index;
- }
- /**
- * Activates an item and helps with further navigation when o.smart is enabled.
- *
- * @param {Mixed} item Item DOM element, or index starting at 0.
- * @param {Bool} immediate Whether to reposition immediately in smart navigation.
- *
- * @return {Void}
- */
- self.activate = function (item, immediate) {
- var index = activate(item);
- // Smart navigation
- if (o.smart && index !== false) {
- // When centeredNav is enabled, center the element.
- // Otherwise, determine where to position the element based on its current position.
- // If the element is currently on the far end side of the frame, assume that user is
- // moving forward and animate it to the start of the visible frame, and vice versa.
- if (centeredNav) {
- self.toCenter(index, immediate);
- } else if (index >= rel.lastItem) {
- self.toStart(index, immediate);
- } else if (index <= rel.firstItem) {
- self.toEnd(index, immediate);
- } else {
- resetCycle();
- }
- }
- };
- /**
- * Activates a page.
- *
- * @param {Int} index Page index, starting from 0.
- * @param {Bool} immediate Whether to reposition immediately without animation.
- *
- * @return {Void}
- */
- self.activatePage = function (index, immediate) {
- if (isNumber(index)) {
- slideTo(pages[within(index, 0, pages.length - 1)], immediate);
- }
- };
- /**
- * Return relative positions of items based on their visibility within FRAME.
- *
- * @param {Int} slideePos Position of SLIDEE.
- *
- * @return {Void}
- */
- function getRelatives(slideePos) {
- slideePos = within(isNumber(slideePos) ? slideePos : pos.dest, pos.start, pos.end);
- var relatives = {};
- var centerOffset = forceCenteredNav ? 0 : frameSize / 2;
- // Determine active page
- if (!parallax) {
- for (var p = 0, pl = pages.length; p < pl; p++) {
- if (slideePos >= pos.end || p === pages.length - 1) {
- relatives.activePage = pages.length - 1;
- break;
- }
- if (slideePos <= pages[p] + centerOffset) {
- relatives.activePage = p;
- break;
- }
- }
- }
- // Relative item indexes
- if (itemNav) {
- var first = false;
- var last = false;
- var center = false;
- // From start
- for (var i = 0, il = items.length; i < il; i++) {
- // First item
- if (first === false && slideePos <= items[i].start + items[i].half) {
- first = i;
- }
- // Center item
- if (center === false && slideePos <= items[i].center + items[i].half) {
- center = i;
- }
- // Last item
- if (i === il - 1 || slideePos <= items[i].end + items[i].half) {
- last = i;
- break;
- }
- }
- // Safe assignment, just to be sure the false won't be returned
- relatives.firstItem = isNumber(first) ? first : 0;
- relatives.centerItem = isNumber(center) ? center : relatives.firstItem;
- relatives.lastItem = isNumber(last) ? last : relatives.centerItem;
- }
- return relatives;
- }
- /**
- * Update object with relative positions.
- *
- * @param {Int} newPos
- *
- * @return {Void}
- */
- function updateRelatives(newPos) {
- $.extend(rel, getRelatives(newPos));
- }
- /**
- * Disable navigation buttons when needed.
- *
- * Adds disabledClass, and when the button is <button> or <input>, activates :disabled state.
- *
- * @return {Void}
- */
- function updateButtonsState() {
- var isStart = pos.dest <= pos.start;
- var isEnd = pos.dest >= pos.end;
- var slideePosState = (isStart ? 1 : 0) | (isEnd ? 2 : 0);
- // Update paging buttons only if there has been a change in SLIDEE position
- if (last.slideePosState !== slideePosState) {
- last.slideePosState = slideePosState;
- if ($prevPageButton.is('button,input')) {
- $prevPageButton.prop('disabled', isStart);
- }
- if ($nextPageButton.is('button,input')) {
- $nextPageButton.prop('disabled', isEnd);
- }
- $prevPageButton.add($backwardButton)[isStart ? 'addClass' : 'removeClass'](o.disabledClass);
- $nextPageButton.add($forwardButton)[isEnd ? 'addClass' : 'removeClass'](o.disabledClass);
- }
- // Forward & Backward buttons need a separate state caching because we cannot "property disable"
- // them while they are being used, as disabled buttons stop emitting mouse events.
- if (last.fwdbwdState !== slideePosState && dragging.released) {
- last.fwdbwdState = slideePosState;
- if ($backwardButton.is('button,input')) {
- $backwardButton.prop('disabled', isStart);
- }
- if ($forwardButton.is('button,input')) {
- $forwardButton.prop('disabled', isEnd);
- }
- }
- // Item navigation
- if (itemNav && rel.activeItem != null) {
- var isFirst = rel.activeItem === 0;
- var isLast = rel.activeItem >= items.length - 1;
- var itemsButtonState = (isFirst ? 1 : 0) | (isLast ? 2 : 0);
- if (last.itemsButtonState !== itemsButtonState) {
- last.itemsButtonState = itemsButtonState;
- if ($prevButton.is('button,input')) {
- $prevButton.prop('disabled', isFirst);
- }
- if ($nextButton.is('button,input')) {
- $nextButton.prop('disabled', isLast);
- }
- $prevButton[isFirst ? 'addClass' : 'removeClass'](o.disabledClass);
- $nextButton[isLast ? 'addClass' : 'removeClass'](o.disabledClass);
- }
- }
- }
- /**
- * Resume cycling.
- *
- * @param {Int} priority Resume pause with priority lower or equal than this. Used internally for pauseOnHover.
- *
- * @return {Void}
- */
- self.resume = function (priority) {
- if (!o.cycleBy || !o.cycleInterval || o.cycleBy === 'items' && (!items[0] || rel.activeItem == null) || priority < self.isPaused) {
- return;
- }
- self.isPaused = 0;
- if (cycleID) {
- cycleID = clearTimeout(cycleID);
- } else {
- trigger('resume');
- }
- cycleID = setTimeout(function () {
- trigger('cycle');
- switch (o.cycleBy) {
- case 'items':
- self.activate(rel.activeItem >= items.length - 1 ? 0 : rel.activeItem + 1);
- break;
- case 'pages':
- self.activatePage(rel.activePage >= pages.length - 1 ? 0 : rel.activePage + 1);
- break;
- }
- }, o.cycleInterval);
- };
- /**
- * Pause cycling.
- *
- * @param {Int} priority Pause priority. 100 is default. Used internally for pauseOnHover.
- *
- * @return {Void}
- */
- self.pause = function (priority) {
- if (priority < self.isPaused) {
- return;
- }
- self.isPaused = priority || 100;
- if (cycleID) {
- cycleID = clearTimeout(cycleID);
- trigger('pause');
- }
- };
- /**
- * Toggle cycling.
- *
- * @return {Void}
- */
- self.toggle = function () {
- self[cycleID ? 'pause' : 'resume']();
- };
- /**
- * Updates a signle or multiple option values.
- *
- * @param {Mixed} name Name of the option that should be updated, or object that will extend the options.
- * @param {Mixed} value New option value.
- *
- * @return {Void}
- */
- self.set = function (name, value) {
- if ($.isPlainObject(name)) {
- $.extend(o, name);
- } else if (o.hasOwnProperty(name)) {
- o[name] = value;
- }
- };
- /**
- * Add one or multiple items to the SLIDEE end, or a specified position index.
- *
- * @param {Mixed} element Node element, or HTML string.
- * @param {Int} index Index of a new item position. By default item is appended at the end.
- *
- * @return {Void}
- */
- self.add = function (element, index) {
- var $element = $(element);
- if (itemNav) {
- // Insert the element(s)
- if (index == null || !items[0] || index >= items.length) {
- $element.appendTo($slidee);
- } else if (items.length) {
- $element.insertBefore(items[index].el);
- }
- // Adjust the activeItem index
- if (rel.activeItem != null && index <= rel.activeItem) {
- last.active = rel.activeItem += $element.length;
- }
- } else {
- $slidee.append($element);
- }
- // Reload
- load();
- };
- /**
- * Remove an item from SLIDEE.
- *
- * @param {Mixed} element Item index, or DOM element.
- * @param {Int} index Index of a new item position. By default item is appended at the end.
- *
- * @return {Void}
- */
- self.remove = function (element) {
- if (itemNav) {
- var index = getRelativeIndex(element);
- if (index > -1) {
- // Remove the element
- $items.eq(index).remove();
- // If the current item is being removed, activate new one after reload
- var reactivate = index === rel.activeItem;
- // Adjust the activeItem index
- if (rel.activeItem != null && index < rel.activeItem) {
- last.active = --rel.activeItem;
- }
- // Reload
- load();
- // Activate new item at the removed position
- if (reactivate) {
- last.active = null;
- self.activate(rel.activeItem);
- }
- }
- } else {
- $(element).remove();
- load();
- }
- };
- /**
- * Helps re-arranging items.
- *
- * @param {Mixed} item Item DOM element, or index starting at 0. Use negative numbers to select items from the end.
- * @param {Mixed} position Item insertion anchor. Accepts same input types as item argument.
- * @param {Bool} after Insert after instead of before the anchor.
- *
- * @return {Void}
- */
- function moveItem(item, position, after) {
- item = getRelativeIndex(item);
- position = getRelativeIndex(position);
- // Move only if there is an actual change requested
- if (item > -1 && position > -1 && item !== position && (!after || position !== item - 1) && (after || position !== item + 1)) {
- $items.eq(item)[after ? 'insertAfter' : 'insertBefore'](items[position].el);
- var shiftStart = item < position ? item : (after ? position : position - 1);
- var shiftEnd = item > position ? item : (after ? position + 1 : position);
- var shiftsUp = item > position;
- // Update activeItem index
- if (rel.activeItem != null) {
- if (item === rel.activeItem) {
- last.active = rel.activeItem = after ? (shiftsUp ? position + 1 : position) : (shiftsUp ? position : position - 1);
- } else if (rel.activeItem > shiftStart && rel.activeItem < shiftEnd) {
- last.active = rel.activeItem += shiftsUp ? 1 : -1;
- }
- }
- // Reload
- load();
- }
- }
- /**
- * Move item after the target anchor.
- *
- * @param {Mixed} item Item to be moved. Can be DOM element or item index.
- * @param {Mixed} position Target position anchor. Can be DOM element or item index.
- *
- * @return {Void}
- */
- self.moveAfter = function (item, position) {
- moveItem(item, position, 1);
- };
- /**
- * Move item before the target anchor.
- *
- * @param {Mixed} item Item to be moved. Can be DOM element or item index.
- * @param {Mixed} position Target position anchor. Can be DOM element or item index.
- *
- * @return {Void}
- */
- self.moveBefore = function (item, position) {
- moveItem(item, position);
- };
- /**
- * Registers callbacks.
- *
- * @param {Mixed} name Event name, or callbacks map.
- * @param {Mixed} fn Callback, or an array of callback functions.
- *
- * @return {Void}
- */
- self.on = function (name, fn) {
- // Callbacks map
- if (type(name) === 'object') {
- for (var key in name) {
- if (name.hasOwnProperty(key)) {
- self.on(key, name[key]);
- }
- }
- // Callback
- } else if (type(fn) === 'function') {
- var names = name.split(' ');
- for (var n = 0, nl = names.length; n < nl; n++) {
- callbacks[names[n]] = callbacks[names[n]] || [];
- if (callbackIndex(names[n], fn) === -1) {
- callbacks[names[n]].push(fn);
- }
- }
- // Callbacks array
- } else if (type(fn) === 'array') {
- for (var f = 0, fl = fn.length; f < fl; f++) {
- self.on(name, fn[f]);
- }
- }
- };
- /**
- * Registers callbacks to be executed only once.
- *
- * @param {Mixed} name Event name, or callbacks map.
- * @param {Mixed} fn Callback, or an array of callback functions.
- *
- * @return {Void}
- */
- self.one = function (name, fn) {
- function proxy() {
- fn.apply(self, arguments);
- self.off(name, proxy);
- }
- self.on(name, proxy);
- };
- /**
- * Remove one or all callbacks.
- *
- * @param {String} name Event name.
- * @param {Mixed} fn Callback, or an array of callback functions. Omit to remove all callbacks.
- *
- * @return {Void}
- */
- self.off = function (name, fn) {
- if (fn instanceof Array) {
- for (var f = 0, fl = fn.length; f < fl; f++) {
- self.off(name, fn[f]);
- }
- } else {
- var names = name.split(' ');
- for (var n = 0, nl = names.length; n < nl; n++) {
- callbacks[names[n]] = callbacks[names[n]] || [];
- if (fn == null) {
- callbacks[names[n]].length = 0;
- } else {
- var index = callbackIndex(names[n], fn);
- if (index !== -1) {
- callbacks[names[n]].splice(index, 1);
- }
- }
- }
- }
- };
- /**
- * Returns callback array index.
- *
- * @param {String} name Event name.
- * @param {Function} fn Function
- *
- * @return {Int} Callback array index, or -1 if isn't registered.
- */
- function callbackIndex(name, fn) {
- for (var i = 0, l = callbacks[name].length; i < l; i++) {
- if (callbacks[name][i] === fn) {
- return i;
- }
- }
- return -1;
- }
- /**
- * Reset next cycle timeout.
- *
- * @return {Void}
- */
- function resetCycle() {
- if (dragging.released && !self.isPaused) {
- self.resume();
- }
- }
- /**
- * Calculate SLIDEE representation of handle position.
- *
- * @param {Int} handlePos
- *
- * @return {Int}
- */
- function handleToSlidee(handlePos) {
- return round(within(handlePos, hPos.start, hPos.end) / hPos.end * (pos.end - pos.start)) + pos.start;
- }
- /**
- * Keeps track of a dragging delta history.
- *
- * @return {Void}
- */
- function draggingHistoryTick() {
- // Looking at this, I know what you're thinking :) But as we need only 4 history states, doing it this way
- // as opposed to a proper loop is ~25 bytes smaller (when minified with GCC), a lot faster, and doesn't
- // generate garbage. The loop version would create 2 new variables on every tick. Unexaptable!
- dragging.history[0] = dragging.history[1];
- dragging.history[1] = dragging.history[2];
- dragging.history[2] = dragging.history[3];
- dragging.history[3] = dragging.delta;
- }
- /**
- * Initialize continuous movement.
- *
- * @return {Void}
- */
- function continuousInit(source) {
- dragging.released = 0;
- dragging.source = source;
- dragging.slidee = source === 'slidee';
- }
- /**
- * Dragging initiator.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function dragInit(event) {
- var isTouch = event.type === 'touchstart';
- var source = event.data.source;
- var isSlidee = source === 'slidee';
- // Ignore when already in progress, or interactive element in non-touch navivagion
- if (dragging.init || !isTouch && isInteractive(event.target)) {
- return;
- }
- // Handle dragging conditions
- if (source === 'handle' && (!o.dragHandle || hPos.start === hPos.end)) {
- return;
- }
- // SLIDEE dragging conditions
- if (isSlidee && !(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) {
- return;
- }
- if (!isTouch) {
- // prevents native image dragging in Firefox
- stopDefault(event);
- }
- // Reset dragging object
- continuousInit(source);
- // Properties used in dragHandler
- dragging.init = 0;
- dragging.$source = $(event.target);
- dragging.touch = isTouch;
- dragging.pointer = isTouch ? event.originalEvent.touches[0] : event;
- dragging.initX = dragging.pointer.pageX;
- dragging.initY = dragging.pointer.pageY;
- dragging.initPos = isSlidee ? pos.cur : hPos.cur;
- dragging.start = +new Date();
- dragging.time = 0;
- dragging.path = 0;
- dragging.delta = 0;
- dragging.locked = 0;
- dragging.history = [0, 0, 0, 0];
- dragging.pathToLock = isSlidee ? isTouch ? 30 : 10 : 0;
- // Bind dragging events
- $doc.on(isTouch ? dragTouchEvents : dragMouseEvents, dragHandler);
- // Pause ongoing cycle
- self.pause(1);
- // Add dragging class
- (isSlidee ? $slidee : $handle).addClass(o.draggedClass);
- // Trigger moveStart event
- trigger('moveStart');
- // Keep track of a dragging path history. This is later used in the
- // dragging release swing calculation when dragging SLIDEE.
- if (isSlidee) {
- historyID = setInterval(draggingHistoryTick, 10);
- }
- }
- /**
- * Handler for dragging scrollbar handle or SLIDEE.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function dragHandler(event) {
- dragging.released = event.type === 'mouseup' || event.type === 'touchend';
- dragging.pointer = dragging.touch ? event.originalEvent[dragging.released ? 'changedTouches' : 'touches'][0] : event;
- dragging.pathX = dragging.pointer.pageX - dragging.initX;
- dragging.pathY = dragging.pointer.pageY - dragging.initY;
- dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2));
- dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY;
- if (!dragging.released && dragging.path < 1) return;
- // We haven't decided whether this is a drag or not...
- if (!dragging.init) {
- // If the drag path was very short, maybe it's not a drag?
- if (dragging.path < o.dragThreshold) {
- // If the pointer was released, the path will not become longer and it's
- // definitely not a drag. If not released yet, decide on next iteration
- return dragging.released ? dragEnd() : undefined;
- }
- else {
- // If dragging path is sufficiently long we can confidently start a drag
- // if drag is in different direction than scroll, ignore it
- if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) {
- dragging.init = 1;
- } else {
- return dragEnd();
- }
- }
- }
- stopDefault(event);
- // Disable click on a source element, as it is unwelcome when dragging
- if (!dragging.locked && dragging.path > dragging.pathToLock && dragging.slidee) {
- dragging.locked = 1;
- dragging.$source.on(clickEvent, disableOneEvent);
- }
- // Cancel dragging on release
- if (dragging.released) {
- dragEnd();
- // Adjust path with a swing on mouse release
- if (o.releaseSwing && dragging.slidee) {
- dragging.swing = (dragging.delta - dragging.history[0]) / 40 * 300;
- dragging.delta += dragging.swing;
- dragging.tweese = abs(dragging.swing) > 10;
- }
- }
- slideTo(dragging.slidee ? round(dragging.initPos - dragging.delta) : handleToSlidee(dragging.initPos + dragging.delta));
- }
- /**
- * Stops dragging and cleans up after it.
- *
- * @return {Void}
- */
- function dragEnd() {
- clearInterval(historyID);
- dragging.released = true;
- $doc.off(dragging.touch ? dragTouchEvents : dragMouseEvents, dragHandler);
- (dragging.slidee ? $slidee : $handle).removeClass(o.draggedClass);
- // Make sure that disableOneEvent is not active in next tick.
- setTimeout(function () {
- dragging.$source.off(clickEvent, disableOneEvent);
- });
- // Normally, this is triggered in render(), but if there
- // is nothing to render, we have to do it manually here.
- if (pos.cur === pos.dest && dragging.init) {
- trigger('moveEnd');
- }
- // Resume ongoing cycle
- self.resume(1);
- dragging.init = 0;
- }
- /**
- * Check whether element is interactive.
- *
- * @return {Boolean}
- */
- function isInteractive(element) {
- return ~$.inArray(element.nodeName, interactiveElements) || $(element).is(o.interactive);
- }
- /**
- * Continuous movement cleanup on mouseup.
- *
- * @return {Void}
- */
- function movementReleaseHandler() {
- self.stop();
- $doc.off('mouseup', movementReleaseHandler);
- }
- /**
- * Buttons navigation handler.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function buttonsHandler(event) {
- /*jshint validthis:true */
- stopDefault(event);
- switch (this) {
- case $forwardButton[0]:
- case $backwardButton[0]:
- self.moveBy($forwardButton.is(this) ? o.moveBy : -o.moveBy);
- $doc.on('mouseup', movementReleaseHandler);
- break;
- case $prevButton[0]:
- self.prev();
- break;
- case $nextButton[0]:
- self.next();
- break;
- case $prevPageButton[0]:
- self.prevPage();
- break;
- case $nextPageButton[0]:
- self.nextPage();
- break;
- }
- }
- /**
- * Mouse wheel delta normalization.
- *
- * @param {Event} event
- *
- * @return {Int}
- */
- function normalizeWheelDelta(event) {
- // wheelDelta needed only for IE8-
- scrolling.curDelta = ((o.horizontal ? event.deltaY || event.deltaX : event.deltaY) || -event.wheelDelta);
- scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100;
- if (!itemNav) {
- return scrolling.curDelta;
- }
- time = +new Date();
- if (scrolling.last < time - scrolling.resetTime) {
- scrolling.delta = 0;
- }
- scrolling.last = time;
- scrolling.delta += scrolling.curDelta;
- if (abs(scrolling.delta) < 1) {
- scrolling.finalDelta = 0;
- } else {
- scrolling.finalDelta = round(scrolling.delta / 1);
- scrolling.delta %= 1;
- }
- return scrolling.finalDelta;
- }
- /**
- * Mouse scrolling handler.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function scrollHandler(event) {
- // Mark event as originating in a Sly instance
- event.originalEvent[namespace] = self;
- // Don't hijack global scrolling
- var time = +new Date();
- if (lastGlobalWheel + o.scrollHijack > time && $scrollSource[0] !== document && $scrollSource[0] !== window) {
- lastGlobalWheel = time;
- return;
- }
- // Ignore if there is no scrolling to be done
- if (!o.scrollBy || pos.start === pos.end) {
- return;
- }
- var delta = normalizeWheelDelta(event.originalEvent);
- // Trap scrolling only when necessary and/or requested
- if (o.scrollTrap || delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) {
- stopDefault(event, 1);
- }
- self.slideBy(o.scrollBy * delta);
- }
- /**
- * Scrollbar click handler.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function scrollbarHandler(event) {
- // Only clicks on scroll bar. Ignore the handle.
- if (o.clickBar && event.target === $sb[0]) {
- stopDefault(event);
- // Calculate new handle position and sync SLIDEE to it
- slideTo(handleToSlidee((o.horizontal ? event.pageX - $sb.offset().left : event.pageY - $sb.offset().top) - handleSize / 2));
- }
- }
- /**
- * Keyboard input handler.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function keyboardHandler(event) {
- if (!o.keyboardNavBy) {
- return;
- }
- switch (event.which) {
- // Left or Up
- case o.horizontal ? 37 : 38:
- stopDefault(event);
- self[o.keyboardNavBy === 'pages' ? 'prevPage' : 'prev']();
- break;
- // Right or Down
- case o.horizontal ? 39 : 40:
- stopDefault(event);
- self[o.keyboardNavBy === 'pages' ? 'nextPage' : 'next']();
- break;
- }
- }
- /**
- * Click on item activation handler.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function activateHandler(event) {
- /*jshint validthis:true */
- // Ignore clicks on interactive elements.
- if (isInteractive(this)) {
- event.originalEvent[namespace + 'ignore'] = true;
- return;
- }
- // Ignore events that:
- // - are not originating from direct SLIDEE children
- // - originated from interactive elements
- if (this.parentNode !== $slidee[0] || event.originalEvent[namespace + 'ignore']) return;
- self.activate(this);
- }
- /**
- * Click on page button handler.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function activatePageHandler() {
- /*jshint validthis:true */
- // Accept only events from direct pages bar children.
- if (this.parentNode === $pb[0]) {
- self.activatePage($pages.index(this));
- }
- }
- /**
- * Pause on hover handler.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function pauseOnHoverHandler(event) {
- if (o.pauseOnHover) {
- self[event.type === 'mouseenter' ? 'pause' : 'resume'](2);
- }
- }
- /**
- * Trigger callbacks for event.
- *
- * @param {String} name Event name.
- * @param {Mixed} argX Arguments passed to callbacks.
- *
- * @return {Void}
- */
- function trigger(name, arg1) {
- if (callbacks[name]) {
- l = callbacks[name].length;
- // Callbacks will be stored and executed from a temporary array to not
- // break the execution queue when one of the callbacks unbinds itself.
- tmpArray.length = 0;
- for (i = 0; i < l; i++) {
- tmpArray.push(callbacks[name][i]);
- }
- // Execute the callbacks
- for (i = 0; i < l; i++) {
- tmpArray[i].call(self, name, arg1);
- }
- }
- }
- /**
- * Destroys instance and everything it created.
- *
- * @return {Void}
- */
- self.destroy = function () {
- // Remove the reference to itself
- Sly.removeInstance(frame);
- // Unbind all events
- $scrollSource
- .add($handle)
- .add($sb)
- .add($pb)
- .add($forwardButton)
- .add($backwardButton)
- .add($prevButton)
- .add($nextButton)
- .add($prevPageButton)
- .add($nextPageButton)
- .off('.' + namespace);
- // Unbinding specifically as to not nuke out other instances
- $doc.off('keydown', keyboardHandler);
- // Remove classes
- $prevButton
- .add($nextButton)
- .add($prevPageButton)
- .add($nextPageButton)
- .removeClass(o.disabledClass);
- if ($items && rel.activeItem != null) {
- $items.eq(rel.activeItem).removeClass(o.activeClass);
- }
- // Remove page items
- $pb.empty();
- if (!parallax) {
- // Unbind events from frame
- $frame.off('.' + namespace);
- // Restore original styles
- frameStyles.restore();
- slideeStyles.restore();
- sbStyles.restore();
- handleStyles.restore();
- // Remove the instance from element data storage
- $.removeData(frame, namespace);
- }
- // Clean up collections
- items.length = pages.length = 0;
- last = {};
- // Reset initialized status and return the instance
- self.initialized = 0;
- return self;
- };
- /**
- * Initialize.
- *
- * @return {Object}
- */
- self.init = function () {
- if (self.initialized) {
- return;
- }
- // Disallow multiple instances on the same element
- if (Sly.getInstance(frame)) throw new Error('There is already a Sly instance on this element');
- // Store the reference to itself
- Sly.storeInstance(frame, self);
- // Register callbacks map
- self.on(callbackMap);
- // Save styles
- var holderProps = ['overflow', 'position'];
- var movableProps = ['position', 'webkitTransform', 'msTransform', 'transform', 'left', 'top', 'width', 'height'];
- frameStyles.save.apply(frameStyles, holderProps);
- sbStyles.save.apply(sbStyles, holderProps);
- slideeStyles.save.apply(slideeStyles, movableProps);
- handleStyles.save.apply(handleStyles, movableProps);
- // Set required styles
- var $movables = $handle;
- if (!parallax) {
- $movables = $movables.add($slidee);
- $frame.css('overflow', 'hidden');
- if (!transform && $frame.css('position') === 'static') {
- $frame.css('position', 'relative');
- }
- }
- if (transform) {
- if (gpuAcceleration) {
- $movables.css(transform, gpuAcceleration);
- }
- } else {
- if ($sb.css('position') === 'static') {
- $sb.css('position', 'relative');
- }
- $movables.css({ position: 'absolute' });
- }
- // Navigation buttons
- if (o.forward) {
- $forwardButton.on(mouseDownEvent, buttonsHandler);
- }
- if (o.backward) {
- $backwardButton.on(mouseDownEvent, buttonsHandler);
- }
- if (o.prev) {
- $prevButton.on(clickEvent, buttonsHandler);
- }
- if (o.next) {
- $nextButton.on(clickEvent, buttonsHandler);
- }
- if (o.prevPage) {
- $prevPageButton.on(clickEvent, buttonsHandler);
- }
- if (o.nextPage) {
- $nextPageButton.on(clickEvent, buttonsHandler);
- }
- // Scrolling navigation
- $scrollSource.on(wheelEvent, scrollHandler);
- // Clicking on scrollbar navigation
- if ($sb[0]) {
- $sb.on(clickEvent, scrollbarHandler);
- }
- // Click on items navigation
- if (itemNav && o.activateOn) {
- $frame.on(o.activateOn + '.' + namespace, '*', activateHandler);
- }
- // Pages navigation
- if ($pb[0] && o.activatePageOn) {
- $pb.on(o.activatePageOn + '.' + namespace, '*', activatePageHandler);
- }
- // Dragging navigation
- $dragSource.on(dragInitEvents, { source: 'slidee' }, dragInit);
- // Scrollbar dragging navigation
- if ($handle) {
- $handle.on(dragInitEvents, { source: 'handle' }, dragInit);
- }
- // Keyboard navigation
- $doc.on('keydown', keyboardHandler);
- if (!parallax) {
- // Pause on hover
- $frame.on('mouseenter.' + namespace + ' mouseleave.' + namespace, pauseOnHoverHandler);
- // Reset native FRAME element scroll
- $frame.on('scroll.' + namespace, resetScroll);
- }
- // Mark instance as initialized
- self.initialized = 1;
- // Load
- load(true);
- // Initiate automatic cycling
- if (o.cycleBy && !parallax) {
- self[o.startPaused ? 'pause' : 'resume']();
- }
- // Return instance
- return self;
- };
- }
- Sly.getInstance = function (element) {
- return $.data(element, namespace);
- };
- Sly.storeInstance = function (element, sly) {
- return $.data(element, namespace, sly);
- };
- Sly.removeInstance = function (element) {
- return $.removeData(element, namespace);
- };
- /**
- * Return type of the value.
- *
- * @param {Mixed} value
- *
- * @return {String}
- */
- function type(value) {
- if (value == null) {
- return String(value);
- }
- if (typeof value === 'object' || typeof value === 'function') {
- return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object';
- }
- return typeof value;
- }
- /**
- * Event preventDefault & stopPropagation helper.
- *
- * @param {Event} event Event object.
- * @param {Bool} noBubbles Cancel event bubbling.
- *
- * @return {Void}
- */
- function stopDefault(event, noBubbles) {
- event.preventDefault();
- if (noBubbles) {
- event.stopPropagation();
- }
- }
- /**
- * Disables an event it was triggered on and unbinds itself.
- *
- * @param {Event} event
- *
- * @return {Void}
- */
- function disableOneEvent(event) {
- /*jshint validthis:true */
- stopDefault(event, 1);
- $(this).off(event.type, disableOneEvent);
- }
- /**
- * Resets native element scroll values to 0.
- *
- * @return {Void}
- */
- function resetScroll() {
- /*jshint validthis:true */
- this.scrollLeft = 0;
- this.scrollTop = 0;
- }
- /**
- * Check if variable is a number.
- *
- * @param {Mixed} value
- *
- * @return {Boolean}
- */
- function isNumber(value) {
- return !isNaN(parseFloat(value)) && isFinite(value);
- }
- /**
- * Parse style to pixels.
- *
- * @param {Object} $item jQuery object with element.
- * @param {Property} property CSS property to get the pixels from.
- *
- * @return {Int}
- */
- function getPx($item, property) {
- return 0 | round(String($item.css(property)).replace(/[^\-0-9.]/g, ''));
- }
- /**
- * Make sure that number is within the limits.
- *
- * @param {Number} number
- * @param {Number} min
- * @param {Number} max
- *
- * @return {Number}
- */
- function within(number, min, max) {
- return number < min ? min : number > max ? max : number;
- }
- /**
- * Saves element styles for later restoration.
- *
- * Example:
- * var styles = new StyleRestorer(frame);
- * styles.save('position');
- * element.style.position = 'absolute';
- * styles.restore(); // restores to state before the assignment above
- *
- * @param {Element} element
- */
- function StyleRestorer(element) {
- var self = {};
- self.style = {};
- self.save = function () {
- if (!element || !element.nodeType) return;
- for (var i = 0; i < arguments.length; i++) {
- self.style[arguments[i]] = element.style[arguments[i]];
- }
- return self;
- };
- self.restore = function () {
- if (!element || !element.nodeType) return;
- for (var prop in self.style) {
- if (self.style.hasOwnProperty(prop)) element.style[prop] = self.style[prop];
- }
- return self;
- };
- return self;
- }
- // Local WindowAnimationTiming interface polyfill
- (function (w) {
- rAF = w.requestAnimationFrame
- || w.webkitRequestAnimationFrame
- || fallback;
- /**
- * Fallback implementation.
- */
- var prev = new Date().getTime();
- function fallback(fn) {
- var curr = new Date().getTime();
- var ms = Math.max(0, 16 - (curr - prev));
- var req = setTimeout(fn, ms);
- prev = curr;
- return req;
- }
- /**
- * Cancel.
- */
- var cancel = w.cancelAnimationFrame
- || w.webkitCancelAnimationFrame
- || w.clearTimeout;
- cAF = function(id){
- cancel.call(w, id);
- };
- }(window));
- // Feature detects
- (function () {
- var prefixes = ['', 'Webkit', 'Moz', 'ms', 'O'];
- var el = document.createElement('div');
- function testProp(prop) {
- for (var p = 0, pl = prefixes.length; p < pl; p++) {
- var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop;
- if (el.style[prefixedProp] != null) {
- return prefixedProp;
- }
- }
- }
- // Global support indicators
- transform = testProp('transform');
- gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : '';
- }());
- // Expose class globally
- w[className] = Sly;
- // jQuery proxy
- $.fn[pluginName] = function (options, callbackMap) {
- var method, methodArgs;
- // Attributes logic
- if (!$.isPlainObject(options)) {
- if (type(options) === 'string' || options === false) {
- method = options === false ? 'destroy' : options;
- methodArgs = Array.prototype.slice.call(arguments, 1);
- }
- options = {};
- }
- // Apply to all elements
- return this.each(function (i, element) {
- // Call with prevention against multiple instantiations
- var plugin = Sly.getInstance(element);
- if (!plugin && !method) {
- // Create a new object if it doesn't exist yet
- plugin = new Sly(element, options, callbackMap).init();
- } else if (plugin && method) {
- // Call method
- if (plugin[method]) {
- plugin[method].apply(plugin, methodArgs);
- }
- }
- });
- };
- // Default options
- Sly.defaults = {
- slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE.
- horizontal: false, // Switch to horizontal mode.
- // Item based navigation
- itemNav: null, // Item navigation type. Can be: 'basic', 'centered', 'forceCentered'.
- itemSelector: null, // Select only items that match this selector.
- smart: false, // Repositions the activated item to help with further navigation.
- activateOn: null, // Activate an item on this event. Can be: 'click', 'mouseenter', ...
- activateMiddle: false, // Always activate the item in the middle of the FRAME. forceCentered only.
- // Scrolling
- scrollSource: null, // Element for catching the mouse wheel scrolling. Default is FRAME.
- scrollBy: 0, // Pixels or items to move per one mouse scroll. 0 to disable scrolling.
- scrollHijack: 300, // Milliseconds since last wheel event after which it is acceptable to hijack global scroll.
- scrollTrap: false, // Don't bubble scrolling when hitting scrolling limits.
- // Dragging
- dragSource: null, // Selector or DOM element for catching dragging events. Default is FRAME.
- mouseDragging: false, // Enable navigation by dragging the SLIDEE with mouse cursor.
- touchDragging: false, // Enable navigation by dragging the SLIDEE with touch events.
- releaseSwing: false, // Ease out on dragging swing release.
- swingSpeed: 0.2, // Swing synchronization speed, where: 1 = instant, 0 = infinite.
- elasticBounds: false, // Stretch SLIDEE position limits when dragging past FRAME boundaries.
- dragThreshold: 3, // Distance in pixels before Sly recognizes dragging.
- interactive: null, // Selector for special interactive elements.
- // Scrollbar
- scrollBar: null, // Selector or DOM element for scrollbar container.
- dragHandle: false, // Whether the scrollbar handle should be draggable.
- dynamicHandle: false, // Scrollbar handle represents the ratio between hidden and visible content.
- minHandleSize: 50, // Minimal height or width (depends on sly direction) of a handle in pixels.
- clickBar: false, // Enable navigation by clicking on scrollbar.
- syncSpeed: 0.5, // Handle => SLIDEE synchronization speed, where: 1 = instant, 0 = infinite.
- // Pagesbar
- pagesBar: null, // Selector or DOM element for pages bar container.
- activatePageOn: null, // Event used to activate page. Can be: click, mouseenter, ...
- pageBuilder: // Page item generator.
- function (index) {
- return '<li>' + (index + 1) + '</li>';
- },
- // Navigation buttons
- forward: null, // Selector or DOM element for "forward movement" button.
- backward: null, // Selector or DOM element for "backward movement" button.
- prev: null, // Selector or DOM element for "previous item" button.
- next: null, // Selector or DOM element for "next item" button.
- prevPage: null, // Selector or DOM element for "previous page" button.
- nextPage: null, // Selector or DOM element for "next page" button.
- // Automated cycling
- cycleBy: null, // Enable automatic cycling by 'items' or 'pages'.
- cycleInterval: 5000, // Delay between cycles in milliseconds.
- pauseOnHover: false, // Pause cycling when mouse hovers over the FRAME.
- startPaused: false, // Whether to start in paused sate.
- // Mixed options
- moveBy: 300, // Speed in pixels per second used by forward and backward buttons.
- speed: 0, // Animations speed in milliseconds. 0 to disable animations.
- easing: 'swing', // Easing for duration based (tweening) animations.
- startAt: null, // Starting offset in pixels or items.
- keyboardNavBy: null, // Enable keyboard navigation by 'items' or 'pages'.
- // Classes
- draggedClass: 'dragged', // Class for dragged elements (like SLIDEE or scrollbar handle).
- activeClass: 'active', // Class for active items and pages.
- disabledClass: 'disabled' // Class for disabled navigation elements.
- };
- }(jQuery, window));
|