iqvoc 4.8.0 → 4.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/app/assets/fonts/FiraMono-Regular.eot +0 -0
  4. data/app/assets/fonts/FiraMono-Regular.ttf +0 -0
  5. data/app/assets/fonts/FiraMono-Regular.woff +0 -0
  6. data/app/assets/fonts/FiraSans-Bold.eot +0 -0
  7. data/app/assets/fonts/FiraSans-Bold.ttf +0 -0
  8. data/app/assets/fonts/FiraSans-Bold.woff +0 -0
  9. data/app/assets/fonts/FiraSans-BoldItalic.eot +0 -0
  10. data/app/assets/fonts/FiraSans-BoldItalic.ttf +0 -0
  11. data/app/assets/fonts/FiraSans-BoldItalic.woff +0 -0
  12. data/app/assets/javascripts/framework.js +3 -2
  13. data/app/assets/javascripts/iqvoc/autocomplete.js +42 -0
  14. data/app/assets/javascripts/iqvoc/concept_mapper.js +4 -3
  15. data/app/assets/javascripts/iqvoc/entityselect.js.erb +19 -30
  16. data/app/assets/javascripts/iqvoc/federated_concept_mapper.js +14 -26
  17. data/app/assets/javascripts/iqvoc/iqvoc.js +15 -4
  18. data/app/assets/javascripts/iqvoc/manifest.js +1 -1
  19. data/app/assets/stylesheets/_framework.scss +2 -4
  20. data/app/assets/stylesheets/iqvoc/_manifest.scss +2 -2
  21. data/app/assets/stylesheets/iqvoc/components/_datepicker.scss +7 -0
  22. data/app/assets/stylesheets/iqvoc/components/typeahead.scss +56 -0
  23. data/app/assets/stylesheets/iqvoc/hacks/_hacks.scss +4 -0
  24. data/app/assets/stylesheets/iqvoc/settings/_bootstrap-variables.scss +8 -8
  25. data/app/assets/stylesheets/iqvoc/settings/_bootstrap.scss +5 -0
  26. data/app/assets/stylesheets/iqvoc/settings/_fonts.scss +25 -64
  27. data/app/views/pages/components.html.erb +44 -0
  28. data/lib/iqvoc/version.rb +1 -1
  29. data/vendor/assets/javascripts/pickadate/picker.date.js +1322 -0
  30. data/vendor/assets/javascripts/pickadate/picker.js +1061 -0
  31. data/vendor/assets/javascripts/typeahead.jquery.js +1184 -0
  32. data/vendor/assets/stylesheets/pickadate/classic.date.scss +278 -0
  33. data/vendor/assets/stylesheets/pickadate/classic.scss +109 -0
  34. metadata +19 -23
  35. data/app/assets/stylesheets/iqvoc/hacks/_jquery-ui_extensions.scss +0 -14
  36. data/vendor/assets/javascripts/jquery-ui.datepicker-de.js +0 -22
  37. data/vendor/assets/javascripts/jquery-ui.js +0 -4676
  38. data/vendor/assets/stylesheets/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  39. data/vendor/assets/stylesheets/images/ui-bg_flat_55_fbec88_40x100.png +0 -0
  40. data/vendor/assets/stylesheets/images/ui-bg_glass_75_d0e5f5_1x400.png +0 -0
  41. data/vendor/assets/stylesheets/images/ui-bg_glass_85_dfeffc_1x400.png +0 -0
  42. data/vendor/assets/stylesheets/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  43. data/vendor/assets/stylesheets/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png +0 -0
  44. data/vendor/assets/stylesheets/images/ui-bg_inset-hard_100_f5f8f9_1x100.png +0 -0
  45. data/vendor/assets/stylesheets/images/ui-bg_inset-hard_100_fcfdfd_1x100.png +0 -0
  46. data/vendor/assets/stylesheets/images/ui-icons_217bc0_256x240.png +0 -0
  47. data/vendor/assets/stylesheets/images/ui-icons_2e83ff_256x240.png +0 -0
  48. data/vendor/assets/stylesheets/images/ui-icons_469bdd_256x240.png +0 -0
  49. data/vendor/assets/stylesheets/images/ui-icons_6da8d5_256x240.png +0 -0
  50. data/vendor/assets/stylesheets/images/ui-icons_cd0a0a_256x240.png +0 -0
  51. data/vendor/assets/stylesheets/images/ui-icons_d8e7f3_256x240.png +0 -0
  52. data/vendor/assets/stylesheets/images/ui-icons_f9bd01_256x240.png +0 -0
  53. data/vendor/assets/stylesheets/jquery-ui.scss +0 -709
  54. data/vendor/assets/stylesheets/jquery-ui.structure.scss +0 -317
  55. data/vendor/assets/stylesheets/jquery-ui.theme.scss +0 -410
@@ -0,0 +1,1061 @@
1
+ /*!
2
+ * pickadate.js v3.5.0, 2014/04/13
3
+ * By Amsul, http://amsul.ca
4
+ * Hosted on http://amsul.github.io/pickadate.js
5
+ * Licensed under MIT
6
+ */
7
+
8
+ (function ( factory ) {
9
+
10
+ // Register as an anonymous module.
11
+ if ( typeof define === 'function' && define.amd )
12
+ define( 'picker', ['jquery'], factory )
13
+
14
+ // Or using browser globals.
15
+ else this.Picker = factory( jQuery )
16
+
17
+ }(function( $ ) {
18
+
19
+ var $window = $( window )
20
+ var $document = $( document )
21
+ var $html = $( document.documentElement )
22
+
23
+
24
+ /**
25
+ * The picker constructor that creates a blank picker.
26
+ */
27
+ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) {
28
+
29
+ // If there’s no element, return the picker constructor.
30
+ if ( !ELEMENT ) return PickerConstructor
31
+
32
+
33
+ var
34
+ IS_DEFAULT_THEME = false,
35
+
36
+
37
+ // The state of the picker.
38
+ STATE = {
39
+ id: ELEMENT.id || 'P' + Math.abs( ~~(Math.random() * new Date()) )
40
+ },
41
+
42
+
43
+ // Merge the defaults and options passed.
44
+ SETTINGS = COMPONENT ? $.extend( true, {}, COMPONENT.defaults, OPTIONS ) : OPTIONS || {},
45
+
46
+
47
+ // Merge the default classes with the settings classes.
48
+ CLASSES = $.extend( {}, PickerConstructor.klasses(), SETTINGS.klass ),
49
+
50
+
51
+ // The element node wrapper into a jQuery object.
52
+ $ELEMENT = $( ELEMENT ),
53
+
54
+
55
+ // Pseudo picker constructor.
56
+ PickerInstance = function() {
57
+ return this.start()
58
+ },
59
+
60
+
61
+ // The picker prototype.
62
+ P = PickerInstance.prototype = {
63
+
64
+ constructor: PickerInstance,
65
+
66
+ $node: $ELEMENT,
67
+
68
+
69
+ /**
70
+ * Initialize everything
71
+ */
72
+ start: function() {
73
+
74
+ // If it’s already started, do nothing.
75
+ if ( STATE && STATE.start ) return P
76
+
77
+
78
+ // Update the picker states.
79
+ STATE.methods = {}
80
+ STATE.start = true
81
+ STATE.open = false
82
+ STATE.type = ELEMENT.type
83
+
84
+
85
+ // Confirm focus state, convert into text input to remove UA stylings,
86
+ // and set as readonly to prevent keyboard popup.
87
+ ELEMENT.autofocus = ELEMENT == document.activeElement
88
+ ELEMENT.type = 'text'
89
+ ELEMENT.readOnly = !SETTINGS.editable
90
+ ELEMENT.id = ELEMENT.id || STATE.id
91
+
92
+
93
+ // Create a new picker component with the settings.
94
+ P.component = new COMPONENT(P, SETTINGS)
95
+
96
+
97
+ // Create the picker root with a holder and then prepare it.
98
+ P.$root = $( PickerConstructor._.node('div', createWrappedComponent(), CLASSES.picker, 'id="' + ELEMENT.id + '_root"') )
99
+ prepareElementRoot()
100
+
101
+
102
+ // If there’s a format for the hidden input element, create the element.
103
+ if ( SETTINGS.formatSubmit ) {
104
+ prepareElementHidden()
105
+ }
106
+
107
+
108
+ // Prepare the input element.
109
+ prepareElement()
110
+
111
+
112
+ // Insert the root as specified in the settings.
113
+ if ( SETTINGS.container ) $( SETTINGS.container ).append( P.$root )
114
+ else $ELEMENT.after( P.$root )
115
+
116
+
117
+ // Bind the default component and settings events.
118
+ P.on({
119
+ start: P.component.onStart,
120
+ render: P.component.onRender,
121
+ stop: P.component.onStop,
122
+ open: P.component.onOpen,
123
+ close: P.component.onClose,
124
+ set: P.component.onSet
125
+ }).on({
126
+ start: SETTINGS.onStart,
127
+ render: SETTINGS.onRender,
128
+ stop: SETTINGS.onStop,
129
+ open: SETTINGS.onOpen,
130
+ close: SETTINGS.onClose,
131
+ set: SETTINGS.onSet
132
+ })
133
+
134
+
135
+ // Once we’re all set, check the theme in use.
136
+ IS_DEFAULT_THEME = isUsingDefaultTheme( P.$root.children()[ 0 ] )
137
+
138
+
139
+ // If the element has autofocus, open the picker.
140
+ if ( ELEMENT.autofocus ) {
141
+ P.open()
142
+ }
143
+
144
+
145
+ // Trigger queued the “start” and “render” events.
146
+ return P.trigger( 'start' ).trigger( 'render' )
147
+ }, //start
148
+
149
+
150
+ /**
151
+ * Render a new picker
152
+ */
153
+ render: function( entireComponent ) {
154
+
155
+ // Insert a new component holder in the root or box.
156
+ if ( entireComponent ) P.$root.html( createWrappedComponent() )
157
+ else P.$root.find( '.' + CLASSES.box ).html( P.component.nodes( STATE.open ) )
158
+
159
+ // Trigger the queued “render” events.
160
+ return P.trigger( 'render' )
161
+ }, //render
162
+
163
+
164
+ /**
165
+ * Destroy everything
166
+ */
167
+ stop: function() {
168
+
169
+ // If it’s already stopped, do nothing.
170
+ if ( !STATE.start ) return P
171
+
172
+ // Then close the picker.
173
+ P.close()
174
+
175
+ // Remove the hidden field.
176
+ if ( P._hidden ) {
177
+ P._hidden.parentNode.removeChild( P._hidden )
178
+ }
179
+
180
+ // Remove the root.
181
+ P.$root.remove()
182
+
183
+ // Remove the input class, remove the stored data, and unbind
184
+ // the events (after a tick for IE - see `P.close`).
185
+ $ELEMENT.removeClass( CLASSES.input ).removeData( NAME )
186
+ setTimeout( function() {
187
+ $ELEMENT.off( '.' + STATE.id )
188
+ }, 0)
189
+
190
+ // Restore the element state
191
+ ELEMENT.type = STATE.type
192
+ ELEMENT.readOnly = false
193
+
194
+ // Trigger the queued “stop” events.
195
+ P.trigger( 'stop' )
196
+
197
+ // Reset the picker states.
198
+ STATE.methods = {}
199
+ STATE.start = false
200
+
201
+ return P
202
+ }, //stop
203
+
204
+
205
+ /*
206
+ * Open up the picker
207
+ */
208
+ open: function( dontGiveFocus ) {
209
+
210
+ // If it’s already open, do nothing.
211
+ if ( STATE.open ) return P
212
+
213
+ // Add the “active” class.
214
+ $ELEMENT.addClass( CLASSES.active )
215
+ aria( ELEMENT, 'expanded', true )
216
+
217
+ // * A Firefox bug, when `html` has `overflow:hidden`, results in
218
+ // killing transitions :(. So add the “opened” state on the next tick.
219
+ // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
220
+ setTimeout( function() {
221
+
222
+ // Add the “opened” class to the picker root.
223
+ P.$root.addClass( CLASSES.opened )
224
+ aria( P.$root[0], 'hidden', false )
225
+
226
+ }, 0 )
227
+
228
+ // If we have to give focus, bind the element and doc events.
229
+ if ( dontGiveFocus !== false ) {
230
+
231
+ // Set it as open.
232
+ STATE.open = true
233
+
234
+ // Prevent the page from scrolling.
235
+ if ( IS_DEFAULT_THEME ) {
236
+ $html.
237
+ css( 'overflow', 'hidden' ).
238
+ css( 'padding-right', '+=' + getScrollbarWidth() )
239
+ }
240
+
241
+ // Pass focus to the element’s jQuery object.
242
+ $ELEMENT.trigger( 'focus' )
243
+
244
+ // Bind the document events.
245
+ $document.on( 'click.' + STATE.id + ' focusin.' + STATE.id, function( event ) {
246
+
247
+ var target = event.target
248
+
249
+ // If the target of the event is not the element, close the picker picker.
250
+ // * Don’t worry about clicks or focusins on the root because those don’t bubble up.
251
+ // Also, for Firefox, a click on an `option` element bubbles up directly
252
+ // to the doc. So make sure the target wasn't the doc.
253
+ // * In Firefox stopPropagation() doesn’t prevent right-click events from bubbling,
254
+ // which causes the picker to unexpectedly close when right-clicking it. So make
255
+ // sure the event wasn’t a right-click.
256
+ if ( target != ELEMENT && target != document && event.which != 3 ) {
257
+
258
+ // If the target was the holder that covers the screen,
259
+ // keep the element focused to maintain tabindex.
260
+ P.close( target === P.$root.children()[0] )
261
+ }
262
+
263
+ }).on( 'keydown.' + STATE.id, function( event ) {
264
+
265
+ var
266
+ // Get the keycode.
267
+ keycode = event.keyCode,
268
+
269
+ // Translate that to a selection change.
270
+ keycodeToMove = P.component.key[ keycode ],
271
+
272
+ // Grab the target.
273
+ target = event.target
274
+
275
+
276
+ // On escape, close the picker and give focus.
277
+ if ( keycode == 27 ) {
278
+ P.close( true )
279
+ }
280
+
281
+
282
+ // Check if there is a key movement or “enter” keypress on the element.
283
+ else if ( target == ELEMENT && ( keycodeToMove || keycode == 13 ) ) {
284
+
285
+ // Prevent the default action to stop page movement.
286
+ event.preventDefault()
287
+
288
+ // Trigger the key movement action.
289
+ if ( keycodeToMove ) {
290
+ PickerConstructor._.trigger( P.component.key.go, P, [ PickerConstructor._.trigger( keycodeToMove ) ] )
291
+ }
292
+
293
+ // On “enter”, if the highlighted item isn’t disabled, set the value and close.
294
+ else if ( !P.$root.find( '.' + CLASSES.highlighted ).hasClass( CLASSES.disabled ) ) {
295
+ P.set( 'select', P.component.item.highlight ).close()
296
+ }
297
+ }
298
+
299
+
300
+ // If the target is within the root and “enter” is pressed,
301
+ // prevent the default action and trigger a click on the target instead.
302
+ else if ( $.contains( P.$root[0], target ) && keycode == 13 ) {
303
+ event.preventDefault()
304
+ target.click()
305
+ }
306
+ })
307
+ }
308
+
309
+ // Trigger the queued “open” events.
310
+ return P.trigger( 'open' )
311
+ }, //open
312
+
313
+
314
+ /**
315
+ * Close the picker
316
+ */
317
+ close: function( giveFocus ) {
318
+
319
+ // If we need to give focus, do it before changing states.
320
+ if ( giveFocus ) {
321
+ // ....ah yes! It would’ve been incomplete without a crazy workaround for IE :|
322
+ // The focus is triggered *after* the close has completed - causing it
323
+ // to open again. So unbind and rebind the event at the next tick.
324
+ $ELEMENT.off( 'focus.' + STATE.id ).trigger( 'focus' )
325
+ setTimeout( function() {
326
+ $ELEMENT.on( 'focus.' + STATE.id, focusToOpen )
327
+ }, 0 )
328
+ }
329
+
330
+ // Remove the “active” class.
331
+ $ELEMENT.removeClass( CLASSES.active )
332
+ aria( ELEMENT, 'expanded', false )
333
+
334
+ // * A Firefox bug, when `html` has `overflow:hidden`, results in
335
+ // killing transitions :(. So remove the “opened” state on the next tick.
336
+ // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
337
+ setTimeout( function() {
338
+
339
+ // Remove the “opened” and “focused” class from the picker root.
340
+ P.$root.removeClass( CLASSES.opened + ' ' + CLASSES.focused )
341
+ aria( P.$root[0], 'hidden', true )
342
+
343
+ }, 0 )
344
+
345
+ // If it’s already closed, do nothing more.
346
+ if ( !STATE.open ) return P
347
+
348
+ // Set it as closed.
349
+ STATE.open = false
350
+
351
+ // Allow the page to scroll.
352
+ if ( IS_DEFAULT_THEME ) {
353
+ $html.
354
+ css( 'overflow', '' ).
355
+ css( 'padding-right', '-=' + getScrollbarWidth() )
356
+ }
357
+
358
+ // Unbind the document events.
359
+ $document.off( '.' + STATE.id )
360
+
361
+ // Trigger the queued “close” events.
362
+ return P.trigger( 'close' )
363
+ }, //close
364
+
365
+
366
+ /**
367
+ * Clear the values
368
+ */
369
+ clear: function() {
370
+ return P.set( 'clear' )
371
+ }, //clear
372
+
373
+
374
+ /**
375
+ * Set something
376
+ */
377
+ set: function( thing, value, options ) {
378
+
379
+ var thingItem, thingValue,
380
+ thingIsObject = $.isPlainObject( thing ),
381
+ thingObject = thingIsObject ? thing : {}
382
+
383
+ // Make sure we have usable options.
384
+ options = thingIsObject && $.isPlainObject( value ) ? value : options || {}
385
+
386
+ if ( thing ) {
387
+
388
+ // If the thing isn’t an object, make it one.
389
+ if ( !thingIsObject ) {
390
+ thingObject[ thing ] = value
391
+ }
392
+
393
+ // Go through the things of items to set.
394
+ for ( thingItem in thingObject ) {
395
+
396
+ // Grab the value of the thing.
397
+ thingValue = thingObject[ thingItem ]
398
+
399
+ // First, if the item exists and there’s a value, set it.
400
+ if ( thingItem in P.component.item ) {
401
+ if ( thingValue === undefined ) thingValue = null
402
+ P.component.set( thingItem, thingValue, options )
403
+ }
404
+
405
+ // Then, check to update the element value and broadcast a change.
406
+ if ( thingItem == 'select' || thingItem == 'clear' ) {
407
+ $ELEMENT.
408
+ val( thingItem == 'clear' ? '' : P.get( thingItem, SETTINGS.format ) ).
409
+ trigger( 'change' )
410
+ }
411
+ }
412
+
413
+ // Render a new picker.
414
+ P.render()
415
+ }
416
+
417
+ // When the method isn’t muted, trigger queued “set” events and pass the `thingObject`.
418
+ return options.muted ? P : P.trigger( 'set', thingObject )
419
+ }, //set
420
+
421
+
422
+ /**
423
+ * Get something
424
+ */
425
+ get: function( thing, format ) {
426
+
427
+ // Make sure there’s something to get.
428
+ thing = thing || 'value'
429
+
430
+ // If a picker state exists, return that.
431
+ if ( STATE[ thing ] != null ) {
432
+ return STATE[ thing ]
433
+ }
434
+
435
+ // Return the value, if that.
436
+ if ( thing == 'value' ) {
437
+ return ELEMENT.value
438
+ }
439
+
440
+ // Check if a component item exists, return that.
441
+ if ( thing in P.component.item ) {
442
+ if ( typeof format == 'string' ) {
443
+ var thingValue = P.component.get( thing )
444
+ return thingValue ?
445
+ PickerConstructor._.trigger(
446
+ P.component.formats.toString,
447
+ P.component,
448
+ [ format, thingValue ]
449
+ ) : ''
450
+ }
451
+ return P.component.get( thing )
452
+ }
453
+ }, //get
454
+
455
+
456
+
457
+ /**
458
+ * Bind events on the things.
459
+ */
460
+ on: function( thing, method ) {
461
+
462
+ var thingName, thingMethod,
463
+ thingIsObject = $.isPlainObject( thing ),
464
+ thingObject = thingIsObject ? thing : {}
465
+
466
+ if ( thing ) {
467
+
468
+ // If the thing isn’t an object, make it one.
469
+ if ( !thingIsObject ) {
470
+ thingObject[ thing ] = method
471
+ }
472
+
473
+ // Go through the things to bind to.
474
+ for ( thingName in thingObject ) {
475
+
476
+ // Grab the method of the thing.
477
+ thingMethod = thingObject[ thingName ]
478
+
479
+ // Make sure the thing methods collection exists.
480
+ STATE.methods[ thingName ] = STATE.methods[ thingName ] || []
481
+
482
+ // Add the method to the relative method collection.
483
+ STATE.methods[ thingName ].push( thingMethod )
484
+ }
485
+ }
486
+
487
+ return P
488
+ }, //on
489
+
490
+
491
+
492
+ /**
493
+ * Unbind events on the things.
494
+ */
495
+ off: function() {
496
+ var i, thingName,
497
+ names = arguments;
498
+ for ( i = 0, namesCount = names.length; i < namesCount; i += 1 ) {
499
+ thingName = names[i]
500
+ if ( thingName in STATE.methods ) {
501
+ delete STATE.methods[thingName]
502
+ }
503
+ }
504
+ return P
505
+ },
506
+
507
+
508
+ /**
509
+ * Fire off method events.
510
+ */
511
+ trigger: function( name, data ) {
512
+ var methodList = STATE.methods[ name ]
513
+ if ( methodList ) {
514
+ methodList.map( function( method ) {
515
+ PickerConstructor._.trigger( method, P, [ data ] )
516
+ })
517
+ }
518
+ return P
519
+ } //trigger
520
+ } //PickerInstance.prototype
521
+
522
+
523
+ /**
524
+ * Wrap the picker holder components together.
525
+ */
526
+ function createWrappedComponent() {
527
+
528
+ // Create a picker wrapper holder
529
+ return PickerConstructor._.node( 'div',
530
+
531
+ // Create a picker wrapper node
532
+ PickerConstructor._.node( 'div',
533
+
534
+ // Create a picker frame
535
+ PickerConstructor._.node( 'div',
536
+
537
+ // Create a picker box node
538
+ PickerConstructor._.node( 'div',
539
+
540
+ // Create the components nodes.
541
+ P.component.nodes( STATE.open ),
542
+
543
+ // The picker box class
544
+ CLASSES.box
545
+ ),
546
+
547
+ // Picker wrap class
548
+ CLASSES.wrap
549
+ ),
550
+
551
+ // Picker frame class
552
+ CLASSES.frame
553
+ ),
554
+
555
+ // Picker holder class
556
+ CLASSES.holder
557
+ ) //endreturn
558
+ } //createWrappedComponent
559
+
560
+
561
+
562
+ /**
563
+ * Prepare the input element with all bindings.
564
+ */
565
+ function prepareElement() {
566
+
567
+ $ELEMENT.
568
+
569
+ // Store the picker data by component name.
570
+ data(NAME, P).
571
+
572
+ // Add the “input” class name.
573
+ addClass(CLASSES.input).
574
+
575
+ // If there’s a `data-value`, update the value of the element.
576
+ val( $ELEMENT.data('value') ?
577
+ P.get('select', SETTINGS.format) :
578
+ ELEMENT.value
579
+ ).
580
+
581
+ // On focus/click, open the picker and adjust the root “focused” state.
582
+ on('focus.' + STATE.id + ' click.' + STATE.id, focusToOpen)
583
+
584
+
585
+ // Only bind keydown events if the element isn’t editable.
586
+ if ( !SETTINGS.editable ) {
587
+
588
+ // Handle keyboard event based on the picker being opened or not.
589
+ $ELEMENT.on('keydown.' + STATE.id, function(event) {
590
+
591
+ var keycode = event.keyCode,
592
+
593
+ // Check if one of the delete keys was pressed.
594
+ isKeycodeDelete = /^(8|46)$/.test(keycode)
595
+
596
+ // For some reason IE clears the input value on “escape”.
597
+ if ( keycode == 27 ) {
598
+ P.close()
599
+ return false
600
+ }
601
+
602
+ // Check if `space` or `delete` was pressed or the picker is closed with a key movement.
603
+ if ( keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[keycode] ) {
604
+
605
+ // Prevent it from moving the page and bubbling to doc.
606
+ event.preventDefault()
607
+ event.stopPropagation()
608
+
609
+ // If `delete` was pressed, clear the values and close the picker.
610
+ // Otherwise open the picker.
611
+ if ( isKeycodeDelete ) { P.clear().close() }
612
+ else { P.open() }
613
+ }
614
+ })
615
+ }
616
+
617
+
618
+ // Update the aria attributes.
619
+ aria(ELEMENT, {
620
+ haspopup: true,
621
+ expanded: false,
622
+ readonly: false,
623
+ owns: ELEMENT.id + '_root' + (P._hidden ? ' ' + P._hidden.id : '')
624
+ })
625
+ }
626
+
627
+
628
+ /**
629
+ * Prepare the root picker element with all bindings.
630
+ */
631
+ function prepareElementRoot() {
632
+
633
+ P.$root.
634
+
635
+ on({
636
+
637
+ // When something within the root is focused, stop from bubbling
638
+ // to the doc and remove the “focused” state from the root.
639
+ focusin: function( event ) {
640
+ P.$root.removeClass( CLASSES.focused )
641
+ event.stopPropagation()
642
+ },
643
+
644
+ // When something within the root holder is clicked, stop it
645
+ // from bubbling to the doc.
646
+ 'mousedown click': function( event ) {
647
+
648
+ var target = event.target
649
+
650
+ // Make sure the target isn’t the root holder so it can bubble up.
651
+ if ( target != P.$root.children()[ 0 ] ) {
652
+
653
+ event.stopPropagation()
654
+
655
+ // * For mousedown events, cancel the default action in order to
656
+ // prevent cases where focus is shifted onto external elements
657
+ // when using things like jQuery mobile or MagnificPopup (ref: #249 & #120).
658
+ // Also, for Firefox, don’t prevent action on the `option` element.
659
+ if ( event.type == 'mousedown' && !$( target ).is( ':input' ) && target.nodeName != 'OPTION' ) {
660
+
661
+ event.preventDefault()
662
+
663
+ // Re-focus onto the element so that users can click away
664
+ // from elements focused within the picker.
665
+ ELEMENT.focus()
666
+ }
667
+ }
668
+ }
669
+ }).
670
+
671
+ // If there’s a click on an actionable element, carry out the actions.
672
+ on( 'click', '[data-pick], [data-nav], [data-clear]', function() {
673
+
674
+ var $target = $( this ),
675
+ targetData = $target.data(),
676
+ targetDisabled = $target.hasClass( CLASSES.navDisabled ) || $target.hasClass( CLASSES.disabled ),
677
+
678
+ // * For IE, non-focusable elements can be active elements as well
679
+ // (http://stackoverflow.com/a/2684561).
680
+ activeElement = document.activeElement
681
+ activeElement = activeElement && ( activeElement.type || activeElement.href ) && activeElement
682
+
683
+ // If it’s disabled or nothing inside is actively focused, re-focus the element.
684
+ if ( targetDisabled || activeElement && !$.contains( P.$root[0], activeElement ) ) {
685
+ ELEMENT.focus()
686
+ }
687
+
688
+ // If something is superficially changed, update the `highlight` based on the `nav`.
689
+ if ( targetData.nav && !targetDisabled ) {
690
+ P.set( 'highlight', P.component.item.highlight, { nav: targetData.nav } )
691
+ }
692
+
693
+ // If something is picked, set `select` then close with focus.
694
+ else if ( PickerConstructor._.isInteger( targetData.pick ) && !targetDisabled ) {
695
+ P.set( 'select', targetData.pick ).close( true )
696
+ }
697
+
698
+ // If a “clear” button is pressed, empty the values and close with focus.
699
+ else if ( targetData.clear ) {
700
+ P.clear().close( true )
701
+ }
702
+ }) //P.$root
703
+
704
+ aria( P.$root[0], 'hidden', true )
705
+ }
706
+
707
+
708
+ /**
709
+ * Prepare the hidden input element along with all bindings.
710
+ */
711
+ function prepareElementHidden() {
712
+
713
+ var id,
714
+ name
715
+
716
+ if ( SETTINGS.hiddenName === true ) {
717
+ id = ELEMENT.name + '_hidden'
718
+ name = ELEMENT.name
719
+ ELEMENT.name = ''
720
+ }
721
+ else {
722
+ name = [
723
+ typeof SETTINGS.hiddenPrefix == 'string' ? SETTINGS.hiddenPrefix : '',
724
+ typeof SETTINGS.hiddenSuffix == 'string' ? SETTINGS.hiddenSuffix : '_submit'
725
+ ]
726
+ name = id = name[0] + ELEMENT.name + name[1]
727
+ }
728
+
729
+ P._hidden = $(
730
+ '<input ' +
731
+ 'type=hidden ' +
732
+
733
+ // Create the name and ID by using the original
734
+ // input’s with a prefix and suffix.
735
+ 'name="' + name + '"' +
736
+ 'id="' + id + '"' +
737
+
738
+ // If the element has a value, set the hidden value as well.
739
+ (
740
+ $ELEMENT.data('value') || ELEMENT.value ?
741
+ ' value="' + P.get('select', SETTINGS.formatSubmit) + '"' :
742
+ ''
743
+ ) +
744
+ '>'
745
+ )[0]
746
+
747
+ $ELEMENT.
748
+
749
+ // If the value changes, update the hidden input with the correct format.
750
+ on('change.' + STATE.id, function() {
751
+ P._hidden.value = ELEMENT.value ?
752
+ P.get('select', SETTINGS.formatSubmit) :
753
+ ''
754
+ }).
755
+
756
+ // Insert the hidden input after the element.
757
+ after(P._hidden)
758
+ }
759
+
760
+
761
+ // Separated for IE
762
+ function focusToOpen( event ) {
763
+
764
+ // Stop the event from propagating to the doc.
765
+ event.stopPropagation()
766
+
767
+ // If it’s a focus event, add the “focused” class to the root.
768
+ if ( event.type == 'focus' ) {
769
+ P.$root.addClass( CLASSES.focused )
770
+ }
771
+
772
+ // And then finally open the picker.
773
+ P.open()
774
+ }
775
+
776
+
777
+ // Return a new picker instance.
778
+ return new PickerInstance()
779
+ } //PickerConstructor
780
+
781
+
782
+
783
+ /**
784
+ * The default classes and prefix to use for the HTML classes.
785
+ */
786
+ PickerConstructor.klasses = function( prefix ) {
787
+ prefix = prefix || 'picker'
788
+ return {
789
+
790
+ picker: prefix,
791
+ opened: prefix + '--opened',
792
+ focused: prefix + '--focused',
793
+
794
+ input: prefix + '__input',
795
+ active: prefix + '__input--active',
796
+
797
+ holder: prefix + '__holder',
798
+
799
+ frame: prefix + '__frame',
800
+ wrap: prefix + '__wrap',
801
+
802
+ box: prefix + '__box'
803
+ }
804
+ } //PickerConstructor.klasses
805
+
806
+
807
+
808
+ /**
809
+ * Check if the default theme is being used.
810
+ */
811
+ function isUsingDefaultTheme( element ) {
812
+
813
+ var theme,
814
+ prop = 'position'
815
+
816
+ // For IE.
817
+ if ( element.currentStyle ) {
818
+ theme = element.currentStyle[prop]
819
+ }
820
+
821
+ // For normal browsers.
822
+ else if ( window.getComputedStyle ) {
823
+ theme = getComputedStyle( element )[prop]
824
+ }
825
+
826
+ return theme == 'fixed'
827
+ }
828
+
829
+
830
+
831
+ /**
832
+ * Get the width of the browser’s scrollbar.
833
+ * Taken from: https://github.com/VodkaBears/Remodal/blob/master/src/jquery.remodal.js
834
+ */
835
+ function getScrollbarWidth() {
836
+
837
+ if ( $html.height() <= $window.height() ) {
838
+ return 0
839
+ }
840
+
841
+ var $outer = $( '<div style="visibility:hidden;width:100px" />' ).
842
+ appendTo( 'body' )
843
+
844
+ // Get the width without scrollbars.
845
+ var widthWithoutScroll = $outer[0].offsetWidth
846
+
847
+ // Force adding scrollbars.
848
+ $outer.css( 'overflow', 'scroll' )
849
+
850
+ // Add the inner div.
851
+ var $inner = $( '<div style="width:100%" />' ).appendTo( $outer )
852
+
853
+ // Get the width with scrollbars.
854
+ var widthWithScroll = $inner[0].offsetWidth
855
+
856
+ // Remove the divs.
857
+ $outer.remove()
858
+
859
+ // Return the difference between the widths.
860
+ return widthWithoutScroll - widthWithScroll
861
+ }
862
+
863
+
864
+
865
+ /**
866
+ * PickerConstructor helper methods.
867
+ */
868
+ PickerConstructor._ = {
869
+
870
+ /**
871
+ * Create a group of nodes. Expects:
872
+ * `
873
+ {
874
+ min: {Integer},
875
+ max: {Integer},
876
+ i: {Integer},
877
+ node: {String},
878
+ item: {Function}
879
+ }
880
+ * `
881
+ */
882
+ group: function( groupObject ) {
883
+
884
+ var
885
+ // Scope for the looped object
886
+ loopObjectScope,
887
+
888
+ // Create the nodes list
889
+ nodesList = '',
890
+
891
+ // The counter starts from the `min`
892
+ counter = PickerConstructor._.trigger( groupObject.min, groupObject )
893
+
894
+
895
+ // Loop from the `min` to `max`, incrementing by `i`
896
+ for ( ; counter <= PickerConstructor._.trigger( groupObject.max, groupObject, [ counter ] ); counter += groupObject.i ) {
897
+
898
+ // Trigger the `item` function within scope of the object
899
+ loopObjectScope = PickerConstructor._.trigger( groupObject.item, groupObject, [ counter ] )
900
+
901
+ // Splice the subgroup and create nodes out of the sub nodes
902
+ nodesList += PickerConstructor._.node(
903
+ groupObject.node,
904
+ loopObjectScope[ 0 ], // the node
905
+ loopObjectScope[ 1 ], // the classes
906
+ loopObjectScope[ 2 ] // the attributes
907
+ )
908
+ }
909
+
910
+ // Return the list of nodes
911
+ return nodesList
912
+ }, //group
913
+
914
+
915
+ /**
916
+ * Create a dom node string
917
+ */
918
+ node: function( wrapper, item, klass, attribute ) {
919
+
920
+ // If the item is false-y, just return an empty string
921
+ if ( !item ) return ''
922
+
923
+ // If the item is an array, do a join
924
+ item = $.isArray( item ) ? item.join( '' ) : item
925
+
926
+ // Check for the class
927
+ klass = klass ? ' class="' + klass + '"' : ''
928
+
929
+ // Check for any attributes
930
+ attribute = attribute ? ' ' + attribute : ''
931
+
932
+ // Return the wrapped item
933
+ return '<' + wrapper + klass + attribute + '>' + item + '</' + wrapper + '>'
934
+ }, //node
935
+
936
+
937
+ /**
938
+ * Lead numbers below 10 with a zero.
939
+ */
940
+ lead: function( number ) {
941
+ return ( number < 10 ? '0': '' ) + number
942
+ },
943
+
944
+
945
+ /**
946
+ * Trigger a function otherwise return the value.
947
+ */
948
+ trigger: function( callback, scope, args ) {
949
+ return typeof callback == 'function' ? callback.apply( scope, args || [] ) : callback
950
+ },
951
+
952
+
953
+ /**
954
+ * If the second character is a digit, length is 2 otherwise 1.
955
+ */
956
+ digits: function( string ) {
957
+ return ( /\d/ ).test( string[ 1 ] ) ? 2 : 1
958
+ },
959
+
960
+
961
+ /**
962
+ * Tell if something is a date object.
963
+ */
964
+ isDate: function( value ) {
965
+ return {}.toString.call( value ).indexOf( 'Date' ) > -1 && this.isInteger( value.getDate() )
966
+ },
967
+
968
+
969
+ /**
970
+ * Tell if something is an integer.
971
+ */
972
+ isInteger: function( value ) {
973
+ return {}.toString.call( value ).indexOf( 'Number' ) > -1 && value % 1 === 0
974
+ },
975
+
976
+
977
+ /**
978
+ * Create ARIA attribute strings.
979
+ */
980
+ ariaAttr: ariaAttr
981
+ } //PickerConstructor._
982
+
983
+
984
+
985
+ /**
986
+ * Extend the picker with a component and defaults.
987
+ */
988
+ PickerConstructor.extend = function( name, Component ) {
989
+
990
+ // Extend jQuery.
991
+ $.fn[ name ] = function( options, action ) {
992
+
993
+ // Grab the component data.
994
+ var componentData = this.data( name )
995
+
996
+ // If the picker is requested, return the data object.
997
+ if ( options == 'picker' ) {
998
+ return componentData
999
+ }
1000
+
1001
+ // If the component data exists and `options` is a string, carry out the action.
1002
+ if ( componentData && typeof options == 'string' ) {
1003
+ return PickerConstructor._.trigger( componentData[ options ], componentData, [ action ] )
1004
+ }
1005
+
1006
+ // Otherwise go through each matched element and if the component
1007
+ // doesn’t exist, create a new picker using `this` element
1008
+ // and merging the defaults and options with a deep copy.
1009
+ return this.each( function() {
1010
+ var $this = $( this )
1011
+ if ( !$this.data( name ) ) {
1012
+ new PickerConstructor( this, name, Component, options )
1013
+ }
1014
+ })
1015
+ }
1016
+
1017
+ // Set the defaults.
1018
+ $.fn[ name ].defaults = Component.defaults
1019
+ } //PickerConstructor.extend
1020
+
1021
+
1022
+
1023
+ function aria(element, attribute, value) {
1024
+ if ( $.isPlainObject(attribute) ) {
1025
+ for ( var key in attribute ) {
1026
+ ariaSet(element, key, attribute[key])
1027
+ }
1028
+ }
1029
+ else {
1030
+ ariaSet(element, attribute, value)
1031
+ }
1032
+ }
1033
+ function ariaSet(element, attribute, value) {
1034
+ element.setAttribute(
1035
+ (attribute == 'role' ? '' : 'aria-') + attribute,
1036
+ value
1037
+ )
1038
+ }
1039
+ function ariaAttr(attribute, data) {
1040
+ if ( !$.isPlainObject(attribute) ) {
1041
+ attribute = { attribute: data }
1042
+ }
1043
+ data = ''
1044
+ for ( var key in attribute ) {
1045
+ var attr = (key == 'role' ? '' : 'aria-') + key,
1046
+ attrVal = attribute[key]
1047
+ data += attrVal == null ? '' : attr + '="' + attribute[key] + '"'
1048
+ }
1049
+ return data
1050
+ }
1051
+
1052
+
1053
+
1054
+ // Expose the picker constructor.
1055
+ return PickerConstructor
1056
+
1057
+
1058
+ }));
1059
+
1060
+
1061
+