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.
- 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
|