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

jquery-impromptu.js 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. /*! jQuery-Impromptu - v6.0.0 - 2014-12-27
  2. * http://trentrichardson.com/Impromptu
  3. * Copyright (c) 2014 Trent Richardson; Licensed MIT */
  4. (function(root, factory) {
  5. if (typeof define === 'function' && define.amd) {
  6. define(['jquery'], factory);
  7. } else {
  8. factory(root.jQuery);
  9. }
  10. }(this, function($) {
  11. 'use strict';
  12. // ########################################################################
  13. // Base object
  14. // ########################################################################
  15. /**
  16. * Imp - Impromptu object - passing no params will not open, only return the instance
  17. * @param message String/Object - String of html or Object of states
  18. * @param options Object - Options to set the prompt
  19. * @return Imp - the instance of this Impromptu object
  20. */
  21. var Imp = function(message, options){
  22. var t = this;
  23. t.id = Imp.count++;
  24. Imp.lifo.push(t);
  25. if(message){
  26. t.open(message, options);
  27. }
  28. return t;
  29. };
  30. // ########################################################################
  31. // static properties and methods
  32. // ########################################################################
  33. /**
  34. * defaults - the default options
  35. */
  36. Imp.defaults = {
  37. prefix:'jqi',
  38. classes: {
  39. box: '',
  40. fade: '',
  41. prompt: '',
  42. form: '',
  43. close: '',
  44. title: '',
  45. message: '',
  46. buttons: '',
  47. button: '',
  48. defaultButton: ''
  49. },
  50. title: '',
  51. closeText: '×',
  52. buttons: {
  53. Ok: true
  54. },
  55. loaded: function(e){},
  56. submit: function(e,v,m,f){},
  57. close: function(e,v,m,f){},
  58. statechanging: function(e, from, to){},
  59. statechanged: function(e, to){},
  60. opacity: 0.6,
  61. zIndex: 999,
  62. overlayspeed: 'slow',
  63. promptspeed: 'fast',
  64. show: 'fadeIn',
  65. hide: 'fadeOut',
  66. focus: 0,
  67. defaultButton: 0,
  68. useiframe: false,
  69. top: '15%',
  70. position: {
  71. container: null,
  72. x: null,
  73. y: null,
  74. arrow: null,
  75. width: null
  76. },
  77. persistent: true,
  78. timeout: 0,
  79. states: {},
  80. state: {
  81. name: null,
  82. title: '',
  83. html: '',
  84. buttons: {
  85. Ok: true
  86. },
  87. focus: 0,
  88. defaultButton: 0,
  89. position: {
  90. container: null,
  91. x: null,
  92. y: null,
  93. arrow: null,
  94. width: null
  95. },
  96. submit: function(e,v,m,f){
  97. return true;
  98. }
  99. }
  100. };
  101. /**
  102. * setDefaults - Sets the default options
  103. * @param o Object - Options to set as defaults
  104. * @return void
  105. */
  106. Imp.setDefaults = function(o) {
  107. Imp.defaults = $.extend({}, Imp.defaults, o);
  108. };
  109. /**
  110. * setStateDefaults - Sets the default options for a state
  111. * @param o Object - Options to set as defaults
  112. * @return void
  113. */
  114. Imp.setStateDefaults = function(o) {
  115. Imp.defaults.state = $.extend({}, Imp.defaults.state, o);
  116. };
  117. /**
  118. * @var Int - A counter used to provide a unique ID for new prompts
  119. */
  120. Imp.count = 0;
  121. /**
  122. * @var Array - An array of Impromptu intances in a LIFO queue (last in first out)
  123. */
  124. Imp.lifo = [];
  125. /**
  126. * getLast - get the last element from the queue (doesn't pop, just returns)
  127. * @return Imp - the instance of this Impromptu object or false if queue is empty
  128. */
  129. Imp.getLast = function(){
  130. var l = Imp.lifo.length;
  131. return (l > 0)? Imp.lifo[l-1] : false;
  132. };
  133. /**
  134. * removeFromStack - remove an element from the lifo stack by its id
  135. * @param id int - id of the instance to remove
  136. * @return api - The api of the element removed from the stack or void
  137. */
  138. Imp.removeFromStack = function(id){
  139. for(var i=Imp.lifo.length-1; i>=0; i--){
  140. if(Imp.lifo[i].id === id){
  141. return Imp.lifo.splice(i,1)[0];
  142. }
  143. }
  144. };
  145. // ########################################################################
  146. // extend our object instance properties and methods
  147. // ########################################################################
  148. Imp.prototype = {
  149. /**
  150. * @var Int - A unique id, simply an autoincremented number
  151. */
  152. id: null,
  153. /**
  154. * open - Opens the prompt
  155. * @param message String/Object - String of html or Object of states
  156. * @param options Object - Options to set the prompt
  157. * @return Imp - the instance of this Impromptu object
  158. */
  159. open: function(message, options) {
  160. var t = this;
  161. t.options = $.extend({},Imp.defaults,options);
  162. // Be sure any previous timeouts are destroyed
  163. if(t.timeout){
  164. clearTimeout(t.timeout);
  165. }
  166. t.timeout = false;
  167. var opts = t.options,
  168. $body = $(document.body),
  169. $window = $(window);
  170. //build the box and fade
  171. var msgbox = '<div class="'+ opts.prefix +'box '+ opts.classes.box +'">';
  172. if(opts.useiframe && ($('object, applet').length > 0)) {
  173. msgbox += '<iframe src="javascript:false;" style="display:block;position:absolute;z-index:-1;" class="'+ opts.prefix +'fade '+ opts.classes.fade +'"></iframe>';
  174. } else {
  175. msgbox += '<div class="'+ opts.prefix +'fade '+ opts.classes.fade +'"></div>';
  176. }
  177. msgbox += '<div class="'+ opts.prefix +' '+ opts.classes.prompt +'">'+
  178. '<form action="javascript:false;" onsubmit="return false;" class="'+ opts.prefix +'form '+ opts.classes.form +'">'+
  179. '<div class="'+ opts.prefix +'close '+ opts.classes.close +'">'+ opts.closeText +'</div>'+
  180. '<div class="'+ opts.prefix +'states"></div>'+
  181. '</form>'+
  182. '</div>'+
  183. '</div>';
  184. t.jqib = $(msgbox).appendTo($body);
  185. t.jqi = t.jqib.children('.'+ opts.prefix);
  186. t.jqif = t.jqib.children('.'+ opts.prefix +'fade');
  187. //if a string was passed, convert to a single state
  188. if(message.constructor === String){
  189. message = {
  190. state0: {
  191. title: opts.title,
  192. html: message,
  193. buttons: opts.buttons,
  194. position: opts.position,
  195. focus: opts.focus,
  196. defaultButton: opts.defaultButton,
  197. submit: opts.submit
  198. }
  199. };
  200. }
  201. //build the states
  202. t.options.states = {};
  203. var k,v;
  204. for(k in message){
  205. v = $.extend({},Imp.defaults.state,{name:k},message[k]);
  206. t.addState(v.name, v);
  207. if(t.currentStateName === ''){
  208. t.currentStateName = v.name;
  209. }
  210. }
  211. //Events
  212. t.jqi.on('click', '.'+ opts.prefix +'buttons button', function(e){
  213. var $t = $(this),
  214. $state = $t.parents('.'+ opts.prefix +'state'),
  215. stateobj = t.options.states[$state.data('jqi-name')],
  216. msg = $state.children('.'+ opts.prefix +'message'),
  217. clicked = stateobj.buttons[$t.text()] || stateobj.buttons[$t.html()],
  218. forminputs = {};
  219. // if for some reason we couldn't get the value
  220. if(clicked === undefined){
  221. for(var i in stateobj.buttons){
  222. if(stateobj.buttons[i].title === $t.text() || stateobj.buttons[i].title === $t.html()){
  223. clicked = stateobj.buttons[i].value;
  224. }
  225. }
  226. }
  227. //collect all form element values from all states.
  228. $.each(t.jqi.children('form').serializeArray(),function(i,obj){
  229. if (forminputs[obj.name] === undefined) {
  230. forminputs[obj.name] = obj.value;
  231. } else if (typeof forminputs[obj.name] === Array || typeof forminputs[obj.name] === 'object') {
  232. forminputs[obj.name].push(obj.value);
  233. } else {
  234. forminputs[obj.name] = [forminputs[obj.name],obj.value];
  235. }
  236. });
  237. // trigger an event
  238. var promptsubmite = new $.Event('impromptu:submit');
  239. promptsubmite.stateName = stateobj.name;
  240. promptsubmite.state = $state;
  241. $state.trigger(promptsubmite, [clicked, msg, forminputs]);
  242. if(!promptsubmite.isDefaultPrevented()){
  243. t.close(true, clicked,msg,forminputs);
  244. }
  245. });
  246. // if the fade is clicked blink the prompt
  247. var fadeClicked = function(){
  248. if(opts.persistent){
  249. var offset = (opts.top.toString().indexOf('%') >= 0? ($window.height()*(parseInt(opts.top,10)/100)) : parseInt(opts.top,10)),
  250. top = parseInt(t.jqi.css('top').replace('px',''),10) - offset;
  251. //$window.scrollTop(top);
  252. $('html,body').animate({ scrollTop: top }, 'fast', function(){
  253. var i = 0;
  254. t.jqib.addClass(opts.prefix +'warning');
  255. var intervalid = setInterval(function(){
  256. t.jqib.toggleClass(opts.prefix +'warning');
  257. if(i++ > 1){
  258. clearInterval(intervalid);
  259. t.jqib.removeClass(opts.prefix +'warning');
  260. }
  261. }, 100);
  262. });
  263. }
  264. else {
  265. t.close(true);
  266. }
  267. };
  268. // listen for esc or tab keys
  269. var keyDownEventHandler = function(e){
  270. var key = (window.event) ? event.keyCode : e.keyCode;
  271. //escape key closes
  272. if(key === 27) {
  273. fadeClicked();
  274. }
  275. //enter key pressed trigger the default button if its not on it, ignore if it is a textarea
  276. if(key === 13){
  277. var $defBtn = t.getCurrentState().find('.'+ opts.prefix +'defaultbutton');
  278. var $tgt = $(e.target);
  279. if($tgt.is('textarea,.'+opts.prefix+'button') === false && $defBtn.length > 0){
  280. e.preventDefault();
  281. $defBtn.click();
  282. }
  283. }
  284. //constrain tabs, tabs should iterate through the state and not leave
  285. if (key === 9){
  286. var $inputels = $('input,select,textarea,button',t.getCurrentState());
  287. var fwd = !e.shiftKey && e.target === $inputels[$inputels.length-1];
  288. var back = e.shiftKey && e.target === $inputels[0];
  289. if (fwd || back) {
  290. setTimeout(function(){
  291. if (!$inputels){
  292. return;
  293. }
  294. var el = $inputels[back===true ? $inputels.length-1 : 0];
  295. if (el){
  296. el.focus();
  297. }
  298. },10);
  299. return false;
  300. }
  301. }
  302. };
  303. t.position();
  304. t.style();
  305. // store copy of the window resize function for interal use only
  306. t._windowResize = function(e){
  307. t.position(e);
  308. };
  309. $window.resize({ animate: false }, t._windowResize);
  310. t.jqif.click(fadeClicked);
  311. t.jqi.find('.'+ opts.prefix +'close').click(function(){ t.close(); });
  312. t.jqib.on("keydown",keyDownEventHandler)
  313. .on('impromptu:loaded', opts.loaded)
  314. .on('impromptu:close', opts.close)
  315. .on('impromptu:statechanging', opts.statechanging)
  316. .on('impromptu:statechanged', opts.statechanged);
  317. // Show it
  318. t.jqif[opts.show](opts.overlayspeed);
  319. t.jqi[opts.show](opts.promptspeed, function(){
  320. var $firstState = t.jqi.find('.'+ opts.prefix +'states .'+ opts.prefix +'state').eq(0);
  321. t.goToState($firstState.data('jqi-name'));
  322. t.jqib.trigger('impromptu:loaded');
  323. });
  324. // Timeout
  325. if(opts.timeout > 0){
  326. t.timeout = setTimeout(function(){ t.close(true); },opts.timeout);
  327. }
  328. return t;
  329. },
  330. /**
  331. * close - Closes the prompt
  332. * @param callback Function - called when the transition is complete
  333. * @param clicked String - value of the button clicked (only used internally)
  334. * @param msg jQuery - The state message body (only used internally)
  335. * @param forvals Object - key/value pairs of all form field names and values (only used internally)
  336. * @return Imp - the instance of this Impromptu object
  337. */
  338. close: function(callCallback, clicked, msg, formvals){
  339. var t = this;
  340. Imp.removeFromStack(t.id);
  341. if(t.timeout){
  342. clearTimeout(t.timeout);
  343. t.timeout = false;
  344. }
  345. if(t.jqib){
  346. t.jqib[t.options.hide]('fast',function(){
  347. t.jqib.trigger('impromptu:close', [clicked,msg,formvals]);
  348. t.jqib.remove();
  349. $(window).off('resize', t._windowResize);
  350. if(typeof callCallback === 'function'){
  351. callCallback();
  352. }
  353. });
  354. }
  355. t.currentStateName = "";
  356. return t;
  357. },
  358. /**
  359. * addState - Injects a state into the prompt
  360. * @param statename String - Name of the state
  361. * @param stateobj Object - options for the state
  362. * @param afterState String - selector of the state to insert after
  363. * @return jQuery - the newly created state
  364. */
  365. addState: function(statename, stateobj, afterState) {
  366. var t = this,
  367. state = '',
  368. $state = null,
  369. arrow = '',
  370. title = '',
  371. opts = t.options,
  372. $jqistates = $('.'+ opts.prefix +'states'),
  373. buttons = [],
  374. showHtml,defbtn,k,v,l,i=0;
  375. stateobj = $.extend({},Imp.defaults.state, {name:statename}, stateobj);
  376. if(stateobj.position.arrow !== null){
  377. arrow = '<div class="'+ opts.prefix + 'arrow '+ opts.prefix + 'arrow'+ stateobj.position.arrow +'"></div>';
  378. }
  379. if(stateobj.title && stateobj.title !== ''){
  380. title = '<div class="lead '+ opts.prefix + 'title '+ opts.classes.title +'">'+ stateobj.title +'</div>';
  381. }
  382. showHtml = stateobj.html;
  383. if (typeof stateobj.html === 'function') {
  384. showHtml = 'Error: html function must return text';
  385. }
  386. state += '<div class="'+ opts.prefix + 'state" data-jqi-name="'+ statename +'" style="display:none;">'+
  387. arrow + title +
  388. '<div class="'+ opts.prefix +'message '+ opts.classes.message +'">' + showHtml +'</div>'+
  389. '<div class="'+ opts.prefix +'buttons '+ opts.classes.buttons +'"'+ ($.isEmptyObject(stateobj.buttons)? 'style="display:none;"':'') +'>';
  390. // state buttons may be in object or array, lets convert objects to arrays
  391. if($.isArray(stateobj.buttons)){
  392. buttons = stateobj.buttons;
  393. }
  394. else if($.isPlainObject(stateobj.buttons)){
  395. for(k in stateobj.buttons){
  396. if(stateobj.buttons.hasOwnProperty(k)){
  397. buttons.push({ title: k, value: stateobj.buttons[k] });
  398. }
  399. }
  400. }
  401. // iterate over each button and create them
  402. for(i=0, l=buttons.length; i<l; i++){
  403. v = buttons[i],
  404. defbtn = stateobj.focus === i || (isNaN(stateobj.focus) && stateobj.defaultButton === i) ? (opts.prefix + 'defaultbutton ' + opts.classes.defaultButton) : '';
  405. state += '<button class="'+ opts.classes.button +' '+ opts.prefix + 'button '+ defbtn;
  406. if(typeof v.classes !== "undefined"){
  407. state += ' '+ ($.isArray(v.classes)? v.classes.join(' ') : v.classes) + ' ';
  408. }
  409. state += '" name="' + opts.prefix + '_' + statename + '_button' + v.title.replace(/[^a-z0-9]+/gi,'') + '" value="' + v.value + '">' + v.title + '</button>';
  410. }
  411. state += '</div></div>';
  412. $state = $(state);
  413. $state.on('impromptu:submit', stateobj.submit);
  414. if(afterState !== undefined){
  415. $jqistates.find('[data-jqi-name="'+afterState+'"]').after($state);
  416. }
  417. else{
  418. $jqistates.append($state);
  419. }
  420. t.options.states[statename] = stateobj;
  421. return $state;
  422. },
  423. /**
  424. * removeState - Removes a state from the prompt
  425. * @param state String - Name of the state
  426. * @param newState String - Name of the state to transition to
  427. * @return Boolean - returns true on success, false on failure
  428. */
  429. removeState: function(state, newState) {
  430. var t = this,
  431. $state = t.getState(state),
  432. rm = function(){ $state.remove(); };
  433. if($state.length === 0){
  434. return false;
  435. }
  436. // transition away from it before deleting
  437. if($state.css('display') !== 'none'){
  438. if(newState !== undefined && t.getState(newState).length > 0){
  439. t.goToState(newState, false, rm);
  440. }
  441. else if($state.next().length > 0){
  442. t.nextState(rm);
  443. }
  444. else if($state.prev().length > 0){
  445. t.prevState(rm);
  446. }
  447. else{
  448. t.close();
  449. }
  450. }
  451. else{
  452. $state.slideUp('slow', rm);
  453. }
  454. return true;
  455. },
  456. /**
  457. * getApi - Get the api, so you can extract it from $.prompt stack
  458. * @return jQuery - the prompt
  459. */
  460. getApi: function() {
  461. return this;
  462. },
  463. /**
  464. * getBox - Get the box containing fade and prompt
  465. * @return jQuery - the prompt
  466. */
  467. getBox: function() {
  468. return this.jqib;
  469. },
  470. /**
  471. * getPrompt - Get the prompt
  472. * @return jQuery - the prompt
  473. */
  474. getPrompt: function() {
  475. return this.jqi;
  476. },
  477. /**
  478. * getState - Get the state by its name
  479. * @param statename String - Name of the state
  480. * @return jQuery - the state
  481. */
  482. getState: function(statename) {
  483. return this.jqi.find('[data-jqi-name="'+ statename +'"]');
  484. },
  485. /**
  486. * getCurrentState - Get the current visible state
  487. * @return jQuery - the current visible state
  488. */
  489. getCurrentState: function() {
  490. return this.getState(this.getCurrentStateName());
  491. },
  492. /**
  493. * getCurrentStateName - Get the name of the current visible state/substate
  494. * @return String - the current visible state's name
  495. */
  496. getCurrentStateName: function() {
  497. return this.currentStateName;
  498. },
  499. /**
  500. * position - Repositions the prompt (Used internally)
  501. * @return void
  502. */
  503. position: function(e){
  504. var t = this,
  505. restoreFx = $.fx.off,
  506. $state = t.getCurrentState(),
  507. stateObj = t.options.states[$state.data('jqi-name')],
  508. pos = stateObj? stateObj.position : undefined,
  509. $window = $(window),
  510. bodyHeight = document.body.scrollHeight, //$(document.body).outerHeight(true),
  511. windowHeight = $(window).height(),
  512. documentHeight = $(document).height(),
  513. height = bodyHeight > windowHeight ? bodyHeight : windowHeight,
  514. top = parseInt($window.scrollTop(),10) + (t.options.top.toString().indexOf('%') >= 0?
  515. (windowHeight*(parseInt(t.options.top,10)/100)) : parseInt(t.options.top,10));
  516. // when resizing the window turn off animation
  517. if(e !== undefined && e.data.animate === false){
  518. $.fx.off = true;
  519. }
  520. t.jqib.css({
  521. position: "absolute",
  522. height: height,
  523. width: "100%",
  524. top: 0,
  525. left: 0,
  526. right: 0,
  527. bottom: 0
  528. });
  529. t.jqif.css({
  530. position: "fixed",
  531. height: height,
  532. width: "100%",
  533. top: 0,
  534. left: 0,
  535. right: 0,
  536. bottom: 0
  537. });
  538. // tour positioning
  539. if(pos && pos.container){
  540. var offset = $(pos.container).offset();
  541. if($.isPlainObject(offset) && offset.top !== undefined){
  542. t.jqi.css({
  543. position: "absolute"
  544. });
  545. t.jqi.animate({
  546. top: offset.top + pos.y,
  547. left: offset.left + pos.x,
  548. marginLeft: 0,
  549. width: (pos.width !== undefined)? pos.width : null
  550. });
  551. top = (offset.top + pos.y) - (t.options.top.toString().indexOf('%') >= 0? (windowHeight*(parseInt(t.options.top,10)/100)) : parseInt(t.options.top,10));
  552. $('html,body').animate({ scrollTop: top }, 'slow', 'swing', function(){});
  553. }
  554. }
  555. // custom state width animation
  556. else if(pos && pos.width){
  557. t.jqi.css({
  558. position: "absolute",
  559. left: '50%'
  560. });
  561. t.jqi.animate({
  562. top: pos.y || top,
  563. left: pos.x || '50%',
  564. marginLeft: ((pos.width/2)*-1),
  565. width: pos.width
  566. });
  567. }
  568. // standard prompt positioning
  569. else{
  570. t.jqi.css({
  571. position: "absolute",
  572. top: top,
  573. left: '50%',//$window.width()/2,
  574. marginLeft: ((t.jqi.outerWidth(false)/2)*-1)
  575. });
  576. }
  577. // restore fx settings
  578. if(e !== undefined && e.data.animate === false){
  579. $.fx.off = restoreFx;
  580. }
  581. },
  582. /**
  583. * style - Restyles the prompt (Used internally)
  584. * @return void
  585. */
  586. style: function(){
  587. var t = this;
  588. t.jqif.css({
  589. zIndex: t.options.zIndex,
  590. display: "none",
  591. opacity: t.options.opacity
  592. });
  593. t.jqi.css({
  594. zIndex: t.options.zIndex+1,
  595. display: "none"
  596. });
  597. t.jqib.css({
  598. zIndex: t.options.zIndex
  599. });
  600. },
  601. /**
  602. * goToState - Goto the specified state
  603. * @param state String - name of the state to transition to
  604. * @param subState Boolean - true to be a sub state within the currently open state
  605. * @param callback Function - called when the transition is complete
  606. * @return jQuery - the newly active state
  607. */
  608. goToState: function(state, subState, callback) {
  609. var t = this,
  610. $jqi = t.jqi,
  611. jqiopts = t.options,
  612. $state = t.getState(state),
  613. stateobj = jqiopts.states[$state.data('jqi-name')],
  614. promptstatechanginge = new $.Event('impromptu:statechanging'),
  615. opts = t.options;
  616. if(stateobj !== undefined){
  617. if (typeof stateobj.html === 'function') {
  618. var contentLaterFunc = stateobj.html;
  619. $state.find('.' + opts.prefix +'message ').html(contentLaterFunc());
  620. }
  621. // subState can be ommitted
  622. if(typeof subState === 'function'){
  623. callback = subState;
  624. subState = false;
  625. }
  626. t.jqib.trigger(promptstatechanginge, [t.getCurrentStateName(), state]);
  627. if(!promptstatechanginge.isDefaultPrevented() && $state.length > 0){
  628. t.jqi.find('.'+ opts.prefix +'parentstate').removeClass(opts.prefix +'parentstate');
  629. if(subState){ // hide any open substates
  630. // get rid of any substates
  631. t.jqi.find('.'+ opts.prefix +'substate').not($state)
  632. .slideUp(jqiopts.promptspeed)
  633. .removeClass('.'+ opts.prefix +'substate')
  634. .find('.'+ opts.prefix +'arrow').hide();
  635. // add parent state class so it can be visible, but blocked
  636. t.jqi.find('.'+ opts.prefix +'state:visible').addClass(opts.prefix +'parentstate');
  637. // add substate class so we know it will be smaller
  638. $state.addClass(opts.prefix +'substate');
  639. }
  640. else{ // hide any open states
  641. t.jqi.find('.'+ opts.prefix +'state').not($state)
  642. .slideUp(jqiopts.promptspeed)
  643. .find('.'+ opts.prefix +'arrow').hide();
  644. }
  645. t.currentStateName = stateobj.name;
  646. $state.slideDown(jqiopts.promptspeed,function(){
  647. var $t = $(this);
  648. // if focus is a selector, find it, else its button index
  649. if(typeof(stateobj.focus) === 'string'){
  650. $t.find(stateobj.focus).eq(0).focus();
  651. }
  652. else{
  653. $t.find('.'+ opts.prefix +'defaultbutton').focus();
  654. }
  655. $t.find('.'+ opts.prefix +'arrow').show(jqiopts.promptspeed);
  656. if (typeof callback === 'function'){
  657. t.jqib.on('impromptu:statechanged', callback);
  658. }
  659. t.jqib.trigger('impromptu:statechanged', [state]);
  660. if (typeof callback === 'function'){
  661. t.jqib.off('impromptu:statechanged', callback);
  662. }
  663. });
  664. if(!subState){
  665. t.position();
  666. }
  667. } // end isDefaultPrevented()
  668. }// end stateobj !== undefined
  669. return $state;
  670. },
  671. /**
  672. * nextState - Transition to the next state
  673. * @param callback Function - called when the transition is complete
  674. * @return jQuery - the newly active state
  675. */
  676. nextState: function(callback) {
  677. var t = this,
  678. $next = t.getCurrentState().next();
  679. if($next.length > 0){
  680. t.goToState( $next.data('jqi-name'), callback );
  681. }
  682. return $next;
  683. },
  684. /**
  685. * prevState - Transition to the previous state
  686. * @param callback Function - called when the transition is complete
  687. * @return jQuery - the newly active state
  688. */
  689. prevState: function(callback) {
  690. var t = this,
  691. $prev = t.getCurrentState().prev();
  692. if($prev.length > 0){
  693. t.goToState( $prev.data('jqi-name'), callback );
  694. }
  695. return $prev;
  696. }
  697. };
  698. // ########################################################################
  699. // $.prompt will manage a queue of Impromptu instances
  700. // ########################################################################
  701. /**
  702. * $.prompt create a new Impromptu instance and push it on the stack of instances
  703. * @param message String/Object - String of html or Object of states
  704. * @param options Object - Options to set the prompt
  705. * @return jQuery - the jQuery object of the prompt within the modal
  706. */
  707. $.prompt = function(message, options){
  708. var api = new Imp(message, options);
  709. return api.jqi;
  710. };
  711. /**
  712. * Copy over static methods
  713. */
  714. $.each(Imp, function(k,v){
  715. $.prompt[k] = v;
  716. });
  717. /**
  718. * Create a proxy for accessing all instance methods. The close method pops from queue.
  719. */
  720. $.each(Imp.prototype, function(k,v){
  721. $.prompt[k] = function(){
  722. var api = Imp.getLast(); // always use the last instance on the stack
  723. if(api && typeof api[k] === "function"){
  724. return api[k].apply(api, arguments);
  725. }
  726. };
  727. });
  728. // ########################################################################
  729. // jQuery Plugin and public access
  730. // ########################################################################
  731. /**
  732. * Enable using $('.selector').prompt({});
  733. * This will grab the html within the prompt as the prompt message
  734. */
  735. $.fn.prompt = function(options){
  736. if(options === undefined){
  737. options = {};
  738. }
  739. if(options.withDataAndEvents === undefined){
  740. options.withDataAndEvents = false;
  741. }
  742. $.prompt($(this).clone(options.withDataAndEvents).html(),options);
  743. };
  744. /**
  745. * Export it as Impromptu and $.prompt
  746. * Can be used from here forth as new Impromptu(states, opts)
  747. */
  748. window.Impromptu = Imp;
  749. }));