// SweetAlert // 2014 (c) - Tristan Edwards // github.com/t4t5/sweetalert (function(window, document) { var modalClass = '.sweet-alert', overlayClass = '.sweet-overlay', alertTypes = ['error', 'warning', 'info', 'success'], defaultParams = { title: '', text: '', type: null, allowOutsideClick: false, showCancelButton: false, showConfirmButton: true, closeOnConfirm: true, closeOnCancel: true, confirmButtonText: 'OK', confirmButtonClass: 'btn-primary', cancelButtonText: 'Cancel', cancelButtonClass: 'btn-default', containerClass: '', titleClass: '', textClass: '', imageUrl: null, imageSize: null, timer: null }; /* * Manipulate DOM */ var getModal = function() { return document.querySelector(modalClass); }, getOverlay = function() { return document.querySelector(overlayClass); }, hasClass = function(elem, className) { return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' '); }, addClass = function(elem, className) { if (className && !hasClass(elem, className)) { elem.className += ' ' + className; } }, removeClass = function(elem, className) { var newClass = ' ' + elem.className.replace(/[\t\r\n]/g, ' ') + ' '; if (hasClass(elem, className)) { while (newClass.indexOf(' ' + className + ' ') >= 0) { newClass = newClass.replace(' ' + className + ' ', ' '); } elem.className = newClass.replace(/^\s+|\s+$/g, ''); } }, escapeHtml = function(str) { var div = document.createElement('div'); div.appendChild(document.createTextNode(str)); return div.innerHTML; }, _show = function(elem) { elem.style.opacity = ''; elem.style.display = 'block'; }, show = function(elems) { if (elems && !elems.length) { return _show(elems); } for (var i = 0; i < elems.length; ++i) { _show(elems[i]); } }, _hide = function(elem) { elem.style.opacity = ''; elem.style.display = 'none'; }, hide = function(elems) { if (elems && !elems.length) { return _hide(elems); } for (var i = 0; i < elems.length; ++i) { _hide(elems[i]); } }, isDescendant = function(parent, child) { var node = child.parentNode; while (node !== null) { if (node === parent) { return true; } node = node.parentNode; } return false; }, getTopMargin = function(elem) { elem.style.left = '-9999px'; elem.style.display = 'block'; var height = elem.clientHeight; var padding = parseInt(getComputedStyle(elem).getPropertyValue('padding'), 10); elem.style.left = ''; elem.style.display = 'none'; return ('-' + parseInt(height / 2 + padding) + 'px'); }, fadeIn = function(elem, interval) { if(+elem.style.opacity < 1) { interval = interval || 16; elem.style.opacity = 0; elem.style.display = 'block'; var last = +new Date(); var tick = function() { elem.style.opacity = +elem.style.opacity + (new Date() - last) / 100; last = +new Date(); if (+elem.style.opacity < 1) { setTimeout(tick, interval); } }; tick(); } }, fadeOut = function(elem, interval) { interval = interval || 16; elem.style.opacity = 1; var last = +new Date(); var tick = function() { elem.style.opacity = +elem.style.opacity - (new Date() - last) / 100; last = +new Date(); if (+elem.style.opacity > 0) { setTimeout(tick, interval); } else { elem.style.display = 'none'; } }; tick(); }, fireClick = function(node) { // Taken from http://www.nonobtrusive.com/2011/11/29/programatically-fire-crossbrowser-click-event-with-javascript/ // Then fixed for today's Chrome browser. if (MouseEvent) { // Up-to-date approach var mevt = new MouseEvent('click', { view: window, bubbles: false, cancelable: true }); node.dispatchEvent(mevt); } else if ( document.createEvent ) { // Fallback var evt = document.createEvent('MouseEvents'); evt.initEvent('click', false, false); node.dispatchEvent(evt); } else if( document.createEventObject ) { node.fireEvent('onclick') ; } else if (typeof node.onclick === 'function' ) { node.onclick(); } }, stopEventPropagation = function(e) { // In particular, make sure the space bar doesn't scroll the main window. if (typeof e.stopPropagation === 'function') { e.stopPropagation(); e.preventDefault(); } else if (window.event && window.event.hasOwnProperty('cancelBubble')) { window.event.cancelBubble = true; } }; // Remember state in cases where opening and handling a modal will fiddle with it. var previousActiveElement, previousDocumentClick, previousWindowKeyDown, lastFocusedButton; /* * Add modal + overlay to DOM */ window.sweetAlertInitialize = function() { var sweetHTML = '

Title

Text

', sweetWrap = document.createElement('div'); sweetWrap.innerHTML = sweetHTML; // For readability: check sweet-alert.html document.body.appendChild(sweetWrap); // For development use only! /*jQuery.ajax({ url: '../lib/sweet-alert.html', // Change path depending on file location dataType: 'html' }) .done(function(html) { jQuery('body').append(html); });*/ } /* * Global sweetAlert function */ window.sweetAlert = window.swal = function() { if (arguments[0] === undefined) { window.console.error('sweetAlert expects at least 1 attribute!'); return false; } var params = extend({}, defaultParams); switch (typeof arguments[0]) { case 'string': params.title = arguments[0]; params.text = arguments[1] || ''; params.type = arguments[2] || ''; params.confirmButtonClass = params.type ? 'btn-' + params.type : defaultParams.confirmButtonClass; break; case 'object': if (arguments[0].title === undefined) { window.console.error('Missing "title" argument!'); return false; } params.title = arguments[0].title; params.text = arguments[0].text || defaultParams.text; params.type = arguments[0].type || defaultParams.type; params.allowOutsideClick = arguments[0].allowOutsideClick || defaultParams.allowOutsideClick; params.showCancelButton = arguments[0].showCancelButton !== undefined ? arguments[0].showCancelButton : defaultParams.showCancelButton; params.showConfirmButton = arguments[0].showConfirmButton !== undefined ? arguments[0].showConfirmButton : defaultParams.showConfirmButton; params.closeOnConfirm = arguments[0].closeOnConfirm !== undefined ? arguments[0].closeOnConfirm : defaultParams.closeOnConfirm; params.closeOnCancel = arguments[0].closeOnCancel !== undefined ? arguments[0].closeOnCancel : defaultParams.closeOnCancel; params.timer = arguments[0].timer || defaultParams.timer; // Show "Confirm" instead of "OK" if cancel button is visible params.confirmButtonText = (defaultParams.showCancelButton) ? 'Confirm' : defaultParams.confirmButtonText; params.confirmButtonText = arguments[0].confirmButtonText || defaultParams.confirmButtonText; params.confirmButtonClass = arguments[0].confirmButtonClass || (arguments[0].type ? 'btn-' + arguments[0].type : null) || defaultParams.confirmButtonClass; params.cancelButtonText = arguments[0].cancelButtonText || defaultParams.cancelButtonText; params.cancelButtonClass = arguments[0].cancelButtonClass || defaultParams.cancelButtonClass; params.containerClass = arguments[0].containerClass || defaultParams.containerClass; params.titleClass = arguments[0].titleClass || defaultParams.titleClass; params.textClass = arguments[0].textClass || defaultParams.textClass; params.imageUrl = arguments[0].imageUrl || defaultParams.imageUrl; params.imageSize = arguments[0].imageSize || defaultParams.imageSize; params.doneFunction = arguments[1] || null; break; default: window.console.error('Unexpected type of argument! Expected "string" or "object", got ' + typeof arguments[0]); return false; } setParameters(params); fixVerticalPosition(); openModal(); // Modal interactions var modal = getModal(); // Mouse interactions var onButtonEvent = function(e) { var target = e.target || e.srcElement, targetedConfirm = (target.className.indexOf('confirm') > -1), modalIsVisible = hasClass(modal, 'visible'), doneFunctionExists = (params.doneFunction && modal.getAttribute('data-has-done-function') === 'true'); switch (e.type) { case ("click"): if (targetedConfirm && doneFunctionExists && modalIsVisible) { // Clicked "confirm" params.doneFunction(true); if (params.closeOnConfirm) { closeModal(); } } else if (doneFunctionExists && modalIsVisible) { // Clicked "cancel" // Check if callback function expects a parameter (to track cancel actions) var functionAsStr = String(params.doneFunction).replace(/\s/g, ''); var functionHandlesCancel = functionAsStr.substring(0, 9) === "function(" && functionAsStr.substring(9, 10) !== ")"; if (functionHandlesCancel) { params.doneFunction(false); } if (params.closeOnCancel) { closeModal(); } } else { closeModal(); } break; } }; var $buttons = modal.querySelectorAll('button'); for (var i = 0; i < $buttons.length; i++) { $buttons[i].onclick = onButtonEvent; } // Remember the current document.onclick event. previousDocumentClick = document.onclick; document.onclick = function(e) { var target = e.target || e.srcElement; var clickedOnModal = (modal === target), clickedOnModalChild = isDescendant(modal, e.target), modalIsVisible = hasClass(modal, 'visible'), outsideClickIsAllowed = modal.getAttribute('data-allow-ouside-click') === 'true'; if (!clickedOnModal && !clickedOnModalChild && modalIsVisible && outsideClickIsAllowed) { closeModal(); } }; // Keyboard interactions var $okButton = modal.querySelector('button.confirm'), $cancelButton = modal.querySelector('button.cancel'), $modalButtons = modal.querySelectorAll('button:not([type=hidden])'); function handleKeyDown(e) { var keyCode = e.keyCode || e.which; if ([9,13,32,27].indexOf(keyCode) === -1) { // Don't do work on keys we don't care about. return; } var $targetElement = e.target || e.srcElement; var btnIndex = -1; // Find the button - note, this is a nodelist, not an array. for (var i = 0; i < $modalButtons.length; i++) { if ($targetElement === $modalButtons[i]) { btnIndex = i; break; } } if (keyCode === 9) { // TAB if (btnIndex === -1) { // No button focused. Jump to the confirm button. $targetElement = $okButton; } else { // Cycle to the next button if (btnIndex === $modalButtons.length - 1) { $targetElement = $modalButtons[0]; } else { $targetElement = $modalButtons[btnIndex + 1]; } } stopEventPropagation(e); $targetElement.focus(); } else { if (keyCode === 13 || keyCode === 32) { if (btnIndex === -1) { // ENTER/SPACE clicked outside of a button. $targetElement = $okButton; } else { // Do nothing - let the browser handle it. $targetElement = undefined; } } else if (keyCode === 27 && !($cancelButton.hidden || $cancelButton.style.display === 'none')) { // ESC to cancel only if there's a cancel button displayed (like the alert() window). $targetElement = $cancelButton; } else { // Fallback - let the browser handle it. $targetElement = undefined; } if ($targetElement !== undefined) { fireClick($targetElement, e); } } } previousWindowKeyDown = window.onkeydown; window.onkeydown = handleKeyDown; function handleOnBlur(e) { var $targetElement = e.target || e.srcElement, $focusElement = e.relatedTarget, modalIsVisible = hasClass(modal, 'visible'); if (modalIsVisible) { var btnIndex = -1; // Find the button - note, this is a nodelist, not an array. if ($focusElement !== null) { // If we picked something in the DOM to focus to, let's see if it was a button. for (var i = 0; i < $modalButtons.length; i++) { if ($focusElement === $modalButtons[i]) { btnIndex = i; break; } } if (btnIndex === -1) { // Something in the dom, but not a visible button. Focus back on the button. $targetElement.focus(); } } else { // Exiting the DOM (e.g. clicked in the URL bar); lastFocusedButton = $targetElement; } } } $okButton.onblur = handleOnBlur; $cancelButton.onblur = handleOnBlur; window.onfocus = function() { // When the user has focused away and focused back from the whole window. window.setTimeout(function() { // Put in a timeout to jump out of the event sequence. Calling focus() in the event // sequence confuses things. if (lastFocusedButton !== undefined) { lastFocusedButton.focus(); lastFocusedButton = undefined; } }, 0); }; }; /** * Set default params for each popup * @param {Object} userParams */ window.swal.setDefaults = function(userParams) { if (!userParams) { throw new Error('userParams is required'); } if (typeof userParams !== 'object') { throw new Error('userParams has to be a object'); } extend(defaultParams, userParams); }; /** * Closes the current modal */ window.swal.close = function() { closeModal(); } /* * Set type, text and actions on modal */ function setParameters(params) { var modal = getModal(); var $title = modal.querySelector('h2'), $text = modal.querySelector('p'), $cancelBtn = modal.querySelector('button.cancel'), $confirmBtn = modal.querySelector('button.confirm'); // Title $title.innerHTML = escapeHtml(params.title).split("\n").join("
"); // Text $text.innerHTML = escapeHtml(params.text || '').split("\n").join("
"); if (params.text) { show($text); } // Icon hide(modal.querySelectorAll('.icon')); if (params.type) { var validType = false; for (var i = 0; i < alertTypes.length; i++) { if (params.type === alertTypes[i]) { validType = true; break; } } if (!validType) { window.console.error('Unknown alert type: ' + params.type); return false; } var $icon = modal.querySelector('.icon.' + params.type); show($icon); // Animate icon switch (params.type) { case "success": addClass($icon, 'animate'); addClass($icon.querySelector('.tip'), 'animateSuccessTip'); addClass($icon.querySelector('.long'), 'animateSuccessLong'); break; case "error": addClass($icon, 'animateErrorIcon'); addClass($icon.querySelector('.x-mark'), 'animateXMark'); break; case "warning": addClass($icon, 'pulseWarning'); addClass($icon.querySelector('.body'), 'pulseWarningIns'); addClass($icon.querySelector('.dot'), 'pulseWarningIns'); break; } } // Custom image if (params.imageUrl) { var $customIcon = modal.querySelector('.icon.custom'); $customIcon.style.backgroundImage = 'url(' + params.imageUrl + ')'; show($customIcon); var _imgWidth = 80, _imgHeight = 80; if (params.imageSize) { var imgWidth = params.imageSize.split('x')[0]; var imgHeight = params.imageSize.split('x')[1]; if (!imgWidth || !imgHeight) { window.console.error("Parameter imageSize expects value with format WIDTHxHEIGHT, got " + params.imageSize); } else { _imgWidth = imgWidth; _imgHeight = imgHeight; $customIcon.css({ 'width': imgWidth + 'px', 'height': imgHeight + 'px' }); } } $customIcon.setAttribute('style', $customIcon.getAttribute('style') + 'width:' + _imgWidth + 'px; height:' + _imgHeight + 'px'); } // Cancel button modal.setAttribute('data-has-cancel-button', params.showCancelButton); if (params.showCancelButton) { $cancelBtn.style.display = 'inline-block'; } else { hide($cancelBtn); } //Confirm button modal.setAttribute('data-has-confirm-button', params.showConfirmButton); if (params.showConfirmButton) { $confirmBtn.style.display = 'inline-block'; } else { hide($confirmBtn); } // Edit text on cancel and confirm buttons if (params.cancelButtonText) { $cancelBtn.innerHTML = escapeHtml(params.cancelButtonText); } if (params.confirmButtonText) { $confirmBtn.innerHTML = escapeHtml(params.confirmButtonText); } // Reset confirm buttons to default class (Ugly fix) $confirmBtn.className = 'confirm btn btn-lg'; // Attach selected class to the sweet alert modal addClass(modal, params.containerClass); // Set confirm button to selected class addClass($confirmBtn, params.confirmButtonClass); // Set cancel button to selected class addClass($cancelBtn, params.cancelButtonClass); // Set title to selected class addClass($title, params.titleClass); // Set text to selected class addClass($text, params.textClass); // Allow outside click? modal.setAttribute('data-allow-ouside-click', params.allowOutsideClick); // Done-function var hasDoneFunction = (params.doneFunction) ? true : false; modal.setAttribute('data-has-done-function', hasDoneFunction); // Close timer modal.setAttribute('data-timer', params.timer); } /* * Set hover, active and focus-states for buttons (source: http://www.sitepoint.com/javascript-generate-lighter-darker-color) */ function colorLuminance(hex, lum) { // Validate hex string hex = String(hex).replace(/[^0-9a-f]/gi, ''); if (hex.length < 6) { hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; } lum = lum || 0; // Convert to decimal and change luminosity var rgb = "#", c, i; for (i = 0; i < 3; i++) { c = parseInt(hex.substr(i*2,2), 16); c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); rgb += ("00"+c).substr(c.length); } return rgb; } function extend(a, b){ for (var key in b) { if (b.hasOwnProperty(key)) { a[key] = b[key]; } } return a; } function hexToRgb(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? parseInt(result[1], 16) + ', ' + parseInt(result[2], 16) + ', ' + parseInt(result[3], 16) : null; } // Add box-shadow style to button (depending on its chosen bg-color) function setFocusStyle($button, bgColor) { var rgbColor = hexToRgb(bgColor); $button.style.boxShadow = '0 0 2px rgba(' + rgbColor +', 0.8), inset 0 0 0 1px rgba(0, 0, 0, 0.05)'; } /* * Animations */ function openModal() { var modal = getModal(); fadeIn(getOverlay(), 10); show(modal); addClass(modal, 'showSweetAlert'); removeClass(modal, 'hideSweetAlert'); previousActiveElement = document.activeElement; var $okButton = modal.querySelector('button.confirm'); $okButton.focus(); setTimeout(function() { addClass(modal, 'visible'); }, 500); var timer = modal.getAttribute('data-timer'); if (timer !== "null" && timer !== "") { setTimeout(function() { closeModal(); }, timer); } } function closeModal() { var modal = getModal(); fadeOut(getOverlay(), 5); fadeOut(modal, 5); removeClass(modal, 'showSweetAlert'); addClass(modal, 'hideSweetAlert'); removeClass(modal, 'visible'); // Reset icon animations var $successIcon = modal.querySelector('.icon.success'); removeClass($successIcon, 'animate'); removeClass($successIcon.querySelector('.tip'), 'animateSuccessTip'); removeClass($successIcon.querySelector('.long'), 'animateSuccessLong'); var $errorIcon = modal.querySelector('.icon.error'); removeClass($errorIcon, 'animateErrorIcon'); removeClass($errorIcon.querySelector('.x-mark'), 'animateXMark'); var $warningIcon = modal.querySelector('.icon.warning'); removeClass($warningIcon, 'pulseWarning'); removeClass($warningIcon.querySelector('.body'), 'pulseWarningIns'); removeClass($warningIcon.querySelector('.dot'), 'pulseWarningIns'); // Reset the page to its previous state window.onkeydown = previousWindowKeyDown; document.onclick = previousDocumentClick; if (previousActiveElement) { previousActiveElement.focus(); } lastFocusedButton = undefined; } /* * Set "margin-top"-property on modal based on its computed height */ function fixVerticalPosition() { var modal = getModal(); modal.style.marginTop = getTopMargin(getModal()); } /* * If library is injected after page has loaded */ (function () { if (document.readyState === "complete" || document.readyState === "interactive" && document.body) { sweetAlertInitialize(); } else { if (document.addEventListener) { document.addEventListener('DOMContentLoaded', function handler() { document.removeEventListener('DOMContentLoaded', handler, false); sweetAlertInitialize(); }, false); } else if (document.attachEvent) { document.attachEvent('onreadystatechange', function handler() { if (document.readyState === 'complete') { document.detachEvent('onreadystatechange', handler); sweetAlertInitialize(); } }); } } })(); })(window, document);