step.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // Step
  2. function Step() {
  3. return this.initialize.apply(this, Array.prototype.slice.call(arguments));
  4. }
  5. $.extend(Step.prototype, {
  6. TRANSITION_DURATION: 200,
  7. initialize: function(element, wizard, index) {
  8. this.$element = $(element);
  9. this.wizard = wizard;
  10. this.events = {};
  11. this.loader = null;
  12. this.loaded = false;
  13. this.validator = this.wizard.options.validator;
  14. this.states = {
  15. done: false,
  16. error: false,
  17. active: false,
  18. disabled: false,
  19. activing: false
  20. };
  21. this.index = index;
  22. this.$element.data('wizard-index', index);
  23. this.$pane = this.getPaneFromTarget();
  24. if(!this.$pane){
  25. this.$pane = this.wizard.options.getPane.call(this.wizard, index, element);
  26. }
  27. this.setValidatorFromData();
  28. this.setLoaderFromData();
  29. },
  30. getPaneFromTarget: function(){
  31. var selector = this.$element.data('target');
  32. if (!selector) {
  33. selector = this.$element.attr('href');
  34. selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '');
  35. }
  36. if(selector) {
  37. return $(selector);
  38. } else {
  39. return null;
  40. }
  41. },
  42. setup: function() {
  43. var current = this.wizard.currentIndex();
  44. if(this.index === current){
  45. this.enter('active');
  46. if(this.loader){
  47. this.load();
  48. }
  49. } else if (this.index > current){
  50. this.enter('disabled');
  51. }
  52. this.$element.attr('aria-expanded', this.is('active'));
  53. this.$pane.attr('aria-expanded', this.is('active'));
  54. var classes = this.wizard.options.classes;
  55. if(this.is('active')){
  56. this.$pane.addClass(classes.pane.active);
  57. } else {
  58. this.$pane.removeClass(classes.pane.active);
  59. }
  60. },
  61. show: function(callback) {
  62. if(this.is('activing') || this.is('active')) {
  63. return;
  64. }
  65. this.trigger('beforeShow');
  66. this.enter('activing');
  67. var classes = this.wizard.options.classes;
  68. this.$element
  69. .attr('aria-expanded', true);
  70. this.$pane
  71. .addClass(classes.pane.activing)
  72. .addClass(classes.pane.active)
  73. .attr('aria-expanded', true);
  74. var complete = function () {
  75. this.$pane
  76. .removeClass(classes.pane.activing)
  77. this.leave('activing');
  78. this.enter('active');
  79. this.trigger('afterShow');
  80. if($.isFunction(callback)){
  81. callback.call(this);
  82. }
  83. }
  84. if (!Support.transition) {
  85. return complete.call(this);
  86. }
  87. this.$pane.one(Support.transition.end, $.proxy(complete, this));
  88. emulateTransitionEnd(this.$pane, this.TRANSITION_DURATION);
  89. },
  90. hide: function(callback) {
  91. if(this.is('activing') || !this.is('active')) {
  92. return;
  93. }
  94. this.trigger('beforeHide');
  95. this.enter('activing');
  96. var classes = this.wizard.options.classes;
  97. this.$element
  98. .attr('aria-expanded', false);
  99. this.$pane
  100. .addClass(classes.pane.activing)
  101. .removeClass(classes.pane.active)
  102. .attr('aria-expanded', false);
  103. var complete = function () {
  104. this.$pane
  105. .removeClass(classes.pane.activing);
  106. this.leave('activing');
  107. this.leave('active');
  108. this.trigger('afterHide');
  109. if($.isFunction(callback)){
  110. callback.call(this);
  111. }
  112. }
  113. if (!Support.transition) {
  114. return complete.call(this);
  115. }
  116. this.$pane.one(Support.transition.end, $.proxy(complete, this));
  117. emulateTransitionEnd(this.$pane, this.TRANSITION_DURATION);
  118. },
  119. empty: function() {
  120. this.$pane.empty();
  121. },
  122. load: function(callback) {
  123. var self = this;
  124. var loader = this.loader;
  125. if($.isFunction(loader)){
  126. loader = loader.call(this.wizard, this);
  127. }
  128. if(this.wizard.options.cacheContent && this.loaded){
  129. if($.isFunction(callback)){
  130. callback.call(this);
  131. }
  132. return true;
  133. }
  134. this.trigger('beforeLoad');
  135. this.enter('loading');
  136. function setContent(content) {
  137. self.$pane.html(content);
  138. self.leave('loading');
  139. self.loaded = true;
  140. self.trigger('afterLoad');
  141. if($.isFunction(callback)){
  142. callback.call(self);
  143. }
  144. }
  145. if (typeof loader === 'string') {
  146. setContent(loader);
  147. } else if (typeof loader === 'object' && loader.hasOwnProperty('url')) {
  148. self.wizard.options.loading.show.call(self.wizard, self);
  149. $.ajax(loader.url, loader.settings || {}).done(function(data) {
  150. setContent(data);
  151. self.wizard.options.loading.hide.call(self.wizard, self);
  152. }).fail(function(){
  153. self.wizard.options.loading.fail.call(self.wizard, self);
  154. });
  155. } else {
  156. setContent('');
  157. }
  158. },
  159. trigger: function(event) {
  160. var method_arguments = Array.prototype.slice.call(arguments, 1);
  161. if($.isArray(this.events[event])){
  162. for(var i in this.events[event]){
  163. this.events[event][i].apply(this, method_arguments);
  164. }
  165. }
  166. this.wizard.trigger.apply(this.wizard, [event, this].concat(method_arguments));
  167. },
  168. enter: function(state) {
  169. this.states[state] = true;
  170. var classes = this.wizard.options.classes;
  171. this.$element.addClass(classes.step[state]);
  172. this.trigger('stateChange', true, state);
  173. },
  174. leave: function(state) {
  175. if(this.states[state]){
  176. this.states[state] = false;
  177. var classes = this.wizard.options.classes;
  178. this.$element.removeClass(classes.step[state]);
  179. this.trigger('stateChange', false, state);
  180. }
  181. },
  182. setValidatorFromData: function(){
  183. var validator = this.$pane.data('validator');
  184. if(validator && $.isFunction(window[validator])){
  185. this.validator = window[validator];
  186. }
  187. },
  188. setLoaderFromData: function(){
  189. var loader = this.$pane.data('loader');
  190. if(loader){
  191. if($.isFunction(window[loader])){
  192. this.loader = window[loader];
  193. }
  194. } else {
  195. var url = this.$pane.data('loader-url');
  196. if(url){
  197. this.loader = {
  198. url: url,
  199. settings: this.$pane.data('settings') || {}
  200. }
  201. }
  202. }
  203. },
  204. /*
  205. * Public methods below
  206. */
  207. active: function(){
  208. return this.wizard.goTo(this.index);
  209. },
  210. on: function(event, handler){
  211. if($.isFunction(handler)){
  212. if($.isArray(this.events[event])){
  213. this.events[event].push(handler);
  214. } else {
  215. this.events[event] = [handler];
  216. }
  217. }
  218. return this;
  219. },
  220. off: function(event, handler){
  221. if($.isFunction(handler) && $.isArray(this.events[event])){
  222. $.each(this.events[event], function(i, f){
  223. if(f === handler) {
  224. delete this.events[event][i];
  225. return false;
  226. }
  227. });
  228. }
  229. return this;
  230. },
  231. is: function(state) {
  232. return this.states[state] && this.states[state] === true;
  233. },
  234. reset: function(){
  235. for(var state in this.states){
  236. this.leave(state);
  237. }
  238. this.setup();
  239. return this;
  240. },
  241. setLoader: function(loader){
  242. this.loader = loader;
  243. if(this.is('active')){
  244. this.load();
  245. }
  246. return this;
  247. },
  248. setValidator: function(validator) {
  249. if($.isFunction(validator)){
  250. this.validator = validator;
  251. }
  252. return this;
  253. },
  254. validate: function() {
  255. return this.validator.call(this.$pane.get(0), this);
  256. }
  257. });