pickadate-rails 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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