You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3609 lines
137 KiB

  1. /**
  2. * alertifyjs 1.11.4 http://alertifyjs.com
  3. * AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
  4. * Copyright 2019 Mohammad Younes <Mohammad@alertifyjs.com> (http://alertifyjs.com)
  5. * Licensed under GPL 3 <https://opensource.org/licenses/gpl-3.0>*/
  6. ( function ( window ) {
  7. 'use strict';
  8. /**
  9. * Keys enum
  10. * @type {Object}
  11. */
  12. var keys = {
  13. ENTER: 13,
  14. ESC: 27,
  15. F1: 112,
  16. F12: 123,
  17. LEFT: 37,
  18. RIGHT: 39
  19. };
  20. /**
  21. * Default options
  22. * @type {Object}
  23. */
  24. var defaults = {
  25. autoReset:true,
  26. basic:false,
  27. closable:true,
  28. closableByDimmer:true,
  29. frameless:false,
  30. maintainFocus:true, //global default not per instance, applies to all dialogs
  31. maximizable:true,
  32. modal:true,
  33. movable:true,
  34. moveBounded:false,
  35. overflow:true,
  36. padding: true,
  37. pinnable:true,
  38. pinned:true,
  39. preventBodyShift:false, //global default not per instance, applies to all dialogs
  40. resizable:true,
  41. startMaximized:false,
  42. transition:'pulse',
  43. notifier:{
  44. delay:5,
  45. position:'bottom-right',
  46. closeButton:false
  47. },
  48. glossary:{
  49. title:'AlertifyJS',
  50. ok: 'OK',
  51. cancel: 'Cancel',
  52. acccpt: 'Accept',
  53. deny: 'Deny',
  54. confirm: 'Confirm',
  55. decline: 'Decline',
  56. close: 'Close',
  57. maximize: 'Maximize',
  58. restore: 'Restore',
  59. },
  60. theme:{
  61. input:'ajs-input',
  62. ok:'ajs-ok',
  63. cancel:'ajs-cancel',
  64. }
  65. };
  66. //holds open dialogs instances
  67. var openDialogs = [];
  68. /**
  69. * [Helper] Adds the specified class(es) to the element.
  70. *
  71. * @element {node} The element
  72. * @className {string} One or more space-separated classes to be added to the class attribute of the element.
  73. *
  74. * @return {undefined}
  75. */
  76. function addClass(element,classNames){
  77. element.className += ' ' + classNames;
  78. }
  79. /**
  80. * [Helper] Removes the specified class(es) from the element.
  81. *
  82. * @element {node} The element
  83. * @className {string} One or more space-separated classes to be removed from the class attribute of the element.
  84. *
  85. * @return {undefined}
  86. */
  87. function removeClass(element, classNames) {
  88. var original = element.className.split(' ');
  89. var toBeRemoved = classNames.split(' ');
  90. for (var x = 0; x < toBeRemoved.length; x += 1) {
  91. var index = original.indexOf(toBeRemoved[x]);
  92. if (index > -1){
  93. original.splice(index,1);
  94. }
  95. }
  96. element.className = original.join(' ');
  97. }
  98. /**
  99. * [Helper] Checks if the document is RTL
  100. *
  101. * @return {Boolean} True if the document is RTL, false otherwise.
  102. */
  103. function isRightToLeft(){
  104. return window.getComputedStyle(document.body).direction === 'rtl';
  105. }
  106. /**
  107. * [Helper] Get the document current scrollTop
  108. *
  109. * @return {Number} current document scrollTop value
  110. */
  111. function getScrollTop(){
  112. return ((document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop);
  113. }
  114. /**
  115. * [Helper] Get the document current scrollLeft
  116. *
  117. * @return {Number} current document scrollLeft value
  118. */
  119. function getScrollLeft(){
  120. return ((document.documentElement && document.documentElement.scrollLeft) || document.body.scrollLeft);
  121. }
  122. /**
  123. * Helper: clear contents
  124. *
  125. */
  126. function clearContents(element){
  127. while (element.lastChild) {
  128. element.removeChild(element.lastChild);
  129. }
  130. }
  131. /**
  132. * Extends a given prototype by merging properties from base into sub.
  133. *
  134. * @sub {Object} sub The prototype being overwritten.
  135. * @base {Object} base The prototype being written.
  136. *
  137. * @return {Object} The extended prototype.
  138. */
  139. function copy(src) {
  140. if(null === src){
  141. return src;
  142. }
  143. var cpy;
  144. if(Array.isArray(src)){
  145. cpy = [];
  146. for(var x=0;x<src.length;x+=1){
  147. cpy.push(copy(src[x]));
  148. }
  149. return cpy;
  150. }
  151. if(src instanceof Date){
  152. return new Date(src.getTime());
  153. }
  154. if(src instanceof RegExp){
  155. cpy = new RegExp(src.source);
  156. cpy.global = src.global;
  157. cpy.ignoreCase = src.ignoreCase;
  158. cpy.multiline = src.multiline;
  159. cpy.lastIndex = src.lastIndex;
  160. return cpy;
  161. }
  162. if(typeof src === 'object'){
  163. cpy = {};
  164. // copy dialog pototype over definition.
  165. for (var prop in src) {
  166. if (src.hasOwnProperty(prop)) {
  167. cpy[prop] = copy(src[prop]);
  168. }
  169. }
  170. return cpy;
  171. }
  172. return src;
  173. }
  174. /**
  175. * Helper: destruct the dialog
  176. *
  177. */
  178. function destruct(instance, initialize){
  179. if(instance.elements){
  180. //delete the dom and it's references.
  181. var root = instance.elements.root;
  182. root.parentNode.removeChild(root);
  183. delete instance.elements;
  184. //copy back initial settings.
  185. instance.settings = copy(instance.__settings);
  186. //re-reference init function.
  187. instance.__init = initialize;
  188. //delete __internal variable to allow re-initialization.
  189. delete instance.__internal;
  190. }
  191. }
  192. /**
  193. * Use a closure to return proper event listener method. Try to use
  194. * `addEventListener` by default but fallback to `attachEvent` for
  195. * unsupported browser. The closure simply ensures that the test doesn't
  196. * happen every time the method is called.
  197. *
  198. * @param {Node} el Node element
  199. * @param {String} event Event type
  200. * @param {Function} fn Callback of event
  201. * @return {Function}
  202. */
  203. var on = (function () {
  204. if (document.addEventListener) {
  205. return function (el, event, fn, useCapture) {
  206. el.addEventListener(event, fn, useCapture === true);
  207. };
  208. } else if (document.attachEvent) {
  209. return function (el, event, fn) {
  210. el.attachEvent('on' + event, fn);
  211. };
  212. }
  213. }());
  214. /**
  215. * Use a closure to return proper event listener method. Try to use
  216. * `removeEventListener` by default but fallback to `detachEvent` for
  217. * unsupported browser. The closure simply ensures that the test doesn't
  218. * happen every time the method is called.
  219. *
  220. * @param {Node} el Node element
  221. * @param {String} event Event type
  222. * @param {Function} fn Callback of event
  223. * @return {Function}
  224. */
  225. var off = (function () {
  226. if (document.removeEventListener) {
  227. return function (el, event, fn, useCapture) {
  228. el.removeEventListener(event, fn, useCapture === true);
  229. };
  230. } else if (document.detachEvent) {
  231. return function (el, event, fn) {
  232. el.detachEvent('on' + event, fn);
  233. };
  234. }
  235. }());
  236. /**
  237. * Prevent default event from firing
  238. *
  239. * @param {Event} event Event object
  240. * @return {undefined}
  241. function prevent ( event ) {
  242. if ( event ) {
  243. if ( event.preventDefault ) {
  244. event.preventDefault();
  245. } else {
  246. event.returnValue = false;
  247. }
  248. }
  249. }
  250. */
  251. var transition = (function () {
  252. var t, type;
  253. var supported = false;
  254. var transitions = {
  255. 'animation' : 'animationend',
  256. 'OAnimation' : 'oAnimationEnd oanimationend',
  257. 'msAnimation' : 'MSAnimationEnd',
  258. 'MozAnimation' : 'animationend',
  259. 'WebkitAnimation' : 'webkitAnimationEnd'
  260. };
  261. for (t in transitions) {
  262. if (document.documentElement.style[t] !== undefined) {
  263. type = transitions[t];
  264. supported = true;
  265. break;
  266. }
  267. }
  268. return {
  269. type: type,
  270. supported: supported
  271. };
  272. }());
  273. /**
  274. * Creates event handler delegate that sends the instance as last argument.
  275. *
  276. * @return {Function} a function wrapper which sends the instance as last argument.
  277. */
  278. function delegate(context, method) {
  279. return function () {
  280. if (arguments.length > 0) {
  281. var args = [];
  282. for (var x = 0; x < arguments.length; x += 1) {
  283. args.push(arguments[x]);
  284. }
  285. args.push(context);
  286. return method.apply(context, args);
  287. }
  288. return method.apply(context, [null, context]);
  289. };
  290. }
  291. /**
  292. * Helper for creating a dialog close event.
  293. *
  294. * @return {object}
  295. */
  296. function createCloseEvent(index, button) {
  297. return {
  298. index: index,
  299. button: button,
  300. cancel: false
  301. };
  302. }
  303. /**
  304. * Helper for dispatching events.
  305. *
  306. * @param {string} evenType The type of the event to disptach.
  307. * @param {object} instance The dialog instance disptaching the event.
  308. *
  309. * @return {any} The result of the invoked function.
  310. */
  311. function dispatchEvent(eventType, instance) {
  312. if ( typeof instance.get(eventType) === 'function' ) {
  313. return instance.get(eventType).call(instance);
  314. }
  315. }
  316. /**
  317. * Super class for all dialogs
  318. *
  319. * @return {Object} base dialog prototype
  320. */
  321. var dialog = (function () {
  322. var //holds the list of used keys.
  323. usedKeys = [],
  324. //dummy variable, used to trigger dom reflow.
  325. reflow = null,
  326. //holds body tab index in case it has any.
  327. tabindex = false,
  328. //condition for detecting safari
  329. isSafari = window.navigator.userAgent.indexOf('Safari') > -1 && window.navigator.userAgent.indexOf('Chrome') < 0,
  330. //dialog building blocks
  331. templates = {
  332. dimmer:'<div class="ajs-dimmer"></div>',
  333. /*tab index required to fire click event before body focus*/
  334. modal: '<div class="ajs-modal" tabindex="0"></div>',
  335. dialog: '<div class="ajs-dialog" tabindex="0"></div>',
  336. reset: '<button class="ajs-reset"></button>',
  337. commands: '<div class="ajs-commands"><button class="ajs-pin"></button><button class="ajs-maximize"></button><button class="ajs-close"></button></div>',
  338. header: '<div class="ajs-header"></div>',
  339. body: '<div class="ajs-body"></div>',
  340. content: '<div class="ajs-content"></div>',
  341. footer: '<div class="ajs-footer"></div>',
  342. buttons: { primary: '<div class="ajs-primary ajs-buttons"></div>', auxiliary: '<div class="ajs-auxiliary ajs-buttons"></div>' },
  343. button: '<button class="ajs-button"></button>',
  344. resizeHandle: '<div class="ajs-handle"></div>',
  345. },
  346. //common class names
  347. classes = {
  348. animationIn: 'ajs-in',
  349. animationOut: 'ajs-out',
  350. base: 'alertify',
  351. basic:'ajs-basic',
  352. capture: 'ajs-capture',
  353. closable:'ajs-closable',
  354. fixed: 'ajs-fixed',
  355. frameless:'ajs-frameless',
  356. hidden: 'ajs-hidden',
  357. maximize: 'ajs-maximize',
  358. maximized: 'ajs-maximized',
  359. maximizable:'ajs-maximizable',
  360. modeless: 'ajs-modeless',
  361. movable: 'ajs-movable',
  362. noSelection: 'ajs-no-selection',
  363. noOverflow: 'ajs-no-overflow',
  364. noPadding:'ajs-no-padding',
  365. pin:'ajs-pin',
  366. pinnable:'ajs-pinnable',
  367. prefix: 'ajs-',
  368. resizable: 'ajs-resizable',
  369. restore: 'ajs-restore',
  370. shake:'ajs-shake',
  371. unpinned:'ajs-unpinned',
  372. };
  373. /**
  374. * Helper: initializes the dialog instance
  375. *
  376. * @return {Number} The total count of currently open modals.
  377. */
  378. function initialize(instance){
  379. if(!instance.__internal){
  380. //no need to expose init after this.
  381. delete instance.__init;
  382. //keep a copy of initial dialog settings
  383. if(!instance.__settings){
  384. instance.__settings = copy(instance.settings);
  385. }
  386. //get dialog buttons/focus setup
  387. var setup;
  388. if(typeof instance.setup === 'function'){
  389. setup = instance.setup();
  390. setup.options = setup.options || {};
  391. setup.focus = setup.focus || {};
  392. }else{
  393. setup = {
  394. buttons:[],
  395. focus:{
  396. element:null,
  397. select:false
  398. },
  399. options:{
  400. }
  401. };
  402. }
  403. //initialize hooks object.
  404. if(typeof instance.hooks !== 'object'){
  405. instance.hooks = {};
  406. }
  407. //copy buttons defintion
  408. var buttonsDefinition = [];
  409. if(Array.isArray(setup.buttons)){
  410. for(var b=0;b<setup.buttons.length;b+=1){
  411. var ref = setup.buttons[b],
  412. cpy = {};
  413. for (var i in ref) {
  414. if (ref.hasOwnProperty(i)) {
  415. cpy[i] = ref[i];
  416. }
  417. }
  418. buttonsDefinition.push(cpy);
  419. }
  420. }
  421. var internal = instance.__internal = {
  422. /**
  423. * Flag holding the open state of the dialog
  424. *
  425. * @type {Boolean}
  426. */
  427. isOpen:false,
  428. /**
  429. * Active element is the element that will receive focus after
  430. * closing the dialog. It defaults as the body tag, but gets updated
  431. * to the last focused element before the dialog was opened.
  432. *
  433. * @type {Node}
  434. */
  435. activeElement:document.body,
  436. timerIn:undefined,
  437. timerOut:undefined,
  438. buttons: buttonsDefinition,
  439. focus: setup.focus,
  440. options: {
  441. title: undefined,
  442. modal: undefined,
  443. basic:undefined,
  444. frameless:undefined,
  445. pinned: undefined,
  446. movable: undefined,
  447. moveBounded:undefined,
  448. resizable: undefined,
  449. autoReset: undefined,
  450. closable: undefined,
  451. closableByDimmer: undefined,
  452. maximizable: undefined,
  453. startMaximized: undefined,
  454. pinnable: undefined,
  455. transition: undefined,
  456. padding:undefined,
  457. overflow:undefined,
  458. onshow:undefined,
  459. onclosing:undefined,
  460. onclose:undefined,
  461. onfocus:undefined,
  462. onmove:undefined,
  463. onmoved:undefined,
  464. onresize:undefined,
  465. onresized:undefined,
  466. onmaximize:undefined,
  467. onmaximized:undefined,
  468. onrestore:undefined,
  469. onrestored:undefined
  470. },
  471. resetHandler:undefined,
  472. beginMoveHandler:undefined,
  473. beginResizeHandler:undefined,
  474. bringToFrontHandler:undefined,
  475. modalClickHandler:undefined,
  476. buttonsClickHandler:undefined,
  477. commandsClickHandler:undefined,
  478. transitionInHandler:undefined,
  479. transitionOutHandler:undefined,
  480. destroy:undefined
  481. };
  482. var elements = {};
  483. //root node
  484. elements.root = document.createElement('div');
  485. //prevent FOUC in case of async styles loading.
  486. elements.root.style.display = 'none';
  487. elements.root.className = classes.base + ' ' + classes.hidden + ' ';
  488. elements.root.innerHTML = templates.dimmer + templates.modal;
  489. //dimmer
  490. elements.dimmer = elements.root.firstChild;
  491. //dialog
  492. elements.modal = elements.root.lastChild;
  493. elements.modal.innerHTML = templates.dialog;
  494. elements.dialog = elements.modal.firstChild;
  495. elements.dialog.innerHTML = templates.reset + templates.commands + templates.header + templates.body + templates.footer + templates.resizeHandle + templates.reset;
  496. //reset links
  497. elements.reset = [];
  498. elements.reset.push(elements.dialog.firstChild);
  499. elements.reset.push(elements.dialog.lastChild);
  500. //commands
  501. elements.commands = {};
  502. elements.commands.container = elements.reset[0].nextSibling;
  503. elements.commands.pin = elements.commands.container.firstChild;
  504. elements.commands.maximize = elements.commands.pin.nextSibling;
  505. elements.commands.close = elements.commands.maximize.nextSibling;
  506. //header
  507. elements.header = elements.commands.container.nextSibling;
  508. //body
  509. elements.body = elements.header.nextSibling;
  510. elements.body.innerHTML = templates.content;
  511. elements.content = elements.body.firstChild;
  512. //footer
  513. elements.footer = elements.body.nextSibling;
  514. elements.footer.innerHTML = templates.buttons.auxiliary + templates.buttons.primary;
  515. //resize handle
  516. elements.resizeHandle = elements.footer.nextSibling;
  517. //buttons
  518. elements.buttons = {};
  519. elements.buttons.auxiliary = elements.footer.firstChild;
  520. elements.buttons.primary = elements.buttons.auxiliary.nextSibling;
  521. elements.buttons.primary.innerHTML = templates.button;
  522. elements.buttonTemplate = elements.buttons.primary.firstChild;
  523. //remove button template
  524. elements.buttons.primary.removeChild(elements.buttonTemplate);
  525. for(var x=0; x < instance.__internal.buttons.length; x+=1) {
  526. var button = instance.__internal.buttons[x];
  527. // add to the list of used keys.
  528. if(usedKeys.indexOf(button.key) < 0){
  529. usedKeys.push(button.key);
  530. }
  531. button.element = elements.buttonTemplate.cloneNode();
  532. button.element.innerHTML = button.text;
  533. if(typeof button.className === 'string' && button.className !== ''){
  534. addClass(button.element, button.className);
  535. }
  536. for(var key in button.attrs){
  537. if(key !== 'className' && button.attrs.hasOwnProperty(key)){
  538. button.element.setAttribute(key, button.attrs[key]);
  539. }
  540. }
  541. if(button.scope === 'auxiliary'){
  542. elements.buttons.auxiliary.appendChild(button.element);
  543. }else{
  544. elements.buttons.primary.appendChild(button.element);
  545. }
  546. }
  547. //make elements pubic
  548. instance.elements = elements;
  549. //save event handlers delegates
  550. internal.resetHandler = delegate(instance, onReset);
  551. internal.beginMoveHandler = delegate(instance, beginMove);
  552. internal.beginResizeHandler = delegate(instance, beginResize);
  553. internal.bringToFrontHandler = delegate(instance, bringToFront);
  554. internal.modalClickHandler = delegate(instance, modalClickHandler);
  555. internal.buttonsClickHandler = delegate(instance, buttonsClickHandler);
  556. internal.commandsClickHandler = delegate(instance, commandsClickHandler);
  557. internal.transitionInHandler = delegate(instance, handleTransitionInEvent);
  558. internal.transitionOutHandler = delegate(instance, handleTransitionOutEvent);
  559. //settings
  560. for(var opKey in internal.options){
  561. if(setup.options[opKey] !== undefined){
  562. // if found in user options
  563. instance.set(opKey, setup.options[opKey]);
  564. }else if(alertify.defaults.hasOwnProperty(opKey)) {
  565. // else if found in defaults options
  566. instance.set(opKey, alertify.defaults[opKey]);
  567. }else if(opKey === 'title' ) {
  568. // else if title key, use alertify.defaults.glossary
  569. instance.set(opKey, alertify.defaults.glossary[opKey]);
  570. }
  571. }
  572. // allow dom customization
  573. if(typeof instance.build === 'function'){
  574. instance.build();
  575. }
  576. }
  577. //add to the end of the DOM tree.
  578. document.body.appendChild(instance.elements.root);
  579. }
  580. /**
  581. * Helper: maintains scroll position
  582. *
  583. */
  584. var scrollX, scrollY;
  585. function saveScrollPosition(){
  586. scrollX = getScrollLeft();
  587. scrollY = getScrollTop();
  588. }
  589. function restoreScrollPosition(){
  590. window.scrollTo(scrollX, scrollY);
  591. }
  592. /**
  593. * Helper: adds/removes no-overflow class from body
  594. *
  595. */
  596. function ensureNoOverflow(){
  597. var requiresNoOverflow = 0;
  598. for(var x=0;x<openDialogs.length;x+=1){
  599. var instance = openDialogs[x];
  600. if(instance.isModal() || instance.isMaximized()){
  601. requiresNoOverflow+=1;
  602. }
  603. }
  604. if(requiresNoOverflow === 0 && document.body.className.indexOf(classes.noOverflow) >= 0){
  605. //last open modal or last maximized one
  606. removeClass(document.body, classes.noOverflow);
  607. preventBodyShift(false);
  608. }else if(requiresNoOverflow > 0 && document.body.className.indexOf(classes.noOverflow) < 0){
  609. //first open modal or first maximized one
  610. preventBodyShift(true);
  611. addClass(document.body, classes.noOverflow);
  612. }
  613. }
  614. var top = '', topScroll = 0;
  615. /**
  616. * Helper: prevents body shift.
  617. *
  618. */
  619. function preventBodyShift(add){
  620. if(alertify.defaults.preventBodyShift){
  621. if(add && document.documentElement.scrollHeight > document.documentElement.clientHeight ){//&& openDialogs[openDialogs.length-1].elements.dialog.clientHeight <= document.documentElement.clientHeight){
  622. topScroll = scrollY;
  623. top = window.getComputedStyle(document.body).top;
  624. addClass(document.body, classes.fixed);
  625. document.body.style.top = -scrollY + 'px';
  626. } else if(!add) {
  627. scrollY = topScroll;
  628. document.body.style.top = top;
  629. removeClass(document.body, classes.fixed);
  630. restoreScrollPosition();
  631. }
  632. }
  633. }
  634. /**
  635. * Sets the name of the transition used to show/hide the dialog
  636. *
  637. * @param {Object} instance The dilog instance.
  638. *
  639. */
  640. function updateTransition(instance, value, oldValue){
  641. if(typeof oldValue === 'string'){
  642. removeClass(instance.elements.root,classes.prefix + oldValue);
  643. }
  644. addClass(instance.elements.root, classes.prefix + value);
  645. reflow = instance.elements.root.offsetWidth;
  646. }
  647. /**
  648. * Toggles the dialog display mode
  649. *
  650. * @param {Object} instance The dilog instance.
  651. *
  652. * @return {undefined}
  653. */
  654. function updateDisplayMode(instance){
  655. if(instance.get('modal')){
  656. //make modal
  657. removeClass(instance.elements.root, classes.modeless);
  658. //only if open
  659. if(instance.isOpen()){
  660. unbindModelessEvents(instance);
  661. //in case a pinned modless dialog was made modal while open.
  662. updateAbsPositionFix(instance);
  663. ensureNoOverflow();
  664. }
  665. }else{
  666. //make modelss
  667. addClass(instance.elements.root, classes.modeless);
  668. //only if open
  669. if(instance.isOpen()){
  670. bindModelessEvents(instance);
  671. //in case pin/unpin was called while a modal is open
  672. updateAbsPositionFix(instance);
  673. ensureNoOverflow();
  674. }
  675. }
  676. }
  677. /**
  678. * Toggles the dialog basic view mode
  679. *
  680. * @param {Object} instance The dilog instance.
  681. *
  682. * @return {undefined}
  683. */
  684. function updateBasicMode(instance){
  685. if (instance.get('basic')) {
  686. // add class
  687. addClass(instance.elements.root, classes.basic);
  688. } else {
  689. // remove class
  690. removeClass(instance.elements.root, classes.basic);
  691. }
  692. }
  693. /**
  694. * Toggles the dialog frameless view mode
  695. *
  696. * @param {Object} instance The dilog instance.
  697. *
  698. * @return {undefined}
  699. */
  700. function updateFramelessMode(instance){
  701. if (instance.get('frameless')) {
  702. // add class
  703. addClass(instance.elements.root, classes.frameless);
  704. } else {
  705. // remove class
  706. removeClass(instance.elements.root, classes.frameless);
  707. }
  708. }
  709. /**
  710. * Helper: Brings the modeless dialog to front, attached to modeless dialogs.
  711. *
  712. * @param {Event} event Focus event
  713. * @param {Object} instance The dilog instance.
  714. *
  715. * @return {undefined}
  716. */
  717. function bringToFront(event, instance){
  718. // Do not bring to front if preceeded by an open modal
  719. var index = openDialogs.indexOf(instance);
  720. for(var x=index+1;x<openDialogs.length;x+=1){
  721. if(openDialogs[x].isModal()){
  722. return;
  723. }
  724. }
  725. // Bring to front by making it the last child.
  726. if(document.body.lastChild !== instance.elements.root){
  727. document.body.appendChild(instance.elements.root);
  728. //also make sure its at the end of the list
  729. openDialogs.splice(openDialogs.indexOf(instance),1);
  730. openDialogs.push(instance);
  731. setFocus(instance);
  732. }
  733. return false;
  734. }
  735. /**
  736. * Helper: reflects dialogs options updates
  737. *
  738. * @param {Object} instance The dilog instance.
  739. * @param {String} option The updated option name.
  740. *
  741. * @return {undefined}
  742. */
  743. function optionUpdated(instance, option, oldValue, newValue){
  744. switch(option){
  745. case 'title':
  746. instance.setHeader(newValue);
  747. break;
  748. case 'modal':
  749. updateDisplayMode(instance);
  750. break;
  751. case 'basic':
  752. updateBasicMode(instance);
  753. break;
  754. case 'frameless':
  755. updateFramelessMode(instance);
  756. break;
  757. case 'pinned':
  758. updatePinned(instance);
  759. break;
  760. case 'closable':
  761. updateClosable(instance);
  762. break;
  763. case 'maximizable':
  764. updateMaximizable(instance);
  765. break;
  766. case 'pinnable':
  767. updatePinnable(instance);
  768. break;
  769. case 'movable':
  770. updateMovable(instance);
  771. break;
  772. case 'resizable':
  773. updateResizable(instance);
  774. break;
  775. case 'padding':
  776. if(newValue){
  777. removeClass(instance.elements.root, classes.noPadding);
  778. }else if(instance.elements.root.className.indexOf(classes.noPadding) < 0){
  779. addClass(instance.elements.root, classes.noPadding);
  780. }
  781. break;
  782. case 'overflow':
  783. if(newValue){
  784. removeClass(instance.elements.root, classes.noOverflow);
  785. }else if(instance.elements.root.className.indexOf(classes.noOverflow) < 0){
  786. addClass(instance.elements.root, classes.noOverflow);
  787. }
  788. break;
  789. case 'transition':
  790. updateTransition(instance,newValue, oldValue);
  791. break;
  792. }
  793. // internal on option updated event
  794. if(typeof instance.hooks.onupdate === 'function'){
  795. instance.hooks.onupdate.call(instance, option, oldValue, newValue);
  796. }
  797. }
  798. /**
  799. * Helper: reflects dialogs options updates
  800. *
  801. * @param {Object} instance The dilog instance.
  802. * @param {Object} obj The object to set/get a value on/from.
  803. * @param {Function} callback The callback function to call if the key was found.
  804. * @param {String|Object} key A string specifying a propery name or a collection of key value pairs.
  805. * @param {Object} value Optional, the value associated with the key (in case it was a string).
  806. * @param {String} option The updated option name.
  807. *
  808. * @return {Object} result object
  809. * The result objects has an 'op' property, indicating of this is a SET or GET operation.
  810. * GET:
  811. * - found: a flag indicating if the key was found or not.
  812. * - value: the property value.
  813. * SET:
  814. * - items: a list of key value pairs of the properties being set.
  815. * each contains:
  816. * - found: a flag indicating if the key was found or not.
  817. * - key: the property key.
  818. * - value: the property value.
  819. */
  820. function update(instance, obj, callback, key, value){
  821. var result = {op:undefined, items: [] };
  822. if(typeof value === 'undefined' && typeof key === 'string') {
  823. //get
  824. result.op = 'get';
  825. if(obj.hasOwnProperty(key)){
  826. result.found = true;
  827. result.value = obj[key];
  828. }else{
  829. result.found = false;
  830. result.value = undefined;
  831. }
  832. }
  833. else
  834. {
  835. var old;
  836. //set
  837. result.op = 'set';
  838. if(typeof key === 'object'){
  839. //set multiple
  840. var args = key;
  841. for (var prop in args) {
  842. if (obj.hasOwnProperty(prop)) {
  843. if(obj[prop] !== args[prop]){
  844. old = obj[prop];
  845. obj[prop] = args[prop];
  846. callback.call(instance,prop, old, args[prop]);
  847. }
  848. result.items.push({ 'key': prop, 'value': args[prop], 'found':true});
  849. }else{
  850. result.items.push({ 'key': prop, 'value': args[prop], 'found':false});
  851. }
  852. }
  853. } else if (typeof key === 'string'){
  854. //set single
  855. if (obj.hasOwnProperty(key)) {
  856. if(obj[key] !== value){
  857. old = obj[key];
  858. obj[key] = value;
  859. callback.call(instance,key, old, value);
  860. }
  861. result.items.push({'key': key, 'value': value , 'found':true});
  862. }else{
  863. result.items.push({'key': key, 'value': value , 'found':false});
  864. }
  865. } else {
  866. //invalid params
  867. throw new Error('args must be a string or object');
  868. }
  869. }
  870. return result;
  871. }
  872. /**
  873. * Triggers a close event.
  874. *
  875. * @param {Object} instance The dilog instance.
  876. *
  877. * @return {undefined}
  878. */
  879. function triggerClose(instance) {
  880. var found;
  881. triggerCallback(instance, function (button) {
  882. return found = (button.invokeOnClose === true);
  883. });
  884. //none of the buttons registered as onclose callback
  885. //close the dialog
  886. if (!found && instance.isOpen()) {
  887. instance.close();
  888. }
  889. }
  890. /**
  891. * Dialogs commands event handler, attached to the dialog commands element.
  892. *
  893. * @param {Event} event DOM event object.
  894. * @param {Object} instance The dilog instance.
  895. *
  896. * @return {undefined}
  897. */
  898. function commandsClickHandler(event, instance) {
  899. var target = event.srcElement || event.target;
  900. switch (target) {
  901. case instance.elements.commands.pin:
  902. if (!instance.isPinned()) {
  903. pin(instance);
  904. } else {
  905. unpin(instance);
  906. }
  907. break;
  908. case instance.elements.commands.maximize:
  909. if (!instance.isMaximized()) {
  910. maximize(instance);
  911. } else {
  912. restore(instance);
  913. }
  914. break;
  915. case instance.elements.commands.close:
  916. triggerClose(instance);
  917. break;
  918. }
  919. return false;
  920. }
  921. /**
  922. * Helper: pins the modeless dialog.
  923. *
  924. * @param {Object} instance The dialog instance.
  925. *
  926. * @return {undefined}
  927. */
  928. function pin(instance) {
  929. //pin the dialog
  930. instance.set('pinned', true);
  931. }
  932. /**
  933. * Helper: unpins the modeless dialog.
  934. *
  935. * @param {Object} instance The dilog instance.
  936. *
  937. * @return {undefined}
  938. */
  939. function unpin(instance) {
  940. //unpin the dialog
  941. instance.set('pinned', false);
  942. }
  943. /**
  944. * Helper: enlarges the dialog to fill the entire screen.
  945. *
  946. * @param {Object} instance The dilog instance.
  947. *
  948. * @return {undefined}
  949. */
  950. function maximize(instance) {
  951. // allow custom `onmaximize` method
  952. dispatchEvent('onmaximize', instance);
  953. //maximize the dialog
  954. addClass(instance.elements.root, classes.maximized);
  955. if (instance.isOpen()) {
  956. ensureNoOverflow();
  957. }
  958. // allow custom `onmaximized` method
  959. dispatchEvent('onmaximized', instance);
  960. }
  961. /**
  962. * Helper: returns the dialog to its former size.
  963. *
  964. * @param {Object} instance The dilog instance.
  965. *
  966. * @return {undefined}
  967. */
  968. function restore(instance) {
  969. // allow custom `onrestore` method
  970. dispatchEvent('onrestore', instance);
  971. //maximize the dialog
  972. removeClass(instance.elements.root, classes.maximized);
  973. if (instance.isOpen()) {
  974. ensureNoOverflow();
  975. }
  976. // allow custom `onrestored` method
  977. dispatchEvent('onrestored', instance);
  978. }
  979. /**
  980. * Show or hide the maximize box.
  981. *
  982. * @param {Object} instance The dilog instance.
  983. * @param {Boolean} on True to add the behavior, removes it otherwise.
  984. *
  985. * @return {undefined}
  986. */
  987. function updatePinnable(instance) {
  988. if (instance.get('pinnable')) {
  989. // add class
  990. addClass(instance.elements.root, classes.pinnable);
  991. } else {
  992. // remove class
  993. removeClass(instance.elements.root, classes.pinnable);
  994. }
  995. }
  996. /**
  997. * Helper: Fixes the absolutly positioned modal div position.
  998. *
  999. * @param {Object} instance The dialog instance.
  1000. *
  1001. * @return {undefined}
  1002. */
  1003. function addAbsPositionFix(instance) {
  1004. var scrollLeft = getScrollLeft();
  1005. instance.elements.modal.style.marginTop = getScrollTop() + 'px';
  1006. instance.elements.modal.style.marginLeft = scrollLeft + 'px';
  1007. instance.elements.modal.style.marginRight = (-scrollLeft) + 'px';
  1008. }
  1009. /**
  1010. * Helper: Removes the absolutly positioned modal div position fix.
  1011. *
  1012. * @param {Object} instance The dialog instance.
  1013. *
  1014. * @return {undefined}
  1015. */
  1016. function removeAbsPositionFix(instance) {
  1017. var marginTop = parseInt(instance.elements.modal.style.marginTop, 10);
  1018. var marginLeft = parseInt(instance.elements.modal.style.marginLeft, 10);
  1019. instance.elements.modal.style.marginTop = '';
  1020. instance.elements.modal.style.marginLeft = '';
  1021. instance.elements.modal.style.marginRight = '';
  1022. if (instance.isOpen()) {
  1023. var top = 0,
  1024. left = 0
  1025. ;
  1026. if (instance.elements.dialog.style.top !== '') {
  1027. top = parseInt(instance.elements.dialog.style.top, 10);
  1028. }
  1029. instance.elements.dialog.style.top = (top + (marginTop - getScrollTop())) + 'px';
  1030. if (instance.elements.dialog.style.left !== '') {
  1031. left = parseInt(instance.elements.dialog.style.left, 10);
  1032. }
  1033. instance.elements.dialog.style.left = (left + (marginLeft - getScrollLeft())) + 'px';
  1034. }
  1035. }
  1036. /**
  1037. * Helper: Adds/Removes the absolutly positioned modal div position fix based on its pinned setting.
  1038. *
  1039. * @param {Object} instance The dialog instance.
  1040. *
  1041. * @return {undefined}
  1042. */
  1043. function updateAbsPositionFix(instance) {
  1044. // if modeless and unpinned add fix
  1045. if (!instance.get('modal') && !instance.get('pinned')) {
  1046. addAbsPositionFix(instance);
  1047. } else {
  1048. removeAbsPositionFix(instance);
  1049. }
  1050. }
  1051. /**
  1052. * Toggles the dialog position lock | modeless only.
  1053. *
  1054. * @param {Object} instance The dilog instance.
  1055. * @param {Boolean} on True to make it modal, false otherwise.
  1056. *
  1057. * @return {undefined}
  1058. */
  1059. function updatePinned(instance) {
  1060. if (instance.get('pinned')) {
  1061. removeClass(instance.elements.root, classes.unpinned);
  1062. if (instance.isOpen()) {
  1063. removeAbsPositionFix(instance);
  1064. }
  1065. } else {
  1066. addClass(instance.elements.root, classes.unpinned);
  1067. if (instance.isOpen() && !instance.isModal()) {
  1068. addAbsPositionFix(instance);
  1069. }
  1070. }
  1071. }
  1072. /**
  1073. * Show or hide the maximize box.
  1074. *
  1075. * @param {Object} instance The dilog instance.
  1076. * @param {Boolean} on True to add the behavior, removes it otherwise.
  1077. *
  1078. * @return {undefined}
  1079. */
  1080. function updateMaximizable(instance) {
  1081. if (instance.get('maximizable')) {
  1082. // add class
  1083. addClass(instance.elements.root, classes.maximizable);
  1084. } else {
  1085. // remove class
  1086. removeClass(instance.elements.root, classes.maximizable);
  1087. }
  1088. }
  1089. /**
  1090. * Show or hide the close box.
  1091. *
  1092. * @param {Object} instance The dilog instance.
  1093. * @param {Boolean} on True to add the behavior, removes it otherwise.
  1094. *
  1095. * @return {undefined}
  1096. */
  1097. function updateClosable(instance) {
  1098. if (instance.get('closable')) {
  1099. // add class
  1100. addClass(instance.elements.root, classes.closable);
  1101. bindClosableEvents(instance);
  1102. } else {
  1103. // remove class
  1104. removeClass(instance.elements.root, classes.closable);
  1105. unbindClosableEvents(instance);
  1106. }
  1107. }
  1108. var cancelClick = false,// flag to cancel click event if already handled by end resize event (the mousedown, mousemove, mouseup sequence fires a click event.).
  1109. modalClickHandlerTS=0 // stores last click timestamp to prevent executing the handler twice on double click.
  1110. ;
  1111. /**
  1112. * Helper: closes the modal dialog when clicking the modal
  1113. *
  1114. * @param {Event} event DOM event object.
  1115. * @param {Object} instance The dilog instance.
  1116. *
  1117. * @return {undefined}
  1118. */
  1119. function modalClickHandler(event, instance) {
  1120. if(event.timeStamp - modalClickHandlerTS > 200 && (modalClickHandlerTS = event.timeStamp) && !cancelClick){
  1121. var target = event.srcElement || event.target;
  1122. if (instance.get('closableByDimmer') === true && target === instance.elements.modal) {
  1123. triggerClose(instance);
  1124. }
  1125. cancelClick = false;
  1126. return false;
  1127. }
  1128. }
  1129. // stores last call timestamp to prevent triggering the callback twice.
  1130. var callbackTS = 0;
  1131. // flag to cancel keyup event if already handled by click event (pressing Enter on a focusted button).
  1132. var cancelKeyup = false;
  1133. /**
  1134. * Helper: triggers a button callback
  1135. *
  1136. * @param {Object} The dilog instance.
  1137. * @param {Function} Callback to check which button triggered the event.
  1138. *
  1139. * @return {undefined}
  1140. */
  1141. function triggerCallback(instance, check) {
  1142. if(Date.now() - callbackTS > 200 && (callbackTS = Date.now())){
  1143. for (var idx = 0; idx < instance.__internal.buttons.length; idx += 1) {
  1144. var button = instance.__internal.buttons[idx];
  1145. if (!button.element.disabled && check(button)) {
  1146. var closeEvent = createCloseEvent(idx, button);
  1147. if (typeof instance.callback === 'function') {
  1148. instance.callback.apply(instance, [closeEvent]);
  1149. }
  1150. //close the dialog only if not canceled.
  1151. if (closeEvent.cancel === false) {
  1152. instance.close();
  1153. }
  1154. break;
  1155. }
  1156. }
  1157. }
  1158. }
  1159. /**
  1160. * Clicks event handler, attached to the dialog footer.
  1161. *
  1162. * @param {Event} DOM event object.
  1163. * @param {Object} The dilog instance.
  1164. *
  1165. * @return {undefined}
  1166. */
  1167. function buttonsClickHandler(event, instance) {
  1168. var target = event.srcElement || event.target;
  1169. triggerCallback(instance, function (button) {
  1170. // if this button caused the click, cancel keyup event
  1171. return button.element === target && (cancelKeyup = true);
  1172. });
  1173. }
  1174. /**
  1175. * Keyup event handler, attached to the document.body
  1176. *
  1177. * @param {Event} DOM event object.
  1178. * @param {Object} The dilog instance.
  1179. *
  1180. * @return {undefined}
  1181. */
  1182. function keyupHandler(event) {
  1183. //hitting enter while button has focus will trigger keyup too.
  1184. //ignore if handled by clickHandler
  1185. if (cancelKeyup) {
  1186. cancelKeyup = false;
  1187. return;
  1188. }
  1189. var instance = openDialogs[openDialogs.length - 1];
  1190. var keyCode = event.keyCode;
  1191. if (instance.__internal.buttons.length === 0 && keyCode === keys.ESC && instance.get('closable') === true) {
  1192. triggerClose(instance);
  1193. return false;
  1194. }else if (usedKeys.indexOf(keyCode) > -1) {
  1195. triggerCallback(instance, function (button) {
  1196. return button.key === keyCode;
  1197. });
  1198. return false;
  1199. }
  1200. }
  1201. /**
  1202. * Keydown event handler, attached to the document.body
  1203. *
  1204. * @param {Event} DOM event object.
  1205. * @param {Object} The dilog instance.
  1206. *
  1207. * @return {undefined}
  1208. */
  1209. function keydownHandler(event) {
  1210. var instance = openDialogs[openDialogs.length - 1];
  1211. var keyCode = event.keyCode;
  1212. if (keyCode === keys.LEFT || keyCode === keys.RIGHT) {
  1213. var buttons = instance.__internal.buttons;
  1214. for (var x = 0; x < buttons.length; x += 1) {
  1215. if (document.activeElement === buttons[x].element) {
  1216. switch (keyCode) {
  1217. case keys.LEFT:
  1218. buttons[(x || buttons.length) - 1].element.focus();
  1219. return;
  1220. case keys.RIGHT:
  1221. buttons[(x + 1) % buttons.length].element.focus();
  1222. return;
  1223. }
  1224. }
  1225. }
  1226. }else if (keyCode < keys.F12 + 1 && keyCode > keys.F1 - 1 && usedKeys.indexOf(keyCode) > -1) {
  1227. event.preventDefault();
  1228. event.stopPropagation();
  1229. triggerCallback(instance, function (button) {
  1230. return button.key === keyCode;
  1231. });
  1232. return false;
  1233. }
  1234. }
  1235. /**
  1236. * Sets focus to proper dialog element
  1237. *
  1238. * @param {Object} instance The dilog instance.
  1239. * @param {Node} [resetTarget=undefined] DOM element to reset focus to.
  1240. *
  1241. * @return {undefined}
  1242. */
  1243. function setFocus(instance, resetTarget) {
  1244. // reset target has already been determined.
  1245. if (resetTarget) {
  1246. resetTarget.focus();
  1247. } else {
  1248. // current instance focus settings
  1249. var focus = instance.__internal.focus;
  1250. // the focus element.
  1251. var element = focus.element;
  1252. switch (typeof focus.element) {
  1253. // a number means a button index
  1254. case 'number':
  1255. if (instance.__internal.buttons.length > focus.element) {
  1256. //in basic view, skip focusing the buttons.
  1257. if (instance.get('basic') === true) {
  1258. element = instance.elements.reset[0];
  1259. } else {
  1260. element = instance.__internal.buttons[focus.element].element;
  1261. }
  1262. }
  1263. break;
  1264. // a string means querySelector to select from dialog body contents.
  1265. case 'string':
  1266. element = instance.elements.body.querySelector(focus.element);
  1267. break;
  1268. // a function should return the focus element.
  1269. case 'function':
  1270. element = focus.element.call(instance);
  1271. break;
  1272. }
  1273. // if no focus element, default to first reset element.
  1274. if ((typeof element === 'undefined' || element === null) && instance.__internal.buttons.length === 0) {
  1275. element = instance.elements.reset[0];
  1276. }
  1277. // focus
  1278. if (element && element.focus) {
  1279. element.focus();
  1280. // if selectable
  1281. if (focus.select && element.select) {
  1282. element.select();
  1283. }
  1284. }
  1285. }
  1286. }
  1287. /**
  1288. * Focus event handler, attached to document.body and dialogs own reset links.
  1289. * handles the focus for modal dialogs only.
  1290. *
  1291. * @param {Event} event DOM focus event object.
  1292. * @param {Object} instance The dilog instance.
  1293. *
  1294. * @return {undefined}
  1295. */
  1296. function onReset(event, instance) {
  1297. // should work on last modal if triggered from document.body
  1298. if (!instance) {
  1299. for (var x = openDialogs.length - 1; x > -1; x -= 1) {
  1300. if (openDialogs[x].isModal()) {
  1301. instance = openDialogs[x];
  1302. break;
  1303. }
  1304. }
  1305. }
  1306. // if modal
  1307. if (instance && instance.isModal()) {
  1308. // determine reset target to enable forward/backward tab cycle.
  1309. var resetTarget, target = event.srcElement || event.target;
  1310. var lastResetElement = target === instance.elements.reset[1] || (instance.__internal.buttons.length === 0 && target === document.body);
  1311. // if last reset link, then go to maximize or close
  1312. if (lastResetElement) {
  1313. if (instance.get('maximizable')) {
  1314. resetTarget = instance.elements.commands.maximize;
  1315. } else if (instance.get('closable')) {
  1316. resetTarget = instance.elements.commands.close;
  1317. }
  1318. }
  1319. // if no reset target found, try finding the best button
  1320. if (resetTarget === undefined) {
  1321. if (typeof instance.__internal.focus.element === 'number') {
  1322. // button focus element, go to first available button
  1323. if (target === instance.elements.reset[0]) {
  1324. resetTarget = instance.elements.buttons.auxiliary.firstChild || instance.elements.buttons.primary.firstChild;
  1325. } else if (lastResetElement) {
  1326. //restart the cycle by going to first reset link
  1327. resetTarget = instance.elements.reset[0];
  1328. }
  1329. } else {
  1330. // will reach here when tapping backwards, so go to last child
  1331. // The focus element SHOULD NOT be a button (logically!).
  1332. if (target === instance.elements.reset[0]) {
  1333. resetTarget = instance.elements.buttons.primary.lastChild || instance.elements.buttons.auxiliary.lastChild;
  1334. }
  1335. }
  1336. }
  1337. // focus
  1338. setFocus(instance, resetTarget);
  1339. }
  1340. }
  1341. /**
  1342. * Transition in transitionend event handler.
  1343. *
  1344. * @param {Event} TransitionEnd event object.
  1345. * @param {Object} The dilog instance.
  1346. *
  1347. * @return {undefined}
  1348. */
  1349. function handleTransitionInEvent(event, instance) {
  1350. // clear the timer
  1351. clearTimeout(instance.__internal.timerIn);
  1352. // once transition is complete, set focus
  1353. setFocus(instance);
  1354. //restore scroll to prevent document jump
  1355. restoreScrollPosition();
  1356. // allow handling key up after transition ended.
  1357. cancelKeyup = false;
  1358. // allow custom `onfocus` method
  1359. dispatchEvent('onfocus', instance);
  1360. // unbind the event
  1361. off(instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);
  1362. removeClass(instance.elements.root, classes.animationIn);
  1363. }
  1364. /**
  1365. * Transition out transitionend event handler.
  1366. *
  1367. * @param {Event} TransitionEnd event object.
  1368. * @param {Object} The dilog instance.
  1369. *
  1370. * @return {undefined}
  1371. */
  1372. function handleTransitionOutEvent(event, instance) {
  1373. // clear the timer
  1374. clearTimeout(instance.__internal.timerOut);
  1375. // unbind the event
  1376. off(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);
  1377. // reset move updates
  1378. resetMove(instance);
  1379. // reset resize updates
  1380. resetResize(instance);
  1381. // restore if maximized
  1382. if (instance.isMaximized() && !instance.get('startMaximized')) {
  1383. restore(instance);
  1384. }
  1385. // return focus to the last active element
  1386. if (alertify.defaults.maintainFocus && instance.__internal.activeElement) {
  1387. instance.__internal.activeElement.focus();
  1388. instance.__internal.activeElement = null;
  1389. }
  1390. //destory the instance
  1391. if (typeof instance.__internal.destroy === 'function') {
  1392. instance.__internal.destroy.apply(instance);
  1393. }
  1394. }
  1395. /* Controls moving a dialog around */
  1396. //holde the current moving instance
  1397. var movable = null,
  1398. //holds the current X offset when move starts
  1399. offsetX = 0,
  1400. //holds the current Y offset when move starts
  1401. offsetY = 0,
  1402. xProp = 'pageX',
  1403. yProp = 'pageY',
  1404. bounds = null,
  1405. refreshTop = false,
  1406. moveDelegate = null
  1407. ;
  1408. /**
  1409. * Helper: sets the element top/left coordinates
  1410. *
  1411. * @param {Event} event DOM event object.
  1412. * @param {Node} element The element being moved.
  1413. *
  1414. * @return {undefined}
  1415. */
  1416. function moveElement(event, element) {
  1417. var left = (event[xProp] - offsetX),
  1418. top = (event[yProp] - offsetY);
  1419. if(refreshTop){
  1420. top -= document.body.scrollTop;
  1421. }
  1422. element.style.left = left + 'px';
  1423. element.style.top = top + 'px';
  1424. }
  1425. /**
  1426. * Helper: sets the element top/left coordinates within screen bounds
  1427. *
  1428. * @param {Event} event DOM event object.
  1429. * @param {Node} element The element being moved.
  1430. *
  1431. * @return {undefined}
  1432. */
  1433. function moveElementBounded(event, element) {
  1434. var left = (event[xProp] - offsetX),
  1435. top = (event[yProp] - offsetY);
  1436. if(refreshTop){
  1437. top -= document.body.scrollTop;
  1438. }
  1439. element.style.left = Math.min(bounds.maxLeft, Math.max(bounds.minLeft, left)) + 'px';
  1440. if(refreshTop){
  1441. element.style.top = Math.min(bounds.maxTop, Math.max(bounds.minTop, top)) + 'px';
  1442. }else{
  1443. element.style.top = Math.max(bounds.minTop, top) + 'px';
  1444. }
  1445. }
  1446. /**
  1447. * Triggers the start of a move event, attached to the header element mouse down event.
  1448. * Adds no-selection class to the body, disabling selection while moving.
  1449. *
  1450. * @param {Event} event DOM event object.
  1451. * @param {Object} instance The dilog instance.
  1452. *
  1453. * @return {Boolean} false
  1454. */
  1455. function beginMove(event, instance) {
  1456. if (resizable === null && !instance.isMaximized() && instance.get('movable')) {
  1457. var eventSrc, left=0, top=0;
  1458. if (event.type === 'touchstart') {
  1459. event.preventDefault();
  1460. eventSrc = event.targetTouches[0];
  1461. xProp = 'clientX';
  1462. yProp = 'clientY';
  1463. } else if (event.button === 0) {
  1464. eventSrc = event;
  1465. }
  1466. if (eventSrc) {
  1467. var element = instance.elements.dialog;
  1468. addClass(element, classes.capture);
  1469. if (element.style.left) {
  1470. left = parseInt(element.style.left, 10);
  1471. }
  1472. if (element.style.top) {
  1473. top = parseInt(element.style.top, 10);
  1474. }
  1475. offsetX = eventSrc[xProp] - left;
  1476. offsetY = eventSrc[yProp] - top;
  1477. if(instance.isModal()){
  1478. offsetY += instance.elements.modal.scrollTop;
  1479. }else if(instance.isPinned()){
  1480. offsetY -= document.body.scrollTop;
  1481. }
  1482. if(instance.get('moveBounded')){
  1483. var current = element,
  1484. offsetLeft = -left,
  1485. offsetTop = -top;
  1486. //calc offset
  1487. do {
  1488. offsetLeft += current.offsetLeft;
  1489. offsetTop += current.offsetTop;
  1490. } while (current = current.offsetParent);
  1491. bounds = {
  1492. maxLeft : offsetLeft,
  1493. minLeft : -offsetLeft,
  1494. maxTop : document.documentElement.clientHeight - element.clientHeight - offsetTop,
  1495. minTop : -offsetTop
  1496. };
  1497. moveDelegate = moveElementBounded;
  1498. }else{
  1499. bounds = null;
  1500. moveDelegate = moveElement;
  1501. }
  1502. // allow custom `onmove` method
  1503. dispatchEvent('onmove', instance);
  1504. refreshTop = !instance.isModal() && instance.isPinned();
  1505. movable = instance;
  1506. moveDelegate(eventSrc, element);
  1507. addClass(document.body, classes.noSelection);
  1508. return false;
  1509. }
  1510. }
  1511. }
  1512. /**
  1513. * The actual move handler, attached to document.body mousemove event.
  1514. *
  1515. * @param {Event} event DOM event object.
  1516. *
  1517. * @return {undefined}
  1518. */
  1519. function move(event) {
  1520. if (movable) {
  1521. var eventSrc;
  1522. if (event.type === 'touchmove') {
  1523. event.preventDefault();
  1524. eventSrc = event.targetTouches[0];
  1525. } else if (event.button === 0) {
  1526. eventSrc = event;
  1527. }
  1528. if (eventSrc) {
  1529. moveDelegate(eventSrc, movable.elements.dialog);
  1530. }
  1531. }
  1532. }
  1533. /**
  1534. * Triggers the end of a move event, attached to document.body mouseup event.
  1535. * Removes no-selection class from document.body, allowing selection.
  1536. *
  1537. * @return {undefined}
  1538. */
  1539. function endMove() {
  1540. if (movable) {
  1541. var instance = movable;
  1542. movable = bounds = null;
  1543. removeClass(document.body, classes.noSelection);
  1544. removeClass(instance.elements.dialog, classes.capture);
  1545. // allow custom `onmoved` method
  1546. dispatchEvent('onmoved', instance);
  1547. }
  1548. }
  1549. /**
  1550. * Resets any changes made by moving the element to its original state,
  1551. *
  1552. * @param {Object} instance The dilog instance.
  1553. *
  1554. * @return {undefined}
  1555. */
  1556. function resetMove(instance) {
  1557. movable = null;
  1558. var element = instance.elements.dialog;
  1559. element.style.left = element.style.top = '';
  1560. }
  1561. /**
  1562. * Updates the dialog move behavior.
  1563. *
  1564. * @param {Object} instance The dilog instance.
  1565. * @param {Boolean} on True to add the behavior, removes it otherwise.
  1566. *
  1567. * @return {undefined}
  1568. */
  1569. function updateMovable(instance) {
  1570. if (instance.get('movable')) {
  1571. // add class
  1572. addClass(instance.elements.root, classes.movable);
  1573. if (instance.isOpen()) {
  1574. bindMovableEvents(instance);
  1575. }
  1576. } else {
  1577. //reset
  1578. resetMove(instance);
  1579. // remove class
  1580. removeClass(instance.elements.root, classes.movable);
  1581. if (instance.isOpen()) {
  1582. unbindMovableEvents(instance);
  1583. }
  1584. }
  1585. }
  1586. /* Controls moving a dialog around */
  1587. //holde the current instance being resized
  1588. var resizable = null,
  1589. //holds the staring left offset when resize starts.
  1590. startingLeft = Number.Nan,
  1591. //holds the staring width when resize starts.
  1592. startingWidth = 0,
  1593. //holds the initial width when resized for the first time.
  1594. minWidth = 0,
  1595. //holds the offset of the resize handle.
  1596. handleOffset = 0
  1597. ;
  1598. /**
  1599. * Helper: sets the element width/height and updates left coordinate if neccessary.
  1600. *
  1601. * @param {Event} event DOM mousemove event object.
  1602. * @param {Node} element The element being moved.
  1603. * @param {Boolean} pinned A flag indicating if the element being resized is pinned to the screen.
  1604. *
  1605. * @return {undefined}
  1606. */
  1607. function resizeElement(event, element, pageRelative) {
  1608. //calculate offsets from 0,0
  1609. var current = element;
  1610. var offsetLeft = 0;
  1611. var offsetTop = 0;
  1612. do {
  1613. offsetLeft += current.offsetLeft;
  1614. offsetTop += current.offsetTop;
  1615. } while (current = current.offsetParent);
  1616. // determine X,Y coordinates.
  1617. var X, Y;
  1618. if (pageRelative === true) {
  1619. X = event.pageX;
  1620. Y = event.pageY;
  1621. } else {
  1622. X = event.clientX;
  1623. Y = event.clientY;
  1624. }
  1625. // rtl handling
  1626. var isRTL = isRightToLeft();
  1627. if (isRTL) {
  1628. // reverse X
  1629. X = document.body.offsetWidth - X;
  1630. // if has a starting left, calculate offsetRight
  1631. if (!isNaN(startingLeft)) {
  1632. offsetLeft = document.body.offsetWidth - offsetLeft - element.offsetWidth;
  1633. }
  1634. }
  1635. // set width/height
  1636. element.style.height = (Y - offsetTop + handleOffset) + 'px';
  1637. element.style.width = (X - offsetLeft + handleOffset) + 'px';
  1638. // if the element being resized has a starting left, maintain it.
  1639. // the dialog is centered, divide by half the offset to maintain the margins.
  1640. if (!isNaN(startingLeft)) {
  1641. var diff = Math.abs(element.offsetWidth - startingWidth) * 0.5;
  1642. if (isRTL) {
  1643. //negate the diff, why?
  1644. //when growing it should decrease left
  1645. //when shrinking it should increase left
  1646. diff *= -1;
  1647. }
  1648. if (element.offsetWidth > startingWidth) {
  1649. //growing
  1650. element.style.left = (startingLeft + diff) + 'px';
  1651. } else if (element.offsetWidth >= minWidth) {
  1652. //shrinking
  1653. element.style.left = (startingLeft - diff) + 'px';
  1654. }
  1655. }
  1656. }
  1657. /**
  1658. * Triggers the start of a resize event, attached to the resize handle element mouse down event.
  1659. * Adds no-selection class to the body, disabling selection while moving.
  1660. *
  1661. * @param {Event} event DOM event object.
  1662. * @param {Object} instance The dilog instance.
  1663. *
  1664. * @return {Boolean} false
  1665. */
  1666. function beginResize(event, instance) {
  1667. if (!instance.isMaximized()) {
  1668. var eventSrc;
  1669. if (event.type === 'touchstart') {
  1670. event.preventDefault();
  1671. eventSrc = event.targetTouches[0];
  1672. } else if (event.button === 0) {
  1673. eventSrc = event;
  1674. }
  1675. if (eventSrc) {
  1676. // allow custom `onresize` method
  1677. dispatchEvent('onresize', instance);
  1678. resizable = instance;
  1679. handleOffset = instance.elements.resizeHandle.offsetHeight / 2;
  1680. var element = instance.elements.dialog;
  1681. addClass(element, classes.capture);
  1682. startingLeft = parseInt(element.style.left, 10);
  1683. element.style.height = element.offsetHeight + 'px';
  1684. element.style.minHeight = instance.elements.header.offsetHeight + instance.elements.footer.offsetHeight + 'px';
  1685. element.style.width = (startingWidth = element.offsetWidth) + 'px';
  1686. if (element.style.maxWidth !== 'none') {
  1687. element.style.minWidth = (minWidth = element.offsetWidth) + 'px';
  1688. }
  1689. element.style.maxWidth = 'none';
  1690. addClass(document.body, classes.noSelection);
  1691. return false;
  1692. }
  1693. }
  1694. }
  1695. /**
  1696. * The actual resize handler, attached to document.body mousemove event.
  1697. *
  1698. * @param {Event} event DOM event object.
  1699. *
  1700. * @return {undefined}
  1701. */
  1702. function resize(event) {
  1703. if (resizable) {
  1704. var eventSrc;
  1705. if (event.type === 'touchmove') {
  1706. event.preventDefault();
  1707. eventSrc = event.targetTouches[0];
  1708. } else if (event.button === 0) {
  1709. eventSrc = event;
  1710. }
  1711. if (eventSrc) {
  1712. resizeElement(eventSrc, resizable.elements.dialog, !resizable.get('modal') && !resizable.get('pinned'));
  1713. }
  1714. }
  1715. }
  1716. /**
  1717. * Triggers the end of a resize event, attached to document.body mouseup event.
  1718. * Removes no-selection class from document.body, allowing selection.
  1719. *
  1720. * @return {undefined}
  1721. */
  1722. function endResize() {
  1723. if (resizable) {
  1724. var instance = resizable;
  1725. resizable = null;
  1726. removeClass(document.body, classes.noSelection);
  1727. removeClass(instance.elements.dialog, classes.capture);
  1728. cancelClick = true;
  1729. // allow custom `onresized` method
  1730. dispatchEvent('onresized', instance);
  1731. }
  1732. }
  1733. /**
  1734. * Resets any changes made by resizing the element to its original state.
  1735. *
  1736. * @param {Object} instance The dilog instance.
  1737. *
  1738. * @return {undefined}
  1739. */
  1740. function resetResize(instance) {
  1741. resizable = null;
  1742. var element = instance.elements.dialog;
  1743. if (element.style.maxWidth === 'none') {
  1744. //clear inline styles.
  1745. element.style.maxWidth = element.style.minWidth = element.style.width = element.style.height = element.style.minHeight = element.style.left = '';
  1746. //reset variables.
  1747. startingLeft = Number.Nan;
  1748. startingWidth = minWidth = handleOffset = 0;
  1749. }
  1750. }
  1751. /**
  1752. * Updates the dialog move behavior.
  1753. *
  1754. * @param {Object} instance The dilog instance.
  1755. * @param {Boolean} on True to add the behavior, removes it otherwise.
  1756. *
  1757. * @return {undefined}
  1758. */
  1759. function updateResizable(instance) {
  1760. if (instance.get('resizable')) {
  1761. // add class
  1762. addClass(instance.elements.root, classes.resizable);
  1763. if (instance.isOpen()) {
  1764. bindResizableEvents(instance);
  1765. }
  1766. } else {
  1767. //reset
  1768. resetResize(instance);
  1769. // remove class
  1770. removeClass(instance.elements.root, classes.resizable);
  1771. if (instance.isOpen()) {
  1772. unbindResizableEvents(instance);
  1773. }
  1774. }
  1775. }
  1776. /**
  1777. * Reset move/resize on window resize.
  1778. *
  1779. * @param {Event} event window resize event object.
  1780. *
  1781. * @return {undefined}
  1782. */
  1783. function windowResize(/*event*/) {
  1784. for (var x = 0; x < openDialogs.length; x += 1) {
  1785. var instance = openDialogs[x];
  1786. if (instance.get('autoReset')) {
  1787. resetMove(instance);
  1788. resetResize(instance);
  1789. }
  1790. }
  1791. }
  1792. /**
  1793. * Bind dialogs events
  1794. *
  1795. * @param {Object} instance The dilog instance.
  1796. *
  1797. * @return {undefined}
  1798. */
  1799. function bindEvents(instance) {
  1800. // if first dialog, hook global handlers
  1801. if (openDialogs.length === 1) {
  1802. //global
  1803. on(window, 'resize', windowResize);
  1804. on(document.body, 'keyup', keyupHandler);
  1805. on(document.body, 'keydown', keydownHandler);
  1806. on(document.body, 'focus', onReset);
  1807. //move
  1808. on(document.documentElement, 'mousemove', move);
  1809. on(document.documentElement, 'touchmove', move);
  1810. on(document.documentElement, 'mouseup', endMove);
  1811. on(document.documentElement, 'touchend', endMove);
  1812. //resize
  1813. on(document.documentElement, 'mousemove', resize);
  1814. on(document.documentElement, 'touchmove', resize);
  1815. on(document.documentElement, 'mouseup', endResize);
  1816. on(document.documentElement, 'touchend', endResize);
  1817. }
  1818. // common events
  1819. on(instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler);
  1820. on(instance.elements.footer, 'click', instance.__internal.buttonsClickHandler);
  1821. on(instance.elements.reset[0], 'focus', instance.__internal.resetHandler);
  1822. on(instance.elements.reset[1], 'focus', instance.__internal.resetHandler);
  1823. //prevent handling key up when dialog is being opened by a key stroke.
  1824. cancelKeyup = true;
  1825. // hook in transition handler
  1826. on(instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);
  1827. // modelss only events
  1828. if (!instance.get('modal')) {
  1829. bindModelessEvents(instance);
  1830. }
  1831. // resizable
  1832. if (instance.get('resizable')) {
  1833. bindResizableEvents(instance);
  1834. }
  1835. // movable
  1836. if (instance.get('movable')) {
  1837. bindMovableEvents(instance);
  1838. }
  1839. }
  1840. /**
  1841. * Unbind dialogs events
  1842. *
  1843. * @param {Object} instance The dilog instance.
  1844. *
  1845. * @return {undefined}
  1846. */
  1847. function unbindEvents(instance) {
  1848. // if last dialog, remove global handlers
  1849. if (openDialogs.length === 1) {
  1850. //global
  1851. off(window, 'resize', windowResize);
  1852. off(document.body, 'keyup', keyupHandler);
  1853. off(document.body, 'keydown', keydownHandler);
  1854. off(document.body, 'focus', onReset);
  1855. //move
  1856. off(document.documentElement, 'mousemove', move);
  1857. off(document.documentElement, 'mouseup', endMove);
  1858. //resize
  1859. off(document.documentElement, 'mousemove', resize);
  1860. off(document.documentElement, 'mouseup', endResize);
  1861. }
  1862. // common events
  1863. off(instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler);
  1864. off(instance.elements.footer, 'click', instance.__internal.buttonsClickHandler);
  1865. off(instance.elements.reset[0], 'focus', instance.__internal.resetHandler);
  1866. off(instance.elements.reset[1], 'focus', instance.__internal.resetHandler);
  1867. // hook out transition handler
  1868. on(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);
  1869. // modelss only events
  1870. if (!instance.get('modal')) {
  1871. unbindModelessEvents(instance);
  1872. }
  1873. // movable
  1874. if (instance.get('movable')) {
  1875. unbindMovableEvents(instance);
  1876. }
  1877. // resizable
  1878. if (instance.get('resizable')) {
  1879. unbindResizableEvents(instance);
  1880. }
  1881. }
  1882. /**
  1883. * Bind modeless specific events
  1884. *
  1885. * @param {Object} instance The dilog instance.
  1886. *
  1887. * @return {undefined}
  1888. */
  1889. function bindModelessEvents(instance) {
  1890. on(instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true);
  1891. }
  1892. /**
  1893. * Unbind modeless specific events
  1894. *
  1895. * @param {Object} instance The dilog instance.
  1896. *
  1897. * @return {undefined}
  1898. */
  1899. function unbindModelessEvents(instance) {
  1900. off(instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true);
  1901. }
  1902. /**
  1903. * Bind movable specific events
  1904. *
  1905. * @param {Object} instance The dilog instance.
  1906. *
  1907. * @return {undefined}
  1908. */
  1909. function bindMovableEvents(instance) {
  1910. on(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
  1911. on(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler);
  1912. }
  1913. /**
  1914. * Unbind movable specific events
  1915. *
  1916. * @param {Object} instance The dilog instance.
  1917. *
  1918. * @return {undefined}
  1919. */
  1920. function unbindMovableEvents(instance) {
  1921. off(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
  1922. off(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler);
  1923. }
  1924. /**
  1925. * Bind resizable specific events
  1926. *
  1927. * @param {Object} instance The dilog instance.
  1928. *
  1929. * @return {undefined}
  1930. */
  1931. function bindResizableEvents(instance) {
  1932. on(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
  1933. on(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler);
  1934. }
  1935. /**
  1936. * Unbind resizable specific events
  1937. *
  1938. * @param {Object} instance The dilog instance.
  1939. *
  1940. * @return {undefined}
  1941. */
  1942. function unbindResizableEvents(instance) {
  1943. off(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
  1944. off(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler);
  1945. }
  1946. /**
  1947. * Bind closable events
  1948. *
  1949. * @param {Object} instance The dilog instance.
  1950. *
  1951. * @return {undefined}
  1952. */
  1953. function bindClosableEvents(instance) {
  1954. on(instance.elements.modal, 'click', instance.__internal.modalClickHandler);
  1955. }
  1956. /**
  1957. * Unbind closable specific events
  1958. *
  1959. * @param {Object} instance The dilog instance.
  1960. *
  1961. * @return {undefined}
  1962. */
  1963. function unbindClosableEvents(instance) {
  1964. off(instance.elements.modal, 'click', instance.__internal.modalClickHandler);
  1965. }
  1966. // dialog API
  1967. return {
  1968. __init:initialize,
  1969. /**
  1970. * Check if dialog is currently open
  1971. *
  1972. * @return {Boolean}
  1973. */
  1974. isOpen: function () {
  1975. return this.__internal.isOpen;
  1976. },
  1977. isModal: function (){
  1978. return this.elements.root.className.indexOf(classes.modeless) < 0;
  1979. },
  1980. isMaximized:function(){
  1981. return this.elements.root.className.indexOf(classes.maximized) > -1;
  1982. },
  1983. isPinned:function(){
  1984. return this.elements.root.className.indexOf(classes.unpinned) < 0;
  1985. },
  1986. maximize:function(){
  1987. if(!this.isMaximized()){
  1988. maximize(this);
  1989. }
  1990. return this;
  1991. },
  1992. restore:function(){
  1993. if(this.isMaximized()){
  1994. restore(this);
  1995. }
  1996. return this;
  1997. },
  1998. pin:function(){
  1999. if(!this.isPinned()){
  2000. pin(this);
  2001. }
  2002. return this;
  2003. },
  2004. unpin:function(){
  2005. if(this.isPinned()){
  2006. unpin(this);
  2007. }
  2008. return this;
  2009. },
  2010. bringToFront:function(){
  2011. bringToFront(null, this);
  2012. return this;
  2013. },
  2014. /**
  2015. * Move the dialog to a specific x/y coordinates
  2016. *
  2017. * @param {Number} x The new dialog x coordinate in pixels.
  2018. * @param {Number} y The new dialog y coordinate in pixels.
  2019. *
  2020. * @return {Object} The dialog instance.
  2021. */
  2022. moveTo:function(x,y){
  2023. if(!isNaN(x) && !isNaN(y)){
  2024. // allow custom `onmove` method
  2025. dispatchEvent('onmove', this);
  2026. var element = this.elements.dialog,
  2027. current = element,
  2028. offsetLeft = 0,
  2029. offsetTop = 0;
  2030. //subtract existing left,top
  2031. if (element.style.left) {
  2032. offsetLeft -= parseInt(element.style.left, 10);
  2033. }
  2034. if (element.style.top) {
  2035. offsetTop -= parseInt(element.style.top, 10);
  2036. }
  2037. //calc offset
  2038. do {
  2039. offsetLeft += current.offsetLeft;
  2040. offsetTop += current.offsetTop;
  2041. } while (current = current.offsetParent);
  2042. //calc left, top
  2043. var left = (x - offsetLeft);
  2044. var top = (y - offsetTop);
  2045. //// rtl handling
  2046. if (isRightToLeft()) {
  2047. left *= -1;
  2048. }
  2049. element.style.left = left + 'px';
  2050. element.style.top = top + 'px';
  2051. // allow custom `onmoved` method
  2052. dispatchEvent('onmoved', this);
  2053. }
  2054. return this;
  2055. },
  2056. /**
  2057. * Resize the dialog to a specific width/height (the dialog must be 'resizable').
  2058. * The dialog can be resized to:
  2059. * A minimum width equal to the initial display width
  2060. * A minimum height equal to the sum of header/footer heights.
  2061. *
  2062. *
  2063. * @param {Number or String} width The new dialog width in pixels or in percent.
  2064. * @param {Number or String} height The new dialog height in pixels or in percent.
  2065. *
  2066. * @return {Object} The dialog instance.
  2067. */
  2068. resizeTo:function(width,height){
  2069. var w = parseFloat(width),
  2070. h = parseFloat(height),
  2071. regex = /(\d*\.\d+|\d+)%/
  2072. ;
  2073. if(!isNaN(w) && !isNaN(h) && this.get('resizable') === true){
  2074. // allow custom `onresize` method
  2075. dispatchEvent('onresize', this);
  2076. if(('' + width).match(regex)){
  2077. w = w / 100 * document.documentElement.clientWidth ;
  2078. }
  2079. if(('' + height).match(regex)){
  2080. h = h / 100 * document.documentElement.clientHeight;
  2081. }
  2082. var element = this.elements.dialog;
  2083. if (element.style.maxWidth !== 'none') {
  2084. element.style.minWidth = (minWidth = element.offsetWidth) + 'px';
  2085. }
  2086. element.style.maxWidth = 'none';
  2087. element.style.minHeight = this.elements.header.offsetHeight + this.elements.footer.offsetHeight + 'px';
  2088. element.style.width = w + 'px';
  2089. element.style.height = h + 'px';
  2090. // allow custom `onresized` method
  2091. dispatchEvent('onresized', this);
  2092. }
  2093. return this;
  2094. },
  2095. /**
  2096. * Gets or Sets dialog settings/options
  2097. *
  2098. * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
  2099. * @param {Object} value Optional, the value associated with the key (in case it was a string).
  2100. *
  2101. * @return {undefined}
  2102. */
  2103. setting : function (key, value) {
  2104. var self = this;
  2105. var result = update(this, this.__internal.options, function(k,o,n){ optionUpdated(self,k,o,n); }, key, value);
  2106. if(result.op === 'get'){
  2107. if(result.found){
  2108. return result.value;
  2109. }else if(typeof this.settings !== 'undefined'){
  2110. return update(this, this.settings, this.settingUpdated || function(){}, key, value).value;
  2111. }else{
  2112. return undefined;
  2113. }
  2114. }else if(result.op === 'set'){
  2115. if(result.items.length > 0){
  2116. var callback = this.settingUpdated || function(){};
  2117. for(var x=0;x<result.items.length;x+=1){
  2118. var item = result.items[x];
  2119. if(!item.found && typeof this.settings !== 'undefined'){
  2120. update(this, this.settings, callback, item.key, item.value);
  2121. }
  2122. }
  2123. }
  2124. return this;
  2125. }
  2126. },
  2127. /**
  2128. * [Alias] Sets dialog settings/options
  2129. */
  2130. set:function(key, value){
  2131. this.setting(key,value);
  2132. return this;
  2133. },
  2134. /**
  2135. * [Alias] Gets dialog settings/options
  2136. */
  2137. get:function(key){
  2138. return this.setting(key);
  2139. },
  2140. /**
  2141. * Sets dialog header
  2142. * @content {string or element}
  2143. *
  2144. * @return {undefined}
  2145. */
  2146. setHeader:function(content){
  2147. if(typeof content === 'string'){
  2148. clearContents(this.elements.header);
  2149. this.elements.header.innerHTML = content;
  2150. }else if (content instanceof window.HTMLElement && this.elements.header.firstChild !== content){
  2151. clearContents(this.elements.header);
  2152. this.elements.header.appendChild(content);
  2153. }
  2154. return this;
  2155. },
  2156. /**
  2157. * Sets dialog contents
  2158. * @content {string or element}
  2159. *
  2160. * @return {undefined}
  2161. */
  2162. setContent:function(content){
  2163. if(typeof content === 'string'){
  2164. clearContents(this.elements.content);
  2165. this.elements.content.innerHTML = content;
  2166. }else if (content instanceof window.HTMLElement && this.elements.content.firstChild !== content){
  2167. clearContents(this.elements.content);
  2168. this.elements.content.appendChild(content);
  2169. }
  2170. return this;
  2171. },
  2172. /**
  2173. * Show the dialog as modal
  2174. *
  2175. * @return {Object} the dialog instance.
  2176. */
  2177. showModal: function(className){
  2178. return this.show(true, className);
  2179. },
  2180. /**
  2181. * Show the dialog
  2182. *
  2183. * @return {Object} the dialog instance.
  2184. */
  2185. show: function (modal, className) {
  2186. // ensure initialization
  2187. initialize(this);
  2188. if ( !this.__internal.isOpen ) {
  2189. // add to open dialogs
  2190. this.__internal.isOpen = true;
  2191. openDialogs.push(this);
  2192. // save last focused element
  2193. if(alertify.defaults.maintainFocus){
  2194. this.__internal.activeElement = document.activeElement;
  2195. }
  2196. // set tabindex attribute on body element this allows script to give it focusable
  2197. if(!document.body.hasAttribute('tabindex')) {
  2198. document.body.setAttribute( 'tabindex', tabindex = '0');
  2199. }
  2200. //allow custom dom manipulation updates before showing the dialog.
  2201. if(typeof this.prepare === 'function'){
  2202. this.prepare();
  2203. }
  2204. bindEvents(this);
  2205. if(modal !== undefined){
  2206. this.set('modal', modal);
  2207. }
  2208. //save scroll to prevent document jump
  2209. saveScrollPosition();
  2210. ensureNoOverflow();
  2211. // allow custom dialog class on show
  2212. if(typeof className === 'string' && className !== ''){
  2213. this.__internal.className = className;
  2214. addClass(this.elements.root, className);
  2215. }
  2216. // maximize if start maximized
  2217. if ( this.get('startMaximized')) {
  2218. this.maximize();
  2219. }else if(this.isMaximized()){
  2220. restore(this);
  2221. }
  2222. updateAbsPositionFix(this);
  2223. this.elements.root.removeAttribute('style');
  2224. removeClass(this.elements.root, classes.animationOut);
  2225. addClass(this.elements.root, classes.animationIn);
  2226. // set 1s fallback in case transition event doesn't fire
  2227. clearTimeout( this.__internal.timerIn);
  2228. this.__internal.timerIn = setTimeout( this.__internal.transitionInHandler, transition.supported ? 1000 : 100 );
  2229. if(isSafari){
  2230. // force desktop safari reflow
  2231. var root = this.elements.root;
  2232. root.style.display = 'none';
  2233. setTimeout(function(){root.style.display = 'block';}, 0);
  2234. }
  2235. //reflow
  2236. reflow = this.elements.root.offsetWidth;
  2237. // show dialog
  2238. removeClass(this.elements.root, classes.hidden);
  2239. // internal on show event
  2240. if(typeof this.hooks.onshow === 'function'){
  2241. this.hooks.onshow.call(this);
  2242. }
  2243. // allow custom `onshow` method
  2244. dispatchEvent('onshow', this);
  2245. }else{
  2246. // reset move updates
  2247. resetMove(this);
  2248. // reset resize updates
  2249. resetResize(this);
  2250. // shake the dialog to indicate its already open
  2251. addClass(this.elements.dialog, classes.shake);
  2252. var self = this;
  2253. setTimeout(function(){
  2254. removeClass(self.elements.dialog, classes.shake);
  2255. },200);
  2256. }
  2257. return this;
  2258. },
  2259. /**
  2260. * Close the dialog
  2261. *
  2262. * @return {Object} The dialog instance
  2263. */
  2264. close: function () {
  2265. if (this.__internal.isOpen ) {
  2266. // custom `onclosing` event
  2267. if(dispatchEvent('onclosing', this) !== false){
  2268. unbindEvents(this);
  2269. removeClass(this.elements.root, classes.animationIn);
  2270. addClass(this.elements.root, classes.animationOut);
  2271. // set 1s fallback in case transition event doesn't fire
  2272. clearTimeout( this.__internal.timerOut );
  2273. this.__internal.timerOut = setTimeout( this.__internal.transitionOutHandler, transition.supported ? 1000 : 100 );
  2274. // hide dialog
  2275. addClass(this.elements.root, classes.hidden);
  2276. //reflow
  2277. reflow = this.elements.modal.offsetWidth;
  2278. // remove custom dialog class on hide
  2279. if (typeof this.__internal.className !== 'undefined' && this.__internal.className !== '') {
  2280. removeClass(this.elements.root, this.__internal.className);
  2281. }
  2282. // internal on close event
  2283. if(typeof this.hooks.onclose === 'function'){
  2284. this.hooks.onclose.call(this);
  2285. }
  2286. // allow custom `onclose` method
  2287. dispatchEvent('onclose', this);
  2288. //remove from open dialogs
  2289. openDialogs.splice(openDialogs.indexOf(this),1);
  2290. this.__internal.isOpen = false;
  2291. ensureNoOverflow();
  2292. }
  2293. }
  2294. // last dialog and tab index was set by us, remove it.
  2295. if(!openDialogs.length && tabindex === '0'){
  2296. document.body.removeAttribute('tabindex');
  2297. }
  2298. return this;
  2299. },
  2300. /**
  2301. * Close all open dialogs except this.
  2302. *
  2303. * @return {undefined}
  2304. */
  2305. closeOthers:function(){
  2306. alertify.closeAll(this);
  2307. return this;
  2308. },
  2309. /**
  2310. * Destroys this dialog instance
  2311. *
  2312. * @return {undefined}
  2313. */
  2314. destroy:function(){
  2315. if(this.__internal) {
  2316. if (this.__internal.isOpen ) {
  2317. //mark dialog for destruction, this will be called on tranistionOut event.
  2318. this.__internal.destroy = function(){
  2319. destruct(this, initialize);
  2320. };
  2321. //close the dialog to unbind all events.
  2322. this.close();
  2323. }else if(!this.__internal.destroy){
  2324. destruct(this, initialize);
  2325. }
  2326. }
  2327. return this;
  2328. },
  2329. };
  2330. } () );
  2331. var notifier = (function () {
  2332. var reflow,
  2333. element,
  2334. openInstances = [],
  2335. classes = {
  2336. base: 'alertify-notifier',
  2337. message: 'ajs-message',
  2338. top: 'ajs-top',
  2339. right: 'ajs-right',
  2340. bottom: 'ajs-bottom',
  2341. left: 'ajs-left',
  2342. center: 'ajs-center',
  2343. visible: 'ajs-visible',
  2344. hidden: 'ajs-hidden',
  2345. close: 'ajs-close'
  2346. };
  2347. /**
  2348. * Helper: initializes the notifier instance
  2349. *
  2350. */
  2351. function initialize(instance) {
  2352. if (!instance.__internal) {
  2353. instance.__internal = {
  2354. position: alertify.defaults.notifier.position,
  2355. delay: alertify.defaults.notifier.delay,
  2356. };
  2357. element = document.createElement('DIV');
  2358. updatePosition(instance);
  2359. }
  2360. //add to DOM tree.
  2361. if (element.parentNode !== document.body) {
  2362. document.body.appendChild(element);
  2363. }
  2364. }
  2365. function pushInstance(instance) {
  2366. instance.__internal.pushed = true;
  2367. openInstances.push(instance);
  2368. }
  2369. function popInstance(instance) {
  2370. openInstances.splice(openInstances.indexOf(instance), 1);
  2371. instance.__internal.pushed = false;
  2372. }
  2373. /**
  2374. * Helper: update the notifier instance position
  2375. *
  2376. */
  2377. function updatePosition(instance) {
  2378. element.className = classes.base;
  2379. switch (instance.__internal.position) {
  2380. case 'top-right':
  2381. addClass(element, classes.top + ' ' + classes.right);
  2382. break;
  2383. case 'top-left':
  2384. addClass(element, classes.top + ' ' + classes.left);
  2385. break;
  2386. case 'top-center':
  2387. addClass(element, classes.top + ' ' + classes.center);
  2388. break;
  2389. case 'bottom-left':
  2390. addClass(element, classes.bottom + ' ' + classes.left);
  2391. break;
  2392. case 'bottom-center':
  2393. addClass(element, classes.bottom + ' ' + classes.center);
  2394. break;
  2395. default:
  2396. case 'bottom-right':
  2397. addClass(element, classes.bottom + ' ' + classes.right);
  2398. break;
  2399. }
  2400. }
  2401. /**
  2402. * creates a new notification message
  2403. *
  2404. * @param {DOMElement} message The notifier message element
  2405. * @param {Number} wait Time (in ms) to wait before the message is dismissed, a value of 0 means keep open till clicked.
  2406. * @param {Function} callback A callback function to be invoked when the message is dismissed.
  2407. *
  2408. * @return {undefined}
  2409. */
  2410. function create(div, callback) {
  2411. function clickDelegate(event, instance) {
  2412. if(!instance.__internal.closeButton || event.target.getAttribute('data-close') === 'true'){
  2413. instance.dismiss(true);
  2414. }
  2415. }
  2416. function transitionDone(event, instance) {
  2417. // unbind event
  2418. off(instance.element, transition.type, transitionDone);
  2419. // remove the message
  2420. element.removeChild(instance.element);
  2421. }
  2422. function initialize(instance) {
  2423. if (!instance.__internal) {
  2424. instance.__internal = {
  2425. pushed: false,
  2426. delay : undefined,
  2427. timer: undefined,
  2428. clickHandler: undefined,
  2429. transitionEndHandler: undefined,
  2430. transitionTimeout: undefined
  2431. };
  2432. instance.__internal.clickHandler = delegate(instance, clickDelegate);
  2433. instance.__internal.transitionEndHandler = delegate(instance, transitionDone);
  2434. }
  2435. return instance;
  2436. }
  2437. function clearTimers(instance) {
  2438. clearTimeout(instance.__internal.timer);
  2439. clearTimeout(instance.__internal.transitionTimeout);
  2440. }
  2441. return initialize({
  2442. /* notification DOM element*/
  2443. element: div,
  2444. /*
  2445. * Pushes a notification message
  2446. * @param {string or DOMElement} content The notification message content
  2447. * @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked.
  2448. *
  2449. */
  2450. push: function (_content, _wait) {
  2451. if (!this.__internal.pushed) {
  2452. pushInstance(this);
  2453. clearTimers(this);
  2454. var content, wait;
  2455. switch (arguments.length) {
  2456. case 0:
  2457. wait = this.__internal.delay;
  2458. break;
  2459. case 1:
  2460. if (typeof (_content) === 'number') {
  2461. wait = _content;
  2462. } else {
  2463. content = _content;
  2464. wait = this.__internal.delay;
  2465. }
  2466. break;
  2467. case 2:
  2468. content = _content;
  2469. wait = _wait;
  2470. break;
  2471. }
  2472. this.__internal.closeButton = alertify.defaults.notifier.closeButton;
  2473. // set contents
  2474. if (typeof content !== 'undefined') {
  2475. this.setContent(content);
  2476. }
  2477. // append or insert
  2478. if (notifier.__internal.position.indexOf('top') < 0) {
  2479. element.appendChild(this.element);
  2480. } else {
  2481. element.insertBefore(this.element, element.firstChild);
  2482. }
  2483. reflow = this.element.offsetWidth;
  2484. addClass(this.element, classes.visible);
  2485. // attach click event
  2486. on(this.element, 'click', this.__internal.clickHandler);
  2487. return this.delay(wait);
  2488. }
  2489. return this;
  2490. },
  2491. /*
  2492. * {Function} callback function to be invoked before dismissing the notification message.
  2493. * Remarks: A return value === 'false' will cancel the dismissal
  2494. *
  2495. */
  2496. ondismiss: function () { },
  2497. /*
  2498. * {Function} callback function to be invoked when the message is dismissed.
  2499. *
  2500. */
  2501. callback: callback,
  2502. /*
  2503. * Dismisses the notification message
  2504. * @param {Boolean} clicked A flag indicating if the dismissal was caused by a click.
  2505. *
  2506. */
  2507. dismiss: function (clicked) {
  2508. if (this.__internal.pushed) {
  2509. clearTimers(this);
  2510. if (!(typeof this.ondismiss === 'function' && this.ondismiss.call(this) === false)) {
  2511. //detach click event
  2512. off(this.element, 'click', this.__internal.clickHandler);
  2513. // ensure element exists
  2514. if (typeof this.element !== 'undefined' && this.element.parentNode === element) {
  2515. //transition end or fallback
  2516. this.__internal.transitionTimeout = setTimeout(this.__internal.transitionEndHandler, transition.supported ? 1000 : 100);
  2517. removeClass(this.element, classes.visible);
  2518. // custom callback on dismiss
  2519. if (typeof this.callback === 'function') {
  2520. this.callback.call(this, clicked);
  2521. }
  2522. }
  2523. popInstance(this);
  2524. }
  2525. }
  2526. return this;
  2527. },
  2528. /*
  2529. * Delays the notification message dismissal
  2530. * @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked.
  2531. *
  2532. */
  2533. delay: function (wait) {
  2534. clearTimers(this);
  2535. this.__internal.delay = typeof wait !== 'undefined' && !isNaN(+wait) ? +wait : notifier.__internal.delay;
  2536. if (this.__internal.delay > 0) {
  2537. var self = this;
  2538. this.__internal.timer = setTimeout(function () { self.dismiss(); }, this.__internal.delay * 1000);
  2539. }
  2540. return this;
  2541. },
  2542. /*
  2543. * Sets the notification message contents
  2544. * @param {string or DOMElement} content The notification message content
  2545. *
  2546. */
  2547. setContent: function (content) {
  2548. if (typeof content === 'string') {
  2549. clearContents(this.element);
  2550. this.element.innerHTML = content;
  2551. } else if (content instanceof window.HTMLElement && this.element.firstChild !== content) {
  2552. clearContents(this.element);
  2553. this.element.appendChild(content);
  2554. }
  2555. if(this.__internal.closeButton){
  2556. var close = document.createElement('span');
  2557. addClass(close, classes.close);
  2558. close.setAttribute('data-close', true);
  2559. this.element.appendChild(close);
  2560. }
  2561. return this;
  2562. },
  2563. /*
  2564. * Dismisses all open notifications except this.
  2565. *
  2566. */
  2567. dismissOthers: function () {
  2568. notifier.dismissAll(this);
  2569. return this;
  2570. }
  2571. });
  2572. }
  2573. //notifier api
  2574. return {
  2575. /**
  2576. * Gets or Sets notifier settings.
  2577. *
  2578. * @param {string} key The setting name
  2579. * @param {Variant} value The setting value.
  2580. *
  2581. * @return {Object} if the called as a setter, return the notifier instance.
  2582. */
  2583. setting: function (key, value) {
  2584. //ensure init
  2585. initialize(this);
  2586. if (typeof value === 'undefined') {
  2587. //get
  2588. return this.__internal[key];
  2589. } else {
  2590. //set
  2591. switch (key) {
  2592. case 'position':
  2593. this.__internal.position = value;
  2594. updatePosition(this);
  2595. break;
  2596. case 'delay':
  2597. this.__internal.delay = value;
  2598. break;
  2599. }
  2600. }
  2601. return this;
  2602. },
  2603. /**
  2604. * [Alias] Sets dialog settings/options
  2605. */
  2606. set:function(key,value){
  2607. this.setting(key,value);
  2608. return this;
  2609. },
  2610. /**
  2611. * [Alias] Gets dialog settings/options
  2612. */
  2613. get:function(key){
  2614. return this.setting(key);
  2615. },
  2616. /**
  2617. * Creates a new notification message
  2618. *
  2619. * @param {string} type The type of notification message (simply a CSS class name 'ajs-{type}' to be added).
  2620. * @param {Function} callback A callback function to be invoked when the message is dismissed.
  2621. *
  2622. * @return {undefined}
  2623. */
  2624. create: function (type, callback) {
  2625. //ensure notifier init
  2626. initialize(this);
  2627. //create new notification message
  2628. var div = document.createElement('div');
  2629. div.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ajs-' + type : '');
  2630. return create(div, callback);
  2631. },
  2632. /**
  2633. * Dismisses all open notifications.
  2634. *
  2635. * @param {Object} excpet [optional] The notification object to exclude from dismissal.
  2636. *
  2637. */
  2638. dismissAll: function (except) {
  2639. var clone = openInstances.slice(0);
  2640. for (var x = 0; x < clone.length; x += 1) {
  2641. var instance = clone[x];
  2642. if (except === undefined || except !== instance) {
  2643. instance.dismiss();
  2644. }
  2645. }
  2646. }
  2647. };
  2648. })();
  2649. /**
  2650. * Alertify public API
  2651. * This contains everything that is exposed through the alertify object.
  2652. *
  2653. * @return {Object}
  2654. */
  2655. function Alertify() {
  2656. // holds a references of created dialogs
  2657. var dialogs = {};
  2658. /**
  2659. * Extends a given prototype by merging properties from base into sub.
  2660. *
  2661. * @sub {Object} sub The prototype being overwritten.
  2662. * @base {Object} base The prototype being written.
  2663. *
  2664. * @return {Object} The extended prototype.
  2665. */
  2666. function extend(sub, base) {
  2667. // copy dialog pototype over definition.
  2668. for (var prop in base) {
  2669. if (base.hasOwnProperty(prop)) {
  2670. sub[prop] = base[prop];
  2671. }
  2672. }
  2673. return sub;
  2674. }
  2675. /**
  2676. * Helper: returns a dialog instance from saved dialogs.
  2677. * and initializes the dialog if its not already initialized.
  2678. *
  2679. * @name {String} name The dialog name.
  2680. *
  2681. * @return {Object} The dialog instance.
  2682. */
  2683. function get_dialog(name) {
  2684. var dialog = dialogs[name].dialog;
  2685. //initialize the dialog if its not already initialized.
  2686. if (dialog && typeof dialog.__init === 'function') {
  2687. dialog.__init(dialog);
  2688. }
  2689. return dialog;
  2690. }
  2691. /**
  2692. * Helper: registers a new dialog definition.
  2693. *
  2694. * @name {String} name The dialog name.
  2695. * @Factory {Function} Factory a function resposible for creating dialog prototype.
  2696. * @transient {Boolean} transient True to create a new dialog instance each time the dialog is invoked, false otherwise.
  2697. * @base {String} base the name of another dialog to inherit from.
  2698. *
  2699. * @return {Object} The dialog definition.
  2700. */
  2701. function register(name, Factory, transient, base) {
  2702. var definition = {
  2703. dialog: null,
  2704. factory: Factory
  2705. };
  2706. //if this is based on an existing dialog, create a new definition
  2707. //by applying the new protoype over the existing one.
  2708. if (base !== undefined) {
  2709. definition.factory = function () {
  2710. return extend(new dialogs[base].factory(), new Factory());
  2711. };
  2712. }
  2713. if (!transient) {
  2714. //create a new definition based on dialog
  2715. definition.dialog = extend(new definition.factory(), dialog);
  2716. }
  2717. return dialogs[name] = definition;
  2718. }
  2719. return {
  2720. /**
  2721. * Alertify defaults
  2722. *
  2723. * @type {Object}
  2724. */
  2725. defaults: defaults,
  2726. /**
  2727. * Dialogs factory
  2728. *
  2729. * @param {string} Dialog name.
  2730. * @param {Function} A Dialog factory function.
  2731. * @param {Boolean} Indicates whether to create a singleton or transient dialog.
  2732. * @param {String} The name of the base type to inherit from.
  2733. */
  2734. dialog: function (name, Factory, transient, base) {
  2735. // get request, create a new instance and return it.
  2736. if (typeof Factory !== 'function') {
  2737. return get_dialog(name);
  2738. }
  2739. if (this.hasOwnProperty(name)) {
  2740. throw new Error('alertify.dialog: name already exists');
  2741. }
  2742. // register the dialog
  2743. var definition = register(name, Factory, transient, base);
  2744. if (transient) {
  2745. // make it public
  2746. this[name] = function () {
  2747. //if passed with no params, consider it a get request
  2748. if (arguments.length === 0) {
  2749. return definition.dialog;
  2750. } else {
  2751. var instance = extend(new definition.factory(), dialog);
  2752. //ensure init
  2753. if (instance && typeof instance.__init === 'function') {
  2754. instance.__init(instance);
  2755. }
  2756. instance['main'].apply(instance, arguments);
  2757. return instance['show'].apply(instance);
  2758. }
  2759. };
  2760. } else {
  2761. // make it public
  2762. this[name] = function () {
  2763. //ensure init
  2764. if (definition.dialog && typeof definition.dialog.__init === 'function') {
  2765. definition.dialog.__init(definition.dialog);
  2766. }
  2767. //if passed with no params, consider it a get request
  2768. if (arguments.length === 0) {
  2769. return definition.dialog;
  2770. } else {
  2771. var dialog = definition.dialog;
  2772. dialog['main'].apply(definition.dialog, arguments);
  2773. return dialog['show'].apply(definition.dialog);
  2774. }
  2775. };
  2776. }
  2777. },
  2778. /**
  2779. * Close all open dialogs.
  2780. *
  2781. * @param {Object} excpet [optional] The dialog object to exclude from closing.
  2782. *
  2783. * @return {undefined}
  2784. */
  2785. closeAll: function (except) {
  2786. var clone = openDialogs.slice(0);
  2787. for (var x = 0; x < clone.length; x += 1) {
  2788. var instance = clone[x];
  2789. if (except === undefined || except !== instance) {
  2790. instance.close();
  2791. }
  2792. }
  2793. },
  2794. /**
  2795. * Gets or Sets dialog settings/options. if the dialog is transient, this call does nothing.
  2796. *
  2797. * @param {string} name The dialog name.
  2798. * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
  2799. * @param {Variant} value Optional, the value associated with the key (in case it was a string).
  2800. *
  2801. * @return {undefined}
  2802. */
  2803. setting: function (name, key, value) {
  2804. if (name === 'notifier') {
  2805. return notifier.setting(key, value);
  2806. }
  2807. var dialog = get_dialog(name);
  2808. if (dialog) {
  2809. return dialog.setting(key, value);
  2810. }
  2811. },
  2812. /**
  2813. * [Alias] Sets dialog settings/options
  2814. */
  2815. set: function(name,key,value){
  2816. return this.setting(name, key,value);
  2817. },
  2818. /**
  2819. * [Alias] Gets dialog settings/options
  2820. */
  2821. get: function(name, key){
  2822. return this.setting(name, key);
  2823. },
  2824. /**
  2825. * Creates a new notification message.
  2826. * If a type is passed, a class name "ajs-{type}" will be added.
  2827. * This allows for custom look and feel for various types of notifications.
  2828. *
  2829. * @param {String | DOMElement} [message=undefined] Message text
  2830. * @param {String} [type=''] Type of log message
  2831. * @param {String} [wait=''] Time (in seconds) to wait before auto-close
  2832. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2833. *
  2834. * @return {Object} Notification object.
  2835. */
  2836. notify: function (message, type, wait, callback) {
  2837. return notifier.create(type, callback).push(message, wait);
  2838. },
  2839. /**
  2840. * Creates a new notification message.
  2841. *
  2842. * @param {String} [message=undefined] Message text
  2843. * @param {String} [wait=''] Time (in seconds) to wait before auto-close
  2844. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2845. *
  2846. * @return {Object} Notification object.
  2847. */
  2848. message: function (message, wait, callback) {
  2849. return notifier.create(null, callback).push(message, wait);
  2850. },
  2851. /**
  2852. * Creates a new notification message of type 'success'.
  2853. *
  2854. * @param {String} [message=undefined] Message text
  2855. * @param {String} [wait=''] Time (in seconds) to wait before auto-close
  2856. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2857. *
  2858. * @return {Object} Notification object.
  2859. */
  2860. success: function (message, wait, callback) {
  2861. return notifier.create('success', callback).push(message, wait);
  2862. },
  2863. /**
  2864. * Creates a new notification message of type 'error'.
  2865. *
  2866. * @param {String} [message=undefined] Message text
  2867. * @param {String} [wait=''] Time (in seconds) to wait before auto-close
  2868. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2869. *
  2870. * @return {Object} Notification object.
  2871. */
  2872. error: function (message, wait, callback) {
  2873. return notifier.create('error', callback).push(message, wait);
  2874. },
  2875. /**
  2876. * Creates a new notification message of type 'warning'.
  2877. *
  2878. * @param {String} [message=undefined] Message text
  2879. * @param {String} [wait=''] Time (in seconds) to wait before auto-close
  2880. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2881. *
  2882. * @return {Object} Notification object.
  2883. */
  2884. warning: function (message, wait, callback) {
  2885. return notifier.create('warning', callback).push(message, wait);
  2886. },
  2887. /**
  2888. * Dismisses all open notifications
  2889. *
  2890. * @return {undefined}
  2891. */
  2892. dismissAll: function () {
  2893. notifier.dismissAll();
  2894. }
  2895. };
  2896. }
  2897. var alertify = new Alertify();
  2898. /**
  2899. * Alert dialog definition
  2900. *
  2901. * invoked by:
  2902. * alertify.alert(message);
  2903. * alertify.alert(title, message);
  2904. * alertify.alert(message, onok);
  2905. * alertify.alert(title, message, onok);
  2906. */
  2907. alertify.dialog('alert', function () {
  2908. return {
  2909. main: function (_title, _message, _onok) {
  2910. var title, message, onok;
  2911. switch (arguments.length) {
  2912. case 1:
  2913. message = _title;
  2914. break;
  2915. case 2:
  2916. if (typeof _message === 'function') {
  2917. message = _title;
  2918. onok = _message;
  2919. } else {
  2920. title = _title;
  2921. message = _message;
  2922. }
  2923. break;
  2924. case 3:
  2925. title = _title;
  2926. message = _message;
  2927. onok = _onok;
  2928. break;
  2929. }
  2930. this.set('title', title);
  2931. this.set('message', message);
  2932. this.set('onok', onok);
  2933. return this;
  2934. },
  2935. setup: function () {
  2936. return {
  2937. buttons: [
  2938. {
  2939. text: alertify.defaults.glossary.ok,
  2940. key: keys.ESC,
  2941. invokeOnClose: true,
  2942. className: alertify.defaults.theme.ok,
  2943. }
  2944. ],
  2945. focus: {
  2946. element: 0,
  2947. select: false
  2948. },
  2949. options: {
  2950. maximizable: false,
  2951. resizable: false
  2952. }
  2953. };
  2954. },
  2955. build: function () {
  2956. // nothing
  2957. },
  2958. prepare: function () {
  2959. //nothing
  2960. },
  2961. setMessage: function (message) {
  2962. this.setContent(message);
  2963. },
  2964. settings: {
  2965. message: undefined,
  2966. onok: undefined,
  2967. label: undefined,
  2968. },
  2969. settingUpdated: function (key, oldValue, newValue) {
  2970. switch (key) {
  2971. case 'message':
  2972. this.setMessage(newValue);
  2973. break;
  2974. case 'label':
  2975. if (this.__internal.buttons[0].element) {
  2976. this.__internal.buttons[0].element.innerHTML = newValue;
  2977. }
  2978. break;
  2979. }
  2980. },
  2981. callback: function (closeEvent) {
  2982. if (typeof this.get('onok') === 'function') {
  2983. var returnValue = this.get('onok').call(this, closeEvent);
  2984. if (typeof returnValue !== 'undefined') {
  2985. closeEvent.cancel = !returnValue;
  2986. }
  2987. }
  2988. }
  2989. };
  2990. });
  2991. /**
  2992. * Confirm dialog object
  2993. *
  2994. * alertify.confirm(message);
  2995. * alertify.confirm(message, onok);
  2996. * alertify.confirm(message, onok, oncancel);
  2997. * alertify.confirm(title, message, onok, oncancel);
  2998. */
  2999. alertify.dialog('confirm', function () {
  3000. var autoConfirm = {
  3001. timer: null,
  3002. index: null,
  3003. text: null,
  3004. duration: null,
  3005. task: function (event, self) {
  3006. if (self.isOpen()) {
  3007. self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text + ' (&#8207;' + autoConfirm.duration + '&#8207;) ';
  3008. autoConfirm.duration -= 1;
  3009. if (autoConfirm.duration === -1) {
  3010. clearAutoConfirm(self);
  3011. var button = self.__internal.buttons[autoConfirm.index];
  3012. var closeEvent = createCloseEvent(autoConfirm.index, button);
  3013. if (typeof self.callback === 'function') {
  3014. self.callback.apply(self, [closeEvent]);
  3015. }
  3016. //close the dialog.
  3017. if (closeEvent.close !== false) {
  3018. self.close();
  3019. }
  3020. }
  3021. } else {
  3022. clearAutoConfirm(self);
  3023. }
  3024. }
  3025. };
  3026. function clearAutoConfirm(self) {
  3027. if (autoConfirm.timer !== null) {
  3028. clearInterval(autoConfirm.timer);
  3029. autoConfirm.timer = null;
  3030. self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text;
  3031. }
  3032. }
  3033. function startAutoConfirm(self, index, duration) {
  3034. clearAutoConfirm(self);
  3035. autoConfirm.duration = duration;
  3036. autoConfirm.index = index;
  3037. autoConfirm.text = self.__internal.buttons[index].element.innerHTML;
  3038. autoConfirm.timer = setInterval(delegate(self, autoConfirm.task), 1000);
  3039. autoConfirm.task(null, self);
  3040. }
  3041. return {
  3042. main: function (_title, _message, _onok, _oncancel) {
  3043. var title, message, onok, oncancel;
  3044. switch (arguments.length) {
  3045. case 1:
  3046. message = _title;
  3047. break;
  3048. case 2:
  3049. message = _title;
  3050. onok = _message;
  3051. break;
  3052. case 3:
  3053. message = _title;
  3054. onok = _message;
  3055. oncancel = _onok;
  3056. break;
  3057. case 4:
  3058. title = _title;
  3059. message = _message;
  3060. onok = _onok;
  3061. oncancel = _oncancel;
  3062. break;
  3063. }
  3064. this.set('title', title);
  3065. this.set('message', message);
  3066. this.set('onok', onok);
  3067. this.set('oncancel', oncancel);
  3068. return this;
  3069. },
  3070. setup: function () {
  3071. return {
  3072. buttons: [
  3073. {
  3074. text: alertify.defaults.glossary.ok,
  3075. key: keys.ENTER,
  3076. className: alertify.defaults.theme.ok,
  3077. },
  3078. {
  3079. text: alertify.defaults.glossary.cancel,
  3080. key: keys.ESC,
  3081. invokeOnClose: true,
  3082. className: alertify.defaults.theme.cancel,
  3083. }
  3084. ],
  3085. focus: {
  3086. element: 0,
  3087. select: false
  3088. },
  3089. options: {
  3090. maximizable: false,
  3091. resizable: false
  3092. }
  3093. };
  3094. },
  3095. build: function () {
  3096. //nothing
  3097. },
  3098. prepare: function () {
  3099. //nothing
  3100. },
  3101. setMessage: function (message) {
  3102. this.setContent(message);
  3103. },
  3104. settings: {
  3105. message: null,
  3106. labels: null,
  3107. onok: null,
  3108. oncancel: null,
  3109. defaultFocus: null,
  3110. reverseButtons: null,
  3111. },
  3112. settingUpdated: function (key, oldValue, newValue) {
  3113. switch (key) {
  3114. case 'message':
  3115. this.setMessage(newValue);
  3116. break;
  3117. case 'labels':
  3118. if ('ok' in newValue && this.__internal.buttons[0].element) {
  3119. this.__internal.buttons[0].text = newValue.ok;
  3120. this.__internal.buttons[0].element.innerHTML = newValue.ok;
  3121. }
  3122. if ('cancel' in newValue && this.__internal.buttons[1].element) {
  3123. this.__internal.buttons[1].text = newValue.cancel;
  3124. this.__internal.buttons[1].element.innerHTML = newValue.cancel;
  3125. }
  3126. break;
  3127. case 'reverseButtons':
  3128. if (newValue === true) {
  3129. this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
  3130. } else {
  3131. this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
  3132. }
  3133. break;
  3134. case 'defaultFocus':
  3135. this.__internal.focus.element = newValue === 'ok' ? 0 : 1;
  3136. break;
  3137. }
  3138. },
  3139. callback: function (closeEvent) {
  3140. clearAutoConfirm(this);
  3141. var returnValue;
  3142. switch (closeEvent.index) {
  3143. case 0:
  3144. if (typeof this.get('onok') === 'function') {
  3145. returnValue = this.get('onok').call(this, closeEvent);
  3146. if (typeof returnValue !== 'undefined') {
  3147. closeEvent.cancel = !returnValue;
  3148. }
  3149. }
  3150. break;
  3151. case 1:
  3152. if (typeof this.get('oncancel') === 'function') {
  3153. returnValue = this.get('oncancel').call(this, closeEvent);
  3154. if (typeof returnValue !== 'undefined') {
  3155. closeEvent.cancel = !returnValue;
  3156. }
  3157. }
  3158. break;
  3159. }
  3160. },
  3161. autoOk: function (duration) {
  3162. startAutoConfirm(this, 0, duration);
  3163. return this;
  3164. },
  3165. autoCancel: function (duration) {
  3166. startAutoConfirm(this, 1, duration);
  3167. return this;
  3168. }
  3169. };
  3170. });
  3171. /**
  3172. * Prompt dialog object
  3173. *
  3174. * invoked by:
  3175. * alertify.prompt(message);
  3176. * alertify.prompt(message, value);
  3177. * alertify.prompt(message, value, onok);
  3178. * alertify.prompt(message, value, onok, oncancel);
  3179. * alertify.prompt(title, message, value, onok, oncancel);
  3180. */
  3181. alertify.dialog('prompt', function () {
  3182. var input = document.createElement('INPUT');
  3183. var p = document.createElement('P');
  3184. return {
  3185. main: function (_title, _message, _value, _onok, _oncancel) {
  3186. var title, message, value, onok, oncancel;
  3187. switch (arguments.length) {
  3188. case 1:
  3189. message = _title;
  3190. break;
  3191. case 2:
  3192. message = _title;
  3193. value = _message;
  3194. break;
  3195. case 3:
  3196. message = _title;
  3197. value = _message;
  3198. onok = _value;
  3199. break;
  3200. case 4:
  3201. message = _title;
  3202. value = _message;
  3203. onok = _value;
  3204. oncancel = _onok;
  3205. break;
  3206. case 5:
  3207. title = _title;
  3208. message = _message;
  3209. value = _value;
  3210. onok = _onok;
  3211. oncancel = _oncancel;
  3212. break;
  3213. }
  3214. this.set('title', title);
  3215. this.set('message', message);
  3216. this.set('value', value);
  3217. this.set('onok', onok);
  3218. this.set('oncancel', oncancel);
  3219. return this;
  3220. },
  3221. setup: function () {
  3222. return {
  3223. buttons: [
  3224. {
  3225. text: alertify.defaults.glossary.ok,
  3226. key: keys.ENTER,
  3227. className: alertify.defaults.theme.ok,
  3228. },
  3229. {
  3230. text: alertify.defaults.glossary.cancel,
  3231. key: keys.ESC,
  3232. invokeOnClose: true,
  3233. className: alertify.defaults.theme.cancel,
  3234. }
  3235. ],
  3236. focus: {
  3237. element: input,
  3238. select: true
  3239. },
  3240. options: {
  3241. maximizable: false,
  3242. resizable: false
  3243. }
  3244. };
  3245. },
  3246. build: function () {
  3247. input.className = alertify.defaults.theme.input;
  3248. input.setAttribute('type', 'text');
  3249. input.value = this.get('value');
  3250. this.elements.content.appendChild(p);
  3251. this.elements.content.appendChild(input);
  3252. },
  3253. prepare: function () {
  3254. //nothing
  3255. },
  3256. setMessage: function (message) {
  3257. if (typeof message === 'string') {
  3258. clearContents(p);
  3259. p.innerHTML = message;
  3260. } else if (message instanceof window.HTMLElement && p.firstChild !== message) {
  3261. clearContents(p);
  3262. p.appendChild(message);
  3263. }
  3264. },
  3265. settings: {
  3266. message: undefined,
  3267. labels: undefined,
  3268. onok: undefined,
  3269. oncancel: undefined,
  3270. value: '',
  3271. type:'text',
  3272. reverseButtons: undefined,
  3273. },
  3274. settingUpdated: function (key, oldValue, newValue) {
  3275. switch (key) {
  3276. case 'message':
  3277. this.setMessage(newValue);
  3278. break;
  3279. case 'value':
  3280. input.value = newValue;
  3281. break;
  3282. case 'type':
  3283. switch (newValue) {
  3284. case 'text':
  3285. case 'color':
  3286. case 'date':
  3287. case 'datetime-local':
  3288. case 'email':
  3289. case 'month':
  3290. case 'number':
  3291. case 'password':
  3292. case 'search':
  3293. case 'tel':
  3294. case 'time':
  3295. case 'week':
  3296. input.type = newValue;
  3297. break;
  3298. default:
  3299. input.type = 'text';
  3300. break;
  3301. }
  3302. break;
  3303. case 'labels':
  3304. if (newValue.ok && this.__internal.buttons[0].element) {
  3305. this.__internal.buttons[0].element.innerHTML = newValue.ok;
  3306. }
  3307. if (newValue.cancel && this.__internal.buttons[1].element) {
  3308. this.__internal.buttons[1].element.innerHTML = newValue.cancel;
  3309. }
  3310. break;
  3311. case 'reverseButtons':
  3312. if (newValue === true) {
  3313. this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
  3314. } else {
  3315. this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
  3316. }
  3317. break;
  3318. }
  3319. },
  3320. callback: function (closeEvent) {
  3321. var returnValue;
  3322. switch (closeEvent.index) {
  3323. case 0:
  3324. this.settings.value = input.value;
  3325. if (typeof this.get('onok') === 'function') {
  3326. returnValue = this.get('onok').call(this, closeEvent, this.settings.value);
  3327. if (typeof returnValue !== 'undefined') {
  3328. closeEvent.cancel = !returnValue;
  3329. }
  3330. }
  3331. break;
  3332. case 1:
  3333. if (typeof this.get('oncancel') === 'function') {
  3334. returnValue = this.get('oncancel').call(this, closeEvent);
  3335. if (typeof returnValue !== 'undefined') {
  3336. closeEvent.cancel = !returnValue;
  3337. }
  3338. }
  3339. if(!closeEvent.cancel){
  3340. input.value = this.settings.value;
  3341. }
  3342. break;
  3343. }
  3344. }
  3345. };
  3346. });
  3347. // CommonJS
  3348. if ( typeof module === 'object' && typeof module.exports === 'object' ) {
  3349. module.exports = alertify;
  3350. // AMD
  3351. } else if ( typeof define === 'function' && define.amd) {
  3352. define( [], function () {
  3353. return alertify;
  3354. } );
  3355. // window
  3356. } else if ( !window.alertify ) {
  3357. window.alertify = alertify;
  3358. }
  3359. } ( typeof window !== 'undefined' ? window : this ) );