pickadate-rails 1.3.2 → 1.4.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.
@@ -1,22 +1,13 @@
1
1
 
2
2
  /*!
3
- * Time picker for pickadate.js v3.3.2
3
+ * Time picker for pickadate.js v3.4.0
4
4
  * http://amsul.github.io/pickadate.js/time.htm
5
5
  */
6
6
 
7
- /*jshint
8
- debug: true,
9
- devel: true,
10
- browser: true,
11
- asi: true,
12
- unused: true,
13
- boss: true
14
- */
15
-
16
7
  (function ( factory ) {
17
8
 
18
9
  // Register as an anonymous module.
19
- if ( typeof define === 'function' && define.amd )
10
+ if ( typeof define == 'function' && define.amd )
20
11
  define( ['picker','jquery'], factory )
21
12
 
22
13
  // Or using browser globals.
@@ -31,7 +22,8 @@
31
22
  var HOURS_IN_DAY = 24,
32
23
  MINUTES_IN_HOUR = 60,
33
24
  HOURS_TO_NOON = 12,
34
- MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR
25
+ MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR,
26
+ _ = Picker._
35
27
 
36
28
 
37
29
 
@@ -41,9 +33,13 @@ var HOURS_IN_DAY = 24,
41
33
  function TimePicker( picker, settings ) {
42
34
 
43
35
  var clock = this,
44
- elementDataValue = picker.$node.data( 'value' )
36
+ elementValue = picker.$node[ 0 ].value,
37
+ elementDataValue = picker.$node.data( 'value' ),
38
+ valueString = elementDataValue || elementValue,
39
+ formatString = elementDataValue ? settings.formatSubmit : settings.format
45
40
 
46
41
  clock.settings = settings
42
+ clock.$node = picker.$node
47
43
 
48
44
  // The queue of methods that will be used to build item objects.
49
45
  clock.queue = {
@@ -52,10 +48,10 @@ function TimePicker( picker, settings ) {
52
48
  max: 'measure create',
53
49
  now: 'now create',
54
50
  select: 'parse create validate',
55
- highlight: 'create validate',
56
- view: 'create validate',
57
- disable: 'flipItem',
58
- enable: 'flipItem'
51
+ highlight: 'parse create validate',
52
+ view: 'parse create validate',
53
+ disable: 'deactivate',
54
+ enable: 'activate'
59
55
  }
60
56
 
61
57
  // The component's item object.
@@ -70,18 +66,23 @@ function TimePicker( picker, settings ) {
70
66
  clock.
71
67
  set( 'min', settings.min ).
72
68
  set( 'max', settings.max ).
73
- set( 'now' ).
74
-
75
- // Setting the `select` also sets the `highlight` and `view`.
76
- set( 'select',
77
-
78
- // If there's a `value` or `data-value`, use that with formatting.
79
- // Otherwise default to the minimum selectable time.
80
- elementDataValue || picker.$node[ 0 ].value || clock.item.min,
69
+ set( 'now' )
70
+
71
+ // When there’s a value, set the `select`, which in turn
72
+ // also sets the `highlight` and `view`.
73
+ if ( valueString ) {
74
+ clock.set( 'select', valueString, {
75
+ format: formatString,
76
+ fromValue: !!elementValue
77
+ })
78
+ }
81
79
 
82
- // Use the relevant format.
83
- { format: elementDataValue ? settings.formatSubmit : settings.format }
84
- )
80
+ // If there’s no value, default to highlighting “today”.
81
+ else {
82
+ clock.
83
+ set( 'select', null ).
84
+ set( 'highlight', clock.item.now )
85
+ }
85
86
 
86
87
  // The keycode to movement mapping.
87
88
  clock.key = {
@@ -90,7 +91,11 @@ function TimePicker( picker, settings ) {
90
91
  39: 1, // Right
91
92
  37: -1, // Left
92
93
  go: function( timeChange ) {
93
- clock.set( 'highlight', clock.item.highlight.pick + timeChange * clock.item.interval, { interval: timeChange * clock.item.interval } )
94
+ clock.set(
95
+ 'highlight',
96
+ clock.item.highlight.pick + timeChange * clock.item.interval,
97
+ { interval: timeChange * clock.item.interval }
98
+ )
94
99
  this.render()
95
100
  }
96
101
  }
@@ -120,35 +125,46 @@ function TimePicker( picker, settings ) {
120
125
  */
121
126
  TimePicker.prototype.set = function( type, value, options ) {
122
127
 
123
- var clock = this
128
+ var clock = this,
129
+ clockItem = clock.item
130
+
131
+ // If the value is `null` just set it immediately.
132
+ if ( value === null ) {
133
+ clockItem[ type ] = value
134
+ return clock
135
+ }
124
136
 
125
- // Go through the queue of methods, and invoke the function. Update this
126
- // as the time unit, and set the final resultant as this item type.
137
+ // Otherwise go through the queue of methods, and invoke the functions.
138
+ // Update this as the time unit, and set the final value as this item.
127
139
  // * In the case of `enable`, keep the queue but set `disable` instead.
128
140
  // And in the case of `flip`, keep the queue but set `enable` instead.
129
- clock.item[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = clock.queue[ type ].split( ' ' ).map( function( method ) {
130
- return value = clock[ method ]( type, value, options )
141
+ clockItem[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = clock.queue[ type ].split( ' ' ).map( function( method ) {
142
+ value = clock[ method ]( type, value, options )
143
+ return value
131
144
  }).pop()
132
145
 
133
146
  // Check if we need to cascade through more updates.
134
147
  if ( type == 'select' ) {
135
- clock.set( 'highlight', clock.item.select, options )
148
+ clock.set( 'highlight', clockItem.select, options )
136
149
  }
137
150
  else if ( type == 'highlight' ) {
138
- clock.set( 'view', clock.item.highlight, options )
151
+ clock.set( 'view', clockItem.highlight, options )
139
152
  }
140
153
  else if ( type == 'interval' ) {
141
154
  clock.
142
- set( 'min', clock.item.min, options ).
143
- set( 'max', clock.item.max, options )
155
+ set( 'min', clockItem.min, options ).
156
+ set( 'max', clockItem.max, options )
144
157
  }
145
- else if ( ( type == 'flip' || type == 'min' || type == 'max' || type == 'disable' || type == 'enable' ) && clock.item.select && clock.item.highlight ) {
158
+ else if ( type.match( /^(flip|min|max|disable|enable)$/ ) ) {
146
159
  if ( type == 'min' ) {
147
- clock.set( 'max', clock.item.max, options )
160
+ clock.set( 'max', clockItem.max, options )
161
+ }
162
+ if ( clockItem.select && clock.disabled( clockItem.select ) ) {
163
+ clock.set( 'select', clockItem.select, options )
164
+ }
165
+ if ( clockItem.highlight && clock.disabled( clockItem.highlight ) ) {
166
+ clock.set( 'highlight', clockItem.highlight, options )
148
167
  }
149
- clock.
150
- set( 'select', clock.item.select, options ).
151
- set( 'highlight', clock.item.highlight, options )
152
168
  }
153
169
 
154
170
  return clock
@@ -174,12 +190,12 @@ TimePicker.prototype.create = function( type, value, options ) {
174
190
  value = value === undefined ? type : value
175
191
 
176
192
  // If it’s a date object, convert it into an array.
177
- if ( Picker._.isDate( value ) ) {
193
+ if ( _.isDate( value ) ) {
178
194
  value = [ value.getHours(), value.getMinutes() ]
179
195
  }
180
196
 
181
197
  // If it’s an object, use the “pick” value.
182
- if ( $.isPlainObject( value ) && Picker._.isInteger( value.pick ) ) {
198
+ if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) {
183
199
  value = value.pick
184
200
  }
185
201
 
@@ -189,7 +205,7 @@ TimePicker.prototype.create = function( type, value, options ) {
189
205
  }
190
206
 
191
207
  // If no valid value is passed, set it to “now”.
192
- else if ( !Picker._.isInteger( value ) ) {
208
+ else if ( !_.isInteger( value ) ) {
193
209
  value = clock.now( type, value, options )
194
210
  }
195
211
 
@@ -225,31 +241,99 @@ TimePicker.prototype.create = function( type, value, options ) {
225
241
  } //TimePicker.prototype.create
226
242
 
227
243
 
244
+ /**
245
+ * Create a range limit object using an array, date object,
246
+ * literal “true”, or integer relative to another time.
247
+ */
248
+ TimePicker.prototype.createRange = function( from, to ) {
249
+
250
+ var clock = this,
251
+ createTime = function( time ) {
252
+ if ( time === true || $.isArray( time ) || _.isDate( time ) ) {
253
+ return clock.create( time )
254
+ }
255
+ return time
256
+ }
257
+
258
+ // Create objects if possible.
259
+ if ( !_.isInteger( from ) ) {
260
+ from = createTime( from )
261
+ }
262
+ if ( !_.isInteger( to ) ) {
263
+ to = createTime( to )
264
+ }
265
+
266
+ // Create relative times.
267
+ if ( _.isInteger( from ) && $.isPlainObject( to ) ) {
268
+ from = [ to.hour, to.mins + ( from * clock.settings.interval ) ];
269
+ }
270
+ else if ( _.isInteger( to ) && $.isPlainObject( from ) ) {
271
+ to = [ from.hour, from.mins + ( to * clock.settings.interval ) ];
272
+ }
273
+
274
+ return {
275
+ from: createTime( from ),
276
+ to: createTime( to )
277
+ }
278
+ } //TimePicker.prototype.createRange
279
+
280
+
281
+ /**
282
+ * Check if a time unit falls within a time range object.
283
+ */
284
+ TimePicker.prototype.withinRange = function( range, timeUnit ) {
285
+ range = this.createRange(range.from, range.to)
286
+ return timeUnit.pick >= range.from.pick && timeUnit.pick <= range.to.pick
287
+ }
288
+
289
+
290
+ /**
291
+ * Check if two time range objects overlap.
292
+ */
293
+ TimePicker.prototype.overlapRanges = function( one, two ) {
294
+
295
+ var clock = this
296
+
297
+ // Convert the ranges into comparable times.
298
+ one = clock.createRange( one.from, one.to )
299
+ two = clock.createRange( two.from, two.to )
300
+
301
+ return clock.withinRange( one, two.from ) || clock.withinRange( one, two.to ) ||
302
+ clock.withinRange( two, one.from ) || clock.withinRange( two, one.to )
303
+ }
304
+
305
+
228
306
  /**
229
307
  * Get the time relative to now.
230
308
  */
231
309
  TimePicker.prototype.now = function( type, value/*, options*/ ) {
232
310
 
233
- var date = new Date(),
234
- dateMinutes = date.getHours() * MINUTES_IN_HOUR + date.getMinutes()
311
+ var interval = this.item.interval,
312
+ date = new Date(),
313
+ nowMinutes = date.getHours() * MINUTES_IN_HOUR + date.getMinutes(),
314
+ isValueInteger = _.isInteger( value ),
315
+ isBelowInterval
235
316
 
236
317
  // Make sure “now” falls within the interval range.
237
- dateMinutes -= dateMinutes % this.item.interval
318
+ nowMinutes -= nowMinutes % interval
238
319
 
239
- // If the value is a number, adjust by that many intervals because
240
- // the time has passed. In the case of “midnight” and a negative `min`,
241
- // increase the value by 2. Otherwise increase it by 1.
242
- if ( Picker._.isInteger( value ) ) {
243
- value += type == 'min' && value < 0 && dateMinutes === 0 ? 2 : 1
244
- }
320
+ // Check if the difference is less than the interval itself.
321
+ isBelowInterval = value < 0 && interval * value + nowMinutes <= -interval
245
322
 
246
- // If the value isn’t a number, default to 1 passed interval.
247
- else {
248
- value = 1
323
+ // Add an interval because the time has passed”.
324
+ nowMinutes += type == 'min' && isBelowInterval ? 0 : interval
325
+
326
+ // If the value is a number, adjust by that many intervals.
327
+ if ( isValueInteger ) {
328
+ nowMinutes += interval * (
329
+ isBelowInterval && type != 'max' ?
330
+ value + 1 :
331
+ value
332
+ )
249
333
  }
250
334
 
251
- // Calculate the final relative time.
252
- return value * this.item.interval + dateMinutes
335
+ // Return the final calculation.
336
+ return nowMinutes
253
337
  } //TimePicker.prototype.now
254
338
 
255
339
 
@@ -259,14 +343,15 @@ TimePicker.prototype.now = function( type, value/*, options*/ ) {
259
343
  TimePicker.prototype.normalize = function( type, value/*, options*/ ) {
260
344
 
261
345
  var interval = this.item.interval,
346
+ minTime = this.item.min && this.item.min.pick || 0
262
347
 
263
- // If setting min time, don’t shift anything.
264
- // Otherwise get the value and min difference and then
265
- // normalize the difference with the interval.
266
- difference = type == 'min' ? 0 : ( value - this.item.min.pick ) % interval
348
+ // If setting min time, don’t shift anything.
349
+ // Otherwise get the value and min difference and then
350
+ // normalize the difference with the interval.
351
+ value -= type == 'min' ? 0 : ( value - minTime ) % interval
267
352
 
268
- // If it’s a negative value, add one interval to keep it as “passed”.
269
- return value - ( difference + ( value < 0 ? interval : 0 ) )
353
+ // Return the adjusted value.
354
+ return value
270
355
  } //TimePicker.prototype.normalize
271
356
 
272
357
 
@@ -283,12 +368,12 @@ TimePicker.prototype.measure = function( type, value, options ) {
283
368
  }
284
369
 
285
370
  // If it’s a literal true, or an integer, make it relative to now.
286
- else if ( value === true || Picker._.isInteger( value ) ) {
371
+ else if ( value === true || _.isInteger( value ) ) {
287
372
  value = clock.now( type, value, options )
288
373
  }
289
374
 
290
375
  // If it’s an object already, just normalize it.
291
- else if ( $.isPlainObject( value ) && Picker._.isInteger( value.pick ) ) {
376
+ else if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) {
292
377
  value = clock.normalize( type, value.pick, options )
293
378
  }
294
379
 
@@ -328,34 +413,39 @@ TimePicker.prototype.validate = function( type, timeObject, options ) {
328
413
  /**
329
414
  * Check if an object is disabled.
330
415
  */
331
- TimePicker.prototype.disabled = function( timeObject ) {
416
+ TimePicker.prototype.disabled = function( timeToVerify ) {
332
417
 
333
- var
334
- clock = this,
418
+ var clock = this,
335
419
 
336
420
  // Filter through the disabled times to check if this is one.
337
421
  isDisabledMatch = clock.item.disable.filter( function( timeToDisable ) {
338
422
 
339
423
  // If the time is a number, match the hours.
340
- if ( Picker._.isInteger( timeToDisable ) ) {
341
- return timeObject.hour == timeToDisable
424
+ if ( _.isInteger( timeToDisable ) ) {
425
+ return timeToVerify.hour == timeToDisable
342
426
  }
343
427
 
344
428
  // If it’s an array, create the object and match the times.
345
- if ( $.isArray( timeToDisable ) || Picker._.isDate( timeToDisable ) ) {
346
- return timeObject.pick == clock.create( timeToDisable ).pick
429
+ if ( $.isArray( timeToDisable ) || _.isDate( timeToDisable ) ) {
430
+ return timeToVerify.pick == clock.create( timeToDisable ).pick
431
+ }
432
+
433
+ // If it’s an object, match a time within the “from” and “to” range.
434
+ if ( $.isPlainObject( timeToDisable ) ) {
435
+ return clock.withinRange( timeToDisable, timeToVerify )
347
436
  }
348
437
  })
349
438
 
350
439
  // If this time matches a disabled time, confirm it’s not inverted.
351
440
  isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( timeToDisable ) {
352
- return $.isArray( timeToDisable ) && timeToDisable[2] == 'inverted'
441
+ return $.isArray( timeToDisable ) && timeToDisable[2] == 'inverted' ||
442
+ $.isPlainObject( timeToDisable ) && timeToDisable.inverted
353
443
  }).length
354
444
 
355
445
  // If the clock is "enabled" flag is flipped, flip the condition.
356
446
  return clock.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
357
- timeObject.pick < clock.item.min.pick ||
358
- timeObject.pick > clock.item.max.pick
447
+ timeToVerify.pick < clock.item.min.pick ||
448
+ timeToVerify.pick > clock.item.max.pick
359
449
  } //TimePicker.prototype.disabled
360
450
 
361
451
 
@@ -366,12 +456,18 @@ TimePicker.prototype.shift = function( timeObject, interval ) {
366
456
 
367
457
  var clock = this,
368
458
  minLimit = clock.item.min.pick,
369
- maxLimit = clock.item.max.pick
459
+ maxLimit = clock.item.max.pick/*,
460
+ safety = 1000*/
370
461
 
371
462
  interval = interval || clock.item.interval
372
463
 
373
464
  // Keep looping as long as the time is disabled.
374
- while ( clock.disabled( timeObject ) ) {
465
+ while ( /*safety &&*/ clock.disabled( timeObject ) ) {
466
+
467
+ /*safety -= 1
468
+ if ( !safety ) {
469
+ throw 'Fell into an infinite loop while shifting to ' + timeObject.hour + ':' + timeObject.mins + '.'
470
+ }*/
375
471
 
376
472
  // Increase/decrease the time by the interval and keep looping.
377
473
  timeObject = clock.create( timeObject.pick += interval )
@@ -402,45 +498,67 @@ TimePicker.prototype.scope = function( timeObject ) {
402
498
  */
403
499
  TimePicker.prototype.parse = function( type, value, options ) {
404
500
 
405
- var clock = this,
501
+ var hour, minutes, isPM, item, parseValue,
502
+ clock = this,
406
503
  parsingObject = {}
407
504
 
408
- if ( !value || Picker._.isInteger( value ) || $.isArray( value ) || Picker._.isDate( value ) || $.isPlainObject( value ) && Picker._.isInteger( value.pick ) ) {
505
+ if ( !value || _.isInteger( value ) || $.isArray( value ) || _.isDate( value ) || $.isPlainObject( value ) && _.isInteger( value.pick ) ) {
409
506
  return value
410
507
  }
411
508
 
412
- // We need a `.format` to parse the value.
509
+ // We need a `.format` to parse the value with.
413
510
  if ( !( options && options.format ) ) {
414
- throw "Need a formatting option to parse this.."
511
+ options = options || {}
512
+ options.format = clock.settings.format
415
513
  }
416
514
 
417
515
  // Convert the format into an array and then map through it.
418
516
  clock.formats.toArray( options.format ).map( function( label ) {
419
517
 
420
518
  var
519
+ substring,
520
+
421
521
  // Grab the formatting label.
422
522
  formattingLabel = clock.formats[ label ],
423
523
 
424
524
  // The format length is from the formatting label function or the
425
525
  // label length without the escaping exclamation (!) mark.
426
- formatLength = formattingLabel ? Picker._.trigger( formattingLabel, clock, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length
526
+ formatLength = formattingLabel ?
527
+ _.trigger( formattingLabel, clock, [ value, parsingObject ] ) :
528
+ label.replace( /^!/, '' ).length
427
529
 
428
530
  // If there's a format label, split the value up to the format length.
429
531
  // Then add it to the parsing object with appropriate label.
430
532
  if ( formattingLabel ) {
431
- parsingObject[ label ] = value.substr( 0, formatLength )
533
+ substring = value.substr( 0, formatLength )
534
+ parsingObject[ label ] = substring.match(/^\d+$/) ? +substring : substring
432
535
  }
433
536
 
434
537
  // Update the time value as the substring from format length to end.
435
538
  value = value.substr( formatLength )
436
539
  })
437
540
 
438
- return +parsingObject.i + MINUTES_IN_HOUR * (
439
-
440
- +( parsingObject.H || parsingObject.HH ) ||
541
+ // Grab the hour and minutes from the parsing object.
542
+ for ( item in parsingObject ) {
543
+ parseValue = parsingObject[item]
544
+ if ( _.isInteger(parseValue) ) {
545
+ if ( item.match(/^(h|hh)$/i) ) {
546
+ hour = parseValue
547
+ if ( item == 'h' || item == 'hh' ) {
548
+ hour %= 12
549
+ }
550
+ }
551
+ else if ( item == 'i' ) {
552
+ minutes = parseValue
553
+ }
554
+ }
555
+ else if ( item.match(/^a$/i) && parseValue.match(/^p/i) && ('h' in parsingObject || 'hh' in parsingObject) ) {
556
+ isPM = true
557
+ }
558
+ }
441
559
 
442
- ( +( parsingObject.h || parsingObject.hh ) % 12 + ( /^p/i.test( parsingObject.A || parsingObject.a ) ? 12 : 0 ) )
443
- )
560
+ // Calculate it in minutes and return.
561
+ return (isPM ? hour + 12 : hour) * MINUTES_IN_HOUR + minutes
444
562
  } //TimePicker.prototype.parse
445
563
 
446
564
 
@@ -453,31 +571,31 @@ TimePicker.prototype.formats = {
453
571
 
454
572
  // If there's string, then get the digits length.
455
573
  // Otherwise return the selected hour in "standard" format.
456
- return string ? Picker._.digits( string ) : timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON
574
+ return string ? _.digits( string ) : timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON
457
575
  },
458
576
  hh: function( string, timeObject ) {
459
577
 
460
578
  // If there's a string, then the length is always 2.
461
579
  // Otherwise return the selected hour in "standard" format with a leading zero.
462
- return string ? 2 : Picker._.lead( timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON )
580
+ return string ? 2 : _.lead( timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON )
463
581
  },
464
582
  H: function( string, timeObject ) {
465
583
 
466
584
  // If there's string, then get the digits length.
467
585
  // Otherwise return the selected hour in "military" format as a string.
468
- return string ? Picker._.digits( string ) : '' + ( timeObject.hour % 24 )
586
+ return string ? _.digits( string ) : '' + ( timeObject.hour % 24 )
469
587
  },
470
588
  HH: function( string, timeObject ) {
471
589
 
472
590
  // If there's string, then get the digits length.
473
591
  // Otherwise return the selected hour in "military" format with a leading zero.
474
- return string ? Picker._.digits( string ) : Picker._.lead( timeObject.hour % 24 )
592
+ return string ? _.digits( string ) : _.lead( timeObject.hour % 24 )
475
593
  },
476
594
  i: function( string, timeObject ) {
477
595
 
478
596
  // If there's a string, then the length is always 2.
479
597
  // Otherwise return the selected minutes.
480
- return string ? 2 : Picker._.lead( timeObject.mins )
598
+ return string ? 2 : _.lead( timeObject.mins )
481
599
  },
482
600
  a: function( string, timeObject ) {
483
601
 
@@ -499,156 +617,235 @@ TimePicker.prototype.formats = {
499
617
  toString: function ( formatString, itemObject ) {
500
618
  var clock = this
501
619
  return clock.formats.toArray( formatString ).map( function( label ) {
502
- return Picker._.trigger( clock.formats[ label ], clock, [ 0, itemObject ] ) || label.replace( /^!/, '' )
620
+ return _.trigger( clock.formats[ label ], clock, [ 0, itemObject ] ) || label.replace( /^!/, '' )
503
621
  }).join( '' )
504
622
  }
505
623
  } //TimePicker.prototype.formats
506
624
 
507
625
 
626
+
627
+
508
628
  /**
509
- * Flip an item as enabled or disabled.
629
+ * Check if two time units are the exact.
510
630
  */
511
- TimePicker.prototype.flipItem = function( type, value/*, options*/ ) {
631
+ TimePicker.prototype.isTimeExact = function( one, two ) {
512
632
 
513
- var clock = this,
514
- collection = clock.item.disable,
515
- isFlippedBase = clock.item.enable === -1
633
+ var clock = this
516
634
 
517
- // Flip the enabled and disabled times.
518
- if ( value == 'flip' ) {
519
- clock.item.enable = isFlippedBase ? 1 : -1
635
+ // When we’re working with minutes, do a direct comparison.
636
+ if (
637
+ ( _.isInteger( one ) && _.isInteger( two ) ) ||
638
+ ( typeof one == 'boolean' && typeof two == 'boolean' )
639
+ ) {
640
+ return one === two
520
641
  }
521
642
 
522
- // Reset the collection and enable the base state.
523
- else if ( ( type == 'enable' && value === true ) || ( type == 'disable' && value === false ) ) {
524
- clock.item.enable = 1
525
- collection = []
643
+ // When we’re working with time representations, compare the “pick” value.
644
+ if (
645
+ ( _.isDate( one ) || $.isArray( one ) ) &&
646
+ ( _.isDate( two ) || $.isArray( two ) )
647
+ ) {
648
+ return clock.create( one ).pick === clock.create( two ).pick
526
649
  }
527
650
 
528
- // Reset the collection and disable the base state.
529
- else if ( ( type == 'enable' && value === false ) || ( type == 'disable' && value === true ) ) {
530
- clock.item.enable = -1
531
- collection = []
651
+ // When we’re working with range objects, compare the “from” and “to”.
652
+ if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
653
+ return clock.isTimeExact( one.from, two.from ) && clock.isTimeExact( one.to, two.to )
532
654
  }
533
655
 
534
- // Make sure a collection of things was passed to add/remove.
535
- else if ( $.isArray( value ) ) {
656
+ return false
657
+ }
536
658
 
537
- // Check if we have to add/remove from collection.
538
- if ( isFlippedBase && type == 'enable' || !isFlippedBase && type == 'disable' ) {
539
- collection = clock.addDisabled( collection, value )
540
- }
541
- else if ( !isFlippedBase && type == 'enable' ) {
542
- collection = clock.addEnabled( collection, value )
543
- }
544
- else if ( isFlippedBase && type == 'disable' ) {
545
- collection = clock.removeDisabled( collection, value )
546
- }
659
+
660
+ /**
661
+ * Check if two time units overlap.
662
+ */
663
+ TimePicker.prototype.isTimeOverlap = function( one, two ) {
664
+
665
+ var clock = this
666
+
667
+ // When we’re working with an integer, compare the hours.
668
+ if ( _.isInteger( one ) && ( _.isDate( two ) || $.isArray( two ) ) ) {
669
+ return one === clock.create( two ).hour
670
+ }
671
+ if ( _.isInteger( two ) && ( _.isDate( one ) || $.isArray( one ) ) ) {
672
+ return two === clock.create( one ).hour
547
673
  }
548
674
 
549
- return collection
550
- } //TimePicker.prototype.flipItem
675
+ // When we’re working with range objects, check if the ranges overlap.
676
+ if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
677
+ return clock.overlapRanges( one, two )
678
+ }
679
+
680
+ return false
681
+ }
551
682
 
552
683
 
553
684
  /**
554
- * Add an enabled (inverted) item to the disabled collection.
685
+ * Flip the enabled state.
555
686
  */
556
- TimePicker.prototype.addEnabled = function( collection, item ) {
687
+ TimePicker.prototype.flipEnable = function(val) {
688
+ var itemObject = this.item
689
+ itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1)
690
+ }
557
691
 
558
- var clock = this
559
692
 
560
- // Go through each item to enable.
561
- item.map( function( timeUnit ) {
562
-
563
- // Check if the time unit is already within the collection.
564
- if ( clock.filterDisabled( collection, timeUnit, 1 ).length ) {
565
-
566
- // Remove the unit directly from the collection.
567
- collection = clock.removeDisabled( collection, [timeUnit] )
568
-
569
- // If the unit is an array and it falls within a
570
- // disabled weekday, invert it and then insert it.
571
- if (
572
- $.isArray( timeUnit ) &&
573
- collection.filter( function( disabledHour ) {
574
- return Picker._.isInteger( disabledHour ) && clock.create( timeUnit ).hour === disabledHour
575
- }).length
576
- ) {
577
- timeUnit = timeUnit.slice(0)
578
- timeUnit.push( 'inverted' )
579
- collection.push( timeUnit )
693
+ /**
694
+ * Mark a collection of times as “disabled”.
695
+ */
696
+ TimePicker.prototype.deactivate = function( type, timesToDisable ) {
697
+
698
+ var clock = this,
699
+ disabledItems = clock.item.disable.slice(0)
700
+
701
+
702
+ // If we’re flipping, that’s all we need to do.
703
+ if ( timesToDisable == 'flip' ) {
704
+ clock.flipEnable()
705
+ }
706
+
707
+ else if ( timesToDisable === false ) {
708
+ clock.flipEnable(1)
709
+ disabledItems = []
710
+ }
711
+
712
+ else if ( timesToDisable === true ) {
713
+ clock.flipEnable(-1)
714
+ disabledItems = []
715
+ }
716
+
717
+ // Otherwise go through the times to disable.
718
+ else {
719
+
720
+ timesToDisable.map(function( unitToDisable ) {
721
+
722
+ var matchFound
723
+
724
+ // When we have disabled items, check for matches.
725
+ // If something is matched, immediately break out.
726
+ for ( var index = 0; index < disabledItems.length; index += 1 ) {
727
+ if ( clock.isTimeExact( unitToDisable, disabledItems[index] ) ) {
728
+ matchFound = true
729
+ break
730
+ }
580
731
  }
581
- }
582
- })
583
732
 
584
- // Return the final collection.
585
- return collection
586
- } //TimePicker.prototype.addEnabled
733
+ // If nothing was found, add the validated unit to the collection.
734
+ if ( !matchFound ) {
735
+ if (
736
+ _.isInteger( unitToDisable ) ||
737
+ _.isDate( unitToDisable ) ||
738
+ $.isArray( unitToDisable ) ||
739
+ ( $.isPlainObject( unitToDisable ) && unitToDisable.from && unitToDisable.to )
740
+ ) {
741
+ disabledItems.push( unitToDisable )
742
+ }
743
+ }
744
+ })
745
+ }
746
+
747
+ // Return the updated collection.
748
+ return disabledItems
749
+ } //TimePicker.prototype.deactivate
587
750
 
588
751
 
589
752
  /**
590
- * Add an item to the disabled collection.
753
+ * Mark a collection of times as “enabled”.
591
754
  */
592
- TimePicker.prototype.addDisabled = function( collection, item ) {
755
+ TimePicker.prototype.activate = function( type, timesToEnable ) {
593
756
 
594
- var clock = this
757
+ var clock = this,
758
+ disabledItems = clock.item.disable,
759
+ disabledItemsCount = disabledItems.length
595
760
 
596
- // Go through each item to disable.
597
- item.map( function( timeUnit ) {
761
+ // If we’re flipping, that’s all we need to do.
762
+ if ( timesToEnable == 'flip' ) {
763
+ clock.flipEnable()
764
+ }
598
765
 
599
- // Add the time unit if it isn’t already within the collection.
600
- if ( !clock.filterDisabled( collection, timeUnit ).length ) {
601
- collection.push( timeUnit )
602
- }
766
+ else if ( timesToEnable === true ) {
767
+ clock.flipEnable(1)
768
+ disabledItems = []
769
+ }
603
770
 
604
- // If the time unit is an array and falls within the range, just remove it.
605
- else if ( $.isArray( timeUnit ) && clock.filterDisabled( collection, timeUnit, 1 ).length ) {
606
- collection = clock.removeDisabled( collection, [timeUnit] )
607
- }
608
- })
771
+ else if ( timesToEnable === false ) {
772
+ clock.flipEnable(-1)
773
+ disabledItems = []
774
+ }
609
775
 
610
- // Return the final collection.
611
- return collection
612
- } //TimePicker.prototype.addDisabled
776
+ // Otherwise go through the disabled times.
777
+ else {
613
778
 
779
+ timesToEnable.map(function( unitToEnable ) {
614
780
 
615
- /**
616
- * Remove an item from the disabled collection.
617
- */
618
- TimePicker.prototype.removeDisabled = function( collection, item ) {
781
+ var matchFound,
782
+ disabledUnit,
783
+ index,
784
+ isRangeMatched
619
785
 
620
- var clock = this
786
+ // Go through the disabled items and try to find a match.
787
+ for ( index = 0; index < disabledItemsCount; index += 1 ) {
621
788
 
622
- // Go through each item to enable.
623
- item.map( function( timeUnit ) {
789
+ disabledUnit = disabledItems[index]
624
790
 
625
- // Filter each item out of the collection.
626
- collection = clock.filterDisabled( collection, timeUnit, 1 )
627
- })
791
+ // When an exact match is found, remove it from the collection.
792
+ if ( clock.isTimeExact( disabledUnit, unitToEnable ) ) {
793
+ matchFound = disabledItems[index] = null
794
+ isRangeMatched = true
795
+ break
796
+ }
628
797
 
629
- // Return the final colleciton.
630
- return collection
631
- } //TimePicker.prototype.removeDisabled
798
+ // When an overlapped match is found, add the “inverted” state to it.
799
+ else if ( clock.isTimeOverlap( disabledUnit, unitToEnable ) ) {
800
+ if ( $.isPlainObject( unitToEnable ) ) {
801
+ unitToEnable.inverted = true
802
+ matchFound = unitToEnable
803
+ }
804
+ else if ( $.isArray( unitToEnable ) ) {
805
+ matchFound = unitToEnable
806
+ if ( !matchFound[2] ) matchFound.push( 'inverted' )
807
+ }
808
+ else if ( _.isDate( unitToEnable ) ) {
809
+ matchFound = [ unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted' ]
810
+ }
811
+ break
812
+ }
813
+ }
632
814
 
815
+ // If a match was found, remove a previous duplicate entry.
816
+ if ( matchFound ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
817
+ if ( clock.isTimeExact( disabledItems[index], unitToEnable ) ) {
818
+ disabledItems[index] = null
819
+ break
820
+ }
821
+ }
633
822
 
634
- /**
635
- * Filter through the disabled collection to find a time unit.
636
- */
637
- TimePicker.prototype.filterDisabled = function( collection, timeUnit, isRemoving ) {
638
- var timeIsArray = $.isArray( timeUnit )
639
- return collection.filter( function( disabledTimeUnit ) {
640
- var isMatch = !timeIsArray && timeUnit === disabledTimeUnit ||
641
- timeIsArray && $.isArray( disabledTimeUnit ) && timeUnit.toString() === disabledTimeUnit.toString()
642
- return isRemoving ? !isMatch : isMatch
643
- })
644
- } //TimePicker.prototype.filterDisabled
823
+ // In the event that we’re dealing with an overlap of range times,
824
+ // make sure there are no “inverted” times because of it.
825
+ if ( isRangeMatched ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
826
+ if ( clock.isTimeOverlap( disabledItems[index], unitToEnable ) ) {
827
+ disabledItems[index] = null
828
+ break
829
+ }
830
+ }
831
+
832
+ // If something is still matched, add it into the collection.
833
+ if ( matchFound ) {
834
+ disabledItems.push( matchFound )
835
+ }
836
+ })
837
+ }
838
+
839
+ // Return the updated collection.
840
+ return disabledItems.filter(function( val ) { return val != null })
841
+ } //TimePicker.prototype.activate
645
842
 
646
843
 
647
844
  /**
648
845
  * The division to use for the range intervals.
649
846
  */
650
847
  TimePicker.prototype.i = function( type, value/*, options*/ ) {
651
- return Picker._.isInteger( value ) && value > 0 ? value : this.item.interval
848
+ return _.isInteger( value ) && value > 0 ? value : this.item.interval
652
849
  }
653
850
 
654
851
 
@@ -665,42 +862,68 @@ TimePicker.prototype.nodes = function( isOpen ) {
665
862
  viewsetObject = clock.item.view,
666
863
  disabledCollection = clock.item.disable
667
864
 
668
- return Picker._.node( 'ul', Picker._.group({
669
- min: clock.item.min.pick,
670
- max: clock.item.max.pick,
671
- i: clock.item.interval,
672
- node: 'li',
673
- item: function( loopedTime ) {
674
- loopedTime = clock.create( loopedTime )
675
- return [
676
- Picker._.trigger( clock.formats.toString, clock, [ Picker._.trigger( settings.formatLabel, clock, [ loopedTime ] ) || settings.format, loopedTime ] ),
677
- (function( klasses, timeMinutes ) {
678
-
679
- if ( selectedObject && selectedObject.pick == timeMinutes ) {
680
- klasses.push( settings.klass.selected )
681
- }
682
-
683
- if ( highlightedObject && highlightedObject.pick == timeMinutes ) {
684
- klasses.push( settings.klass.highlighted )
685
- }
686
-
687
- if ( viewsetObject && viewsetObject.pick == timeMinutes ) {
688
- klasses.push( settings.klass.viewset )
689
- }
690
-
691
- if ( disabledCollection && clock.disabled( loopedTime ) ) {
692
- klasses.push( settings.klass.disabled )
693
- }
694
-
695
- return klasses.join( ' ' )
696
- })( [ settings.klass.listItem ], loopedTime.pick ),
697
- 'data-pick=' + loopedTime.pick
698
- ]
699
- }
700
- }) +
701
-
702
- // * For Firefox forms to submit, make sure to set the button’s `type` attribute as “button”.
703
- Picker._.node( 'li', Picker._.node( 'button', settings.clear, settings.klass.buttonClear, 'type=button data-clear=1' + ( isOpen ? '' : ' disable' ) ) ), settings.klass.list )
865
+ return _.node(
866
+ 'ul',
867
+ _.group({
868
+ min: clock.item.min.pick,
869
+ max: clock.item.max.pick,
870
+ i: clock.item.interval,
871
+ node: 'li',
872
+ item: function( loopedTime ) {
873
+ loopedTime = clock.create( loopedTime )
874
+ var timeMinutes = loopedTime.pick,
875
+ isSelected = selectedObject && selectedObject.pick == timeMinutes,
876
+ isHighlighted = highlightedObject && highlightedObject.pick == timeMinutes,
877
+ isDisabled = disabledCollection && clock.disabled( loopedTime )
878
+ return [
879
+ _.trigger( clock.formats.toString, clock, [ _.trigger( settings.formatLabel, clock, [ loopedTime ] ) || settings.format, loopedTime ] ),
880
+ (function( klasses ) {
881
+
882
+ if ( isSelected ) {
883
+ klasses.push( settings.klass.selected )
884
+ }
885
+
886
+ if ( isHighlighted ) {
887
+ klasses.push( settings.klass.highlighted )
888
+ }
889
+
890
+ if ( viewsetObject && viewsetObject.pick == timeMinutes ) {
891
+ klasses.push( settings.klass.viewset )
892
+ }
893
+
894
+ if ( isDisabled ) {
895
+ klasses.push( settings.klass.disabled )
896
+ }
897
+
898
+ return klasses.join( ' ' )
899
+ })( [ settings.klass.listItem ] ),
900
+ 'data-pick=' + loopedTime.pick + ' ' + _.ariaAttr({
901
+ role: 'button',
902
+ controls: clock.$node[0].id,
903
+ checked: isSelected && clock.$node.val() === _.trigger(
904
+ clock.formats.toString,
905
+ clock,
906
+ [ settings.format, loopedTime ]
907
+ ) ? true : null,
908
+ activedescendant: isHighlighted ? true : null,
909
+ disabled: isDisabled ? true : null
910
+ })
911
+ ]
912
+ }
913
+ }) +
914
+
915
+ // * For Firefox forms to submit, make sure to set the button’s `type` attribute as “button”.
916
+ _.node(
917
+ 'li',
918
+ _.node(
919
+ 'button',
920
+ settings.clear,
921
+ settings.klass.buttonClear,
922
+ 'type=button data-clear=1' + ( isOpen ? '' : ' disable' )
923
+ )
924
+ ),
925
+ settings.klass.list
926
+ )
704
927
  } //TimePicker.prototype.nodes
705
928
 
706
929
 
@@ -751,7 +974,7 @@ TimePicker.defaults = (function( prefix ) {
751
974
 
752
975
 
753
976
  /**
754
- * Extend the picker to add the date picker.
977
+ * Extend the picker to add the time picker.
755
978
  */
756
979
  Picker.extend( 'pickatime', TimePicker )
757
980