rolodex 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +25 -0
  6. data/Rakefile +1 -0
  7. data/karma.conf.js +87 -0
  8. data/lib/rolodex.rb +31 -0
  9. data/lib/rolodex/engine.rb +12 -0
  10. data/lib/rolodex/sass.rb +5 -0
  11. data/lib/rolodex/version.rb +3 -0
  12. data/package.json +33 -0
  13. data/rolodex.gemspec +29 -0
  14. data/spec/javascripts/rolodex_angular/modal.spec.coffee +13 -0
  15. data/spec/spec_helper.rb +0 -0
  16. data/spec/test_lib/angular-mocks.js +2173 -0
  17. data/spec/test_lib/angular.js +21883 -0
  18. data/spec/test_lib/jquery-2.1.1.js +9190 -0
  19. data/spec/test_lib/lodash.js +6785 -0
  20. data/vendor/assets/.keep +0 -0
  21. data/vendor/assets/images/alerts/icon-danger-dark.png +0 -0
  22. data/vendor/assets/images/alerts/icon-danger-dark.svg +9 -0
  23. data/vendor/assets/images/alerts/icon-danger-light.png +0 -0
  24. data/vendor/assets/images/alerts/icon-danger-light.svg +9 -0
  25. data/vendor/assets/images/alerts/icon-flop-dark.png +0 -0
  26. data/vendor/assets/images/alerts/icon-flop-dark.svg +11 -0
  27. data/vendor/assets/images/alerts/icon-flop-light.png +0 -0
  28. data/vendor/assets/images/alerts/icon-flop-light.svg +11 -0
  29. data/vendor/assets/images/alerts/icon-info-dark.png +0 -0
  30. data/vendor/assets/images/alerts/icon-info-dark.svg +9 -0
  31. data/vendor/assets/images/alerts/icon-info-light.png +0 -0
  32. data/vendor/assets/images/alerts/icon-info-light.svg +9 -0
  33. data/vendor/assets/images/alerts/icon-tick-dark.png +0 -0
  34. data/vendor/assets/images/alerts/icon-tick-dark.svg +9 -0
  35. data/vendor/assets/images/alerts/icon-tick-light.png +0 -0
  36. data/vendor/assets/images/alerts/icon-tick-light.svg +9 -0
  37. data/vendor/assets/images/forms/checkbox.png +0 -0
  38. data/vendor/assets/images/forms/checkbox.svg +13 -0
  39. data/vendor/assets/images/forms/icon-calendar.png +0 -0
  40. data/vendor/assets/images/forms/icon-calendar.svg +20 -0
  41. data/vendor/assets/images/forms/icon-search.png +0 -0
  42. data/vendor/assets/images/forms/icon-search.svg +13 -0
  43. data/vendor/assets/images/forms/icon-validating.gif +0 -0
  44. data/vendor/assets/images/forms/icon-validating.psd +0 -0
  45. data/vendor/assets/images/forms/radio-button.png +0 -0
  46. data/vendor/assets/images/forms/radio-button.svg +12 -0
  47. data/vendor/assets/images/icons/icon-caret.png +0 -0
  48. data/vendor/assets/images/icons/icon-caret.svg +24 -0
  49. data/vendor/assets/images/icons/icon-close-x.png +0 -0
  50. data/vendor/assets/images/icons/icon-close-x.svg +9 -0
  51. data/vendor/assets/images/icons/icon-expand-gray.png +0 -0
  52. data/vendor/assets/images/icons/icon-expand-gray.svg +11 -0
  53. data/vendor/assets/images/icons/icon-expand-white.png +0 -0
  54. data/vendor/assets/images/icons/icon-expand-white.svg +11 -0
  55. data/vendor/assets/images/icons/icon-image.png +0 -0
  56. data/vendor/assets/images/icons/icon-image.svg +12 -0
  57. data/vendor/assets/images/icons/icon-invalid.png +0 -0
  58. data/vendor/assets/images/icons/icon-invalid.svg +14 -0
  59. data/vendor/assets/images/icons/icon-plane.png +0 -0
  60. data/vendor/assets/images/icons/icon-plane.svg +15 -0
  61. data/vendor/assets/images/icons/icon-star-banner.png +0 -0
  62. data/vendor/assets/images/icons/icon-star-banner.svg +16 -0
  63. data/vendor/assets/images/icons/icon-suggest.png +0 -0
  64. data/vendor/assets/images/icons/icon-suggest.svg +15 -0
  65. data/vendor/assets/images/icons/icon-tick.png +0 -0
  66. data/vendor/assets/images/icons/icon-tick.svg +9 -0
  67. data/vendor/assets/images/icons/icon-valid.png +0 -0
  68. data/vendor/assets/images/icons/icon-valid.svg +9 -0
  69. data/vendor/assets/images/loading/loading.png +0 -0
  70. data/vendor/assets/images/loading/loading.svg +23 -0
  71. data/vendor/assets/images/logo/belly-logo.png +0 -0
  72. data/vendor/assets/images/logo/belly-logo.svg +19 -0
  73. data/vendor/assets/images/tooltips/tooltip-star.png +0 -0
  74. data/vendor/assets/images/tooltips/tooltip-star.svg +20 -0
  75. data/vendor/assets/javascripts/rolodex_angular.coffee +3 -0
  76. data/vendor/assets/javascripts/rolodex_angular/README.md +68 -0
  77. data/vendor/assets/javascripts/rolodex_angular/rolodex.coffee +11 -0
  78. data/vendor/assets/javascripts/rolodex_angular/src/accordion.coffee +100 -0
  79. data/vendor/assets/javascripts/rolodex_angular/src/alert.coffee +17 -0
  80. data/vendor/assets/javascripts/rolodex_angular/src/buttons.coffee +59 -0
  81. data/vendor/assets/javascripts/rolodex_angular/src/collapse.coffee +65 -0
  82. data/vendor/assets/javascripts/rolodex_angular/src/dateparser.coffee +125 -0
  83. data/vendor/assets/javascripts/rolodex_angular/src/datepicker.coffee +547 -0
  84. data/vendor/assets/javascripts/rolodex_angular/src/dropdown.coffee +143 -0
  85. data/vendor/assets/javascripts/rolodex_angular/src/modal.coffee +331 -0
  86. data/vendor/assets/javascripts/rolodex_angular/src/position.coffee +128 -0
  87. data/vendor/assets/javascripts/rolodex_angular/src/transition.coffee +73 -0
  88. data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion-group.ngt.haml +7 -0
  89. data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion.ngt.haml +1 -0
  90. data/vendor/assets/javascripts/rolodex_angular/template/alert/alert.ngt.haml +3 -0
  91. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/datepicker.ngt.haml +4 -0
  92. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/day.ngt.haml +20 -0
  93. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/month.ngt.haml +17 -0
  94. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/popup.ngt.haml +2 -0
  95. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/year.ngt.haml +17 -0
  96. data/vendor/assets/javascripts/rolodex_angular/template/modal/window.ngt.haml +2 -0
  97. data/vendor/assets/javascripts/vendor/jquery-1.10.2.min.js +6 -0
  98. data/vendor/assets/javascripts/vendor/modernizr-2.6.2.min.js +4 -0
  99. data/vendor/assets/stylesheets/rolodex.css.sass +26 -0
  100. data/vendor/assets/stylesheets/rolodex/base/_layout.sass +58 -0
  101. data/vendor/assets/stylesheets/rolodex/base/_typography.sass +123 -0
  102. data/vendor/assets/stylesheets/rolodex/components/_alerts.sass +112 -0
  103. data/vendor/assets/stylesheets/rolodex/components/_buttons.sass +103 -0
  104. data/vendor/assets/stylesheets/rolodex/components/_dropdowns.sass +121 -0
  105. data/vendor/assets/stylesheets/rolodex/components/_forms.sass +326 -0
  106. data/vendor/assets/stylesheets/rolodex/components/_icons.sass +76 -0
  107. data/vendor/assets/stylesheets/rolodex/components/_labels.sass +29 -0
  108. data/vendor/assets/stylesheets/rolodex/components/_lists.sass +15 -0
  109. data/vendor/assets/stylesheets/rolodex/components/_loading.sass +32 -0
  110. data/vendor/assets/stylesheets/rolodex/components/_media.sass +14 -0
  111. data/vendor/assets/stylesheets/rolodex/components/_modals.sass +83 -0
  112. data/vendor/assets/stylesheets/rolodex/components/_tables.sass +9 -0
  113. data/vendor/assets/stylesheets/rolodex/components/_tooltips.sass +123 -0
  114. data/vendor/assets/stylesheets/rolodex/settings/_settings.sass +33 -0
  115. data/vendor/assets/stylesheets/rolodex/settings/mixins/_bump.sass +51 -0
  116. data/vendor/assets/stylesheets/rolodex/settings/mixins/_icons.sass +17 -0
  117. data/vendor/assets/stylesheets/rolodex/settings/mixins/_layout.sass +52 -0
  118. data/vendor/assets/stylesheets/rolodex/settings/mixins/_rems.sass +41 -0
  119. data/vendor/assets/stylesheets/rolodex/settings/mixins/_svg.sass +10 -0
  120. data/vendor/assets/stylesheets/rolodex/settings/mixins/_typography.sass +65 -0
  121. data/vendor/assets/stylesheets/rolodex/settings/utilities/_layout.sass +51 -0
  122. data/vendor/assets/stylesheets/rolodex/settings/utilities/_misc.sass +13 -0
  123. data/vendor/assets/stylesheets/rolodex/settings/utilities/_typography.sass +60 -0
  124. data/vendor/assets/stylesheets/rolodex/settings/variables/_alerts.scss +27 -0
  125. data/vendor/assets/stylesheets/rolodex/settings/variables/_buttons.scss +10 -0
  126. data/vendor/assets/stylesheets/rolodex/settings/variables/_colors.scss +46 -0
  127. data/vendor/assets/stylesheets/rolodex/settings/variables/_labels.scss +10 -0
  128. data/vendor/assets/stylesheets/rolodex/settings/variables/_layout.scss +22 -0
  129. data/vendor/assets/stylesheets/rolodex/settings/variables/_misc.scss +12 -0
  130. data/vendor/assets/stylesheets/rolodex/settings/variables/_typography.scss +34 -0
  131. metadata +263 -0
@@ -0,0 +1,143 @@
1
+ angular.module('rolodex.dropdown', [])
2
+
3
+ .constant('dropdownConfig', dropDownOpen: 'data-dropdown-open')
4
+
5
+ .service('dropdownService', [
6
+ '$document'
7
+ ($document) ->
8
+ openScope = null
9
+
10
+ @open = (dropdownScope) ->
11
+ unless openScope
12
+ $document.bind 'click', closeDropdown
13
+ $document.bind 'keydown', escapeKeyBind
14
+ openScope.isOpen = false if openScope and openScope isnt dropdownScope
15
+ openScope = dropdownScope
16
+
17
+ @close = (dropdownScope) ->
18
+ if openScope is dropdownScope or
19
+ dropdownScope.forceClose
20
+
21
+ if dropdownScope.forceClose
22
+ openScope.isOpen = false if openScope
23
+
24
+ openScope = null
25
+ $document.unbind 'click', closeDropdown
26
+ $document.unbind 'keydown', escapeKeyBind
27
+
28
+ closeDropdown = (evt) ->
29
+ toggleElement = openScope.getToggleElement()
30
+ if evt and toggleElement?[0].contains(evt.target) or
31
+ evt?.target.nodeName.toLowerCase() is 'input' # Don't close if the drop down has an input interaction
32
+ return
33
+
34
+ openScope.$apply ->
35
+ openScope.isOpen = false
36
+
37
+ escapeKeyBind = (evt) ->
38
+ if evt.which is 27
39
+ openScope.focusToggleElement()
40
+ closeDropdown()
41
+
42
+ return this
43
+ ])
44
+
45
+ .controller('DropdownController', [
46
+ '$scope'
47
+ '$attrs'
48
+ '$parse'
49
+ 'dropdownConfig'
50
+ 'dropdownService'
51
+ '$animate'
52
+ ($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) ->
53
+ self = this
54
+ scope = $scope.$new()
55
+ dropDownOpen = dropdownConfig.dropDownOpen
56
+ getIsOpen = undefined
57
+ setIsOpen = angular.noop
58
+ toggleInvoker = (if $attrs.onToggle then $parse($attrs.onToggle) else angular.noop)
59
+
60
+ @init = (element) ->
61
+ self.$element = element
62
+ if $attrs.isOpen
63
+ getIsOpen = $parse($attrs.isOpen)
64
+ setIsOpen = getIsOpen.assign
65
+ $scope.$watch getIsOpen, (value) ->
66
+ scope.isOpen = !!value
67
+
68
+ @toggle = (open) ->
69
+ scope.isOpen = (if arguments.length then !!open else not scope.isOpen)
70
+
71
+ @isOpen = ->
72
+ scope.isOpen
73
+
74
+ scope.getToggleElement = ->
75
+ self.toggleElement
76
+
77
+ scope.focusToggleElement = ->
78
+ self.toggleElement[0].focus() if self.toggleElement
79
+ return
80
+
81
+ scope.$watch 'isOpen', (isOpen, wasOpen) ->
82
+ anchors = self.$element.find('a')
83
+
84
+ if isOpen
85
+ self.$element.attr(dropDownOpen, '')
86
+ scope.focusToggleElement()
87
+ dropdownService.open scope
88
+
89
+ if anchors.length
90
+ scope.forceClose = true
91
+ _.each anchors, (anchor) ->
92
+ angular.element(anchor).on 'click', (evt) ->
93
+ dropdownService.close(scope)
94
+ scope.$digest()
95
+
96
+ else
97
+ self.$element.removeAttr(dropDownOpen)
98
+ dropdownService.close(scope)
99
+
100
+ setIsOpen $scope, isOpen
101
+ if angular.isDefined(isOpen) and isOpen isnt wasOpen
102
+ toggleInvoker $scope,
103
+ open: !!isOpen
104
+
105
+ $scope.$on '$locationChangeSuccess', ->
106
+ scope.isOpen = false
107
+
108
+ $scope.$on '$destroy', ->
109
+ scope.$destroy()
110
+
111
+ return this
112
+
113
+ ])
114
+
115
+ .directive 'dropdown', ->
116
+ controller: 'DropdownController'
117
+ link: (scope, element, attrs, dropdownCtrl) ->
118
+ dropdownCtrl.init element
119
+
120
+ .directive 'dropdownToggle', ->
121
+ require: '?^dropdown'
122
+ link: (scope, element, attrs, dropdownCtrl) ->
123
+ return unless dropdownCtrl
124
+ dropdownCtrl.toggleElement = element
125
+
126
+ toggleDropdown = (event) ->
127
+ event.preventDefault()
128
+ if not element.hasClass('disabled') and not attrs.disabled
129
+ scope.$apply ->
130
+ dropdownCtrl.toggle()
131
+
132
+ element.bind 'click', toggleDropdown
133
+
134
+ # WAI-ARIA
135
+ element.attr
136
+ 'aria-haspopup': true
137
+ 'aria-expanded': false
138
+
139
+ scope.$watch dropdownCtrl.isOpen, (isOpen) ->
140
+ element.attr 'aria-expanded', !!isOpen
141
+
142
+ scope.$on '$destroy', ->
143
+ element.unbind 'click', toggleDropdown
@@ -0,0 +1,331 @@
1
+ #= require 'rolodex_angular/template/modal/window'
2
+
3
+ # Because of the flexibility of UI-Bootstraps modal this is a copy of their source converted to CoffeeScript for
4
+ # maintainability. The module namespace has been changed to suite Rolodex and the template has been adjusted to
5
+ # work with the Rolodex styles. The rest remains true to the Bootstrap source including using their transition
6
+ # animation library and not ngAnimate.
7
+
8
+ angular.module("rolodex.modal", ["rolodex.transition", "templates"])
9
+
10
+ # A helper, internal data structure that acts as a map but also allows getting / removing
11
+ # elements in the LIFO order
12
+ .factory "$$stackedMap", ->
13
+ createNew: ->
14
+ stack = []
15
+ add: (key, value) ->
16
+ stack.push
17
+ key: key
18
+ value: value
19
+
20
+ get: (key) ->
21
+ i = 0
22
+
23
+ while i < stack.length
24
+ return stack[i] if key is stack[i].key
25
+ i++
26
+
27
+ keys: ->
28
+ keys = []
29
+ i = 0
30
+
31
+ while i < stack.length
32
+ keys.push stack[i].key
33
+ i++
34
+ keys
35
+
36
+ top: ->
37
+ stack[stack.length - 1]
38
+
39
+ remove: (key) ->
40
+ idx = -1
41
+ i = 0
42
+
43
+ while i < stack.length
44
+ if key is stack[i].key
45
+ idx = i
46
+ break
47
+ i++
48
+ stack.splice(idx, 1)[0]
49
+
50
+ removeTop: ->
51
+ stack.splice(stack.length - 1, 1)[0]
52
+
53
+ length: ->
54
+ stack.length
55
+
56
+ .directive("modalWindow", [
57
+ "$modalStack"
58
+ "$timeout"
59
+ ($modalStack, $timeout) ->
60
+
61
+ restrict: "EA"
62
+ scope:
63
+ index: "@"
64
+ animate: "="
65
+
66
+ replace: true
67
+ transclude: true
68
+ templateUrl: (tElement, tAttrs) ->
69
+ tAttrs.templateUrl or "rolodex_angular/template/modal/window"
70
+
71
+ link: (scope, element, attrs) ->
72
+ element.addClass attrs.windowClass or ""
73
+ scope.size = attrs.size
74
+ $timeout ->
75
+ scope.animate = true
76
+ element[0].focus() unless element[0].querySelectorAll("[autofocus]").length
77
+ return
78
+
79
+ scope.close = (evt) ->
80
+ modal = $modalStack.getTop()
81
+ # Auto-focusing of a freshly-opened modal element causes any child elements
82
+ # with the autofocus attribute to loose focus. This is an issue on touch
83
+ # based devices which will show and then hide the onscreen keyboard.
84
+ # Attempts to refocus the autofocus element via JavaScript will not reopen
85
+ # the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
86
+ # the modal element if the modal does not contain an autofocus element.
87
+ if modal and modal.value.backdrop and modal.value.backdrop isnt "static" and (evt.target is evt.currentTarget)
88
+ evt.preventDefault()
89
+ evt.stopPropagation()
90
+ $modalStack.dismiss modal.key, "backdrop click"
91
+ ])
92
+
93
+ .directive "modalTransclude", ->
94
+ link: ($scope, $element, $attrs, controller, $transclude) ->
95
+ $transclude $scope.$parent, (clone) ->
96
+ $element.empty()
97
+ $element.append clone
98
+
99
+ .factory("$modalStack", [
100
+ "$transition"
101
+ "$timeout"
102
+ "$document"
103
+ "$compile"
104
+ "$rootScope"
105
+ "$$stackedMap"
106
+ ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) ->
107
+ backdropIndex = ->
108
+ topBackdropIndex = -1
109
+ opened = openedWindows.keys()
110
+ i = 0
111
+
112
+ while i < opened.length
113
+ topBackdropIndex = i if openedWindows.get(opened[i]).value.backdrop
114
+ i++
115
+ topBackdropIndex
116
+ removeModalWindow = (modalInstance) ->
117
+ body = $document.find("body").eq(0)
118
+ modalWindow = openedWindows.get(modalInstance).value
119
+ openedWindows.remove modalInstance
120
+ removeAfterAnimate modalWindow.modalDomEl, modalWindow.modalScope, 300, ->
121
+ modalWindow.modalScope.$destroy()
122
+ body.toggleClass OPENED_MODAL_CLASS, openedWindows.length() > 0
123
+ checkRemoveBackdrop()
124
+ return
125
+
126
+ return
127
+ checkRemoveBackdrop = ->
128
+ if backdropDomEl and backdropIndex() is -1
129
+ backdropScopeRef = backdropScope
130
+ removeAfterAnimate backdropDomEl, backdropScope, 150, ->
131
+ backdropScopeRef.$destroy()
132
+ backdropScopeRef = null
133
+ return
134
+
135
+ backdropDomEl = `undefined`
136
+ backdropScope = `undefined`
137
+ return
138
+ removeAfterAnimate = (domEl, scope, emulateTime, done) ->
139
+ afterAnimating = ->
140
+ return if afterAnimating.done
141
+ afterAnimating.done = true
142
+ domEl.remove()
143
+ done() if done
144
+ return
145
+ scope.animate = false
146
+ transitionEndEventName = $transition.transitionEndEventName
147
+ if transitionEndEventName
148
+ timeout = $timeout(afterAnimating, emulateTime)
149
+ domEl.bind transitionEndEventName, ->
150
+ $timeout.cancel timeout
151
+ afterAnimating()
152
+ scope.$apply()
153
+ return
154
+
155
+ else
156
+ $timeout afterAnimating
157
+ return
158
+ OPENED_MODAL_CLASS = "modal-open"
159
+ backdropDomEl = undefined
160
+ backdropScope = undefined
161
+ openedWindows = $$stackedMap.createNew()
162
+ $modalStack = {}
163
+ $rootScope.$watch backdropIndex, (newBackdropIndex) ->
164
+ backdropScope.index = newBackdropIndex if backdropScope
165
+ return
166
+
167
+ $document.bind "keydown", (evt) ->
168
+ modal = undefined
169
+ if evt.which is 27
170
+ modal = openedWindows.top()
171
+ if modal and modal.value.keyboard
172
+ evt.preventDefault()
173
+ $rootScope.$apply ->
174
+ $modalStack.dismiss modal.key, "escape key press"
175
+ return
176
+
177
+ return
178
+
179
+ $modalStack.open = (modalInstance, modal) ->
180
+ openedWindows.add modalInstance,
181
+ deferred: modal.deferred
182
+ modalScope: modal.scope
183
+ backdrop: modal.backdrop
184
+ keyboard: modal.keyboard
185
+
186
+ body = $document.find("body").eq(0)
187
+ currBackdropIndex = backdropIndex()
188
+ if currBackdropIndex >= 0 and not backdropDomEl
189
+ backdropScope = $rootScope.$new(true)
190
+ backdropScope.index = currBackdropIndex
191
+ angularBackgroundDomEl = angular.element("<div modal-backdrop></div>")
192
+ angularBackgroundDomEl.attr "backdrop-class", modal.backdropClass
193
+ backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope)
194
+ body.append backdropDomEl
195
+ angularDomEl = angular.element("<div modal-window></div>")
196
+ angularDomEl.attr(
197
+ "template-url": modal.windowTemplateUrl
198
+ "window-class": modal.windowClass
199
+ size: modal.size
200
+ index: openedWindows.length() - 1
201
+ animate: "animate"
202
+ ).html modal.content
203
+ modalDomEl = $compile(angularDomEl)(modal.scope)
204
+ openedWindows.top().value.modalDomEl = modalDomEl
205
+ body.append modalDomEl
206
+ body.addClass OPENED_MODAL_CLASS
207
+ return
208
+
209
+ $modalStack.close = (modalInstance, result) ->
210
+ modalWindow = openedWindows.get(modalInstance)
211
+ if modalWindow
212
+ modalWindow.value.deferred.resolve result
213
+ removeModalWindow modalInstance
214
+ return
215
+
216
+ $modalStack.dismiss = (modalInstance, reason) ->
217
+ modalWindow = openedWindows.get(modalInstance)
218
+ if modalWindow
219
+ modalWindow.value.deferred.reject reason
220
+ removeModalWindow modalInstance
221
+ return
222
+
223
+ $modalStack.dismissAll = (reason) ->
224
+ topModal = @getTop()
225
+ while topModal
226
+ @dismiss topModal.key, reason
227
+ topModal = @getTop()
228
+ return
229
+
230
+ $modalStack.getTop = ->
231
+ openedWindows.top()
232
+
233
+ return $modalStack
234
+ ])
235
+
236
+ .provider "$modal", ->
237
+ $modalProvider =
238
+ options:
239
+ backdrop: true #can be also false or 'static'
240
+ keyboard: true
241
+
242
+ $get: [
243
+ "$injector"
244
+ "$rootScope"
245
+ "$q"
246
+ "$http"
247
+ "$templateCache"
248
+ "$controller"
249
+ "$modalStack"
250
+ ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) ->
251
+ getTemplatePromise = (options) ->
252
+ (if options.template then $q.when(options.template) else $http.get((if angular.isFunction(options.templateUrl) then (options.templateUrl)() else options.templateUrl),
253
+ cache: $templateCache
254
+ ).then((result) ->
255
+ result.data
256
+ ))
257
+ getResolvePromises = (resolves) ->
258
+ promisesArr = []
259
+ angular.forEach resolves, (value) ->
260
+ promisesArr.push $q.when($injector.invoke(value)) if angular.isFunction(value) or angular.isArray(value)
261
+ return
262
+
263
+ promisesArr
264
+ $modal = {}
265
+ $modal.open = (modalOptions) ->
266
+ modalResultDeferred = $q.defer()
267
+ modalOpenedDeferred = $q.defer()
268
+
269
+ #prepare an instance of a modal to be injected into controllers and returned to a caller
270
+ modalInstance =
271
+ result: modalResultDeferred.promise
272
+ opened: modalOpenedDeferred.promise
273
+ close: (result) ->
274
+ $modalStack.close modalInstance, result
275
+ return
276
+
277
+ dismiss: (reason) ->
278
+ $modalStack.dismiss modalInstance, reason
279
+ return
280
+
281
+
282
+ #merge and clean up options
283
+ modalOptions = angular.extend({}, $modalProvider.options, modalOptions)
284
+ modalOptions.resolve = modalOptions.resolve or {}
285
+
286
+ #verify options
287
+ throw new Error("One of template or templateUrl options is required.") if not modalOptions.template and not modalOptions.templateUrl
288
+ templateAndResolvePromise = $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)))
289
+ templateAndResolvePromise.then (resolveSuccess = (tplAndVars) ->
290
+ modalScope = (modalOptions.scope or $rootScope).$new()
291
+ modalScope.$close = modalInstance.close
292
+ modalScope.$dismiss = modalInstance.dismiss
293
+ ctrlInstance = undefined
294
+ ctrlLocals = {}
295
+ resolveIter = 1
296
+
297
+ #controllers
298
+ if modalOptions.controller
299
+ ctrlLocals.$scope = modalScope
300
+ ctrlLocals.$modalInstance = modalInstance
301
+ angular.forEach modalOptions.resolve, (value, key) ->
302
+ ctrlLocals[key] = tplAndVars[resolveIter++]
303
+ return
304
+
305
+ ctrlInstance = $controller(modalOptions.controller, ctrlLocals)
306
+ modalScope[modalOptions.controllerAs] = ctrlInstance if modalOptions.controller
307
+ $modalStack.open modalInstance,
308
+ scope: modalScope
309
+ deferred: modalResultDeferred
310
+ content: tplAndVars[0]
311
+ backdrop: modalOptions.backdrop
312
+ keyboard: modalOptions.keyboard
313
+ backdropClass: modalOptions.backdropClass
314
+ windowClass: modalOptions.windowClass
315
+ windowTemplateUrl: modalOptions.windowTemplateUrl
316
+ size: modalOptions.size
317
+
318
+ ), resolveError = (reason) ->
319
+ modalResultDeferred.reject reason
320
+
321
+ templateAndResolvePromise.then (->
322
+ modalOpenedDeferred.resolve true
323
+ ), ->
324
+ modalOpenedDeferred.reject false
325
+
326
+ modalInstance
327
+
328
+ return $modal
329
+ ]
330
+
331
+ $modalProvider