diff --git a/Gruntfile.js b/Gruntfile.js index b4fe75dd..9b5d86f6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -52,10 +52,13 @@ module.exports = function(grunt) { }, uglify: { + options: { + sourceMap: true + }, minifyjs: { files: { - "dist/js/material.min.js": "scripts/material.js", - "dist/js/ripples.min.js": "scripts/ripples.js" + "dist/js/material.min.js": "dist/js/material.js", + "dist/js/ripples.min.js": "dist/js/ripples.js" } } }, @@ -164,9 +167,9 @@ module.exports = function(grunt) { }); - grunt.registerTask("default", ["less", "autoprefixer", "cssmin", "uglify", "copy"]); + grunt.registerTask("default", ["less", "autoprefixer", "cssmin", "copy", "uglify"]); - grunt.registerTask("scss", ["sass", "autoprefixer", "cssmin", "uglify", "copy"]); + grunt.registerTask("scss", ["sass", "autoprefixer", "cssmin", "copy", "uglify"]); grunt.registerTask("build", function(target) { var buildType = "default"; diff --git a/dist/css/ripples.css b/dist/css/ripples.css index f6595fac..b9cbc21e 100644 --- a/dist/css/ripples.css +++ b/dist/css/ripples.css @@ -19,22 +19,16 @@ margin-top: -10px; border-radius: 100%; background-color: rgba(0, 0, 0, 0.05); - -webkit-transform: scale(1); - -ms-transform: scale(1); - transform: scale(1); - -webkit-transform-origin: 50%; - -ms-transform-origin: 50%; - transform-origin: 50%; + transform: scale(1); + transform-origin: 50%; opacity: 0; pointer-events: none; } .ripple.ripple-on { - -webkit-transition: opacity 0.15s ease-in 0s, -webkit-transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s; - transition: opacity 0.15s ease-in 0s, transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s; - opacity: 1; + transition: opacity 0.15s ease-in 0s, transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s; + opacity: 0.2; } .ripple.ripple-out { - -webkit-transition: opacity 0.1s linear 0s !important; - transition: opacity 0.1s linear 0s !important; + transition: opacity 0.1s linear 0s !important; opacity: 0; } diff --git a/dist/css/ripples.min.css b/dist/css/ripples.min.css index c53a3d61..62e46469 100644 --- a/dist/css/ripples.min.css +++ b/dist/css/ripples.min.css @@ -1 +1 @@ -.withripple{position:relative}.ripple-wrapper{position:absolute;top:0;left:0;z-index:1;width:100%;height:100%;overflow:hidden;border-radius:inherit}.ripple{position:absolute;width:20px;height:20px;margin-left:-10px;margin-top:-10px;border-radius:100%;background-color:rgba(0,0,0,.05);-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);-webkit-transform-origin:50%;-ms-transform-origin:50%;transform-origin:50%;opacity:0;pointer-events:none}.ripple.ripple-on{-webkit-transition:opacity .15s ease-in 0s,-webkit-transform .5s cubic-bezier(0.4,0,.2,1) .1s;transition:opacity .15s ease-in 0s,transform .5s cubic-bezier(0.4,0,.2,1) .1s;opacity:1}.ripple.ripple-out{-webkit-transition:opacity .1s linear 0s!important;transition:opacity .1s linear 0s!important;opacity:0} +.withripple{position:relative}.ripple-wrapper{position:absolute;top:0;left:0;z-index:1;width:100%;height:100%;overflow:hidden;border-radius:inherit}.ripple{position:absolute;width:20px;height:20px;margin-left:-10px;margin-top:-10px;border-radius:100%;background-color:rgba(0,0,0,.05);-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);-webkit-transform-origin:50%;-ms-transform-origin:50%;transform-origin:50%;opacity:0;pointer-events:none}.ripple.ripple-on{-webkit-transition:opacity .15s ease-in 0s,-webkit-transform .5s cubic-bezier(0.4,0,.2,1) .1s;transition:opacity .15s ease-in 0s,transform .5s cubic-bezier(0.4,0,.2,1) .1s;opacity:.2}.ripple.ripple-out{-webkit-transition:opacity .1s linear 0s!important;transition:opacity .1s linear 0s!important;opacity:0} diff --git a/dist/js/material.js b/dist/js/material.js index 6cb3804b..4706667e 100644 --- a/dist/js/material.js +++ b/dist/js/material.js @@ -1,159 +1,170 @@ -/* globals jQuery, ripples */ +/* globals jQuery */ (function($) { - // Selector to select only not already processed elements - $.expr[":"].notmdproc = function(obj){ - if ($(obj).data("mdproc")) { - return false; - } else { - return true; - } - }; - - function _isChar(evt) { - if (typeof evt.which == "undefined") { - return true; - } else if (typeof evt.which == "number" && evt.which > 0) { - return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which != 8; - } - return false; + // Selector to select only not already processed elements + $.expr[":"].notmdproc = function(obj){ + if ($(obj).data("mdproc")) { + return false; + } else { + return true; } + }; - $.material = { - "options": { - "withRipples": [ - ".btn:not(.btn-link)", - ".card-image", - ".navbar a:not(.withoutripple)", - ".dropdown-menu a", - ".nav-tabs a:not(.withoutripple)", - ".withripple" - ].join(","), - "inputElements": "input.form-control, textarea.form-control, select.form-control", - "checkboxElements": ".checkbox > label > input[type=checkbox]", - "radioElements": ".radio > label > input[type=radio]" - }, - "checkbox": function(selector) { - // Add fake-checkbox to material checkboxes - $((selector) ? selector : this.options.checkboxElements) - .filter(":notmdproc") - .data("mdproc", true) - .after(""); - }, - "radio": function(selector) { - // Add fake-radio to material radios - $((selector) ? selector : this.options.radioElements) - .filter(":notmdproc") - .data("mdproc", true) - .after(""); - }, - "input": function(selector) { - $((selector) ? selector : this.options.inputElements) - .filter(":notmdproc") - .data("mdproc", true) - .each( function() { - var $this = $(this); - $this.wrap("
"); - $this.after(""); - if ($this.hasClass("floating-label")) { - var placeholder = $this.attr("placeholder"); - $this.attr("placeholder", null).removeClass("floating-label"); - $this.after("
" + placeholder + "
"); - } - if ($this.val() === null || $this.val() == "undefined" || $this.val() === "") { - $this.addClass("empty"); - } - if ($this.parent().next().is("[type=file]")) { - $this.parent().addClass("fileinput"); - var $input = $this.parent().next().detach(); - $this.after($input); - } - }); + function _isChar(evt) { + if (typeof evt.which == "undefined") { + return true; + } else if (typeof evt.which == "number" && evt.which > 0) { + return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which != 8; + } + return false; + } - $(document) - .on("change", ".checkbox input", function() { $(this).blur(); }) - .on("keydown paste", ".form-control", function(e) { - if(_isChar(e)) { - $(this).removeClass("empty"); - } - }) - .on("keyup change", ".form-control", function() { - var $this = $(this); - if($this.val() === "") { - $this.addClass("empty"); - } else { - $this.removeClass("empty"); - } - }) - .on("focus", ".form-control-wrapper.fileinput", function() { - $(this).find("input").addClass("focus"); - }) - .on("blur", ".form-control-wrapper.fileinput", function() { - $(this).find("input").removeClass("focus"); - }) - .on("change", ".form-control-wrapper.fileinput [type=file]", function() { - var value = ""; - $.each($(this)[0].files, function(i, file) { - console.log(file); - value += file.name + ", "; - }); - value = value.substring(0, value.length - 2); - if (value) { - $(this).prev().removeClass("empty"); - } else { - $(this).prev().addClass("empty"); - } - $(this).prev().val(value); - }); - }, - "ripples": function(selector) { - ripples.init((selector) ? selector : this.options.withRipples); - }, - "init": function() { - this.ripples(); - this.input(); - this.checkbox(); - this.radio(); + $.material = { + "options": { + "withRipples": [ + ".btn:not(.btn-link)", + ".card-image", + ".navbar a:not(.withoutripple)", + ".dropdown-menu a", + ".nav-tabs a:not(.withoutripple)", + ".withripple" + ].join(","), + "inputElements": "input.form-control, textarea.form-control, select.form-control", + "checkboxElements": ".checkbox > label > input[type=checkbox]", + "radioElements": ".radio > label > input[type=radio]" + }, + "checkbox": function(selector) { + // Add fake-checkbox to material checkboxes + $((selector) ? selector : this.options.checkboxElements) + .filter(":notmdproc") + .data("mdproc", true) + .after(""); + }, + "radio": function(selector) { + // Add fake-radio to material radios + $((selector) ? selector : this.options.radioElements) + .filter(":notmdproc") + .data("mdproc", true) + .after(""); + }, + "input": function(selector) { + $((selector) ? selector : this.options.inputElements) + .filter(":notmdproc") + .data("mdproc", true) + .each( function() { + var $this = $(this); + $this.wrap("
"); + $this.after(""); - if (document.arrive) { - document.arrive("input, textarea, select", function() { - $.material.init(); - }); - } - - // Detect autofill - (function() { - // This part of code will detect autofill when the page is loading (username and password inputs for example) - var loading = setInterval(function() { - $("input").each(function() { - if ($(this).val() !== $(this).attr("value")) { - $(this).trigger("change"); - } - }); - }, 100); - // After 10 seconds we are quite sure all the needed inputs are autofilled then we can stop checking them - setTimeout(function() { - clearInterval(loading); - }, 10000); - // Now we just listen on inputs of the focused form (because user can select from the autofill dropdown only when the input has focus) - var focused; - $(document) - .on("focus", "input", function() { - var $inputs = $(this).parents("form").find("input"); - focused = setInterval(function() { - $inputs.each(function() { - if ($(this).val() !== $(this).attr("value")) { - $(this).trigger("change"); - } - }); - }, 100); - }) - .on("blur", "input", function() { - clearInterval(focused); - }); - - })(); + // Add floating label if required + if ($this.hasClass("floating-label")) { + var placeholder = $this.attr("placeholder"); + $this.attr("placeholder", null).removeClass("floating-label"); + $this.after("
" + placeholder + "
"); } - }; + + // Add hint label if required + if ($this.attr("data-hint")) { + $this.after("
" + $this.attr("data-hint") + "
"); + } + + // Set as empty if is empty (damn I must improve this...) + if ($this.val() === null || $this.val() == "undefined" || $this.val() === "") { + $this.addClass("empty"); + } + + // Support for file input + if ($this.parent().next().is("[type=file]")) { + $this.parent().addClass("fileinput"); + var $input = $this.parent().next().detach(); + $this.after($input); + } + }); + + $(document) + .on("change", ".checkbox input[type=checkbox]", function() { $(this).blur(); }) + .on("keydown paste", ".form-control", function(e) { + if(_isChar(e)) { + $(this).removeClass("empty"); + } + }) + .on("keyup change", ".form-control", function() { + var $this = $(this); + if($this.val() === "") { + $this.addClass("empty"); + } else { + $this.removeClass("empty"); + } + }) + .on("focus", ".form-control-wrapper.fileinput", function() { + $(this).find("input").addClass("focus"); + }) + .on("blur", ".form-control-wrapper.fileinput", function() { + $(this).find("input").removeClass("focus"); + }) + .on("change", ".form-control-wrapper.fileinput [type=file]", function() { + var value = ""; + $.each($(this)[0].files, function(i, file) { + console.log(file); + value += file.name + ", "; + }); + value = value.substring(0, value.length - 2); + if (value) { + $(this).prev().removeClass("empty"); + } else { + $(this).prev().addClass("empty"); + } + $(this).prev().val(value); + }); + }, + "ripples": function(selector) { + $.ripples({"target": (selector) ? selector : this.options.withRipples}); + }, + "init": function() { + this.ripples(); + this.input(); + this.checkbox(); + this.radio(); + + if (document.arrive) { + document.arrive("input, textarea, select", function() { + $.material.init(); + }); + } + + // Detect autofill + (function() { + // This part of code will detect autofill when the page is loading (username and password inputs for example) + var loading = setInterval(function() { + $("input[type!=checkbox]").each(function() { + if ($(this).val() && $(this).val() !== $(this).attr("value")) { + $(this).trigger("change"); + } + }); + }, 100); + // After 10 seconds we are quite sure all the needed inputs are autofilled then we can stop checking them + setTimeout(function() { + clearInterval(loading); + }, 10000); + // Now we just listen on inputs of the focused form (because user can select from the autofill dropdown only when the input has focus) + var focused; + $(document) + .on("focus", "input", function() { + var $inputs = $(this).parents("form").find("input"); + focused = setInterval(function() { + $inputs.each(function() { + if ($(this).val() !== $(this).attr("value")) { + $(this).trigger("change"); + } + }); + }, 100); + }) + .on("blur", "input", function() { + clearInterval(focused); + }); + + })(); + } + }; })(jQuery); diff --git a/dist/js/material.min.js b/dist/js/material.min.js index 6d9bb49e..2055294a 100644 --- a/dist/js/material.min.js +++ b/dist/js/material.min.js @@ -1 +1,2 @@ -!function(a){function b(a){return"undefined"==typeof a.which?!0:"number"==typeof a.which&&a.which>0?!a.ctrlKey&&!a.metaKey&&!a.altKey&&8!=a.which:!1}a.expr[":"].notmdproc=function(b){return a(b).data("mdproc")?!1:!0},a.material={options:{withRipples:[".btn:not(.btn-link)",".card-image",".navbar a:not(.withoutripple)",".dropdown-menu a",".nav-tabs a:not(.withoutripple)",".withripple"].join(","),inputElements:"input.form-control, textarea.form-control, select.form-control",checkboxElements:".checkbox > label > input[type=checkbox]",radioElements:".radio > label > input[type=radio]"},checkbox:function(b){a(b?b:this.options.checkboxElements).filter(":notmdproc").data("mdproc",!0).after("")},radio:function(b){a(b?b:this.options.radioElements).filter(":notmdproc").data("mdproc",!0).after("")},input:function(c){a(c?c:this.options.inputElements).filter(":notmdproc").data("mdproc",!0).each(function(){var b=a(this);if(b.wrap("
"),b.after(""),b.hasClass("floating-label")){var c=b.attr("placeholder");b.attr("placeholder",null).removeClass("floating-label"),b.after("
"+c+"
")}if(b.attr("data-hint")&&b.after("
"+b.attr("data-hint")+"
"),(null===b.val()||"undefined"==b.val()||""===b.val())&&b.addClass("empty"),b.parent().next().is("[type=file]")){b.parent().addClass("fileinput");var d=b.parent().next().detach();b.after(d)}}),a(document).on("change",".checkbox input[type=checkbox]",function(){a(this).blur()}).on("keydown paste",".form-control",function(c){b(c)&&a(this).removeClass("empty")}).on("keyup change",".form-control",function(){var b=a(this);""===b.val()?b.addClass("empty"):b.removeClass("empty")}).on("focus",".form-control-wrapper.fileinput",function(){a(this).find("input").addClass("focus")}).on("blur",".form-control-wrapper.fileinput",function(){a(this).find("input").removeClass("focus")}).on("change",".form-control-wrapper.fileinput [type=file]",function(){var b="";a.each(a(this)[0].files,function(a,c){console.log(c),b+=c.name+", "}),b=b.substring(0,b.length-2),b?a(this).prev().removeClass("empty"):a(this).prev().addClass("empty"),a(this).prev().val(b)})},ripples:function(a){ripples.init(a?a:this.options.withRipples)},init:function(){this.ripples(),this.input(),this.checkbox(),this.radio(),document.arrive&&document.arrive("input, textarea, select",function(){a.material.init()}),function(){var b=setInterval(function(){a("input[type!=checkbox]").each(function(){a(this).val()&&a(this).val()!==a(this).attr("value")&&a(this).trigger("change")})},100);setTimeout(function(){clearInterval(b)},1e4);var c;a(document).on("focus","input",function(){var b=a(this).parents("form").find("input");c=setInterval(function(){b.each(function(){a(this).val()!==a(this).attr("value")&&a(this).trigger("change")})},100)}).on("blur","input",function(){clearInterval(c)})}()}}}(jQuery); +!function(a){function b(a){return"undefined"==typeof a.which?!0:"number"==typeof a.which&&a.which>0?!a.ctrlKey&&!a.metaKey&&!a.altKey&&8!=a.which:!1}a.expr[":"].notmdproc=function(b){return a(b).data("mdproc")?!1:!0},a.material={options:{withRipples:[".btn:not(.btn-link)",".card-image",".navbar a:not(.withoutripple)",".dropdown-menu a",".nav-tabs a:not(.withoutripple)",".withripple"].join(","),inputElements:"input.form-control, textarea.form-control, select.form-control",checkboxElements:".checkbox > label > input[type=checkbox]",radioElements:".radio > label > input[type=radio]"},checkbox:function(b){a(b?b:this.options.checkboxElements).filter(":notmdproc").data("mdproc",!0).after("")},radio:function(b){a(b?b:this.options.radioElements).filter(":notmdproc").data("mdproc",!0).after("")},input:function(c){a(c?c:this.options.inputElements).filter(":notmdproc").data("mdproc",!0).each(function(){var b=a(this);if(b.wrap("
"),b.after(""),b.hasClass("floating-label")){var c=b.attr("placeholder");b.attr("placeholder",null).removeClass("floating-label"),b.after("
"+c+"
")}if(b.attr("data-hint")&&b.after("
"+b.attr("data-hint")+"
"),(null===b.val()||"undefined"==b.val()||""===b.val())&&b.addClass("empty"),b.parent().next().is("[type=file]")){b.parent().addClass("fileinput");var d=b.parent().next().detach();b.after(d)}}),a(document).on("change",".checkbox input[type=checkbox]",function(){a(this).blur()}).on("keydown paste",".form-control",function(c){b(c)&&a(this).removeClass("empty")}).on("keyup change",".form-control",function(){var b=a(this);""===b.val()?b.addClass("empty"):b.removeClass("empty")}).on("focus",".form-control-wrapper.fileinput",function(){a(this).find("input").addClass("focus")}).on("blur",".form-control-wrapper.fileinput",function(){a(this).find("input").removeClass("focus")}).on("change",".form-control-wrapper.fileinput [type=file]",function(){var b="";a.each(a(this)[0].files,function(a,c){console.log(c),b+=c.name+", "}),b=b.substring(0,b.length-2),b?a(this).prev().removeClass("empty"):a(this).prev().addClass("empty"),a(this).prev().val(b)})},ripples:function(b){a.ripples({target:b?b:this.options.withRipples})},init:function(){this.ripples(),this.input(),this.checkbox(),this.radio(),document.arrive&&document.arrive("input, textarea, select",function(){a.material.init()}),function(){var b=setInterval(function(){a("input[type!=checkbox]").each(function(){a(this).val()&&a(this).val()!==a(this).attr("value")&&a(this).trigger("change")})},100);setTimeout(function(){clearInterval(b)},1e4);var c;a(document).on("focus","input",function(){var b=a(this).parents("form").find("input");c=setInterval(function(){b.each(function(){a(this).val()!==a(this).attr("value")&&a(this).trigger("change")})},100)}).on("blur","input",function(){clearInterval(c)})}()}}}(jQuery); +//# sourceMappingURL=material.min.js.map diff --git a/dist/js/material.min.js.map b/dist/js/material.min.js.map new file mode 100644 index 00000000..efb628fd --- /dev/null +++ b/dist/js/material.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"material.min.js","sources":["material.js"],"names":["$","_isChar","evt","which","ctrlKey","metaKey","altKey","expr","notmdproc","obj","data","material","options","withRipples","join","inputElements","checkboxElements","radioElements","checkbox","selector","this","filter","after","radio","input","each","$this","wrap","hasClass","placeholder","attr","removeClass","val","addClass","parent","next","is","$input","detach","document","on","blur","e","find","value","files","i","file","console","log","name","substring","length","prev","ripples","target","init","arrive","loading","setInterval","trigger","setTimeout","clearInterval","focused","$inputs","parents","jQuery"],"mappings":"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"} \ No newline at end of file diff --git a/dist/js/ripples.js b/dist/js/ripples.js index 0f10784b..399c4fdf 100644 --- a/dist/js/ripples.js +++ b/dist/js/ripples.js @@ -1,189 +1,105 @@ /* Copyright 2014+, Federico Zivolo, LICENSE at https://github.com/FezVrasta/bootstrap-material-design/blob/master/LICENSE.md */ -/* globals CustomEvent */ -window.ripples = { - done: false, - init : function(withRipple) { - "use strict"; +/* globals jQuery */ - if (this.done) { - return console.log("Ripples.js was already initialzied."); - } +(function($) { + $.ripples = function(options) { - this.done = true; + // Default options + var defaultOptions = { + "target": ".btn:not(.btn-link), .card-image, .navbar a:not(.withoutripple), .nav-tabs a:not(.withoutripple), .withripple" + }; - // Cross browser matches function - function matchesSelector(domElement, selector) { - var matches = domElement.matches || - domElement.matchesSelector || - domElement.webkitMatchesSelector || - domElement.mozMatchesSelector || - domElement.msMatchesSelector || - domElement.oMatchesSelector; - return matches.call(domElement, selector); - } + // Fade out the ripple and then destroy it + function rippleOut(ripple) { - // animations time - var rippleOutTime = 100, - rippleStartTime = 500; + // Unbind events from ripple + ripple.off(); - // Helper to bind events on dynamically created elements - var bind = function(events, selector, callback) { - if (typeof events === "string") { - events = [events]; - } - events.forEach(function(event) { - document.addEventListener(event, function(e) { - var target = (typeof e.detail !== "number") ? e.detail : e.target; + // Start the out animation + ripple.addClass("ripple-out"); - if (matchesSelector(target, selector)) { - callback(e, target); - } - }); - }); - }; - - var rippleStart = function(e, target, callback) { - - // Init variables - var $rippleWrapper = target, - $el = $rippleWrapper.parentNode, - $ripple = document.createElement("div"), - elPos = $el.getBoundingClientRect(), - // Mouse pos in most cases - mousePos = {x: e.clientX - elPos.left, y: ((window.ontouchstart) ? e.clientY - window.scrollY: e.clientY) - elPos.top}, - scale = "scale(" + Math.round($rippleWrapper.offsetWidth / 5) + ")", - rippleEnd = new CustomEvent("rippleEnd", {detail: $ripple}), - _rippleOpacity = 0.3, - refreshElementStyle; - - - // If multitouch is detected or some other black magic suff is happening... - if (e.touches) { - mousePos = {x: e.touches[0].clientX - elPos.left, y: e.touches[0].clientY - elPos.top}; - } - - $ripplecache = $ripple; - - // Set ripple class - $ripple.className = "ripple"; - - // Move ripple to the mouse position - $ripple.setAttribute("style", "left:" + mousePos.x + "px; top:" + mousePos.y + "px;"); - - // Get the clicked target's text color, this will be applied to the ripple as background-color. - var targetColor = window.getComputedStyle($el).color; - - // Convert the rgb color to an rgba color with opacity set to __rippleOpacity__ - if ( targetColor.indexOf("rgba") >= 0 ) { - var alphaPosition = targetColor.lastIndexOf(",") + 1; - targetColor = targetColor.substring(0, alphaPosition) + _rippleOpacity + ")"; - } else { - targetColor = targetColor.replace("rgb", "rgba").replace(")", ", " + _rippleOpacity + ")"); - } - - // Insert new ripple into ripple wrapper - $rippleWrapper.appendChild($ripple); - - // Make sure the ripple has the class applied (ugly hack but it works) - refreshElementStyle = window.getComputedStyle($ripple).opacity; - - // Let other funtions know that this element is animating - $ripple.dataset.animating = 1; - - // Set scale value, background-color and opacity to ripple and animate it - $ripple.className = "ripple ripple-on"; - - // Prepare the style of the ripple - var rippleStyle = [ - $ripple.getAttribute("style"), - "background-color: " + targetColor, - "-ms-transform: " + scale, - "-moz-transform:" + scale, - "-webkit-transform:" + scale, - "transform: " + scale - ]; - - // Apply the style - $ripple.setAttribute("style", rippleStyle.join(";")); - - // This function is called when the animation is finished - setTimeout(function() { - - // Let know to other functions that this element has finished the animation - $ripple.dataset.animating = 0; - document.dispatchEvent(rippleEnd); - if (callback) { - callback(); - } - - }, rippleStartTime); - - }; - - var rippleOut = function($ripple) { - // Clear previous animation - $ripple.className = "ripple ripple-on ripple-out"; - - // Let ripple fade out (with CSS) - setTimeout(function() { - $ripple.remove(); - }, rippleOutTime); - }; - - // Helper, need to know if mouse is up or down - var mouseDown = false; - bind(["mousedown", "touchstart"], "*", function() { - mouseDown = true; - }); - bind(["mouseup", "touchend", "mouseout"], "*", function() { - mouseDown = false; - }); - - // Append ripple wrapper if not exists already - var rippleInit = function(e, target) { - if (target.getElementsByClassName("ripple-wrapper").length === 0) { - target.className += " withripple"; - var $rippleWrapper = document.createElement("div"); - $rippleWrapper.className = "ripple-wrapper"; - target.appendChild($rippleWrapper); - } - }; - - var $ripplecache; - - // Events handler - // init RippleJS and start ripple effect on mousedown - bind(["mouseover"], withRipple, rippleInit); - - // Init if the device is touch screen - bind(["touchstart"], withRipple, rippleInit); - - // start ripple effect on mousedown - bind(["mousedown", "touchstart"], ".ripple-wrapper", function(e, $ripple) { - // Start ripple only on left or middle mouse click and touch click - if (e.which === 0 || e.which === 1 || e.which === 2) { - rippleStart(e, $ripple); - } - }); - - // if animation ends and user is not holding mouse then destroy the ripple - bind("rippleEnd", ".ripple-wrapper .ripple", function(e, $ripple) { - - var $ripples = $ripple.parentNode.getElementsByClassName("ripple"); - - if (!mouseDown || ( $ripples[0] == $ripple && $ripples.length > 1)) { - rippleOut($ripple); - } - }); - - // Destroy ripple when mouse is not holded anymore if the ripple still exists - bind(["mouseup", "touchend", "mouseout"], ".ripple-wrapper", function() { - var $ripple = $ripplecache; - if ($ripple && $ripple.dataset.animating != 1) { - rippleOut($ripple); - } - }); + // This function is called when the transition "out" ends + ripple.on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){ + ripple.remove(); + }); } -}; + + // Apply custom options + options = $.extend(defaultOptions, options); + + + $(document) + .on("mousedown", options.target, function(e) { + // If the ripple wrapper does not exists, create it + if (!$(this).find(".ripple-wrapper").length) { + $(this).append("
"); + } + + var wrapper = $(this).find(".ripple-wrapper"); + + // Get the mouse position relative to the ripple wrapper + var wrapperOffset = wrapper.offset(); + var relX = e.pageX - wrapperOffset.left; + var relY = e.pageY - wrapperOffset.top; + + // Meet the new ripple + var ripple = $("
"); + + // Add to it the ripple class + ripple.addClass("ripple"); + + // Position it in the right place + ripple.css({"left": relX, "top": relY}); + + // Set the background color of the ripple + ripple.css({"background-color": window.getComputedStyle($(this)[0]).color}); + + // Spawn it + wrapper.append(ripple); + + // Make sure the ripple has the styles applied (ugly hack but it works) + (function() { return window.getComputedStyle(ripple[0]).opacity; })(); + + // Set the new size + var size = (Math.max($(this).outerWidth(), $(this).outerHeight()) / ripple.outerWidth()) * 2.5; + + ripple.css({ + "-ms-transform": "scale(" + size + ")", + "-moz-transform": "scale(" + size + ")", + "-webkit-transform": "scale(" + size + ")", + "transform": "scale(" + size + ")" + }); + + // Start the transition + ripple.addClass("ripple-on"); + ripple.data("animating", "on"); + ripple.data("mousedown", "on"); + + // This function is called when the transition "on" ends + setTimeout(function() { + ripple.data("animating", "off"); + if (ripple.data("mousedown") == "off") { + rippleOut(ripple); + } + }, 500); + + // On mouseup or on mouseleave, set the mousedown flag to "off" and try to destroy the ripple + wrapper.on("mouseup mouseleave", function() { + ripple.data("mousedown", "off"); + // If the transition "on" is finished then we can destroy the ripple with transition "out" + if (ripple.data("animating") == "off") { + rippleOut(ripple); + } + }); + + }); + + }; + + $.fn.ripples = function() { + $.ripples({"target": $(this)}); + }; + +})(jQuery); diff --git a/dist/js/ripples.min.js b/dist/js/ripples.min.js index fa37eb6d..d057eccd 100644 --- a/dist/js/ripples.min.js +++ b/dist/js/ripples.min.js @@ -1 +1,2 @@ -window.ripples={done:!1,init:function(a){"use strict";function b(a,b){var c=a.matches||a.matchesSelector||a.webkitMatchesSelector||a.mozMatchesSelector||a.msMatchesSelector||a.oMatchesSelector;return c.call(a,b)}if(this.done)return console.log("Ripples.js was already initialzied.");this.done=!0;var c=100,d=500,e=function(a,c,d){"string"==typeof a&&(a=[a]),a.forEach(function(a){document.addEventListener(a,function(a){var e="number"!=typeof a.detail?a.detail:a.target;b(e,c)&&d(a,e)})})},f=function(a,b,c){var e,f=b,g=f.parentNode,h=document.createElement("div"),j=g.getBoundingClientRect(),k={x:a.clientX-j.left,y:(window.ontouchstart?a.clientY-window.scrollY:a.clientY)-j.top},l="scale("+Math.round(f.offsetWidth/5)+")",m=new CustomEvent("rippleEnd",{detail:h}),n=.3;a.touches&&(k={x:a.touches[0].clientX-j.left,y:a.touches[0].clientY-j.top}),i=h,h.className="ripple",h.setAttribute("style","left:"+k.x+"px; top:"+k.y+"px;");var o=window.getComputedStyle(g).color;if(o.indexOf("rgba")>=0){var p=o.lastIndexOf(",")+1;o=o.substring(0,p)+n+")"}else o=o.replace("rgb","rgba").replace(")",", "+n+")");f.appendChild(h),e=window.getComputedStyle(h).opacity,h.dataset.animating=1,h.className="ripple ripple-on";var q=[h.getAttribute("style"),"background-color: "+o,"-ms-transform: "+l,"-moz-transform:"+l,"-webkit-transform:"+l,"transform: "+l];h.setAttribute("style",q.join(";")),setTimeout(function(){h.dataset.animating=0,document.dispatchEvent(m),c&&c()},d)},g=function(a){a.className="ripple ripple-on ripple-out",setTimeout(function(){a.remove()},c)},h=!1;e(["mousedown","touchstart"],"*",function(){h=!0}),e(["mouseup","touchend","mouseout"],"*",function(){h=!1});var i,j=function(a,b){if(0===b.getElementsByClassName("ripple-wrapper").length){b.className+=" withripple";var c=document.createElement("div");c.className="ripple-wrapper",b.appendChild(c)}};e(["mouseover"],a,j),e(["touchstart"],a,j),e(["mousedown","touchstart"],".ripple-wrapper",function(a,b){(0===a.which||1===a.which||2===a.which)&&f(a,b)}),e("rippleEnd",".ripple-wrapper .ripple",function(a,b){var c=b.parentNode.getElementsByClassName("ripple");(!h||c[0]==b&&c.length>1)&&g(b)}),e(["mouseup","touchend","mouseout"],".ripple-wrapper",function(){var a=i;a&&1!=a.dataset.animating&&g(a)})}}; +!function(a){a.ripples=function(b){function c(a){a.off(),a.addClass("ripple-out"),a.on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd",function(){a.remove()})}var d={target:".btn:not(.btn-link), .card-image, .navbar a:not(.withoutripple), .nav-tabs a:not(.withoutripple), .withripple"};b=a.extend(d,b),a(document).on("mousedown",b.target,function(b){a(this).find(".ripple-wrapper").length||a(this).append("
");var d=a(this).find(".ripple-wrapper"),e=d.offset(),f=b.pageX-e.left,g=b.pageY-e.top,h=a("
");h.addClass("ripple"),h.css({left:f,top:g}),h.css({"background-color":window.getComputedStyle(a(this)[0]).color}),d.append(h),function(){return window.getComputedStyle(h[0]).opacity}();var i=Math.max(a(this).outerWidth(),a(this).outerHeight())/h.outerWidth()*2.5;h.css({"-ms-transform":"scale("+i+")","-moz-transform":"scale("+i+")","-webkit-transform":"scale("+i+")",transform:"scale("+i+")"}),h.addClass("ripple-on"),h.data("animating","on"),h.data("mousedown","on"),setTimeout(function(){h.data("animating","off"),"off"==h.data("mousedown")&&c(h)},500),d.on("mouseup mouseleave",function(){h.data("mousedown","off"),"off"==h.data("animating")&&c(h)})})},a.fn.ripples=function(){a.ripples({target:a(this)})}}(jQuery); +//# sourceMappingURL=ripples.min.js.map diff --git a/dist/js/ripples.min.js.map b/dist/js/ripples.min.js.map new file mode 100644 index 00000000..34744f62 --- /dev/null +++ b/dist/js/ripples.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ripples.min.js","sources":["ripples.js"],"names":["$","ripples","options","rippleOut","ripple","off","addClass","on","remove","defaultOptions","target","extend","document","e","this","find","length","append","wrapper","wrapperOffset","offset","relX","pageX","left","relY","pageY","top","css","background-color","window","getComputedStyle","color","opacity","size","Math","max","outerWidth","outerHeight","-ms-transform","-moz-transform","-webkit-transform","transform","data","setTimeout","fn","jQuery"],"mappings":"CAGA,SAAUA,GACRA,EAAEC,QAAU,SAASC,GASnB,QAASC,GAAUC,GAGjBA,EAAOC,MAGPD,EAAOE,SAAS,cAGhBF,EAAOG,GAAG,mEAAoE,WAC5EH,EAAOI,WAhBX,GAAIC,IACFC,OAAU,gHAqBZR,GAAUF,EAAEW,OAAOF,EAAgBP,GAGnCF,EAAEY,UACDL,GAAG,YAAaL,EAAQQ,OAAQ,SAASG,GAEnCb,EAAEc,MAAMC,KAAK,mBAAmBC,QACnChB,EAAEc,MAAMG,OAAO,mCAGjB,IAAIC,GAAUlB,EAAEc,MAAMC,KAAK,mBAGvBI,EAAgBD,EAAQE,SACxBC,EAAOR,EAAES,MAAQH,EAAcI,KAC/BC,EAAOX,EAAEY,MAAQN,EAAcO,IAG/BtB,EAASJ,EAAE,cAGfI,GAAOE,SAAS,UAGhBF,EAAOuB,KAAKJ,KAAQF,EAAMK,IAAOF,IAGjCpB,EAAOuB,KAAKC,mBAAoBC,OAAOC,iBAAiB9B,EAAEc,MAAM,IAAIiB,QAGpEb,EAAQD,OAAOb,GAGf,WAAc,MAAOyB,QAAOC,iBAAiB1B,EAAO,IAAI4B,UAGxD,IAAIC,GAAQC,KAAKC,IAAInC,EAAEc,MAAMsB,aAAcpC,EAAEc,MAAMuB,eAAiBjC,EAAOgC,aAAgB,GAE3FhC,GAAOuB,KACLW,gBAAiB,SAAWL,EAAO,IACnCM,iBAAkB,SAAWN,EAAO,IACpCO,oBAAqB,SAAWP,EAAO,IACvCQ,UAAa,SAAWR,EAAO,MAIjC7B,EAAOE,SAAS,aAChBF,EAAOsC,KAAK,YAAa,MACzBtC,EAAOsC,KAAK,YAAa,MAGzBC,WAAW,WACTvC,EAAOsC,KAAK,YAAa,OACO,OAA5BtC,EAAOsC,KAAK,cACdvC,EAAUC,IAEX,KAGHc,EAAQX,GAAG,qBAAsB,WAC/BH,EAAOsC,KAAK,YAAa,OAEO,OAA5BtC,EAAOsC,KAAK,cACdvC,EAAUC,QAQlBJ,EAAE4C,GAAG3C,QAAU,WACbD,EAAEC,SAASS,OAAUV,EAAEc,UAGxB+B"} \ No newline at end of file diff --git a/less/ripples.less b/less/ripples.less index 1c3b689f..eb209ec5 100644 --- a/less/ripples.less +++ b/less/ripples.less @@ -26,7 +26,7 @@ } .ripple.ripple-on { transition: opacity 0.15s ease-in 0s, transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s; - opacity: 1; + opacity: 0.2; } .ripple.ripple-out { transition: opacity 0.1s linear 0s !important; diff --git a/scripts/material.js b/scripts/material.js index 30cabe1b..4706667e 100644 --- a/scripts/material.js +++ b/scripts/material.js @@ -1,170 +1,170 @@ -/* globals jQuery, ripples */ +/* globals jQuery */ (function($) { - // Selector to select only not already processed elements - $.expr[":"].notmdproc = function(obj){ - if ($(obj).data("mdproc")) { - return false; - } else { - return true; - } - }; - - function _isChar(evt) { - if (typeof evt.which == "undefined") { - return true; - } else if (typeof evt.which == "number" && evt.which > 0) { - return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which != 8; - } - return false; + // Selector to select only not already processed elements + $.expr[":"].notmdproc = function(obj){ + if ($(obj).data("mdproc")) { + return false; + } else { + return true; } + }; - $.material = { - "options": { - "withRipples": [ - ".btn:not(.btn-link)", - ".card-image", - ".navbar a:not(.withoutripple)", - ".dropdown-menu a", - ".nav-tabs a:not(.withoutripple)", - ".withripple" - ].join(","), - "inputElements": "input.form-control, textarea.form-control, select.form-control", - "checkboxElements": ".checkbox > label > input[type=checkbox]", - "radioElements": ".radio > label > input[type=radio]" - }, - "checkbox": function(selector) { - // Add fake-checkbox to material checkboxes - $((selector) ? selector : this.options.checkboxElements) - .filter(":notmdproc") - .data("mdproc", true) - .after(""); - }, - "radio": function(selector) { - // Add fake-radio to material radios - $((selector) ? selector : this.options.radioElements) - .filter(":notmdproc") - .data("mdproc", true) - .after(""); - }, - "input": function(selector) { - $((selector) ? selector : this.options.inputElements) - .filter(":notmdproc") - .data("mdproc", true) - .each( function() { - var $this = $(this); - $this.wrap("
"); - $this.after(""); + function _isChar(evt) { + if (typeof evt.which == "undefined") { + return true; + } else if (typeof evt.which == "number" && evt.which > 0) { + return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which != 8; + } + return false; + } - // Add floating label if required - if ($this.hasClass("floating-label")) { - var placeholder = $this.attr("placeholder"); - $this.attr("placeholder", null).removeClass("floating-label"); - $this.after("
" + placeholder + "
"); - } + $.material = { + "options": { + "withRipples": [ + ".btn:not(.btn-link)", + ".card-image", + ".navbar a:not(.withoutripple)", + ".dropdown-menu a", + ".nav-tabs a:not(.withoutripple)", + ".withripple" + ].join(","), + "inputElements": "input.form-control, textarea.form-control, select.form-control", + "checkboxElements": ".checkbox > label > input[type=checkbox]", + "radioElements": ".radio > label > input[type=radio]" + }, + "checkbox": function(selector) { + // Add fake-checkbox to material checkboxes + $((selector) ? selector : this.options.checkboxElements) + .filter(":notmdproc") + .data("mdproc", true) + .after(""); + }, + "radio": function(selector) { + // Add fake-radio to material radios + $((selector) ? selector : this.options.radioElements) + .filter(":notmdproc") + .data("mdproc", true) + .after(""); + }, + "input": function(selector) { + $((selector) ? selector : this.options.inputElements) + .filter(":notmdproc") + .data("mdproc", true) + .each( function() { + var $this = $(this); + $this.wrap("
"); + $this.after(""); - // Add hint label if required - if ($this.attr("data-hint")) { - $this.after("
" + $this.attr("data-hint") + "
"); - } - - // Set as empty if is empty (damn I must improve this...) - if ($this.val() === null || $this.val() == "undefined" || $this.val() === "") { - $this.addClass("empty"); - } - - // Support for file input - if ($this.parent().next().is("[type=file]")) { - $this.parent().addClass("fileinput"); - var $input = $this.parent().next().detach(); - $this.after($input); - } - }); - - $(document) - .on("change", ".checkbox input[type=checkbox]", function() { $(this).blur(); }) - .on("keydown paste", ".form-control", function(e) { - if(_isChar(e)) { - $(this).removeClass("empty"); - } - }) - .on("keyup change", ".form-control", function() { - var $this = $(this); - if($this.val() === "") { - $this.addClass("empty"); - } else { - $this.removeClass("empty"); - } - }) - .on("focus", ".form-control-wrapper.fileinput", function() { - $(this).find("input").addClass("focus"); - }) - .on("blur", ".form-control-wrapper.fileinput", function() { - $(this).find("input").removeClass("focus"); - }) - .on("change", ".form-control-wrapper.fileinput [type=file]", function() { - var value = ""; - $.each($(this)[0].files, function(i, file) { - console.log(file); - value += file.name + ", "; - }); - value = value.substring(0, value.length - 2); - if (value) { - $(this).prev().removeClass("empty"); - } else { - $(this).prev().addClass("empty"); - } - $(this).prev().val(value); - }); - }, - "ripples": function(selector) { - ripples.init((selector) ? selector : this.options.withRipples); - }, - "init": function() { - this.ripples(); - this.input(); - this.checkbox(); - this.radio(); - - if (document.arrive) { - document.arrive("input, textarea, select", function() { - $.material.init(); - }); - } - - // Detect autofill - (function() { - // This part of code will detect autofill when the page is loading (username and password inputs for example) - var loading = setInterval(function() { - $("input[type!=checkbox]").each(function() { - if ($(this).val() && $(this).val() !== $(this).attr("value")) { - $(this).trigger("change"); - } - }); - }, 100); - // After 10 seconds we are quite sure all the needed inputs are autofilled then we can stop checking them - setTimeout(function() { - clearInterval(loading); - }, 10000); - // Now we just listen on inputs of the focused form (because user can select from the autofill dropdown only when the input has focus) - var focused; - $(document) - .on("focus", "input", function() { - var $inputs = $(this).parents("form").find("input"); - focused = setInterval(function() { - $inputs.each(function() { - if ($(this).val() !== $(this).attr("value")) { - $(this).trigger("change"); - } - }); - }, 100); - }) - .on("blur", "input", function() { - clearInterval(focused); - }); - - })(); + // Add floating label if required + if ($this.hasClass("floating-label")) { + var placeholder = $this.attr("placeholder"); + $this.attr("placeholder", null).removeClass("floating-label"); + $this.after("
" + placeholder + "
"); } - }; + + // Add hint label if required + if ($this.attr("data-hint")) { + $this.after("
" + $this.attr("data-hint") + "
"); + } + + // Set as empty if is empty (damn I must improve this...) + if ($this.val() === null || $this.val() == "undefined" || $this.val() === "") { + $this.addClass("empty"); + } + + // Support for file input + if ($this.parent().next().is("[type=file]")) { + $this.parent().addClass("fileinput"); + var $input = $this.parent().next().detach(); + $this.after($input); + } + }); + + $(document) + .on("change", ".checkbox input[type=checkbox]", function() { $(this).blur(); }) + .on("keydown paste", ".form-control", function(e) { + if(_isChar(e)) { + $(this).removeClass("empty"); + } + }) + .on("keyup change", ".form-control", function() { + var $this = $(this); + if($this.val() === "") { + $this.addClass("empty"); + } else { + $this.removeClass("empty"); + } + }) + .on("focus", ".form-control-wrapper.fileinput", function() { + $(this).find("input").addClass("focus"); + }) + .on("blur", ".form-control-wrapper.fileinput", function() { + $(this).find("input").removeClass("focus"); + }) + .on("change", ".form-control-wrapper.fileinput [type=file]", function() { + var value = ""; + $.each($(this)[0].files, function(i, file) { + console.log(file); + value += file.name + ", "; + }); + value = value.substring(0, value.length - 2); + if (value) { + $(this).prev().removeClass("empty"); + } else { + $(this).prev().addClass("empty"); + } + $(this).prev().val(value); + }); + }, + "ripples": function(selector) { + $.ripples({"target": (selector) ? selector : this.options.withRipples}); + }, + "init": function() { + this.ripples(); + this.input(); + this.checkbox(); + this.radio(); + + if (document.arrive) { + document.arrive("input, textarea, select", function() { + $.material.init(); + }); + } + + // Detect autofill + (function() { + // This part of code will detect autofill when the page is loading (username and password inputs for example) + var loading = setInterval(function() { + $("input[type!=checkbox]").each(function() { + if ($(this).val() && $(this).val() !== $(this).attr("value")) { + $(this).trigger("change"); + } + }); + }, 100); + // After 10 seconds we are quite sure all the needed inputs are autofilled then we can stop checking them + setTimeout(function() { + clearInterval(loading); + }, 10000); + // Now we just listen on inputs of the focused form (because user can select from the autofill dropdown only when the input has focus) + var focused; + $(document) + .on("focus", "input", function() { + var $inputs = $(this).parents("form").find("input"); + focused = setInterval(function() { + $inputs.each(function() { + if ($(this).val() !== $(this).attr("value")) { + $(this).trigger("change"); + } + }); + }, 100); + }) + .on("blur", "input", function() { + clearInterval(focused); + }); + + })(); + } + }; })(jQuery); diff --git a/scripts/ripples.js b/scripts/ripples.js index 0f10784b..399c4fdf 100644 --- a/scripts/ripples.js +++ b/scripts/ripples.js @@ -1,189 +1,105 @@ /* Copyright 2014+, Federico Zivolo, LICENSE at https://github.com/FezVrasta/bootstrap-material-design/blob/master/LICENSE.md */ -/* globals CustomEvent */ -window.ripples = { - done: false, - init : function(withRipple) { - "use strict"; +/* globals jQuery */ - if (this.done) { - return console.log("Ripples.js was already initialzied."); - } +(function($) { + $.ripples = function(options) { - this.done = true; + // Default options + var defaultOptions = { + "target": ".btn:not(.btn-link), .card-image, .navbar a:not(.withoutripple), .nav-tabs a:not(.withoutripple), .withripple" + }; - // Cross browser matches function - function matchesSelector(domElement, selector) { - var matches = domElement.matches || - domElement.matchesSelector || - domElement.webkitMatchesSelector || - domElement.mozMatchesSelector || - domElement.msMatchesSelector || - domElement.oMatchesSelector; - return matches.call(domElement, selector); - } + // Fade out the ripple and then destroy it + function rippleOut(ripple) { - // animations time - var rippleOutTime = 100, - rippleStartTime = 500; + // Unbind events from ripple + ripple.off(); - // Helper to bind events on dynamically created elements - var bind = function(events, selector, callback) { - if (typeof events === "string") { - events = [events]; - } - events.forEach(function(event) { - document.addEventListener(event, function(e) { - var target = (typeof e.detail !== "number") ? e.detail : e.target; + // Start the out animation + ripple.addClass("ripple-out"); - if (matchesSelector(target, selector)) { - callback(e, target); - } - }); - }); - }; - - var rippleStart = function(e, target, callback) { - - // Init variables - var $rippleWrapper = target, - $el = $rippleWrapper.parentNode, - $ripple = document.createElement("div"), - elPos = $el.getBoundingClientRect(), - // Mouse pos in most cases - mousePos = {x: e.clientX - elPos.left, y: ((window.ontouchstart) ? e.clientY - window.scrollY: e.clientY) - elPos.top}, - scale = "scale(" + Math.round($rippleWrapper.offsetWidth / 5) + ")", - rippleEnd = new CustomEvent("rippleEnd", {detail: $ripple}), - _rippleOpacity = 0.3, - refreshElementStyle; - - - // If multitouch is detected or some other black magic suff is happening... - if (e.touches) { - mousePos = {x: e.touches[0].clientX - elPos.left, y: e.touches[0].clientY - elPos.top}; - } - - $ripplecache = $ripple; - - // Set ripple class - $ripple.className = "ripple"; - - // Move ripple to the mouse position - $ripple.setAttribute("style", "left:" + mousePos.x + "px; top:" + mousePos.y + "px;"); - - // Get the clicked target's text color, this will be applied to the ripple as background-color. - var targetColor = window.getComputedStyle($el).color; - - // Convert the rgb color to an rgba color with opacity set to __rippleOpacity__ - if ( targetColor.indexOf("rgba") >= 0 ) { - var alphaPosition = targetColor.lastIndexOf(",") + 1; - targetColor = targetColor.substring(0, alphaPosition) + _rippleOpacity + ")"; - } else { - targetColor = targetColor.replace("rgb", "rgba").replace(")", ", " + _rippleOpacity + ")"); - } - - // Insert new ripple into ripple wrapper - $rippleWrapper.appendChild($ripple); - - // Make sure the ripple has the class applied (ugly hack but it works) - refreshElementStyle = window.getComputedStyle($ripple).opacity; - - // Let other funtions know that this element is animating - $ripple.dataset.animating = 1; - - // Set scale value, background-color and opacity to ripple and animate it - $ripple.className = "ripple ripple-on"; - - // Prepare the style of the ripple - var rippleStyle = [ - $ripple.getAttribute("style"), - "background-color: " + targetColor, - "-ms-transform: " + scale, - "-moz-transform:" + scale, - "-webkit-transform:" + scale, - "transform: " + scale - ]; - - // Apply the style - $ripple.setAttribute("style", rippleStyle.join(";")); - - // This function is called when the animation is finished - setTimeout(function() { - - // Let know to other functions that this element has finished the animation - $ripple.dataset.animating = 0; - document.dispatchEvent(rippleEnd); - if (callback) { - callback(); - } - - }, rippleStartTime); - - }; - - var rippleOut = function($ripple) { - // Clear previous animation - $ripple.className = "ripple ripple-on ripple-out"; - - // Let ripple fade out (with CSS) - setTimeout(function() { - $ripple.remove(); - }, rippleOutTime); - }; - - // Helper, need to know if mouse is up or down - var mouseDown = false; - bind(["mousedown", "touchstart"], "*", function() { - mouseDown = true; - }); - bind(["mouseup", "touchend", "mouseout"], "*", function() { - mouseDown = false; - }); - - // Append ripple wrapper if not exists already - var rippleInit = function(e, target) { - if (target.getElementsByClassName("ripple-wrapper").length === 0) { - target.className += " withripple"; - var $rippleWrapper = document.createElement("div"); - $rippleWrapper.className = "ripple-wrapper"; - target.appendChild($rippleWrapper); - } - }; - - var $ripplecache; - - // Events handler - // init RippleJS and start ripple effect on mousedown - bind(["mouseover"], withRipple, rippleInit); - - // Init if the device is touch screen - bind(["touchstart"], withRipple, rippleInit); - - // start ripple effect on mousedown - bind(["mousedown", "touchstart"], ".ripple-wrapper", function(e, $ripple) { - // Start ripple only on left or middle mouse click and touch click - if (e.which === 0 || e.which === 1 || e.which === 2) { - rippleStart(e, $ripple); - } - }); - - // if animation ends and user is not holding mouse then destroy the ripple - bind("rippleEnd", ".ripple-wrapper .ripple", function(e, $ripple) { - - var $ripples = $ripple.parentNode.getElementsByClassName("ripple"); - - if (!mouseDown || ( $ripples[0] == $ripple && $ripples.length > 1)) { - rippleOut($ripple); - } - }); - - // Destroy ripple when mouse is not holded anymore if the ripple still exists - bind(["mouseup", "touchend", "mouseout"], ".ripple-wrapper", function() { - var $ripple = $ripplecache; - if ($ripple && $ripple.dataset.animating != 1) { - rippleOut($ripple); - } - }); + // This function is called when the transition "out" ends + ripple.on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){ + ripple.remove(); + }); } -}; + + // Apply custom options + options = $.extend(defaultOptions, options); + + + $(document) + .on("mousedown", options.target, function(e) { + // If the ripple wrapper does not exists, create it + if (!$(this).find(".ripple-wrapper").length) { + $(this).append("
"); + } + + var wrapper = $(this).find(".ripple-wrapper"); + + // Get the mouse position relative to the ripple wrapper + var wrapperOffset = wrapper.offset(); + var relX = e.pageX - wrapperOffset.left; + var relY = e.pageY - wrapperOffset.top; + + // Meet the new ripple + var ripple = $("
"); + + // Add to it the ripple class + ripple.addClass("ripple"); + + // Position it in the right place + ripple.css({"left": relX, "top": relY}); + + // Set the background color of the ripple + ripple.css({"background-color": window.getComputedStyle($(this)[0]).color}); + + // Spawn it + wrapper.append(ripple); + + // Make sure the ripple has the styles applied (ugly hack but it works) + (function() { return window.getComputedStyle(ripple[0]).opacity; })(); + + // Set the new size + var size = (Math.max($(this).outerWidth(), $(this).outerHeight()) / ripple.outerWidth()) * 2.5; + + ripple.css({ + "-ms-transform": "scale(" + size + ")", + "-moz-transform": "scale(" + size + ")", + "-webkit-transform": "scale(" + size + ")", + "transform": "scale(" + size + ")" + }); + + // Start the transition + ripple.addClass("ripple-on"); + ripple.data("animating", "on"); + ripple.data("mousedown", "on"); + + // This function is called when the transition "on" ends + setTimeout(function() { + ripple.data("animating", "off"); + if (ripple.data("mousedown") == "off") { + rippleOut(ripple); + } + }, 500); + + // On mouseup or on mouseleave, set the mousedown flag to "off" and try to destroy the ripple + wrapper.on("mouseup mouseleave", function() { + ripple.data("mousedown", "off"); + // If the transition "on" is finished then we can destroy the ripple with transition "out" + if (ripple.data("animating") == "off") { + rippleOut(ripple); + } + }); + + }); + + }; + + $.fn.ripples = function() { + $.ripples({"target": $(this)}); + }; + +})(jQuery);