[
  {
    "path": "Gruntfile.js",
    "content": "/*global module */\nmodule.exports = function( grunt ) {\n  \n  'use strict';\n\n  grunt.initConfig({\n\n    uglify: {\n      options: {\n        mangle: {\n          except: ['Swipe']\n        }\n      },\n      dist: {\n        files: {\n          'build/swipe.min.js': 'swipe.js'\n        }\n      }\n    }\n\n  });\n\n  // build\n  grunt.loadNpmTasks('grunt-contrib-uglify');\n  grunt.registerTask('build', 'uglify');\n  grunt.registerTask('default', 'build');\n};"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2013 Brad Birdsall\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "## Usage\nSwipe only needs to follow a simple pattern. Here is an example:\n\n``` html\n<div id='slider' class='swipe'>\n  <div class='swipe-wrap'>\n    <div></div>\n    <div></div>\n    <div></div>\n  </div>\n</div>\n```\n\nAbove is the initial required structure– a series of elements wrapped in two containers. Place any content you want within the items. The containing div will need to be passed to the Swipe function like so:\n\n``` js\nwindow.mySwipe = Swipe(document.getElementById('slider'));\n```\n\nI always place this at the bottom of the page, externally, to verify the page is ready.\n\nAlso Swipe needs just a few styles added to your stylesheet:\n\n``` css\n.swipe {\n  overflow: hidden;\n  visibility: hidden;\n  position: relative;\n}\n.swipe-wrap {\n  overflow: hidden;\n  position: relative;\n}\n.swipe-wrap > div {\n  float:left;\n  width:100%;\n  position: relative;\n}\n```\n\n## Config Options\n\nSwipe can take an optional second parameter– an object of key/value settings:\n\n- **startSlide** Integer *(default:0)* - index position Swipe should start at\n\n-\t**speed** Integer *(default:300)* - speed of prev and next transitions in milliseconds.\n\n- **auto** Integer - begin with auto slideshow (time in milliseconds between slides)\n\n- **continuous** Boolean *(default:true)* - create an infinite feel with no endpoints\n\n- **disableScroll** Boolean *(default:false)* - stop any touches on this container from scrolling the page\n\n- **stopPropagation** Boolean *(default:false)* - stop event propagation\n \n-\t**callback** Function - runs at slide change.\n\n- **transitionEnd** Function - runs at the end slide transition.\n\n### Example\n\n``` js\n\nwindow.mySwipe = new Swipe(document.getElementById('slider'), {\n  startSlide: 2,\n  speed: 400,\n  auto: 3000,\n  continuous: true,\n  disableScroll: false,\n  stopPropagation: false,\n  callback: function(index, elem) {},\n  transitionEnd: function(index, elem) {}\n});\n\n```\n\n## Swipe API\n\nSwipe exposes a few functions that can be useful for script control of your slider.\n\n`prev()` slide to prev\n\n`next()` slide to next\n\n`getPos()` returns current slide index position\n\n`getNumSlides()` returns the total amount of slides\n\n`slide(index, duration)` slide to set index position (duration: speed of transition in milliseconds)\n\n## Browser Support\nSwipe is now compatible with all browsers, including IE7+. Swipe works best on devices that support CSS transforms and touch, but can be used without these as well. A few helper methods determine touch and CSS transition support and choose the proper animation methods accordingly.\n\n## Who's using Swipe\n- CNN\n- Craigslist\n- Airbnb\n- NHL\n- many more…\n\n## License\nCopyright (c) 2013 Brad Birdsall Licensed under the [The MIT License (MIT)](http://opensource.org/licenses/MIT).\n"
  },
  {
    "path": "component.json",
    "content": "{\n  \"name\": \"Swipe\",\n  \"version\": \"2.0.0\",\n  \"main\": [\"./swipe.js\"],\n  \"author\": \"Brad Birdsall\",\n  \"license\": \"MIT\",\n  \"ignore\": [\n    \"**/*.html\",\n    \"**/*.css\",\n    \"Gruntfile.js\"\n  ]\n}\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE HTML> \n<html>\n<head>\n<title>Swipe 2</title>\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=8\">\n<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0'/> \n<link href='style.css' rel='stylesheet'/>\n<style>\n\n/* Swipe 2 required styles */\n\n.swipe {\n  overflow: hidden;\n  visibility: hidden;\n  position: relative;\n}\n.swipe-wrap {\n  overflow: hidden;\n  position: relative;\n}\n.swipe-wrap > div {\n  float:left;\n  width:100%;\n  position: relative;\n}\n\n/* END required styles */\n\n</style>\n\n</head>\n<body>\n\n<h1>Swipe 2</h1>\n\n\n\n<div id='mySwipe' style='max-width:500px;margin:0 auto' class='swipe'>\n  <div class='swipe-wrap'>\n    <div><b>0</b></div>\n    <div><b>1</b></div>\n    <div><b>2</b></div>\n    <div><b>3</b></div>\n    <div><b>4</b></div>\n    <div><b>5</b></div>\n    <div><b>6</b></div>\n    <div><b>7</b></div>\n    <div><b>8</b></div>\n    <div><b>9</b></div>\n    <div><b>10</b></div>\n    <div><b>11</b></div>\n    <div><b>12</b></div>\n    <div><b>13</b></div>\n    <div><b>14</b></div>\n    <div><b>15</b></div>\n    <div><b>16</b></div>\n    <div><b>17</b></div>\n    <div><b>18</b></div>\n    <div><b>19</b></div>\n    <div><b>20</b></div>\n  </div>\n</div>\n\n\n\n<div style='text-align:center;padding-top:20px;'>\n  \n  <button onclick='mySwipe.prev()'>prev</button> \n  <button onclick='mySwipe.next()'>next</button>\n\n</div>\n\n\n\n\n<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\"></script>\n<script src='swipe.js'></script>\n<script>\n\n// pure JS\nvar elem = document.getElementById('mySwipe');\nwindow.mySwipe = Swipe(elem, {\n  // startSlide: 4,\n  // auto: 3000,\n  // continuous: true,\n  // disableScroll: true,\n  // stopPropagation: true,\n  // callback: function(index, element) {},\n  // transitionEnd: function(index, element) {}\n});\n\n// with jQuery\n// window.mySwipe = $('#mySwipe').Swipe().data('Swipe');\n\n</script>"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"Swipe\",\n  \"version\": \"2.0.0\",\n  \"dependencies\": {},\n  \"devDependencies\": {\n    \"grunt\": \"~0.4.1\",\n    \"grunt-contrib-uglify\": \"~0.2.4\"\n  },\n  \"engines\": {\n    \"node\": \">=0.8.0\"\n  }\n}\n"
  },
  {
    "path": "style.css",
    "content": "html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, del, dfn, em, img, ins, kbd, q, samp, small, strong, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, table, tbody, tfoot, thead, tr, th, td, article, aside, footer, header, nav, section {\n  margin:0;\n  padding:0;\n  border:0;\n  outline:0;\n  font-size:100%;\n  vertical-align:baseline;\n  background:transparent;\n}\nbody {\n  -webkit-text-size-adjust:none;\n  font-family:sans-serif;\n  min-height:416px;\n}\nh1 {\n  font-size:33px;\n  margin:50px 0 15px;\n  text-align:center;\n  color:#212121;\n}\nh2 {\n  font-size:14px;\n  font-weight:bold;\n  color:#3c3c3c;\n  margin:20px 10px 10px;\n}\nsmall {\n  margin:0 10px 30px;\n  display:block;\n  font-size:12px;\n}\na {\n  margin:0 0 0 10px;\n  font-size:12px;\n  color:#3c3c3c;\n}\n\n\nhtml, body {\n  background: #f3f3f3;\n}\n\n#console {\n  font-size: 12px;\n  font-family:\"Inconsolata\", \"Monaco\", \"Consolas\", \"Andale Mono\", \"Bitstream Vera Sans Mono\", \"Courier New\", Courier, monospace;\n  color: #999;\n  line-height: 18px;\n  margin-top: 20px;\n  max-height: 150px;\n  overflow: auto;\n}\n\n#mySwipe div b {\n  display:block;\n  font-weight:bold;\n  color:#14ADE5;\n  font-size:20px;\n  text-align:center;\n  margin:10px;\n  padding:100px 10px;\n  box-shadow: 0 1px #EBEBEB;\n  background: #fff;\n  border-radius: 3px;\n  border: 1px solid;\n  border-color: #E5E5E5 #D3D3D3 #B9C1C6;\n}"
  },
  {
    "path": "swipe.js",
    "content": "/*\n * Swipe 2.0\n *\n * Brad Birdsall\n * Copyright 2013, MIT License\n *\n*/\n\nfunction Swipe(container, options) {\n\n  \"use strict\";\n\n  // utilities\n  var noop = function() {}; // simple no operation function\n  var offloadFn = function(fn) { setTimeout(fn || noop, 0) }; // offload a functions execution\n\n  // check browser capabilities\n  var browser = {\n    addEventListener: !!window.addEventListener,\n    touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,\n    transitions: (function(temp) {\n      var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];\n      for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true;\n      return false;\n    })(document.createElement('swipe'))\n  };\n\n  // quit if no root element\n  if (!container) return;\n  var element = container.children[0];\n  var slides, slidePos, width, length;\n  options = options || {};\n  var index = parseInt(options.startSlide, 10) || 0;\n  var speed = options.speed || 300;\n  options.continuous = options.continuous !== undefined ? options.continuous : true;\n\n  function setup() {\n\n    // cache slides\n    slides = element.children;\n    length = slides.length;\n\n    // set continuous to false if only one slide\n    if (slides.length < 2) options.continuous = false;\n\n    //special case if two slides\n    if (browser.transitions && options.continuous && slides.length < 3) {\n      element.appendChild(slides[0].cloneNode(true));\n      element.appendChild(element.children[1].cloneNode(true));\n      slides = element.children;\n    }\n\n    // create an array to store current positions of each slide\n    slidePos = new Array(slides.length);\n\n    // determine width of each slide\n    width = container.getBoundingClientRect().width || container.offsetWidth;\n\n    element.style.width = (slides.length * width) + 'px';\n\n    // stack elements\n    var pos = slides.length;\n    while(pos--) {\n\n      var slide = slides[pos];\n\n      slide.style.width = width + 'px';\n      slide.setAttribute('data-index', pos);\n\n      if (browser.transitions) {\n        slide.style.left = (pos * -width) + 'px';\n        move(pos, index > pos ? -width : (index < pos ? width : 0), 0);\n      }\n\n    }\n\n    // reposition elements before and after index\n    if (options.continuous && browser.transitions) {\n      move(circle(index-1), -width, 0);\n      move(circle(index+1), width, 0);\n    }\n\n    if (!browser.transitions) element.style.left = (index * -width) + 'px';\n\n    container.style.visibility = 'visible';\n\n  }\n\n  function prev() {\n\n    if (options.continuous) slide(index-1);\n    else if (index) slide(index-1);\n\n  }\n\n  function next() {\n\n    if (options.continuous) slide(index+1);\n    else if (index < slides.length - 1) slide(index+1);\n\n  }\n\n  function circle(index) {\n\n    // a simple positive modulo using slides.length\n    return (slides.length + (index % slides.length)) % slides.length;\n\n  }\n\n  function slide(to, slideSpeed) {\n\n    // do nothing if already on requested slide\n    if (index == to) return;\n\n    if (browser.transitions) {\n\n      var direction = Math.abs(index-to) / (index-to); // 1: backward, -1: forward\n\n      // get the actual position of the slide\n      if (options.continuous) {\n        var natural_direction = direction;\n        direction = -slidePos[circle(to)] / width;\n\n        // if going forward but to < index, use to = slides.length + to\n        // if going backward but to > index, use to = -slides.length + to\n        if (direction !== natural_direction) to =  -direction * slides.length + to;\n\n      }\n\n      var diff = Math.abs(index-to) - 1;\n\n      // move all the slides between index and to in the right direction\n      while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0);\n\n      to = circle(to);\n\n      move(index, width * direction, slideSpeed || speed);\n      move(to, 0, slideSpeed || speed);\n\n      if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place\n\n    } else {\n\n      to = circle(to);\n      animate(index * -width, to * -width, slideSpeed || speed);\n      //no fallback for a circular continuous if the browser does not accept transitions\n    }\n\n    index = to;\n    offloadFn(options.callback && options.callback(index, slides[index]));\n  }\n\n  function move(index, dist, speed) {\n\n    translate(index, dist, speed);\n    slidePos[index] = dist;\n\n  }\n\n  function translate(index, dist, speed) {\n\n    var slide = slides[index];\n    var style = slide && slide.style;\n\n    if (!style) return;\n\n    style.webkitTransitionDuration =\n    style.MozTransitionDuration =\n    style.msTransitionDuration =\n    style.OTransitionDuration =\n    style.transitionDuration = speed + 'ms';\n\n    style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';\n    style.msTransform =\n    style.MozTransform =\n    style.OTransform = 'translateX(' + dist + 'px)';\n\n  }\n\n  function animate(from, to, speed) {\n\n    // if not an animation, just reposition\n    if (!speed) {\n\n      element.style.left = to + 'px';\n      return;\n\n    }\n\n    var start = +new Date;\n\n    var timer = setInterval(function() {\n\n      var timeElap = +new Date - start;\n\n      if (timeElap > speed) {\n\n        element.style.left = to + 'px';\n\n        if (delay) begin();\n\n        options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);\n\n        clearInterval(timer);\n        return;\n\n      }\n\n      element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px';\n\n    }, 4);\n\n  }\n\n  // setup auto slideshow\n  var delay = options.auto || 0;\n  var interval;\n\n  function begin() {\n\n    interval = setTimeout(next, delay);\n\n  }\n\n  function stop() {\n\n    delay = 0;\n    clearTimeout(interval);\n\n  }\n\n\n  // setup initial vars\n  var start = {};\n  var delta = {};\n  var isScrolling;\n\n  // setup event capturing\n  var events = {\n\n    handleEvent: function(event) {\n\n      switch (event.type) {\n        case 'touchstart': this.start(event); break;\n        case 'touchmove': this.move(event); break;\n        case 'touchend': offloadFn(this.end(event)); break;\n        case 'webkitTransitionEnd':\n        case 'msTransitionEnd':\n        case 'oTransitionEnd':\n        case 'otransitionend':\n        case 'transitionend': offloadFn(this.transitionEnd(event)); break;\n        case 'resize': offloadFn(setup); break;\n      }\n\n      if (options.stopPropagation) event.stopPropagation();\n\n    },\n    start: function(event) {\n\n      var touches = event.touches[0];\n\n      // measure start values\n      start = {\n\n        // get initial touch coords\n        x: touches.pageX,\n        y: touches.pageY,\n\n        // store time to determine touch duration\n        time: +new Date\n\n      };\n\n      // used for testing first move event\n      isScrolling = undefined;\n\n      // reset delta and end measurements\n      delta = {};\n\n      // attach touchmove and touchend listeners\n      element.addEventListener('touchmove', this, false);\n      element.addEventListener('touchend', this, false);\n\n    },\n    move: function(event) {\n\n      // ensure swiping with one touch and not pinching\n      if ( event.touches.length > 1 || event.scale && event.scale !== 1) return\n\n      if (options.disableScroll) event.preventDefault();\n\n      var touches = event.touches[0];\n\n      // measure change in x and y\n      delta = {\n        x: touches.pageX - start.x,\n        y: touches.pageY - start.y\n      }\n\n      // determine if scrolling test has run - one time test\n      if ( typeof isScrolling == 'undefined') {\n        isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) );\n      }\n\n      // if user is not trying to scroll vertically\n      if (!isScrolling) {\n\n        // prevent native scrolling\n        event.preventDefault();\n\n        // stop slideshow\n        stop();\n\n        // increase resistance if first or last slide\n        if (options.continuous) { // we don't add resistance at the end\n\n          translate(circle(index-1), delta.x + slidePos[circle(index-1)], 0);\n          translate(index, delta.x + slidePos[index], 0);\n          translate(circle(index+1), delta.x + slidePos[circle(index+1)], 0);\n\n        } else {\n\n          delta.x =\n            delta.x /\n              ( (!index && delta.x > 0               // if first slide and sliding left\n                || index == slides.length - 1        // or if last slide and sliding right\n                && delta.x < 0                       // and if sliding at all\n              ) ?\n              ( Math.abs(delta.x) / width + 1 )      // determine resistance level\n              : 1 );                                 // no resistance if false\n\n          // translate 1:1\n          translate(index-1, delta.x + slidePos[index-1], 0);\n          translate(index, delta.x + slidePos[index], 0);\n          translate(index+1, delta.x + slidePos[index+1], 0);\n        }\n\n      }\n\n    },\n    end: function(event) {\n\n      // measure duration\n      var duration = +new Date - start.time;\n\n      // determine if slide attempt triggers next/prev slide\n      var isValidSlide =\n            Number(duration) < 250               // if slide duration is less than 250ms\n            && Math.abs(delta.x) > 20            // and if slide amt is greater than 20px\n            || Math.abs(delta.x) > width/2;      // or if slide amt is greater than half the width\n\n      // determine if slide attempt is past start and end\n      var isPastBounds =\n            !index && delta.x > 0                            // if first slide and slide amt is greater than 0\n            || index == slides.length - 1 && delta.x < 0;    // or if last slide and slide amt is less than 0\n\n      if (options.continuous) isPastBounds = false;\n\n      // determine direction of swipe (true:right, false:left)\n      var direction = delta.x < 0;\n\n      // if not scrolling vertically\n      if (!isScrolling) {\n\n        if (isValidSlide && !isPastBounds) {\n\n          if (direction) {\n\n            if (options.continuous) { // we need to get the next in this direction in place\n\n              move(circle(index-1), -width, 0);\n              move(circle(index+2), width, 0);\n\n            } else {\n              move(index-1, -width, 0);\n            }\n\n            move(index, slidePos[index]-width, speed);\n            move(circle(index+1), slidePos[circle(index+1)]-width, speed);\n            index = circle(index+1);\n\n          } else {\n            if (options.continuous) { // we need to get the next in this direction in place\n\n              move(circle(index+1), width, 0);\n              move(circle(index-2), -width, 0);\n\n            } else {\n              move(index+1, width, 0);\n            }\n\n            move(index, slidePos[index]+width, speed);\n            move(circle(index-1), slidePos[circle(index-1)]+width, speed);\n            index = circle(index-1);\n\n          }\n\n          options.callback && options.callback(index, slides[index]);\n\n        } else {\n\n          if (options.continuous) {\n\n            move(circle(index-1), -width, speed);\n            move(index, 0, speed);\n            move(circle(index+1), width, speed);\n\n          } else {\n\n            move(index-1, -width, speed);\n            move(index, 0, speed);\n            move(index+1, width, speed);\n          }\n\n        }\n\n      }\n\n      // kill touchmove and touchend event listeners until touchstart called again\n      element.removeEventListener('touchmove', events, false)\n      element.removeEventListener('touchend', events, false)\n\n    },\n    transitionEnd: function(event) {\n\n      if (parseInt(event.target.getAttribute('data-index'), 10) == index) {\n\n        if (delay) begin();\n\n        options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);\n\n      }\n\n    }\n\n  }\n\n  // trigger setup\n  setup();\n\n  // start auto slideshow if applicable\n  if (delay) begin();\n\n\n  // add event listeners\n  if (browser.addEventListener) {\n\n    // set touchstart event on element\n    if (browser.touch) element.addEventListener('touchstart', events, false);\n\n    if (browser.transitions) {\n      element.addEventListener('webkitTransitionEnd', events, false);\n      element.addEventListener('msTransitionEnd', events, false);\n      element.addEventListener('oTransitionEnd', events, false);\n      element.addEventListener('otransitionend', events, false);\n      element.addEventListener('transitionend', events, false);\n    }\n\n    // set resize event on window\n    window.addEventListener('resize', events, false);\n\n  } else {\n\n    window.onresize = function () { setup() }; // to play nice with old IE\n\n  }\n\n  // expose the Swipe API\n  return {\n    setup: function() {\n\n      setup();\n\n    },\n    slide: function(to, speed) {\n\n      // cancel slideshow\n      stop();\n\n      slide(to, speed);\n\n    },\n    prev: function() {\n\n      // cancel slideshow\n      stop();\n\n      prev();\n\n    },\n    next: function() {\n\n      // cancel slideshow\n      stop();\n\n      next();\n\n    },\n    stop: function() {\n\n      // cancel slideshow\n      stop();\n\n    },\n    getPos: function() {\n\n      // return current index position\n      return index;\n\n    },\n    getNumSlides: function() {\n\n      // return total number of slides\n      return length;\n    },\n    kill: function() {\n\n      // cancel slideshow\n      stop();\n\n      // reset element\n      element.style.width = '';\n      element.style.left = '';\n\n      // reset slides\n      var pos = slides.length;\n      while(pos--) {\n\n        var slide = slides[pos];\n        slide.style.width = '';\n        slide.style.left = '';\n\n        if (browser.transitions) translate(pos, 0, 0);\n\n      }\n\n      // removed event listeners\n      if (browser.addEventListener) {\n\n        // remove current event listeners\n        element.removeEventListener('touchstart', events, false);\n        element.removeEventListener('webkitTransitionEnd', events, false);\n        element.removeEventListener('msTransitionEnd', events, false);\n        element.removeEventListener('oTransitionEnd', events, false);\n        element.removeEventListener('otransitionend', events, false);\n        element.removeEventListener('transitionend', events, false);\n        window.removeEventListener('resize', events, false);\n\n      }\n      else {\n\n        window.onresize = null;\n\n      }\n\n    }\n  }\n\n}\n\n\nif ( window.jQuery || window.Zepto ) {\n  (function($) {\n    $.fn.Swipe = function(params) {\n      return this.each(function() {\n        $(this).data('Swipe', new Swipe($(this)[0], params));\n      });\n    }\n  })( window.jQuery || window.Zepto )\n}\n"
  }
]