/public/assets-dev/js/2.alertify/alertify.js

https://github.com/lixiaokai2008/nested-sets-laravel-jquery · JavaScript · 636 lines · 351 code · 54 blank · 231 comment · 90 complexity · a968bbbb314368147fa922aae7b64de4 MD5 · raw file

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