pickadate-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +61 -0
  6. data/Rakefile +26 -0
  7. data/lib/pickadate-rails.rb +7 -0
  8. data/lib/pickadate-rails/version.rb +3 -0
  9. data/pickadate-rails.gemspec +25 -0
  10. data/vendor/assets/javascripts/pickadate/picker.date.js +925 -0
  11. data/vendor/assets/javascripts/pickadate/picker.js +785 -0
  12. data/vendor/assets/javascripts/pickadate/picker.time.js +651 -0
  13. data/vendor/assets/javascripts/pickadate/translations/bg_BG.js +13 -0
  14. data/vendor/assets/javascripts/pickadate/translations/bs_BA.js +13 -0
  15. data/vendor/assets/javascripts/pickadate/translations/ca_ES.js +13 -0
  16. data/vendor/assets/javascripts/pickadate/translations/cs_CZ.js +13 -0
  17. data/vendor/assets/javascripts/pickadate/translations/da_DK.js +13 -0
  18. data/vendor/assets/javascripts/pickadate/translations/de_DE.js +13 -0
  19. data/vendor/assets/javascripts/pickadate/translations/el_GR.js +13 -0
  20. data/vendor/assets/javascripts/pickadate/translations/es_ES.js +13 -0
  21. data/vendor/assets/javascripts/pickadate/translations/et_EE.js +13 -0
  22. data/vendor/assets/javascripts/pickadate/translations/eu_ES.js +13 -0
  23. data/vendor/assets/javascripts/pickadate/translations/fi_FI.js +13 -0
  24. data/vendor/assets/javascripts/pickadate/translations/fr_FR.js +13 -0
  25. data/vendor/assets/javascripts/pickadate/translations/he_IL.js +12 -0
  26. data/vendor/assets/javascripts/pickadate/translations/hr_HR.js +13 -0
  27. data/vendor/assets/javascripts/pickadate/translations/hu_HU.js +13 -0
  28. data/vendor/assets/javascripts/pickadate/translations/id_ID.js +13 -0
  29. data/vendor/assets/javascripts/pickadate/translations/it_IT.js +13 -0
  30. data/vendor/assets/javascripts/pickadate/translations/nl_NL.js +13 -0
  31. data/vendor/assets/javascripts/pickadate/translations/no_NO.js +13 -0
  32. data/vendor/assets/javascripts/pickadate/translations/pl_PL.js +13 -0
  33. data/vendor/assets/javascripts/pickadate/translations/pt_BR.js +12 -0
  34. data/vendor/assets/javascripts/pickadate/translations/pt_PT.js +12 -0
  35. data/vendor/assets/javascripts/pickadate/translations/ro_RO.js +13 -0
  36. data/vendor/assets/javascripts/pickadate/translations/ru_RU.js +13 -0
  37. data/vendor/assets/javascripts/pickadate/translations/sk_SK.js +13 -0
  38. data/vendor/assets/javascripts/pickadate/translations/sv_SE.js +13 -0
  39. data/vendor/assets/javascripts/pickadate/translations/th_TH.js +12 -0
  40. data/vendor/assets/javascripts/pickadate/translations/tr_TR.js +13 -0
  41. data/vendor/assets/javascripts/pickadate/translations/uk_UA.js +13 -0
  42. data/vendor/assets/javascripts/pickadate/translations/zh_CN.js +13 -0
  43. data/vendor/assets/stylesheets/pickadate/classic.css +159 -0
  44. data/vendor/assets/stylesheets/pickadate/classic.date.css +332 -0
  45. data/vendor/assets/stylesheets/pickadate/classic.time.css +198 -0
  46. data/vendor/assets/stylesheets/pickadate/default.css +234 -0
  47. data/vendor/assets/stylesheets/pickadate/default.date.css +332 -0
  48. data/vendor/assets/stylesheets/pickadate/default.time.css +193 -0
  49. metadata +134 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c6b81367f90f3d6dee7f72cf6d8a6e9a006fa352
4
+ data.tar.gz: 1629cb671ef09e5e28c562796bcc9b03eaef468d
5
+ SHA512:
6
+ metadata.gz: a107b42a2766e930a491fa7c8a195543ee59a31652b62d5c338f224abc8751d454bf4dec328c28580f7e29ad799c80e146b97a9ea532a7ac8bd952efd723aa83
7
+ data.tar.gz: 8f20da9035b8a2891d76ba1cae06c97ed66f675f66befb45b7f6bc62ac7332dc328869c561f6e9778deaa6fd7a199da3609b9584b4a86e2400ce7581ee87530d
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pickadate-rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jeff Fraser
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Pickadate-Rails
2
+
3
+ ## Pickadate Version: 3.0.3
4
+
5
+ Easily add [pickadate.js](https://github.com/amsul/pickadate.js) to your Rails 3.1+ application using the asset pipeline.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'pickadate-rails'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ ## Usage
18
+
19
+ Pickadate has a number of files associated with it. They are documented on the [pickadate repository](https://github.com/amsul/pickadate.js). Files in this gem are namespaced into a `pickadate` folder, and otherwise match the filenames from the pickadate repo.
20
+
21
+ ### Add the desired files to your javascript/coffeescript files (often `application.js` or `application.js.coffee`):
22
+
23
+ //= require pickadate/picker # required
24
+ //= require pickadate/picker.date # for the date picker
25
+ //= require pickadate/picker.time # for the time picker
26
+
27
+ ### Add the stylesheets for the theme you want to your stylesheet file (often `application.css`)
28
+
29
+ For the default theme:
30
+
31
+ *= require pickadate/default
32
+ *= require pickadate/default.date
33
+ *= require pickadate/default.time
34
+
35
+ For the classic theme:
36
+
37
+ *= require pickadate/classic
38
+ *= require pickadate/classic.date
39
+ *= require pickadate/classic.time
40
+
41
+ ### Localizations
42
+
43
+ Translations are available by loading them in your javascript/coffeescript file. For example, in `application.js`
44
+
45
+ //= require pickadate/translations/bg_BG
46
+
47
+ ## Versioning
48
+
49
+ This project will use Semantic Versioning and follow Pickadate itself as much as possible in terms of major, minor, and patch level bumps. Version 1.0 of this gem starts with Pickadate version 3.0.3.
50
+
51
+
52
+ ## Contributing
53
+
54
+ 1. Fork it
55
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
56
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
57
+ 4. Push to the branch (`git push origin my-new-feature`)
58
+ 5. Create new Pull Request
59
+
60
+ ## License
61
+ Pickadate itself and this gem are under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ namespace :pickadate do
4
+ desc "Download latest assets"
5
+ task :download do
6
+ require 'fileutils'
7
+
8
+ system "curl https://github.com/amsul/pickadate.js/archive/gh-pages.zip -f -L -o tmp/pickadate.zip"
9
+ system "unzip tmp/pickadate.zip -d tmp/"
10
+ system "rm tmp/pickadate.zip"
11
+
12
+ Dir["tmp/pickadate*/lib/picker*.js"].each do |file|
13
+ FileUtils.cp file, "vendor/assets/javascripts/pickadate/#{File.basename(file)}", preserve: false
14
+ end
15
+
16
+ Dir["tmp/pickadate*/lib/translations/*.js"].each do |file|
17
+ FileUtils.cp file, "vendor/assets/javascripts/pickadate/translations/#{File.basename(file)}", preserve: false
18
+ end
19
+
20
+ Dir["tmp/pickadate*/lib/themes/*"].each do |file|
21
+ FileUtils.cp file, "vendor/assets/stylesheets/pickadate/#{File.basename(file)}", preserve: false
22
+ end
23
+
24
+ system "rm -rf tmp/pickadate*"
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ require 'pickadate-rails/version'
2
+ require 'rails'
3
+
4
+ module PickadateRails
5
+ class Engine < Rails::Engine
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module PickadateRails
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pickadate-rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pickadate-rails"
8
+ spec.version = PickadateRails::VERSION
9
+ spec.authors = ["Jeff Fraser"]
10
+ spec.email = ["jfraser@breuer.com"]
11
+ spec.description = "Add pickadate.js to Rails 3.1+ via the asset pipeline"
12
+ spec.summary = "Add pickadate.js to Rails 3.1+ via the asset pipeline. See http://amsul.ca/pickadate.js/ for more information about pickadate.js."
13
+ spec.homepage = "https://github.com/veracross/pickadate-rails"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency('railties', '>= 3.1.0')
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ end
@@ -0,0 +1,925 @@
1
+
2
+ /*!
3
+ * Date picker for pickadate.js v3.0.3
4
+ * http://amsul.github.io/pickadate.js/date.htm
5
+ */
6
+
7
+ /*jshint
8
+ debug: true,
9
+ devel: true,
10
+ browser: true,
11
+ asi: true,
12
+ unused: true,
13
+ boss: true
14
+ */
15
+
16
+
17
+ // Create a new scope.
18
+ (function() {
19
+
20
+
21
+ /**
22
+ * Globals and constants
23
+ */
24
+ var DAYS_IN_WEEK = 7,
25
+ WEEKS_IN_CALENDAR = 6
26
+
27
+
28
+
29
+ /**
30
+ * The date picker constructor
31
+ */
32
+ function DatePicker( picker, settings ) {
33
+
34
+ var calendar = this,
35
+ elementDataValue = picker.$node.data( 'value' )
36
+
37
+ calendar.settings = settings
38
+
39
+ // The queue of methods that will be used to build item objects.
40
+ calendar.queue = {
41
+ min: 'measure create',
42
+ max: 'measure create',
43
+ now: 'now create',
44
+ select: 'parse create validate',
45
+ highlight: 'navigate create validate',
46
+ view: 'create validate viewset',
47
+ disable: 'flipItem',
48
+ enable: 'flipItem'
49
+ }
50
+
51
+ // The component's item object.
52
+ calendar.item = {}
53
+
54
+ calendar.item.disable = ( settings.disable || [] ).slice( 0 )
55
+ calendar.item.enable = -(function( collectionDisabled ) {
56
+ return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1
57
+ })( calendar.item.disable )
58
+
59
+ calendar.
60
+ set( 'min', settings.min ).
61
+ set( 'max', settings.max ).
62
+ set( 'now' ).
63
+
64
+ // Setting the `select` also sets the `highlight` and `view`.
65
+ set( 'select',
66
+
67
+ // If there's a `value` or `data-value`, use that with formatting.
68
+ // Otherwise default to selecting “today”.
69
+ elementDataValue || picker.$node[ 0 ].value || calendar.item.now,
70
+
71
+ // Use the relevant format and data property.
72
+ { format: elementDataValue ? settings.formatSubmit : settings.format, data: !!elementDataValue }
73
+ )
74
+
75
+
76
+ // The keycode to movement mapping.
77
+ calendar.key = {
78
+ 40: 7, // Down
79
+ 38: -7, // Up
80
+ 39: 1, // Right
81
+ 37: -1, // Left
82
+ go: function( timeChange ) {
83
+ calendar.set( 'highlight', [ calendar.item.highlight.year, calendar.item.highlight.month, calendar.item.highlight.date + timeChange ], { interval: timeChange } )
84
+ this.render()
85
+ }
86
+ }
87
+
88
+
89
+ // Bind some picker events.
90
+ picker.
91
+ on( 'render', function() {
92
+ picker.$root.find( '.' + settings.klass.selectMonth ).on( 'change', function() {
93
+ picker.set( 'highlight', [ picker.get( 'view' ).year, this.value, picker.get( 'highlight' ).date ] )
94
+ picker.$root.find( '.' + settings.klass.selectMonth ).focus()
95
+ })
96
+ picker.$root.find( '.' + settings.klass.selectYear ).on( 'change', function() {
97
+ picker.set( 'highlight', [ this.value, picker.get( 'view' ).month, picker.get( 'highlight' ).date ] )
98
+ picker.$root.find( '.' + settings.klass.selectYear ).focus()
99
+ })
100
+ }).
101
+ on( 'open', function() {
102
+ picker.$root.find( 'button, select' ).attr( 'disabled', false )
103
+ }).
104
+ on( 'close', function() {
105
+ picker.$root.find( 'button, select' ).attr( 'disabled', true )
106
+ })
107
+
108
+ } //DatePicker
109
+
110
+
111
+ /**
112
+ * Set a datepicker item object.
113
+ */
114
+ DatePicker.prototype.set = function( type, value, options ) {
115
+
116
+ var calendar = this
117
+
118
+ // Go through the queue of methods, and invoke the function. Update this
119
+ // as the time unit, and set the final resultant as this item type.
120
+ // * In the case of `enable`, keep the queue but set `disable` instead.
121
+ // And in the case of `flip`, keep the queue but set `enable` instead.
122
+ calendar.item[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = calendar.queue[ type ].split( ' ' ).map( function( method ) {
123
+ return value = calendar[ method ]( type, value, options )
124
+ }).pop()
125
+
126
+ // Check if we need to cascade through more updates.
127
+ if ( type == 'select' ) {
128
+ calendar.set( 'highlight', calendar.item.select, options )
129
+ }
130
+ else if ( type == 'highlight' ) {
131
+ calendar.set( 'view', calendar.item.highlight, options )
132
+ }
133
+ else if ( ( type == 'flip' || type == 'min' || type == 'max' || type == 'disable' || type == 'enable' ) && calendar.item.select && calendar.item.highlight ) {
134
+ calendar.
135
+ set( 'select', calendar.item.select, options ).
136
+ set( 'highlight', calendar.item.highlight, options )
137
+ }
138
+
139
+ return calendar
140
+ } //DatePicker.prototype.set
141
+
142
+
143
+ /**
144
+ * Get a datepicker item object.
145
+ */
146
+ DatePicker.prototype.get = function( type ) {
147
+ return this.item[ type ]
148
+ } //DatePicker.prototype.get
149
+
150
+
151
+ /**
152
+ * Create a picker date object.
153
+ */
154
+ DatePicker.prototype.create = function( type, value, options ) {
155
+
156
+ var isInfiniteValue,
157
+ calendar = this
158
+
159
+ // If there's no value, use the type as the value.
160
+ value = value === undefined ? type : value
161
+
162
+
163
+ // If it's infinity, update the value.
164
+ if ( value == -Infinity || value == Infinity ) {
165
+ isInfiniteValue = value
166
+ }
167
+
168
+ // If it's an object, use the “time” value.
169
+ else if ( Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) {
170
+ value = value.obj
171
+ }
172
+
173
+ // If it's an array, convert it into a date.
174
+ else if ( Array.isArray( value ) ) {
175
+ value = new Date( value[ 0 ], value[ 1 ], value[ 2 ] )
176
+ }
177
+
178
+ // If it's a number or date object, make a normalized date.
179
+ else if ( Picker._.isInteger( value ) || Picker._.isDate( value ) ) {
180
+ value = calendar.normalize( new Date( value ), options )
181
+ }
182
+
183
+ // If it's a literal true or any other case, set it to now.
184
+ else /*if ( value === true )*/ {
185
+ value = calendar.now( type, value, options )
186
+ }
187
+
188
+ // Return the compiled object.
189
+ return {
190
+ year: isInfiniteValue || value.getFullYear(),
191
+ month: isInfiniteValue || value.getMonth(),
192
+ date: isInfiniteValue || value.getDate(),
193
+ day: isInfiniteValue || value.getDay(),
194
+ obj: isInfiniteValue || value,
195
+ pick: isInfiniteValue || value.getTime()
196
+ }
197
+ } //DatePicker.prototype.create
198
+
199
+
200
+ /**
201
+ * Get the date today.
202
+ */
203
+ DatePicker.prototype.now = function( type, value, options ) {
204
+ value = new Date()
205
+ if ( options && options.rel ) {
206
+ value.setDate( value.getDate() + options.rel )
207
+ }
208
+ return this.normalize( value, options )
209
+ } //DatePicker.prototype.now
210
+
211
+
212
+ /**
213
+ * Navigate to next/prev month.
214
+ */
215
+ DatePicker.prototype.navigate = function( type, value, options ) {
216
+
217
+ if ( Picker._.isObject( value ) ) {
218
+
219
+ var targetDateObject = new Date( value.year, value.month + ( options && options.nav ? options.nav : 0 ), 1 ),
220
+ year = targetDateObject.getFullYear(),
221
+ month = targetDateObject.getMonth(),
222
+ date = value.date
223
+
224
+ // If the month we’re going to doesn’t have enough days,
225
+ // keep decreasing the date until we reach the month’s last date.
226
+ while ( new Date( year, month, date ).getMonth() !== month ) {
227
+ date -= 1
228
+ }
229
+
230
+ value = [ year, month, date ]
231
+ }
232
+
233
+ return value
234
+ } //DatePicker.prototype.navigate
235
+
236
+
237
+ /**
238
+ * Normalize a date by setting the hours to midnight.
239
+ */
240
+ DatePicker.prototype.normalize = function( value/*, options*/ ) {
241
+ value.setHours( 0, 0, 0, 0 )
242
+ return value
243
+ }
244
+
245
+
246
+ /**
247
+ * Measure the range of dates.
248
+ */
249
+ DatePicker.prototype.measure = function( type, value/*, options*/ ) {
250
+
251
+ var calendar = this
252
+
253
+ // If it's anything false-y, remove the limits.
254
+ if ( !value ) {
255
+ value = type == 'min' ? -Infinity : Infinity
256
+ }
257
+
258
+ // If it's an integer, get a date relative to today.
259
+ else if ( Picker._.isInteger( value ) ) {
260
+ value = calendar.now( type, value, { rel: value } )
261
+ }
262
+
263
+ return value
264
+ } ///DatePicker.prototype.measure
265
+
266
+
267
+ /**
268
+ * Create a viewset object based on navigation.
269
+ */
270
+ DatePicker.prototype.viewset = function( type, dateObject/*, options*/ ) {
271
+ return this.create([ dateObject.year, dateObject.month, 1 ])
272
+ }
273
+
274
+
275
+ /**
276
+ * Validate a date as enabled and shift if needed.
277
+ */
278
+ DatePicker.prototype.validate = function( type, dateObject, options ) {
279
+
280
+ var calendar = this,
281
+
282
+ // Keep a reference to the original date.
283
+ originalDateObject = dateObject,
284
+
285
+ // Make sure we have an interval.
286
+ interval = options && options.interval ? options.interval : 1,
287
+
288
+ // Check if we have any enabled dates after/before now.
289
+ hasEnabledBeforeTarget, hasEnabledAfterTarget,
290
+
291
+ // The min & max limits.
292
+ minLimitObject = calendar.item.min,
293
+ maxLimitObject = calendar.item.max,
294
+
295
+ // Check if we’ve reached the limit during shifting.
296
+ reachedMin, reachedMax,
297
+
298
+ // Check if the calendar is flipped and at least one weekday is enabled.
299
+ hasEnabledWeekdays = calendar.item.enable === -1 && calendar.item.disable.filter( function( value ) {
300
+
301
+ // If there’s a date, check where it is relative to the target.
302
+ if ( Array.isArray( value ) ) {
303
+ var dateTime = calendar.create( value ).pick
304
+ if ( dateTime < dateObject.pick ) hasEnabledBeforeTarget = true
305
+ else if ( dateTime > dateObject.pick ) hasEnabledAfterTarget = true
306
+ }
307
+
308
+ // Return only integers for enabled weekdays.
309
+ return Picker._.isInteger( value )
310
+ }).length
311
+
312
+
313
+
314
+ // No need to validate if we’re navigating months or there are no enabled days.
315
+ if (
316
+ ( !( options && options.nav ) && !( !hasEnabledWeekdays && !hasEnabledBeforeTarget && !hasEnabledAfterTarget ) ) ||
317
+ ( dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick )
318
+ ) {
319
+
320
+
321
+ // Flip the direction if there aren’t any enabled weekdays
322
+ // and there are no enabled dates in the direction of the interval.
323
+ if ( !hasEnabledWeekdays && ( ( !hasEnabledAfterTarget && interval > 0 ) || ( !hasEnabledBeforeTarget && interval < 0 ) ) ) {
324
+ interval *= -1
325
+ }
326
+
327
+
328
+ // Keep looping until we reach an enabled date.
329
+ while ( calendar.disabled( dateObject ) ) {
330
+
331
+
332
+ // If we’ve looped into the next/prev month, return to the original date and flatten the interval.
333
+ if ( Math.abs( interval ) > 1 && ( dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month ) ) {
334
+ dateObject = originalDateObject
335
+ interval = Math.abs( interval ) / interval
336
+ }
337
+
338
+
339
+ // If we’ve reached the min/max limit, reverse the direction and flatten the interval.
340
+ if ( dateObject.pick <= minLimitObject.pick ) {
341
+ reachedMin = true
342
+ interval = 1
343
+ }
344
+ else if ( dateObject.pick >= maxLimitObject.pick ) {
345
+ reachedMax = true
346
+ interval = -1
347
+ }
348
+
349
+
350
+ // If we’ve reached both limits, just break out of the loop.
351
+ if ( reachedMin && reachedMax ) {
352
+ break
353
+ }
354
+
355
+
356
+ // Finally, create the shifted date using the interval and keep looping.
357
+ dateObject = calendar.create([ dateObject.year, dateObject.month, dateObject.date + interval ])
358
+ }
359
+
360
+ } //endif
361
+
362
+
363
+ // Return the date object settled on.
364
+ return dateObject
365
+ } //DatePicker.prototype.validate
366
+
367
+
368
+ /**
369
+ * Check if an object is disabled.
370
+ */
371
+ DatePicker.prototype.disabled = function( dateObject ) {
372
+
373
+ var calendar = this,
374
+
375
+ // Filter through the disabled dates to check if this is one.
376
+ isDisabledDate = calendar.item.disable.filter( function( dateToDisable ) {
377
+
378
+ // If the date is a number, match the weekday with 0index and `firstDay` check.
379
+ if ( Picker._.isInteger( dateToDisable ) ) {
380
+ return dateObject.day === ( calendar.settings.firstDay ? dateToDisable : dateToDisable - 1 ) % 7
381
+ }
382
+
383
+ // If it's an array, create the object and match the exact date.
384
+ if ( Array.isArray( dateToDisable ) ) {
385
+ return dateObject.pick === calendar.create( dateToDisable ).pick
386
+ }
387
+ }).length
388
+
389
+
390
+ // It’s disabled beyond the min/max limits. If within the limits, check the
391
+ // calendar “enabled” flag is flipped and respectively flip the condition.
392
+ return dateObject.pick < calendar.item.min.pick ||
393
+ dateObject.pick > calendar.item.max.pick ||
394
+ calendar.item.enable === -1 ? !isDisabledDate : isDisabledDate
395
+ } //DatePicker.prototype.disabled
396
+
397
+
398
+ /**
399
+ * Parse a string into a usable type.
400
+ */
401
+ DatePicker.prototype.parse = function( type, value, options ) {
402
+
403
+ var calendar = this,
404
+ parsingObject = {}
405
+
406
+ if ( !value || Picker._.isInteger( value ) || Array.isArray( value ) || Picker._.isDate( value ) || Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) {
407
+ return value
408
+ }
409
+
410
+ // We need a `.format` to parse the value.
411
+ if ( !( options && options.format ) ) {
412
+ // should probably default to the default format.
413
+ throw "Need a formatting option to parse this.."
414
+ }
415
+
416
+ // Convert the format into an array and then map through it.
417
+ calendar.formats.toArray( options.format ).map( function( label ) {
418
+
419
+ var
420
+ // Grab the formatting label.
421
+ formattingLabel = calendar.formats[ label ],
422
+
423
+ // The format length is from the formatting label function or the
424
+ // label length without the escaping exclamation (!) mark.
425
+ formatLength = formattingLabel ? Picker._.trigger( formattingLabel, calendar, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length
426
+
427
+ // If there's a format label, split the value up to the format length.
428
+ // Then add it to the parsing object with appropriate label.
429
+ if ( formattingLabel ) {
430
+ parsingObject[ label ] = value.substr( 0, formatLength )
431
+ }
432
+
433
+ // Update the value as the substring from format length to end.
434
+ value = value.substr( formatLength )
435
+ })
436
+
437
+ // If it’s parsing a `data-value`, compensate for month 0index.
438
+ return [ parsingObject.yyyy || parsingObject.yy, +( parsingObject.mm || parsingObject.m ) - ( options.data ? 1 : 0 ), parsingObject.dd || parsingObject.d ]
439
+ } //DatePicker.prototype.parse
440
+
441
+
442
+ /**
443
+ * Various formats to display the object in.
444
+ */
445
+ DatePicker.prototype.formats = (function() {
446
+
447
+ // Return the length of the first word in a collection.
448
+ var getWordLengthFromCollection = function( string, collection, dateObject ) {
449
+
450
+ // Grab the first word from the string.
451
+ var word = string.match( /\w+/ )[ 0 ]
452
+
453
+ // If there's no month index, add it to the date object
454
+ if ( !dateObject.mm && !dateObject.m ) {
455
+ dateObject.m = collection.indexOf( word )
456
+ }
457
+
458
+ // Return the length of the word.
459
+ return word.length
460
+ }
461
+
462
+ return {
463
+
464
+ d: function( string, dateObject ) {
465
+
466
+ // If there's string, then get the digits length.
467
+ // Otherwise return the selected date.
468
+ return string ? Picker._.digits( string ) : dateObject.date
469
+ },
470
+ dd: function( string, dateObject ) {
471
+
472
+ // If there's a string, then the length is always 2.
473
+ // Otherwise return the selected date with a leading zero.
474
+ return string ? 2 : Picker._.lead( dateObject.date )
475
+ },
476
+ ddd: function( string, dateObject ) {
477
+
478
+ // If there's a string, then get the length of the first word.
479
+ // Otherwise return the short selected weekday.
480
+ return string ? getFirstWordLength( string ) : this.settings.weekdaysShort[ dateObject.day ]
481
+ },
482
+ dddd: function( string, dateObject ) {
483
+
484
+ // If there's a string, then get the length of the first word.
485
+ // Otherwise return the full selected weekday.
486
+ return string ? getFirstWordLength( string ) : this.settings.weekdaysFull[ dateObject.day ]
487
+ },
488
+ m: function( string, dateObject ) {
489
+
490
+ // If there's a string, then get the length of the digits
491
+ // Otherwise return the selected month with 0index compensation.
492
+ return string ? Picker._.digits( string ) : dateObject.month + 1
493
+ },
494
+ mm: function( string, dateObject ) {
495
+
496
+ // If there's a string, then the length is always 2.
497
+ // Otherwise return the selected month with 0index and leading zero.
498
+ return string ? 2 : Picker._.lead( dateObject.month + 1 )
499
+ },
500
+ mmm: function( string, dateObject ) {
501
+
502
+ var collection = this.settings.monthsShort
503
+
504
+ // If there's a string, get length of the relevant month from the short
505
+ // months collection. Otherwise return the selected month from that collection.
506
+ return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
507
+ },
508
+ mmmm: function( string, dateObject ) {
509
+
510
+ var collection = this.settings.monthsFull
511
+
512
+ // If there's a string, get length of the relevant month from the full
513
+ // months collection. Otherwise return the selected month from that collection.
514
+ return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
515
+ },
516
+ yy: function( string, dateObject ) {
517
+
518
+ // If there's a string, then the length is always 2.
519
+ // Otherwise return the selected year by slicing out the first 2 digits.
520
+ return string ? 2 : ( '' + dateObject.year ).slice( 2 )
521
+ },
522
+ yyyy: function( string, dateObject ) {
523
+
524
+ // If there's a string, then the length is always 4.
525
+ // Otherwise return the selected year.
526
+ return string ? 4 : dateObject.year
527
+ },
528
+
529
+ // Create an array by splitting the formatting string passed.
530
+ toArray: function( formatString ) { return formatString.split( /(d{1,4}|m{1,4}|y{4}|yy|!.)/g ) },
531
+
532
+ // Format an object into a string using the formatting options.
533
+ toString: function ( formatString, itemObject ) {
534
+ var calendar = this
535
+ return calendar.formats.toArray( formatString ).map( function( label ) {
536
+ return Picker._.trigger( calendar.formats[ label ], calendar, [ 0, itemObject ] ) || label.replace( /^!/, '' )
537
+ }).join( '' )
538
+ }
539
+ }
540
+ })() //DatePicker.prototype.formats
541
+
542
+
543
+ /**
544
+ * Flip an item as enabled or disabled.
545
+ */
546
+ DatePicker.prototype.flipItem = function( type, value/*, options*/ ) {
547
+
548
+ var calendar = this,
549
+ collection = calendar.item.disable,
550
+ isFlipped = calendar.item.enable === -1
551
+
552
+ // Flip the enabled and disabled dates.
553
+ if ( value == 'flip' ) {
554
+ calendar.item.enable = isFlipped ? 1 : -1
555
+ }
556
+
557
+ // Check if we have to add/remove from collection.
558
+ else if ( !isFlipped && type == 'enable' || isFlipped && type == 'disable' ) {
559
+ collection = calendar.removeDisabled( collection, value )
560
+ }
561
+ else if ( !isFlipped && type == 'disable' || isFlipped && type == 'enable' ) {
562
+ collection = calendar.addDisabled( collection, value )
563
+ }
564
+
565
+ return collection
566
+ } //DatePicker.prototype.flipItem
567
+
568
+
569
+ /**
570
+ * Add an item to the disabled collection.
571
+ */
572
+ DatePicker.prototype.addDisabled = function( collection, item ) {
573
+ var calendar = this
574
+ item.map( function( timeUnit ) {
575
+ if ( !calendar.filterDisabled( collection, timeUnit ).length ) {
576
+ collection.push( timeUnit )
577
+ }
578
+ })
579
+ return collection
580
+ } //DatePicker.prototype.addDisabled
581
+
582
+
583
+ /**
584
+ * Remove an item from the disabled collection.
585
+ */
586
+ DatePicker.prototype.removeDisabled = function( collection, item ) {
587
+ var calendar = this
588
+ item.map( function( timeUnit ) {
589
+ collection = calendar.filterDisabled( collection, timeUnit, 1 )
590
+ })
591
+ return collection
592
+ } //DatePicker.prototype.removeDisabled
593
+
594
+
595
+ /**
596
+ * Filter through the disabled collection to find a time unit.
597
+ */
598
+ DatePicker.prototype.filterDisabled = function( collection, timeUnit, isRemoving ) {
599
+ var timeIsArray = Array.isArray( timeUnit )
600
+ return collection.filter( function( disabledTimeUnit ) {
601
+ var isMatch = !timeIsArray && timeUnit === disabledTimeUnit ||
602
+ timeIsArray && Array.isArray( disabledTimeUnit ) && timeUnit.toString() === disabledTimeUnit.toString()
603
+ return isRemoving ? !isMatch : isMatch
604
+ })
605
+ } //DatePicker.prototype.filterDisabled
606
+
607
+
608
+ /**
609
+ * Create a string for the nodes in the picker.
610
+ */
611
+ DatePicker.prototype.nodes = function( isOpen ) {
612
+
613
+ var
614
+ calendar = this,
615
+ settings = calendar.settings,
616
+ nowObject = calendar.item.now,
617
+ selectedObject = calendar.item.select,
618
+ highlightedObject = calendar.item.highlight,
619
+ viewsetObject = calendar.item.view,
620
+ disabledCollection = calendar.item.disable,
621
+ minLimitObject = calendar.item.min,
622
+ maxLimitObject = calendar.item.max,
623
+
624
+
625
+ // Create the calendar table head using a copy of weekday labels collection.
626
+ // * We do a copy so we don't mutate the original array.
627
+ tableHead = (function( collection ) {
628
+
629
+ // If the first day should be Monday, move Sunday to the end.
630
+ if ( settings.firstDay ) {
631
+ collection.push( collection.shift() )
632
+ }
633
+
634
+ // Create and return the table head group.
635
+ return Picker._.node(
636
+ 'thead',
637
+ Picker._.group({
638
+ min: 0,
639
+ max: DAYS_IN_WEEK - 1,
640
+ i: 1,
641
+ node: 'th',
642
+ item: function( counter ) {
643
+ return [
644
+ collection[ counter ],
645
+ settings.klass.weekdays
646
+ ]
647
+ }
648
+ })
649
+ ) //endreturn
650
+ })( ( settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysShort ).slice( 0 ) ), //tableHead
651
+
652
+
653
+ // Create the nav for next/prev month.
654
+ createMonthNav = function( next ) {
655
+
656
+ // Otherwise, return the created month tag.
657
+ return Picker._.node(
658
+ 'div',
659
+ ' ',
660
+ settings.klass[ 'nav' + ( next ? 'Next' : 'Prev' ) ] + (
661
+
662
+ // If the focused month is outside the range, disabled the button.
663
+ ( next && viewsetObject.year >= maxLimitObject.year && viewsetObject.month >= maxLimitObject.month ) ||
664
+ ( !next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month ) ?
665
+ ' ' + settings.klass.navDisabled : ''
666
+ ),
667
+ 'data-nav=' + ( next || -1 )
668
+ ) //endreturn
669
+ }, //createMonthNav
670
+
671
+
672
+ // Create the month label
673
+ createMonthLabel = function( monthsCollection ) {
674
+
675
+ // If there are months to select, add a dropdown menu.
676
+ if ( settings.selectMonths ) {
677
+
678
+ return Picker._.node( 'select', Picker._.group({
679
+ min: 0,
680
+ max: 11,
681
+ i: 1,
682
+ node: 'option',
683
+ item: function( loopedMonth ) {
684
+
685
+ return [
686
+
687
+ // The looped month and no classes.
688
+ monthsCollection[ loopedMonth ], 0,
689
+
690
+ // Set the value and selected index.
691
+ 'value=' + loopedMonth +
692
+ ( viewsetObject.month == loopedMonth ? ' selected' : '' ) +
693
+ (
694
+ (
695
+ ( viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month ) ||
696
+ ( viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month )
697
+ ) ?
698
+ ' disabled' : ''
699
+ )
700
+ ]
701
+ }
702
+ }), settings.klass.selectMonth, isOpen ? '' : 'disabled' )
703
+ }
704
+
705
+ // If there's a need for a month selector
706
+ return Picker._.node( 'div', monthsCollection[ viewsetObject.month ], settings.klass.month )
707
+ }, //createMonthLabel
708
+
709
+
710
+ // Create the year label
711
+ createYearLabel = function() {
712
+
713
+ var focusedYear = viewsetObject.year,
714
+
715
+ // If years selector is set to a literal "true", set it to 5. Otherwise
716
+ // divide in half to get half before and half after focused year.
717
+ numberYears = settings.selectYears === true ? 5 : ~~( settings.selectYears / 2 )
718
+
719
+ // If there are years to select, add a dropdown menu.
720
+ if ( numberYears ) {
721
+
722
+ var
723
+ minYear = minLimitObject.year,
724
+ maxYear = maxLimitObject.year,
725
+ lowestYear = focusedYear - numberYears,
726
+ highestYear = focusedYear + numberYears
727
+
728
+ // If the min year is greater than the lowest year, increase the highest year
729
+ // by the difference and set the lowest year to the min year.
730
+ if ( minYear > lowestYear ) {
731
+ highestYear += minYear - lowestYear
732
+ lowestYear = minYear
733
+ }
734
+
735
+ // If the max year is less than the highest year, decrease the lowest year
736
+ // by the lower of the two: available and needed years. Then set the
737
+ // highest year to the max year.
738
+ if ( maxYear < highestYear ) {
739
+
740
+ var availableYears = lowestYear - minYear,
741
+ neededYears = highestYear - maxYear
742
+
743
+ lowestYear -= availableYears > neededYears ? neededYears : availableYears
744
+ highestYear = maxYear
745
+ }
746
+
747
+ return Picker._.node( 'select', Picker._.group({
748
+ min: lowestYear,
749
+ max: highestYear,
750
+ i: 1,
751
+ node: 'option',
752
+ item: function( loopedYear ) {
753
+ return [
754
+
755
+ // The looped year and no classes.
756
+ loopedYear, 0,
757
+
758
+ // Set the value and selected index.
759
+ 'value=' + loopedYear + ( focusedYear == loopedYear ? ' selected' : '' )
760
+ ]
761
+ }
762
+ }), settings.klass.selectYear, isOpen ? '' : 'disabled' )
763
+ }
764
+
765
+ // Otherwise just return the year focused
766
+ return Picker._.node( 'div', focusedYear, settings.klass.year )
767
+ } //createYearLabel
768
+
769
+
770
+ // Create and return the entire calendar.
771
+ return Picker._.node(
772
+ 'div',
773
+ createMonthNav() + createMonthNav( 1 ) +
774
+ createMonthLabel( settings.showMonthsShort ? settings.monthsShort : settings.monthsFull ) +
775
+ createYearLabel(),
776
+ settings.klass.header
777
+ ) + Picker._.node(
778
+ 'table',
779
+ tableHead +
780
+ Picker._.node(
781
+ 'tbody',
782
+ Picker._.group({
783
+ min: 0,
784
+ max: WEEKS_IN_CALENDAR - 1,
785
+ i: 1,
786
+ node: 'tr',
787
+ item: function( rowCounter ) {
788
+
789
+ return [
790
+ Picker._.group({
791
+ min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + 1, // Add 1 for weekday 0index
792
+ max: function() {
793
+ return this.min + DAYS_IN_WEEK - 1
794
+ },
795
+ i: 1,
796
+ node: 'td',
797
+ item: function( timeDate ) {
798
+
799
+ // Convert the time date from a relative date to a date object
800
+ timeDate = calendar.create([ viewsetObject.year, viewsetObject.month, timeDate + ( settings.firstDay ? 1 : 0 ) ])
801
+
802
+ return [
803
+ Picker._.node(
804
+ 'div',
805
+ timeDate.date,
806
+ (function( klasses ) {
807
+
808
+ // Add the `infocus` or `outfocus` classes based on month in view.
809
+ klasses.push( viewsetObject.month == timeDate.month ? settings.klass.infocus : settings.klass.outfocus )
810
+
811
+ // Add the `today` class if needed.
812
+ if ( nowObject.pick == timeDate.pick ) {
813
+ klasses.push( settings.klass.now )
814
+ }
815
+
816
+ // Add the `selected` class if something's selected and the time matches.
817
+ if ( selectedObject && selectedObject.pick == timeDate.pick ) {
818
+ klasses.push( settings.klass.selected )
819
+ }
820
+
821
+ // Add the `highlighted` class if something's highlighted and the time matches.
822
+ if ( highlightedObject && highlightedObject.pick == timeDate.pick ) {
823
+ klasses.push( settings.klass.highlighted )
824
+ }
825
+
826
+ // Add the `disabled` class if something's disabled and the object matches.
827
+ if ( disabledCollection && calendar.disabled( timeDate ) || timeDate.pick < minLimitObject.pick || timeDate.pick > maxLimitObject.pick ) {
828
+ klasses.push( settings.klass.disabled )
829
+ }
830
+
831
+ return klasses.join( ' ' )
832
+ })([ settings.klass.day ]),
833
+ 'data-pick=' + timeDate.pick
834
+ )
835
+ ] //endreturn
836
+ }
837
+ })
838
+ ] //endreturn
839
+ }
840
+ })
841
+ ),
842
+ settings.klass.table
843
+ ) +
844
+
845
+ Picker._.node(
846
+ 'div',
847
+ Picker._.node( 'button', settings.today, settings.klass.buttonToday, 'data-pick=' + nowObject.pick + ( isOpen ? '' : ' disabled' ) ) +
848
+ Picker._.node( 'button', settings.clear, settings.klass.buttonClear, 'data-clear=1' + ( isOpen ? '' : ' disabled' ) ),
849
+ settings.klass.footer
850
+ ) //endreturn
851
+ } //DatePicker.prototype.nodes
852
+
853
+
854
+
855
+
856
+ /**
857
+ * The date picker defaults.
858
+ */
859
+ DatePicker.defaults = (function( prefix ) {
860
+
861
+ return {
862
+
863
+ // Months and weekdays
864
+ monthsFull: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
865
+ monthsShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
866
+ weekdaysFull: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
867
+ weekdaysShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
868
+
869
+ // Today and clear
870
+ today: 'Today',
871
+ clear: 'Clear',
872
+
873
+ // The format to show on the `input` element
874
+ format: 'd mmmm, yyyy',
875
+
876
+ // Classes
877
+ klass: {
878
+
879
+ table: prefix + 'table',
880
+
881
+ header: prefix + 'header',
882
+
883
+ navPrev: prefix + 'nav--prev',
884
+ navNext: prefix + 'nav--next',
885
+ navDisabled: prefix + 'nav--disabled',
886
+
887
+ month: prefix + 'month',
888
+ year: prefix + 'year',
889
+
890
+ selectMonth: prefix + 'select--month',
891
+ selectYear: prefix + 'select--year',
892
+
893
+ weekdays: prefix + 'weekday',
894
+
895
+ day: prefix + 'day',
896
+ disabled: prefix + 'day--disabled',
897
+ selected: prefix + 'day--selected',
898
+ highlighted: prefix + 'day--highlighted',
899
+ now: prefix + 'day--today',
900
+ infocus: prefix + 'day--infocus',
901
+ outfocus: prefix + 'day--outfocus',
902
+
903
+ footer: prefix + 'footer',
904
+
905
+ buttonClear: prefix + 'button--clear',
906
+ buttonToday: prefix + 'button--today'
907
+ }
908
+ }
909
+ })( Picker.klasses().picker + '__' )
910
+
911
+
912
+
913
+
914
+
915
+ /**
916
+ * Extend the picker to add the date picker.
917
+ */
918
+ Picker.extend( 'pickadate', DatePicker )
919
+
920
+
921
+ // Close the scope.
922
+ })();
923
+
924
+
925
+