/build/alertify.js

https://github.com/alihalabyah/AlertifyJS · JavaScript · 2769 lines · 1651 code · 209 blank · 909 comment · 247 complexity · e326dcdfa9255ae037b3488a86d91c19 MD5 · raw file

  1. /**
  2. * AlertifyJS
  3. * AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
  4. *
  5. * @author Mohammad Younes <Mohammad@alertifyjs.com> (http://alertifyjs.com)
  6. * @copyright 2014
  7. * @license MIT <http://opensource.org/licenses/mit-license.php>
  8. * @link http://alertifyjs.com
  9. * @module AlertifyJS
  10. * @version 0.0.0
  11. */
  12. ( function ( window ) {
  13. 'use strict';
  14. /**
  15. * Keys enum
  16. * @type {Object}
  17. */
  18. var keys = {
  19. ENTER: 13,
  20. ESC: 27
  21. };
  22. /**
  23. * Default options
  24. * @type {Object}
  25. */
  26. var defaults = {
  27. modal:true,
  28. movable:true,
  29. resizable:true,
  30. closable:true,
  31. maximizable:true,
  32. pinnable:true,
  33. pinned:true,
  34. transition:'pulse',
  35. padding: true,
  36. overflow:true,
  37. notifier:{
  38. delay:5,
  39. position:'bottom-right'
  40. },
  41. glossary:{
  42. title:'AlertifyJS',
  43. ok: 'OK',
  44. cancel: 'Cancel',
  45. acccpt: 'Accept',
  46. deny: 'Deny',
  47. confirm: 'Confirm',
  48. decline: 'Decline',
  49. close: 'Close',
  50. maximize: 'Maximize',
  51. restore: 'Restore',
  52. },
  53. theme:{
  54. input:'ajs-input',
  55. ok:'ajs-ok',
  56. cancel:'ajs-cancel',
  57. }
  58. };
  59. /**
  60. * [Helper] Adds the specified class(es) to the element.
  61. *
  62. * @element {node} The element
  63. * @className {string} One or more space-separated classes to be added to the class attribute of the element.
  64. *
  65. * @return {undefined}
  66. */
  67. function addClass(element,classNames){
  68. element.className += ' ' + classNames;
  69. }
  70. /**
  71. * [Helper] Removes the specified class(es) from the element.
  72. *
  73. * @element {node} The element
  74. * @className {string} One or more space-separated classes to be removed from the class attribute of the element.
  75. *
  76. * @return {undefined}
  77. */
  78. function removeClass(element,classNames){
  79. var classes = classNames.split(' ');
  80. for(var x=0;x<classes.length;x+=1){
  81. element.className = element.className.replace(' ' + classes[x], '');
  82. }
  83. }
  84. /**
  85. * [Helper] Checks if the document is RTL
  86. *
  87. * @return {Boolean} True if the document is RTL, false otherwise.
  88. */
  89. function isRightToLeft(){
  90. return window.getComputedStyle(document.body).direction === 'rtl';
  91. }
  92. /**
  93. * [Helper] Get the document current scrollTop
  94. *
  95. * @return {Number} current document scrollTop value
  96. */
  97. function getScrollTop(){
  98. return ((document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop);
  99. }
  100. /**
  101. * [Helper] Get the document current scrollLeft
  102. *
  103. * @return {Number} current document scrollLeft value
  104. */
  105. function getScrollLeft(){
  106. return ((document.documentElement && document.documentElement.scrollLeft) || document.body.scrollLeft);
  107. }
  108. /**
  109. * Use a closure to return proper event listener method. Try to use
  110. * `addEventListener` by default but fallback to `attachEvent` for
  111. * unsupported browser. The closure simply ensures that the test doesn't
  112. * happen every time the method is called.
  113. *
  114. * @param {Node} el Node element
  115. * @param {String} event Event type
  116. * @param {Function} fn Callback of event
  117. * @return {Function}
  118. */
  119. var on = ( function () {
  120. if ( document.addEventListener ) {
  121. return function ( el, event, fn, useCapture ) {
  122. el.addEventListener( event, fn, useCapture === true );
  123. };
  124. } else if ( document.attachEvent ) {
  125. return function ( el, event, fn ) {
  126. el.attachEvent( 'on' + event, fn );
  127. };
  128. }
  129. } () );
  130. /**
  131. * Use a closure to return proper event listener method. Try to use
  132. * `removeEventListener` by default but fallback to `detachEvent` for
  133. * unsupported browser. The closure simply ensures that the test doesn't
  134. * happen every time the method is called.
  135. *
  136. * @param {Node} el Node element
  137. * @param {String} event Event type
  138. * @param {Function} fn Callback of event
  139. * @return {Function}
  140. */
  141. var off = ( function () {
  142. if ( document.removeEventListener ) {
  143. return function ( el, event, fn, useCapture ) {
  144. el.removeEventListener( event, fn, useCapture === true );
  145. };
  146. } else if ( document.detachEvent ) {
  147. return function ( el, event, fn ) {
  148. el.detachEvent( 'on' + event, fn );
  149. };
  150. }
  151. } () );
  152. /**
  153. * Prevent default event from firing
  154. *
  155. * @param {Event} event Event object
  156. * @return {undefined}
  157. function prevent ( event ) {
  158. if ( event ) {
  159. if ( event.preventDefault ) {
  160. event.preventDefault();
  161. } else {
  162. event.returnValue = false;
  163. }
  164. }
  165. }
  166. */
  167. var transition = ( function () {
  168. var t, type;
  169. var supported = false;
  170. var el = document.createElement( 'fakeelement' );
  171. var transitions = {
  172. 'WebkitTransition': 'webkitTransitionEnd',
  173. 'MozTransition': 'transitionend',
  174. 'OTransition': 'otransitionend',
  175. 'transition': 'transitionend'
  176. };
  177. for ( t in transitions ) {
  178. if ( el.style[ t ] !== undefined ) {
  179. type = transitions[ t ];
  180. supported = true;
  181. break;
  182. }
  183. }
  184. return {
  185. type: type,
  186. supported: supported
  187. };
  188. } () );
  189. /**
  190. * Creates event handler delegate that sends the instance as last argument.
  191. *
  192. * @return {Function} a function wrapper which sends the instance as last argument.
  193. */
  194. function delegate(context,method){
  195. return function(){
  196. if ( arguments.length > 0 ) {
  197. var args = [];
  198. for(var x=0; x<arguments.length;x+=1){
  199. args.push(arguments[x]);
  200. }
  201. args.push(context);
  202. return method.apply(context, args);
  203. }
  204. return method.apply(context, [null,context]);
  205. };
  206. }
  207. /**
  208. * Helper for creating a dialog close event.
  209. *
  210. * @return {object}
  211. */
  212. function createCloseEvent(index, button){
  213. return {
  214. index: index,
  215. button: button,
  216. cancel: false
  217. };
  218. }
  219. /**
  220. * Super class for all dialogs
  221. *
  222. * @return {Object} base dialog prototype
  223. */
  224. var dialog = (function () {
  225. //holds open dialogs instances
  226. var openInstances = [],
  227. //dummy variable, used to trigger dom reflow.
  228. reflow = null,
  229. //dialog building blocks
  230. templates = {
  231. dimmer:'<div class="ajs-dimmer"></div>',
  232. /*tab index required to fire click event before body focus*/
  233. modal: '<div class="ajs-modal" tabindex="0"></div>',
  234. dialog: '<div class="ajs-dialog" tabindex="0"></div>',
  235. reset: '<a class="ajs-reset" href="#"></a>',
  236. commands: '<div class="ajs-commands"><button class="ajs-pin"></button><button class="ajs-maximize"></button><button class="ajs-close"></button></div>',
  237. header: '<div class="ajs-header"></div>',
  238. body: '<div class="ajs-body"></div>',
  239. content: '<div class="ajs-content"></div>',
  240. footer: '<div class="ajs-footer"></div>',
  241. buttons: { primary: '<div class="ajs-primary ajs-buttons"></div>', auxiliary: '<div class="ajs-auxiliary ajs-buttons"></div>' },
  242. button: '<button class="ajs-button"></button>',
  243. resizeHandle: '<div class="ajs-handle"></div>',
  244. },
  245. //common class names
  246. classes = {
  247. base: 'alertify',
  248. prefix: 'ajs-',
  249. hidden: 'ajs-hidden',
  250. noSelection: 'ajs-no-selection',
  251. noOverflow: 'ajs-no-overflow',
  252. noPadding:'ajs-no-padding',
  253. modeless: 'ajs-modeless',
  254. movable: 'ajs-movable',
  255. resizable: 'ajs-resizable',
  256. fixed: 'ajs-fixed',
  257. closable:'ajs-closable',
  258. maximizable:'ajs-maximizable',
  259. maximize: 'ajs-maximize',
  260. restore: 'ajs-restore',
  261. pinnable:'ajs-pinnable',
  262. unpinned:'ajs-unpinned',
  263. pin:'ajs-pin',
  264. maximized: 'ajs-maximized',
  265. animationIn: 'ajs-in',
  266. animationOut: 'ajs-out',
  267. shake:'ajs-shake'
  268. };
  269. /**
  270. * Helper: initializes the dialog instance
  271. *
  272. * @return {Number} The total count of currently open modals.
  273. */
  274. function initialize(instance){
  275. if(!instance.__internal){
  276. //no need to expose init after this.
  277. delete instance.__init;
  278. //in case the script was included before body.
  279. //after first dialog gets initialized, it won't be null anymore!
  280. if(null === reflow){
  281. // set tabindex attribute on body element this allows script to give it
  282. // focus after the dialog is closed
  283. document.body.setAttribute( 'tabindex', '0' );
  284. }
  285. //get dialog buttons/focus setup
  286. var setup;
  287. if(typeof instance.setup === 'function'){
  288. setup = instance.setup();
  289. setup.options = setup.options || {};
  290. setup.focus = setup.focus || {};
  291. }else{
  292. setup = {
  293. buttons:[],
  294. focus:{
  295. element:null,
  296. select:false
  297. },
  298. options:{
  299. }
  300. };
  301. }
  302. var internal = instance.__internal = {
  303. /**
  304. * Flag holding the open state of the dialog
  305. *
  306. * @type {Boolean}
  307. */
  308. isOpen:false,
  309. /**
  310. * Active element is the element that will receive focus after
  311. * closing the dialog. It defaults as the body tag, but gets updated
  312. * to the last focused element before the dialog was opened.
  313. *
  314. * @type {Node}
  315. */
  316. activeElement:document.body,
  317. buttons: setup.buttons,
  318. focus: setup.focus,
  319. options: {
  320. title: undefined,
  321. modal: undefined,
  322. pinned: undefined,
  323. movable: undefined,
  324. resizable: undefined,
  325. closable: undefined,
  326. maximizable: undefined,
  327. pinnable: undefined,
  328. transition: undefined,
  329. padding:undefined,
  330. overflow:undefined,
  331. onshow:undefined,
  332. onclose:undefined
  333. },
  334. resetHandler:undefined,
  335. beginMoveHandler:undefined,
  336. beginResizeHandler:undefined,
  337. bringToFrontHandler:undefined,
  338. modalClickHandler:undefined,
  339. buttonsClickHandler:undefined,
  340. commandsClickHandler:undefined,
  341. transitionInHandler:undefined,
  342. transitionOutHandler:undefined
  343. };
  344. var elements = {};
  345. //root node
  346. elements.root = document.createElement('div');
  347. elements.root.className = classes.base + ' ' + classes.hidden + ' ';
  348. elements.root.innerHTML = templates.dimmer + templates.modal;
  349. //dimmer
  350. elements.dimmer = elements.root.firstChild;
  351. //dialog
  352. elements.modal = elements.root.lastChild;
  353. elements.modal.innerHTML = templates.dialog;
  354. elements.dialog = elements.modal.firstChild;
  355. elements.dialog.innerHTML = templates.reset + templates.commands + templates.header + templates.body + templates.footer + templates.reset;
  356. //reset links
  357. elements.reset = [];
  358. elements.reset.push(elements.dialog.firstChild);
  359. elements.reset.push(elements.dialog.lastChild);
  360. //commands
  361. elements.commands = {};
  362. elements.commands.container = elements.reset[0].nextSibling;
  363. elements.commands.pin = elements.commands.container.firstChild;
  364. elements.commands.maximize = elements.commands.pin.nextSibling;
  365. elements.commands.close = elements.commands.maximize.nextSibling;
  366. //header
  367. elements.header = elements.commands.container.nextSibling;
  368. //body
  369. elements.body = elements.header.nextSibling;
  370. elements.body.innerHTML = templates.content;
  371. elements.content = elements.body.firstChild;
  372. //footer
  373. elements.footer = elements.body.nextSibling;
  374. elements.footer.innerHTML = templates.buttons.auxiliary + templates.buttons.primary + templates.resizeHandle;
  375. elements.resizeHandle = elements.footer.lastChild;
  376. //buttons
  377. elements.buttons = {};
  378. elements.buttons.auxiliary = elements.footer.firstChild;
  379. elements.buttons.primary = elements.buttons.auxiliary.nextSibling;
  380. elements.buttons.primary.innerHTML = templates.button;
  381. elements.buttonTemplate = elements.buttons.primary.firstChild;
  382. //remove button template
  383. elements.buttons.primary.removeChild(elements.buttonTemplate);
  384. for(var x=0; x < instance.__internal.buttons.length; x+=1) {
  385. var button = instance.__internal.buttons[x];
  386. button.element = elements.buttonTemplate.cloneNode();
  387. button.element.innerHTML = button.text;
  388. if(typeof button.className === 'string' && button.className !== ''){
  389. addClass(button.element, button.className);
  390. }
  391. for(var key in button.attrs){
  392. if(key !== 'className' && button.attrs.hasOwnProperty(key)){
  393. button.element.setAttribute(key, button.attrs[key]);
  394. }
  395. }
  396. if(button.scope === 'auxiliary'){
  397. elements.buttons.auxiliary.appendChild(button.element);
  398. }else{
  399. elements.buttons.primary.appendChild(button.element);
  400. }
  401. }
  402. //make elements pubic
  403. instance.elements = elements;
  404. //save event handlers delegates
  405. internal.resetHandler = delegate(instance, onReset);
  406. internal.beginMoveHandler = delegate(instance, beginMove);
  407. internal.beginResizeHandler = delegate(instance, beginResize);
  408. internal.bringToFrontHandler = delegate(instance, bringToFront);
  409. internal.modalClickHandler = delegate(instance, modalClickHandler);
  410. internal.buttonsClickHandler = delegate(instance, buttonsClickHandler);
  411. internal.commandsClickHandler = delegate(instance, commandsClickHandler);
  412. internal.transitionInHandler = delegate(instance, handleTransitionInEvent);
  413. internal.transitionOutHandler = delegate(instance, handleTransitionOutEvent);
  414. //settings
  415. instance.setting('title', setup.options.title === undefined ? alertify.defaults.glossary.title : setup.options.title);
  416. instance.setting('modal', setup.options.modal === undefined ? alertify.defaults.modal : setup.options.modal);
  417. instance.setting('movable', setup.options.movable === undefined ? alertify.defaults.movable : setup.options.movable);
  418. instance.setting('resizable', setup.options.resizable === undefined ? alertify.defaults.resizable : setup.options.resizable);
  419. instance.setting('closable', setup.options.closable === undefined ? alertify.defaults.closable : setup.options.closable);
  420. instance.setting('maximizable', setup.options.maximizable === undefined ? alertify.defaults.maximizable : setup.options.maximizable);
  421. instance.setting('pinnable', setup.options.pinnable === undefined ? alertify.defaults.pinnable : setup.options.pinnable);
  422. instance.setting('pinned', setup.options.pinned === undefined ? alertify.defaults.pinned : setup.options.pinned);
  423. instance.setting('transition', setup.options.transition === undefined ? alertify.defaults.transition : setup.options.transition);
  424. instance.setting('padding', setup.options.padding === undefined ? alertify.defaults.padding : setup.options.padding);
  425. instance.setting('overflow', setup.options.overflow === undefined ? alertify.defaults.overflow : setup.options.overflow);
  426. // allow dom customization
  427. if(typeof instance.build === 'function'){
  428. instance.build();
  429. }
  430. }
  431. //add to DOM tree.
  432. document.body.appendChild(instance.elements.root);
  433. }
  434. /**
  435. * Helper: adds/removes no-overflow class from body
  436. *
  437. */
  438. function ensureNoOverflow(){
  439. var requiresNoOverflow = 0;
  440. for(var x=0;x<openInstances.length;x+=1){
  441. var instance = openInstances[x];
  442. if(instance.isModal() || instance.isMaximized()){
  443. requiresNoOverflow+=1;
  444. }
  445. }
  446. if(requiresNoOverflow === 0){
  447. //last open modal or last maximized one
  448. removeClass(document.body, classes.noOverflow);
  449. }else if(requiresNoOverflow > 0 && document.body.className.indexOf(classes.noOverflow) < 0){
  450. //first open modal or first maximized one
  451. addClass(document.body, classes.noOverflow);
  452. }
  453. }
  454. /**
  455. * Sets the name of the transition used to show/hide the dialog
  456. *
  457. * @param {Object} instance The dilog instance.
  458. *
  459. */
  460. function updateTransition(instance, value, oldValue){
  461. if(typeof oldValue === 'string'){
  462. removeClass(instance.elements.root,classes.prefix + oldValue);
  463. }
  464. addClass(instance.elements.root, classes.prefix + value);
  465. reflow = instance.elements.root.offsetWidth;
  466. }
  467. /**
  468. * Toggles the dialog display mode
  469. *
  470. * @param {Object} instance The dilog instance.
  471. * @param {Boolean} on True to make it modal, false otherwise.
  472. *
  473. * @return {undefined}
  474. */
  475. function updateDisplayMode(instance){
  476. if(instance.setting('modal')){
  477. //make modal
  478. removeClass(instance.elements.root, classes.modeless);
  479. //only if open
  480. if(instance.isOpen()){
  481. unbindModelessEvents(instance);
  482. //in case a pinned modless dialog was made modal while open.
  483. updateAbsPositionFix(instance);
  484. ensureNoOverflow();
  485. }
  486. }else{
  487. //make modelss
  488. addClass(instance.elements.root, classes.modeless);
  489. //only if open
  490. if(instance.isOpen()){
  491. bindModelessEvents(instance);
  492. //in case pin/unpin was called while a modal is open
  493. updateAbsPositionFix(instance);
  494. ensureNoOverflow();
  495. }
  496. }
  497. }
  498. /**
  499. * Helper: Brings the modeless dialog to front, attached to modeless dialogs.
  500. *
  501. * @param {Event} event Focus event
  502. * @param {Object} instance The dilog instance.
  503. *
  504. * @return {undefined}
  505. */
  506. function bringToFront(event, instance){
  507. // Do not bring to front if preceeded by an open modal
  508. var index = openInstances.indexOf(instance);
  509. for(var x=index+1;x<openInstances.length;x+=1){
  510. if(openInstances[x].isModal()){
  511. return;
  512. }
  513. }
  514. // Bring to front by making it the last child.
  515. if(document.body.lastChild !== instance.elements.root){
  516. document.body.appendChild(instance.elements.root);
  517. setFocus(instance);
  518. }
  519. return false;
  520. }
  521. /**
  522. * Helper: reflects dialogs options updates
  523. *
  524. * @param {Object} instance The dilog instance.
  525. * @param {String} option The updated option name.
  526. *
  527. * @return {undefined}
  528. */
  529. function optionUpdated(instance, option, oldValue, newValue){
  530. switch(option){
  531. case 'title':
  532. instance.setHeader(newValue);
  533. break;
  534. case 'modal':
  535. updateDisplayMode(instance);
  536. break;
  537. case 'pinned':
  538. updatePinned(instance);
  539. break;
  540. case 'closable':
  541. updateClosable(instance);
  542. break;
  543. case 'maximizable':
  544. updateMaximizable(instance);
  545. break;
  546. case 'pinnable':
  547. updatePinnable(instance);
  548. break;
  549. case 'movable':
  550. updateMovable(instance);
  551. break;
  552. case 'resizable':
  553. updateResizable(instance);
  554. break;
  555. case 'transition':
  556. updateTransition(instance,newValue, oldValue);
  557. break;
  558. case 'padding':
  559. if(newValue){
  560. removeClass(instance.elements.root, classes.noPadding);
  561. }else if(instance.elements.root.className.indexOf(classes.noPadding) < 0){
  562. addClass(instance.elements.root, classes.noPadding);
  563. }
  564. break;
  565. case 'overflow':
  566. if(newValue){
  567. removeClass(instance.elements.root, classes.noOverflow);
  568. }else if(instance.elements.root.className.indexOf(classes.noOverflow) < 0){
  569. addClass(instance.elements.root, classes.noOverflow);
  570. }
  571. break;
  572. case 'transition':
  573. updateTransition(instance,newValue, oldValue);
  574. break;
  575. }
  576. }
  577. /**
  578. * Helper: reflects dialogs options updates
  579. *
  580. * @param {Object} instance The dilog instance.
  581. * @param {Object} obj The object to set/get a value on/from.
  582. * @param {Function} callback The callback function to call if the key was found.
  583. * @param {String|Object} key A string specifying a propery name or a collection of key value pairs.
  584. * @param {Object} value Optional, the value associated with the key (in case it was a string).
  585. * @param {String} option The updated option name.
  586. *
  587. * @return {Object} result object
  588. * The result objects has an 'op' property, indicating of this is a SET or GET operation.
  589. * GET:
  590. * - found: a flag indicating if the key was found or not.
  591. * - value: the property value.
  592. * SET:
  593. * - items: a list of key value pairs of the properties being set.
  594. * each contains:
  595. * - found: a flag indicating if the key was found or not.
  596. * - key: the property key.
  597. * - value: the property value.
  598. */
  599. function update(instance, obj, callback, key, value){
  600. var result = {op:undefined, items: [] };
  601. if(typeof value === 'undefined' && typeof key === 'string') {
  602. //get
  603. result.op = 'get';
  604. if(obj.hasOwnProperty(key)){
  605. result.found = true;
  606. result.value = obj[key];
  607. }else{
  608. result.found = false;
  609. result.value = undefined;
  610. }
  611. }
  612. else
  613. {
  614. var old;
  615. //set
  616. result.op = 'set';
  617. if(typeof key === 'object'){
  618. //set multiple
  619. var args = key;
  620. for (var prop in args) {
  621. if (obj.hasOwnProperty(prop)) {
  622. if(obj[prop] !== args[prop]){
  623. old = obj[prop];
  624. obj[prop] = args[prop];
  625. callback.call(instance,prop, old, args[prop]);
  626. }
  627. result.items.push({ 'key': prop, 'value': args[prop], 'found':true});
  628. }else{
  629. result.items.push({ 'key': prop, 'value': args[prop], 'found':false});
  630. }
  631. }
  632. } else if (typeof key === 'string'){
  633. //set single
  634. if (obj.hasOwnProperty(key)) {
  635. if(obj[key] !== value){
  636. old = obj[key];
  637. obj[key] = value;
  638. callback.call(instance,key, old, value);
  639. }
  640. result.items.push({'key': key, 'value': value , 'found':true});
  641. }else{
  642. result.items.push({'key': key, 'value': value , 'found':false});
  643. }
  644. } else {
  645. //invalid params
  646. throw new Error('args must be a string or object');
  647. }
  648. }
  649. return result;
  650. }
  651. /**
  652. * Triggers a close event.
  653. *
  654. * @param {Object} instance The dilog instance.
  655. *
  656. * @return {undefined}
  657. */
  658. function triggerClose(instance){
  659. var found;
  660. triggerCallback(instance, function(button){
  661. return found = (button.invokeOnClose === true);
  662. });
  663. //none of the buttons registered as onclose callback
  664. //close the dialog
  665. if(!found && instance.isOpen()){
  666. instance.close();
  667. }
  668. }
  669. /**
  670. * Dialogs commands event handler, attached to the dialog commands element.
  671. *
  672. * @param {Event} event DOM event object.
  673. * @param {Object} instance The dilog instance.
  674. *
  675. * @return {undefined}
  676. */
  677. function commandsClickHandler(event,instance){
  678. var target = event.srcElement || event.target;
  679. switch(target){
  680. case instance.elements.commands.pin:
  681. if(!instance.isPinned()){
  682. pin(instance);
  683. } else {
  684. unpin(instance);
  685. }
  686. break;
  687. case instance.elements.commands.maximize:
  688. if(!instance.isMaximized()){
  689. maximize(instance);
  690. } else {
  691. restore(instance);
  692. }
  693. break;
  694. case instance.elements.commands.close:
  695. triggerClose(instance);
  696. break;
  697. }
  698. return false;
  699. }
  700. /**
  701. * Helper: pins the modeless dialog.
  702. *
  703. * @param {Object} instance The dialog instance.
  704. *
  705. * @return {undefined}
  706. */
  707. function pin(instance){
  708. //pin the dialog
  709. instance.setting('pinned', true);
  710. }
  711. /**
  712. * Helper: unpins the modeless dialog.
  713. *
  714. * @param {Object} instance The dilog instance.
  715. *
  716. * @return {undefined}
  717. */
  718. function unpin(instance){
  719. //unpin the dialog
  720. instance.setting('pinned', false);
  721. }
  722. /**
  723. * Helper: enlarges the dialog to fill the entire screen.
  724. *
  725. * @param {Object} instance The dilog instance.
  726. *
  727. * @return {undefined}
  728. */
  729. function maximize(instance){
  730. //maximize the dialog
  731. addClass(instance.elements.root, classes.maximized);
  732. if(instance.isOpen()){
  733. ensureNoOverflow();
  734. }
  735. }
  736. /**
  737. * Helper: returns the dialog to its former size.
  738. *
  739. * @param {Object} instance The dilog instance.
  740. *
  741. * @return {undefined}
  742. */
  743. function restore(instance){
  744. //maximize the dialog
  745. removeClass(instance.elements.root, classes.maximized);
  746. if(instance.isOpen()){
  747. ensureNoOverflow();
  748. }
  749. }
  750. /**
  751. * Show or hide the maximize box.
  752. *
  753. * @param {Object} instance The dilog instance.
  754. * @param {Boolean} on True to add the behavior, removes it otherwise.
  755. *
  756. * @return {undefined}
  757. */
  758. function updatePinnable(instance){
  759. if(instance.setting('pinnable')){
  760. // add class
  761. addClass(instance.elements.root, classes.pinnable);
  762. }else{
  763. // remove class
  764. removeClass(instance.elements.root, classes.pinnable);
  765. }
  766. }
  767. /**
  768. * Helper: Fixes the absolutly positioned modal div position.
  769. *
  770. * @param {Object} instance The dialog instance.
  771. *
  772. * @return {undefined}
  773. */
  774. function addAbsPositionFix(instance){
  775. var scrollLeft = getScrollLeft();
  776. instance.elements.modal.style.marginTop = getScrollTop() + 'px';
  777. instance.elements.modal.style.marginLeft = scrollLeft + 'px';
  778. instance.elements.modal.style.marginRight = (-scrollLeft) + 'px';
  779. }
  780. /**
  781. * Helper: Removes the absolutly positioned modal div position fix.
  782. *
  783. * @param {Object} instance The dialog instance.
  784. *
  785. * @return {undefined}
  786. */
  787. function removeAbsPositionFix(instance){
  788. var marginTop = parseInt(instance.elements.modal.style.marginTop,10);
  789. var marginLeft = parseInt(instance.elements.modal.style.marginLeft,10);
  790. instance.elements.modal.style.marginTop = '';
  791. instance.elements.modal.style.marginLeft = '';
  792. instance.elements.modal.style.marginRight = '';
  793. if (instance.isOpen()) {
  794. var top = 0,
  795. left = 0
  796. ;
  797. if(instance.elements.dialog.style.top !== ''){
  798. top = parseInt(instance.elements.dialog.style.top, 10);
  799. }
  800. instance.elements.dialog.style.top = (top + (marginTop - getScrollTop())) + 'px';
  801. if(instance.elements.dialog.style.left !== ''){
  802. left = parseInt(instance.elements.dialog.style.left, 10);
  803. }
  804. instance.elements.dialog.style.left = (left + (marginLeft - getScrollLeft())) + 'px';
  805. }
  806. }
  807. /**
  808. * Helper: Adds/Removes the absolutly positioned modal div position fix based on its pinned setting.
  809. *
  810. * @param {Object} instance The dialog instance.
  811. *
  812. * @return {undefined}
  813. */
  814. function updateAbsPositionFix(instance){
  815. // if modeless and unpinned add fix
  816. if(!instance.setting('modal') && !instance.setting('pinned')){
  817. addAbsPositionFix(instance);
  818. }else{
  819. removeAbsPositionFix(instance);
  820. }
  821. }
  822. /**
  823. * Toggles the dialog position lock | modeless only.
  824. *
  825. * @param {Object} instance The dilog instance.
  826. * @param {Boolean} on True to make it modal, false otherwise.
  827. *
  828. * @return {undefined}
  829. */
  830. function updatePinned(instance){
  831. if(instance.setting('pinned')){
  832. removeClass(instance.elements.root, classes.unpinned);
  833. if(instance.isOpen()){
  834. removeAbsPositionFix(instance);
  835. }
  836. }else{
  837. addClass(instance.elements.root, classes.unpinned);
  838. if(instance.isOpen() && !instance.isModal()){
  839. addAbsPositionFix(instance);
  840. }
  841. }
  842. }
  843. /**
  844. * Show or hide the maximize box.
  845. *
  846. * @param {Object} instance The dilog instance.
  847. * @param {Boolean} on True to add the behavior, removes it otherwise.
  848. *
  849. * @return {undefined}
  850. */
  851. function updateMaximizable(instance){
  852. if(instance.setting('maximizable')){
  853. // add class
  854. addClass(instance.elements.root, classes.maximizable);
  855. }else{
  856. // remove class
  857. removeClass(instance.elements.root, classes.maximizable);
  858. }
  859. }
  860. /**
  861. * Show or hide the close box.
  862. *
  863. * @param {Object} instance The dilog instance.
  864. * @param {Boolean} on True to add the behavior, removes it otherwise.
  865. *
  866. * @return {undefined}
  867. */
  868. function updateClosable(instance){
  869. if(instance.setting('closable')){
  870. // add class
  871. addClass(instance.elements.root, classes.closable);
  872. bindClosableEvents(instance);
  873. }else{
  874. // remove class
  875. removeClass(instance.elements.root, classes.closable);
  876. unbindClosableEvents(instance);
  877. }
  878. }
  879. // flag to cancel click event if already handled by end resize event (the mousedown, mousemove, mouseup sequence fires a click event.).
  880. var cancelClick = false;
  881. /**
  882. * Helper: closes the modal dialog when clicking the modal
  883. *
  884. * @param {Event} event DOM event object.
  885. * @param {Object} instance The dilog instance.
  886. *
  887. * @return {undefined}
  888. */
  889. function modalClickHandler(event, instance){
  890. var target = event.srcElement || event.target;
  891. if(!cancelClick && target === instance.elements.modal){
  892. triggerClose(instance);
  893. }
  894. cancelClick = false;
  895. return false;
  896. }
  897. // flag to cancel keyup event if already handled by click event (pressing Enter on a focusted button).
  898. var cancelKeyup = false;
  899. /**
  900. * Helper: triggers a button callback
  901. *
  902. * @param {Object} The dilog instance.
  903. * @param {Function} Callback to check which button triggered the event.
  904. *
  905. * @return {undefined}
  906. */
  907. function triggerCallback(instance, check){
  908. for(var idx=0; idx < instance.__internal.buttons.length; idx+=1){
  909. var button = instance.__internal.buttons[idx];
  910. if(!button.element.disabled && check(button)){
  911. var closeEvent = createCloseEvent(idx, button);
  912. if( typeof instance.callback === 'function'){
  913. instance.callback.apply(instance, [closeEvent]);
  914. }
  915. //close the dialog only if not canceled.
  916. if (closeEvent.cancel === false){
  917. instance.close();
  918. }
  919. break;
  920. }
  921. }
  922. }
  923. /**
  924. * Clicks event handler, attached to the dialog footer.
  925. *
  926. * @param {Event} DOM event object.
  927. * @param {Object} The dilog instance.
  928. *
  929. * @return {undefined}
  930. */
  931. function buttonsClickHandler(event,instance){
  932. var target = event.srcElement || event.target;
  933. triggerCallback(instance, function(button){
  934. // if this button caused the click, cancel keyup event
  935. return button.element === target && (cancelKeyup = true);
  936. });
  937. }
  938. /**
  939. * Keyup event handler, attached to the document.body
  940. *
  941. * @param {Event} DOM event object.
  942. * @param {Object} The dilog instance.
  943. *
  944. * @return {undefined}
  945. */
  946. function keyupHandler(event){
  947. //hitting enter while button has focus will trigger keyup too.
  948. //ignore if handled by clickHandler
  949. if(cancelKeyup){
  950. cancelKeyup = false;
  951. return;
  952. }
  953. var instance = openInstances[openInstances.length-1];
  954. var keyCode = event.keyCode;
  955. if (keyCode === keys.ENTER || keyCode === keys.ESC) {
  956. triggerCallback(instance, function(button){
  957. return button.key === keyCode;
  958. });
  959. return false;
  960. }
  961. }
  962. /**
  963. * Sets focus to proper dialog element
  964. *
  965. * @param {Object} instance The dilog instance.
  966. * @param {Node} [resetTarget=undefined] DOM element to reset focus to.
  967. *
  968. * @return {undefined}
  969. */
  970. function setFocus ( instance, resetTarget ) {
  971. // reset target has already been determined.
  972. if (resetTarget) {
  973. resetTarget.focus();
  974. } else {
  975. // current instance focus settings
  976. var focus = instance.__internal.focus;
  977. // the focus element.
  978. var element = focus.element;
  979. // a number means a button index
  980. if (typeof focus.element === 'number') {
  981. element = instance.__internal.buttons[focus.element].element;
  982. }
  983. // focus
  984. if (element && element.focus) {
  985. element.focus();
  986. // if selectable
  987. if (focus.select && element.select) {
  988. element.select();
  989. }
  990. }
  991. }
  992. }
  993. /**
  994. * Focus event handler, attached to document.body and dialogs own reset links.
  995. * handles the focus for modal dialogs only.
  996. *
  997. * @param {Event} event DOM focus event object.
  998. * @param {Object} instance The dilog instance.
  999. *
  1000. * @return {undefined}
  1001. */
  1002. function onReset ( event, instance ) {
  1003. // should work on last modal if triggered from document.body
  1004. if(!instance){
  1005. for(var x=openInstances.length-1;x>-1;x-=1){
  1006. if(openInstances[x].isModal()){
  1007. instance = openInstances[x];
  1008. break;
  1009. }
  1010. }
  1011. }
  1012. // if modal
  1013. if(instance && instance.isModal()){
  1014. // determine reset target to enable forward/backward tab cycle.
  1015. var resetTarget, target = event.srcElement || event.target;
  1016. var lastResetLink = target === instance.elements.reset[1];
  1017. // if last reset link, then go to maximize or close
  1018. if(lastResetLink){
  1019. if(instance.setting('maximizable')){
  1020. resetTarget = instance.elements.commands.maximize;
  1021. }else if(instance.setting('closable')){
  1022. resetTarget = instance.elements.commands.close;
  1023. }
  1024. }
  1025. // if no reset target found, try finding the best button
  1026. if(resetTarget === undefined){
  1027. if(typeof instance.__internal.focus.element === 'number'){
  1028. // button focus element, go to first available button
  1029. if(target === instance.elements.reset[0]){
  1030. resetTarget = instance.elements.buttons.auxiliary.firstChild || instance.elements.buttons.primary.firstChild;
  1031. }else if(lastResetLink){
  1032. //restart the cycle by going to first reset link
  1033. resetTarget = instance.elements.reset[0];
  1034. }
  1035. }else{
  1036. // will reach here when tapping backwards, so go to last child
  1037. // The focus element SHOULD NOT be a button (logically!).
  1038. if(target === instance.elements.reset[0]){
  1039. resetTarget = instance.elements.buttons.primary.lastChild || instance.elements.buttons.auxiliary.lastChild;
  1040. }
  1041. }
  1042. }
  1043. // focus
  1044. setFocus(instance, resetTarget);
  1045. }
  1046. }
  1047. //animation timers
  1048. var transitionInTimeout,transitionOutTimeout;
  1049. /**
  1050. * Transition in transitionend event handler.
  1051. *
  1052. * @param {Event} TransitionEnd event object.
  1053. * @param {Object} The dilog instance.
  1054. *
  1055. * @return {undefined}
  1056. */
  1057. function handleTransitionInEvent ( event, instance) {
  1058. // clear the timer
  1059. clearTimeout( transitionInTimeout );
  1060. // once transition is complete, set focus
  1061. setFocus(instance);
  1062. // allow handling key up after transition ended.
  1063. cancelKeyup = false;
  1064. // allow custom `onfocus` method
  1065. if ( typeof instance.onfocus === 'function' ) {
  1066. instance.onfocus();
  1067. }
  1068. // unbind the event
  1069. off( instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);
  1070. }
  1071. /**
  1072. * Transition out transitionend event handler.
  1073. *
  1074. * @param {Event} TransitionEnd event object.
  1075. * @param {Object} The dilog instance.
  1076. *
  1077. * @return {undefined}
  1078. */
  1079. function handleTransitionOutEvent ( event, instance) {
  1080. // clear the timer
  1081. clearTimeout( transitionOutTimeout );
  1082. // unbind the event
  1083. off( instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);
  1084. // reset move updates
  1085. resetMove(instance);
  1086. // reset resize updates
  1087. resetResize(instance);
  1088. // restore if maximized
  1089. if(instance.isMaximized()){
  1090. restore(instance);
  1091. }
  1092. // return focus to the last active element
  1093. instance.__internal.activeElement.focus();
  1094. }
  1095. /* Controls moving a dialog around */
  1096. //holde the current moving instance
  1097. var movable = null,
  1098. //holds the current X offset when move starts
  1099. offsetX = 0,
  1100. //holds the current Y offset when move starts
  1101. offsetY = 0
  1102. ;
  1103. /**
  1104. * Helper: sets the element top/left coordinates
  1105. *
  1106. * @param {Event} event DOM event object.
  1107. * @param {Node} element The element being moved.
  1108. *
  1109. * @return {undefined}
  1110. */
  1111. function moveElement(event, element){
  1112. element.style.left = (event.pageX - offsetX) + 'px';
  1113. element.style.top = (event.pageY - offsetY) + 'px';
  1114. }
  1115. /**
  1116. * Triggers the start of a move event, attached to the header element mouse down event.
  1117. * Adds no-selection class to the body, disabling selection while moving.
  1118. *
  1119. * @param {Event} event DOM event object.
  1120. * @param {Object} instance The dilog instance.
  1121. *
  1122. * @return {Boolean} false
  1123. */
  1124. function beginMove(event, instance){
  1125. if(event.button === 0 && !instance.isMaximized() && instance.setting('movable')){
  1126. movable = instance;
  1127. offsetX = event.pageX;
  1128. offsetY = event.pageY;
  1129. var element = instance.elements.dialog;
  1130. if(element.style.left){
  1131. offsetX -= parseInt(element.style.left,10);
  1132. }
  1133. if(element.style.top){
  1134. offsetY -= parseInt(element.style.top,10);
  1135. }
  1136. moveElement(event, element);
  1137. addClass(document.body, classes.noSelection);
  1138. return false;
  1139. }
  1140. }
  1141. /**
  1142. * The actual move handler, attached to document.body mousemove event.
  1143. *
  1144. * @param {Event} event DOM event object.
  1145. *
  1146. * @return {undefined}
  1147. */
  1148. function move(event){
  1149. if(movable){
  1150. moveElement(event, movable.elements.dialog);
  1151. }
  1152. }
  1153. /**
  1154. * Triggers the end of a move event, attached to document.body mouseup event.
  1155. * Removes no-selection class from document.body, allowing selection.
  1156. *
  1157. * @return {undefined}
  1158. */
  1159. function endMove(){
  1160. if(movable){
  1161. movable = null;
  1162. removeClass(document.body, classes.noSelection);
  1163. }
  1164. }
  1165. /**
  1166. * Resets any changes made by moving the element to its original state,
  1167. *
  1168. * @param {Object} instance The dilog instance.
  1169. *
  1170. * @return {undefined}
  1171. */
  1172. function resetMove(instance){
  1173. var element = instance.elements.dialog;
  1174. element.style.left = element.style.top = '';
  1175. }
  1176. /**
  1177. * Updates the dialog move behavior.
  1178. *
  1179. * @param {Object} instance The dilog instance.
  1180. * @param {Boolean} on True to add the behavior, removes it otherwise.
  1181. *
  1182. * @return {undefined}
  1183. */
  1184. function updateMovable(instance){
  1185. if(instance.setting('movable')){
  1186. // add class
  1187. addClass(instance.elements.root, classes.movable);
  1188. if(instance.isOpen()){
  1189. bindMovableEvents(instance);
  1190. }
  1191. }else{
  1192. //reset
  1193. resetMove(instance);
  1194. // remove class
  1195. removeClass(instance.elements.root, classes.movable);
  1196. if(instance.isOpen()){
  1197. unbindMovableEvents(instance);
  1198. }
  1199. }
  1200. }
  1201. /* Controls moving a dialog around */
  1202. //holde the current instance being resized
  1203. var resizable = null,
  1204. //holds the staring left offset when resize starts.
  1205. startingLeft = Number.Nan,
  1206. //holds the staring width when resize starts.
  1207. startingWidth = 0,
  1208. //holds the initial width when resized for the first time.
  1209. minWidth = 0,
  1210. //holds the offset of the resize handle.
  1211. handleOffset = 0
  1212. ;
  1213. /**
  1214. * Helper: sets the element width/height and updates left coordinate if neccessary.
  1215. *
  1216. * @param {Event} event DOM mousemove event object.
  1217. * @param {Node} element The element being moved.
  1218. * @param {Boolean} pinned A flag indicating if the element being resized is pinned to the screen.
  1219. *
  1220. * @return {undefined}
  1221. */
  1222. function resizeElement(event, element, pageRelative){
  1223. //calculate offsets from 0,0
  1224. var current = element;
  1225. var offsetLeft = 0;
  1226. var offsetTop = 0;
  1227. do
  1228. {
  1229. offsetLeft += current.offsetLeft;
  1230. offsetTop += current.offsetTop;
  1231. }while(current = current.offsetParent);
  1232. // determine X,Y coordinates.
  1233. var X,Y;
  1234. if(pageRelative === true){
  1235. X = event.pageX;
  1236. Y = event.pageY;
  1237. }else{
  1238. X = event.clientX;
  1239. Y = event.clientY;
  1240. }
  1241. // rtl handling
  1242. var isRTL = isRightToLeft();
  1243. if(isRTL){
  1244. // reverse X
  1245. X = document.body.offsetWidth - X;
  1246. // if has a starting left, calculate offsetRight
  1247. if(!isNaN(startingLeft)){
  1248. offsetLeft = document.body.offsetWidth - offsetLeft - element.offsetWidth;
  1249. }
  1250. }
  1251. // set width/height
  1252. element.style.height = (Y - offsetTop + handleOffset) + 'px';
  1253. element.style.width = (X - offsetLeft + handleOffset) + 'px';
  1254. // if the element being resized has a starting left, maintain it.
  1255. // the dialog is centered, divide by half the offset to maintain the margins.
  1256. if(!isNaN(startingLeft)){
  1257. var diff = Math.abs(element.offsetWidth - startingWidth) * 0.5;
  1258. if(isRTL){
  1259. //negate the diff, why?
  1260. //when growing it should decrease left
  1261. //when shrinking it should increase left
  1262. diff *= -1;
  1263. }
  1264. if(element.offsetWidth > startingWidth){
  1265. //growing
  1266. element.style.left = (startingLeft + diff) + 'px';
  1267. }else if(element.offsetWidth >= minWidth){
  1268. //shrinking
  1269. element.style.left = (startingLeft - diff) + 'px';
  1270. }
  1271. }
  1272. }
  1273. /**
  1274. * Triggers the start of a resize event, attached to the resize handle element mouse down event.
  1275. * Adds no-selection class to the body, disabling selection while moving.
  1276. *
  1277. * @param {Event} event DOM event object.
  1278. * @param {Object} instance The dilog instance.
  1279. *
  1280. * @return {Boolean} false
  1281. */
  1282. function beginResize(event, instance){
  1283. if(event.button === 0 && !instance.isMaximized()){
  1284. resizable = instance;
  1285. handleOffset = instance.elements.resizeHandle.offsetHeight/2;
  1286. var element = instance.elements.dialog;
  1287. startingLeft = parseInt(element.style.left,10);
  1288. element.style.height = element.offsetHeight + 'px';
  1289. element.style.minHeight = instance.elements.header.offsetHeight + instance.elements.footer.offsetHeight + 'px';
  1290. element.style.width = (startingWidth = element.offsetWidth) + 'px';
  1291. if(element.style.maxWidth !== 'none'){
  1292. element.style.minWidth = (minWidth = element.offsetWidth) + 'px';
  1293. }
  1294. element.style.maxWidth = 'none';
  1295. addClass(document.body, classes.noSelection);
  1296. return false;
  1297. }
  1298. }
  1299. /**
  1300. * The actual resize handler, attached to document.body mousemove event.
  1301. *
  1302. * @param {Event} event DOM event object.
  1303. *
  1304. * @return {undefined}
  1305. */
  1306. function resize(event){
  1307. if(resizable){
  1308. resizeElement(event, resizable.elements.dialog, !resizable.setting('modal') && !resizable.setting('pinned'));
  1309. }
  1310. }
  1311. /**
  1312. * Triggers the end of a resize event, attached to document.body mouseup event.
  1313. * Removes no-selection class from document.body, allowing selection.
  1314. *
  1315. * @return {undefined}
  1316. */
  1317. function endResize(){
  1318. if(resizable){
  1319. resizable = null;
  1320. removeClass(document.body, classes.noSelection);
  1321. cancelClick = true;
  1322. }
  1323. }
  1324. /**
  1325. * Resets any changes made by resizing the element to its original state.
  1326. *
  1327. * @param {Object} instance The dilog instance.
  1328. *
  1329. * @return {undefined}
  1330. */
  1331. function resetResize(instance){
  1332. var element = instance.elements.dialog;
  1333. if(element.style.maxWidth === 'none'){
  1334. //clear inline styles.
  1335. element.style.maxWidth = element.style.minWidth = element.style.width = element.style.height = element.style.minHeight = element.style.left = '';
  1336. //reset variables.
  1337. startingLeft = Number.Nan;
  1338. startingWidth = minWidth = handleOffset = 0;
  1339. }
  1340. }
  1341. /**
  1342. * Updates the dialog move behavior.
  1343. *
  1344. * @param {Object} instance The dilog instance.
  1345. * @param {Boolean} on True to add the behavior, removes it otherwise.
  1346. *
  1347. * @return {undefined}
  1348. */
  1349. function updateResizable(instance){
  1350. if(instance.setting('resizable')){
  1351. // add class
  1352. addClass(instance.elements.root, classes.resizable);
  1353. if(instance.isOpen()){
  1354. bindResizableEvents(instance);
  1355. }
  1356. }else{
  1357. //reset
  1358. resetResize(instance);
  1359. // remove class
  1360. removeClass(instance.elements.root, classes.resizable);
  1361. if(instance.isOpen()){
  1362. unbindResizableEvents(instance);
  1363. }
  1364. }
  1365. }
  1366. /**
  1367. * Reset move/resize on window resize.
  1368. *
  1369. * @param {Event} event window resize event object.
  1370. *
  1371. * @return {undefined}
  1372. */
  1373. function windowResize(/*event*/){
  1374. for(var x=0;x<openInstances.length;x+=1){
  1375. var instance = openInstances[x];
  1376. resetMove(instance);
  1377. resetResize(instance);
  1378. }
  1379. }
  1380. /**
  1381. * Bind dialogs events
  1382. *
  1383. * @param {Object} instance The dilog instance.
  1384. *
  1385. * @return {undefined}
  1386. */
  1387. function bindEvents(instance){
  1388. // if first dialog, hook body handlers
  1389. if(openInstances.length === 1){
  1390. //global
  1391. on( window, 'resize', windowResize);
  1392. on( document.body, 'keyup', keyupHandler);
  1393. on( document.body, 'focus', onReset);
  1394. //move
  1395. on( document.body, 'mousemove', move);
  1396. on( document.body, 'mouseup', endMove);
  1397. //resize
  1398. on( document.body, 'mousemove', resize);
  1399. on( document.body, 'mouseup', endResize);
  1400. }
  1401. // common events
  1402. on( instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler);
  1403. on( instance.elements.footer, 'click', instance.__internal.buttonsClickHandler);
  1404. on( instance.elements.reset[0], 'focus', instance.__internal.resetHandler);
  1405. on( instance.elements.reset[1], 'focus', instance.__internal.resetHandler);
  1406. //prevent handling key up when dialog is being opened by a key stroke.
  1407. cancelKeyup = true;
  1408. // hook in transition handler
  1409. on( instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);
  1410. // modelss only events
  1411. if(!instance.setting('modal')){
  1412. bindModelessEvents(instance);
  1413. }
  1414. // resizable
  1415. if(instance.setting('resizable')){
  1416. bindResizableEvents(instance);
  1417. }
  1418. // movable
  1419. if(instance.setting('movable')){
  1420. bindMovableEvents(instance);
  1421. }
  1422. }
  1423. /**
  1424. * Unbind dialogs events
  1425. *
  1426. * @param {Object} instance The dilog instance.
  1427. *
  1428. * @return {undefined}
  1429. */
  1430. function unbindEvents(instance){
  1431. // if last dialog, remove body handlers
  1432. if(openInstances.length === 1){
  1433. //global
  1434. off( window, 'resize', windowResize);
  1435. off( document.body, 'keyup', keyupHandler);
  1436. off( document.body, 'focus', onReset);
  1437. //move
  1438. off( document.body, 'mousemove', move);
  1439. off( document.body, 'mouseup', endMove);
  1440. //resize
  1441. off( document.body, 'mousemove', resize);
  1442. off( document.body, 'mouseup', endResize);
  1443. }
  1444. // common events
  1445. off( instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler);
  1446. off( instance.elements.footer, 'click', instance.__internal.buttonsClickHandler);
  1447. off( instance.elements.reset[0], 'focus', instance.__internal.resetHandler);
  1448. off( instance.elements.reset[1], 'focus', instance.__internal.resetHandler);
  1449. // hook out transition handler
  1450. on( instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);
  1451. // modelss only events
  1452. if(!instance.setting('modal')){
  1453. unbindModelessEvents(instance);
  1454. }
  1455. // movable
  1456. if(instance.setting('movable')){
  1457. unbindMovableEvents(instance);
  1458. }
  1459. // resizable
  1460. if(instance.setting('resizable')){
  1461. unbindResizableEvents(instance);
  1462. }
  1463. }
  1464. /**
  1465. * Bind modeless specific events
  1466. *
  1467. * @param {Object} instance The dilog instance.
  1468. *
  1469. * @return {undefined}
  1470. */
  1471. function bindModelessEvents(instance){
  1472. on( instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true);
  1473. }
  1474. /**
  1475. * Unbind modeless specific events
  1476. *
  1477. * @param {Object} instance The dilog instance.
  1478. *
  1479. * @return {undefined}
  1480. */
  1481. function unbindModelessEvents(instance){
  1482. off( instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true);
  1483. }
  1484. /**
  1485. * Bind movable specific events
  1486. *
  1487. * @param {Object} instance The dilog instance.
  1488. *
  1489. * @return {undefined}
  1490. */
  1491. function bindMovableEvents(instance){
  1492. on(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
  1493. }
  1494. /**
  1495. * Unbind movable specific events
  1496. *
  1497. * @param {Object} instance The dilog instance.
  1498. *
  1499. * @return {undefined}
  1500. */
  1501. function unbindMovableEvents(instance){
  1502. off(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
  1503. }
  1504. /**
  1505. * Bind resizable specific events
  1506. *
  1507. * @param {Object} instance The dilog instance.
  1508. *
  1509. * @return {undefined}
  1510. */
  1511. function bindResizableEvents(instance){
  1512. on( instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
  1513. }
  1514. /**
  1515. * Unbind resizable specific events
  1516. *
  1517. * @param {Object} instance The dilog instance.
  1518. *
  1519. * @return {undefined}
  1520. */
  1521. function unbindResizableEvents(instance){
  1522. off( instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
  1523. }
  1524. /**
  1525. * Bind closable events
  1526. *
  1527. * @param {Object} instance The dilog instance.
  1528. *
  1529. * @return {undefined}
  1530. */
  1531. function bindClosableEvents(instance){
  1532. on( instance.elements.modal, 'click', instance.__internal.modalClickHandler);
  1533. }
  1534. /**
  1535. * Unbind closable specific events
  1536. *
  1537. * @param {Object} instance The dilog instance.
  1538. *
  1539. * @return {undefined}
  1540. */
  1541. function unbindClosableEvents(instance){
  1542. off( instance.elements.modal, 'click', instance.__internal.modalClickHandler);
  1543. }
  1544. // dialog API
  1545. return {
  1546. __init:initialize,
  1547. /**
  1548. * Check if dialog is currently open
  1549. *
  1550. * @return {Boolean}
  1551. */
  1552. isOpen: function () {
  1553. return this.__internal.isOpen;
  1554. },
  1555. isModal: function (){
  1556. return this.elements.root.className.indexOf(classes.modeless) < 0;
  1557. },
  1558. isMaximized:function(){
  1559. return this.elements.root.className.indexOf(classes.maximized) > -1;
  1560. },
  1561. isPinned:function(){
  1562. return this.elements.root.className.indexOf(classes.unpinned) < 0;
  1563. },
  1564. maximize:function(){
  1565. if(!this.isMaximized()){
  1566. maximize(this);
  1567. }
  1568. return this;
  1569. },
  1570. restore:function(){
  1571. if(this.isMaximized()){
  1572. restore(this);
  1573. }
  1574. return this;
  1575. },
  1576. pin:function(){
  1577. if(!this.isPinned()){
  1578. pin(this);
  1579. }
  1580. return this;
  1581. },
  1582. unpin:function(){
  1583. if(this.isPinned()){
  1584. unpin(this);
  1585. }
  1586. return this;
  1587. },
  1588. /**
  1589. * Gets or Sets dialog settings/options
  1590. *
  1591. * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
  1592. * @param {Object} value Optional, the value associated with the key (in case it was a string).
  1593. *
  1594. * @return {undefined}
  1595. */
  1596. setting : function (key, value) {
  1597. var self = this;
  1598. var result = update(this, this.__internal.options, function(k,o,n){ optionUpdated(self,k,o,n); }, key, value);
  1599. if(result.op === 'get'){
  1600. if(result.found){
  1601. return result.value;
  1602. }else if(typeof this.settings !== 'undefined'){
  1603. return update(this, this.settings, this.settingUpdated || function(){}, key, value).value;
  1604. }else{
  1605. return undefined;
  1606. }
  1607. }else if(result.op === 'set'){
  1608. if(result.items.length > 0){
  1609. var callback = this.settingUpdated || function(){};
  1610. for(var x=0;x<result.items.length;x+=1){
  1611. var item = result.items[x];
  1612. if(!item.found && typeof this.settings !== 'undefined'){
  1613. update(this, this.settings, callback, item.key, item.value);
  1614. }
  1615. }
  1616. }
  1617. return this;
  1618. }
  1619. },
  1620. /**
  1621. * Sets dialog header
  1622. * @content {string or element}
  1623. *
  1624. * @return {undefined}
  1625. */
  1626. setHeader:function(content){
  1627. if(typeof content === 'string'){
  1628. this.elements.header.innerHTML = content;
  1629. }else if (content instanceof window.HTMLElement && this.elements.header.firstChild !== content){
  1630. this.elements.header.innerHTML = '';
  1631. this.elements.header.appendChild(content);
  1632. }
  1633. return this;
  1634. },
  1635. /**
  1636. * Sets dialog contents
  1637. * @content {string or element}
  1638. *
  1639. * @return {undefined}
  1640. */
  1641. setContent:function(content){
  1642. if(typeof content === 'string'){
  1643. this.elements.content.innerHTML = content;
  1644. }else if (content instanceof window.HTMLElement && this.elements.content.firstChild !== content){
  1645. this.elements.content.innerHTML = '';
  1646. this.elements.content.appendChild(content);
  1647. }
  1648. return this;
  1649. },
  1650. /**
  1651. * Show the dialog as modal
  1652. *
  1653. * @return {Object} the dialog instance.
  1654. */
  1655. showModal: function(className){
  1656. return this.show(true, className);
  1657. },
  1658. /**
  1659. * Show the dialog
  1660. *
  1661. * @return {Object} the dialog instance.
  1662. */
  1663. show: function (modal, className) {
  1664. // ensure initialization
  1665. initialize(this);
  1666. if ( !this.__internal.isOpen ) {
  1667. // add to open dialogs
  1668. this.__internal.isOpen = true;
  1669. openInstances.push(this);
  1670. // save last focused element
  1671. this.__internal.activeElement = document.activeElement;
  1672. //allow custom dom manipulation updates before showing the dialog.
  1673. if(typeof this.prepare === 'function'){
  1674. this.prepare();
  1675. }
  1676. bindEvents(this);
  1677. if(modal !== undefined){
  1678. this.setting('modal', modal);
  1679. }
  1680. ensureNoOverflow();
  1681. // allow custom dialog class on show
  1682. if(typeof className === 'string' && className !== ''){
  1683. this.__internal.className = className;
  1684. addClass(this.elements.root, className);
  1685. }
  1686. updateAbsPositionFix(this);
  1687. removeClass(this.elements.root, classes.animationOut);
  1688. addClass(this.elements.root, classes.animationIn);
  1689. // set 1s fallback in case transition event doesn't fire
  1690. clearTimeout( transitionInTimeout );
  1691. transitionInTimeout = setTimeout( this.__internal.transitionInHandler, transition.supported ? 1000 : 100 );
  1692. //reflow
  1693. reflow = this.elements.modal.offsetWidth;
  1694. // show dialog
  1695. removeClass(this.elements.root, classes.hidden);
  1696. // allow custom `onshow` method
  1697. if ( typeof this.setting('onshow') === 'function' ) {
  1698. this.setting('onshow')();
  1699. }
  1700. }else{
  1701. // reset move updates
  1702. resetMove(this);
  1703. // reset resize updates
  1704. resetResize(this);
  1705. // shake the dialog to indicate its already open
  1706. addClass(this.elements.dialog, classes.shake);
  1707. var self = this;
  1708. setTimeout(function(){
  1709. removeClass(self.elements.dialog, classes.shake);
  1710. },200);
  1711. }
  1712. return this;
  1713. },
  1714. /**
  1715. * Close the dialog
  1716. *
  1717. * @return {undefined}
  1718. */
  1719. close: function () {
  1720. if (this.__internal.isOpen ) {
  1721. unbindEvents(this);
  1722. removeClass(this.elements.root, classes.animationIn);
  1723. addClass(this.elements.root, classes.animationOut);
  1724. // set 1s fallback in case transition event doesn't fire
  1725. clearTimeout( transitionOutTimeout );
  1726. transitionOutTimeout = setTimeout( this.__internal.transitionOutHandler, transition.supported ? 1000 : 100 );
  1727. // hide dialog
  1728. addClass(this.elements.root, classes.hidden);
  1729. //reflow
  1730. reflow = this.elements.modal.offsetWidth;
  1731. // remove custom dialog class on hide
  1732. if (typeof this.__internal.className !== 'undefined' && this.__internal.className !== '') {
  1733. removeClass(this.elements.root, this.__internal.className);
  1734. }
  1735. // allow custom `onclose` method
  1736. if ( typeof this.setting('onclose') === 'function' ) {
  1737. this.setting('onclose')();
  1738. }
  1739. //remove from open dialogs
  1740. openInstances.splice(openInstances.indexOf(this),1);
  1741. this.__internal.isOpen = false;
  1742. ensureNoOverflow();
  1743. }
  1744. return this;
  1745. },
  1746. };
  1747. } () );
  1748. var notifier = (function () {
  1749. var reflow,
  1750. element,
  1751. classes = {
  1752. base:'alertify-notifier',
  1753. message:'ajs-message',
  1754. top:'ajs-top',
  1755. right:'ajs-right',
  1756. bottom:'ajs-bottom',
  1757. left:'ajs-left',
  1758. visible:'ajs-visible',
  1759. hidden:'ajs-hidden'
  1760. };
  1761. /**
  1762. * Helper: initializes the notifier instance
  1763. *
  1764. */
  1765. function initialize(instance) {
  1766. if (!instance.__internal) {
  1767. instance.__internal = {
  1768. position: alertify.defaults.notifier.position,
  1769. delay: alertify.defaults.notifier.delay,
  1770. };
  1771. element = document.createElement('DIV');
  1772. updatePosition(instance);
  1773. //add to DOM tree.
  1774. document.body.appendChild(element);
  1775. }
  1776. }
  1777. /**
  1778. * Helper: update the notifier instance position
  1779. *
  1780. */
  1781. function updatePosition(instance){
  1782. element.className = classes.base;
  1783. switch (instance.__internal.position) {
  1784. case 'top-right':
  1785. addClass(element, classes.top + ' ' + classes.right);
  1786. break;
  1787. case 'top-left':
  1788. addClass(element, classes.top + ' ' + classes.left);
  1789. break;
  1790. case 'bottom-left':
  1791. addClass(element, classes.bottom+ ' ' + classes.left);
  1792. break;
  1793. default:
  1794. case 'bottom-right':
  1795. addClass(element, classes.bottom+ ' ' + classes.right);
  1796. break;
  1797. }
  1798. }
  1799. /**
  1800. * creates a new notification message
  1801. *
  1802. * @param {DOMElement} message The notifier message element
  1803. * @param {Number} wait Time (in ms) to wait before the message is dismissed, a value of 0 means keep open till clicked.
  1804. * @param {Function} callback A callback function to be invoked when the message is dismissed.
  1805. *
  1806. * @return {undefined}
  1807. */
  1808. function create(message, wait, callback) {
  1809. var clickDelegate, hideElement, transitionDone;
  1810. clickDelegate = function () {
  1811. off(message, 'click', clickDelegate);
  1812. hideElement(message,true);
  1813. };
  1814. transitionDone = function () {
  1815. // unbind event
  1816. off(message, transition.type, transitionDone);
  1817. // remove the message
  1818. element.removeChild(message);
  1819. };
  1820. // set click event on messages
  1821. on(message, 'click', clickDelegate);
  1822. // this sets the hide class to transition out
  1823. // or removes the child if css transitions aren't supported
  1824. hideElement = function (message,clicked) {
  1825. // ensure element exists
  1826. if (typeof message !== 'undefined' && message.parentNode === element) {
  1827. // whether CSS transition exists
  1828. if (transition.supported) {
  1829. on(message, transition.type, transitionDone);
  1830. removeClass(message, classes.visible);
  1831. } else {
  1832. element.removeChild(message);
  1833. }
  1834. // custom callback on hide
  1835. if(typeof callback === 'function'){
  1836. callback.call(undefined,clicked);
  1837. }
  1838. }
  1839. };
  1840. // never close (until click) if wait is set to 0
  1841. if (wait === 0) {
  1842. return;
  1843. }
  1844. // set timeout to auto close the notifier message
  1845. setTimeout(function () { hideElement(message); }, wait);
  1846. }
  1847. //notifier api
  1848. return {
  1849. /**
  1850. * Gets or Sets notifier settings.
  1851. *
  1852. * @param {string} key The setting name
  1853. * @param {Variant} value The setting value.
  1854. *
  1855. * @return {Object} if the called as a setter, return the notifier instance.
  1856. */
  1857. setting:function(key, value){
  1858. //ensure init
  1859. initialize(this);
  1860. if(typeof value === 'undefined'){
  1861. //get
  1862. return this.__internal[key];
  1863. }else{
  1864. //set
  1865. switch(key){
  1866. case 'position':
  1867. this.__internal.position = value;
  1868. updatePosition(this);
  1869. break;
  1870. case 'delay':
  1871. this.__internal.delay = value;
  1872. break;
  1873. }
  1874. }
  1875. return this;
  1876. },
  1877. /**
  1878. * Creates a new notification message
  1879. *
  1880. * @param {string or DOMElement} content The message content
  1881. * @param {string} type The type of notification message (simply a CSS class name 'ajs-{type}' to be added).
  1882. * @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked.
  1883. * @param {Function} callback A callback function to be invoked when the message is dismissed.
  1884. *
  1885. * @return {undefined}
  1886. */
  1887. notify:function(content, type, wait, callback){
  1888. //ensure init
  1889. initialize(this);
  1890. var message = document.createElement('div');
  1891. message.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ajs-' + type : '');
  1892. //html or dom
  1893. if(typeof content === 'string'){
  1894. message.innerHTML = content;
  1895. }else{
  1896. message.appendChild(content);
  1897. }
  1898. // append or insert
  1899. if(this.__internal.position.indexOf('top') < 0){
  1900. element.appendChild(message);
  1901. }else{
  1902. element.insertBefore(message,element.firstChild);
  1903. }
  1904. reflow = message.offsetWidth;
  1905. addClass(message, classes.visible);
  1906. var delay = typeof wait !== 'undefined' && !isNaN(+wait) ? +wait : this.__internal.delay;
  1907. create(message, delay * 1000, callback);
  1908. }
  1909. };
  1910. }) ();
  1911. /**
  1912. * Alertify public API
  1913. * This contains everything that is exposed through the alertify object.
  1914. *
  1915. * @return {Object}
  1916. */
  1917. function Alertify () {
  1918. // holds a references of created dialogs
  1919. var dialogs = {};
  1920. /**
  1921. * Extends a given prototype by merging properties from base into sub.
  1922. *
  1923. * @sub {Object} sub The prototype being overwritten.
  1924. * @base {Object} base The prototype being written.
  1925. *
  1926. * @return {Object} The extended prototype.
  1927. */
  1928. function extend(sub, base){
  1929. // copy dialog pototype over definition.
  1930. for(var prop in base){
  1931. if(base.hasOwnProperty(prop)){
  1932. sub[prop] = base[prop];
  1933. }
  1934. }
  1935. return sub;
  1936. }
  1937. /**
  1938. * Helper: returns a dialog instance from saved dialogs.
  1939. * and initializes the dialog if its not already initialized.
  1940. *
  1941. * @name {String} name The dialog name.
  1942. *
  1943. * @return {Object} The dialog instance.
  1944. */
  1945. function get_dialog(name){
  1946. var dialog = dialogs[name].dialog;
  1947. //initialize the dialog if its not already initialized.
  1948. if(dialog && typeof dialog.__init === 'function'){
  1949. dialog.__init(dialog);
  1950. }
  1951. return dialog;
  1952. }
  1953. /**
  1954. * Helper: registers a new dialog definition.
  1955. *
  1956. * @name {String} name The dialog name.
  1957. * @Factory {Function} Factory a function resposible for creating dialog prototype.
  1958. * @transient {Boolean} transient True to create a new dialog instance each time the dialog is invoked, false otherwise.
  1959. * @base {String} base the name of another dialog to inherit from.
  1960. *
  1961. * @return {Object} The dialog definition.
  1962. */
  1963. function register(name, Factory, transient, base){
  1964. var definition = {
  1965. dialog:null,
  1966. factory:Factory
  1967. };
  1968. //if this is based on an existing dialog, create a new definition
  1969. //by applying the new protoype over the existing one.
  1970. if(base !== undefined){
  1971. definition.factory = function(){
  1972. return extend(new dialogs[base].factory(),new Factory());
  1973. };
  1974. }
  1975. if(!transient){
  1976. //create a new definition based on dialog
  1977. definition.dialog = extend(new definition.factory(), dialog);
  1978. }
  1979. return dialogs[name] = definition;
  1980. }
  1981. return {
  1982. /**
  1983. * Alertify defaults
  1984. *
  1985. * @type {Object}
  1986. */
  1987. defaults: defaults,
  1988. /**
  1989. * Dialogs factory
  1990. *
  1991. * @param {string} Dialog name.
  1992. * @param {Function} A Dialog factory function.
  1993. * @param {Boolean} indicates whether to create a singleton or transient dialog.
  1994. * @type {Object}
  1995. */
  1996. dialog: function(name, Factory, transient, base){
  1997. // get request, create a new instance and return it.
  1998. if(typeof Factory !== 'function'){
  1999. return get_dialog(name);
  2000. }
  2001. if(this.hasOwnProperty(name)){
  2002. throw new Error('alertify.dialog: name already exists');
  2003. }
  2004. // register the dialog
  2005. var definition = register(name, Factory, transient, base);
  2006. if(transient){
  2007. // make it public
  2008. this[name] = function () {
  2009. //if passed with no params, consider it a get request
  2010. if (arguments.length === 0) {
  2011. return definition.dialog;
  2012. } else {
  2013. var instance = extend(new definition.factory(), dialog);
  2014. //ensure init
  2015. if (instance && typeof instance.__init === 'function') {
  2016. instance.__init(instance);
  2017. }
  2018. instance['main'].apply(instance, arguments);
  2019. return instance['show'].apply(instance);
  2020. }
  2021. };
  2022. }else{
  2023. // make it public
  2024. this[name] = function(){
  2025. //ensure init
  2026. if(definition.dialog && typeof definition.dialog.__init === 'function'){
  2027. definition.dialog.__init(definition.dialog);
  2028. }
  2029. //if passed with no params, consider it a get request
  2030. if(arguments.length === 0){
  2031. return definition.dialog;
  2032. } else {
  2033. var dialog = definition.dialog;
  2034. dialog['main'].apply(definition.dialog,arguments);
  2035. return dialog['show'].apply(definition.dialog);
  2036. }
  2037. };
  2038. }
  2039. },
  2040. /**
  2041. * Gets or Sets dialog settings/options. if the dialog is transient, this call does nothing.
  2042. *
  2043. * @param {string} name The dialog name.
  2044. * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
  2045. * @param {Variant} value Optional, the value associated with the key (in case it was a string).
  2046. *
  2047. * @return {undefined}
  2048. */
  2049. setting:function(name,key,value){
  2050. if(name === 'notifier'){
  2051. return notifier.setting(key, value);
  2052. }
  2053. var dialog = get_dialog(name);
  2054. if(dialog){
  2055. return dialog.setting(key,value);
  2056. }
  2057. },
  2058. /**
  2059. * Creates a new notification message.
  2060. * If a type is passed, a class name "ajs-{type}" will be added.
  2061. * This allows for custom look and feel for various types of notifications.
  2062. *
  2063. * @param {String} [message=undefined] Message text
  2064. * @param {String} [type=''] Type of log message
  2065. * @param {String} [value=''] Time (in ms) to wait before auto-close
  2066. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2067. *
  2068. * @return {undefined}
  2069. */
  2070. notify: function ( message, type, wait, callback ) {
  2071. notifier.notify(message, type, wait, callback);
  2072. },
  2073. /**
  2074. * Creates a new notification message.
  2075. *
  2076. * @param {String} [message=undefined] Message text
  2077. * @param {String} [type=''] Type of log message
  2078. * @param {String} [value=''] Time (in ms) to wait before auto-close
  2079. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2080. *
  2081. * @return {undefined}
  2082. */
  2083. message: function ( message, wait, callback ) {
  2084. notifier.notify(message, null, wait, callback);
  2085. },
  2086. /**
  2087. * Creates a new notification message of type 'success'.
  2088. *
  2089. * @param {String} [message=undefined] Message text
  2090. * @param {String} [type=''] Type of log message
  2091. * @param {String} [value=''] Time (in ms) to wait before auto-close
  2092. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2093. *
  2094. * @return {undefined}
  2095. */
  2096. success: function ( message, wait, callback ) {
  2097. notifier.notify(message, 'success', wait, callback);
  2098. },
  2099. /**
  2100. * Creates a new notification message of type 'error'.
  2101. *
  2102. * @param {String} [message=undefined] Message text
  2103. * @param {String} [type=''] Type of log message
  2104. * @param {String} [value=''] Time (in ms) to wait before auto-close
  2105. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2106. *
  2107. * @return {undefined}
  2108. */
  2109. error: function ( message, wait, callback ) {
  2110. notifier.notify(message, 'error', wait, callback);
  2111. },
  2112. /**
  2113. * Creates a new notification message of type 'warning'.
  2114. *
  2115. * @param {String} [message=undefined] Message text
  2116. * @param {String} [type=''] Type of log message
  2117. * @param {String} [value=''] Time (in ms) to wait before auto-close
  2118. * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
  2119. *
  2120. * @return {undefined}
  2121. */
  2122. warning: function ( message, wait, callback ) {
  2123. notifier.notify(message, 'warning', wait, callback);
  2124. }
  2125. };
  2126. }
  2127. var alertify = new Alertify();
  2128. /**
  2129. * Alert dialog definition
  2130. *
  2131. * invoked by:
  2132. * alertify.alert(message);
  2133. * alertify.alert(title, message);
  2134. * alertify.alert(message, onok);
  2135. * alertify.alert(title, message, onok);
  2136. */
  2137. alertify.dialog('alert',function(){
  2138. return{
  2139. main : function(_title, _message, _onok){
  2140. var title, message, onok;
  2141. switch(arguments.length){
  2142. case 1:
  2143. message = _title;
  2144. break;
  2145. case 2:
  2146. if(typeof _message === 'function'){
  2147. message = _title;
  2148. onok = _message;
  2149. }else{
  2150. title = _title;
  2151. message = _message;
  2152. }
  2153. break;
  2154. case 3:
  2155. title = _title;
  2156. message = _message;
  2157. onok = _onok;
  2158. break;
  2159. }
  2160. this.setting('title', title);
  2161. this.setting('message', message);
  2162. this.setting('onok', onok);
  2163. return this;
  2164. },
  2165. setup:function(){
  2166. return {
  2167. buttons:[
  2168. {
  2169. text: alertify.defaults.glossary.ok,
  2170. key: keys.ESC,
  2171. invokeOnClose: true,
  2172. className: alertify.defaults.theme.ok,
  2173. }
  2174. ],
  2175. focus:{
  2176. element: 0,
  2177. select: false
  2178. },
  2179. options:{
  2180. maximizable:false,
  2181. resizable:false
  2182. }
  2183. };
  2184. },
  2185. build:function(){
  2186. // nothing
  2187. },
  2188. prepare:function(){
  2189. //nothing
  2190. },
  2191. setMessage:function(message){
  2192. this.setContent(message);
  2193. },
  2194. settings:{
  2195. message:undefined,
  2196. onok:undefined,
  2197. label:undefined,
  2198. },
  2199. settingUpdated:function(key, oldValue, newValue){
  2200. switch(key){
  2201. case 'message':
  2202. this.setMessage(newValue);
  2203. break;
  2204. case 'label':
  2205. if(this.__internal.buttons[0].element){
  2206. this.__internal.buttons[0].element.innerHTML = newValue;
  2207. }
  2208. break;
  2209. }
  2210. },
  2211. callback:function(closeEvent){
  2212. if(typeof this.settings.onok === 'function'){
  2213. var returnValue = this.settings.onok.call(undefined, closeEvent);
  2214. if(typeof returnValue !== 'undefined'){
  2215. closeEvent.cancel = !returnValue;
  2216. }
  2217. }
  2218. }
  2219. };
  2220. });
  2221. /**
  2222. * Confirm dialog object
  2223. *
  2224. * alertify.confirm(message);
  2225. * alertify.confirm(message, onok);
  2226. * alertify.confirm(message, onok, oncancel);
  2227. * alertify.confirm(title, message, onok, oncancel);
  2228. */
  2229. alertify.dialog('confirm',function(){
  2230. var autoConfirm = {
  2231. timer: null,
  2232. index: null,
  2233. text : null,
  2234. duratuin: null,
  2235. task: function(event,self){
  2236. if(self.isOpen()){
  2237. self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text + ' (&#8207;' + autoConfirm.duration +'&#8207;) ';
  2238. autoConfirm.duration-=1;
  2239. if(autoConfirm.duration === -1){
  2240. clearAutoConfirm(self);
  2241. var button = self.__internal.buttons[autoConfirm.index];
  2242. var closeEvent = createCloseEvent(autoConfirm.index, button);
  2243. if( typeof self.callback === 'function'){
  2244. self.callback.apply(self, [closeEvent]);
  2245. }
  2246. //close the dialog.
  2247. if (closeEvent.close !== false){
  2248. self.close();
  2249. }
  2250. }
  2251. }else{
  2252. clearAutoConfirm(self);
  2253. }
  2254. }
  2255. };
  2256. function clearAutoConfirm(self){
  2257. if(autoConfirm.timer !== null){
  2258. clearInterval(autoConfirm.timer);
  2259. autoConfirm.timer = null;
  2260. self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text;
  2261. }
  2262. }
  2263. function startAutoConfirm(self, index, duration){
  2264. clearAutoConfirm(self);
  2265. autoConfirm.duration = duration;
  2266. autoConfirm.index = index;
  2267. autoConfirm.text = self.__internal.buttons[index].element.innerHTML;
  2268. autoConfirm.timer = setInterval(delegate(self, autoConfirm.task), 1000);
  2269. autoConfirm.task(null, self);
  2270. }
  2271. return{
  2272. main:function(_title, _message, _onok, _oncancel){
  2273. var title, message, onok, oncancel;
  2274. switch(arguments.length){
  2275. case 1:
  2276. message = _title;
  2277. break;
  2278. case 2:
  2279. message = _title;
  2280. onok = _message;
  2281. break;
  2282. case 3:
  2283. message = _title;
  2284. onok = _message;
  2285. oncancel= _onok;
  2286. break;
  2287. case 4:
  2288. title = _title;
  2289. message = _message;
  2290. onok = _onok;
  2291. oncancel= _oncancel;
  2292. break;
  2293. }
  2294. this.setting('title', title);
  2295. this.setting('message', message);
  2296. this.setting('onok', onok);
  2297. this.setting('oncancel', oncancel);
  2298. return this;
  2299. },
  2300. setup:function(){
  2301. return {
  2302. buttons:[
  2303. {
  2304. text: alertify.defaults.glossary.ok,
  2305. key: keys.ENTER,
  2306. className: alertify.defaults.theme.ok,
  2307. },
  2308. {
  2309. text: alertify.defaults.glossary.cancel,
  2310. key: keys.ESC,
  2311. invokeOnClose: true,
  2312. className: alertify.defaults.theme.cancel,
  2313. }
  2314. ],
  2315. focus:{
  2316. element: 0,
  2317. select: false
  2318. },
  2319. options:{
  2320. maximizable:false,
  2321. resizable:false
  2322. }
  2323. };
  2324. },
  2325. build:function(){
  2326. //nothing
  2327. },
  2328. prepare:function(){
  2329. //nothing
  2330. },
  2331. setMessage:function(message){
  2332. this.setContent(message);
  2333. },
  2334. settings:{
  2335. message:null,
  2336. labels:null,
  2337. onok:null,
  2338. oncancel:null,
  2339. defaultFocus:null,
  2340. reverseButtons:null,
  2341. },
  2342. settingUpdated:function(key, oldValue, newValue){
  2343. switch(key){
  2344. case 'message':
  2345. this.setMessage(newValue);
  2346. break;
  2347. case 'labels':
  2348. if('ok' in newValue && this.__internal.buttons[0].element){
  2349. this.__internal.buttons[0].text = newValue.ok;
  2350. this.__internal.buttons[0].element.innerHTML = newValue.ok;
  2351. }
  2352. if('cancel' in newValue && this.__internal.buttons[1].element){
  2353. this.__internal.buttons[1].text = newValue.cancel;
  2354. this.__internal.buttons[1].element.innerHTML = newValue.cancel;
  2355. }
  2356. break;
  2357. case 'reverseButtons':
  2358. if(newValue === true){
  2359. this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
  2360. }else{
  2361. this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
  2362. }
  2363. break;
  2364. case 'defaultFocus':
  2365. this.__internal.focus.element = newValue === 'ok' ? 0 : 1;
  2366. break;
  2367. }
  2368. },
  2369. callback:function(closeEvent){
  2370. clearAutoConfirm(this);
  2371. var returnValue;
  2372. switch(closeEvent.index){
  2373. case 0:
  2374. if(typeof this.settings.onok === 'function'){
  2375. returnValue = this.settings.onok.call(undefined, closeEvent);
  2376. if(typeof returnValue !== 'undefined'){
  2377. closeEvent.cancel = !returnValue;
  2378. }
  2379. }
  2380. break;
  2381. case 1:
  2382. if(typeof this.settings.oncancel === 'function'){
  2383. returnValue = this.settings.oncancel.call(undefined, closeEvent);
  2384. if(typeof returnValue !== 'undefined'){
  2385. closeEvent.cancel = !returnValue;
  2386. }
  2387. }
  2388. break;
  2389. }
  2390. },
  2391. autoOk:function(duration){
  2392. startAutoConfirm(this, 0, duration);
  2393. return this;
  2394. },
  2395. autoCancel:function(duration){
  2396. startAutoConfirm(this, 1, duration);
  2397. return this;
  2398. }
  2399. };
  2400. });
  2401. /**
  2402. * Prompt dialog object
  2403. *
  2404. * invoked by:
  2405. * alertify.prompt(message);
  2406. * alertify.prompt(message, value);
  2407. * alertify.prompt(message, value, onok);
  2408. * alertify.prompt(message, value, onok, oncancel);
  2409. * alertify.prompt(title, message, value, onok, oncancel);
  2410. */
  2411. alertify.dialog('prompt',function(){
  2412. var input = document.createElement('INPUT');
  2413. var p = document.createElement('P');
  2414. return {
  2415. main:function(_title, _message, _value , _onok, _oncancel){
  2416. var title, message,value, onok, oncancel;
  2417. switch(arguments.length){
  2418. case 1:
  2419. message = _title;
  2420. break;
  2421. case 2:
  2422. message = _title;
  2423. value = _message;
  2424. break;
  2425. case 3:
  2426. message = _title;
  2427. value = _message;
  2428. onok = _value;
  2429. break;
  2430. case 4:
  2431. message = _title;
  2432. value = _message;
  2433. onok = _value;
  2434. oncancel= _onok;
  2435. break;
  2436. case 4:
  2437. title = _title;
  2438. message = _message;
  2439. value = _value;
  2440. onok = _onok;
  2441. oncancel= _oncancel;
  2442. break;
  2443. }
  2444. this.setting('title', title);
  2445. this.setting('message', message);
  2446. this.setting('value', value);
  2447. this.setting('onok', onok);
  2448. this.setting('oncancel', oncancel);
  2449. return this;
  2450. },
  2451. setup:function(){
  2452. return {
  2453. buttons:[
  2454. {
  2455. text: alertify.defaults.glossary.ok,
  2456. key: keys.ENTER,
  2457. className: alertify.defaults.theme.ok,
  2458. },
  2459. {
  2460. text: alertify.defaults.glossary.cancel,
  2461. key: keys.ESC,
  2462. invokeOnClose: true,
  2463. className: alertify.defaults.theme.cancel,
  2464. }
  2465. ],
  2466. focus:{
  2467. element: input,
  2468. select: true
  2469. },
  2470. options:{
  2471. maximizable:false,
  2472. resizable:false
  2473. }
  2474. };
  2475. },
  2476. build:function(){
  2477. input.className = alertify.defaults.theme.input;
  2478. input.setAttribute('type','text');
  2479. input.value = this.settings.value;
  2480. this.elements.content.appendChild(p);
  2481. this.elements.content.appendChild(input);
  2482. },
  2483. prepare:function(){
  2484. //nothing
  2485. },
  2486. setMessage:function(message){
  2487. if(typeof message === 'string'){
  2488. p.innerHTML = message;
  2489. }else if (message instanceof window.HTMLElement && p.firstChild !== message){
  2490. p.innerHTML = '';
  2491. p.appendChild(message);
  2492. }
  2493. },
  2494. settings:{
  2495. message:undefined,
  2496. labels:undefined,
  2497. onok:undefined,
  2498. oncancel:undefined,
  2499. value:'',
  2500. reverseButtons:undefined,
  2501. },
  2502. settingUpdated:function(key, oldValue, newValue){
  2503. switch(key){
  2504. case 'message':
  2505. this.setMessage(newValue);
  2506. break;
  2507. case 'value':
  2508. input.value = newValue;
  2509. break;
  2510. case 'labels':
  2511. if(newValue.ok && this.__internal.buttons[0].element){
  2512. this.__internal.buttons[0].element.innerHTML = newValue.ok;
  2513. }
  2514. if(newValue.cancel && this.__internal.buttons[1].element){
  2515. this.__internal.buttons[1].element.innerHTML = newValue.cancel;
  2516. }
  2517. break;
  2518. case 'reverseButtons':
  2519. if(newValue === true){
  2520. this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
  2521. }else{
  2522. this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
  2523. }
  2524. break;
  2525. }
  2526. },
  2527. callback:function(closeEvent){
  2528. var returnValue;
  2529. switch(closeEvent.index){
  2530. case 0:
  2531. this.value = input.value;
  2532. if(typeof this.settings.onok === 'function'){
  2533. returnValue = this.settings.onok.call(undefined, closeEvent, this.value);
  2534. if(typeof returnValue !== 'undefined'){
  2535. closeEvent.cancel = !returnValue;
  2536. }
  2537. }
  2538. break;
  2539. case 1:
  2540. if(typeof this.settings.oncancel === 'function'){
  2541. returnValue = this.settings.oncancel.call(undefined, closeEvent);
  2542. if(typeof returnValue !== 'undefined'){
  2543. closeEvent.cancel = !returnValue;
  2544. }
  2545. }
  2546. break;
  2547. }
  2548. }
  2549. };
  2550. });
  2551. // AMD and window support
  2552. if ( typeof define === 'function' ) {
  2553. define( [], function () {
  2554. return alertify;
  2555. } );
  2556. } else if ( !window.alertify ) {
  2557. window.alertify = alertify;
  2558. }
  2559. } ( this ) );