overwriting old ratchet css and js in dist
[ratchet] / dist / ratchet.js
1 /**
2  * ==================================
3  * Ratchet v1.0.0
4  * Licensed under The MIT License
5  * http://opensource.org/licenses/MIT
6  * ==================================
7  */
8
9 /* ----------------------------------
10  * POPOVER v1.0.0
11  * Licensed under The MIT License
12  * http://opensource.org/licenses/MIT
13  * ---------------------------------- */
14
15 !function () {
16
17   var popover;
18
19   var findPopovers = function (target) {
20     var i, popovers = document.querySelectorAll('a');
21     for (; target && target !== document; target = target.parentNode) {
22       for (i = popovers.length; i--;) { if (popovers[i] === target) return target; }
23     }
24   };
25
26   var onPopoverHidden = function () {
27     document.body.removeChild(backdrop);
28     popover.style.display = 'none';
29     popover.removeEventListener('webkitTransitionEnd', onPopoverHidden);
30   }
31
32   var backdrop = function () {
33     var element = document.createElement('div');
34
35     element.classList.add('backdrop');
36
37     element.addEventListener('touchend', function () {
38       popover.addEventListener('webkitTransitionEnd', onPopoverHidden);
39       popover.classList.remove('visible');
40     });
41
42     return element;
43   }();
44
45   var getPopover = function (e) {
46     var anchor = findPopovers(e.target);
47
48     if (!anchor || !anchor.hash) return;
49
50     popover = document.querySelector(anchor.hash);
51
52     if (!popover || !popover.classList.contains('popover')) return;
53
54     return popover;
55   }
56
57   window.addEventListener('touchend', function (e) {
58     var popover = getPopover(e);
59
60     if (!popover) return;
61
62     popover.style.display = 'block';
63     popover.offsetHeight;
64     popover.classList.add('visible');
65
66     popover.parentNode.appendChild(backdrop);
67   });
68
69   window.addEventListener('click', function (e) { if (getPopover(e)) e.preventDefault(); });
70
71 }();
72 /* ----------------------------------
73  * PUSH v1.0.0
74  * Licensed under The MIT License
75  * inspired by chris's jquery.pjax.js
76  * http://opensource.org/licenses/MIT
77  * ---------------------------------- */
78
79 !function () {
80
81   var noop = function () {};
82
83
84   // Pushstate cacheing
85   // ==================
86
87   var isScrolling;
88   var maxCacheLength = 20;
89   var cacheMapping   = sessionStorage;
90   var domCache       = {};
91   var transitionMap  = {
92     'slide-in'  : 'slide-out',
93     'slide-out' : 'slide-in',
94     'fade'      : 'fade'
95   };
96   var bars = {
97     bartab             : '.bar-tab',
98     bartitle           : '.bar-title',
99     barfooter          : '.bar-footer',
100     barheadersecondary : '.bar-header-secondary'
101   }
102
103   var cacheReplace = function (data, updates) {
104     PUSH.id = data.id;
105     if (updates) data = getCached(data.id);
106     cacheMapping[data.id] = JSON.stringify(data);
107     window.history.replaceState(data.id, data.title, data.url);
108     domCache[data.id] = document.body.cloneNode(true);
109   };
110
111   var cachePush = function () {
112     var id = PUSH.id;
113
114     var cacheForwardStack = JSON.parse(cacheMapping.cacheForwardStack || '[]');
115     var cacheBackStack    = JSON.parse(cacheMapping.cacheBackStack    || '[]');
116
117     cacheBackStack.push(id);
118
119     while (cacheForwardStack.length)               delete cacheMapping[cacheForwardStack.shift()];
120     while (cacheBackStack.length > maxCacheLength) delete cacheMapping[cacheBackStack.shift()];
121
122     window.history.pushState(null, '', cacheMapping[PUSH.id].url);
123
124     cacheMapping.cacheForwardStack = JSON.stringify(cacheForwardStack);
125     cacheMapping.cacheBackStack    = JSON.stringify(cacheBackStack);
126   };
127
128   var cachePop = function (id, direction) {
129     var forward           = direction == 'forward';
130     var cacheForwardStack = JSON.parse(cacheMapping.cacheForwardStack || '[]');
131     var cacheBackStack    = JSON.parse(cacheMapping.cacheBackStack    || '[]');
132     var pushStack         = forward ? cacheBackStack    : cacheForwardStack;
133     var popStack          = forward ? cacheForwardStack : cacheBackStack;
134
135     if (PUSH.id) pushStack.push(PUSH.id);
136     popStack.pop();
137
138     cacheMapping.cacheForwardStack = JSON.stringify(cacheForwardStack);
139     cacheMapping.cacheBackStack    = JSON.stringify(cacheBackStack);
140   };
141
142   var getCached = function (id) {
143     return JSON.parse(cacheMapping[id] || null) || {};
144   };
145
146   var getTarget = function (e) {
147     var target = findTarget(e.target);
148
149     if (
150       !  target
151       || e.which > 1
152       || e.metaKey
153       || e.ctrlKey
154       || isScrolling
155       || location.protocol !== target.protocol
156       || location.host     !== target.host
157       || !target.hash && /#/.test(target.href)
158       || target.hash && target.href.replace(target.hash, '') === location.href.replace(location.hash, '')
159       || target.getAttribute('data-ignore') == 'push'
160     ) return;
161
162     return target;
163   };
164
165
166   // Main event handlers (touchend, popstate)
167   // ==========================================
168
169   var touchend = function (e) {
170     var target = getTarget(e);
171
172     if (!target) return;
173
174     e.preventDefault();
175
176     PUSH({
177       url        : target.href,
178       hash       : target.hash,
179       timeout    : target.getAttribute('data-timeout'),
180       transition : target.getAttribute('data-transition')
181     });
182   };
183
184   var popstate = function (e) {
185     var key;
186     var barElement;
187     var activeObj;
188     var activeDom;
189     var direction;
190     var transition;
191     var transitionFrom;
192     var transitionFromObj;
193     var id = e.state;
194
195     if (!id || !cacheMapping[id]) return;
196
197     direction = PUSH.id < id ? 'forward' : 'back';
198
199     cachePop(id, direction);
200
201     activeObj = getCached(id);
202     activeDom = domCache[id];
203
204     if (activeObj.title) document.title = activeObj.title;
205
206     if (direction == 'back') {
207       transitionFrom    = JSON.parse(direction == 'back' ? cacheMapping.cacheForwardStack : cacheMapping.cacheBackStack);
208       transitionFromObj = getCached(transitionFrom[transitionFrom.length - 1]);
209     } else {
210       transitionFromObj = activeObj;
211     }
212
213     if (direction == 'back' && !transitionFromObj.id) return PUSH.id = id;
214
215     transition = direction == 'back' ? transitionMap[transitionFromObj.transition] : transitionFromObj.transition;
216
217     if (!activeDom) {
218       return PUSH({
219         id         : activeObj.id,
220         url        : activeObj.url,
221         title      : activeObj.title,
222         timeout    : activeObj.timeout,
223         transition : transition,
224         ignorePush : true
225       });
226     }
227
228     if (transitionFromObj.transition) {
229       activeObj = extendWithDom(activeObj, '.content', activeDom.cloneNode(true));
230       for (key in bars) {
231         barElement = document.querySelector(bars[key])
232         if (activeObj[key]) swapContent(activeObj[key], barElement);
233         else if (barElement) barElement.parentNode.removeChild(barElement);
234       }
235     }
236
237     swapContent(
238       (activeObj.contents || activeDom).cloneNode(true),
239       document.querySelector('.content'),
240       transition
241     );
242
243     PUSH.id = id;
244
245     document.body.offsetHeight; // force reflow to prevent scroll
246   };
247
248
249   // Core PUSH functionality
250   // =======================
251
252   var PUSH = function (options) {
253     var key;
254     var data = {};
255     var xhr  = PUSH.xhr;
256
257     options.container = options.container || options.transition ? document.querySelector('.content') : document.body;
258
259     for (key in bars) {
260       options[key] = options[key] || document.querySelector(bars[key]);
261     }
262
263     if (xhr && xhr.readyState < 4) {
264       xhr.onreadystatechange = noop;
265       xhr.abort()
266     }
267
268     xhr = new XMLHttpRequest();
269     xhr.open('GET', options.url, true);
270     xhr.setRequestHeader('X-PUSH', 'true');
271
272     xhr.onreadystatechange = function () {
273       if (options._timeout) clearTimeout(options._timeout);
274       if (xhr.readyState == 4) xhr.status == 200 ? success(xhr, options) : failure(options.url);
275     };
276
277     if (!PUSH.id) {
278       cacheReplace({
279         id         : +new Date,
280         url        : window.location.href,
281         title      : document.title,
282         timeout    : options.timeout,
283         transition : null
284       });
285     }
286
287     if (options.timeout) {
288       options._timeout = setTimeout(function () {  xhr.abort('timeout'); }, options.timeout);
289     }
290
291     xhr.send();
292
293     if (xhr.readyState && !options.ignorePush) cachePush();
294   };
295
296
297   // Main XHR handlers
298   // =================
299
300   var success = function (xhr, options) {
301     var key;
302     var barElement;
303     var data = parseXHR(xhr, options);
304
305     if (!data.contents) return locationReplace(options.url);
306
307     if (data.title) document.title = data.title;
308
309     if (options.transition) {
310       for (key in bars) {
311         barElement = document.querySelector(bars[key])
312         if (data[key]) swapContent(data[key], barElement);
313         else if (barElement) barElement.parentNode.removeChild(barElement);
314       }
315     }
316
317     swapContent(data.contents, options.container, options.transition, function () {
318       cacheReplace({
319         id         : options.id || +new Date,
320         url        : data.url,
321         title      : data.title,
322         timeout    : options.timeout,
323         transition : options.transition
324       }, options.id);
325       triggerStateChange();
326     });
327
328     if (!options.ignorePush && window._gaq) _gaq.push(['_trackPageview']) // google analytics
329     if (!options.hash) return;
330   };
331
332   var failure = function (url) {
333     throw new Error('Could not get: ' + url)
334   };
335
336
337   // PUSH helpers
338   // ============
339
340   var swapContent = function (swap, container, transition, complete) {
341     var enter;
342     var containerDirection;
343     var swapDirection;
344
345     if (!transition) {
346       if (container) container.innerHTML = swap.innerHTML;
347       else if (swap.classList.contains('content')) document.body.appendChild(swap);
348       else document.body.insertBefore(swap, document.querySelector('.content'));
349     } else {
350       enter  = /in$/.test(transition);
351
352       if (transition == 'fade') {
353         container.classList.add('in');
354         container.classList.add('fade');
355         swap.classList.add('fade');
356       }
357
358       if (/slide/.test(transition)) {
359         swap.classList.add(enter ? 'right' : 'left');
360         swap.classList.add('slide');
361         container.classList.add('slide');
362       }
363
364       container.parentNode.insertBefore(swap, container);
365     }
366
367     if (!transition) complete && complete();
368
369     if (transition == 'fade') {
370       container.offsetWidth; // force reflow
371       container.classList.remove('in');
372       container.addEventListener('webkitTransitionEnd', fadeContainerEnd);
373
374       function fadeContainerEnd() {
375         container.removeEventListener('webkitTransitionEnd', fadeContainerEnd);
376         swap.classList.add('in');
377         swap.addEventListener('webkitTransitionEnd', fadeSwapEnd);
378       }
379       function fadeSwapEnd () {
380         swap.removeEventListener('webkitTransitionEnd', fadeSwapEnd);
381         container.parentNode.removeChild(container);
382         swap.classList.remove('fade');
383         swap.classList.remove('in');
384         complete && complete();
385       }
386     }
387
388     if (/slide/.test(transition)) {
389       container.offsetWidth; // force reflow
390       swapDirection      = enter ? 'right' : 'left'
391       containerDirection = enter ? 'left' : 'right'
392       container.classList.add(containerDirection);
393       swap.classList.remove(swapDirection);
394       swap.addEventListener('webkitTransitionEnd', slideEnd);
395
396       function slideEnd() {
397         swap.removeEventListener('webkitTransitionEnd', slideEnd);
398         swap.classList.remove('slide');
399         swap.classList.remove(swapDirection);
400         container.parentNode.removeChild(container);
401         complete && complete();
402       }
403     }
404   };
405
406   var triggerStateChange = function () {
407     var e = new CustomEvent('push', {
408       detail: { state: getCached(PUSH.id) },
409       bubbles: true,
410       cancelable: true
411     });
412
413     window.dispatchEvent(e);
414   };
415
416   var findTarget = function (target) {
417     var i, toggles = document.querySelectorAll('a');
418     for (; target && target !== document; target = target.parentNode) {
419       for (i = toggles.length; i--;) { if (toggles[i] === target) return target; }
420     }
421   };
422
423   var locationReplace = function (url) {
424     window.history.replaceState(null, '', '#');
425     window.location.replace(url);
426   };
427
428   var parseURL = function (url) {
429     var a = document.createElement('a'); a.href = url; return a;
430   };
431
432   var extendWithDom = function (obj, fragment, dom) {
433     var i;
434     var result    = {};
435
436     for (i in obj) result[i] = obj[i];
437
438     Object.keys(bars).forEach(function (key) {
439       var el = dom.querySelector(bars[key]);
440       if (el) el.parentNode.removeChild(el);
441       result[key] = el;
442     });
443
444     result.contents = dom.querySelector(fragment);
445
446     return result;
447   };
448
449   var parseXHR = function (xhr, options) {
450     var head;
451     var body;
452     var data = {};
453     var responseText = xhr.responseText;
454
455     data.url = options.url;
456
457     if (!responseText) return data;
458
459     if (/<html/i.test(responseText)) {
460       head           = document.createElement('div');
461       body           = document.createElement('div');
462       head.innerHTML = responseText.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]
463       body.innerHTML = responseText.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]
464     } else {
465       head           = body = document.createElement('div');
466       head.innerHTML = responseText;
467     }
468
469     data.title = head.querySelector('title');
470     data.title = data.title && data.title.innerText.trim();
471
472     if (options.transition) data = extendWithDom(data, '.content', body);
473     else data.contents = body;
474
475     return data;
476   };
477
478
479   // Attach PUSH event handlers
480   // ==========================
481
482   window.addEventListener('touchstart', function () { isScrolling = false; });
483   window.addEventListener('touchmove', function () { isScrolling = true; })
484   window.addEventListener('touchend', touchend);
485   window.addEventListener('click', function (e) { if (getTarget(e)) e.preventDefault(); });
486   window.addEventListener('popstate', popstate);
487
488 }();/* ----------------------------------
489  * TABS v1.0.0
490  * Licensed under The MIT License
491  * http://opensource.org/licenses/MIT
492  * ---------------------------------- */
493
494 !function () {
495   var getTarget = function (target) {
496     var i, popovers = document.querySelectorAll('.segmented-controller li a');
497     for (; target && target !== document; target = target.parentNode) {
498       for (i = popovers.length; i--;) { if (popovers[i] === target) return target; }
499     }
500   };
501
502   window.addEventListener("touchend", function (e) {
503     var activeTab;
504     var activeBody;
505     var targetBody;
506     var targetTab;
507     var className     = 'active';
508     var classSelector = '.' + className;
509     var targetAnchor  = getTarget(e.target);
510
511     if (!targetAnchor) return;
512
513     targetTab = targetAnchor.parentNode;
514     activeTab = targetTab.parentNode.querySelector(classSelector);
515
516     if (activeTab) activeTab.classList.remove(className);
517
518     targetTab.classList.add(className);
519
520     if (!targetAnchor.hash) return;
521
522     targetBody = document.querySelector(targetAnchor.hash);
523
524     if (!targetBody) return;
525
526     activeBody = targetBody.parentNode.querySelector(classSelector);
527
528     if (activeBody) activeBody.classList.remove(className);
529
530     targetBody.classList.add(className)
531   });
532
533   window.addEventListener('click', function (e) { if (getTarget(e.target)) e.preventDefault(); });
534 }();/* ----------------------------------
535  * SLIDER v1.0.0
536  * Licensed under The MIT License
537  * Adapted from Brad Birdsall's swipe
538  * http://opensource.org/licenses/MIT
539  * ---------------------------------- */
540
541 !function () {
542
543   var pageX;
544   var pageY;
545   var slider;
546   var deltaX;
547   var deltaY;
548   var offsetX;
549   var lastSlide;
550   var startTime;
551   var resistance;
552   var sliderWidth;
553   var slideNumber;
554   var isScrolling;
555   var scrollableArea;
556
557   var getSlider = function (target) {
558     var i, sliders = document.querySelectorAll('.slider ul');
559     for (; target && target !== document; target = target.parentNode) {
560       for (i = sliders.length; i--;) { if (sliders[i] === target) return target; }
561     }
562   }
563
564   var getScroll = function () {
565     var translate3d = slider.style.webkitTransform.match(/translate3d\(([^,]*)/);
566     return parseInt(translate3d ? translate3d[1] : 0)
567   };
568
569   var setSlideNumber = function (offset) {
570     var round = offset ? (deltaX < 0 ? 'ceil' : 'floor') : 'round';
571     slideNumber = Math[round](getScroll() / ( scrollableArea / slider.children.length) );
572     slideNumber += offset;
573     slideNumber = Math.min(slideNumber, 0);
574     slideNumber = Math.max(-(slider.children.length - 1), slideNumber);
575   }
576
577   var onTouchStart = function (e) {
578     slider = getSlider(e.target);
579
580     if (!slider) return;
581
582     var firstItem  = slider.querySelector('li');
583
584     scrollableArea = firstItem.offsetWidth * slider.children.length;
585     isScrolling    = undefined;
586     sliderWidth    = slider.offsetWidth;
587     resistance     = 1;
588     lastSlide      = -(slider.children.length - 1);
589     startTime      = +new Date;
590     pageX          = e.touches[0].pageX;
591     pageY          = e.touches[0].pageY;
592
593     setSlideNumber(0);
594
595     slider.style['-webkit-transition-duration'] = 0;
596   };
597
598   var onTouchMove = function (e) {
599     if (e.touches.length > 1 || !slider) return; // Exit if a pinch || no slider
600
601     deltaX = e.touches[0].pageX - pageX;
602     deltaY = e.touches[0].pageY - pageY;
603     pageX  = e.touches[0].pageX;
604     pageY  = e.touches[0].pageY;
605
606     if (typeof isScrolling == 'undefined') {
607       isScrolling = Math.abs(deltaY) > Math.abs(deltaX);
608     }
609
610     if (isScrolling) return;
611
612     offsetX = (deltaX / resistance) + getScroll();
613
614     e.preventDefault();
615
616     resistance = slideNumber == 0         && deltaX > 0 ? (pageX / sliderWidth) + 1.25 :
617                  slideNumber == lastSlide && deltaX < 0 ? (Math.abs(pageX) / sliderWidth) + 1.25 : 1;
618
619     slider.style.webkitTransform = 'translate3d(' + offsetX + 'px,0,0)';
620   };
621
622   var onTouchEnd = function (e) {
623     if (!slider || isScrolling) return;
624
625     setSlideNumber(
626       (+new Date) - startTime < 1000 && Math.abs(deltaX) > 15 ? (deltaX < 0 ? -1 : 1) : 0
627     );
628
629     offsetX = slideNumber * sliderWidth;
630
631     slider.style['-webkit-transition-duration'] = '.2s';
632     slider.style.webkitTransform = 'translate3d(' + offsetX + 'px,0,0)';
633
634     e = new CustomEvent('slide', {
635       detail: { slideNumber: Math.abs(slideNumber) },
636       bubbles: true,
637       cancelable: true
638     });
639
640     slider.parentNode.dispatchEvent(e);
641   };
642
643   window.addEventListener('touchstart', onTouchStart);
644   window.addEventListener('touchmove', onTouchMove);
645   window.addEventListener('touchend', onTouchEnd);
646
647 }();
648 /* ----------------------------------
649  * TOGGLE v1.0.0
650  * Licensed under The MIT License
651  * http://opensource.org/licenses/MIT
652  * ---------------------------------- */
653
654 !function () {
655
656   var start     = {};
657   var touchMove = false;
658   var distanceX = false;
659   var toggle    = false;
660
661   var findToggle = function (target) {
662     var i, toggles = document.querySelectorAll('.toggle');
663     for (; target && target !== document; target = target.parentNode) {
664       for (i = toggles.length; i--;) { if (toggles[i] === target) return target; }
665     }
666   }
667
668   window.addEventListener('touchstart', function (e) {
669     e = e.originalEvent || e;
670
671     toggle = findToggle(e.target);
672
673     if (!toggle) return;
674
675     var handle      = toggle.querySelector('.toggle-handle');
676     var toggleWidth = toggle.offsetWidth;
677     var handleWidth = handle.offsetWidth;
678     var offset      = toggle.classList.contains('active') ? toggleWidth - handleWidth : 0;
679
680     start     = { pageX : e.touches[0].pageX - offset, pageY : e.touches[0].pageY };
681     touchMove = false;
682
683     // todo: probably should be moved to the css
684     toggle.style['-webkit-transition-duration'] = 0;
685   });
686
687   window.addEventListener('touchmove', function (e) {
688     e = e.originalEvent || e;
689
690     if (e.touches.length > 1) return; // Exit if a pinch
691
692     if (!toggle) return;
693
694     var handle      = toggle.querySelector('.toggle-handle');
695     var current     = e.touches[0];
696     var toggleWidth = toggle.offsetWidth;
697     var handleWidth = handle.offsetWidth;
698     var offset      = toggleWidth - handleWidth;
699
700     touchMove = true;
701     distanceX = current.pageX - start.pageX;
702
703     if (Math.abs(distanceX) < Math.abs(current.pageY - start.pageY)) return;
704
705     e.preventDefault();
706
707     if (distanceX < 0)      return handle.style.webkitTransform = 'translate3d(0,0,0)';
708     if (distanceX > offset) return handle.style.webkitTransform = 'translate3d(' + offset + 'px,0,0)';
709
710     handle.style.webkitTransform = 'translate3d(' + distanceX + 'px,0,0)';
711
712     toggle.classList[(distanceX > (toggleWidth/2 - handleWidth/2)) ? 'add' : 'remove']('active');
713   });
714
715   window.addEventListener('touchend', function (e) {
716     if (!toggle) return;
717
718     var handle      = toggle.querySelector('.toggle-handle');
719     var toggleWidth = toggle.offsetWidth;
720     var handleWidth = handle.offsetWidth;
721     var offset      = toggleWidth - handleWidth;
722     var slideOn     = (!touchMove && !toggle.classList.contains('active')) || (touchMove && (distanceX > (toggleWidth/2 - handleWidth/2)));
723
724     if (slideOn) handle.style.webkitTransform = 'translate3d(' + offset + 'px,0,0)';
725     else handle.style.webkitTransform = 'translate3d(0,0,0)';
726
727     toggle.classList[slideOn ? 'add' : 'remove']('active');
728
729     e = new CustomEvent('toggle', {
730       detail: { isActive: slideOn },
731       bubbles: true,
732       cancelable: true
733     });
734
735     toggle.dispatchEvent(e);
736
737     touchMove = false;
738     toggle    = false;
739   });
740
741 }();