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,547 @@
1
+ #= require 'rolodex_angular/template/datepicker/datepicker'
2
+ #= require 'rolodex_angular/template/datepicker/day'
3
+ #= require 'rolodex_angular/template/datepicker/month'
4
+ #= require 'rolodex_angular/template/datepicker/popup'
5
+ #= require 'rolodex_angular/template/datepicker/year'
6
+
7
+ angular.module('rolodex.datepicker', [
8
+ 'rolodex.dateparser'
9
+ 'rolodex.dropdown'
10
+ 'rolodex.position'
11
+ ])
12
+
13
+ .constant 'datepickerConfig',
14
+ formatDay: 'd'
15
+ formatMonth: 'MMMM'
16
+ formatYear: 'yyyy'
17
+ formatDayHeader: 'EEE'
18
+ formatDayTitle: 'MMMM yyyy'
19
+ formatMonthTitle: 'yyyy'
20
+ datepickerMode: 'day'
21
+ minMode: 'day'
22
+ maxMode: 'year'
23
+ showWeeks: true
24
+ startingDay: 0
25
+ yearRange: 20
26
+ minDate: null
27
+ maxDate: null
28
+
29
+ .controller 'DatepickerController', [
30
+ '$scope'
31
+ '$attrs'
32
+ '$parse'
33
+ '$interpolate'
34
+ '$timeout'
35
+ '$log'
36
+ 'dateFilter'
37
+ 'datepickerConfig'
38
+ ($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) ->
39
+ self = this
40
+ ngModelCtrl = $setViewValue: angular.noop
41
+ @modes = [
42
+ 'day'
43
+ 'month'
44
+ 'year'
45
+ ]
46
+ angular.forEach [
47
+ 'formatDay'
48
+ 'formatMonth'
49
+ 'formatYear'
50
+ 'formatDayHeader'
51
+ 'formatDayTitle'
52
+ 'formatMonthTitle'
53
+ 'minMode'
54
+ 'maxMode'
55
+ 'showWeeks'
56
+ 'startingDay'
57
+ 'yearRange'
58
+ ], (key, index) ->
59
+ self[key] = (if angular.isDefined($attrs[key]) then ((if index < 8 then $interpolate($attrs[key])($scope.$parent) else $scope.$parent.$eval($attrs[key]))) else datepickerConfig[key])
60
+ return
61
+
62
+ angular.forEach [
63
+ 'minDate'
64
+ 'maxDate'
65
+ ], (key) ->
66
+ if $attrs[key]
67
+ $scope.$parent.$watch $parse($attrs[key]), (value) ->
68
+ self[key] = (if value then new Date(value) else null)
69
+ self.refreshView()
70
+
71
+ else
72
+ self[key] = (if datepickerConfig[key] then new Date(datepickerConfig[key]) else null)
73
+
74
+ $scope.datepickerMode = $scope.datepickerMode or datepickerConfig.datepickerMode
75
+
76
+ $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000)
77
+
78
+ @activeDate = (if angular.isDefined($attrs.initDate) then $scope.$parent.$eval($attrs.initDate) else new Date())
79
+
80
+ @init = (ngModelCtrl_) ->
81
+ ngModelCtrl = ngModelCtrl_
82
+ ngModelCtrl.$render = ->
83
+ self.render()
84
+
85
+ @render = ->
86
+ if ngModelCtrl.$modelValue
87
+ date = new Date(ngModelCtrl.$modelValue)
88
+ isValid = not isNaN(date)
89
+
90
+ if isValid
91
+ @activeDate = date
92
+ else
93
+ $log.error 'Datepicker directive: \'ng-model\' value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'
94
+
95
+ ngModelCtrl.$setValidity 'date', isValid
96
+
97
+ @refreshView()
98
+
99
+ @refreshView = ->
100
+ if @element
101
+ @_refreshView()
102
+ date = (if ngModelCtrl.$modelValue then new Date(ngModelCtrl.$modelValue) else null)
103
+ ngModelCtrl.$setValidity 'date-disabled', not date or (@element and not @isDisabled(date))
104
+
105
+ @createDateObject = (date, format) ->
106
+ model = (if ngModelCtrl.$modelValue then new Date(ngModelCtrl.$modelValue) else null)
107
+ date: date
108
+ label: dateFilter(date, format)
109
+ selected: model and @compare(date, model) is 0
110
+ disabled: @isDisabled(date)
111
+ current: @compare(date, new Date()) is 0
112
+
113
+ @isDisabled = (date) ->
114
+ (@minDate and @compare(date, @minDate) < 0) or (@maxDate and @compare(date, @maxDate) > 0) or ($attrs.dateDisabled and $scope.dateDisabled(date: date, mode: $scope.datepickerMode))
115
+
116
+ @split = (arr, size) ->
117
+ arrays = []
118
+ arrays.push arr.splice(0, size) while arr.length > 0
119
+ arrays
120
+
121
+ $scope.select = (date) =>
122
+ return if @isDisabled(date)
123
+
124
+ if $scope.datepickerMode is self.minMode
125
+ dt = (if ngModelCtrl.$modelValue then new Date(ngModelCtrl.$modelValue) else new Date(0, 0, 0, 0, 0, 0, 0))
126
+ dt.setFullYear date.getFullYear(), date.getMonth(), date.getDate()
127
+ ngModelCtrl.$setViewValue dt
128
+ ngModelCtrl.$render()
129
+ else
130
+ self.activeDate = date
131
+ $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1]
132
+
133
+ $scope.move = (direction) ->
134
+ year = self.activeDate.getFullYear() + direction * (self.step.years or 0)
135
+ month = self.activeDate.getMonth() + direction * (self.step.months or 0)
136
+ self.activeDate.setFullYear year, month, 1
137
+ self.refreshView()
138
+
139
+ $scope.toggleMode = (direction) ->
140
+ direction = direction or 1
141
+ return if ($scope.datepickerMode is self.maxMode and direction is 1) or ($scope.datepickerMode is self.minMode and direction is -1)
142
+ $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction]
143
+
144
+ $scope.keys =
145
+ 13: 'enter'
146
+ 32: 'space'
147
+ 33: 'pageup'
148
+ 34: 'pagedown'
149
+ 35: 'end'
150
+ 36: 'home'
151
+ 37: 'left'
152
+ 38: 'up'
153
+ 39: 'right'
154
+ 40: 'down'
155
+
156
+ focusElement = ->
157
+ $timeout (->
158
+ self.element[0].focus()
159
+ ), 0, false
160
+
161
+ $scope.$on 'datepicker.focus', focusElement
162
+
163
+ $scope.keydown = (evt) ->
164
+ key = $scope.keys[evt.which]
165
+ return if not key or evt.shiftKey or evt.altKey
166
+ evt.preventDefault()
167
+ evt.stopPropagation()
168
+
169
+ if key is 'enter' or key is 'space'
170
+ return if self.isDisabled(self.activeDate)
171
+ $scope.select self.activeDate
172
+ focusElement()
173
+ else if evt.ctrlKey and (key is 'up' or key is 'down')
174
+ $scope.toggleMode (if key is 'up' then 1 else -1)
175
+ focusElement()
176
+ else
177
+ self.handleKeyDown key, evt
178
+ self.refreshView()
179
+
180
+ return this
181
+ ]
182
+
183
+ .directive 'datepicker', ->
184
+ restrict: 'EA'
185
+ replace: true
186
+ templateUrl: 'rolodex_angular/template/datepicker/datepicker'
187
+ scope:
188
+ datepickerMode: '=?'
189
+ dateDisabled: '&'
190
+
191
+ require: [
192
+ 'datepicker'
193
+ '?^ngModel'
194
+ ]
195
+ controller: 'DatepickerController'
196
+ link: (scope, element, attrs, ctrls) ->
197
+ datepickerCtrl = ctrls[0]
198
+ ngModelCtrl = ctrls[1]
199
+ datepickerCtrl.init ngModelCtrl if ngModelCtrl
200
+
201
+ .directive 'daypicker', [
202
+ 'dateFilter'
203
+ (dateFilter) ->
204
+ return (
205
+ restrict: 'EA'
206
+ replace: true
207
+ templateUrl: 'rolodex_angular/template/datepicker/day'
208
+ require: '^datepicker'
209
+ link: (scope, element, attrs, ctrl) ->
210
+ getDaysInMonth = (year, month) ->
211
+ (if ((month is 1) and (year % 4 is 0) and ((year % 100 isnt 0) or (year % 400 is 0))) then 29 else DAYS_IN_MONTH[month])
212
+ getDates = (startDate, n) ->
213
+ dates = new Array(n)
214
+ current = new Date(startDate)
215
+ i = 0
216
+ current.setHours 12
217
+ while i < n
218
+ dates[i++] = new Date(current)
219
+ current.setDate current.getDate() + 1
220
+ dates
221
+ getISO8601WeekNumber = (date) ->
222
+ checkDate = new Date(date)
223
+ checkDate.setDate checkDate.getDate() + 4 - (checkDate.getDay() or 7)
224
+ time = checkDate.getTime()
225
+ checkDate.setMonth 0
226
+ checkDate.setDate 1
227
+ Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1
228
+ scope.showWeeks = ctrl.showWeeks
229
+ ctrl.step = months: 1
230
+ ctrl.element = element
231
+ DAYS_IN_MONTH = [
232
+ 31
233
+ 28
234
+ 31
235
+ 30
236
+ 31
237
+ 30
238
+ 31
239
+ 31
240
+ 30
241
+ 31
242
+ 30
243
+ 31
244
+ ]
245
+ ctrl._refreshView = ->
246
+ year = ctrl.activeDate.getFullYear()
247
+ month = ctrl.activeDate.getMonth()
248
+ firstDayOfMonth = new Date(year, month, 1)
249
+ difference = ctrl.startingDay - firstDayOfMonth.getDay()
250
+ numDisplayedFromPreviousMonth = (if (difference > 0) then 7 - difference else -difference)
251
+ firstDate = new Date(firstDayOfMonth)
252
+ firstDate.setDate -numDisplayedFromPreviousMonth + 1 if numDisplayedFromPreviousMonth > 0
253
+ days = getDates(firstDate, 42)
254
+ i = 0
255
+
256
+ while i < 42
257
+ days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay),
258
+ secondary: days[i].getMonth() isnt month
259
+ uid: scope.uniqueId + '-' + i
260
+ )
261
+ i++
262
+ scope.labels = new Array(7)
263
+ j = 0
264
+
265
+ while j < 7
266
+ scope.labels[j] =
267
+ abbr: dateFilter(days[j].date, ctrl.formatDayHeader).substr(0, 2)
268
+ full: dateFilter(days[j].date, 'EEEE')
269
+ j++
270
+ scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle)
271
+ scope.rows = ctrl.split(days, 7)
272
+ if scope.showWeeks
273
+ scope.weekNumbers = []
274
+ weekNumber = getISO8601WeekNumber(scope.rows[0][0].date)
275
+ numWeeks = scope.rows.length
276
+ continue while scope.weekNumbers.push(weekNumber++) < numWeeks
277
+
278
+ ctrl.compare = (date1, date2) ->
279
+ new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate())
280
+
281
+ ctrl.handleKeyDown = (key, evt) ->
282
+ date = ctrl.activeDate.getDate()
283
+ if key is 'left'
284
+ date = date - 1
285
+ else if key is 'up'
286
+ date = date - 7
287
+ else if key is 'right'
288
+ date = date + 1
289
+ else if key is 'down'
290
+ date = date + 7
291
+ else if key is 'pageup' or key is 'pagedown'
292
+ month = ctrl.activeDate.getMonth() + ((if key is 'pageup' then -1 else 1))
293
+ ctrl.activeDate.setMonth month, 1
294
+ date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date)
295
+ else if key is 'home'
296
+ date = 1
297
+ else date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()) if key is 'end'
298
+ ctrl.activeDate.setDate date
299
+
300
+ ctrl.refreshView()
301
+ )
302
+ ]
303
+
304
+ .directive 'monthpicker', [
305
+ 'dateFilter'
306
+ (dateFilter) ->
307
+ return (
308
+ restrict: 'EA'
309
+ replace: true
310
+ templateUrl: 'rolodex_angular/template/datepicker/month'
311
+ require: '^datepicker'
312
+ link: (scope, element, attrs, ctrl) ->
313
+ ctrl.step = years: 1
314
+ ctrl.element = element
315
+ ctrl._refreshView = ->
316
+ months = new Array(12)
317
+ year = ctrl.activeDate.getFullYear()
318
+ i = 0
319
+
320
+ while i < 12
321
+ months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth),
322
+ uid: scope.uniqueId + '-' + i
323
+ )
324
+ i++
325
+ scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle)
326
+ scope.rows = ctrl.split(months, 3)
327
+
328
+ ctrl.compare = (date1, date2) ->
329
+ new Date(date1.getFullYear(), date1.getMonth()) - new Date(date2.getFullYear(), date2.getMonth())
330
+
331
+ ctrl.handleKeyDown = (key, evt) ->
332
+ date = ctrl.activeDate.getMonth()
333
+ if key is 'left'
334
+ date = date - 1
335
+ else if key is 'up'
336
+ date = date - 3
337
+ else if key is 'right'
338
+ date = date + 1
339
+ else if key is 'down'
340
+ date = date + 3
341
+ else if key is 'pageup' or key is 'pagedown'
342
+ year = ctrl.activeDate.getFullYear() + ((if key is 'pageup' then -1 else 1))
343
+ ctrl.activeDate.setFullYear year
344
+ else if key is 'home'
345
+ date = 0
346
+ else date = 11 if key is 'end'
347
+ ctrl.activeDate.setMonth date
348
+
349
+ ctrl.refreshView()
350
+ )
351
+ ]
352
+
353
+ .directive 'yearpicker', [
354
+ 'dateFilter'
355
+ (dateFilter) ->
356
+ return (
357
+ restrict: 'EA'
358
+ replace: true
359
+ templateUrl: 'rolodex_angular/template/datepicker/year'
360
+ require: '^datepicker'
361
+ link: (scope, element, attrs, ctrl) ->
362
+ getStartingYear = (year) ->
363
+ parseInt((year - 1) / range, 10) * range + 1
364
+ range = ctrl.yearRange
365
+ ctrl.step = years: range
366
+ ctrl.element = element
367
+ ctrl._refreshView = ->
368
+ years = new Array(range)
369
+ i = 0
370
+ start = getStartingYear(ctrl.activeDate.getFullYear())
371
+
372
+ while i < range
373
+ years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear),
374
+ uid: scope.uniqueId + '-' + i
375
+ )
376
+ i++
377
+ scope.title = [
378
+ years[0].label
379
+ years[range - 1].label
380
+ ].join(' - ')
381
+ scope.rows = ctrl.split(years, 5)
382
+
383
+ ctrl.compare = (date1, date2) ->
384
+ date1.getFullYear() - date2.getFullYear()
385
+
386
+ ctrl.handleKeyDown = (key, evt) ->
387
+ date = ctrl.activeDate.getFullYear()
388
+ if key is 'left'
389
+ date = date - 1
390
+ else if key is 'up'
391
+ date = date - 5
392
+ else if key is 'right'
393
+ date = date + 1
394
+ else if key is 'down'
395
+ date = date + 5
396
+ else if key is 'pageup' or key is 'pagedown'
397
+ date += ((if key is 'pageup' then -1 else 1)) * ctrl.step.years
398
+ else if key is 'home'
399
+ date = getStartingYear(ctrl.activeDate.getFullYear())
400
+ else date = getStartingYear(ctrl.activeDate.getFullYear()) + range - 1 if key is 'end'
401
+ ctrl.activeDate.setFullYear date
402
+
403
+ ctrl.refreshView()
404
+ )
405
+ ]
406
+
407
+ .constant 'datepickerPopupConfig',
408
+ datepickerPopup: 'yyyy-MM-dd'
409
+ currentText: 'Today'
410
+ clearText: 'Clear'
411
+ closeText: 'Done'
412
+ closeOnDateSelection: true
413
+ appendToBody: false
414
+ showButtonBar: true
415
+
416
+ .directive 'datepickerPopup', [
417
+ '$compile'
418
+ '$parse'
419
+ '$document'
420
+ 'dateFilter'
421
+ 'dateParser'
422
+ 'datepickerPopupConfig'
423
+ 'dropdownService'
424
+ ($compile, $parse, $document, dateFilter, dateParser, datepickerPopupConfig, dropdownService) ->
425
+ return (
426
+ restrict: 'EA'
427
+ require: 'ngModel'
428
+ scope:
429
+ isOpen: '=?'
430
+ currentText: '@'
431
+ clearText: '@'
432
+ closeText: '@'
433
+ dateDisabled: '&'
434
+
435
+ link: (scope, element, attrs, ngModel) ->
436
+
437
+ cameltoDash = (string) ->
438
+ string.replace /([A-Z])/g, ($1) ->
439
+ '-' + $1.toLowerCase()
440
+
441
+ parseDate = (viewValue) ->
442
+ unless viewValue
443
+ ngModel.$setValidity 'date', true
444
+ null
445
+ else if angular.isDate(viewValue) and not isNaN(viewValue)
446
+ ngModel.$setValidity 'date', true
447
+ viewValue
448
+ else if angular.isString(viewValue)
449
+ date = dateParser.parse(viewValue, dateFormat) or new Date(viewValue)
450
+ if isNaN(date)
451
+ ngModel.$setValidity 'date', false
452
+ `undefined`
453
+ else
454
+ ngModel.$setValidity 'date', true
455
+ date
456
+ else
457
+ ngModel.$setValidity 'date', false
458
+ `undefined`
459
+ dateFormat = undefined
460
+ closeOnDateSelection = (if angular.isDefined(attrs.closeOnDateSelection) then scope.$parent.$eval(attrs.closeOnDateSelection) else datepickerPopupConfig.closeOnDateSelection)
461
+ appendToBody = (if angular.isDefined(attrs.datepickerAppendToBody) then scope.$parent.$eval(attrs.datepickerAppendToBody) else datepickerPopupConfig.appendToBody)
462
+ scope.showButtonBar = (if angular.isDefined(attrs.showButtonBar) then scope.$parent.$eval(attrs.showButtonBar) else datepickerPopupConfig.showButtonBar)
463
+ scope.getText = (key) ->
464
+ scope[key + 'Text'] or datepickerPopupConfig[key + 'Text']
465
+
466
+ attrs.$observe 'datepickerPopup', (value) ->
467
+ dateFormat = value or datepickerPopupConfig.datepickerPopup
468
+ ngModel.$render()
469
+
470
+ popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>')
471
+ popupEl.attr
472
+ 'ng-model': 'date'
473
+ 'ng-change': 'dateSelection()'
474
+
475
+ datepickerEl = angular.element(popupEl.children()[0])
476
+
477
+ if attrs.datepickerOptions
478
+ angular.forEach scope.$parent.$eval(attrs.datepickerOptions), (value, option) ->
479
+ datepickerEl.attr cameltoDash(option), value
480
+
481
+ scope.watchData = {}
482
+ angular.forEach [
483
+ 'minDate'
484
+ 'maxDate'
485
+ 'datepickerMode'
486
+ ], (key) ->
487
+ if attrs[key]
488
+ getAttribute = $parse(attrs[key])
489
+
490
+ scope.$parent.$watch getAttribute, (value) ->
491
+ scope.watchData[key] = value
492
+
493
+ datepickerEl.attr cameltoDash(key), 'watchData.' + key
494
+ if key is 'datepickerMode'
495
+ setAttribute = getAttribute.assign
496
+ scope.$watch 'watchData.' + key, (value, oldvalue) ->
497
+ setAttribute scope.$parent, value if value isnt oldvalue
498
+
499
+ datepickerEl.attr 'date-disabled', 'dateDisabled({ date: date, mode: mode })' if attrs.dateDisabled
500
+ ngModel.$parsers.unshift parseDate
501
+ scope.dateSelection = (dt) ->
502
+ scope.date = dt if angular.isDefined(dt)
503
+ ngModel.$setViewValue scope.date
504
+ ngModel.$render()
505
+ if closeOnDateSelection
506
+ scope.isOpen = false
507
+ scope.forceClose = true
508
+ dropdownService.close(scope)
509
+ element[0].focus()
510
+
511
+ element.bind 'input change keyup', ->
512
+ scope.$apply ->
513
+ scope.date = ngModel.$modelValue
514
+
515
+ ngModel.$render = ->
516
+ date = (if ngModel.$viewValue then dateFilter(ngModel.$viewValue, dateFormat) else '')
517
+ element.val date
518
+ scope.date = parseDate(ngModel.$modelValue)
519
+
520
+ scope.select = (date) ->
521
+
522
+ if date is 'today'
523
+ today = new Date()
524
+ if angular.isDate(ngModel.$modelValue)
525
+ date = new Date(ngModel.$modelValue)
526
+ date.setFullYear today.getFullYear(), today.getMonth(), today.getDate()
527
+ else
528
+ date = new Date(today.setHours(0, 0, 0, 0))
529
+ scope.dateSelection date
530
+ $popup = $compile(popupEl)(scope)
531
+ if appendToBody
532
+ $document.find('body').append $popup
533
+ else
534
+ element.after $popup
535
+ scope.$on '$destroy', ->
536
+ $popup.remove()
537
+ )
538
+ ]
539
+ .directive 'datepickerPopupWrap', ->
540
+ restrict: 'EA'
541
+ replace: true
542
+ transclude: true
543
+ templateUrl: 'rolodex_angular/template/datepicker/popup'
544
+ link: (scope, element, attrs) ->
545
+ element.bind 'click', (event) ->
546
+ event.preventDefault()
547
+ event.stopPropagation()