diff --git a/.gitignore b/.gitignore index 3db19e71..f5fdfbee 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ .sass-cache/ example/assets/sass/ app.js +.idea/ diff --git a/example/index.html b/example/index.html index 5bf94a06..defd20b8 100644 --- a/example/index.html +++ b/example/index.html @@ -1,200 +1,229 @@ - - alertify.js - example page - - - - + + alertify.js - example page + + + + -

Dialogs

- Alert Dialog
- Confirm Dialog
- Prompt Dialog
- Custom Labels
- Button Focus
- Button Order - -

Ajax

- Ajax - Multiple Dialog - -

Logs

- Standard Log
- Success Log
- Error Log
- Custom Log
- Hide in 10 seconds
- Persistent Log - -

Themes

- Bootstrap Theme - - - - +

Dialogs

+Alert Dialog
+Confirm Dialog
+Prompt Dialog
+Custom Labels
+Button Focus
+Button Order + +

Ajax

+Ajax - Multiple Dialog + +

Logs

+Standard Log
+Success Log
+Error Log
+Custom Log
+Hide in 10 seconds
+Persistent Log + +

Themes

+Bootstrap Theme + +

Close from API

+Open a few persistent notifications.
+ + +

Close all from API

+Close All Opened Notifications + + + + + \ No newline at end of file diff --git a/lib/alertify.js b/lib/alertify.js index 373d32d7..9f701a65 100644 --- a/lib/alertify.js +++ b/lib/alertify.js @@ -3,634 +3,730 @@ * An unobtrusive customizable JavaScript notification system * * @author Fabien Doiron - * @copyright Fabien Doiron 2013 + * @copyright Fabien Doiron 2014 * @license MIT * @link http://fabien-d.github.com/alertify.js/ * @module alertify * @version 0.3.11 */ (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 = [], - $, btnCancel, btnOK, btnReset, btnResetBack, btnFocus, elCallee, elCover, elDialog, elLog, form, input, getTransitionEvent; - - /** - * Markup pieces - * @type {Object} - */ - dialogs = { - buttons : { - holder : "", - submit : "", - ok : "", - cancel : "" - }, - input : "
", - message : "

{{message}}

", - log : "
{{message}}
" - }; - - /** - * Return the proper transitionend event - * @return {String} Transition type string - */ - getTransitionEvent = function () { - var t, - type, - supported = false, - el = document.createElement("fakeelement"), - transitions = { - "WebkitTransition" : "webkitTransitionEnd", - "MozTransition" : "transitionend", - "OTransition" : "otransitionend", - "transition" : "transitionend" - }; - - for (t in transitions) { - if (el.style[t] !== undefined) { - type = transitions[t]; - supported = true; - break; - } - } - - return { - type : type, - supported : supported - }; - }; - - /** - * 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 : "OK", - cancel : "Cancel" - }, - - /** - * Delay number - * @type {Number} - */ - delay : 5000, - - /** - * Whether buttons are reversed (default is secondary/primary) - * @type {Boolean} - */ - buttonReverse : false, - - /** - * Which button should be focused by default - * @type {String} "ok" (default), "cancel", or "none" - */ - buttonFocus : "ok", - - /** - * Set the transition event on load - * @type {[type]} - */ - transition : undefined, - - /** - * Set the proper button click events - * - * @param {Function} fn [Optional] Callback function - * - * @return {undefined} - */ - addListeners : function (fn) { - var 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") { - if (typeof input !== "undefined") { - fn(true, val); - } - else fn(true); - } - return false; - }; - - // cancel event handler - cancel = function (event) { - if (typeof event.preventDefault !== "undefined") event.preventDefault(); - common(event); - if (typeof fn === "function") fn(false); - return 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 (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) || (hasInput && keyCode === keys.ENTER)) 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 || self.buttonReverse) btnOK.focus(); - else btnCancel.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); - this.bind(btnResetBack, "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); - if (!this.transition.supported) { - this.setFocus(); - } - }, - - /** - * 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); - } - }, - - /** - * Use alertify as the global error handler (using window.onerror) - * - * @return {boolean} success - */ - handleErrors : function () { - if (typeof global.onerror !== "undefined") { - var self = this; - global.onerror = function (msg, url, line) { - self.error("[" + msg + " on line " + line + " of " + url + "]", 0); - }; - return true; - } else { - return false; - } - }, - - /** - * Append button HTML strings - * - * @param {String} secondary The secondary button HTML string - * @param {String} primary The primary button HTML string - * - * @return {String} The appended button HTML strings - */ - appendButtons : function (secondary, primary) { - return this.buttonReverse ? primary + secondary : secondary + primary; - }, - - /** - * 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, - css = item.cssClass || ""; - - html += "
"; - html += "Reset Focus"; - - if (_alertify.buttonFocus === "none") html += ""; - - // doens't require an actual form - 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}}", this.appendButtons(dialogs.buttons.cancel, dialogs.buttons.ok)); - html = html.replace("{{ok}}", this.labels.ok).replace("{{cancel}}", this.labels.cancel); - break; - case "prompt": - html = html.replace("{{buttons}}", this.appendButtons(dialogs.buttons.cancel, dialogs.buttons.submit)); - 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-" + type + " " + css; - 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, if 0 never hide - * - * @return {undefined} - */ - close : function (elem, wait) { - // Unary Plus: +"2" === 2 - var timer = (wait && !isNaN(wait)) ? +wait : this.delay, - self = this, - hideElement, transitionDone; - - // set click event on log messages - this.bind(elem, "click", function () { - hideElement(elem); - }); - // Hide the dialog box after transition - // This ensure it doens't block any element from being clicked - transitionDone = function (event) { - event.stopPropagation(); - // unbind event so function only gets called once - self.unbind(this, self.transition.type, transitionDone); - // remove log message - elLog.removeChild(this); - if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden"; - }; - // this sets the hide class to transition out - // or removes the child if css transitions aren't supported - hideElement = function (el) { - // ensure element exists - if (typeof el !== "undefined" && el.parentNode === elLog) { - // whether CSS transition exists - if (self.transition.supported) { - self.bind(el, self.transition.type, transitionDone); - el.className += " alertify-log-hide"; - } else { - elLog.removeChild(el); - if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden"; - } - } - }; - // never close (until click) if wait is set to 0 - if (wait === 0) return; - // set timeout to auto close the log message - setTimeout(function () { hideElement(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 - * @param {String} cssClass [Optional] Class(es) to append to dialog box - * - * @return {Object} - */ - dialog : function (message, type, fn, placeholder, cssClass) { - // 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 ((elLog && elLog.scrollTop !== null) && (elCover && elCover.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 - this.init(); - check(); - - queue.push({ type: type, message: message, callback: fn, placeholder: placeholder, cssClass: cssClass }); - 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) { - if (typeof type !== "string") throw new Error("extend method must have exactly one paramter"); - return function (message, wait) { - this.log(message, type, wait); - return this; - }; - }, - - /** - * Hide the dialog and rest to defaults - * - * @return {undefined} - */ - hide : function () { - var transitionDone, - self = this; - // remove reference from queue - queue.splice(0,1); - // if items remaining in the queue - if (queue.length > 0) this.setup(true); - else { - isopen = false; - // Hide the dialog box after transition - // This ensure it doens't block any element from being clicked - transitionDone = function (event) { - event.stopPropagation(); - // unbind event so function only gets called once - self.unbind(elDialog, self.transition.type, transitionDone); - }; - // whether CSS transition exists - if (this.transition.supported) { - this.bind(elDialog, this.transition.type, transitionDone); - elDialog.className = "alertify alertify-hide alertify-hidden"; - } else { - elDialog.className = "alertify alertify-hide alertify-hidden alertify-isHidden"; - } - elCover.className = "alertify-cover alertify-cover-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 - if ($("alertify-cover") == null) { - elCover = document.createElement("div"); - elCover.setAttribute("id", "alertify-cover"); - elCover.className = "alertify-cover alertify-cover-hidden"; - document.body.appendChild(elCover); - } - // main element - if ($("alertify") == null) { - isopen = false; - queue = []; - elDialog = document.createElement("section"); - elDialog.setAttribute("id", "alertify"); - elDialog.className = "alertify alertify-hidden"; - document.body.appendChild(elDialog); - } - // log element - if ($("alertify-logs") == null) { - elLog = document.createElement("section"); - elLog.setAttribute("id", "alertify-logs"); - elLog.className = "alertify-logs alertify-logs-hidden"; - 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"); - // set transition type - this.transition = getTransitionEvent(); - }, - - /** - * 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 - this.init(); - check(); - - elLog.className = "alertify-logs"; - 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; - // append child - elLog.appendChild(log); - // 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]; - } - } - }, - - /** - * Common place to set focus to proper element - * - * @return {undefined} - */ - setFocus : function () { - if (input) { - input.focus(); - input.select(); - } - else btnFocus.focus(); - }, - - /** - * Initiate all the required pieces for the dialog box - * - * @return {undefined} - */ - setup : function (fromQueue) { - var item = queue[0], - self = this, - transitionDone; - - // dialog is open - isopen = true; - // Set button focus after transition - transitionDone = function (event) { - event.stopPropagation(); - self.setFocus(); - // unbind event so function only gets called once - self.unbind(elDialog, self.transition.type, transitionDone); - }; - // whether CSS transition exists - if (this.transition.supported && !fromQueue) { - this.bind(elDialog, this.transition.type, transitionDone); - } - // build the proper dialog HTML - elDialog.innerHTML = this.build(item); - // assign all the common elements - btnReset = $("alertify-resetFocus"); - btnResetBack = $("alertify-resetFocusBack"); - btnOK = $("alertify-ok") || undefined; - btnCancel = $("alertify-cancel") || undefined; - btnFocus = (_alertify.buttonFocus === "cancel") ? btnCancel : ((_alertify.buttonFocus === "none") ? $("alertify-noneFocus") : btnOK), - input = $("alertify-text") || undefined; - form = $("alertify-form") || undefined; - // add placeholder value to the input field - if (typeof item.placeholder === "string" && item.placeholder !== "") input.value = item.placeholder; - if (fromQueue) this.setFocus(); - 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, cssClass) { _alertify.dialog(message, "alert", fn, "", cssClass); return this; }, - confirm : function (message, fn, cssClass) { _alertify.dialog(message, "confirm", fn, "", cssClass); 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, cssClass) { _alertify.dialog(message, "prompt", fn, placeholder, cssClass); 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, - debug : _alertify.handleErrors - }; - }; - - // AMD and window support - if (typeof define === "function") { - define([], function () { return new Alertify(); }); - } else if (typeof global.alertify === "undefined") { - global.alertify = new Alertify(); - } - -}(this)); + "use strict"; + + var document = global.document, + Alertify; + + Alertify = function () { + + var _alertify = {}, + dialogs = {}, + isopen = false, + keys = {ENTER: 13, ESC: 27, SPACE: 32}, + queue = [], + $, btnCancel, btnOK, btnReset, btnResetBack, btnFocus, elCallee, elCover, elDialog, elLog, form, input, getTransitionEvent, uniqueId; + + /** + * Markup pieces + * @type {Object} + */ + dialogs = { + buttons: { + holder: "", + submit: "", + ok: "", + cancel: "" + }, + input: "
", + message: "

{{message}}

", + log: "
{{message}}
" + }; + + /** + * Return the proper transitionend event + * @return {String} Transition type string + */ + getTransitionEvent = function () { + var t, + type, + supported = false, + el = document.createElement("fakeelement"), + transitions = { + "WebkitTransition": "webkitTransitionEnd", + "MozTransition": "transitionend", + "OTransition": "otransitionend", + "transition": "transitionend" + }; + + for (t in transitions) { + if (el.style[t] !== undefined) { + type = transitions[t]; + supported = true; + break; + } + } + + return { + type: type, + supported: supported + }; + }; + + /** + * 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: "OK", + cancel: "Cancel" + }, + + /** + * Delay number + * @type {Number} + */ + delay: 5000, + + /** + * Whether buttons are reversed (default is secondary/primary) + * @type {Boolean} + */ + buttonReverse: false, + + /** + * Which button should be focused by default + * @type {String} "ok" (default), "cancel", or "none" + */ + buttonFocus: "ok", + + /** + * Set the transition event on load + * @type {[type]} + */ + transition: undefined, + + /** + * Set the proper button click events + * + * @param {Function} fn [Optional] Callback function + * + * @return {undefined} + */ + addListeners: function (fn) { + var 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") { + if (typeof input !== "undefined") { + fn(true, val); + } + else fn(true); + } + return false; + }; + + // cancel event handler + cancel = function (event) { + if (typeof event.preventDefault !== "undefined") event.preventDefault(); + common(event); + if (typeof fn === "function") fn(false); + return 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 (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) || (hasInput && keyCode === keys.ENTER)) 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 || self.buttonReverse) btnOK.focus(); + else btnCancel.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); + this.bind(btnResetBack, "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); + if (!this.transition.supported) { + this.setFocus(); + } + }, + + /** + * 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); + } + }, + + /** + * Use alertify as the global error handler (using window.onerror) + * + * @return {boolean} success + */ + handleErrors: function () { + if (typeof global.onerror !== "undefined") { + var self = this; + global.onerror = function (msg, url, line) { + self.error("[" + msg + " on line " + line + " of " + url + "]", 0); + }; + return true; + } else { + return false; + } + }, + + /** + * Append button HTML strings + * + * @param {String} secondary The secondary button HTML string + * @param {String} primary The primary button HTML string + * + * @return {String} The appended button HTML strings + */ + appendButtons: function (secondary, primary) { + return this.buttonReverse ? primary + secondary : secondary + primary; + }, + + /** + * 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, + css = item.cssClass || ""; + + html += "
"; + html += "Reset Focus"; + + if (_alertify.buttonFocus === "none") html += ""; + + // doens't require an actual form + 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}}", this.appendButtons(dialogs.buttons.cancel, dialogs.buttons.ok)); + html = html.replace("{{ok}}", this.labels.ok).replace("{{cancel}}", this.labels.cancel); + break; + case "prompt": + html = html.replace("{{buttons}}", this.appendButtons(dialogs.buttons.cancel, dialogs.buttons.submit)); + 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-" + type + " " + css; + 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, if 0 never hide + * + * @return {undefined} + */ + close: function (elem, wait, clickToClose) { + // Unary Plus: +"2" === 2 + var timer = (wait && !isNaN(wait)) ? +wait : this.delay, + self = this, + hideElement, transitionDone; + + // set click event on log messages + if (clickToClose) { + this.bind(elem, "click", function () { + hideElement(elem); + }); + } + + // Hide the dialog box after transition + // This ensure it doens't block any element from being clicked + transitionDone = function (event) { + event.stopPropagation(); + // unbind event so function only gets called once + self.unbind(this, self.transition.type, transitionDone); + // remove log message + elLog.removeChild(this); + if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden"; + }; + // this sets the hide class to transition out + // or removes the child if css transitions aren't supported + hideElement = function (el) { + // ensure element exists + if (typeof el !== "undefined" && el.parentNode === elLog) { + // whether CSS transition exists + if (self.transition.supported) { + self.bind(el, self.transition.type, transitionDone); + el.className += " alertify-log-hide"; + } else { + elLog.removeChild(el); + if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden"; + } + } + }; + + // never close (until click) if wait is set to 0 + if (wait === 0) return; + // set timeout to auto close the log message + setTimeout(function () { + hideElement(elem); + }, timer); + }, + + /** + * Close a log message immediately + * + * @param {Object} className HTML class of log messages to close now + * + * @return {undefined} + */ + closeNow: function (className) { + + // Unary Plus: +"2" === 2 + var self = this, + hideElement, transitionDone; + + // Hide the dialog box after transition + // This ensure it doens't block any element from being clicked + transitionDone = function (event) { + event.stopPropagation(); + // unbind event so function only gets called once + self.unbind(this, self.transition.type, transitionDone); + // remove log message + elLog.removeChild(this); + if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden"; + }; + + // this sets the hide class to transition out + // or removes the child if css transitions aren't supported + hideElement = function (el) { + // ensure element exists + if (typeof el !== "undefined" && el.parentNode === elLog) { + // whether CSS transition exists + if (self.transition.supported) { + self.bind(el, self.transition.type, transitionDone); + el.className += " alertify-log-hide"; + } else { + elLog.removeChild(el); + if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden"; + } + } + }; + + var elems = document.getElementsByClassName('alertify-log-' + className); + for (var i = 0; i < elems.length; i++) { + hideElement(elems[i]); + } + return; + }, + + + /** + * 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 + * @param {String} cssClass [Optional] Class(es) to append to dialog box + * + * @return {Object} + */ + dialog: function (message, type, fn, placeholder, cssClass) { + // 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 ((elLog && elLog.scrollTop !== null) && (elCover && elCover.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 + this.init(); + check(); + + queue.push({type: type, message: message, callback: fn, placeholder: placeholder, cssClass: cssClass}); + 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) { + if (typeof type !== "string") throw new Error("extend method must have exactly one parameter"); + return function (message, wait) { + this.log(message, type, wait); + return this; + }; + }, + + /** + * Hide the dialog and rest to defaults + * + * @return {undefined} + */ + hide: function () { + var transitionDone, + self = this; + // remove reference from queue + queue.splice(0, 1); + // if items remaining in the queue + if (queue.length > 0) this.setup(true); + else { + isopen = false; + // Hide the dialog box after transition + // This ensure it doens't block any element from being clicked + transitionDone = function (event) { + event.stopPropagation(); + // unbind event so function only gets called once + self.unbind(elDialog, self.transition.type, transitionDone); + }; + // whether CSS transition exists + if (this.transition.supported) { + this.bind(elDialog, this.transition.type, transitionDone); + elDialog.className = "alertify alertify-hide alertify-hidden"; + } else { + elDialog.className = "alertify alertify-hide alertify-hidden alertify-isHidden"; + } + elCover.className = "alertify-cover alertify-cover-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 + if ($("alertify-cover") == null) { + elCover = document.createElement("div"); + elCover.setAttribute("id", "alertify-cover"); + elCover.className = "alertify-cover alertify-cover-hidden"; + document.body.appendChild(elCover); + } + // main element + if ($("alertify") == null) { + isopen = false; + queue = []; + elDialog = document.createElement("section"); + elDialog.setAttribute("id", "alertify"); + elDialog.className = "alertify alertify-hidden"; + document.body.appendChild(elDialog); + } + // log element + if ($("alertify-logs") == null) { + elLog = document.createElement("section"); + elLog.setAttribute("id", "alertify-logs"); + elLog.className = "alertify-logs alertify-logs-hidden"; + 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"); + // set transition type + this.transition = getTransitionEvent(); + }, + + /** + * 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, clickToClose) { + // set default clickToClose value to true + clickToClose = (clickToClose === undefined) ? true : clickToClose; + + // 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 + this.init(); + check(); + elLog.className = "alertify-logs"; + this.notify(message, type, wait, clickToClose); + 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, clickToClose) { + uniqueId = Math.floor((Math.random() * 100) + 1); + var log = document.createElement("article"); + log.className = "alertify-log" + ((typeof type === "string" && type !== "") ? " alertify-log-" + type : " alertify-log-" + uniqueId); + log.innerHTML = message; + // append child + elLog.appendChild(log); + // triggers the CSS animation + setTimeout(function () { + log.className = log.className + " alertify-log-show"; + }, 50); + this.close(log, wait, clickToClose); + }, + + /** + * 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]; + } + } + }, + + /** + * Common place to set focus to proper element + * + * @return {undefined} + */ + setFocus: function () { + if (input) { + input.focus(); + input.select(); + } + else btnFocus.focus(); + }, + + /** + * Initiate all the required pieces for the dialog box + * + * @return {undefined} + */ + setup: function (fromQueue) { + var item = queue[0], + self = this, + transitionDone; + + // dialog is open + isopen = true; + // Set button focus after transition + transitionDone = function (event) { + event.stopPropagation(); + self.setFocus(); + // unbind event so function only gets called once + self.unbind(elDialog, self.transition.type, transitionDone); + }; + // whether CSS transition exists + if (this.transition.supported && !fromQueue) { + this.bind(elDialog, this.transition.type, transitionDone); + } + // build the proper dialog HTML + elDialog.innerHTML = this.build(item); + // assign all the common elements + btnReset = $("alertify-resetFocus"); + btnResetBack = $("alertify-resetFocusBack"); + btnOK = $("alertify-ok") || undefined; + btnCancel = $("alertify-cancel") || undefined; + btnFocus = (_alertify.buttonFocus === "cancel") ? btnCancel : ((_alertify.buttonFocus === "none") ? $("alertify-noneFocus") : btnOK), + input = $("alertify-text") || undefined; + form = $("alertify-form") || undefined; + // add placeholder value to the input field + if (typeof item.placeholder === "string" && item.placeholder !== "") input.value = item.placeholder; + if (fromQueue) this.setFocus(); + 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 { + extend: _alertify.extend, + init: _alertify.init, + labels: _alertify.labels, + debug: _alertify.handleErrors, + + alert: function (message, fn, cssClass) { + _alertify.dialog(message, "alert", fn, "", cssClass); + return this; + }, + + confirm: function (message, fn, cssClass) { + _alertify.dialog(message, "confirm", fn, "", cssClass); + return this; + }, + + prompt: function (message, fn, placeholder, cssClass) { + _alertify.dialog(message, "prompt", fn, placeholder, cssClass); + return this; + }, + + log: function (message, type, wait, clickToClose) { + _alertify.log(message, type, wait, clickToClose); + return uniqueId; + }, + + success: function (message, wait, clickToClose) { + _alertify.log(message, "success", wait, clickToClose); + return uniqueId; + }, + + error: function (message, wait, clickToClose) { + _alertify.log(message, "error", wait, clickToClose); + return uniqueId; + }, + + set: function (args) { + _alertify.set(args); + }, + + closeNow: function (uniqueId) { + _alertify.closeNow(uniqueId); + }, + + closeAll: function () { + _alertify.closeNow("show"); + } + }; + }; + + // AMD and window support + if (typeof define === "function") { + define([], function () { + return new Alertify(); + }); + } else if (typeof global.alertify === "undefined") { + global.alertify = new Alertify(); + } + +}(this)); \ No newline at end of file diff --git a/lib/alertify.min.js b/lib/alertify.min.js index c4b6cc81..0d0e7363 100644 --- a/lib/alertify.min.js +++ b/lib/alertify.min.js @@ -1,2 +1 @@ -/*! alertify - v0.3.11 - 2013-10-08 */ -!function(a,b){"use strict";var c,d=a.document;c=function(){var c,e,f,g,h,i,j,k,l,m,n,o,p,q={},r={},s=!1,t={ENTER:13,ESC:27,SPACE:32},u=[];return r={buttons:{holder:'',submit:'',ok:'',cancel:''},input:'
',message:'

{{message}}

',log:'
{{message}}
'},p=function(){var a,c,e=!1,f=d.createElement("fakeelement"),g={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"otransitionend",transition:"transitionend"};for(a in g)if(f.style[a]!==b){c=g[a],e=!0;break}return{type:c,supported:e}},c=function(a){return d.getElementById(a)},q={labels:{ok:"OK",cancel:"Cancel"},delay:5e3,buttonReverse:!1,buttonFocus:"ok",transition:b,addListeners:function(a){var b,c,i,j,k,l="undefined"!=typeof f,m="undefined"!=typeof e,n="undefined"!=typeof o,p="",q=this;b=function(b){return"undefined"!=typeof b.preventDefault&&b.preventDefault(),i(b),"undefined"!=typeof o&&(p=o.value),"function"==typeof a&&("undefined"!=typeof o?a(!0,p):a(!0)),!1},c=function(b){return"undefined"!=typeof b.preventDefault&&b.preventDefault(),i(b),"function"==typeof a&&a(!1),!1},i=function(){q.hide(),q.unbind(d.body,"keyup",j),q.unbind(g,"focus",k),l&&q.unbind(f,"click",b),m&&q.unbind(e,"click",c)},j=function(a){var d=a.keyCode;(d===t.SPACE&&!n||n&&d===t.ENTER)&&b(a),d===t.ESC&&m&&c(a)},k=function(){n?o.focus():!m||q.buttonReverse?f.focus():e.focus()},this.bind(g,"focus",k),this.bind(h,"focus",k),l&&this.bind(f,"click",b),m&&this.bind(e,"click",c),this.bind(d.body,"keyup",j),this.transition.supported||this.setFocus()},bind:function(a,b,c){"function"==typeof a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,c)},handleErrors:function(){if("undefined"!=typeof a.onerror){var b=this;return a.onerror=function(a,c,d){b.error("["+a+" on line "+d+" of "+c+"]",0)},!0}return!1},appendButtons:function(a,b){return this.buttonReverse?b+a:a+b},build:function(a){var b="",c=a.type,d=a.message,e=a.cssClass||"";switch(b+='
',b+='Reset Focus',"none"===q.buttonFocus&&(b+=''),"prompt"===c&&(b+='
'),b+='
',b+=r.message.replace("{{message}}",d),"prompt"===c&&(b+=r.input),b+=r.buttons.holder,b+="
","prompt"===c&&(b+="
"),b+='Reset Focus',b+="
",c){case"confirm":b=b.replace("{{buttons}}",this.appendButtons(r.buttons.cancel,r.buttons.ok)),b=b.replace("{{ok}}",this.labels.ok).replace("{{cancel}}",this.labels.cancel);break;case"prompt":b=b.replace("{{buttons}}",this.appendButtons(r.buttons.cancel,r.buttons.submit)),b=b.replace("{{ok}}",this.labels.ok).replace("{{cancel}}",this.labels.cancel);break;case"alert":b=b.replace("{{buttons}}",r.buttons.ok),b=b.replace("{{ok}}",this.labels.ok)}return l.className="alertify alertify-"+c+" "+e,k.className="alertify-cover",b},close:function(a,b){var c,d,e=b&&!isNaN(b)?+b:this.delay,f=this;this.bind(a,"click",function(){c(a)}),d=function(a){a.stopPropagation(),f.unbind(this,f.transition.type,d),m.removeChild(this),m.hasChildNodes()||(m.className+=" alertify-logs-hidden")},c=function(a){"undefined"!=typeof a&&a.parentNode===m&&(f.transition.supported?(f.bind(a,f.transition.type,d),a.className+=" alertify-log-hide"):(m.removeChild(a),m.hasChildNodes()||(m.className+=" alertify-logs-hidden")))},0!==b&&setTimeout(function(){c(a)},e)},dialog:function(a,b,c,e,f){j=d.activeElement;var g=function(){m&&null!==m.scrollTop&&k&&null!==k.scrollTop||g()};if("string"!=typeof a)throw new Error("message must be a string");if("string"!=typeof b)throw new Error("type must be a string");if("undefined"!=typeof c&&"function"!=typeof c)throw new Error("fn must be a function");return this.init(),g(),u.push({type:b,message:a,callback:c,placeholder:e,cssClass:f}),s||this.setup(),this},extend:function(a){if("string"!=typeof a)throw new Error("extend method must have exactly one paramter");return function(b,c){return this.log(b,a,c),this}},hide:function(){var a,b=this;u.splice(0,1),u.length>0?this.setup(!0):(s=!1,a=function(c){c.stopPropagation(),b.unbind(l,b.transition.type,a)},this.transition.supported?(this.bind(l,this.transition.type,a),l.className="alertify alertify-hide alertify-hidden"):l.className="alertify alertify-hide alertify-hidden alertify-isHidden",k.className="alertify-cover alertify-cover-hidden",j.focus())},init:function(){d.createElement("nav"),d.createElement("article"),d.createElement("section"),null==c("alertify-cover")&&(k=d.createElement("div"),k.setAttribute("id","alertify-cover"),k.className="alertify-cover alertify-cover-hidden",d.body.appendChild(k)),null==c("alertify")&&(s=!1,u=[],l=d.createElement("section"),l.setAttribute("id","alertify"),l.className="alertify alertify-hidden",d.body.appendChild(l)),null==c("alertify-logs")&&(m=d.createElement("section"),m.setAttribute("id","alertify-logs"),m.className="alertify-logs alertify-logs-hidden",d.body.appendChild(m)),d.body.setAttribute("tabindex","0"),this.transition=p()},log:function(a,b,c){var d=function(){m&&null!==m.scrollTop||d()};return this.init(),d(),m.className="alertify-logs",this.notify(a,b,c),this},notify:function(a,b,c){var e=d.createElement("article");e.className="alertify-log"+("string"==typeof b&&""!==b?" alertify-log-"+b:""),e.innerHTML=a,m.appendChild(e),setTimeout(function(){e.className=e.className+" alertify-log-show"},50),this.close(e,c)},set:function(a){var b;if("object"!=typeof a&&a instanceof Array)throw new Error("args must be an object");for(b in a)a.hasOwnProperty(b)&&(this[b]=a[b])},setFocus:function(){o?(o.focus(),o.select()):i.focus()},setup:function(a){var d,j=u[0],k=this;s=!0,d=function(a){a.stopPropagation(),k.setFocus(),k.unbind(l,k.transition.type,d)},this.transition.supported&&!a&&this.bind(l,this.transition.type,d),l.innerHTML=this.build(j),g=c("alertify-resetFocus"),h=c("alertify-resetFocusBack"),f=c("alertify-ok")||b,e=c("alertify-cancel")||b,i="cancel"===q.buttonFocus?e:"none"===q.buttonFocus?c("alertify-noneFocus"):f,o=c("alertify-text")||b,n=c("alertify-form")||b,"string"==typeof j.placeholder&&""!==j.placeholder&&(o.value=j.placeholder),a&&this.setFocus(),this.addListeners(j.callback)},unbind:function(a,b,c){"function"==typeof a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent&&a.detachEvent("on"+b,c)}},{alert:function(a,b,c){return q.dialog(a,"alert",b,"",c),this},confirm:function(a,b,c){return q.dialog(a,"confirm",b,"",c),this},extend:q.extend,init:q.init,log:function(a,b,c){return q.log(a,b,c),this},prompt:function(a,b,c,d){return q.dialog(a,"prompt",b,c,d),this},success:function(a,b){return q.log(a,"success",b),this},error:function(a,b){return q.log(a,"error",b),this},set:function(a){q.set(a)},labels:q.labels,debug:q.handleErrors}},"function"==typeof define?define([],function(){return new c}):"undefined"==typeof a.alertify&&(a.alertify=new c)}(this); \ No newline at end of file +!function(a,b){"use strict";var d,c=a.document;d=function(){var i,j,k,l,m,n,o,p,q,r,s,t,u,v,d={},e={},f=!1,g={ENTER:13,ESC:27,SPACE:32},h=[];return e={buttons:{holder:'',submit:'',ok:'',cancel:''},input:'
',message:'

{{message}}

',log:'
{{message}}
'},u=function(){var a,d,e=!1,f=c.createElement("fakeelement"),g={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"otransitionend",transition:"transitionend"};for(a in g)if(f.style[a]!==b){d=g[a],e=!0;break}return{type:d,supported:e}},i=function(a){return c.getElementById(a)},d={labels:{ok:"OK",cancel:"Cancel"},delay:5e3,buttonReverse:!1,buttonFocus:"ok",transition:b,addListeners:function(a){var i,n,o,p,q,b="undefined"!=typeof k,d="undefined"!=typeof j,e="undefined"!=typeof t,f="",h=this;i=function(b){return"undefined"!=typeof b.preventDefault&&b.preventDefault(),o(b),"undefined"!=typeof t&&(f=t.value),"function"==typeof a&&("undefined"!=typeof t?a(!0,f):a(!0)),!1},n=function(b){return"undefined"!=typeof b.preventDefault&&b.preventDefault(),o(b),"function"==typeof a&&a(!1),!1},o=function(a){h.hide(),h.unbind(c.body,"keyup",p),h.unbind(l,"focus",q),b&&h.unbind(k,"click",i),d&&h.unbind(j,"click",n)},p=function(a){var b=a.keyCode;(b===g.SPACE&&!e||e&&b===g.ENTER)&&i(a),b===g.ESC&&d&&n(a)},q=function(a){e?t.focus():!d||h.buttonReverse?k.focus():j.focus()},this.bind(l,"focus",q),this.bind(m,"focus",q),b&&this.bind(k,"click",i),d&&this.bind(j,"click",n),this.bind(c.body,"keyup",p),this.transition.supported||this.setFocus()},bind:function(a,b,c){"function"==typeof a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,c)},handleErrors:function(){if("undefined"!=typeof a.onerror){var b=this;return a.onerror=function(a,c,d){b.error("["+a+" on line "+d+" of "+c+"]",0)},!0}return!1},appendButtons:function(a,b){return this.buttonReverse?b+a:a+b},build:function(a){var b="",c=a.type,f=a.message,g=a.cssClass||"";switch(b+='
',b+='Reset Focus',"none"===d.buttonFocus&&(b+=''),"prompt"===c&&(b+='
'),b+='
',b+=e.message.replace("{{message}}",f),"prompt"===c&&(b+=e.input),b+=e.buttons.holder,b+="
","prompt"===c&&(b+="
"),b+='Reset Focus',b+="
",c){case"confirm":b=b.replace("{{buttons}}",this.appendButtons(e.buttons.cancel,e.buttons.ok)),b=b.replace("{{ok}}",this.labels.ok).replace("{{cancel}}",this.labels.cancel);break;case"prompt":b=b.replace("{{buttons}}",this.appendButtons(e.buttons.cancel,e.buttons.submit)),b=b.replace("{{ok}}",this.labels.ok).replace("{{cancel}}",this.labels.cancel);break;case"alert":b=b.replace("{{buttons}}",e.buttons.ok),b=b.replace("{{ok}}",this.labels.ok)}return q.className="alertify alertify-"+c+" "+g,p.className="alertify-cover",b},close:function(a,b,c){var f,g,d=b&&!isNaN(b)?+b:this.delay,e=this;c&&this.bind(a,"click",function(){f(a)}),g=function(a){a.stopPropagation(),e.unbind(this,e.transition.type,g),r.removeChild(this),r.hasChildNodes()||(r.className+=" alertify-logs-hidden")},f=function(a){"undefined"!=typeof a&&a.parentNode===r&&(e.transition.supported?(e.bind(a,e.transition.type,g),a.className+=" alertify-log-hide"):(r.removeChild(a),r.hasChildNodes()||(r.className+=" alertify-logs-hidden")))},0!==b&&setTimeout(function(){f(a)},d)},closeNow:function(a){var d,e,b=this;e=function(a){a.stopPropagation(),b.unbind(this,b.transition.type,e),r.removeChild(this),r.hasChildNodes()||(r.className+=" alertify-logs-hidden")},d=function(a){"undefined"!=typeof a&&a.parentNode===r&&(b.transition.supported?(b.bind(a,b.transition.type,e),a.className+=" alertify-log-hide"):(r.removeChild(a),r.hasChildNodes()||(r.className+=" alertify-logs-hidden")))};for(var f=c.getElementsByClassName("alertify-log-"+a),g=0;g0?this.setup(!0):(f=!1,a=function(c){c.stopPropagation(),b.unbind(q,b.transition.type,a)},this.transition.supported?(this.bind(q,this.transition.type,a),q.className="alertify alertify-hide alertify-hidden"):q.className="alertify alertify-hide alertify-hidden alertify-isHidden",p.className="alertify-cover alertify-cover-hidden",o.focus())},init:function(){c.createElement("nav"),c.createElement("article"),c.createElement("section"),null==i("alertify-cover")&&(p=c.createElement("div"),p.setAttribute("id","alertify-cover"),p.className="alertify-cover alertify-cover-hidden",c.body.appendChild(p)),null==i("alertify")&&(f=!1,h=[],q=c.createElement("section"),q.setAttribute("id","alertify"),q.className="alertify alertify-hidden",c.body.appendChild(q)),null==i("alertify-logs")&&(r=c.createElement("section"),r.setAttribute("id","alertify-logs"),r.className="alertify-logs alertify-logs-hidden",c.body.appendChild(r)),c.body.setAttribute("tabindex","0"),this.transition=u()},log:function(a,c,d,e){e=e===b||e;var f=function(){r&&null!==r.scrollTop||f()};return this.init(),f(),r.className="alertify-logs",this.notify(a,c,d,e),this},notify:function(a,b,d,e){v=Math.floor(100*Math.random()+1);var f=c.createElement("article");f.className="alertify-log"+("string"==typeof b&&""!==b?" alertify-log-"+b:" alertify-log-"+v),f.innerHTML=a,r.appendChild(f),setTimeout(function(){f.className=f.className+" alertify-log-show"},50),this.close(f,d,e)},set:function(a){var b;if("object"!=typeof a&&a instanceof Array)throw new Error("args must be an object");for(b in a)a.hasOwnProperty(b)&&(this[b]=a[b])},setFocus:function(){t?(t.focus(),t.select()):n.focus()},setup:function(a){var g,c=h[0],e=this;f=!0,g=function(a){a.stopPropagation(),e.setFocus(),e.unbind(q,e.transition.type,g)},this.transition.supported&&!a&&this.bind(q,this.transition.type,g),q.innerHTML=this.build(c),l=i("alertify-resetFocus"),m=i("alertify-resetFocusBack"),k=i("alertify-ok")||b,j=i("alertify-cancel")||b,n="cancel"===d.buttonFocus?j:"none"===d.buttonFocus?i("alertify-noneFocus"):k,t=i("alertify-text")||b,s=i("alertify-form")||b,"string"==typeof c.placeholder&&""!==c.placeholder&&(t.value=c.placeholder),a&&this.setFocus(),this.addListeners(c.callback)},unbind:function(a,b,c){"function"==typeof a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent&&a.detachEvent("on"+b,c)}},{extend:d.extend,init:d.init,labels:d.labels,debug:d.handleErrors,alert:function(a,b,c){return d.dialog(a,"alert",b,"",c),this},confirm:function(a,b,c){return d.dialog(a,"confirm",b,"",c),this},prompt:function(a,b,c,e){return d.dialog(a,"prompt",b,c,e),this},log:function(a,b,c,e){return d.log(a,b,c,e),v},success:function(a,b,c){return d.log(a,"success",b,c),v},error:function(a,b,c){return d.log(a,"error",b,c),v},set:function(a){d.set(a)},closeNow:function(a){d.closeNow(a)},closeAll:function(){d.closeNow("show")}}},"function"==typeof define?define([],function(){return new d}):"undefined"==typeof a.alertify&&(a.alertify=new d)}(this); \ No newline at end of file