Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

alertify.js 138KB

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