mirror of https://github.com/twbs/ratchet.git
Build mobile apps with simple HTML, CSS, and JS components.
http://goratchet.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
422 lines
10 KiB
422 lines
10 KiB
// modal |
|
!(function () { |
|
'use strict'; |
|
|
|
var findModals = function (target) { |
|
var i; |
|
var modals = document.querySelectorAll('a'); |
|
|
|
for (; target && target !== document; target = target.parentNode) { |
|
for (i = modals.length; i--;) { |
|
if (modals[i] === target) { |
|
return target; |
|
} |
|
} |
|
} |
|
}; |
|
|
|
var getModal = function (event) { |
|
var modalToggle = findModals(event.target); |
|
if (modalToggle && modalToggle.hash && -1 === modalToggle.hash.indexOf('/')) { |
|
return document.querySelector(modalToggle.hash); |
|
} |
|
}; |
|
|
|
window.addEventListener('touchend', function (event) { |
|
var modal = getModal(event); |
|
if (modal) { |
|
if (modal && modal.classList.contains('modal')) { |
|
modal.classList.toggle('active'); |
|
} |
|
event.preventDefault(); // prevents rewriting url (apps can still use hash values in url) |
|
} |
|
}); |
|
}()); |
|
// popover |
|
!(function () { |
|
'use strict'; |
|
|
|
var popover; |
|
|
|
var findPopovers = function (target) { |
|
var i; |
|
var popovers = document.querySelectorAll('a'); |
|
|
|
for (; target && target !== document; target = target.parentNode) { |
|
for (i = popovers.length; i--;) { |
|
if (popovers[i] === target) { |
|
return target; |
|
} |
|
} |
|
} |
|
}; |
|
|
|
var onPopoverHidden = function () { |
|
popover.style.display = 'none'; |
|
popover.removeEventListener('webkitTransitionEnd', onPopoverHidden); |
|
}; |
|
|
|
// HACK to hide programatically |
|
var hide = function(e){ |
|
e.preventDefault() |
|
e.stopPropagation() |
|
popover.addEventListener('webkitTransitionEnd', onPopoverHidden); |
|
popover.classList.remove('visible'); |
|
popover.parentNode.removeChild(backdrop); |
|
}; |
|
|
|
var backdrop = (function () { |
|
var element = document.createElement('div'); |
|
|
|
element.classList.add('backdrop'); |
|
|
|
element.addEventListener('click', hide, false); |
|
|
|
return element; |
|
}()); |
|
|
|
var getPopover = function (e) { |
|
var anchor = findPopovers(e.target); |
|
|
|
if (!anchor || !anchor.hash || (anchor.hash.indexOf('/') > 0)) { |
|
return; |
|
} |
|
|
|
try { |
|
popover = document.querySelector(anchor.hash); |
|
} catch (error) { |
|
popover = null; |
|
} |
|
|
|
if (popover === null) { |
|
return; |
|
} |
|
|
|
if (!popover || !popover.classList.contains('popover')) { |
|
return; |
|
} |
|
|
|
return popover; |
|
}; |
|
|
|
var showHidePopover = function (e) { |
|
var popover = getPopover(e); |
|
|
|
if (popover) { |
|
// if clicked fast enough, function hide may not call at all |
|
popover.removeEventListener('webkitTransitionEnd', onPopoverHidden); |
|
e.preventDefault() |
|
e.stopPropagation() |
|
}else{ |
|
// HACK, click anyway to close existing popover |
|
popover = document.querySelector('.popover.visible'); |
|
if (popover) hide(e); |
|
return; |
|
} |
|
|
|
popover.style.display = 'block'; |
|
popover.offsetHeight; |
|
popover.classList.add('visible'); |
|
|
|
popover.parentNode.appendChild(backdrop); |
|
}; |
|
|
|
window.addEventListener('click', showHidePopover); |
|
|
|
}()); |
|
// segmented-controllers |
|
!(function () { |
|
'use strict'; |
|
|
|
var getTarget = function (target) { |
|
var i; |
|
var segmentedControls = document.querySelectorAll('.segmented-control .control-item'); |
|
|
|
for (; target && target !== document; target = target.parentNode) { |
|
for (i = segmentedControls.length; i--;) { |
|
if (segmentedControls[i] === target) { |
|
return target; |
|
} |
|
} |
|
} |
|
}; |
|
|
|
window.addEventListener('touchend', function (e) { |
|
var activeTab; |
|
var activeBodies; |
|
var targetBody; |
|
var targetTab = getTarget(e.target); |
|
var className = 'active'; |
|
var classSelector = '.' + className; |
|
|
|
if (!targetTab) { |
|
return; |
|
} |
|
|
|
activeTab = targetTab.parentNode.querySelector(classSelector); |
|
|
|
if (activeTab) { |
|
activeTab.classList.remove(className); |
|
} |
|
|
|
targetTab.classList.add(className); |
|
|
|
if (!targetTab.hash) { |
|
return; |
|
} |
|
|
|
targetBody = document.querySelector(targetTab.hash); |
|
|
|
if (!targetBody) { |
|
return; |
|
} |
|
|
|
activeBodies = targetBody.parentNode.querySelectorAll(classSelector); |
|
|
|
for (var i = 0; i < activeBodies.length; i++) { |
|
activeBodies[i].classList.remove(className); |
|
} |
|
|
|
targetBody.classList.add(className); |
|
}); |
|
|
|
window.addEventListener('click', function (e) { if (getTarget(e.target)) {e.preventDefault();} }); |
|
}()); |
|
// slider |
|
!(function () { |
|
'use strict'; |
|
|
|
var pageX; |
|
var pageY; |
|
var slider; |
|
var deltaX; |
|
var deltaY; |
|
var offsetX; |
|
var lastSlide; |
|
var startTime; |
|
var resistance; |
|
var sliderWidth; |
|
var slideNumber; |
|
var isScrolling; |
|
var scrollableArea; |
|
|
|
var getSlider = function (target) { |
|
var i; |
|
var sliders = document.querySelectorAll('.slider > .slide-group'); |
|
|
|
for (; target && target !== document; target = target.parentNode) { |
|
for (i = sliders.length; i--;) { |
|
if (sliders[i] === target) { |
|
return target; |
|
} |
|
} |
|
} |
|
}; |
|
|
|
var getScroll = function () { |
|
if ('webkitTransform' in slider.style) { |
|
var translate3d = slider.style.webkitTransform.match(/translate3d\(([^,]*)/); |
|
var ret = translate3d ? translate3d[1] : 0; |
|
return parseInt(ret, 10); |
|
} |
|
}; |
|
|
|
var setSlideNumber = function (offset) { |
|
var round = offset ? (deltaX < 0 ? 'ceil' : 'floor') : 'round'; |
|
slideNumber = Math[round](getScroll() / (scrollableArea / slider.children.length)); |
|
slideNumber += offset; |
|
slideNumber = Math.min(slideNumber, 0); |
|
slideNumber = Math.max(-(slider.children.length - 1), slideNumber); |
|
}; |
|
|
|
var onTouchStart = function (e) { |
|
slider = getSlider(e.target); |
|
|
|
if (!slider) { |
|
return; |
|
} |
|
|
|
var firstItem = slider.querySelector('.slide'); |
|
|
|
scrollableArea = firstItem.offsetWidth * slider.children.length; |
|
isScrolling = undefined; |
|
sliderWidth = slider.offsetWidth; |
|
resistance = 1; |
|
lastSlide = -(slider.children.length - 1); |
|
startTime = +new Date(); |
|
pageX = e.touches[0].pageX; |
|
pageY = e.touches[0].pageY; |
|
deltaX = 0; |
|
deltaY = 0; |
|
|
|
setSlideNumber(0); |
|
|
|
slider.style['-webkit-transition-duration'] = 0; |
|
}; |
|
|
|
var onTouchMove = function (e) { |
|
if (e.touches.length > 1 || !slider) { |
|
return; // Exit if a pinch || no slider |
|
} |
|
|
|
deltaX = e.touches[0].pageX - pageX; |
|
deltaY = e.touches[0].pageY - pageY; |
|
pageX = e.touches[0].pageX; |
|
pageY = e.touches[0].pageY; |
|
|
|
if (typeof isScrolling === 'undefined') { |
|
isScrolling = Math.abs(deltaY) > Math.abs(deltaX); |
|
} |
|
|
|
if (isScrolling) { |
|
return; |
|
} |
|
|
|
offsetX = (deltaX / resistance) + getScroll(); |
|
|
|
e.preventDefault(); |
|
|
|
resistance = slideNumber === 0 && deltaX > 0 ? (pageX / sliderWidth) + 1.25 : |
|
slideNumber === lastSlide && deltaX < 0 ? (Math.abs(pageX) / sliderWidth) + 1.25 : 1; |
|
|
|
slider.style.webkitTransform = 'translate3d(' + offsetX + 'px,0,0)'; |
|
}; |
|
|
|
var onTouchEnd = function (e) { |
|
if (!slider || isScrolling) { |
|
return; |
|
} |
|
|
|
setSlideNumber( |
|
(+new Date()) - startTime < 1000 && Math.abs(deltaX) > 15 ? (deltaX < 0 ? -1 : 1) : 0 |
|
); |
|
|
|
offsetX = slideNumber * sliderWidth; |
|
|
|
slider.style['-webkit-transition-duration'] = '.2s'; |
|
slider.style.webkitTransform = 'translate3d(' + offsetX + 'px,0,0)'; |
|
|
|
e = new CustomEvent('slide', { |
|
detail: { slideNumber: Math.abs(slideNumber) }, |
|
bubbles: true, |
|
cancelable: true |
|
}); |
|
|
|
slider.parentNode.dispatchEvent(e); |
|
}; |
|
|
|
window.addEventListener('touchstart', onTouchStart); |
|
window.addEventListener('touchmove', onTouchMove); |
|
window.addEventListener('touchend', onTouchEnd); |
|
|
|
}()); |
|
// toggle |
|
!(function () { |
|
'use strict'; |
|
|
|
var start = {}; |
|
var touchMove = false; |
|
var distanceX = false; |
|
var toggle = false; |
|
|
|
var findToggle = function (target) { |
|
var i; |
|
var toggles = document.querySelectorAll('.toggle'); |
|
|
|
for (; target && target !== document; target = target.parentNode) { |
|
for (i = toggles.length; i--;) { |
|
if (toggles[i] === target) { |
|
return target; |
|
} |
|
} |
|
} |
|
}; |
|
|
|
window.addEventListener('touchstart', function (e) { |
|
e = e.originalEvent || e; |
|
|
|
toggle = findToggle(e.target); |
|
|
|
if (!toggle) { |
|
return; |
|
} |
|
|
|
var handle = toggle.querySelector('.toggle-handle'); |
|
var toggleWidth = toggle.clientWidth; |
|
var handleWidth = handle.clientWidth; |
|
var offset = toggle.classList.contains('active') ? (toggleWidth - handleWidth) : 0; |
|
|
|
start = { pageX : e.touches[0].pageX - offset, pageY : e.touches[0].pageY }; |
|
touchMove = false; |
|
}); |
|
|
|
window.addEventListener('touchmove', function (e) { |
|
e = e.originalEvent || e; |
|
|
|
if (e.touches.length > 1) { |
|
return; // Exit if a pinch |
|
} |
|
|
|
if (!toggle) { |
|
return; |
|
} |
|
|
|
var handle = toggle.querySelector('.toggle-handle'); |
|
var current = e.touches[0]; |
|
var toggleWidth = toggle.clientWidth; |
|
var handleWidth = handle.clientWidth; |
|
var offset = toggleWidth - handleWidth; |
|
|
|
touchMove = true; |
|
distanceX = current.pageX - start.pageX; |
|
|
|
if (Math.abs(distanceX) < Math.abs(current.pageY - start.pageY)) { |
|
return; |
|
} |
|
|
|
e.preventDefault(); |
|
|
|
if (distanceX < 0) { |
|
return (handle.style.webkitTransform = 'translate3d(0,0,0)'); |
|
} |
|
if (distanceX > offset) { |
|
return (handle.style.webkitTransform = 'translate3d(' + offset + 'px,0,0)'); |
|
} |
|
|
|
handle.style.webkitTransform = 'translate3d(' + distanceX + 'px,0,0)'; |
|
|
|
toggle.classList[(distanceX > (toggleWidth / 2 - handleWidth / 2)) ? 'add' : 'remove']('active'); |
|
}); |
|
|
|
window.addEventListener('touchend', function (e) { |
|
if (!toggle) { |
|
return; |
|
} |
|
|
|
var handle = toggle.querySelector('.toggle-handle'); |
|
var toggleWidth = toggle.clientWidth; |
|
var handleWidth = handle.clientWidth; |
|
var offset = (toggleWidth - handleWidth); |
|
var slideOn = (!touchMove && !toggle.classList.contains('active')) || (touchMove && (distanceX > (toggleWidth / 2 - handleWidth / 2))); |
|
|
|
if (slideOn) { |
|
handle.style.webkitTransform = 'translate3d(' + offset + 'px,0,0)'; |
|
} else { |
|
handle.style.webkitTransform = 'translate3d(0,0,0)'; |
|
} |
|
|
|
toggle.classList[slideOn ? 'add' : 'remove']('active'); |
|
|
|
e = new CustomEvent('toggle', { |
|
detail: { isActive: slideOn }, |
|
bubbles: true, |
|
cancelable: true |
|
}); |
|
|
|
toggle.dispatchEvent(e); |
|
|
|
touchMove = false; |
|
toggle = false; |
|
}); |
|
|
|
}());
|
|
|