diff options
| author | horus_arch | 2015-05-08 01:46:18 +0200 |
|---|---|---|
| committer | horus_arch | 2015-05-08 01:46:18 +0200 |
| commit | fb7050acf2545c9d222d2c014460e4fcad5cc462 (patch) | |
| tree | 942e1afa24dca916d8c003fb0a763424a93a2110 /static/js/sweet-alert.js | |
| download | scribbled-fb7050acf2545c9d222d2c014460e4fcad5cc462.tar.gz | |
Initial commit.
Diffstat (limited to 'static/js/sweet-alert.js')
| -rw-r--r-- | static/js/sweet-alert.js | 726 |
1 files changed, 726 insertions, 0 deletions
diff --git a/static/js/sweet-alert.js b/static/js/sweet-alert.js new file mode 100644 index 0000000..324718a --- /dev/null +++ b/static/js/sweet-alert.js @@ -0,0 +1,726 @@ +// 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, + 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 = '<div class="sweet-overlay" tabIndex="-1"></div><div class="sweet-alert" tabIndex="-1"><div class="icon error"><span class="x-mark"><span class="line left"></span><span class="line right"></span></span></div><div class="icon warning"> <span class="body"></span> <span class="dot"></span> </div> <div class="icon info"></div> <div class="icon success"> <span class="line tip"></span> <span class="line long"></span> <div class="placeholder"></div> <div class="fix"></div> </div> <div class="icon custom"></div> <h2>Title</h2><p class="lead text-muted">Text</p><p><button class="cancel btn btn-lg" tabIndex="2">Cancel</button> <button class="confirm btn btn-lg" tabIndex="1">OK</button></p></div>', + 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] || ''; + + 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.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 || 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); + }; + + /* + * 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("<br>"); + + // Text + $text.innerHTML = escapeHtml(params.text || '').split("\n").join("<br>"); + 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); + } + + // 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 factorial() { + document.removeEventListener('DOMContentLoaded', arguments.callee, false); + sweetAlertInitialize(); + }, false); + } else if (document.attachEvent) { + document.attachEvent('onreadystatechange', function() { + if (document.readyState === 'complete') { + document.detachEvent('onreadystatechange', arguments.callee); + sweetAlertInitialize(); + } + }); + } + } + })(); + +})(window, document); |
