rolodex 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,3 @@
1
+ #= require_tree './rolodex_angular/src'
2
+
3
+ #= require 'rolodex_angular/rolodex'
@@ -0,0 +1,68 @@
1
+ # [Angular](https://angularjs.org/) modules just for [Rolodex](https://github.com/bellycard/rolodex)
2
+
3
+ ***
4
+
5
+ ## Wat?
6
+ Rolodex components written in Angular. Rolodex Angular has very few dependencies, requiring only [AngularJS](https://angularjs.org/), of course, and [LoDash](http://lodash.com/) (or [Underscore](http://underscorejs.org/)). The collection of directives should be very simple to use, fast, and drop in seamlessly when using Rolodex styles and classes. jQuery is not required for any of these modules to work now and forever. This project is heavily infulenced by [UI Bootstrap](http://angular-ui.github.io/bootstrap/).
7
+
8
+ ## See Rolodex Angular In Action
9
+ [Belly Style Guide](https://style.bellycard.com/)
10
+
11
+ ## Installation
12
+ Required libraries
13
+
14
+ * [AngularJS](https://angularjs.org/)
15
+ * [LoDash](http://lodash.com/) or [Underscore](http://underscorejs.org/)
16
+
17
+ Make sure that both are loaded before Rolodex.
18
+
19
+ Add to your `Gemfile:`
20
+
21
+ ```ruby
22
+ gem 'rolodex', :git => 'https://github.com/bellycard/rolodex.git'
23
+ ```
24
+
25
+ Import to your main coffee/js file
26
+
27
+ ```coffeescript
28
+ #= require 'rolodex_angular'
29
+ ```
30
+
31
+ Add Rolodex to your Angular App
32
+
33
+ ```coffeescript
34
+ angular.module('myApp', ['rolodex'])
35
+ ```
36
+
37
+ ## Dial Up Only What You Need
38
+ Utilizing Angular's flexible module system each directive is its own module which means you don't have to import then entire rolodex module if you don't need it. No need for jQuery either - each module should be independent of jQuery. Guaranteed. Forever.
39
+
40
+ ## Tested Browsers
41
+ * Chrome
42
+ * Safari
43
+ * IE 9/10
44
+ * Opera
45
+
46
+ ## Development
47
+ You will need already installed:
48
+
49
+ * Ruby
50
+ * NodeJS (For Karma, the test runner)
51
+
52
+ Run Bundler
53
+
54
+ ```
55
+ $: bundle install
56
+ ```
57
+
58
+ Run NPM install
59
+
60
+ ```
61
+ $: npm install
62
+ ```
63
+
64
+ Run Karma
65
+
66
+ ```
67
+ $: npm test_angular
68
+ ```
@@ -0,0 +1,11 @@
1
+ # Initialize primary namespace
2
+ angular.module('rolodex', [
3
+ 'templates'
4
+ 'rolodex.accordion'
5
+ 'rolodex.alert'
6
+ 'rolodex.buttons'
7
+ 'rolodex.datepicker'
8
+ 'rolodex.dropdown'
9
+ 'rolodex.modal'
10
+ 'rolodex.transition'
11
+ ])
@@ -0,0 +1,100 @@
1
+ #= require 'rolodex_angular/template/accordion/accordion'
2
+ #= require 'rolodex_angular/template/accordion/accordion-group'
3
+
4
+ angular.module("rolodex.accordion", ["rolodex.collapse"])
5
+
6
+ .constant("accordionConfig", closeOthers: true)
7
+
8
+ .controller("AccordionController", [
9
+ "$scope"
10
+ "$attrs"
11
+ "accordionConfig"
12
+ ($scope, $attrs, accordionConfig) ->
13
+ # This array keeps track of the accordion groups
14
+ @groups = []
15
+
16
+ # Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
17
+ @closeOthers = (openGroup) ->
18
+ closeOthers = (if angular.isDefined($attrs.closeOthers) then $scope.$eval($attrs.closeOthers) else accordionConfig.closeOthers)
19
+ if closeOthers
20
+ angular.forEach @groups, (group) ->
21
+ group.isOpen = false if group isnt openGroup
22
+
23
+ # This is called from the accordion-group directive to add itself to the accordion
24
+ @addGroup = (groupScope) ->
25
+ that = this
26
+ @groups.push groupScope
27
+ groupScope.$on "$destroy", (event) ->
28
+ that.removeGroup groupScope
29
+
30
+ # This is called from the accordion-group directive when to remove itself
31
+ @removeGroup = (group) ->
32
+ index = @groups.indexOf(group)
33
+ @groups.splice index, 1 if index isnt -1
34
+
35
+ return this
36
+ ])
37
+
38
+ # The accordion directive simply sets up the directive controller
39
+ # and adds an accordion CSS class to itself element.
40
+ .directive "accordion", ->
41
+ restrict: "EA"
42
+ controller: "AccordionController"
43
+ transclude: true
44
+ replace: false
45
+ templateUrl: 'rolodex_angular/template/accordion/accordion'
46
+
47
+ # The accordion-group directive indicates a block of html that will expand and collapse in an accordion
48
+ .directive "accordionGroup", ->
49
+ require: "^accordion" # We need this directive to be inside an accordion
50
+ restrict: "EA"
51
+ transclude: true # It transcludes the contents of the directive into the template
52
+ replace: true # The element containing the directive will be replaced with the template
53
+ templateUrl: 'rolodex_angular/template/accordion/accordion-group'
54
+ scope:
55
+ heading: "@" # Interpolate the heading attribute onto this scope
56
+ isOpen: "=?"
57
+ isDisabled: "=?"
58
+
59
+ controller: ->
60
+ @setHeading = (element) ->
61
+ @heading = element
62
+
63
+ link: (scope, element, attrs, accordionCtrl) ->
64
+ accordionCtrl.addGroup scope
65
+ scope.$watch "isOpen", (value) ->
66
+ accordionCtrl.closeOthers scope if value
67
+ return
68
+
69
+ scope.toggleOpen = ->
70
+ scope.isOpen = not scope.isOpen unless scope.isDisabled
71
+
72
+ # Use accordion-heading below an accordion-group to provide a heading containing HTML
73
+ # <accordion-group>
74
+ # <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>
75
+ # </accordion-group>
76
+ .directive "accordionHeading", ->
77
+ restrict: "EA"
78
+ transclude: true # Grab the contents to be used as the heading
79
+ template: "" # In effect remove this element!
80
+ replace: true
81
+ require: "^accordionGroup"
82
+ link: (scope, element, attr, accordionGroupCtrl, transclude) ->
83
+ # Pass the heading to the accordion-group controller
84
+ # so that it can be transcluded into the right place in the template
85
+ # [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
86
+ accordionGroupCtrl.setHeading transclude(scope, (->))
87
+
88
+ # Use in the accordion-group template to indicate where you want the heading to be transcluded
89
+ # You must provide the property on the accordion-group controller that will hold the transcluded element
90
+ # <div class="accordion-group">
91
+ # <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>
92
+ # ...
93
+ # </div>
94
+ .directive "accordionTransclude", ->
95
+ require: "^accordionGroup"
96
+ link: (scope, element, attr, controller) ->
97
+ scope.$watch (-> controller[attr.accordionTransclude]), (heading) ->
98
+ if heading
99
+ element.html ""
100
+ element.append heading
@@ -0,0 +1,17 @@
1
+ #= require 'rolodex_angular/template/alert/alert'
2
+
3
+ angular.module('rolodex.alert', [])
4
+
5
+ .controller('AlertController', ['$scope', '$attrs', ($scope, $attrs) ->
6
+ $scope.closeable = 'close' of $attrs
7
+ ])
8
+
9
+ .directive 'alert', ->
10
+ restrict: 'EA'
11
+ controller: 'AlertController'
12
+ templateUrl: 'rolodex_angular/template/alert/alert'
13
+ transclude: true
14
+ replace: true
15
+ scope:
16
+ type: '@'
17
+ close: '&'
@@ -0,0 +1,59 @@
1
+ angular.module("rolodex.buttons", [])
2
+
3
+ .constant("buttonConfig", activeClass: "active", toggleEvent: "click")
4
+
5
+ .controller("ButtonsController", [
6
+ "buttonConfig"
7
+ (buttonConfig) ->
8
+ @activeClass = buttonConfig.activeClass or "active"
9
+ @toggleEvent = buttonConfig.toggleEvent or "click"
10
+
11
+ return this
12
+ ])
13
+
14
+ .directive("btnRadio", ->
15
+ require: [
16
+ "btnRadio"
17
+ "ngModel"
18
+ ]
19
+ controller: "ButtonsController"
20
+ link: (scope, element, attrs, ctrls) ->
21
+ buttonsCtrl = ctrls[0]
22
+ ngModelCtrl = ctrls[1]
23
+ ngModelCtrl.$render = ->
24
+ element.toggleClass buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio))
25
+
26
+ element.bind buttonsCtrl.toggleEvent, ->
27
+ isActive = element.hasClass(buttonsCtrl.activeClass)
28
+ if not isActive or angular.isDefined(attrs.uncheckable)
29
+ scope.$apply ->
30
+ ngModelCtrl.$setViewValue (if isActive then null else scope.$eval(attrs.btnRadio))
31
+ ngModelCtrl.$render()
32
+ )
33
+
34
+ .directive "btnCheckbox", ->
35
+ require: [
36
+ "btnCheckbox"
37
+ "ngModel"
38
+ ]
39
+ controller: "ButtonsController"
40
+ link: (scope, element, attrs, ctrls) ->
41
+ getTrueValue = ->
42
+ getCheckboxValue attrs.btnCheckboxTrue, true
43
+ getFalseValue = ->
44
+ getCheckboxValue attrs.btnCheckboxFalse, false
45
+ getCheckboxValue = (attributeValue, defaultValue) ->
46
+ val = scope.$eval(attributeValue)
47
+ (if angular.isDefined(val) then val else defaultValue)
48
+ buttonsCtrl = ctrls[0]
49
+ ngModelCtrl = ctrls[1]
50
+
51
+ #model -> UI
52
+ ngModelCtrl.$render = ->
53
+ element.toggleClass buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())
54
+
55
+ #ui->model
56
+ element.bind buttonsCtrl.toggleEvent, ->
57
+ scope.$apply ->
58
+ ngModelCtrl.$setViewValue (if element.hasClass(buttonsCtrl.activeClass) then getFalseValue() else getTrueValue())
59
+ ngModelCtrl.$render()
@@ -0,0 +1,65 @@
1
+ angular.module("rolodex.collapse", ["rolodex.transition"])
2
+
3
+ .directive "collapse", [
4
+ "$transition"
5
+ ($transition) ->
6
+ return link: (scope, element, attrs) ->
7
+ doTransition = (change) ->
8
+ newTransitionDone = ->
9
+
10
+ # Make sure it's this transition, otherwise, leave it alone.
11
+ currentTransition = `undefined` if currentTransition is newTransition
12
+
13
+ newTransition = $transition(element, change)
14
+
15
+ currentTransition.cancel() if currentTransition
16
+
17
+ currentTransition = newTransition
18
+
19
+ newTransition.then(newTransitionDone, newTransitionDone)
20
+
21
+ return newTransition
22
+ return
23
+ expand = ->
24
+ if initialAnimSkip
25
+ initialAnimSkip = false
26
+ expandDone()
27
+ else
28
+ element.removeClass("collapse").addClass "collapsing"
29
+ doTransition(height: element[0].scrollHeight + "px").then expandDone
30
+ return
31
+ expandDone = ->
32
+ element.removeClass "collapsing"
33
+ element.addClass "collapse in"
34
+ element.css height: "auto"
35
+ return
36
+ collapse = ->
37
+ if initialAnimSkip
38
+ initialAnimSkip = false
39
+ collapseDone()
40
+ element.css height: 0
41
+ else
42
+
43
+ # CSS transitions don't work with height: auto, so we have to manually change the height to a specific value
44
+ element.css height: element[0].scrollHeight + "px"
45
+
46
+ #trigger reflow so a browser realizes that height was updated from auto to a specific value
47
+ x = element[0].offsetWidth
48
+ element.removeClass("collapse in").addClass "collapsing"
49
+ doTransition(height: 0).then collapseDone
50
+ return
51
+ collapseDone = ->
52
+ element.removeClass "collapsing"
53
+ element.addClass "collapse"
54
+ return
55
+ initialAnimSkip = true
56
+ currentTransition = undefined
57
+ scope.$watch attrs.collapse, (shouldCollapse) ->
58
+ if shouldCollapse
59
+ collapse()
60
+ else
61
+ expand()
62
+ return
63
+
64
+ return
65
+ ]
@@ -0,0 +1,125 @@
1
+ angular.module('rolodex.dateparser', [])
2
+
3
+ .service "dateParser", [
4
+ "$locale"
5
+ "orderByFilter"
6
+ ($locale, orderByFilter) ->
7
+ createParser = (format) ->
8
+ map = []
9
+ regex = format.split("")
10
+ angular.forEach formatCodeToRegex, (data, code) ->
11
+ index = format.indexOf(code)
12
+ if index > -1
13
+ format = format.split("")
14
+ regex[index] = "(" + data.regex + ")"
15
+ format[index] = "$" # Custom symbol to define consumed part of format
16
+ i = index + 1
17
+ n = index + code.length
18
+
19
+ while i < n
20
+ regex[i] = ""
21
+ format[i] = "$"
22
+ i++
23
+ format = format.join("")
24
+ map.push
25
+ index: index
26
+ apply: data.apply
27
+
28
+ return
29
+
30
+ regex: new RegExp("^" + regex.join("") + "$")
31
+ map: orderByFilter(map, "index")
32
+
33
+ # Check if date is valid for specific month (and year for February).
34
+ # Month: 0 = Jan, 1 = Feb, etc
35
+ isValid = (year, month, date) ->
36
+ return date is 29 and ((year % 4 is 0 and year % 100 isnt 0) or year % 400 is 0) if month is 1 and date > 28
37
+ return date < 31 if month is 3 or month is 5 or month is 8 or month is 10
38
+ true
39
+ @parsers = {}
40
+ formatCodeToRegex =
41
+ yyyy:
42
+ regex: "\\d{4}"
43
+ apply: (value) ->
44
+ @year = +value
45
+ return
46
+
47
+ yy:
48
+ regex: "\\d{2}"
49
+ apply: (value) ->
50
+ @year = +value + 2000
51
+ return
52
+
53
+ y:
54
+ regex: "\\d{1,4}"
55
+ apply: (value) ->
56
+ @year = +value
57
+ return
58
+
59
+ MMMM:
60
+ regex: $locale.DATETIME_FORMATS.MONTH.join("|")
61
+ apply: (value) ->
62
+ @month = $locale.DATETIME_FORMATS.MONTH.indexOf(value)
63
+ return
64
+
65
+ MMM:
66
+ regex: $locale.DATETIME_FORMATS.SHORTMONTH.join("|")
67
+ apply: (value) ->
68
+ @month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value)
69
+ return
70
+
71
+ MM:
72
+ regex: "0[1-9]|1[0-2]"
73
+ apply: (value) ->
74
+ @month = value - 1
75
+ return
76
+
77
+ M:
78
+ regex: "[1-9]|1[0-2]"
79
+ apply: (value) ->
80
+ @month = value - 1
81
+ return
82
+
83
+ dd:
84
+ regex: "[0-2][0-9]{1}|3[0-1]{1}"
85
+ apply: (value) ->
86
+ @date = +value
87
+ return
88
+
89
+ d:
90
+ regex: "[1-2]?[0-9]{1}|3[0-1]{1}"
91
+ apply: (value) ->
92
+ @date = +value
93
+
94
+ EEEE:
95
+ regex: $locale.DATETIME_FORMATS.DAY.join("|")
96
+
97
+ EEE:
98
+ regex: $locale.DATETIME_FORMATS.SHORTDAY.join("|")
99
+
100
+ @parse = (input, format) ->
101
+ return input if not angular.isString(input) or not format
102
+ format = $locale.DATETIME_FORMATS[format] or format
103
+ @parsers[format] = createParser(format) unless @parsers[format]
104
+ parser = @parsers[format]
105
+ regex = parser.regex
106
+ map = parser.map
107
+ results = input.match(regex)
108
+ if results and results.length
109
+ fields =
110
+ year: 1900
111
+ month: 0
112
+ date: 1
113
+ hours: 0
114
+
115
+ dt = undefined
116
+ i = 1
117
+ n = results.length
118
+
119
+ while i < n
120
+ mapper = map[i - 1]
121
+ mapper.apply.call fields, results[i] if mapper.apply
122
+ i++
123
+ dt = new Date(fields.year, fields.month, fields.date, fields.hours) if isValid(fields.year, fields.month, fields.date)
124
+ dt
125
+ ]