1/** 2 * alertify 3 * An unobtrusive customizable JavaScript notification system 4 * 5 * @author Fabien Doiron <[email protected]> 6 * @copyright Fabien Doiron 2013 7 * @license MIT <http://opensource.org/licenses/mit-license.php> 8 * @link http://fabien-d.github.com/alertify.js/ 9 * @module alertify 10 * @version 0.3.11 11 */ 12(function (global, undefined) { 13 "use strict"; 14 15 var document = global.document, 16 Alertify; 17 18 Alertify = function () { 19 20 var _alertify = {}, 21 dialogs = {}, 22 isopen = false, 23 keys = { ENTER: 13, ESC: 27, SPACE: 32 }, 24 queue = [], 25 $, btnCancel, btnOK, btnReset, btnResetBack, btnFocus, elCallee, elCover, elDialog, elLog, form, input, getTransitionEvent; 26 27 /** 28 * Markup pieces 29 * @type {Object} 30 */ 31 dialogs = { 32 buttons : { 33 holder : "<nav class=\"alertify-buttons\">{{buttons}}</nav>", 34 submit : "<button type=\"submit\" class=\"alertify-button alertify-button-ok\" id=\"alertify-ok\">{{ok}}</button>", 35 ok : "<button class=\"alertify-button alertify-button-ok\" id=\"alertify-ok\">{{ok}}</button>", 36 cancel : "<button class=\"alertify-button alertify-button-cancel\" id=\"alertify-cancel\">{{cancel}}</button>" 37 }, 38 input : "<div class=\"alertify-text-wrapper\"><input type=\"text\" class=\"alertify-text\" id=\"alertify-text\"></div>", 39 message : "<p class=\"alertify-message\">{{message}}</p>", 40 log : "<article class=\"alertify-log{{class}}\">{{message}}</article>" 41 }; 42 43 /** 44 * Return the proper transitionend event 45 * @return {String} Transition type string 46 */ 47 getTransitionEvent = function () { 48 var t, 49 type, 50 supported = false, 51 el = document.createElement("fakeelement"), 52 transitions = { 53 "WebkitTransition" : "webkitTransitionEnd", 54 "MozTransition" : "transitionend", 55 "OTransition" : "otransitionend", 56 "transition" : "transitionend" 57 }; 58 59 for (t in transitions) { 60 if (el.style[t] !== undefined) { 61 type = transitions[t]; 62 supported = true; 63 break; 64 } 65 } 66 67 return { 68 type : type, 69 supported : supported 70 }; 71 }; 72 73 /** 74 * Shorthand for document.getElementById() 75 * 76 * @param {String} id A specific element ID 77 * @return {Object} HTML element 78 */ 79 $ = function (id) { 80 return document.getElementById(id); 81 }; 82 83 /** 84 * Alertify private object 85 * @type {Object} 86 */ 87 _alertify = { 88 89 /** 90 * Labels object 91 * @type {Object} 92 */ 93 labels : { 94 ok : "OK", 95 cancel : "Cancel" 96 }, 97 98 /** 99 * Delay number 100 * @type {Number} 101 */ 102 delay : 5000, 103 104 /** 105 * Whether buttons are reversed (default is secondary/primary) 106 * @type {Boolean} 107 */ 108 buttonReverse : false, 109 110 /** 111 * Which button should be focused by default 112 * @type {String} "ok" (default), "cancel", or "none" 113 */ 114 buttonFocus : "ok", 115 116 /** 117 * Set the transition event on load 118 * @type {[type]} 119 */ 120 transition : undefined, 121 122 /** 123 * Set the proper button click events 124 * 125 * @param {Function} fn [Optional] Callback function 126 * 127 * @return {undefined} 128 */ 129 addListeners : function (fn) { 130 var hasOK = (typeof btnOK !== "undefined"), 131 hasCancel = (typeof btnCancel !== "undefined"), 132 hasInput = (typeof input !== "undefined"), 133 val = "", 134 self = this, 135 ok, cancel, common, key, reset; 136 137 // ok event handler 138 ok = function (event) { 139 if (typeof event.preventDefault !== "undefined") event.preventDefault(); 140 common(event); 141 if (typeof input !== "undefined") val = input.value; 142 if (typeof fn === "function") { 143 if (typeof input !== "undefined") { 144 fn(true, val); 145 } 146 else fn(true); 147 } 148 return false; 149 }; 150 151 // cancel event handler 152 cancel = function (event) { 153 if (typeof event.preventDefault !== "undefined") event.preventDefault(); 154 common(event); 155 if (typeof fn === "function") fn(false); 156 return false; 157 }; 158 159 // common event handler (keyup, ok and cancel) 160 common = function (event) { 161 self.hide(); 162 self.unbind(document.body, "keyup", key);
163 self.unbind(btnReset, "focus", reset); 164 if (hasOK) self.unbind(btnOK, "click", ok); 165 if (hasCancel) self.unbind(btnCancel, "click", cancel); 166 }; 167 168 // keyup handler 169 key = function (event) { 170 var keyCode = event.keyCode; 171 if ((keyCode === keys.SPACE && !hasInput) || (hasInput && keyCode === keys.ENTER)) ok(event); 172 if (keyCode === keys.ESC && hasCancel) cancel(event); 173 }; 174 175 // reset focus to first item in the dialog 176 reset = function (event) { 177 if (hasInput) input.focus(); 178 else if (!hasCancel || self.buttonReverse) btnOK.focus(); 179 else btnCancel.focus(); 180 }; 181 182 // handle reset focus link 183 // this ensures that the keyboard focus does not 184 // ever leave the dialog box until an action has 185 // been taken 186 this.bind(btnReset, "focus", reset); 187 this.bind(btnResetBack, "focus", reset); 188 // handle OK click 189 if (hasOK) this.bind(btnOK, "click", ok); 190 // handle Cancel click 191 if (hasCancel) this.bind(btnCancel, "click", cancel); 192 // listen for keys, Cancel => ESC 193 this.bind(document.body, "keyup", key); 194 if (!this.transition.supported) { 195 this.setFocus(); 196 } 197 }, 198 199 /** 200 * Bind events to elements 201 * 202 * @param {Object} el HTML Object 203 * @param {Event} event Event to attach to element 204 * @param {Function} fn Callback function 205 * 206 * @return {undefined} 207 */ 208 bind : function (el, event, fn) { 209 if (typeof el.addEventListener === "function") { 210 el.addEventListener(event, fn, false); 211 } else if (el.attachEvent) { 212 el.attachEvent("on" + event, fn); 213 } 214 }, 215 216 /** 217 * Use alertify as the global error handler (using window.onerror) 218 * 219 * @return {boolean} success 220 */ 221 handleErrors : function () { 222 if (typeof global.onerror !== "undefined") { 223 var self = this; 224 global.onerror = function (msg, url, line) { 225 self.error("[" + msg + " on line " + line + " of " + url + "]", 0); 226 }; 227 return true; 228 } else { 229 return false; 230 } 231 }, 232 233 /** 234 * Append button HTML strings 235 * 236 * @param {String} secondary The secondary button HTML string 237 * @param {String} primary The primary button HTML string 238 * 239 * @return {String} The appended button HTML strings 240 */ 241 appendButtons : function (secondary, primary) { 242 return this.buttonReverse ? primary + secondary : secondary + primary; 243 }, 244 245 /** 246 * Build the proper message box 247 * 248 * @param {Object} item Current object in the queue 249 * 250 * @return {String} An HTML string of the message box 251 */ 252 build : function (item) { 253 var html = "", 254 type = item.type, 255 message = item.message, 256 css = item.cssClass || ""; 257 258 html += "<div class=\"alertify-dialog\">"; 259 html += "<a id=\"alertify-resetFocusBack\" class=\"alertify-resetFocus\" href=\"#\">Reset Focus</a>"; 260 261 if (_alertify.buttonFocus === "none") html += "<a href=\"#\" id=\"alertify-noneFocus\" class=\"alertify-hidden\"></a>"; 262 263 // doens't require an actual form 264 if (type === "prompt") html += "<div id=\"alertify-form\">"; 265 266 html += "<article class=\"alertify-inner\">"; 267 html += dialogs.message.replace("{{message}}", message); 268 269 if (type === "prompt") html += dialogs.input; 270 271 html += dialogs.buttons.holder; 272 html += "</article>"; 273 274 if (type === "prompt") html += "</div>"; 275 276 html += "<a id=\"alertify-resetFocus\" class=\"alertify-resetFocus\" href=\"#\">Reset Focus</a>"; 277 html += "</div>"; 278 279 switch (type) { 280 case "confirm": 281 html = html.replace("{{buttons}}", this.appendButtons(dialogs.buttons.cancel, dialogs.buttons.ok)); 282 html = html.replace("{{ok}}", this.labels.ok).replace("{{cancel}}", this.labels.cancel); 283 break; 284 case "prompt": 285 html = html.replace("{{buttons}}", this.appendButtons(dialogs.buttons.cancel, dialogs.buttons.submit)); 286 html = html.replace("{{ok}}", this.labels.ok).replace("{{cancel}}", this.labels.cancel); 287 break; 288 case "alert": 289 html = html.replace("{{buttons}}", dialogs.buttons.ok); 290 html = html.replace("{{ok}}", this.labels.ok); 291 break; 292 default: 293 break; 294 } 295 296 elDialog.className = "alertify alertify-" + type + " " + css; 297 elCover.className = "alertify-cover"; 298 return html; 299 }, 300 301 /** 302 * Close the log messages 303 * 304 * @param {Object} elem HTML Element of log message to close 305 * @param {Number} wait [optional] Time (in ms) to wait before automatically hiding the message, if 0 never hide 306 * 307 * @return {undefined} 308 */ 309 close : function (elem, wait) { 310 // Unary Plus: +"2" === 2 311 var timer = (wait && !isNaN(wait)) ? +wait : this.delay, 312 self = this, 313 hideElement, transitionDone; 314 315 // set click event on log messages 316 this.bind(elem, "click", function () { 317 hideElement(elem); 318 }); 319 // Hide the dialog box after transition 320 // This ensure it doens't block any element from being clicked 321 transitionDone = function (event) { 322 event.stopPropagation(); 323 // unbind event so function only gets called once 324 self.unbind(this, self.transition.type, transitionDone); 325 // remove log message 326 elLog.removeChild(this);
327 if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden"; 328 }; 329 // this sets the hide class to transition out 330 // or removes the child if css transitions aren't supported 331 hideElement = function (el) { 332 // ensure element exists 333 if (typeof el !== "undefined" && el.parentNode === elLog) { 334 // whether CSS transition exists 335 if (self.transition.supported) { 336 self.bind(el, self.transition.type, transitionDone); 337 el.className += " alertify-log-hide"; 338 } else { 339 elLog.removeChild(el); 340 if (!elLog.hasChildNodes()) elLog.className += " alertify-logs-hidden"; 341 } 342 } 343 }; 344 // never close (until click) if wait is set to 0 345 if (wait === 0) return; 346 // set timeout to auto close the log message 347 setTimeout(function () { hideElement(elem); }, timer); 348 }, 349 350 /** 351 * Create a dialog box 352 * 353 * @param {String} message The message passed from the callee 354 * @param {String} type Type of dialog to create 355 * @param {Function} fn [Optional] Callback function 356 * @param {String} placeholder [Optional] Default value for prompt input field 357 * @param {String} cssClass [Optional] Class(es) to append to dialog box 358 * 359 * @return {Object} 360 */ 361 dialog : function (message, type, fn, placeholder, cssClass) { 362 // set the current active element 363 // this allows the keyboard focus to be resetted 364 // after the dialog box is closed 365 elCallee = document.activeElement; 366 // check to ensure the alertify dialog element 367 // has been successfully created 368 var check = function () { 369 if ((elLog && elLog.scrollTop !== null) && (elCover && elCover.scrollTop !== null)) return; 370 else check(); 371 }; 372 // error catching 373 if (typeof message !== "string") throw new Error("message must be a string"); 374 if (typeof type !== "string") throw new Error("type must be a string"); 375 if (typeof fn !== "undefined" && typeof fn !== "function") throw new Error("fn must be a function"); 376 // initialize alertify if it hasn't already been done 377 this.init(); 378 check(); 379 380 queue.push({ type: type, message: message, callback: fn, placeholder: placeholder, cssClass: cssClass }); 381 if (!isopen) this.setup(); 382 383 return this; 384 }, 385 386 /** 387 * Extend the log method to create custom methods 388 * 389 * @param {String} type Custom method name 390 * 391 * @return {Function} 392 */ 393 extend : function (type) { 394 if (typeof type !== "string") throw new Error("extend method must have exactly one paramter"); 395 return function (message, wait) { 396 this.log(message, type, wait); 397 return this; 398 }; 399 }, 400 401 /** 402 * Hide the dialog and rest to defaults 403 * 404 * @return {undefined} 405 */ 406 hide : function () { 407 var transitionDone, 408 self = this; 409 // remove reference from queue 410 queue.splice(0,1); 411 // if items remaining in the queue 412 if (queue.length > 0) this.setup(true); 413 else { 414 isopen = false; 415 // Hide the dialog box after transition 416 // This ensure it doens't block any element from being clicked 417 transitionDone = function (event) { 418 event.stopPropagation(); 419 // unbind event so function only gets called once 420 self.unbind(elDialog, self.transition.type, transitionDone); 421 }; 422 // whether CSS transition exists 423 if (this.transition.supported) { 424 this.bind(elDialog, this.transition.type, transitionDone); 425 elDialog.className = "alertify alertify-hide alertify-hidden"; 426 } else { 427 elDialog.className = "alertify alertify-hide alertify-hidden alertify-isHidden"; 428 } 429 elCover.className = "alertify-cover alertify-cover-hidden"; 430 // set focus to the last element or body 431 // after the dialog is closed 432 elCallee.focus(); 433 } 434 }, 435 436 /** 437 * Initialize Alertify 438 * Create the 2 main elements 439 * 440 * @return {undefined} 441 */ 442 init : function () { 443 // ensure legacy browsers support html5 tags 444 document.createElement("nav"); 445 document.createElement("article"); 446 document.createElement("section"); 447 // cover
448 if ($("alertify-cover") == null) { 449 elCover = document.createElement("div"); 450 elCover.setAttribute("id", "alertify-cover"); 451 elCover.className = "alertify-cover alertify-cover-hidden"; 452 document.body.appendChild(elCover); 453 } 454 // main element 455 if ($("alertify") == null) { 456 isopen = false; 457 queue = []; 458 elDialog = document.createElement("section"); 459 elDialog.setAttribute("id", "alertify"); 460 elDialog.className = "alertify alertify-hidden"; 461 document.body.appendChild(elDialog); 462 } 463 // log element 464 if ($("alertify-logs") == null) { 465 elLog = document.createElement("section"); 466 elLog.setAttribute("id", "alertify-logs"); 467 elLog.className = "alertify-logs alertify-logs-hidden"; 468 document.body.appendChild(elLog); 469 } 470 // set tabindex attribute on body element 471 // this allows script to give it focus 472 // after the dialog is closed 473 document.body.setAttribute("tabindex", "0"); 474 // set transition type 475 this.transition = getTransitionEvent(); 476 }, 477 478 /** 479 * Show a new log message box 480 * 481 * @param {String} message The message passed from the callee 482 * @param {String} type [Optional] Optional type of log message 483 * @param {Number} wait [Optional] Time (in ms) to wait before auto-hiding the log 484 * 485 * @return {Object} 486 */ 487 log : function (message, type, wait) { 488 // check to ensure the alertify dialog element 489 // has been successfully created 490 var check = function () { 491 if (elLog && elLog.scrollTop !== null) return; 492 else check(); 493 }; 494 // initialize alertify if it hasn't already been done 495 this.init(); 496 check(); 497 498 elLog.className = "alertify-logs"; 499 this.notify(message, type, wait); 500 return this; 501 }, 502 503 /** 504 * Add new log message 505 * If a type is passed, a class name "alertify-log-{type}" will get added. 506 * This allows for custom look and feel for various types of notifications. 507 * 508 * @param {String} message The message passed from the callee 509 * @param {String} type [Optional] Type of log message 510 * @param {Number} wait [Optional] Time (in ms) to wait before auto-hiding 511 * 512 * @return {undefined} 513 */ 514 notify : function (message, type, wait) { 515 var log = document.createElement("article"); 516 log.className = "alertify-log" + ((typeof type === "string" && type !== "") ? " alertify-log-" + type : ""); 517 log.innerHTML = message; 518 // append child 519 elLog.appendChild(log); 520 // triggers the CSS animation 521 setTimeout(function() { log.className = log.className + " alertify-log-show"; }, 50); 522 this.close(log, wait); 523 }, 524 525 /** 526 * Set properties 527 * 528 * @param {Object} args Passing parameters 529 * 530 * @return {undefined} 531 */ 532 set : function (args) { 533 var k; 534 // error catching 535 if (typeof args !== "object" && args instanceof Array) throw new Error("args must be an object"); 536 // set parameters 537 for (k in args) { 538 if (args.hasOwnProperty(k)) { 539 this[k] = args[k]; 540 } 541 } 542 }, 543 544 /** 545 * Common place to set focus to proper element 546 * 547 * @return {undefined} 548 */ 549 setFocus : function () { 550 if (input) { 551 input.focus(); 552 input.select(); 553 } 554 else btnFocus.focus(); 555 }, 556 557 /** 558 * Initiate all the required pieces for the dialog box 559 * 560 * @return {undefined} 561 */ 562 setup : function (fromQueue) { 563 var item = queue[0], 564 self = this, 565 transitionDone; 566 567 // dialog is open 568 isopen = true; 569 // Set button focus after transition 570 transitionDone = function (event) { 571 event.stopPropagation(); 572 self.setFocus(); 573 // unbind event so function only gets called once 574 self.unbind(elDialog, self.transition.type, transitionDone); 575 }; 576 // whether CSS transition exists 577 if (this.transition.supported && !fromQueue) { 578 this.bind(elDialog, this.transition.type, transitionDone); 579 } 580 // build the proper dialog HTML 581 elDialog.innerHTML = this.build(item); 582 // assign all the common elements 583 btnReset = $("alertify-resetFocus"); 584 btnResetBack = $("alertify-resetFocusBack"); 585 btnOK = $("alertify-ok") || undefined; 586 btnCancel = $("alertify-cancel") || undefined; 587 btnFocus = (_alertify.buttonFocus === "cancel") ? btnCancel : ((_alertify.buttonFocus === "none") ? $("alertify-noneFocus") : btnOK), 588 input = $("alertify-text") || undefined; 589 form = $("alertify-form") || undefined; 590 // add placeholder value to the input field 591 if (typeof item.placeholder === "string" && item.placeholder !== "") input.value = item.placeholder; 592 if (fromQueue) this.setFocus(); 593 this.addListeners(item.callback); 594 }, 595 596 /** 597 * Unbind events to elements 598 * 599 * @param {Object} el HTML Object 600 * @param {Event} event Event to detach to element 601 * @param {Function} fn Callback function 602 * 603 * @return {undefined} 604 */ 605 unbind : function (el, event, fn) { 606 if (typeof el.removeEventListener === "function") { 607 el.removeEventListener(event, fn, false); 608 } else if (el.detachEvent) { 609 el.detachEvent("on" + event, fn); 610 } 611 } 612 }; 613 614 return { 615 alert : function (message, fn, cssClass) { _alertify.dialog(message, "alert", fn, "", cssClass); return this; },
616 confirm : function (message, fn, cssClass) { _alertify.dialog(message, "confirm", fn, "", cssClass); return this; }, 617 extend : _alertify.extend, 618 init : _alertify.init, 619 log : function (message, type, wait) { _alertify.log(message, type, wait); return this; }, 620 prompt : function (message, fn, placeholder, cssClass) { _alertify.dialog(message, "prompt", fn, placeholder, cssClass); return this; }, 621 success : function (message, wait) { _alertify.log(message, "success", wait); return this; }, 622 error : function (message, wait) { _alertify.log(message, "error", wait); return this; }, 623 set : function (args) { _alertify.set(args); }, 624 labels : _alertify.labels, 625 debug : _alertify.handleErrors 626 }; 627 }; 628 629 // AMD and window support 630 if (typeof define === "function") { 631 define([], function () { return new Alertify(); }); 632 } else if (typeof global.alertify === "undefined") { 633 global.alertify = new Alertify(); 634 } 635 636}(this));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.