rolodex 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +25 -0
- data/Rakefile +1 -0
- data/karma.conf.js +87 -0
- data/lib/rolodex.rb +31 -0
- data/lib/rolodex/engine.rb +12 -0
- data/lib/rolodex/sass.rb +5 -0
- data/lib/rolodex/version.rb +3 -0
- data/package.json +33 -0
- data/rolodex.gemspec +29 -0
- data/spec/javascripts/rolodex_angular/modal.spec.coffee +13 -0
- data/spec/spec_helper.rb +0 -0
- data/spec/test_lib/angular-mocks.js +2173 -0
- data/spec/test_lib/angular.js +21883 -0
- data/spec/test_lib/jquery-2.1.1.js +9190 -0
- data/spec/test_lib/lodash.js +6785 -0
- data/vendor/assets/.keep +0 -0
- data/vendor/assets/images/alerts/icon-danger-dark.png +0 -0
- data/vendor/assets/images/alerts/icon-danger-dark.svg +9 -0
- data/vendor/assets/images/alerts/icon-danger-light.png +0 -0
- data/vendor/assets/images/alerts/icon-danger-light.svg +9 -0
- data/vendor/assets/images/alerts/icon-flop-dark.png +0 -0
- data/vendor/assets/images/alerts/icon-flop-dark.svg +11 -0
- data/vendor/assets/images/alerts/icon-flop-light.png +0 -0
- data/vendor/assets/images/alerts/icon-flop-light.svg +11 -0
- data/vendor/assets/images/alerts/icon-info-dark.png +0 -0
- data/vendor/assets/images/alerts/icon-info-dark.svg +9 -0
- data/vendor/assets/images/alerts/icon-info-light.png +0 -0
- data/vendor/assets/images/alerts/icon-info-light.svg +9 -0
- data/vendor/assets/images/alerts/icon-tick-dark.png +0 -0
- data/vendor/assets/images/alerts/icon-tick-dark.svg +9 -0
- data/vendor/assets/images/alerts/icon-tick-light.png +0 -0
- data/vendor/assets/images/alerts/icon-tick-light.svg +9 -0
- data/vendor/assets/images/forms/checkbox.png +0 -0
- data/vendor/assets/images/forms/checkbox.svg +13 -0
- data/vendor/assets/images/forms/icon-calendar.png +0 -0
- data/vendor/assets/images/forms/icon-calendar.svg +20 -0
- data/vendor/assets/images/forms/icon-search.png +0 -0
- data/vendor/assets/images/forms/icon-search.svg +13 -0
- data/vendor/assets/images/forms/icon-validating.gif +0 -0
- data/vendor/assets/images/forms/icon-validating.psd +0 -0
- data/vendor/assets/images/forms/radio-button.png +0 -0
- data/vendor/assets/images/forms/radio-button.svg +12 -0
- data/vendor/assets/images/icons/icon-caret.png +0 -0
- data/vendor/assets/images/icons/icon-caret.svg +24 -0
- data/vendor/assets/images/icons/icon-close-x.png +0 -0
- data/vendor/assets/images/icons/icon-close-x.svg +9 -0
- data/vendor/assets/images/icons/icon-expand-gray.png +0 -0
- data/vendor/assets/images/icons/icon-expand-gray.svg +11 -0
- data/vendor/assets/images/icons/icon-expand-white.png +0 -0
- data/vendor/assets/images/icons/icon-expand-white.svg +11 -0
- data/vendor/assets/images/icons/icon-image.png +0 -0
- data/vendor/assets/images/icons/icon-image.svg +12 -0
- data/vendor/assets/images/icons/icon-invalid.png +0 -0
- data/vendor/assets/images/icons/icon-invalid.svg +14 -0
- data/vendor/assets/images/icons/icon-plane.png +0 -0
- data/vendor/assets/images/icons/icon-plane.svg +15 -0
- data/vendor/assets/images/icons/icon-star-banner.png +0 -0
- data/vendor/assets/images/icons/icon-star-banner.svg +16 -0
- data/vendor/assets/images/icons/icon-suggest.png +0 -0
- data/vendor/assets/images/icons/icon-suggest.svg +15 -0
- data/vendor/assets/images/icons/icon-tick.png +0 -0
- data/vendor/assets/images/icons/icon-tick.svg +9 -0
- data/vendor/assets/images/icons/icon-valid.png +0 -0
- data/vendor/assets/images/icons/icon-valid.svg +9 -0
- data/vendor/assets/images/loading/loading.png +0 -0
- data/vendor/assets/images/loading/loading.svg +23 -0
- data/vendor/assets/images/logo/belly-logo.png +0 -0
- data/vendor/assets/images/logo/belly-logo.svg +19 -0
- data/vendor/assets/images/tooltips/tooltip-star.png +0 -0
- data/vendor/assets/images/tooltips/tooltip-star.svg +20 -0
- data/vendor/assets/javascripts/rolodex_angular.coffee +3 -0
- data/vendor/assets/javascripts/rolodex_angular/README.md +68 -0
- data/vendor/assets/javascripts/rolodex_angular/rolodex.coffee +11 -0
- data/vendor/assets/javascripts/rolodex_angular/src/accordion.coffee +100 -0
- data/vendor/assets/javascripts/rolodex_angular/src/alert.coffee +17 -0
- data/vendor/assets/javascripts/rolodex_angular/src/buttons.coffee +59 -0
- data/vendor/assets/javascripts/rolodex_angular/src/collapse.coffee +65 -0
- data/vendor/assets/javascripts/rolodex_angular/src/dateparser.coffee +125 -0
- data/vendor/assets/javascripts/rolodex_angular/src/datepicker.coffee +547 -0
- data/vendor/assets/javascripts/rolodex_angular/src/dropdown.coffee +143 -0
- data/vendor/assets/javascripts/rolodex_angular/src/modal.coffee +331 -0
- data/vendor/assets/javascripts/rolodex_angular/src/position.coffee +128 -0
- data/vendor/assets/javascripts/rolodex_angular/src/transition.coffee +73 -0
- data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion-group.ngt.haml +7 -0
- data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion.ngt.haml +1 -0
- data/vendor/assets/javascripts/rolodex_angular/template/alert/alert.ngt.haml +3 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/datepicker.ngt.haml +4 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/day.ngt.haml +20 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/month.ngt.haml +17 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/popup.ngt.haml +2 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/year.ngt.haml +17 -0
- data/vendor/assets/javascripts/rolodex_angular/template/modal/window.ngt.haml +2 -0
- data/vendor/assets/javascripts/vendor/jquery-1.10.2.min.js +6 -0
- data/vendor/assets/javascripts/vendor/modernizr-2.6.2.min.js +4 -0
- data/vendor/assets/stylesheets/rolodex.css.sass +26 -0
- data/vendor/assets/stylesheets/rolodex/base/_layout.sass +58 -0
- data/vendor/assets/stylesheets/rolodex/base/_typography.sass +123 -0
- data/vendor/assets/stylesheets/rolodex/components/_alerts.sass +112 -0
- data/vendor/assets/stylesheets/rolodex/components/_buttons.sass +103 -0
- data/vendor/assets/stylesheets/rolodex/components/_dropdowns.sass +121 -0
- data/vendor/assets/stylesheets/rolodex/components/_forms.sass +326 -0
- data/vendor/assets/stylesheets/rolodex/components/_icons.sass +76 -0
- data/vendor/assets/stylesheets/rolodex/components/_labels.sass +29 -0
- data/vendor/assets/stylesheets/rolodex/components/_lists.sass +15 -0
- data/vendor/assets/stylesheets/rolodex/components/_loading.sass +32 -0
- data/vendor/assets/stylesheets/rolodex/components/_media.sass +14 -0
- data/vendor/assets/stylesheets/rolodex/components/_modals.sass +83 -0
- data/vendor/assets/stylesheets/rolodex/components/_tables.sass +9 -0
- data/vendor/assets/stylesheets/rolodex/components/_tooltips.sass +123 -0
- data/vendor/assets/stylesheets/rolodex/settings/_settings.sass +33 -0
- data/vendor/assets/stylesheets/rolodex/settings/mixins/_bump.sass +51 -0
- data/vendor/assets/stylesheets/rolodex/settings/mixins/_icons.sass +17 -0
- data/vendor/assets/stylesheets/rolodex/settings/mixins/_layout.sass +52 -0
- data/vendor/assets/stylesheets/rolodex/settings/mixins/_rems.sass +41 -0
- data/vendor/assets/stylesheets/rolodex/settings/mixins/_svg.sass +10 -0
- data/vendor/assets/stylesheets/rolodex/settings/mixins/_typography.sass +65 -0
- data/vendor/assets/stylesheets/rolodex/settings/utilities/_layout.sass +51 -0
- data/vendor/assets/stylesheets/rolodex/settings/utilities/_misc.sass +13 -0
- data/vendor/assets/stylesheets/rolodex/settings/utilities/_typography.sass +60 -0
- data/vendor/assets/stylesheets/rolodex/settings/variables/_alerts.scss +27 -0
- data/vendor/assets/stylesheets/rolodex/settings/variables/_buttons.scss +10 -0
- data/vendor/assets/stylesheets/rolodex/settings/variables/_colors.scss +46 -0
- data/vendor/assets/stylesheets/rolodex/settings/variables/_labels.scss +10 -0
- data/vendor/assets/stylesheets/rolodex/settings/variables/_layout.scss +22 -0
- data/vendor/assets/stylesheets/rolodex/settings/variables/_misc.scss +12 -0
- data/vendor/assets/stylesheets/rolodex/settings/variables/_typography.scss +34 -0
- 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
|