|
|
/*! * Waves v0.6.5 * http://fian.my.id/Waves
* * Copyright 2014 Alfiana E. Sibuea and other contributors * Released under the MIT license * https://github.com/fians/Waves/blob/master/LICENSE
*/
; (function (window, factory) { "use strict";
// AMD. Register as an anonymous module. Wrap in function so we have access
// to root via `this`.
if (typeof define === "function" && define.amd) { define([], function () { return factory.apply(window); }); }
// Node. Does not work with strict CommonJS, but only CommonJS-like
// environments that support module.exports, like Node.
else if (typeof exports === "object") { module.exports = factory.call(window); }
// Browser globals.
else { window.Waves = factory.call(window); } })(typeof global === "object" ? global : this, function () { "use strict";
var Waves = Waves || {}; var $$ = document.querySelectorAll.bind(document);
// Find exact position of element
function isWindow(obj) { return obj !== null && obj === obj.window; }
function getWindow(elem) { return isWindow(elem) ? elem : elem.nodeType === 9 && elem.defaultView; }
function offset(elem) { var docElem, win, box = { top: 0, left: 0 }, doc = elem && elem.ownerDocument;
docElem = doc.documentElement;
if (typeof elem.getBoundingClientRect !== typeof undefined) { box = elem.getBoundingClientRect(); } win = getWindow(doc); return { top: box.top + win.pageYOffset - docElem.clientTop, left: box.left + win.pageXOffset - docElem.clientLeft }; }
function convertStyle(obj) { var style = '';
for (var a in obj) { if (obj.hasOwnProperty(a)) { style += (a + ':' + obj[a] + ';'); } }
return style; }
var Effect = { // Effect delay
duration: 750,
show: function (e, element) { // Disable right click
if (e.button === 2) { return false; }
var el = element || this;
// Create ripple
var ripple = document.createElement('div'); ripple.className = 'waves-ripple'; el.appendChild(ripple);
// Get click coordinate and element witdh
var pos = offset(el); var relativeY = (e.pageY - pos.top); var relativeX = (e.pageX - pos.left); var scale = 'scale(' + ((el.clientWidth / 100) * 3) + ')';
// Support for touch devices
if ('touches' in e) { relativeY = (e.touches[0].pageY - pos.top); relativeX = (e.touches[0].pageX - pos.left); }
// Attach data to element
ripple.setAttribute('data-hold', Date.now()); ripple.setAttribute('data-scale', scale); ripple.setAttribute('data-x', relativeX); ripple.setAttribute('data-y', relativeY);
// Set ripple position
var rippleStyle = { 'top': relativeY + 'px', 'left': relativeX + 'px' };
ripple.className = ripple.className + ' waves-notransition'; ripple.setAttribute('style', convertStyle(rippleStyle)); ripple.className = ripple.className.replace('waves-notransition', '');
// Scale the ripple
rippleStyle['-webkit-transform'] = scale; rippleStyle['-moz-transform'] = scale; rippleStyle['-ms-transform'] = scale; rippleStyle['-o-transform'] = scale; rippleStyle.transform = scale; rippleStyle.opacity = '1';
rippleStyle['-webkit-transition-duration'] = Effect.duration + 'ms'; rippleStyle['-moz-transition-duration'] = Effect.duration + 'ms'; rippleStyle['-o-transition-duration'] = Effect.duration + 'ms'; rippleStyle['transition-duration'] = Effect.duration + 'ms';
ripple.setAttribute('style', convertStyle(rippleStyle)); },
hide: function (e) { TouchHandler.touchup(e);
var el = this; var width = el.clientWidth * 1.4;
// Get first ripple
var ripple = null; var ripples = el.getElementsByClassName('waves-ripple'); if (ripples.length > 0) { ripple = ripples[ripples.length - 1]; } else { return false; }
var relativeX = ripple.getAttribute('data-x'); var relativeY = ripple.getAttribute('data-y'); var scale = ripple.getAttribute('data-scale');
// Get delay beetween mousedown and mouse leave
var diff = Date.now() - Number(ripple.getAttribute('data-hold')); var delay = 350 - diff;
if (delay < 0) { delay = 0; }
// Fade out ripple after delay
setTimeout(function () { var style = { 'top': relativeY + 'px', 'left': relativeX + 'px', 'opacity': '0',
// Duration
'-webkit-transition-duration': Effect.duration + 'ms', '-moz-transition-duration': Effect.duration + 'ms', '-o-transition-duration': Effect.duration + 'ms', 'transition-duration': Effect.duration + 'ms', '-webkit-transform': scale, '-moz-transform': scale, '-ms-transform': scale, '-o-transform': scale, 'transform': scale, };
ripple.setAttribute('style', convertStyle(style));
setTimeout(function () { try { el.removeChild(ripple); } catch (e) { return false; } }, Effect.duration); }, delay); },
// Little hack to make <input> can perform waves effect
wrapInput: function (elements) { for (var a = 0; a < elements.length; a++) { var el = elements[a];
if (el.tagName.toLowerCase() === 'input') { var parent = el.parentNode;
// If input already have parent just pass through
if (parent.tagName.toLowerCase() === 'i' && parent.className.indexOf('waves-effect') !== -1) { continue; }
// Put element class and style to the specified parent
var wrapper = document.createElement('i'); wrapper.className = el.className + ' waves-input-wrapper';
var elementStyle = el.getAttribute('style');
if (!elementStyle) { elementStyle = ''; }
wrapper.setAttribute('style', elementStyle);
el.className = 'waves-button-input'; el.removeAttribute('style');
// Put element as child
parent.replaceChild(wrapper, el); wrapper.appendChild(el); } } } };
/** * Disable mousedown event for 500ms during and after touch */ var TouchHandler = { /* uses an integer rather than bool so there's no issues with * needing to clear timeouts if another touch event occurred * within the 500ms. Cannot mouseup between touchstart and * touchend, nor in the 500ms after touchend. */ touches: 0, allowEvent: function (e) { var allow = true;
if (e.type === 'touchstart') { TouchHandler.touches += 1; //push
} else if (e.type === 'touchend' || e.type === 'touchcancel') { setTimeout(function () { if (TouchHandler.touches > 0) { TouchHandler.touches -= 1; //pop after 500ms
} }, 500); } else if (e.type === 'mousedown' && TouchHandler.touches > 0) { allow = false; }
return allow; }, touchup: function (e) { TouchHandler.allowEvent(e); } };
/** * Delegated click handler for .waves-effect element. * returns null when .waves-effect element not in "click tree" */ function getWavesEffectElement(e) { if (TouchHandler.allowEvent(e) === false) { return null; }
var element = null; var target = e.target || e.srcElement;
while (target.parentElement !== null) { if (!(target instanceof SVGElement) && target.className.indexOf('waves-effect') !== -1) { element = target; break; } else if (target.classList.contains('waves-effect')) { element = target; break; } target = target.parentElement; }
return element; }
/** * Bubble the click and show effect if .waves-effect elem was found */ function showEffect(e) { var element = getWavesEffectElement(e);
if (element !== null) { Effect.show(e, element);
if ('ontouchstart' in window) { element.addEventListener('touchend', Effect.hide, false); element.addEventListener('touchcancel', Effect.hide, false); }
element.addEventListener('mouseup', Effect.hide, false); element.addEventListener('mouseleave', Effect.hide, false); } }
Waves.displayEffect = function (options) { options = options || {};
if ('duration' in options) { Effect.duration = options.duration; }
//Wrap input inside <i> tag
Effect.wrapInput($$('.waves-effect'));
if ('ontouchstart' in window) { document.body.addEventListener('touchstart', showEffect, false); }
document.body.addEventListener('mousedown', showEffect, false); };
/** * Attach Waves to an input element (or any element which doesn't * bubble mouseup/mousedown events). * Intended to be used with dynamically loaded forms/inputs, or * where the user doesn't want a delegated click handler. */ Waves.attach = function (element) { //FUTURE: automatically add waves classes and allow users
// to specify them with an options param? Eg. light/classic/button
if (element.tagName.toLowerCase() === 'input') { Effect.wrapInput([element]); element = element.parentElement; }
if ('ontouchstart' in window) { element.addEventListener('touchstart', showEffect, false); }
element.addEventListener('mousedown', showEffect, false); };
return Waves; });
|