Seems like you're using an old browser. Things on the website might not work as they should.
Free worldwide shippingFree returns
Select shipping country
Select your language
Safe payments
1,754,984
Followers @nakdfashion
 

/*! * Flickity PACKAGED v2.1.1 * Touch, responsive, flickable carousels * * Licensed GPLv3 for open source use * or Flickity Commercial License for commercial use * * https://flickity.metafizzy.co * Copyright 2015-2018 Metafizzy */ /** * Bridget makes jQuery widgets * v2.0.1 * MIT license */ /* jshint browser: true, strict: true, undef: true, unused: true */ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /* globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) { return factory( window, jQuery ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('jquery') ); } else { // browser global window.jQueryBridget = factory( window, window.jQuery ); } }( window, function factory( window, jQuery ) { 'use strict'; // ----- utils ----- // var arraySlice = Array.prototype.slice; // helper function for logging errors // $.error breaks jQuery chaining var console = window.console; var logError = typeof console == 'undefined' ? function() {} : function( message ) { console.error( message ); }; // ----- jQueryBridget ----- // function jQueryBridget( namespace, PluginClass, $ ) { $ = $ || jQuery || window.jQuery; if ( !$ ) { return; } // add option method -> $().plugin('option', {...}) if ( !PluginClass.prototype.option ) { // option setter PluginClass.prototype.option = function( opts ) { // bail out if not an object if ( !$.isPlainObject( opts ) ){ return; } this.options = $.extend( true, this.options, opts ); }; } // make jQuery plugin $.fn[ namespace ] = function( arg0 /*, arg1 */ ) { if ( typeof arg0 == 'string' ) { // method call $().plugin( 'methodName', { options } ) // shift arguments by 1 var args = arraySlice.call( arguments, 1 ); return methodCall( this, arg0, args ); } // just $().plugin({ options }) plainCall( this, arg0 ); return this; }; // $().plugin('methodName') function methodCall( $elems, methodName, args ) { var returnValue; var pluginMethodStr = '$().' + namespace + '("' + methodName + '")'; $elems.each( function( i, elem ) { // get instance var instance = $.data( elem, namespace ); if ( !instance ) { logError( namespace + ' not initialized. Cannot call methods, i.e. ' + pluginMethodStr ); return; } var method = instance[ methodName ]; if ( !method || methodName.charAt(0) == '_' ) { logError( pluginMethodStr + ' is not a valid method' ); return; } // apply method, get return value var value = method.apply( instance, args ); // set return value if value is returned, use only first value returnValue = returnValue === undefined ? value : returnValue; }); return returnValue !== undefined ? returnValue : $elems; } function plainCall( $elems, options ) { $elems.each( function( i, elem ) { var instance = $.data( elem, namespace ); if ( instance ) { // set options & init instance.option( options ); instance._init(); } else { // initialize new instance instance = new PluginClass( elem, options ); $.data( elem, namespace, instance ); } }); } updateJQuery( $ ); } // ----- updateJQuery ----- // // set $.bridget for v1 backwards compatibility function updateJQuery( $ ) { if ( !$ || ( $ && $.bridget ) ) { return; } $.bridget = jQueryBridget; } updateJQuery( jQuery || window.jQuery ); // ----- ----- // return jQueryBridget; })); /** * EvEmitter v1.1.0 * Lil' event emitter * MIT License */ /* jshint unused: true, undef: true, strict: true */ ( function( global, factory ) { // universal module definition /* jshint strict: false */ /* globals define, module, window */ if ( typeof define == 'function' && define.amd ) { // AMD - RequireJS define( 'ev-emitter/ev-emitter',factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS - Browserify, Webpack module.exports = factory(); } else { // Browser globals global.EvEmitter = factory(); } }( typeof window != 'undefined' ? window : this, function() { function EvEmitter() {} var proto = EvEmitter.prototype; proto.on = function( eventName, listener ) { if ( !eventName || !listener ) { return; } // set events hash var events = this._events = this._events || {}; // set listeners array var listeners = events[ eventName ] = events[ eventName ] || []; // only add once if ( listeners.indexOf( listener ) == -1 ) { listeners.push( listener ); } return this; }; proto.once = function( eventName, listener ) { if ( !eventName || !listener ) { return; } // add event this.on( eventName, listener ); // set once flag // set onceEvents hash var onceEvents = this._onceEvents = this._onceEvents || {}; // set onceListeners object var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; // set flag onceListeners[ listener ] = true; return this; }; proto.off = function( eventName, listener ) { var listeners = this._events && this._events[ eventName ]; if ( !listeners || !listeners.length ) { return; } var index = listeners.indexOf( listener ); if ( index != -1 ) { listeners.splice( index, 1 ); } return this; }; proto.emitEvent = function( eventName, args ) { var listeners = this._events && this._events[ eventName ]; if ( !listeners || !listeners.length ) { return; } // copy over to avoid interference if .off() in listener listeners = listeners.slice(0); args = args || []; // once stuff var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; for ( var i=0; i < listeners.length; i++ ) { var listener = listeners[i] var isOnce = onceListeners && onceListeners[ listener ]; if ( isOnce ) { // remove listener // remove before trigger to prevent recursion this.off( eventName, listener ); // unset once flag delete onceListeners[ listener ]; } // trigger listener listener.apply( this, args ); } return this; }; proto.allOff = function() { delete this._events; delete this._onceEvents; }; return EvEmitter; })); /*! * getSize v2.0.2 * measure size of elements * MIT license */ /*jshint browser: true, strict: true, undef: true, unused: true */ /*global define: false, module: false, console: false */ ( function( window, factory ) { 'use strict'; if ( typeof define == 'function' && define.amd ) { // AMD define( 'get-size/get-size',[],function() { return factory(); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory(); } else { // browser global window.getSize = factory(); } })( window, function factory() { 'use strict'; // -------------------------- helpers -------------------------- // // get a number from a string, not a percentage function getStyleSize( value ) { var num = parseFloat( value ); // not a percent like '100%', and a number var isValid = value.indexOf('%') == -1 && !isNaN( num ); return isValid && num; } function noop() {} var logError = typeof console == 'undefined' ? noop : function( message ) { console.error( message ); }; // -------------------------- measurements -------------------------- // var measurements = [ 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth', 'borderBottomWidth' ]; var measurementsLength = measurements.length; function getZeroSize() { var size = { width: 0, height: 0, innerWidth: 0, innerHeight: 0, outerWidth: 0, outerHeight: 0 }; for ( var i=0; i < measurementsLength; i++ ) { var measurement = measurements[i]; size[ measurement ] = 0; } return size; } // -------------------------- getStyle -------------------------- // /** * getStyle, get style of element, check for Firefox bug * https://bugzilla.mozilla.org/show_bug.cgi?id=548397 */ function getStyle( elem ) { var style = getComputedStyle( elem ); if ( !style ) { logError( 'Style returned ' + style + '. Are you running this code in a hidden iframe on Firefox? ' + 'See http://bit.ly/getsizebug1' ); } return style; } // -------------------------- setup -------------------------- // var isSetup = false; var isBoxSizeOuter; /** * setup * check isBoxSizerOuter * do on first getSize() rather than on page load for Firefox bug */ function setup() { // setup once if ( isSetup ) { return; } isSetup = true; // -------------------------- box sizing -------------------------- // /** * WebKit measures the outer-width on style.width on border-box elems * IE & Firefox<29 measures="" the="" inner-width="" var="" div="" style="" width="" padding="" borderstyle="solid" borderwidth="" boxsizing="=" body="" document="" documentelement="" appendchild="" getsize="function()" isboxsizeouter="" getstylesize="" 200="" removechild="" --------------------------="" function="" elem="" setup="" use="" queryseletor="" if="" is="" string="" typeof="" do="" not="" proceed="" on="" non-objects="" object="" nodetype="" return="" hidden="" everything="" 0="" display="=" none="" getzerosize="" size="" height="" isborderbox="size.isBorderBox" border-box="" get="" all="" measurements="" for="" i="" measurementslength="" measurement="" value="" num="" any="" auto="" medium="" will="" be="" isnan="" :="" paddingwidth="" paddingright="" paddingheight="" paddingbottom="" marginwidth="" marginright="" marginheight="" marginbottom="" borderrightwidth="" borderheight="" borderbottomwidth="" isborderboxsizeouter="" overwrite="" and="" we="" can="" it="" from="" stylewidth="" false="" add="" border="" unless="" s="" already="" including="" styleheight="" innerwidth="size.width" -="" innerheight="size.height" outerwidth="" outerheight="" matchesselector="" v2="" 2="" element="" selector="" mit="" license="" jshint="" browser:="" true="" strict:="" undef:="" unused:="" window="" factory="" global="" define:="" module:="" strict="" universal="" module="" definition="" define="" amd="" desandro-matches-selector="" matches-selector="" else="" exports="factory(" commonjs="" browser="" matchesmethod="" elemproto="" check="" standard="" method="" name="" first="" matches="" un-prefixed="" vendor="" prefixes="" webkit="" moz="" ms="" o="" length="" prefix="prefixes[i];" fizzy="" ui="" utils="" 7="" globals="" require="" fizzy-ui-utils="" fizzyuiutils="" -----="" extend="function(" extends="" objects="" a="" b="" prop="" in="" modulo="function(" makearray="" arrayslice="" turn="" or="" nodelist="" into="" an="" array="" obj="" isarray="" empty="" undefined="" null="" 6="" isarraylike="" number="" convert="" to="" call="" of="" single="" index="" removefrom="function(" ary="" -1="" splice="" 1="" getparent="function(" while="" parentnode="" getqueryelement="function(" as="" queryselector="" handleevent="function(" enable="" ontype="" trigger="" addeventlistener="" type="" event="" this="" filterfindelements="function(" elems="" make="" ffelems="" foreach="" that="" actual="" instanceof="" htmlelement="" no="" push="" filter="" find="" items="" have="" children="" childelems="" concat="" filterfound="" debouncemethod="function(" _class="" methodname="" threshold="" 100="" original="" timeoutname="" timeout="" prototype="" cleartimeout="" args="" _this="" settimeout="" apply="" delete="" docready="" callback="" readystate="=" complete="" interactive="" async="" allow="" other="" scripts="" run="" metafizzy="" flickity="" 441="" domcontentloaded="" htmlinit="function(" http:="" jamesroberts="" blog="" 2010="" 02="" 22="" string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore="" todashed="function(" str="" replace="" a-z="" g="" match="" tolowercase="" console="" user="" initialize="" classes="" via="" data-namespace="" js-namespace="" class="" widget="" widgetname="" options="" are="" parsed="" data-namespace-options="" widgetclass="" namespace="" dashednamespace="" dataattr="" dataattrelems="" jsdashelems="" js-="" dataoptionsattr="" -options="" jquery="" attr="" getattribute="" try="" json="" parse="" catch="" error="" log="" parsing="" classname="" instance="" available="" data="" cell="" js="" get-size="" parent="" create="function()" proto="" position="" setattribute="" aria-selected="" x="this.x;" shift="" destroy="function()" reset="" side="" removeattribute="" setposition="function(" updatetarget="function()" renderposition="" setdefaulttarget="" v1="" backwards="" compatibility="" remove="" v3="" marginproperty="" left="" marginleft="" target="this.x" cellalign="" render="" with="" slider="" getpositionvalue="" param="" integer="" factor="" wrapshift="function(" slideablewidth="" slide="" isoriginleft="parent.originSide" cells="" addcell="function(" stuff="" comes="" beginmargin="" firstmargin="" endmargin="" lastcell="" lastmargin="" slidewidth="" getlastcell="function()" select="function()" changeselected="function(" unselect="function()" isselected="" classmethod="" classlist="" is-selected="" tostring="" getcellelements="function()" map="" animate="" animateprototype="factory(" startanimation="function()" isanimating="" restingframes="0;" applydragforce="" applyselectedattraction="" previousx="" integratephysics="" positionslider="function()" settle="" next="" frame="" requestanimationframe="" animateframe="" wrap="" around="" wraparound="" data-mce-style=""> 1 ) { x = utils.modulo( x, this.slideableWidth ); x = x - this.slideableWidth; this.shiftWrapCells( x ); } x = x + this.cursorPosition; // reverse if right-to-left and using transform x = this.options.rightToLeft ? -x : x; var value = this.getPositionValue( x ); // use 3D tranforms for hardware acceleration on iOS // but use 2D when settled, for better font-rendering this.slider.style.transform = this.isAnimating ? 'translate3d(' + value + ',0,0)' : 'translateX(' + value + ')'; // scroll event var firstSlide = this.slides[0]; if ( firstSlide ) { var positionX = -this.x - firstSlide.target; var progress = positionX / this.slidesWidth; this.dispatchEvent( 'scroll', null, [ progress, positionX ] ); } }; proto.positionSliderAtSelected = function() { if ( !this.cells.length ) { return; } this.x = -this.selectedSlide.target; this.velocity = 0; // stop wobble this.positionSlider(); }; proto.getPositionValue = function( position ) { if ( this.options.percentPosition ) { // percent position, round to 2 digits, like 12.34% return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 )+ '%'; } else { // pixel positioning return Math.round( position ) + 'px'; } }; proto.settle = function( previousX ) { // keep track of frames where x hasn't moved if ( !this.isPointerDown && Math.round( this.x * 100 ) == Math.round( previousX * 100 ) ) { this.restingFrames++; } // stop animating if resting for 3 or more frames if ( this.restingFrames > 2 ) { this.isAnimating = false; delete this.isFreeScrolling; // render position with translateX when settled this.positionSlider(); this.dispatchEvent( 'settle', null, [ this.selectedIndex ] ); } }; proto.shiftWrapCells = function( x ) { // shift before cells var beforeGap = this.cursorPosition + x; this._shiftCells( this.beforeShiftCells, beforeGap, -1 ); // shift after cells var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition ); this._shiftCells( this.afterShiftCells, afterGap, 1 ); }; proto._shiftCells = function( cells, gap, shift ) { for ( var i=0; i < cells.length; i++ ) { var cell = cells[i]; var cellShift = gap > 0 ? shift : 0; cell.wrapShift( cellShift ); gap -= cell.size.outerWidth; } }; proto._unshiftCells = function( cells ) { if ( !cells || !cells.length ) { return; } for ( var i=0; i < cells.length; i++ ) { cells[i].wrapShift( 0 ); } }; // -------------------------- physics -------------------------- // proto.integratePhysics = function() { this.x += this.velocity; this.velocity *= this.getFrictionFactor(); }; proto.applyForce = function( force ) { this.velocity += force; }; proto.getFrictionFactor = function() { return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ]; }; proto.getRestingPosition = function() { // my thanks to Steven Wittens, who simplified this math greatly return this.x + this.velocity / ( 1 - this.getFrictionFactor() ); }; proto.applyDragForce = function() { if ( !this.isDraggable || !this.isPointerDown ) { return; } // change the position to drag position by applying force var dragVelocity = this.dragX - this.x; var dragForce = dragVelocity - this.velocity; this.applyForce( dragForce ); }; proto.applySelectedAttraction = function() { // do not attract if pointer down or no slides var dragDown = this.isDraggable && this.isPointerDown; if ( dragDown || this.isFreeScrolling || !this.slides.length ) { return; } var distance = this.selectedSlide.target * -1 - this.x; var force = distance * this.options.selectedAttraction; this.applyForce( force ); }; return proto; })); // Flickity main ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/flickity',[ 'ev-emitter/ev-emitter', 'get-size/get-size', 'fizzy-ui-utils/utils', './cell', './slide', './animate' ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) { return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('ev-emitter'), require('get-size'), require('fizzy-ui-utils'), require('./cell'), require('./slide'), require('./animate') ); } else { // browser global var _Flickity = window.Flickity; window.Flickity = factory( window, window.EvEmitter, window.getSize, window.fizzyUIUtils, _Flickity.Cell, _Flickity.Slide, _Flickity.animatePrototype ); } }( window, function factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) { // vars var jQuery = window.jQuery; var getComputedStyle = window.getComputedStyle; var console = window.console; function moveElements( elems, toElem ) { elems = utils.makeArray( elems ); while ( elems.length ) { toElem.appendChild( elems.shift() ); } } // -------------------------- Flickity -------------------------- // // globally unique identifiers var GUID = 0; // internal store of all Flickity intances var instances = {}; function Flickity( element, options ) { var queryElement = utils.getQueryElement( element ); if ( !queryElement ) { if ( console ) { console.error( 'Bad element for Flickity: ' + ( queryElement || element ) ); } return; } this.element = queryElement; // do not initialize twice on same element if ( this.element.flickityGUID ) { var instance = instances[ this.element.flickityGUID ]; instance.option( options ); return instance; } // add jQuery if ( jQuery ) { this.$element = jQuery( this.element ); } // options this.options = utils.extend( {}, this.constructor.defaults ); this.option( options ); // kick things off this._create(); } Flickity.defaults = { accessibility: true, // adaptiveHeight: false, cellAlign: 'center', // cellSelector: undefined, // contain: false, freeScrollFriction: 0.075, // friction when free-scrolling friction: 0.28, // friction when selecting namespaceJQueryEvents: true, // initialIndex: 0, percentPosition: true, resize: true, selectedAttraction: 0.025, setGallerySize: true // watchCSS: false, // wrapAround: false }; // hash of methods triggered on _create() Flickity.createMethods = []; var proto = Flickity.prototype; // inherit EventEmitter utils.extend( proto, EvEmitter.prototype ); proto._create = function() { // add id for Flickity.data var id = this.guid = ++GUID; this.element.flickityGUID = id; // expando instances[ id ] = this; // associate via id // initial properties this.selectedIndex = 0; // how many frames slider has been in same position this.restingFrames = 0; // initial physics properties this.x = 0; this.velocity = 0; this.originSide = this.options.rightToLeft ? 'right' : 'left'; // create viewport & slider this.viewport = document.createElement('div'); this.viewport.className = 'flickity-viewport'; this._createSlider(); if ( this.options.resize || this.options.watchCSS ) { window.addEventListener( 'resize', this ); } // add listeners from on option for ( var eventName in this.options.on ) { var listener = this.options.on[ eventName ]; this.on( eventName, listener ); } Flickity.createMethods.forEach( function( method ) { this[ method ](); }, this ); if ( this.options.watchCSS ) { this.watchCSS(); } else { this.activate(); } }; /** * set options * @param {Object} opts */ proto.option = function( opts ) { utils.extend( this.options, opts ); }; proto.activate = function() { if ( this.isActive ) { return; } this.isActive = true; this.element.classList.add('flickity-enabled'); if ( this.options.rightToLeft ) { this.element.classList.add('flickity-rtl'); } this.getSize(); // move initial cell elements so they can be loaded as cells var cellElems = this._filterFindCellElements( this.element.children ); moveElements( cellElems, this.slider ); this.viewport.appendChild( this.slider ); this.element.appendChild( this.viewport ); // get cells from children this.reloadCells(); if ( this.options.accessibility ) { // allow element to focusable this.element.tabIndex = 0; // listen for key presses this.element.addEventListener( 'keydown', this ); } this.emitEvent('activate'); var index; var initialIndex = this.options.initialIndex; if ( this.isInitActivated ) { index = this.selectedIndex; } else if ( initialIndex !== undefined ) { index = this.cells[ initialIndex ] ? initialIndex : 0; } else { index = 0; } // select instantly this.select( index, false, true ); // flag for initial activation, for using initialIndex this.isInitActivated = true; // ready event. #493 this.dispatchEvent('ready'); }; // slider positions the cells proto._createSlider = function() { // slider element does all the positioning var slider = document.createElement('div'); slider.className = 'flickity-slider'; slider.style[ this.originSide ] = 0; this.slider = slider; }; proto._filterFindCellElements = function( elems ) { return utils.filterFindElements( elems, this.options.cellSelector ); }; // goes through all children proto.reloadCells = function() { // collection of item elements this.cells = this._makeCells( this.slider.children ); this.positionCells(); this._getWrapShiftCells(); this.setGallerySize(); }; /** * turn elements into Flickity.Cells * @param {Array or NodeList or HTMLElement} elems * @returns {Array} items - collection of new Flickity Cells */ proto._makeCells = function( elems ) { var cellElems = this._filterFindCellElements( elems ); // create new Flickity for collection var cells = cellElems.map( function( cellElem ) { return new Cell( cellElem, this ); }, this ); return cells; }; proto.getLastCell = function() { return this.cells[ this.cells.length - 1 ]; }; proto.getLastSlide = function() { return this.slides[ this.slides.length - 1 ]; }; // positions all cells proto.positionCells = function() { // size all cells this._sizeCells( this.cells ); // position all cells this._positionCells( 0 ); }; /** * position certain cells * @param {Integer} index - which cell to start with */ proto._positionCells = function( index ) { index = index || 0; // also measure maxCellHeight // start 0 if positioning all cells this.maxCellHeight = index ? this.maxCellHeight || 0 : 0; var cellX = 0; // get cellX if ( index > 0 ) { var startCell = this.cells[ index - 1 ]; cellX = startCell.x + startCell.size.outerWidth; } var len = this.cells.length; for ( var i=index; i < len; i++ ) { var cell = this.cells[i]; cell.setPosition( cellX ); cellX += cell.size.outerWidth; this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight ); } // keep track of cellX for wrap-around this.slideableWidth = cellX; // slides this.updateSlides(); // contain slides target this._containSlides(); // update slidesWidth this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0; }; /** * cell.getSize() on multiple cells * @param {Array} cells */ proto._sizeCells = function( cells ) { cells.forEach( function( cell ) { cell.getSize(); }); }; // -------------------------- -------------------------- // proto.updateSlides = function() { this.slides = []; if ( !this.cells.length ) { return; } var slide = new Slide( this ); this.slides.push( slide ); var isOriginLeft = this.originSide == 'left'; var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft'; var canCellFit = this._getCanCellFit(); this.cells.forEach( function( cell, i ) { // just add cell if first cell in slide if ( !slide.cells.length ) { slide.addCell( cell ); return; } var slideWidth = ( slide.outerWidth - slide.firstMargin ) + ( cell.size.outerWidth - cell.size[ nextMargin ] ); if ( canCellFit.call( this, i, slideWidth ) ) { slide.addCell( cell ); } else { // doesn't fit, new slide slide.updateTarget(); slide = new Slide( this ); this.slides.push( slide ); slide.addCell( cell ); } }, this ); // last slide slide.updateTarget(); // update .selectedSlide this.updateSelectedSlide(); }; proto._getCanCellFit = function() { var groupCells = this.options.groupCells; if ( !groupCells ) { return function() { return false; }; } else if ( typeof groupCells == 'number' ) { // group by number. 3 -> [0,1,2], [3,4,5], ... var number = parseInt( groupCells, 10 ); return function( i ) { return ( i % number ) !== 0; }; } // default, group by width of slide // parse '75% var percentMatch = typeof groupCells == 'string' && groupCells.match(/^(\d+)%$/); var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1; return function( i, slideWidth ) { return slideWidth <= ( this.size.innerWidth + 1 ) * percent; }; }; // alias _init for jQuery plugin .flickity() proto._init = proto.reposition = function() { this.positionCells(); this.positionSliderAtSelected(); }; proto.getSize = function() { this.size = getSize( this.element ); this.setCellAlign(); this.cursorPosition = this.size.innerWidth * this.cellAlign; }; var cellAlignShorthands = { // cell align, then based on origin side center: { left: 0.5, right: 0.5 }, left: { left: 0, right: 1 }, right: { right: 0, left: 1 } }; proto.setCellAlign = function() { var shorthand = cellAlignShorthands[ this.options.cellAlign ]; this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign; }; proto.setGallerySize = function() { if ( this.options.setGallerySize ) { var height = this.options.adaptiveHeight && this.selectedSlide ? this.selectedSlide.height : this.maxCellHeight; this.viewport.style.height = height + 'px'; } }; proto._getWrapShiftCells = function() { // only for wrap-around if ( !this.options.wrapAround ) { return; } // unshift previous cells this._unshiftCells( this.beforeShiftCells ); this._unshiftCells( this.afterShiftCells ); // get before cells // initial gap var gapX = this.cursorPosition; var cellIndex = this.cells.length - 1; this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 ); // get after cells // ending gap between last cell and end of gallery viewport gapX = this.size.innerWidth - this.cursorPosition; // start cloning at first cell, working forwards this.afterShiftCells = this._getGapCells( gapX, 0, 1 ); }; proto._getGapCells = function( gapX, cellIndex, increment ) { // keep adding cells until the cover the initial gap var cells = []; while ( gapX > 0 ) { var cell = this.cells[ cellIndex ]; if ( !cell ) { break; } cells.push( cell ); cellIndex += increment; gapX -= cell.size.outerWidth; } return cells; }; // ----- contain ----- // // contain cell targets so no excess sliding proto._containSlides = function() { if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) { return; } var isRightToLeft = this.options.rightToLeft; var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft'; var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight'; var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ]; // content is less than gallery size var isContentSmaller = contentWidth < this.size.innerWidth; // bounds var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ]; var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign ); // contain each cell target this.slides.forEach( function( slide ) { if ( isContentSmaller ) { // all cells fit inside gallery slide.target = contentWidth * this.cellAlign; } else { // contain to bounds slide.target = Math.max( slide.target, beginBound ); slide.target = Math.min( slide.target, endBound ); } }, this ); }; // ----- ----- // /** * emits events via eventEmitter and jQuery events * @param {String} type - name of event * @param {Event} event - original event * @param {Array} args - extra arguments */ proto.dispatchEvent = function( type, event, args ) { var emitArgs = event ? [ event ].concat( args ) : args; this.emitEvent( type, emitArgs ); if ( jQuery && this.$element ) { // default trigger with type if no event type += this.options.namespaceJQueryEvents ? '.flickity' : ''; var $event = type; if ( event ) { // create jQuery event var jQEvent = jQuery.Event( event ); jQEvent.type = type; $event = jQEvent; } this.$element.trigger( $event, args ); } }; // -------------------------- select -------------------------- // /** * @param {Integer} index - index of the slide * @param {Boolean} isWrap - will wrap-around to last/first if at the end * @param {Boolean} isInstant - will immediately set position at selected cell */ proto.select = function( index, isWrap, isInstant ) { if ( !this.isActive ) { return; } index = parseInt( index, 10 ); this._wrapSelect( index ); if ( this.options.wrapAround || isWrap ) { index = utils.modulo( index, this.slides.length ); } // bail if invalid index if ( !this.slides[ index ] ) { return; } var prevIndex = this.selectedIndex; this.selectedIndex = index; this.updateSelectedSlide(); if ( isInstant ) { this.positionSliderAtSelected(); } else { this.startAnimation(); } if ( this.options.adaptiveHeight ) { this.setGallerySize(); } // events this.dispatchEvent( 'select', null, [ index ] ); // change event if new index if ( index != prevIndex ) { this.dispatchEvent( 'change', null, [ index ] ); } // old v1 event name, remove in v3 this.dispatchEvent('cellSelect'); }; // wraps position for wrapAround, to move to closest slide. #113 proto._wrapSelect = function( index ) { var len = this.slides.length; var isWrapping = this.options.wrapAround && len > 1; if ( !isWrapping ) { return index; } var wrapIndex = utils.modulo( index, len ); // go to shortest var delta = Math.abs( wrapIndex - this.selectedIndex ); var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex ); var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex ); if ( !this.isDragSelect && backWrapDelta < delta ) { index += len; } else if ( !this.isDragSelect && forewardWrapDelta < delta ) { index -= len; } // wrap position so slider is within normal area if ( index < 0 ) { this.x -= this.slideableWidth; } else if ( index >= len ) { this.x += this.slideableWidth; } }; proto.previous = function( isWrap, isInstant ) { this.select( this.selectedIndex - 1, isWrap, isInstant ); }; proto.next = function( isWrap, isInstant ) { this.select( this.selectedIndex + 1, isWrap, isInstant ); }; proto.updateSelectedSlide = function() { var slide = this.slides[ this.selectedIndex ]; // selectedIndex could be outside of slides, if triggered before resize() if ( !slide ) { return; } // unselect previous selected slide this.unselectSelectedSlide(); // update new selected slide this.selectedSlide = slide; slide.select(); this.selectedCells = slide.cells; this.selectedElements = slide.getCellElements(); // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility // Remove in v3? this.selectedCell = slide.cells[0]; this.selectedElement = this.selectedElements[0]; }; proto.unselectSelectedSlide = function() { if ( this.selectedSlide ) { this.selectedSlide.unselect(); } }; /** * select slide from number or cell element * @param {Element or Number} elem */ proto.selectCell = function( value, isWrap, isInstant ) { // get cell var cell = this.queryCell( value ); if ( !cell ) { return; } var index = this.getCellSlideIndex( cell ); this.select( index, isWrap, isInstant ); }; proto.getCellSlideIndex = function( cell ) { // get index of slides that has cell for ( var i=0; i < this.slides.length; i++ ) { var slide = this.slides[i]; var index = slide.cells.indexOf( cell ); if ( index != -1 ) { return i; } } }; // -------------------------- get cells -------------------------- // /** * get Flickity.Cell, given an Element * @param {Element} elem * @returns {Flickity.Cell} item */ proto.getCell = function( elem ) { // loop through cells to get the one that matches for ( var i=0; i < this.cells.length; i++ ) { var cell = this.cells[i]; if ( cell.element == elem ) { return cell; } } }; /** * get collection of Flickity.Cells, given Elements * @param {Element, Array, NodeList} elems * @returns {Array} cells - Flickity.Cells */ proto.getCells = function( elems ) { elems = utils.makeArray( elems ); var cells = []; elems.forEach( function( elem ) { var cell = this.getCell( elem ); if ( cell ) { cells.push( cell ); } }, this ); return cells; }; /** * get cell elements * @returns {Array} cellElems */ proto.getCellElements = function() { return this.cells.map( function( cell ) { return cell.element; }); }; /** * get parent cell from an element * @param {Element} elem * @returns {Flickit.Cell} cell */ proto.getParentCell = function( elem ) { // first check if elem is cell var cell = this.getCell( elem ); if ( cell ) { return cell; } // try to get parent cell elem elem = utils.getParent( elem, '.flickity-slider > *' ); return this.getCell( elem ); }; /** * get cells adjacent to a slide * @param {Integer} adjCount - number of adjacent slides * @param {Integer} index - index of slide to start * @returns {Array} cells - array of Flickity.Cells */ proto.getAdjacentCellElements = function( adjCount, index ) { if ( !adjCount ) { return this.selectedSlide.getCellElements(); } index = index === undefined ? this.selectedIndex : index; var len = this.slides.length; if ( 1 + ( adjCount * 2 ) >= len ) { return this.getCellElements(); } var cellElems = []; for ( var i = index - adjCount; i <= index + adjCount ; i++ ) { var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i; var slide = this.slides[ slideIndex ]; if ( slide ) { cellElems = cellElems.concat( slide.getCellElements() ); } } return cellElems; }; /** * select slide from number or cell element * @param {Element, Selector String, or Number} selector */ proto.queryCell = function( selector ) { if ( typeof selector == 'number' ) { // use number as index return this.cells[ selector ]; } if ( typeof selector == 'string' ) { // use string as selector, get element selector = this.element.querySelector( selector ); } // get cell from element return this.getCell( selector ); }; // -------------------------- events -------------------------- // proto.uiChange = function() { this.emitEvent('uiChange'); }; proto.childUIPointerDown = function( event ) { this.emitEvent( 'childUIPointerDown', [ event ] ); }; // ----- resize ----- // proto.onresize = function() { this.watchCSS(); this.resize(); }; utils.debounceMethod( Flickity, 'onresize', 150 ); proto.resize = function() { if ( !this.isActive ) { return; } this.getSize(); // wrap values if ( this.options.wrapAround ) { this.x = utils.modulo( this.x, this.slideableWidth ); } this.positionCells(); this._getWrapShiftCells(); this.setGallerySize(); this.emitEvent('resize'); // update selected index for group slides, instant // TODO: position can be lost between groups of various numbers var selectedElement = this.selectedElements && this.selectedElements[0]; this.selectCell( selectedElement, false, true ); }; // watches the :after property, activates/deactivates proto.watchCSS = function() { var watchOption = this.options.watchCSS; if ( !watchOption ) { return; } var afterContent = getComputedStyle( this.element, ':after' ).content; // activate if :after { content: 'flickity' } if ( afterContent.indexOf('flickity') != -1 ) { this.activate(); } else { this.deactivate(); } }; // ----- keydown ----- // // go previous/next if left/right keys pressed proto.onkeydown = function( event ) { // only work if element is in focus var isNotFocused = document.activeElement && document.activeElement != this.element; if ( !this.options.accessibility ||isNotFocused ) { return; } var handler = Flickity.keyboardHandlers[ event.keyCode ]; if ( handler ) { handler.call( this ); } }; Flickity.keyboardHandlers = { // left arrow 37: function() { var leftMethod = this.options.rightToLeft ? 'next' : 'previous'; this.uiChange(); this[ leftMethod ](); }, // right arrow 39: function() { var rightMethod = this.options.rightToLeft ? 'previous' : 'next'; this.uiChange(); this[ rightMethod ](); }, }; // ----- focus ----- // proto.focus = function() { var prevScrollY = window.pageYOffset; this.element.focus(); // hack to fix scroll jump after focus, #76 if ( window.pageYOffset != prevScrollY ) { window.scrollTo( window.pageXOffset, prevScrollY ); } }; // -------------------------- destroy -------------------------- // // deactivate all Flickity functionality, but keep stuff available proto.deactivate = function() { if ( !this.isActive ) { return; } this.element.classList.remove('flickity-enabled'); this.element.classList.remove('flickity-rtl'); this.unselectSelectedSlide(); // destroy cells this.cells.forEach( function( cell ) { cell.destroy(); }); this.element.removeChild( this.viewport ); // move child elements back into element moveElements( this.slider.children, this.element ); if ( this.options.accessibility ) { this.element.removeAttribute('tabIndex'); this.element.removeEventListener( 'keydown', this ); } // set flags this.isActive = false; this.emitEvent('deactivate'); }; proto.destroy = function() { this.deactivate(); window.removeEventListener( 'resize', this ); this.emitEvent('destroy'); if ( jQuery && this.$element ) { jQuery.removeData( this.element, 'flickity' ); } delete this.element.flickityGUID; delete instances[ this.guid ]; }; // -------------------------- prototype -------------------------- // utils.extend( proto, animatePrototype ); // -------------------------- extras -------------------------- // /** * get Flickity instance from element * @param {Element} elem * @returns {Flickity} */ Flickity.data = function( elem ) { elem = utils.getQueryElement( elem ); var id = elem && elem.flickityGUID; return id && instances[ id ]; }; utils.htmlInit( Flickity, 'flickity' ); if ( jQuery && jQuery.bridget ) { jQuery.bridget( 'flickity', Flickity ); } // set internal jQuery, for Webpack + jQuery v3, #478 Flickity.setJQuery = function( jq ) { jQuery = jq; }; Flickity.Cell = Cell; return Flickity; })); /*! * Unipointer v2.3.0 * base class for doing one thing with pointer event * MIT license */ /*jshint browser: true, undef: true, unused: true, strict: true */ ( function( window, factory ) { // universal module definition /* jshint strict: false */ /*global define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'unipointer/unipointer',[ 'ev-emitter/ev-emitter' ], function( EvEmitter ) { return factory( window, EvEmitter ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('ev-emitter') ); } else { // browser global window.Unipointer = factory( window, window.EvEmitter ); } }( window, function factory( window, EvEmitter ) { function noop() {} function Unipointer() {} // inherit EvEmitter var proto = Unipointer.prototype = Object.create( EvEmitter.prototype ); proto.bindStartEvent = function( elem ) { this._bindStartEvent( elem, true ); }; proto.unbindStartEvent = function( elem ) { this._bindStartEvent( elem, false ); }; /** * Add or remove start event * @param {Boolean} isAdd - remove if falsey */ proto._bindStartEvent = function( elem, isAdd ) { // munge isAdd, default to true isAdd = isAdd === undefined ? true : isAdd; var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener'; // default to mouse events var startEvent = 'mousedown'; if ( window.PointerEvent ) { // Pointer Events startEvent = 'pointerdown'; } else if ( 'ontouchstart' in window ) { // Touch Events. iOS Safari startEvent = 'touchstart'; } elem[ bindMethod ]( startEvent, this ); }; // trigger handler methods for events proto.handleEvent = function( event ) { var method = 'on' + event.type; if ( this[ method ] ) { this[ method ]( event ); } }; // returns the touch that we're keeping track of proto.getTouch = function( touches ) { for ( var i=0; i < touches.length; i++ ) { var touch = touches[i]; if ( touch.identifier == this.pointerIdentifier ) { return touch; } } }; // ----- start event ----- // proto.onmousedown = function( event ) { // dismiss clicks from right or middle buttons var button = event.button; if ( button && ( button !== 0 && button !== 1 ) ) { return; } this._pointerDown( event, event ); }; proto.ontouchstart = function( event ) { this._pointerDown( event, event.changedTouches[0] ); }; proto.onpointerdown = function( event ) { this._pointerDown( event, event ); }; /** * pointer start * @param {Event} event * @param {Event or Touch} pointer */ proto._pointerDown = function( event, pointer ) { // dismiss right click and other pointers // button = 0 is okay, 1-4 not if ( event.button || this.isPointerDown ) { return; } this.isPointerDown = true; // save pointer identifier to match up touch events this.pointerIdentifier = pointer.pointerId !== undefined ? // pointerId for pointer events, touch.indentifier for touch events pointer.pointerId : pointer.identifier; this.pointerDown( event, pointer ); }; proto.pointerDown = function( event, pointer ) { this._bindPostStartEvents( event ); this.emitEvent( 'pointerDown', [ event, pointer ] ); }; // hash of events to be bound after start event var postStartEvents = { mousedown: [ 'mousemove', 'mouseup' ], touchstart: [ 'touchmove', 'touchend', 'touchcancel' ], pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ], }; proto._bindPostStartEvents = function( event ) { if ( !event ) { return; } // get proper events to match start event var events = postStartEvents[ event.type ]; // bind events to node events.forEach( function( eventName ) { window.addEventListener( eventName, this ); }, this ); // save these arguments this._boundPointerEvents = events; }; proto._unbindPostStartEvents = function() { // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug) if ( !this._boundPointerEvents ) { return; } this._boundPointerEvents.forEach( function( eventName ) { window.removeEventListener( eventName, this ); }, this ); delete this._boundPointerEvents; }; // ----- move event ----- // proto.onmousemove = function( event ) { this._pointerMove( event, event ); }; proto.onpointermove = function( event ) { if ( event.pointerId == this.pointerIdentifier ) { this._pointerMove( event, event ); } }; proto.ontouchmove = function( event ) { var touch = this.getTouch( event.changedTouches ); if ( touch ) { this._pointerMove( event, touch ); } }; /** * pointer move * @param {Event} event * @param {Event or Touch} pointer * @private */ proto._pointerMove = function( event, pointer ) { this.pointerMove( event, pointer ); }; // public proto.pointerMove = function( event, pointer ) { this.emitEvent( 'pointerMove', [ event, pointer ] ); }; // ----- end event ----- // proto.onmouseup = function( event ) { this._pointerUp( event, event ); }; proto.onpointerup = function( event ) { if ( event.pointerId == this.pointerIdentifier ) { this._pointerUp( event, event ); } }; proto.ontouchend = function( event ) { var touch = this.getTouch( event.changedTouches ); if ( touch ) { this._pointerUp( event, touch ); } }; /** * pointer up * @param {Event} event * @param {Event or Touch} pointer * @private */ proto._pointerUp = function( event, pointer ) { this._pointerDone(); this.pointerUp( event, pointer ); }; // public proto.pointerUp = function( event, pointer ) { this.emitEvent( 'pointerUp', [ event, pointer ] ); }; // ----- pointer done ----- // // triggered on pointer up & pointer cancel proto._pointerDone = function() { this._pointerReset(); this._unbindPostStartEvents(); this.pointerDone(); }; proto._pointerReset = function() { // reset properties this.isPointerDown = false; delete this.pointerIdentifier; }; proto.pointerDone = noop; // ----- pointer cancel ----- // proto.onpointercancel = function( event ) { if ( event.pointerId == this.pointerIdentifier ) { this._pointerCancel( event, event ); } }; proto.ontouchcancel = function( event ) { var touch = this.getTouch( event.changedTouches ); if ( touch ) { this._pointerCancel( event, touch ); } }; /** * pointer cancel * @param {Event} event * @param {Event or Touch} pointer * @private */ proto._pointerCancel = function( event, pointer ) { this._pointerDone(); this.pointerCancel( event, pointer ); }; // public proto.pointerCancel = function( event, pointer ) { this.emitEvent( 'pointerCancel', [ event, pointer ] ); }; // ----- ----- // // utility function for getting x/y coords from event Unipointer.getPointerPoint = function( pointer ) { return { x: pointer.pageX, y: pointer.pageY }; }; // ----- ----- // return Unipointer; })); /*! * Unidragger v2.3.0 * Draggable base class * MIT license */ /*jshint browser: true, unused: true, undef: true, strict: true */ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /*globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'unidragger/unidragger',[ 'unipointer/unipointer' ], function( Unipointer ) { return factory( window, Unipointer ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('unipointer') ); } else { // browser global window.Unidragger = factory( window, window.Unipointer ); } }( window, function factory( window, Unipointer ) { // -------------------------- Unidragger -------------------------- // function Unidragger() {} // inherit Unipointer & EvEmitter var proto = Unidragger.prototype = Object.create( Unipointer.prototype ); // ----- bind start ----- // proto.bindHandles = function() { this._bindHandles( true ); }; proto.unbindHandles = function() { this._bindHandles( false ); }; /** * Add or remove start event * @param {Boolean} isAdd */ proto._bindHandles = function( isAdd ) { // munge isAdd, default to true isAdd = isAdd === undefined ? true : isAdd; // bind each handle var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener'; var touchAction = isAdd ? this._touchActionValue : ''; for ( var i=0; i < this.handles.length; i++ ) { var handle = this.handles[i]; this._bindStartEvent( handle, isAdd ); handle[ bindMethod ]( 'click', this ); // touch-action: none to override browser touch gestures. metafizzy/flickity#540 if ( window.PointerEvent ) { handle.style.touchAction = touchAction; } } }; // prototype so it can be overwriteable by Flickity proto._touchActionValue = 'none'; // ----- start event ----- // /** * pointer start * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerDown = function( event, pointer ) { var isOkay = this.okayPointerDown( event ); if ( !isOkay ) { return; } // track start event position this.pointerDownPointer = pointer; event.preventDefault(); this.pointerDownBlur(); // bind move and end events this._bindPostStartEvents( event ); this.emitEvent( 'pointerDown', [ event, pointer ] ); }; // nodes that have text fields var cursorNodes = { TEXTAREA: true, INPUT: true, SELECT: true, OPTION: true, }; // input types that do not have text fields var clickTypes = { radio: true, checkbox: true, button: true, submit: true, image: true, file: true, }; // dismiss inputs with text fields. flickity#403, flickity#404 proto.okayPointerDown = function( event ) { var isCursorNode = cursorNodes[ event.target.nodeName ]; var isClickType = clickTypes[ event.target.type ]; var isOkay = !isCursorNode || isClickType; if ( !isOkay ) { this._pointerReset(); } return isOkay; }; // kludge to blur previously focused input proto.pointerDownBlur = function() { var focused = document.activeElement; // do not blur body for IE10, metafizzy/flickity#117 var canBlur = focused && focused.blur && focused != document.body; if ( canBlur ) { focused.blur(); } }; // ----- move event ----- // /** * drag move * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerMove = function( event, pointer ) { var moveVector = this._dragPointerMove( event, pointer ); this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] ); this._dragMove( event, pointer, moveVector ); }; // base pointer move logic proto._dragPointerMove = function( event, pointer ) { var moveVector = { x: pointer.pageX - this.pointerDownPointer.pageX, y: pointer.pageY - this.pointerDownPointer.pageY }; // start drag if pointer has moved far enough to start drag if ( !this.isDragging && this.hasDragStarted( moveVector ) ) { this._dragStart( event, pointer ); } return moveVector; }; // condition if pointer has moved far enough to start drag proto.hasDragStarted = function( moveVector ) { return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3; }; // ----- end event ----- // /** * pointer up * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerUp = function( event, pointer ) { this.emitEvent( 'pointerUp', [ event, pointer ] ); this._dragPointerUp( event, pointer ); }; proto._dragPointerUp = function( event, pointer ) { if ( this.isDragging ) { this._dragEnd( event, pointer ); } else { // pointer didn't move enough for drag to start this._staticClick( event, pointer ); } }; // -------------------------- drag -------------------------- // // dragStart proto._dragStart = function( event, pointer ) { this.isDragging = true; // prevent clicks this.isPreventingClicks = true; this.dragStart( event, pointer ); }; proto.dragStart = function( event, pointer ) { this.emitEvent( 'dragStart', [ event, pointer ] ); }; // dragMove proto._dragMove = function( event, pointer, moveVector ) { // do not drag if not dragging yet if ( !this.isDragging ) { return; } this.dragMove( event, pointer, moveVector ); }; proto.dragMove = function( event, pointer, moveVector ) { event.preventDefault(); this.emitEvent( 'dragMove', [ event, pointer, moveVector ] ); }; // dragEnd proto._dragEnd = function( event, pointer ) { // set flags this.isDragging = false; // re-enable clicking async setTimeout( function() { delete this.isPreventingClicks; }.bind( this ) ); this.dragEnd( event, pointer ); }; proto.dragEnd = function( event, pointer ) { this.emitEvent( 'dragEnd', [ event, pointer ] ); }; // ----- onclick ----- // // handle all clicks and prevent clicks when dragging proto.onclick = function( event ) { if ( this.isPreventingClicks ) { event.preventDefault(); } }; // ----- staticClick ----- // // triggered after pointer down & up with no/tiny movement proto._staticClick = function( event, pointer ) { // ignore emulated mouse up clicks if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) { return; } this.staticClick( event, pointer ); // set flag for emulated clicks 300ms after touchend if ( event.type != 'mouseup' ) { this.isIgnoringMouseUp = true; // reset flag after 300ms setTimeout( function() { delete this.isIgnoringMouseUp; }.bind( this ), 400 ); } }; proto.staticClick = function( event, pointer ) { this.emitEvent( 'staticClick', [ event, pointer ] ); }; // ----- utils ----- // Unidragger.getPointerPoint = Unipointer.getPointerPoint; // ----- ----- // return Unidragger; })); // drag ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/drag',[ './flickity', 'unidragger/unidragger', 'fizzy-ui-utils/utils' ], function( Flickity, Unidragger, utils ) { return factory( window, Flickity, Unidragger, utils ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('unidragger'), require('fizzy-ui-utils') ); } else { // browser global window.Flickity = factory( window, window.Flickity, window.Unidragger, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, Unidragger, utils ) { // ----- defaults ----- // utils.extend( Flickity.defaults, { draggable: '>1', dragThreshold: 3, }); // ----- create ----- // Flickity.createMethods.push('_createDrag'); // -------------------------- drag prototype -------------------------- // var proto = Flickity.prototype; utils.extend( proto, Unidragger.prototype ); proto._touchActionValue = 'pan-y'; // -------------------------- -------------------------- // var isTouch = 'createTouch' in document; var isTouchmoveScrollCanceled = false; proto._createDrag = function() { this.on( 'activate', this.onActivateDrag ); this.on( 'uiChange', this._uiChangeDrag ); this.on( 'childUIPointerDown', this._childUIPointerDownDrag ); this.on( 'deactivate', this.unbindDrag ); this.on( 'cellChange', this.updateDraggable ); // TODO updateDraggable on resize? if groupCells & slides change // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior // #457, RubaXa/Sortable#973 if ( isTouch && !isTouchmoveScrollCanceled ) { window.addEventListener( 'touchmove', function() {}); isTouchmoveScrollCanceled = true; } }; proto.onActivateDrag = function() { this.handles = [ this.viewport ]; this.bindHandles(); this.updateDraggable(); }; proto.onDeactivateDrag = function() { this.unbindHandles(); this.element.classList.remove('is-draggable'); }; proto.updateDraggable = function() { // disable dragging if less than 2 slides. #278 if ( this.options.draggable == '>1' ) { this.isDraggable = this.slides.length > 1; } else { this.isDraggable = this.options.draggable; } if ( this.isDraggable ) { this.element.classList.add('is-draggable'); } else { this.element.classList.remove('is-draggable'); } }; // backwards compatibility proto.bindDrag = function() { this.options.draggable = true; this.updateDraggable(); }; proto.unbindDrag = function() { this.options.draggable = false; this.updateDraggable(); }; proto._uiChangeDrag = function() { delete this.isFreeScrolling; }; proto._childUIPointerDownDrag = function( event ) { // allow focus & preventDefault even when not draggable // so child UI elements keep focus on carousel. #721 event.preventDefault(); this.pointerDownFocus( event ); }; // -------------------------- pointer events -------------------------- // proto.pointerDown = function( event, pointer ) { if ( !this.isDraggable ) { this._pointerDownDefault( event, pointer ); return; } var isOkay = this.okayPointerDown( event ); if ( !isOkay ) { return; } this._pointerDownPreventDefault( event ); this.pointerDownFocus( event ); // blur if ( document.activeElement != this.element ) { // do not blur if already focused this.pointerDownBlur(); } // stop if it was moving this.dragX = this.x; this.viewport.classList.add('is-pointer-down'); // track scrolling this.pointerDownScroll = getScrollPosition(); window.addEventListener( 'scroll', this ); this._pointerDownDefault( event, pointer ); }; // default pointerDown logic, used for staticClick proto._pointerDownDefault = function( event, pointer ) { // track start event position this.pointerDownPointer = pointer; // bind move and end events this._bindPostStartEvents( event ); this.dispatchEvent( 'pointerDown', event, [ pointer ] ); }; var focusNodes = { INPUT: true, TEXTAREA: true, SELECT: true, }; proto.pointerDownFocus = function( event ) { var isFocusNode = focusNodes[ event.target.nodeName ]; if ( !isFocusNode ) { this.focus(); } }; proto._pointerDownPreventDefault = function( event ) { var isTouchStart = event.type == 'touchstart'; var isTouchPointer = event.pointerType == 'touch'; var isFocusNode = focusNodes[ event.target.nodeName ]; if ( !isTouchStart && !isTouchPointer && !isFocusNode ) { event.preventDefault(); } }; // ----- move ----- // proto.hasDragStarted = function( moveVector ) { return Math.abs( moveVector.x ) > this.options.dragThreshold; }; // ----- up ----- // proto.pointerUp = function( event, pointer ) { delete this.isTouchScrolling; this.viewport.classList.remove('is-pointer-down'); this.dispatchEvent( 'pointerUp', event, [ pointer ] ); this._dragPointerUp( event, pointer ); }; proto.pointerDone = function() { window.removeEventListener( 'scroll', this ); delete this.pointerDownScroll; }; // -------------------------- dragging -------------------------- // proto.dragStart = function( event, pointer ) { if ( !this.isDraggable ) { return; } this.dragStartPosition = this.x; this.startAnimation(); window.removeEventListener( 'scroll', this ); this.dispatchEvent( 'dragStart', event, [ pointer ] ); }; proto.pointerMove = function( event, pointer ) { var moveVector = this._dragPointerMove( event, pointer ); this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] ); this._dragMove( event, pointer, moveVector ); }; proto.dragMove = function( event, pointer, moveVector ) { if ( !this.isDraggable ) { return; } event.preventDefault(); this.previousDragX = this.dragX; // reverse if right-to-left var direction = this.options.rightToLeft ? -1 : 1; if ( this.options.wrapAround ) { // wrap around move. #589 moveVector.x = moveVector.x % this.slideableWidth; } var dragX = this.dragStartPosition + moveVector.x * direction; if ( !this.options.wrapAround && this.slides.length ) { // slow drag var originBound = Math.max( -this.slides[0].target, this.dragStartPosition ); dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX; var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition ); dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX; } this.dragX = dragX; this.dragMoveTime = new Date(); this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] ); }; proto.dragEnd = function( event, pointer ) { if ( !this.isDraggable ) { return; } if ( this.options.freeScroll ) { this.isFreeScrolling = true; } // set selectedIndex based on where flick will end up var index = this.dragEndRestingSelect(); if ( this.options.freeScroll && !this.options.wrapAround ) { // if free-scroll & not wrap around // do not free-scroll if going outside of bounding slides // so bounding slides can attract slider, and keep it in bounds var restingX = this.getRestingPosition(); this.isFreeScrolling = -restingX > this.slides[0].target && -restingX < this.getLastSlide().target; } else if ( !this.options.freeScroll && index == this.selectedIndex ) { // boost selection if selected index has not changed index += this.dragEndBoostSelect(); } delete this.previousDragX; // apply selection // TODO refactor this, selecting here feels weird // HACK, set flag so dragging stays in correct direction this.isDragSelect = this.options.wrapAround; this.select( index ); delete this.isDragSelect; this.dispatchEvent( 'dragEnd', event, [ pointer ] ); }; proto.dragEndRestingSelect = function() { var restingX = this.getRestingPosition(); // how far away from selected slide var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) ); // get closet resting going up and going down var positiveResting = this._getClosestResting( restingX, distance, 1 ); var negativeResting = this._getClosestResting( restingX, distance, -1 ); // use closer resting for wrap-around var index = positiveResting.distance < negativeResting.distance ? positiveResting.index : negativeResting.index; return index; }; /** * given resting X and distance to selected cell * get the distance and index of the closest cell * @param {Number} restingX - estimated post-flick resting position * @param {Number} distance - distance to selected cell * @param {Integer} increment - +1 or -1, going up or down * @returns {Object} - { distance: {Number}, index: {Integer} } */ proto._getClosestResting = function( restingX, distance, increment ) { var index = this.selectedIndex; var minDistance = Infinity; var condition = this.options.contain && !this.options.wrapAround ? // if contain, keep going if distance is equal to minDistance function( d, md ) { return d <= md; } : function( d, md ) { return d < md; }; while ( condition( distance, minDistance ) ) { // measure distance to next cell index += increment; minDistance = distance; distance = this.getSlideDistance( -restingX, index ); if ( distance === null ) { break; } distance = Math.abs( distance ); } return { distance: minDistance, // selected was previous index index: index - increment }; }; /** * measure distance between x and a slide target * @param {Number} x * @param {Integer} index - slide index */ proto.getSlideDistance = function( x, index ) { var len = this.slides.length; // wrap around if at least 2 slides var isWrapAround = this.options.wrapAround && len > 1; var slideIndex = isWrapAround ? utils.modulo( index, len ) : index; var slide = this.slides[ slideIndex ]; if ( !slide ) { return null; } // add distance for wrap-around slides var wrap = isWrapAround ? this.slideableWidth * Math.floor( index / len ) : 0; return x - ( slide.target + wrap ); }; proto.dragEndBoostSelect = function() { // do not boost if no previousDragX or dragMoveTime if ( this.previousDragX === undefined || !this.dragMoveTime || // or if drag was held for 100 ms new Date() - this.dragMoveTime > 100 ) { return 0; } var distance = this.getSlideDistance( -this.dragX, this.selectedIndex ); var delta = this.previousDragX - this.dragX; if ( distance > 0 && delta > 0 ) { // boost to next if moving towards the right, and positive velocity return 1; } else if ( distance < 0 && delta < 0 ) { // boost to previous if moving towards the left, and negative velocity return -1; } return 0; }; // ----- staticClick ----- // proto.staticClick = function( event, pointer ) { // get clickedCell, if cell was clicked var clickedCell = this.getParentCell( event.target ); var cellElem = clickedCell && clickedCell.element; var cellIndex = clickedCell && this.cells.indexOf( clickedCell ); this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] ); }; // ----- scroll ----- // proto.onscroll = function() { var scroll = getScrollPosition(); var scrollMoveX = this.pointerDownScroll.x - scroll.x; var scrollMoveY = this.pointerDownScroll.y - scroll.y; // cancel click/tap if scroll is too much if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) { this._pointerDone(); } }; // ----- utils ----- // function getScrollPosition() { return { x: window.pageXOffset, y: window.pageYOffset }; } // ----- ----- // return Flickity; })); /*! * Tap listener v2.0.0 * listens to taps * MIT license */ /*jshint browser: true, unused: true, undef: true, strict: true */ ( function( window, factory ) { // universal module definition /*jshint strict: false*/ /*globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'tap-listener/tap-listener',[ 'unipointer/unipointer' ], function( Unipointer ) { return factory( window, Unipointer ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('unipointer') ); } else { // browser global window.TapListener = factory( window, window.Unipointer ); } }( window, function factory( window, Unipointer ) { // -------------------------- TapListener -------------------------- // function TapListener( elem ) { this.bindTap( elem ); } // inherit Unipointer & EventEmitter var proto = TapListener.prototype = Object.create( Unipointer.prototype ); /** * bind tap event to element * @param {Element} elem */ proto.bindTap = function( elem ) { if ( !elem ) { return; } this.unbindTap(); this.tapElement = elem; this._bindStartEvent( elem, true ); }; proto.unbindTap = function() { if ( !this.tapElement ) { return; } this._bindStartEvent( this.tapElement, true ); delete this.tapElement; }; /** * pointer up * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerUp = function( event, pointer ) { // ignore emulated mouse up clicks if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) { return; } var pointerPoint = Unipointer.getPointerPoint( pointer ); var boundingRect = this.tapElement.getBoundingClientRect(); var scrollX = window.pageXOffset; var scrollY = window.pageYOffset; // calculate if pointer is inside tapElement var isInside = pointerPoint.x >= boundingRect.left + scrollX && pointerPoint.x <= boundingRect.right + scrollX && pointerPoint.y >= boundingRect.top + scrollY && pointerPoint.y <= boundingRect.bottom + scrollY; // trigger callback if pointer is inside element if ( isInside ) { this.emitEvent( 'tap', [ event, pointer ] ); } // set flag for emulated clicks 300ms after touchend if ( event.type != 'mouseup' ) { this.isIgnoringMouseUp = true; // reset flag after 300ms var _this = this; setTimeout( function() { delete _this.isIgnoringMouseUp; }, 400 ); } }; proto.destroy = function() { this.pointerDone(); this.unbindTap(); }; // ----- ----- // return TapListener; })); // prev/next buttons ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/prev-next-button',[ './flickity', 'tap-listener/tap-listener', 'fizzy-ui-utils/utils' ], function( Flickity, TapListener, utils ) { return factory( window, Flickity, TapListener, utils ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('tap-listener'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.TapListener, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, TapListener, utils ) { 'use strict'; var svgURI = 'http://www.w3.org/2000/svg'; // -------------------------- PrevNextButton -------------------------- // function PrevNextButton( direction, parent ) { this.direction = direction; this.parent = parent; this._create(); } PrevNextButton.prototype = Object.create( TapListener.prototype ); PrevNextButton.prototype._create = function() { // properties this.isEnabled = true; this.isPrevious = this.direction == -1; var leftDirection = this.parent.options.rightToLeft ? 1 : -1; this.isLeft = this.direction == leftDirection; var element = this.element = document.createElement('button'); element.className = 'flickity-button flickity-prev-next-button'; element.className += this.isPrevious ? ' previous' : ' next'; // prevent button from submitting form http://stackoverflow.com/a/10836076/182183 element.setAttribute( 'type', 'button' ); // init as disabled this.disable(); element.setAttribute( 'aria-label', this.isPrevious ? 'Previous' : 'Next' ); // create arrow var svg = this.createSVG(); element.appendChild( svg ); // events this.on( 'tap', this.onTap ); this.parent.on( 'select', this.update.bind( this ) ); this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) ); }; PrevNextButton.prototype.activate = function() { this.bindTap( this.element ); // click events from keyboard this.element.addEventListener( 'click', this ); // add to DOM this.parent.element.appendChild( this.element ); }; PrevNextButton.prototype.deactivate = function() { // remove from DOM this.parent.element.removeChild( this.element ); // do regular TapListener destroy TapListener.prototype.destroy.call( this ); // click events from keyboard this.element.removeEventListener( 'click', this ); }; PrevNextButton.prototype.createSVG = function() { var svg = document.createElementNS( svgURI, 'svg'); svg.setAttribute( 'class', 'flickity-button-icon' ); svg.setAttribute( 'viewBox', '0 0 100 100' ); var path = document.createElementNS( svgURI, 'path'); var pathMovements = getArrowMovements( this.parent.options.arrowShape ); path.setAttribute( 'd', pathMovements ); path.setAttribute( 'class', 'arrow' ); // rotate arrow if ( !this.isLeft ) { path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' ); } svg.appendChild( path ); return svg; }; // get SVG path movmement function getArrowMovements( shape ) { // use shape as movement if string if ( typeof shape == 'string' ) { return shape; } // create movement string return 'M ' + shape.x0 + ',50' + ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) + ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) + ' L ' + shape.x3 + ',50 ' + ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) + ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) + ' Z'; } PrevNextButton.prototype.onTap = function() { if ( !this.isEnabled ) { return; } this.parent.uiChange(); var method = this.isPrevious ? 'previous' : 'next'; this.parent[ method ](); }; PrevNextButton.prototype.handleEvent = utils.handleEvent; PrevNextButton.prototype.onclick = function( event ) { // only allow clicks from keyboard var focused = document.activeElement; if ( focused && focused == this.element ) { this.onTap( event, event ); } }; // ----- ----- // PrevNextButton.prototype.enable = function() { if ( this.isEnabled ) { return; } this.element.disabled = false; this.isEnabled = true; }; PrevNextButton.prototype.disable = function() { if ( !this.isEnabled ) { return; } this.element.disabled = true; this.isEnabled = false; }; PrevNextButton.prototype.update = function() { // index of first or last slide, if previous or next var slides = this.parent.slides; // enable is wrapAround and at least 2 slides if ( this.parent.options.wrapAround && slides.length > 1 ) { this.enable(); return; } var lastIndex = slides.length ? slides.length - 1 : 0; var boundIndex = this.isPrevious ? 0 : lastIndex; var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable'; this[ method ](); }; PrevNextButton.prototype.destroy = function() { this.deactivate(); }; // -------------------------- Flickity prototype -------------------------- // utils.extend( Flickity.defaults, { prevNextButtons: true, arrowShape: { x0: 10, x1: 60, y1: 50, x2: 70, y2: 40, x3: 30 } }); Flickity.createMethods.push('_createPrevNextButtons'); var proto = Flickity.prototype; proto._createPrevNextButtons = function() { if ( !this.options.prevNextButtons ) { return; } this.prevButton = new PrevNextButton( -1, this ); this.nextButton = new PrevNextButton( 1, this ); this.on( 'activate', this.activatePrevNextButtons ); }; proto.activatePrevNextButtons = function() { this.prevButton.activate(); this.nextButton.activate(); this.on( 'deactivate', this.deactivatePrevNextButtons ); }; proto.deactivatePrevNextButtons = function() { this.prevButton.deactivate(); this.nextButton.deactivate(); this.off( 'deactivate', this.deactivatePrevNextButtons ); }; // -------------------------- -------------------------- // Flickity.PrevNextButton = PrevNextButton; return Flickity; })); // page dots ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/page-dots',[ './flickity', 'tap-listener/tap-listener', 'fizzy-ui-utils/utils' ], function( Flickity, TapListener, utils ) { return factory( window, Flickity, TapListener, utils ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('tap-listener'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.TapListener, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, TapListener, utils ) { // -------------------------- PageDots -------------------------- // function PageDots( parent ) { this.parent = parent; this._create(); } PageDots.prototype = new TapListener(); PageDots.prototype._create = function() { // create holder element this.holder = document.createElement('ol'); this.holder.className = 'flickity-page-dots'; // create dots, array of elements this.dots = []; // events this.on( 'tap', this.onTap ); this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) ); }; PageDots.prototype.activate = function() { this.setDots(); this.bindTap( this.holder ); // add to DOM this.parent.element.appendChild( this.holder ); }; PageDots.prototype.deactivate = function() { // remove from DOM this.parent.element.removeChild( this.holder ); TapListener.prototype.destroy.call( this ); }; PageDots.prototype.setDots = function() { // get difference between number of slides and number of dots var delta = this.parent.slides.length - this.dots.length; if ( delta > 0 ) { this.addDots( delta ); } else if ( delta < 0 ) { this.removeDots( -delta ); } }; PageDots.prototype.addDots = function( count ) { var fragment = document.createDocumentFragment(); var newDots = []; var length = this.dots.length; var max = length + count; for ( var i = length; i < max; i++ ) { var dot = document.createElement('li'); dot.className = 'dot'; dot.setAttribute( 'aria-label', 'Page dot ' + ( i + 1 ) ); fragment.appendChild( dot ); newDots.push( dot ); } this.holder.appendChild( fragment ); this.dots = this.dots.concat( newDots ); }; PageDots.prototype.removeDots = function( count ) { // remove from this.dots collection var removeDots = this.dots.splice( this.dots.length - count, count ); // remove from DOM removeDots.forEach( function( dot ) { this.holder.removeChild( dot ); }, this ); }; PageDots.prototype.updateSelected = function() { // remove selected class on previous if ( this.selectedDot ) { this.selectedDot.className = 'dot'; this.selectedDot.removeAttribute('aria-current'); } // don't proceed if no dots if ( !this.dots.length ) { return; } this.selectedDot = this.dots[ this.parent.selectedIndex ]; this.selectedDot.className = 'dot is-selected'; this.selectedDot.setAttribute( 'aria-current', 'step' ); }; PageDots.prototype.onTap = function( event ) { var target = event.target; // only care about dot clicks if ( target.nodeName != 'LI' ) { return; } this.parent.uiChange(); var index = this.dots.indexOf( target ); this.parent.select( index ); }; PageDots.prototype.destroy = function() { this.deactivate(); }; Flickity.PageDots = PageDots; // -------------------------- Flickity -------------------------- // utils.extend( Flickity.defaults, { pageDots: true }); Flickity.createMethods.push('_createPageDots'); var proto = Flickity.prototype; proto._createPageDots = function() { if ( !this.options.pageDots ) { return; } this.pageDots = new PageDots( this ); // events this.on( 'activate', this.activatePageDots ); this.on( 'select', this.updateSelectedPageDots ); this.on( 'cellChange', this.updatePageDots ); this.on( 'resize', this.updatePageDots ); this.on( 'deactivate', this.deactivatePageDots ); }; proto.activatePageDots = function() { this.pageDots.activate(); }; proto.updateSelectedPageDots = function() { this.pageDots.updateSelected(); }; proto.updatePageDots = function() { this.pageDots.setDots(); }; proto.deactivatePageDots = function() { this.pageDots.deactivate(); }; // ----- ----- // Flickity.PageDots = PageDots; return Flickity; })); // player & autoPlay ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/player',[ 'ev-emitter/ev-emitter', 'fizzy-ui-utils/utils', './flickity' ], function( EvEmitter, utils, Flickity ) { return factory( EvEmitter, utils, Flickity ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( require('ev-emitter'), require('fizzy-ui-utils'), require('./flickity') ); } else { // browser global factory( window.EvEmitter, window.fizzyUIUtils, window.Flickity ); } }( window, function factory( EvEmitter, utils, Flickity ) { // -------------------------- Player -------------------------- // function Player( parent ) { this.parent = parent; this.state = 'stopped'; // visibility change event handler this.onVisibilityChange = this.visibilityChange.bind( this ); this.onVisibilityPlay = this.visibilityPlay.bind( this ); } Player.prototype = Object.create( EvEmitter.prototype ); // start play Player.prototype.play = function() { if ( this.state == 'playing' ) { return; } // do not play if page is hidden, start playing when page is visible var isPageHidden = document.hidden; if ( isPageHidden ) { document.addEventListener( 'visibilitychange', this.onVisibilityPlay ); return; } this.state = 'playing'; // listen to visibility change document.addEventListener( 'visibilitychange', this.onVisibilityChange ); // start ticking this.tick(); }; Player.prototype.tick = function() { // do not tick if not playing if ( this.state != 'playing' ) { return; } var time = this.parent.options.autoPlay; // default to 3 seconds time = typeof time == 'number' ? time : 3000; var _this = this; // HACK: reset ticks if stopped and started within interval this.clear(); this.timeout = setTimeout( function() { _this.parent.next( true ); _this.tick(); }, time ); }; Player.prototype.stop = function() { this.state = 'stopped'; this.clear(); // remove visibility change event document.removeEventListener( 'visibilitychange', this.onVisibilityChange ); }; Player.prototype.clear = function() { clearTimeout( this.timeout ); }; Player.prototype.pause = function() { if ( this.state == 'playing' ) { this.state = 'paused'; this.clear(); } }; Player.prototype.unpause = function() { // re-start play if paused if ( this.state == 'paused' ) { this.play(); } }; // pause if page visibility is hidden, unpause if visible Player.prototype.visibilityChange = function() { var isPageHidden = document.hidden; this[ isPageHidden ? 'pause' : 'unpause' ](); }; Player.prototype.visibilityPlay = function() { this.play(); document.removeEventListener( 'visibilitychange', this.onVisibilityPlay ); }; // -------------------------- Flickity -------------------------- // utils.extend( Flickity.defaults, { pauseAutoPlayOnHover: true }); Flickity.createMethods.push('_createPlayer'); var proto = Flickity.prototype; proto._createPlayer = function() { this.player = new Player( this ); this.on( 'activate', this.activatePlayer ); this.on( 'uiChange', this.stopPlayer ); this.on( 'pointerDown', this.stopPlayer ); this.on( 'deactivate', this.deactivatePlayer ); }; proto.activatePlayer = function() { if ( !this.options.autoPlay ) { return; } this.player.play(); this.element.addEventListener( 'mouseenter', this ); }; // Player API, don't hate the ... thanks I know where the door is proto.playPlayer = function() { this.player.play(); }; proto.stopPlayer = function() { this.player.stop(); }; proto.pausePlayer = function() { this.player.pause(); }; proto.unpausePlayer = function() { this.player.unpause(); }; proto.deactivatePlayer = function() { this.player.stop(); this.element.removeEventListener( 'mouseenter', this ); }; // ----- mouseenter/leave ----- // // pause auto-play on hover proto.onmouseenter = function() { if ( !this.options.pauseAutoPlayOnHover ) { return; } this.player.pause(); this.element.addEventListener( 'mouseleave', this ); }; // resume auto-play on hover off proto.onmouseleave = function() { this.player.unpause(); this.element.removeEventListener( 'mouseleave', this ); }; // ----- ----- // Flickity.Player = Player; return Flickity; })); // add, remove cell ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/add-remove-cell',[ './flickity', 'fizzy-ui-utils/utils' ], function( Flickity, utils ) { return factory( window, Flickity, utils ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, utils ) { // append cells to a document fragment function getCellsFragment( cells ) { var fragment = document.createDocumentFragment(); cells.forEach( function( cell ) { fragment.appendChild( cell.element ); }); return fragment; } // -------------------------- add/remove cell prototype -------------------------- // var proto = Flickity.prototype; /** * Insert, prepend, or append cells * @param {Element, Array, NodeList} elems * @param {Integer} index */ proto.insert = function( elems, index ) { var cells = this._makeCells( elems ); if ( !cells || !cells.length ) { return; } var len = this.cells.length; // default to append index = index === undefined ? len : index; // add cells with document fragment var fragment = getCellsFragment( cells ); // append to slider var isAppend = index == len; if ( isAppend ) { this.slider.appendChild( fragment ); } else { var insertCellElement = this.cells[ index ].element; this.slider.insertBefore( fragment, insertCellElement ); } // add to this.cells if ( index === 0 ) { // prepend, add to start this.cells = cells.concat( this.cells ); } else if ( isAppend ) { // append, add to end this.cells = this.cells.concat( cells ); } else { // insert in this.cells var endCells = this.cells.splice( index, len - index ); this.cells = this.cells.concat( cells ).concat( endCells ); } this._sizeCells( cells ); this.cellChange( index, true ); }; proto.append = function( elems ) { this.insert( elems, this.cells.length ); }; proto.prepend = function( elems ) { this.insert( elems, 0 ); }; /** * Remove cells * @param {Element, Array, NodeList} elems */ proto.remove = function( elems ) { var cells = this.getCells( elems ); if ( !cells || !cells.length ) { return; } var minCellIndex = this.cells.length - 1; // remove cells from collection & DOM cells.forEach( function( cell ) { cell.remove(); var index = this.cells.indexOf( cell ); minCellIndex = Math.min( index, minCellIndex ); utils.removeFrom( this.cells, cell ); }, this ); this.cellChange( minCellIndex, true ); }; /** * logic to be run after a cell's size changes * @param {Element} elem - cell's element */ proto.cellSizeChange = function( elem ) { var cell = this.getCell( elem ); if ( !cell ) { return; } cell.getSize(); var index = this.cells.indexOf( cell ); this.cellChange( index ); }; /** * logic any time a cell is changed: added, removed, or size changed * @param {Integer} changedCellIndex - index of the changed cell, optional */ proto.cellChange = function( changedCellIndex, isPositioningSlider ) { var prevSelectedElem = this.selectedElement; this._positionCells( changedCellIndex ); this._getWrapShiftCells(); this.setGallerySize(); // update selectedIndex // try to maintain position & select previous selected element var cell = this.getCell( prevSelectedElem ); if ( cell ) { this.selectedIndex = this.getCellSlideIndex( cell ); } this.selectedIndex = Math.min( this.slides.length - 1, this.selectedIndex ); this.emitEvent( 'cellChange', [ changedCellIndex ] ); // position slider this.select( this.selectedIndex ); // do not position slider after lazy load if ( isPositioningSlider ) { this.positionSliderAtSelected(); } }; // ----- ----- // return Flickity; })); // lazyload ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/lazyload',[ './flickity', 'fizzy-ui-utils/utils' ], function( Flickity, utils ) { return factory( window, Flickity, utils ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, utils ) { 'use strict'; Flickity.createMethods.push('_createLazyload'); var proto = Flickity.prototype; proto._createLazyload = function() { this.on( 'select', this.lazyLoad ); }; proto.lazyLoad = function() { var lazyLoad = this.options.lazyLoad; if ( !lazyLoad ) { return; } // get adjacent cells, use lazyLoad option for adjacent count var adjCount = typeof lazyLoad == 'number' ? lazyLoad : 0; var cellElems = this.getAdjacentCellElements( adjCount ); // get lazy images in those cells var lazyImages = []; cellElems.forEach( function( cellElem ) { var lazyCellImages = getCellLazyImages( cellElem ); lazyImages = lazyImages.concat( lazyCellImages ); }); // load lazy images lazyImages.forEach( function( img ) { new LazyLoader( img, this ); }, this ); }; function getCellLazyImages( cellElem ) { // check if cell element is lazy image if ( cellElem.nodeName == 'IMG' ) { var lazyloadAttr = cellElem.getAttribute('data-flickity-lazyload'); var srcAttr = cellElem.getAttribute('data-flickity-lazyload-src'); var srcsetAttr = cellElem.getAttribute('data-flickity-lazyload-srcset'); if ( lazyloadAttr || srcAttr || srcsetAttr ) { return [ cellElem ]; } } // select lazy images in cell var lazySelector = 'img[data-flickity-lazyload], ' + 'img[data-flickity-lazyload-src], img[data-flickity-lazyload-srcset]'; var imgs = cellElem.querySelectorAll( lazySelector ); return utils.makeArray( imgs ); } // -------------------------- LazyLoader -------------------------- // /** * class to handle loading images */ function LazyLoader( img, flickity ) { this.img = img; this.flickity = flickity; this.load(); } LazyLoader.prototype.handleEvent = utils.handleEvent; LazyLoader.prototype.load = function() { this.img.addEventListener( 'load', this ); this.img.addEventListener( 'error', this ); // get src & srcset var src = this.img.getAttribute('data-flickity-lazyload') || this.img.getAttribute('data-flickity-lazyload-src'); var srcset = this.img.getAttribute('data-flickity-lazyload-srcset'); // set src & serset this.img.src = src; if ( srcset ) { this.img.setAttribute( 'srcset', srcset ); } // remove attr this.img.removeAttribute('data-flickity-lazyload'); this.img.removeAttribute('data-flickity-lazyload-src'); this.img.removeAttribute('data-flickity-lazyload-srcset'); }; LazyLoader.prototype.onload = function( event ) { this.complete( event, 'flickity-lazyloaded' ); }; LazyLoader.prototype.onerror = function( event ) { this.complete( event, 'flickity-lazyerror' ); }; LazyLoader.prototype.complete = function( event, className ) { // unbind events this.img.removeEventListener( 'load', this ); this.img.removeEventListener( 'error', this ); var cell = this.flickity.getParentCell( this.img ); var cellElem = cell && cell.element; this.flickity.cellSizeChange( cellElem ); this.img.classList.add( className ); this.flickity.dispatchEvent( 'lazyLoad', event, cellElem ); }; // ----- ----- // Flickity.LazyLoader = LazyLoader; return Flickity; })); /*! * Flickity v2.1.1 * Touch, responsive, flickable carousels * * Licensed GPLv3 for open source use * or Flickity Commercial License for commercial use * * https://flickity.metafizzy.co * Copyright 2015-2018 Metafizzy */ ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/index',[ './flickity', './drag', './prev-next-button', './page-dots', './player', './add-remove-cell', './lazyload' ], factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( require('./flickity'), require('./drag'), require('./prev-next-button'), require('./page-dots'), require('./player'), require('./add-remove-cell'), require('./lazyload') ); } })( window, function factory( Flickity ) { /*jshint strict: false*/ return Flickity; }); /*! * Flickity asNavFor v2.0.1 * enable asNavFor for Flickity */ /*jshint browser: true, undef: true, unused: true, strict: true*/ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /*globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity-as-nav-for/as-nav-for',[ 'flickity/js/index', 'fizzy-ui-utils/utils' ], factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( require('flickity'), require('fizzy-ui-utils') ); } else { // browser global window.Flickity = factory( window.Flickity, window.fizzyUIUtils ); } }( window, function factory( Flickity, utils ) { // -------------------------- asNavFor prototype -------------------------- // // Flickity.defaults.asNavFor = null; Flickity.createMethods.push('_createAsNavFor'); var proto = Flickity.prototype; proto._createAsNavFor = function() { this.on( 'activate', this.activateAsNavFor ); this.on( 'deactivate', this.deactivateAsNavFor ); this.on( 'destroy', this.destroyAsNavFor ); var asNavForOption = this.options.asNavFor; if ( !asNavForOption ) { return; } // HACK do async, give time for other flickity to be initalized var _this = this; setTimeout( function initNavCompanion() { _this.setNavCompanion( asNavForOption ); }); }; proto.setNavCompanion = function( elem ) { elem = utils.getQueryElement( elem ); var companion = Flickity.data( elem ); // stop if no companion or companion is self if ( !companion || companion == this ) { return; } this.navCompanion = companion; // companion select var _this = this; this.onNavCompanionSelect = function() { _this.navCompanionSelect(); }; companion.on( 'select', this.onNavCompanionSelect ); // click this.on( 'staticClick', this.onNavStaticClick ); this.navCompanionSelect( true ); }; proto.navCompanionSelect = function( isInstant ) { if ( !this.navCompanion ) { return; } // select slide that matches first cell of slide var selectedCell = this.navCompanion.selectedCells[0]; var firstIndex = this.navCompanion.cells.indexOf( selectedCell ); var lastIndex = firstIndex + this.navCompanion.selectedCells.length - 1; var selectIndex = Math.floor( lerp( firstIndex, lastIndex, this.navCompanion.cellAlign ) ); this.selectCell( selectIndex, false, isInstant ); // set nav selected class this.removeNavSelectedElements(); // stop if companion has more cells than this one if ( selectIndex >= this.cells.length ) { return; } var selectedCells = this.cells.slice( firstIndex, lastIndex + 1 ); this.navSelectedElements = selectedCells.map( function( cell ) { return cell.element; }); this.changeNavSelectedClass('add'); }; function lerp( a, b, t ) { return ( b - a ) * t + a; } proto.changeNavSelectedClass = function( method ) { this.navSelectedElements.forEach( function( navElem ) { navElem.classList[ method ]('is-nav-selected'); }); }; proto.activateAsNavFor = function() { this.navCompanionSelect( true ); }; proto.removeNavSelectedElements = function() { if ( !this.navSelectedElements ) { return; } this.changeNavSelectedClass('remove'); delete this.navSelectedElements; }; proto.onNavStaticClick = function( event, pointer, cellElement, cellIndex ) { if ( typeof cellIndex == 'number' ) { this.navCompanion.selectCell( cellIndex ); } }; proto.deactivateAsNavFor = function() { this.removeNavSelectedElements(); }; proto.destroyAsNavFor = function() { if ( !this.navCompanion ) { return; } this.navCompanion.off( 'select', this.onNavCompanionSelect ); this.off( 'staticClick', this.onNavStaticClick ); delete this.navCompanion; }; // ----- ----- // return Flickity; })); /*! * imagesLoaded v4.1.4 * JavaScript is all like "You images are done yet or what?" * MIT License */ ( function( window, factory ) { 'use strict'; // universal module definition /*global define: false, module: false, require: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'imagesloaded/imagesloaded',[ 'ev-emitter/ev-emitter' ], function( EvEmitter ) { return factory( window, EvEmitter ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('ev-emitter') ); } else { // browser global window.imagesLoaded = factory( window, window.EvEmitter ); } })( typeof window !== 'undefined' ? window : this, // -------------------------- factory -------------------------- // function factory( window, EvEmitter ) { var $ = window.jQuery; var console = window.console; // -------------------------- helpers -------------------------- // // extend objects function extend( a, b ) { for ( var prop in b ) { a[ prop ] = b[ prop ]; } return a; } var arraySlice = Array.prototype.slice; // turn element or nodeList into an array function makeArray( obj ) { if ( Array.isArray( obj ) ) { // use object if already an array return obj; } var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number'; if ( isArrayLike ) { // convert nodeList to array return arraySlice.call( obj ); } // array of single index return [ obj ]; } // -------------------------- imagesLoaded -------------------------- // /** * @param {Array, Element, NodeList, String} elem * @param {Object or Function} options - if function, use as callback * @param {Function} onAlways - callback function */ function ImagesLoaded( elem, options, onAlways ) { // coerce ImagesLoaded() without new, to be new ImagesLoaded() if ( !( this instanceof ImagesLoaded ) ) { return new ImagesLoaded( elem, options, onAlways ); } // use elem as selector string var queryElem = elem; if ( typeof elem == 'string' ) { queryElem = document.querySelectorAll( elem ); } // bail if bad element if ( !queryElem ) { console.error( 'Bad element for imagesLoaded ' + ( queryElem || elem ) ); return; } this.elements = makeArray( queryElem ); this.options = extend( {}, this.options ); // shift arguments if no options set if ( typeof options == 'function' ) { onAlways = options; } else { extend( this.options, options ); } if ( onAlways ) { this.on( 'always', onAlways ); } this.getImages(); if ( $ ) { // add jQuery Deferred object this.jqDeferred = new $.Deferred(); } // HACK check async to allow time to bind listeners setTimeout( this.check.bind( this ) ); } ImagesLoaded.prototype = Object.create( EvEmitter.prototype ); ImagesLoaded.prototype.options = {}; ImagesLoaded.prototype.getImages = function() { this.images = []; // filter & find items if we have an item selector this.elements.forEach( this.addElementImages, this ); }; /** * @param {Node} element */ ImagesLoaded.prototype.addElementImages = function( elem ) { // filter siblings if ( elem.nodeName == 'IMG' ) { this.addImage( elem ); } // get background image on element if ( this.options.background === true ) { this.addElementBackgroundImages( elem ); } // find children // no non-element nodes, #143 var nodeType = elem.nodeType; if ( !nodeType || !elementNodeTypes[ nodeType ] ) { return; } var childImgs = elem.querySelectorAll('img'); // concat childElems to filterFound array for ( var i=0; i < childImgs.length; i++ ) { var img = childImgs[i]; this.addImage( img ); } // get child background images if ( typeof this.options.background == 'string' ) { var children = elem.querySelectorAll( this.options.background ); for ( i=0; i < children.length; i++ ) { var child = children[i]; this.addElementBackgroundImages( child ); } } }; var elementNodeTypes = { 1: true, 9: true, 11: true }; ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) { var style = getComputedStyle( elem ); if ( !style ) { // Firefox returns null if in a hidden iframe https://bugzil.la/548397 return; } // get url inside url("...") var reURL = /url\((['"])?(.*?)\1\)/gi; var matches = reURL.exec( style.backgroundImage ); while ( matches !== null ) { var url = matches && matches[2]; if ( url ) { this.addBackground( url, elem ); } matches = reURL.exec( style.backgroundImage ); } }; /** * @param {Image} img */ ImagesLoaded.prototype.addImage = function( img ) { var loadingImage = new LoadingImage( img ); this.images.push( loadingImage ); }; ImagesLoaded.prototype.addBackground = function( url, elem ) { var background = new Background( url, elem ); this.images.push( background ); }; ImagesLoaded.prototype.check = function() { var _this = this; this.progressedCount = 0; this.hasAnyBroken = false; // complete if no images if ( !this.images.length ) { this.complete(); return; } function onProgress( image, elem, message ) { // HACK - Chrome triggers event before object properties have changed. #83 setTimeout( function() { _this.progress( image, elem, message ); }); } this.images.forEach( function( loadingImage ) { loadingImage.once( 'progress', onProgress ); loadingImage.check(); }); }; ImagesLoaded.prototype.progress = function( image, elem, message ) { this.progressedCount++; this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; // progress event this.emitEvent( 'progress', [ this, image, elem ] ); if ( this.jqDeferred && this.jqDeferred.notify ) { this.jqDeferred.notify( this, image ); } // check if completed if ( this.progressedCount == this.images.length ) { this.complete(); } if ( this.options.debug && console ) { console.log( 'progress: ' + message, image, elem ); } }; ImagesLoaded.prototype.complete = function() { var eventName = this.hasAnyBroken ? 'fail' : 'done'; this.isComplete = true; this.emitEvent( eventName, [ this ] ); this.emitEvent( 'always', [ this ] ); if ( this.jqDeferred ) { var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; this.jqDeferred[ jqMethod ]( this ); } }; // -------------------------- -------------------------- // function LoadingImage( img ) { this.img = img; } LoadingImage.prototype = Object.create( EvEmitter.prototype ); LoadingImage.prototype.check = function() { // If complete is true and browser supports natural sizes, // try to check for image status manually. var isComplete = this.getIsImageComplete(); if ( isComplete ) { // report based on naturalWidth this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); return; } // If none of the checks above matched, simulate loading on detached element. this.proxyImage = new Image(); this.proxyImage.addEventListener( 'load', this ); this.proxyImage.addEventListener( 'error', this ); // bind to image as well for Firefox. #191 this.img.addEventListener( 'load', this ); this.img.addEventListener( 'error', this ); this.proxyImage.src = this.img.src; }; LoadingImage.prototype.getIsImageComplete = function() { // check for non-zero, non-undefined naturalWidth // fixes Safari+InfiniteScroll+Masonry bug infinite-scroll#671 return this.img.complete && this.img.naturalWidth; }; LoadingImage.prototype.confirm = function( isLoaded, message ) { this.isLoaded = isLoaded; this.emitEvent( 'progress', [ this, this.img, message ] ); }; // ----- events ----- // // trigger specified handler for event type LoadingImage.prototype.handleEvent = function( event ) { var method = 'on' + event.type; if ( this[ method ] ) { this[ method ]( event ); } }; LoadingImage.prototype.onload = function() { this.confirm( true, 'onload' ); this.unbindEvents(); }; LoadingImage.prototype.onerror = function() { this.confirm( false, 'onerror' ); this.unbindEvents(); }; LoadingImage.prototype.unbindEvents = function() { this.proxyImage.removeEventListener( 'load', this ); this.proxyImage.removeEventListener( 'error', this ); this.img.removeEventListener( 'load', this ); this.img.removeEventListener( 'error', this ); }; // -------------------------- Background -------------------------- // function Background( url, element ) { this.url = url; this.element = element; this.img = new Image(); } // inherit LoadingImage prototype Background.prototype = Object.create( LoadingImage.prototype ); Background.prototype.check = function() { this.img.addEventListener( 'load', this ); this.img.addEventListener( 'error', this ); this.img.src = this.url; // check if image is already complete var isComplete = this.getIsImageComplete(); if ( isComplete ) { this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); this.unbindEvents(); } }; Background.prototype.unbindEvents = function() { this.img.removeEventListener( 'load', this ); this.img.removeEventListener( 'error', this ); }; Background.prototype.confirm = function( isLoaded, message ) { this.isLoaded = isLoaded; this.emitEvent( 'progress', [ this, this.element, message ] ); }; // -------------------------- jQuery -------------------------- // ImagesLoaded.makeJQueryPlugin = function( jQuery ) { jQuery = jQuery || window.jQuery; if ( !jQuery ) { return; } // set local variable $ = jQuery; // $().imagesLoaded() $.fn.imagesLoaded = function( options, callback ) { var instance = new ImagesLoaded( this, options, callback ); return instance.jqDeferred.promise( $(this) ); }; }; // try making plugin ImagesLoaded.makeJQueryPlugin(); // -------------------------- -------------------------- // return ImagesLoaded; }); /*! * Flickity imagesLoaded v2.0.0 * enables imagesLoaded option for Flickity */ /*jshint browser: true, strict: true, undef: true, unused: true */ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /*globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ 'flickity/js/index', 'imagesloaded/imagesloaded' ], function( Flickity, imagesLoaded ) { return factory( window, Flickity, imagesLoaded ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('flickity'), require('imagesloaded') ); } else { // browser global window.Flickity = factory( window, window.Flickity, window.imagesLoaded ); } }( window, function factory( window, Flickity, imagesLoaded ) { 'use strict'; Flickity.createMethods.push('_createImagesLoaded'); var proto = Flickity.prototype; proto._createImagesLoaded = function() { this.on( 'activate', this.imagesLoaded ); }; proto.imagesLoaded = function() { if ( !this.options.imagesLoaded ) { return; } var _this = this; function onImagesLoadedProgress( instance, image ) { var cell = _this.getParentCell( image.img ); _this.cellSizeChange( cell && cell.element ); if ( !_this.options.freeScroll ) { _this.positionSliderAtSelected(); } } imagesLoaded( this.slider ).on( 'progress', onImagesLoadedProgress ); }; return Flickity; }));

Filter products
Showing: 8895 products
Naomi Boyfriend Jeans NA-KD.COM
USD 14.39USD 71.95
Floral Maxi Top NA-KD.COM
Floral Maxi Top
Free People
60
USD 67.78USD 225.95
Carla Jacket NA-KD.COM
Carla Jacket
Samsoe & Samsoe
707
USD 297.95
Velvet Cut Tie Ankle Boot NA-KD.COM
USD 33.88USD 112.95
Mesh Round Neck Dress NA-KD.COM
3494
USD 15.98USD 39.95
+1
Cold Shoulder Trench Coat NA-KD.COM
USD 106.95
Light Up LS Top NA-KD.COM
381
USD 66.78USD 166.95
Big Collar Teddy Coat NA-KD.COM
2208
USD 95.95
Belted Aviator Jacket NA-KD.COM
6009
USD 118.95
Glittery Flared Trousers NA-KD.COM
USD 23.98USD 59.95
Checked Belted Blazer Dress NA-KD.COM
3971
USD 95.95
Plenty Jeans NA-KD.COM
Plenty Jeans
Dr Denim
1793
USD 47.95
Chainmail Crop Body Chain NA-KD.COM
USD 8.98USD 29.95
Amabel Dress NA-KD.COM
750
USD 35.68USD 118.95
The Prince NA-KD.COM
The Prince
Le Specs
2007
USD 38.38USD 95.95
Blocking Color Denim Jacket NA-KD.COM
USD 83.95
Foiled Smock Midi Dress NA-KD.COM
USD 96.58USD 321.95
LS Draped Jersey Dress NA-KD.COM
1154
USD 35.97USD 59.95
Tied Sleeve Printed Blazer NA-KD.COM
USD 21.58USD 71.95
501 Crop Spectator Sport NA-KD.COM
USD 118.95
Rounded Hem Panel Jeans NA-KD.COM
USD 28.77USD 47.95
Off Shoulder Knitted Sweater NA-KD.COM
USD 35.95
+12
High Waist Shiny Flared Pants NA-KD.COM
USD 14.38USD 47.95
Cropped Folded Knitted Sweater NA-KD.COM
USD 29.36USD 41.95
Classic Belted Coat NA-KD.COM
Classic Belted Coat
Dilara x NA-KD
1885
USD 130.95
Cabrina Shirt NA-KD.COM
128
USD 32.97USD 65.95
Animal Printed Leo Sweater NA-KD.COM
USD 71.95
Mesh Frill Dress NA-KD.COM
250
USD 71.95
+3
Satin Kitten Heel Sock Boots NA-KD.COM
USD 21.58USD 71.95
+2
Satin Suit Jacket NA-KD.COM
Satin Suit Jacket
Iva Nikolina x NA-KD
2324
USD 71.95
Pearl Sleeve Detail Sweater NA-KD.COM
USD 19.18USD 47.95
8895 products


Free worldwide shipping
Express delivery
Safe payment
New items every day
sale
Sign up for newsletter
Be the first with the latest news and take advantage of some great offers!
Don’t worry! You will be able to unsubscribe at any time! Check our Privacy Policy here

Payment methods

Shipping methods

Select country and language
Country
Language

Popular brands at NA-KD
NA-KD.com - The hottest online global marketplace bringing you the best in fashion, accessories and beauty from around the world. We offer our customers new arrivals every day and free worldwide shipping. Stocking over 100 international brands including Aeryne Paris, Amuse Society, Calvin Klein, For Love and Lemons, Ray-Ban, Cheap Monday, Dagmar, Delacy, Just Female, Motel Rocks, Naanaa, Nookie, Quay Australia, Samsoe and Samsoe, Saylor and our own NA-KD brand including Accessories, Basics, Boho, Lingerie, Party, Shoes, Swimwear, Trend and Vintage.
Popular categories at NA-KD
Our buyers have sourced the most cutting edge and fashion forward styles from the brands you love. Find the category you're looking for here: Dresses, Tops, Shirts and Blouses, Jackets, Pants, Jeans, Shorts, Jumpsuits, Skirts, Swimwear, Lingerie, Accessories, Shoes and Beauty and even Books.

v1.0
Copyright 2018 Nakdcom One World AB
    Size guide
    Clothes
    Jeans / Trousers
    Bras
    Shoes
    Please use these size charts to help determine your size.

    Please use these size charts to help determine your size. Please note that these charts are indicative only. Sizes and measurements may vary to actual item. Please email us if you need the fit of a particular item.  

    Artboard 1
    • 1. Bust
      Measure around the widest point over the bust.
    • 2. Waist
      Measure around natural waistline.
    • 3. Hips
      Measure around natural waistline.
    Clothes
    INTEUUKUSFR/ESAUJAPIT(RU)
    XXS324034433638
    XS346236653838/40
    S368438874040
    M38106401094242/44
    L401284212114446
    XL4214104414134648
    XXL4416124616154850
    XXXL4618144818175054
    Body measurements
    EUBust (cm)Waist (cm)Hips (cm)
    3276-8060-6486-90
    3480-8464-6890-94
    3684-8868-7294-98
    3888-9272-7698-102
    4092-9676-80102-106
    4296-10080-84106-110
    44100-10484-88110-114
    46104-10888-92114-118