marionette.modal 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +36 -0
  3. data/Gemfile +4 -0
  4. data/Gruntfile.coffee +109 -0
  5. data/LICENSE +22 -0
  6. data/README.md +42 -0
  7. data/Rakefile +1 -0
  8. data/backbone.marionette.modals-min.js +1 -0
  9. data/backbone.marionette.modals.js +104 -0
  10. data/backbone.modal-min.js +1 -0
  11. data/backbone.modal.js +382 -0
  12. data/examples/1_single_view.html +71 -0
  13. data/examples/2_tab_based.html +104 -0
  14. data/examples/3_stacked_modal_with_marionette.html +105 -0
  15. data/examples/4_wizard.html +132 -0
  16. data/examples/css/style.css +45 -0
  17. data/examples/img/tab-icons.png +0 -0
  18. data/examples/style.css +35 -0
  19. data/examples/vendor/backbone.js +1571 -0
  20. data/examples/vendor/backbone.marionette.modals.js +104 -0
  21. data/examples/vendor/backbone.modal.css +24 -0
  22. data/examples/vendor/backbone.modal.js +382 -0
  23. data/examples/vendor/backbone.modal.theme.css +324 -0
  24. data/examples/vendor/jquery-1.9.1.js +9597 -0
  25. data/examples/vendor/marionette.js +2340 -0
  26. data/examples/vendor/marionette.modal.css +24 -0
  27. data/examples/vendor/marionette.modal.js +370 -0
  28. data/examples/vendor/marionette.modal.theme.css +324 -0
  29. data/examples/vendor/underscore.js +1227 -0
  30. data/lib/marionette.modal/version.rb +3 -0
  31. data/lib/marionette.modal.rb +5 -0
  32. data/marionette.modal-bundled-min.js +1 -0
  33. data/marionette.modal-bundled.js +858 -0
  34. data/marionette.modal-min.js +1 -0
  35. data/marionette.modal.css +24 -0
  36. data/marionette.modal.gemspec +23 -0
  37. data/marionette.modal.js +370 -0
  38. data/marionette.modal.theme.css +324 -0
  39. data/package.json +18 -0
  40. data/src/backbone.marionette.modals.coffee +67 -0
  41. data/src/backbone.modal.coffee +253 -0
  42. data/src/marionette.modal.coffee +248 -0
  43. data/src/marionette.modal.sass +26 -0
  44. data/src/marionette.modal.theme.sass +486 -0
  45. data/src/style.sass +48 -0
  46. data/test/spec/backbone.marionette.modals.spec.js +87 -0
  47. data/test/spec/backbone.modal.spec.js +224 -0
  48. data/test/spec.html +41 -0
  49. data/test/src/backbone.marionette.modals.spec.coffee +47 -0
  50. data/test/src/backbone.modal.spec.coffee +139 -0
  51. metadata +128 -0
@@ -0,0 +1,324 @@
1
+ .bbm-wrapper {
2
+ background: rgba(0, 0, 0, 0.75);
3
+ -webkit-transition: background-color 0.3s; }
4
+
5
+ .bbm-modal {
6
+ background: white;
7
+ box-shadow: 0 0px 6px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.9); }
8
+
9
+ /* BLOCKS */
10
+ .bbm-modal__topbar,
11
+ .bbm-modal__bottombar {
12
+ padding: 0 30px; }
13
+
14
+ .bbm-modal__topbar {
15
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
16
+ margin-bottom: 30px; }
17
+ .bbm-modal__topbar > ul {
18
+ list-style: none;
19
+ text-align: center;
20
+ padding: 0;
21
+ margin: 0; }
22
+
23
+ .bbm-modal__tab {
24
+ display: inline-block;
25
+ padding: 15px 10px; }
26
+ .bbm-modal__tab a {
27
+ font-size: 16px;
28
+ font-weight: bold;
29
+ color: #999999; }
30
+ .bbm-modal__tab a:hover, .bbm-modal__tab a.active {
31
+ color: #222222; }
32
+
33
+ .bbm-modal__title {
34
+ padding: 20px 0 19px;
35
+ margin: 0;
36
+ font-weight: normal;
37
+ font-size: 22px;
38
+ line-height: 1em;
39
+ color: #312d3a; }
40
+
41
+ .bbm-modal__section {
42
+ padding: 0 30px;
43
+ margin-top: 0px;
44
+ font-size: 16px;
45
+ line-height: 26px;
46
+ color: #575656; }
47
+ .bbm-modal__section p {
48
+ font-size: 16px;
49
+ line-height: 26px;
50
+ color: #575656; }
51
+ .bbm-modal__section p:last-child {
52
+ padding: 0;
53
+ margin-bottom: 0; }
54
+ .bbm-modal__section a {
55
+ color: #ff643c; }
56
+ .bbm-modal__section h3 {
57
+ margin: 0;
58
+ font-size: 20px;
59
+ line-height: 1em; }
60
+
61
+ .bbm-modal__bottombar {
62
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
63
+ padding: 18px;
64
+ text-align: right;
65
+ margin-top: 30px; }
66
+
67
+ /* MODULES */
68
+ .bbm-group {
69
+ content: "";
70
+ display: table;
71
+ clear: both; }
72
+
73
+ .bbm-button {
74
+ display: inline-block;
75
+ color: rgba(49, 45, 58, 0.8);
76
+ text-decoration: none;
77
+ font-size: 14px;
78
+ font-weight: 500;
79
+ position: relative;
80
+ line-height: 1em;
81
+ padding: 10px 14px;
82
+ border-radius: 3px;
83
+ background: #fcfcfc;
84
+ background-image: -o-linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.15) 100%);
85
+ background-image: -moz-linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.15) 100%);
86
+ background-image: -webkit-linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.15) 100%);
87
+ background-image: -ms-linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.15) 100%);
88
+ background-image: linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.15) 100%);
89
+ -moz-box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.1), inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2);
90
+ -webkit-box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.1), inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2);
91
+ box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.1), inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2); }
92
+ .bbm-button.inactive {
93
+ opacity: 0.5;
94
+ pointer-events: none; }
95
+ .bbm-button:active {
96
+ background-image: -o-linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.25) 100%);
97
+ background-image: -moz-linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.25) 100%);
98
+ background-image: -webkit-linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.25) 100%);
99
+ background-image: -ms-linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.25) 100%);
100
+ background-image: linear-gradient(rgba(70, 30, 170, 0) 0%, rgba(65, 61, 75, 0.25) 100%);
101
+ -moz-box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 0.5), inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2);
102
+ -webkit-box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 0.5), inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2);
103
+ box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 0.5), inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2); }
104
+
105
+ /* ANIMATIONS */
106
+ /* Open modal */
107
+ @-webkit-keyframes bbm-open {
108
+ 0% {
109
+ -webkit-transform: matrix(0.99126, 0, 0, 0.99126, 0, 43.8813);
110
+ opacity: 0.1259; }
111
+
112
+ 4% {
113
+ -webkit-transform: matrix(0.99295, 0, 0, 0.99295, 0, 45.06809);
114
+ opacity: 0.29544; }
115
+
116
+ 8% {
117
+ -webkit-transform: matrix(0.99467, 0, 0, 0.99467, 0, 46.26922);
118
+ opacity: 0.46703; }
119
+
120
+ 12% {
121
+ -webkit-transform: matrix(0.99619, 0, 0, 0.99619, 0, 47.33355);
122
+ opacity: 0.61908; }
123
+
124
+ 16% {
125
+ -webkit-transform: matrix(0.99743, 0, 0, 0.99743, 0, 48.19991);
126
+ opacity: 0.74284; }
127
+
128
+ 20% {
129
+ -webkit-transform: matrix(0.99837, 0, 0, 0.99837, 0, 48.86067);
130
+ opacity: 0.83724; }
131
+
132
+ 24% {
133
+ -webkit-transform: matrix(0.99905, 0, 0, 0.99905, 0, 49.33658);
134
+ opacity: 0.90523; }
135
+
136
+ 28% {
137
+ -webkit-transform: matrix(0.99952, 0, 0, 0.99952, 0, 49.66049);
138
+ opacity: 0.9515; }
139
+
140
+ 32% {
141
+ -webkit-transform: matrix(0.99981, 0, 0, 0.99981, 0, 49.8675);
142
+ opacity: 0.98107; }
143
+
144
+ 36% {
145
+ -webkit-transform: matrix(0.99999, 0, 0, 0.99999, 0, 49.98966);
146
+ opacity: 0.99852; }
147
+
148
+ 40% {
149
+ -webkit-transform: matrix(1.00008, 0, 0, 1.00008, 0, 50.05361);
150
+ opacity: 1.00766; }
151
+
152
+ 44% {
153
+ -webkit-transform: matrix(1.00011, 0, 0, 1.00011, 0, 50.08);
154
+ opacity: 1.01143; }
155
+
156
+ 48% {
157
+ -webkit-transform: matrix(1.00012, 0, 0, 1.00012, 0, 50.08394);
158
+ opacity: 1.01199; }
159
+
160
+ 52% {
161
+ -webkit-transform: matrix(1.00011, 0, 0, 1.00011, 0, 50.07589);
162
+ opacity: 1.01084; }
163
+
164
+ 56% {
165
+ -webkit-transform: matrix(1.00009, 0, 0, 1.00009, 0, 50.06265);
166
+ opacity: 1.00895; }
167
+
168
+ 60% {
169
+ -webkit-transform: matrix(1.00007, 0, 0, 1.00007, 0, 50.04833);
170
+ opacity: 1.0069; }
171
+
172
+ 64% {
173
+ -webkit-transform: matrix(1.00005, 0, 0, 1.00005, 0, 50.03518);
174
+ opacity: 1.00503; }
175
+
176
+ 68% {
177
+ -webkit-transform: matrix(1.00004, 0, 0, 1.00004, 0, 50.02421);
178
+ opacity: 1.00346; }
179
+
180
+ 72% {
181
+ -webkit-transform: matrix(1.00002, 0, 0, 1.00002, 0, 50.01567);
182
+ opacity: 1.00224; }
183
+
184
+ 76% {
185
+ -webkit-transform: matrix(1.00001, 0, 0, 1.00001, 0, 50.00941);
186
+ opacity: 1.00134; }
187
+
188
+ 80% {
189
+ -webkit-transform: matrix(1.00001, 0, 0, 1.00001, 0, 50.00506);
190
+ opacity: 1.00072; }
191
+
192
+ 84% {
193
+ -webkit-transform: matrix(1, 0, 0, 1, 0, 50.00223);
194
+ opacity: 1.00032; }
195
+
196
+ 88% {
197
+ -webkit-transform: matrix(1, 0, 0, 1, 0, 50.0005);
198
+ opacity: 1.00007; }
199
+
200
+ 92% {
201
+ -webkit-transform: matrix(1, 0, 0, 1, 0, 49.99956);
202
+ opacity: 0.99994; }
203
+
204
+ 96% {
205
+ -webkit-transform: matrix(1, 0, 0, 1, 0, 49.99913);
206
+ opacity: 0.99988; }
207
+
208
+ 100% {
209
+ -webkit-transform: matrix(1, 0, 0, 1, 0, 50);
210
+ opacity: 1; } }
211
+
212
+ .bbm-modal--open {
213
+ -webkit-animation-duration: 0.3s;
214
+ -webkit-animation-name: bbm-open;
215
+ -webkit-animation-timing-function: linear;
216
+ -webkit-animation-fill-mode: both;
217
+ -webkit-transform-origin: 50% 50%;
218
+ -webkit-backface-visibility: hidden; }
219
+
220
+ /* Open a stacked modal */
221
+ @-webkit-keyframes bbm-stacked {
222
+ 0% {
223
+ -webkit-transform: matrix(0.99874, 0, 0, 0.99874, 0, 49.1187);
224
+ opacity: 0.93705; }
225
+
226
+ 4% {
227
+ -webkit-transform: matrix(0.99705, 0, 0, 0.99705, 0, 47.93192);
228
+ opacity: 0.85228; }
229
+
230
+ 8% {
231
+ -webkit-transform: matrix(0.99533, 0, 0, 0.99533, 0, 46.73078);
232
+ opacity: 0.76648; }
233
+
234
+ 12% {
235
+ -webkit-transform: matrix(0.99381, 0, 0, 0.99381, 0, 45.66645);
236
+ opacity: 0.69046; }
237
+
238
+ 16% {
239
+ -webkit-transform: matrix(0.99257, 0, 0, 0.99257, 0, 44.80009);
240
+ opacity: 0.62858; }
241
+
242
+ 20% {
243
+ -webkit-transform: matrix(0.99163, 0, 0, 0.99163, 0, 44.13933);
244
+ opacity: 0.58138; }
245
+
246
+ 24% {
247
+ -webkit-transform: matrix(0.99095, 0, 0, 0.99095, 0, 43.66342);
248
+ opacity: 0.54739; }
249
+
250
+ 28% {
251
+ -webkit-transform: matrix(0.99049, 0, 0, 0.99049, 0, 43.33951);
252
+ opacity: 0.52425; }
253
+
254
+ 32% {
255
+ -webkit-transform: matrix(0.99019, 0, 0, 0.99019, 0, 43.1325);
256
+ opacity: 0.50946; }
257
+
258
+ 36% {
259
+ -webkit-transform: matrix(0.99002, 0, 0, 0.99002, 0, 43.01034);
260
+ opacity: 0.50074; }
261
+
262
+ 40% {
263
+ -webkit-transform: matrix(0.98992, 0, 0, 0.98992, 0, 42.94639);
264
+ opacity: 0.49617; }
265
+
266
+ 44% {
267
+ -webkit-transform: matrix(0.98989, 0, 0, 0.98989, 0, 42.92001);
268
+ opacity: 0.49429; }
269
+
270
+ 48% {
271
+ -webkit-transform: matrix(0.98988, 0, 0, 0.98988, 0, 42.91606);
272
+ opacity: 0.494; }
273
+
274
+ 52% {
275
+ -webkit-transform: matrix(0.98989, 0, 0, 0.98989, 0, 42.92411);
276
+ opacity: 0.49458; }
277
+
278
+ 56% {
279
+ -webkit-transform: matrix(0.98991, 0, 0, 0.98991, 0, 42.93736);
280
+ opacity: 0.49553; }
281
+
282
+ 60% {
283
+ -webkit-transform: matrix(0.98993, 0, 0, 0.98993, 0, 42.95167);
284
+ opacity: 0.49655; }
285
+
286
+ 64% {
287
+ -webkit-transform: matrix(0.98995, 0, 0, 0.98995, 0, 42.96482);
288
+ opacity: 0.49749; }
289
+
290
+ 68% {
291
+ -webkit-transform: matrix(0.98997, 0, 0, 0.98997, 0, 42.97579);
292
+ opacity: 0.49827; }
293
+
294
+ 72% {
295
+ -webkit-transform: matrix(0.98998, 0, 0, 0.98998, 0, 42.98433);
296
+ opacity: 0.49888; }
297
+
298
+ 76% {
299
+ -webkit-transform: matrix(0.98999, 0, 0, 0.98999, 0, 42.99059);
300
+ opacity: 0.49933; }
301
+
302
+ 80% {
303
+ -webkit-transform: matrix(0.98999, 0, 0, 0.98999, 0, 42.99494);
304
+ opacity: 0.49964; }
305
+
306
+ 84% {
307
+ -webkit-transform: matrix(0.99, 0, 0, 0.99, 0, 42.99777);
308
+ opacity: 0.49984; }
309
+
310
+ 88% {
311
+ -webkit-transform: matrix(0.99, 0, 0, 0.99, 0, 42.9995);
312
+ opacity: 0.49996; }
313
+
314
+ 92% {
315
+ -webkit-transform: matrix(0.99, 0, 0, 0.99, 0, 43.00044);
316
+ opacity: 0.50003; }
317
+
318
+ 96% {
319
+ -webkit-transform: matrix(0.99, 0, 0, 0.99, 0, 43.00088);
320
+ opacity: 0.50006; }
321
+
322
+ 100% {
323
+ -webkit-transform: matrix(0.99, 0, 0, 0.99, 0, 43);
324
+ opacity: 0.5; } }
data/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "Marionette.Modal",
3
+ "version": "1.0.0",
4
+ "description": "A plugin for Backbone.js that simplifies creating modals for your application.",
5
+ "devDependencies": {
6
+ "grunt": "~0.4.1",
7
+ "grunt-contrib-coffee": "~0.6.4",
8
+ "grunt-regarde": "0.1.x",
9
+ "grunt-contrib-livereload": "0.1.x",
10
+ "grunt-contrib-jasmine": "~0.4.1",
11
+ "grunt-contrib-connect": "~0.2.0",
12
+ "grunt-open": "~0.2.0",
13
+ "grunt-contrib-uglify": "~0.2.0",
14
+ "grunt-contrib-sass": "~0.3.0",
15
+ "grunt-concurrent": "~0.2.0"
16
+ },
17
+ "author": "jcsmith1859"
18
+ }
@@ -0,0 +1,67 @@
1
+ unless Backbone?
2
+ throw new Error("Backbone is not defined. Please include the latest version from http://documentcloud.github.com/backbone/backbone.js")
3
+
4
+ class Backbone.Marionette.Modals extends Backbone.Marionette.Region
5
+ modals: []
6
+ zIndex: 0
7
+
8
+ show: (modal, options = {}) ->
9
+ @ensureEl()
10
+
11
+ if @modals.length > 0
12
+ lastModal = _.last(@modals)
13
+ lastModal.modalEl.addClass("#{lastModal.prefix}-modal--stacked")
14
+ secondLastModal = @modals[@modals.length-1]
15
+ secondLastModal?.modalEl.removeClass("#{secondLastModal.prefix}-modal--stacked-reverse")
16
+
17
+ modal.render()
18
+ modal.regionEnabled = true
19
+
20
+ @$el.show()
21
+ @$el.append modal.el
22
+
23
+ modal.$el.css(background: 'none') if @modals.length > 0
24
+
25
+ Marionette.triggerMethod.call(modal, "show")
26
+ Marionette.triggerMethod.call(this, "show", modal)
27
+
28
+ @currentView = modal
29
+
30
+ m.undelegateModalEvents() for m in @modals
31
+
32
+ modal.on('modal:close', @close)
33
+
34
+ @modals.push(modal)
35
+ @zIndex++
36
+
37
+ close: =>
38
+ modal = @currentView
39
+ return if !modal or modal.isClosed
40
+
41
+ if modal.close
42
+ modal.close()
43
+ else if modal.remove
44
+ modal.remove()
45
+
46
+ modal.off('modal:close', @close)
47
+
48
+ @modals.splice(_.indexOf(@modals, modal), 1)
49
+
50
+ @zIndex--
51
+
52
+ @currentView = @modals[@zIndex-1]
53
+
54
+ lastModal = _.last(@modals)
55
+
56
+ if lastModal
57
+ lastModal.modalEl.addClass("#{lastModal.prefix}-modal--stacked-reverse")
58
+ _.delay =>
59
+ lastModal.modalEl.removeClass("#{lastModal.prefix}-modal--stacked")
60
+ , 300
61
+
62
+ lastModal.delegateModalEvents() if @zIndex isnt 0
63
+
64
+ Marionette.triggerMethod.call(this, "close")
65
+
66
+ closeAll: ->
67
+ @close() for modal in @modals
@@ -0,0 +1,253 @@
1
+ unless Backbone?
2
+ throw new Error("Backbone is not defined. Please include the latest version from http://documentcloud.github.com/backbone/backbone.js")
3
+
4
+ class Backbone.Modal extends Backbone.View
5
+ prefix: 'bbm'
6
+ constructor: ->
7
+ @args = Array::slice.apply(arguments)
8
+ Backbone.View::constructor.apply(this, @args)
9
+
10
+ @setUIElements()
11
+ @delegateModalEvents()
12
+
13
+ render: (options = {}) ->
14
+ # use openAt or overwrite this with your own functionality
15
+ data = @serializeData()
16
+
17
+ @$el.addClass("#{@prefix}-wrapper")
18
+ @modalEl = Backbone.$('<div />').addClass("#{@prefix}-modal")
19
+ @modalEl.html @template(data) if @template
20
+ @$el.html @modalEl
21
+
22
+ # global events for key and click outside the modal
23
+ Backbone.$('body').on 'keyup', @checkKey
24
+ Backbone.$('body').on 'click', @clickOutside
25
+
26
+ if @viewContainer
27
+ @viewContainerEl = @modalEl.find(@viewContainer)
28
+ @viewContainerEl.addClass("#{@prefix}-modal__views")
29
+ else
30
+ @viewContainerEl = @modalEl
31
+
32
+ @$el.show()
33
+ @openAt(0) if @views?.length > 0
34
+ @onRender?()
35
+
36
+ @modalEl.css(opacity: 0)
37
+ @$el.fadeIn
38
+ duration: 100
39
+ complete: =>
40
+ @modalEl.css(opacity: 1).addClass("#{@prefix}-modal--open")
41
+
42
+ return this
43
+
44
+ setUIElements: ->
45
+ # get modal options
46
+ @template = @getOption('template')
47
+ @views = @getOption('views')
48
+ @views?.length = _.size(@views)
49
+ @viewContainer = @getOption('viewContainer')
50
+
51
+ # hide modal
52
+ @$el.hide()
53
+
54
+ throw new Error('No template or views defined for Backbone.Modal') if _.isUndefined(@template) and _.isUndefined(@views)
55
+ throw new Error('No viewContainer defined for Backbone.Modal') if @template and @views and _.isUndefined(@viewContainer)
56
+
57
+ getOption: (option) ->
58
+ # get class instance property
59
+ return unless option
60
+ if @options and option in @options and @options[option]?
61
+ return @options[option]
62
+ else
63
+ return @[option]
64
+
65
+ serializeData: ->
66
+ # return the appropriate data for this view
67
+ data = {}
68
+
69
+ data = _.extend(data, @model.toJSON()) if @model
70
+ data = _.extend(data, {items: @collection.toJSON()}) if @collection
71
+
72
+ return data
73
+
74
+ delegateModalEvents: ->
75
+ @active = true
76
+
77
+ # get elements
78
+ cancelEl = @getOption('cancelEl')
79
+ submitEl = @getOption('submitEl')
80
+
81
+ # set event handlers for submit and cancel
82
+ @$el.on('click', submitEl, @triggerSubmit) if submitEl
83
+ @$el.on('click', cancelEl, @triggerCancel) if cancelEl
84
+
85
+ # set event handlers for views
86
+ for key of @views
87
+ unless key is 'length'
88
+ match = key.match(/^(\S+)\s*(.*)$/)
89
+ trigger = match[1]
90
+ selector = match[2]
91
+
92
+ @$el.on trigger, selector, @views[key], @triggerView
93
+
94
+ undelegateModalEvents: ->
95
+ @active = false
96
+
97
+ # get elements
98
+ cancelEl = @getOption('cancelEl')
99
+ submitEl = @getOption('submitEl')
100
+
101
+ # remove event handlers for submit and cancel
102
+ @$el.off('click', submitEl, @triggerSubmit) if submitEl
103
+ @$el.off('click', cancelEl, @triggerCancel) if cancelEl
104
+
105
+ # remove event handlers for views
106
+ for key of @views
107
+ unless key is 'length'
108
+ match = key.match(/^(\S+)\s*(.*)$/)
109
+ trigger = match[1]
110
+ selector = match[2]
111
+
112
+ @$el.off trigger, selector, @views[key], @triggerView
113
+
114
+ checkKey: (e) =>
115
+ if @active
116
+ switch e.keyCode
117
+ when 27 then @triggerCancel()
118
+ when 13 then @triggerSubmit()
119
+
120
+ clickOutside: (e) =>
121
+ @triggerCancel(null, true) if Backbone.$(e.target).hasClass("#{@prefix}-wrapper") and @active
122
+
123
+ buildView: (viewType) ->
124
+ # returns a Backbone.View instance, a function or an object
125
+ return unless viewType
126
+ if _.isFunction(viewType)
127
+ view = new viewType(@args[0])
128
+
129
+ if view instanceof Backbone.View
130
+ return {el: view.render().$el, view: view}
131
+ else
132
+ return {el: viewType(@args[0])}
133
+
134
+ return {view: viewType, el: viewType.$el}
135
+
136
+ triggerView: (e) =>
137
+ # trigger what view should be rendered
138
+ e?.preventDefault?()
139
+ options = e.data
140
+ instance = @buildView(options.view)
141
+
142
+ @previousView = @currentView if @currentView
143
+ @currentView = instance.view || instance.el
144
+
145
+ index = 0
146
+ for key of @views
147
+ @currentIndex = index if options.view is @views[key].view
148
+ index++
149
+
150
+ if options.onActive
151
+ if _.isFunction(options.onActive)
152
+ options.onActive(this)
153
+ else if _.isString(options.onActive)
154
+ this[options.onActive].call(this, options)
155
+
156
+ if @shouldAnimate
157
+ @animateToView(instance.el)
158
+ else
159
+ @shouldAnimate = true
160
+ @$(@viewContainerEl).html instance.el
161
+
162
+ animateToView: (view) ->
163
+ style = position: 'relative', top: -9999, left: -9999
164
+ tester = Backbone.$('<tester/>').css(style)
165
+ tester.html @$el.clone().css(style)
166
+ if Backbone.$('tester').length isnt 0 then Backbone.$('tester').replaceWith tester else Backbone.$('body').append tester
167
+
168
+ if @viewContainer
169
+ container = tester.find(@viewContainer)
170
+ else
171
+ container = tester.find(".#{@prefix}-modal")
172
+
173
+ container.removeAttr('style')
174
+
175
+ previousHeight = container.outerHeight()
176
+ container.html(view)
177
+ newHeight = container.outerHeight()
178
+
179
+ if previousHeight is newHeight
180
+ @$(@viewContainerEl).html view
181
+ @previousView?.close?()
182
+ else
183
+ @$(@viewContainerEl).css(opacity: 0)
184
+
185
+ @$(@viewContainerEl).animate {height: newHeight}, 100, =>
186
+ @$(@viewContainerEl).css(opacity: 1).removeAttr('style')
187
+ @$(@viewContainerEl).html view
188
+ @previousView?.close?()
189
+
190
+ triggerSubmit: (e) =>
191
+ return unless e
192
+ # triggers submit
193
+ e?.preventDefault()
194
+
195
+ if @beforeSubmit
196
+ return if @beforeSubmit() is false
197
+
198
+ @submit?()
199
+
200
+ if @regionEnabled
201
+ @trigger('modal:close')
202
+ else
203
+ @close()
204
+
205
+ triggerCancel: (e) =>
206
+ # triggers cancel
207
+ e?.preventDefault()
208
+
209
+ if @beforeCancel
210
+ return if @beforeCancel() is false
211
+
212
+ @cancel?()
213
+
214
+ if @regionEnabled
215
+ @trigger('modal:close')
216
+ else
217
+ @close()
218
+
219
+ close: ->
220
+ # closes view
221
+ Backbone.$('body').off 'keyup', @checkKey
222
+ Backbone.$('body').off 'click', @clickOutside
223
+
224
+ @onClose?()
225
+
226
+ @shouldAnimate = false
227
+ @modalEl.addClass("#{@prefix}-modal--close")
228
+ @$el.fadeOut(duration: 200)
229
+
230
+ _.delay =>
231
+ @currentView?.remove?()
232
+ @remove()
233
+ , 200
234
+
235
+ openAt: (index) ->
236
+ # loop through views and trigger the index
237
+ i = 0
238
+ for key of @views
239
+ unless key is 'length'
240
+ view = @views[key] if i is index
241
+ i++
242
+
243
+ if view
244
+ @currentIndex = index
245
+ @triggerView(data: view)
246
+
247
+ return this
248
+
249
+ next: ->
250
+ @openAt(@currentIndex+1) if @currentIndex+1 < @views.length
251
+
252
+ previous: ->
253
+ @openAt(@currentIndex-1) if @currentIndex-1 < @views.length-1