/** * alertify * An unobtrusive customizable JavaScript notification system * * @author Fabien Doiron * @copyright Fabien Doiron 2012 * @license MIT * @link http://www.github.com/fabien-d * @module alertify * @version 0.2.12 */ /*global define*/ (function (global, undefined) { "use strict"; var document = global.document, Alertify; Alertify = function () { var _alertify = {}, dialogs = {}, isopen = false, keys = { ENTER: 13, ESC: 27, SPACE: 32 }, queue = [], $, elCallee, elCover, elDialog, elLog; /** * Markup pieces * @type {Object} */ dialogs = { buttons : { holder : "", submit : "", ok : "{{ok}}", cancel : "{{cancel}}" }, input : "", message : "

{{message}}

", log : "
{{message}}
" }; /** * Shorthand for document.getElementById() * * @param {String} id A specific element ID * @return {Object} HTML element */ $ = function (id) { return document.getElementById(id); }; /** * Alertify private object * @type {Object} */ _alertify = { /** * Labels object * @type {Object} */ labels : { ok : "Aceptar", cancel : "Cancelar" }, /** * Delay number * @type {Number} */ delay : 5000, /** * Set the proper button click events * * @param {Function} fn [Optional] Callback function * * @return {undefined} */ addListeners : function (fn) { var btnReset = $("alertify-resetFocus"), btnOK = $("alertify-ok") || undefined, btnCancel = $("alertify-cancel") || undefined, input = $("alertify-text") || undefined, form = $("alertify-form") || undefined, hasOK = (typeof btnOK !== "undefined"), hasCancel = (typeof btnCancel !== "undefined"), hasInput = (typeof input !== "undefined"), val = "", self = this, ok, cancel, common, key, reset; // ok event handler ok = function (event) { if (typeof event.preventDefault !== "undefined") event.preventDefault(); common(event); if (typeof input !== "undefined") val = input.value; if (typeof fn === "function") fn(true, val); }; // cancel event handler cancel = function (event) { if (typeof event.preventDefault !== "undefined") event.preventDefault(); common(event); if (typeof fn === "function") fn(false); }; // common event handler (keyup, ok and cancel) common = function (event) { self.hide(); self.unbind(document.body, "keyup", key); self.unbind(btnReset, "focus", reset); if (hasInput) self.unbind(form, "submit", ok); if (hasOK) self.unbind(btnOK, "click", ok); if (hasCancel) self.unbind(btnCancel, "click", cancel); }; // keyup handler key = function (event) { var keyCode = event.keyCode; if (keyCode === keys.SPACE && !hasInput) ok(event); if (keyCode === keys.ESC && hasCancel) cancel(event); }; // reset focus to first item in the dialog reset = function (event) { if (hasInput) input.focus(); else if (hasCancel) btnCancel.focus(); else btnOK.focus(); }; // handle reset focus link // this ensures that the keyboard focus does not // ever leave the dialog box until an action has // been taken this.bind(btnReset, "focus", reset); // handle OK click if (hasOK) this.bind(btnOK, "click", ok); // handle Cancel click if (hasCancel) this.bind(btnCancel, "click", cancel); // listen for keys, Cancel => ESC this.bind(document.body, "keyup", key); // bind form submit if (hasInput) this.bind(form, "submit", ok); // set focus on OK button or the input text global.setTimeout(function () { if (input) { input.focus(); input.select(); } else btnOK.focus(); }, 50); }, /** * Bind events to elements * * @param {Object} el HTML Object * @param {Event} event Event to attach to element * @param {Function} fn Callback function * * @return {undefined} */ bind : function (el, event, fn) { if (typeof el.addEventListener === "function") { el.addEventListener(event, fn, false); } else if (el.attachEvent) { el.attachEvent("on" + event, fn); } }, /** * Build the proper message box * * @param {Object} item Current object in the queue * * @return {String} An HTML string of the message box */ build : function (item) { var html = "", type = item.type, message = item.message; html += "
"; if (type === "prompt") html += "
"; html += "
"; html += dialogs.message.replace("{{message}}", message); if (type === "prompt") html += dialogs.input; html += dialogs.buttons.holder; html += "
"; if (type === "prompt") html += "
"; html += "Reset Focus"; html += "
"; switch (type) { case "confirm": html = html.replace("{{buttons}}", dialogs.buttons.ok + dialogs.buttons.cancel); html = html.replace("{{ok}}", this.labels.ok).replace("{{cancel}}", this.labels.cancel); break; case "prompt": html = html.replace("{{buttons}}", dialogs.buttons.submit + dialogs.buttons.cancel); html = html.replace("{{ok}}", this.labels.ok).replace("{{cancel}}", this.labels.cancel); break; case "alert": html = html.replace("{{buttons}}", dialogs.buttons.ok); html = html.replace("{{ok}}", this.labels.ok); break; default: break; } elDialog.className = "alertify alertify-show alertify-" + type; elCover.className = "alertify-cover"; return html; }, /** * Close the log messages * * @param {Object} elem HTML Element of log message to close * @param {Number} wait [optional] Time (in ms) to wait before automatically hiding the message * * @return {undefined} */ close : function (elem, wait) { var timer = (wait && !isNaN(wait)) ? +wait : this.delay; // Unary Plus: +"2" === 2 this.bind(elem, "click", function () { elLog.removeChild(elem); }); setTimeout(function () { if (typeof elem !== "undefined" && elem.parentNode === elLog) elLog.removeChild(elem); }, timer); }, /** * Create a dialog box * * @param {String} message The message passed from the callee * @param {String} type Type of dialog to create * @param {Function} fn [Optional] Callback function * @param {String} placeholder [Optional] Default value for prompt input field * * @return {Object} */ dialog : function (message, type, fn, placeholder) { // set the current active element // this allows the keyboard focus to be resetted // after the dialog box is closed elCallee = document.activeElement; // check to ensure the alertify dialog element // has been successfully created var check = function () { if (elDialog && elDialog.scrollTop !== null) return; else check(); }; // error catching if (typeof message !== "string") throw new Error("message must be a string"); if (typeof type !== "string") throw new Error("type must be a string"); if (typeof fn !== "undefined" && typeof fn !== "function") throw new Error("fn must be a function"); // initialize alertify if it hasn't already been done if (typeof this.init === "function") { this.init(); check(); } queue.push({ type: type, message: message, callback: fn, placeholder: placeholder }); if (!isopen) this.setup(); return this; }, /** * Extend the log method to create custom methods * * @param {String} type Custom method name * * @return {Function} */ extend : function (type) { return function (message, wait) { this.log(message, type, wait); }; }, /** * Hide the dialog and rest to defaults * * @return {undefined} */ hide : function () { // remove reference from queue queue.splice(0,1); // if items remaining in the queue if (queue.length > 0) this.setup(); else { isopen = false; elDialog.className = "alertify alertify-hide alertify-hidden"; elCover.className = "alertify-cover alertify-hidden"; // set focus to the last element or body // after the dialog is closed elCallee.focus(); } }, /** * Initialize Alertify * Create the 2 main elements * * @return {undefined} */ init : function () { // ensure legacy browsers support html5 tags document.createElement("nav"); document.createElement("article"); document.createElement("section"); // cover elCover = document.createElement("div"); elCover.setAttribute("id", "alertify-cover"); elCover.className = "alertify-cover alertify-hidden"; document.body.appendChild(elCover); // main element elDialog = document.createElement("section"); elDialog.setAttribute("id", "alertify"); elDialog.className = "alertify alertify-hidden"; document.body.appendChild(elDialog); // log element elLog = document.createElement("section"); elLog.setAttribute("id", "alertify-logs"); elLog.className = "alertify-logs"; document.body.appendChild(elLog); // set tabindex attribute on body element // this allows script to give it focus // after the dialog is closed document.body.setAttribute("tabindex", "0"); // clean up init method delete this.init; }, /** * Show a new log message box * * @param {String} message The message passed from the callee * @param {String} type [Optional] Optional type of log message * @param {Number} wait [Optional] Time (in ms) to wait before auto-hiding the log * * @return {Object} */ log : function (message, type, wait) { // check to ensure the alertify dialog element // has been successfully created var check = function () { if (elLog && elLog.scrollTop !== null) return; else check(); }; // initialize alertify if it hasn't already been done if (typeof this.init === "function") { this.init(); check(); } this.notify(message, type, wait); return this; }, /** * Add new log message * If a type is passed, a class name "alertify-log-{type}" will get added. * This allows for custom look and feel for various types of notifications. * * @param {String} message The message passed from the callee * @param {String} type [Optional] Type of log message * @param {Number} wait [Optional] Time (in ms) to wait before auto-hiding * * @return {undefined} */ notify : function (message, type, wait) { var log = document.createElement("article"); log.className = "alertify-log" + ((typeof type === "string" && type !== "") ? " alertify-log-" + type : ""); log.innerHTML = message; // prepend child elLog.insertBefore(log, elLog.firstChild); // triggers the CSS animation setTimeout(function() { log.className = log.className + " alertify-log-show"; }, 50); this.close(log, wait); }, /** * Set properties * * @param {Object} args Passing parameters * * @return {undefined} */ set : function (args) { var k; // error catching if (typeof args !== "object" && args instanceof Array) throw new Error("args must be an object"); // set parameters for (k in args) { if (args.hasOwnProperty(k)) { this[k] = args[k]; } } }, /** * Initiate all the required pieces for the dialog box * * @return {undefined} */ setup : function () { var item = queue[0]; isopen = true; elDialog.innerHTML = this.build(item); if (typeof item.placeholder === "string") $("alertify-text").value = item.placeholder; this.addListeners(item.callback); }, /** * Unbind events to elements * * @param {Object} el HTML Object * @param {Event} event Event to detach to element * @param {Function} fn Callback function * * @return {undefined} */ unbind : function (el, event, fn) { if (typeof el.removeEventListener === "function") { el.removeEventListener(event, fn, false); } else if (el.detachEvent) { el.detachEvent("on" + event, fn); } } }; return { alert : function (message, fn) { _alertify.dialog(message, "alert", fn); return this; }, confirm : function (message, fn) { _alertify.dialog(message, "confirm", fn); return this; }, extend : _alertify.extend, init : _alertify.init, log : function (message, type, wait) { _alertify.log(message, type, wait); return this; }, prompt : function (message, fn, placeholder) { _alertify.dialog(message, "prompt", fn, placeholder); return this; }, success : function (message, wait) { _alertify.log(message, "success", wait); return this; }, error : function (message, wait) { _alertify.log(message, "error", wait); return this; }, set : function (args) { _alertify.set(args); }, labels : _alertify.labels }; }; // AMD and window support if (typeof define === "function") { define([], function () { return new Alertify(); }); } else { if (typeof global.alertify === "undefined") { global.alertify = new Alertify(); } } }(this));