fleetio_spark 0.1.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.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +43 -0
  4. data/app/assets/javascripts/spark/_cookie.js +23 -0
  5. data/app/assets/javascripts/spark/_esvg.js +73 -0
  6. data/app/assets/javascripts/spark/_icons.js +19 -0
  7. data/app/assets/javascripts/spark/_modal.js +421 -0
  8. data/app/assets/javascripts/spark/_search.js +79 -0
  9. data/app/assets/javascripts/spark/_sidebar.js +13 -0
  10. data/app/assets/javascripts/spark/_stack.js +248 -0
  11. data/app/assets/javascripts/spark/_tree.js +68 -0
  12. data/app/assets/javascripts/spark/shims/_dataset.js +241 -0
  13. data/app/assets/javascripts/spark/spark.js +31 -0
  14. data/app/assets/stylesheets/spark/_icons.scss +15 -0
  15. data/app/assets/stylesheets/spark/_index.scss +5 -0
  16. data/app/assets/stylesheets/spark/components/_index.scss +6 -0
  17. data/app/assets/stylesheets/spark/components/_modal.scss +73 -0
  18. data/app/assets/stylesheets/spark/components/_nav-menu.scss +102 -0
  19. data/app/assets/stylesheets/spark/components/_sidebar.scss +280 -0
  20. data/app/assets/stylesheets/spark/components/_tooltip.scss +42 -0
  21. data/app/assets/stylesheets/spark/components/_tree-nav.scss +13 -0
  22. data/app/assets/stylesheets/spark/components/header/_app-admin-header.scss +82 -0
  23. data/app/assets/stylesheets/spark/components/header/_app-nav-header.scss +154 -0
  24. data/app/assets/stylesheets/spark/components/header/_index.scss +5 -0
  25. data/app/assets/stylesheets/spark/components/header/_search-results.scss +29 -0
  26. data/app/assets/stylesheets/spark/components/header/_search.scss +195 -0
  27. data/app/assets/stylesheets/spark/components/header/_trial-status.scss +43 -0
  28. data/app/assets/stylesheets/spark/core/_animations.scss +154 -0
  29. data/app/assets/stylesheets/spark/core/_base.scss +11 -0
  30. data/app/assets/stylesheets/spark/core/_colors.scss +94 -0
  31. data/app/assets/stylesheets/spark/core/_index.scss +7 -0
  32. data/app/assets/stylesheets/spark/core/_layout.scss +39 -0
  33. data/app/assets/stylesheets/spark/core/_mixins.scss +86 -0
  34. data/app/assets/stylesheets/spark/core/_text.scss +5 -0
  35. data/app/assets/stylesheets/spark/core/_vars.scss +76 -0
  36. data/app/assets/stylesheets/spark/form/_base.scss +124 -0
  37. data/app/assets/stylesheets/spark/form/_check-switch.scss +99 -0
  38. data/app/assets/stylesheets/spark/form/_index.scss +2 -0
  39. data/app/assets/stylesheets/spark/spark.scss +1 -0
  40. data/app/assets/svgs/spark/add.svg +3 -0
  41. data/app/assets/svgs/spark/admin-user.svg +5 -0
  42. data/app/assets/svgs/spark/chevron-down.svg +3 -0
  43. data/app/assets/svgs/spark/chevron-up.svg +3 -0
  44. data/app/assets/svgs/spark/close.svg +3 -0
  45. data/app/assets/svgs/spark/contact.svg +6 -0
  46. data/app/assets/svgs/spark/dashboard.svg +3 -0
  47. data/app/assets/svgs/spark/fuel.svg +3 -0
  48. data/app/assets/svgs/spark/inspection.svg +3 -0
  49. data/app/assets/svgs/spark/issue.svg +3 -0
  50. data/app/assets/svgs/spark/leaderboard.svg +5 -0
  51. data/app/assets/svgs/spark/logo.svg +1 -0
  52. data/app/assets/svgs/spark/map.svg +4 -0
  53. data/app/assets/svgs/spark/nav-menu.svg +5 -0
  54. data/app/assets/svgs/spark/part.svg +6 -0
  55. data/app/assets/svgs/spark/place.svg +5 -0
  56. data/app/assets/svgs/spark/question.svg +10 -0
  57. data/app/assets/svgs/spark/reminder.svg +3 -0
  58. data/app/assets/svgs/spark/report.svg +5 -0
  59. data/app/assets/svgs/spark/search.svg +3 -0
  60. data/app/assets/svgs/spark/service.svg +3 -0
  61. data/app/assets/svgs/spark/settings.svg +3 -0
  62. data/app/assets/svgs/spark/trip.svg +4 -0
  63. data/app/assets/svgs/spark/vehicle.svg +3 -0
  64. data/app/assets/svgs/spark/vendor.svg +4 -0
  65. data/app/helpers/spark/application_helper.rb +42 -0
  66. data/app/helpers/spark/icon_helper.rb +40 -0
  67. data/app/helpers/spark/input_helper.rb +163 -0
  68. data/app/helpers/spark/menu_helper.rb +113 -0
  69. data/app/helpers/spark/modal_helper.rb +52 -0
  70. data/app/helpers/spark/nav_menu_helper.rb +167 -0
  71. data/app/helpers/spark/tag_helper.rb +27 -0
  72. data/app/helpers/spark/trial_helper.rb +23 -0
  73. data/app/views/layouts/spark/application.html.slim +28 -0
  74. data/app/views/layouts/spark/blank.html.slim +9 -0
  75. data/app/views/layouts/spark/default.html.slim +13 -0
  76. data/config/autoprefixer.yml +4 -0
  77. data/config/esvg.yml +20 -0
  78. data/lib/fleetio_spark.rb +14 -0
  79. data/lib/fleetio_spark/helper.rb +163 -0
  80. data/lib/fleetio_spark/version.rb +3 -0
  81. data/public/spark-0.1.0.css +1442 -0
  82. data/public/spark-0.1.0.css.gz +0 -0
  83. data/public/spark-0.1.0.js +11927 -0
  84. data/public/spark-0.1.0.js.gz +0 -0
  85. metadata +211 -0
@@ -0,0 +1,79 @@
1
+ var toolbox = require( 'compose-toolbox' ),
2
+ Event = toolbox.event,
3
+ modal = require( './_modal' ),
4
+ stack = require( './_stack' )
5
+
6
+ // Focus on search when `/` key is pressed
7
+ Event.keyOn( '/', function(event){
8
+ var q = document.querySelector('[name=q]')
9
+ if ( q ) {
10
+ event.preventDefault()
11
+ q.focus()
12
+ }
13
+ })
14
+
15
+ // Reset search results panel when query is cleared
16
+ Event.ready( function() {
17
+ var searchStack = stack.get( '#search-stack' )
18
+ var q = document.querySelector('[name=q]')
19
+ var searchModal = modal.get( '#search-results' )
20
+
21
+ if ( !searchModal || !q ) return
22
+
23
+ function keyWatcher ( event ) {
24
+ var focusEl = document.activeElement
25
+
26
+ // If a letter, number, or backspace is pressed focus on search input
27
+ if ( focusEl != q && event.key.length === 1 || Event.key.isPressed("backspace") ) {
28
+ q.focus()
29
+
30
+ // Restore default search input behavior
31
+ // When foucsed, esc clears input
32
+ } else if ( focusEl == q && Event.key.isPressed( "esc" ) ) {
33
+ q.value = ''
34
+ }
35
+ }
36
+
37
+ // Scroll all navs back to top
38
+ function resetScroll () {
39
+ toolbox.each( searchStack.root.querySelectorAll( 'nav' ), function (nav) {
40
+ nav.scrollTop = 0
41
+ })
42
+ }
43
+
44
+ searchModal.on( 'open', function() {
45
+ Event.on( document, 'keydown', keyWatcher )
46
+ })
47
+
48
+ // Reset stack when modal is closed and query is clear
49
+ searchModal.on( 'close', function() {
50
+ // Restore state of search panel
51
+ if ( document.querySelector( 'input[name=q]' ).value == '' ) {
52
+ searchStack.reset()
53
+ resetScroll()
54
+ }
55
+
56
+ Event.off( document, 'keydown', keyWatcher )
57
+ })
58
+
59
+ // Show first panel stack when query is cleared
60
+ Event.on( document, 'input', '[name=q]', Event.debounce( function ( event ) {
61
+ if ( event.currentTarget.value == '' ) {
62
+ searchStack.show( 0 )
63
+ resetScroll()
64
+ }
65
+ }, 50 ))
66
+
67
+ // Wire up clear search input behavior
68
+ Event.on( document, 'click', '.clear-search', function( event ) {
69
+ var button = event.currentTarget
70
+ event.preventDefault()
71
+ event.stopImmediatePropagation()
72
+
73
+ var searchInput = toolbox.getClosest( button, 'label' ).querySelector( '.input-container input' )
74
+ searchInput.value = ''
75
+ Event.fire( searchInput, 'input' )
76
+
77
+ searchInput.focus()
78
+ }, { useCapture: true })
79
+ })
@@ -0,0 +1,13 @@
1
+ var toolbox = require( 'compose-toolbox' ),
2
+ cookie = require( './_cookie' ),
3
+ Event = toolbox.event
4
+
5
+ Event.ready( function() {
6
+ Event.on( document, 'click', '.sidebar-nav-toggle', function( event ) {
7
+ document.body.classList.toggle( 'hide-sidebar' )
8
+
9
+ var visible = !document.body.classList.contains( 'hide-sidebar' )
10
+ cookie.update( 'sidebar-nav', { sidebar: visible } )
11
+ })
12
+ if ( cookie.get( 'sidebar-nav' ).sidebar === false ) document.body.classList.add( 'hide-sidebar' )
13
+ })
@@ -0,0 +1,248 @@
1
+ var toolbox = require( 'compose-toolbox' ),
2
+ Event = toolbox.event,
3
+ Stacks = []
4
+
5
+ var Stack = {
6
+ new: function( root ) {
7
+
8
+ if ( !isElement( root ) ) root = document.querySelector( root )
9
+
10
+ // Don't add a new stack if no element found or if stack already exists
11
+ if ( !isElement( root ) || root.dataset.stackId ) return
12
+
13
+ // Set up stack by searching DOM node under it.
14
+ // The first child found will be set up as a default
15
+ // After that each child will be added as a stack push
16
+
17
+ var stack = {
18
+ root: root,
19
+ current: 0,
20
+ panels: [],
21
+ focus: [],
22
+
23
+ add: function ( panelEl, name ) {
24
+ panelEl.dataset.stackIndex = stack.panels.length
25
+
26
+ // Hide all panels but the first panel
27
+ if ( stack.panels.length == 0 ) {
28
+ showEl( panelEl )
29
+ } else {
30
+ hideEl( panelEl )
31
+ }
32
+
33
+ stack.panels.push( panelEl )
34
+ },
35
+
36
+ // Reset stack to first panel
37
+ reset: function () {
38
+
39
+ // Already reset? We're done.
40
+ if ( stack.current == 0 ) return
41
+
42
+ // If hidden, manually reset all
43
+ if ( stack.root.offsetParent == null ) {
44
+ stack.focus = [] // Clear focus stack
45
+
46
+ // Hide and reset scroll on all panels
47
+ stack.panels.forEach( function( el ) {
48
+ el.scrollTop = 0
49
+ hideEl( el )
50
+ })
51
+
52
+ // Show first panel
53
+ stack.current = 0
54
+ showEl( stack.panels[0] )
55
+ }
56
+
57
+ // If not hidden, dismiss current panel and reset to first panel
58
+ else stack.show( 0 )
59
+ },
60
+
61
+ // Show a specific panel, hiding all others
62
+ show: function ( name ) {
63
+ var panel = stack.findPanel( name )
64
+
65
+ // If a panel isn't the current panel
66
+ // dismiss it before going to the requested panel
67
+ if ( panel && panel != stack.currentPanel() ) {
68
+
69
+ // Dismiss the current panel and with the callback load in the next panel
70
+ return stack.dismiss( stack.direction( panel ), function() {
71
+ stack.enter( panel )
72
+ })
73
+
74
+ }
75
+ },
76
+
77
+ enter: function ( el ) {
78
+ var direction = stack.direction( el )
79
+ el.dataset.direction = direction
80
+ stack.current = Number( el.dataset.stackIndex )
81
+
82
+ Event.afterAnimation( el, function() {
83
+ el.dataset.direction = ''
84
+ el.classList.remove( 'enter' )
85
+ }, 50 )
86
+
87
+ el.classList.add( 'enter' )
88
+ showEl( el )
89
+
90
+ if ( direction == 'forward' ) {
91
+ // If the previous focused element was in the stack
92
+ //if ( toolbox.childOf( stack.lastFocus(), stack.root ) ) {
93
+
94
+ // focus on the first input
95
+ //var firstItem = el.querySelector( 'input:not([hidden]), textarea, select, a[tabindex]' )
96
+ //if ( firstItem ) firstItem.focus()
97
+
98
+ //}
99
+
100
+ // Focus on the previous focused element
101
+ } else if ( direction == 'reverse' && stack.focus.length > 0 ) {
102
+ stack.focus.pop().focus()
103
+ }
104
+ },
105
+
106
+ dismiss: function ( direction, callback ) {
107
+
108
+ var el = stack.currentPanel()
109
+
110
+ if ( direction == 'forward' )
111
+ stack.focus.push( document.activeElement )
112
+
113
+ el.classList.remove( 'enter' )
114
+ el.classList.add( 'exit' )
115
+ el.dataset.direction = direction
116
+
117
+ Event.afterAnimation( el, function() {
118
+ // Ensure panel is scrolled to top
119
+ if ( direction == 'reverse' ) el.scrollTop = 0
120
+
121
+ hideEl( el )
122
+ el.classList.remove( 'exit' )
123
+ el.dataset.direction = ''
124
+
125
+ if ( typeof callback === 'function' ) callback()
126
+
127
+ }, 50 )
128
+
129
+ },
130
+
131
+ lastFocus: function() {
132
+ return stack.focus[ stack.focus.length -1 ]
133
+ },
134
+
135
+ // Find a panel by name or index
136
+ findPanel: function ( name ) {
137
+ if ( isElement( name ) )
138
+ return name
139
+ else
140
+ return root.querySelector( '[data-stack="'+name+'"], [data-stack-index="'+name+'"]' )
141
+ },
142
+
143
+ // Walk backwards in the stack, showing the panel previous to the current panel
144
+ back: function () {
145
+ stack.show( stack.panels[ stack.current - 1 ] )
146
+ },
147
+
148
+ // add aria-hidden=false to the first hidden element
149
+ next: function () {
150
+ stack.show( stack.panels[ stack.current + 1 ] )
151
+ },
152
+
153
+ direction: function( panel ) {
154
+ var index = stack.panelIndex( panel )
155
+
156
+ if ( index == stack.current ) return 'none'
157
+ else return ( stack.current < index ) ? 'forward' : 'reverse'
158
+
159
+ },
160
+
161
+ currentPanel: function ( name ) {
162
+ if ( name ) {
163
+ return stack.panels[ stack.current ] == stack.findPanel( name )
164
+ }
165
+ return stack.panels[ stack.current ]
166
+ },
167
+
168
+ panelIndex: function ( panel ) {
169
+ return Number( panel.dataset.stackIndex )
170
+ }
171
+ }
172
+
173
+ // Add a queryable stack id
174
+ root.dataset.stackId = Stacks.length
175
+
176
+ Stacks.push( stack )
177
+ toolbox.each( root.children, stack.add )
178
+
179
+ return stack
180
+ }
181
+ }
182
+
183
+ function showEl ( el ) {
184
+ el.setAttribute( 'aria-hidden', false )
185
+ }
186
+
187
+ function hideEl ( el ) {
188
+ el.setAttribute( 'aria-hidden', true )
189
+ }
190
+
191
+ function isElement ( item ) {
192
+ return item.constructor.toString().search(/HTML.+Element/) > -1
193
+ }
194
+
195
+ function navClick ( event ) {
196
+ var el = event.currentTarget
197
+ var panel = el.dataset.stackNav
198
+ var stack = getStack( toolbox.getClosest( el, '[data-stack="root"]' ) )
199
+ if ( !stack ) return
200
+
201
+ if ( panel == 'next' ) stack.next()
202
+ else if ( panel == 'back' ) stack.back()
203
+ else if ( stack.findPanel( panel ) ) stack.show( panel )
204
+ else return
205
+
206
+ //event.preventDefault()
207
+ }
208
+
209
+ function prevPanel ( event ) {
210
+ var stack = getStack( toolbox.getClosest( event.target, '[data-stack="root"]' ) )
211
+ stack.back()
212
+ event.preventDefault()
213
+ }
214
+
215
+ function setup () {
216
+ Event.change( function() {
217
+ toolbox.each( document.querySelectorAll( '[data-stack="root"]'), Stack.new )
218
+ })
219
+
220
+ // Localise clicks to a stack root
221
+ // Future: consider expanding this to allow controls to live anywhere and point to a stack
222
+ Event.on( document, 'click', '[data-stack="root"] [data-stack-nav]', navClick )
223
+ }
224
+
225
+ function getStack ( search ) {
226
+
227
+ if ( isElement( search ) ) {
228
+ if ( search.dataset.stackId ) return Stacks[ search.dataset.stackId ]
229
+ }
230
+
231
+ if ( typeof search === 'string' ) {
232
+
233
+ // Well, let's hope it's a DOM selector
234
+ var stack = document.querySelector( search )
235
+
236
+ // If it is woo hoo
237
+ if ( stack && stack.dataset.stackId ) {
238
+ return Stacks[ stack.dataset.stackId ]
239
+ }
240
+ }
241
+ }
242
+
243
+ module.exports = {
244
+ new: Stack.new,
245
+ setup: setup,
246
+ get: getStack,
247
+ stacks: Stacks
248
+ }
@@ -0,0 +1,68 @@
1
+ // Purpose: Toggles expansion of tree nav and stores cookies to retain state
2
+ //
3
+ // Usage:
4
+ // - Trees must have a [data-tree='tree-key']
5
+ // - Nodes are named by [data-node='node-name']
6
+ //
7
+ // Cookies are stored as tree-key => { node-name: true }
8
+ // node boolean matches aria-expanded state
9
+ //
10
+
11
+ var toolbox = require( 'compose-toolbox' ),
12
+ cookie = require( './_cookie' ),
13
+ Event = toolbox.event
14
+
15
+ function setCookie ( node ) {
16
+ if ( node.dataset.node ) {
17
+ var tree = toolbox.getClosest( node, '[data-tree]' )
18
+ if ( tree ) {
19
+ data = {}
20
+ data[ node.dataset.node ] = node.getAttribute( 'aria-expanded' ) == 'true'
21
+ cookie.update( tree.dataset.tree, data )
22
+ }
23
+ }
24
+ }
25
+
26
+ function click ( event ) {
27
+ var target = event.currentTarget,
28
+ parent = target.parentElement,
29
+ expanded = parent.getAttribute( 'aria-expanded' )
30
+
31
+ // Add a classname to indicate that it was manually triggered
32
+ // This allows for animating interactions, but not other attribute changes
33
+ parent.classList.add( 'triggered' )
34
+ parent.setAttribute( 'aria-expanded', expanded != 'true' )
35
+
36
+ setCookie( parent )
37
+ }
38
+
39
+ // Sets tree nav state based off of cookies
40
+ function restoreNav () {
41
+ toolbox.each( document.querySelectorAll( '[data-tree]' ), function( tree ) {
42
+ var key = tree.dataset.tree
43
+ if ( key ) {
44
+ var data = cookie.get( key )
45
+
46
+ Object.keys( data ).forEach( function( node ) {
47
+ var nodeEl = tree.querySelector( '[data-node='+node+']' )
48
+ // Use cookies to expand.
49
+ if ( nodeEl && data[node] == 'true' ) nodeEl.setAttribute( 'aria-expanded', data[node] )
50
+ })
51
+ }
52
+ })
53
+ }
54
+
55
+ function setup() {
56
+ Event.ready( function() {
57
+
58
+ // The first child of a node is the trigger
59
+ Event.on( document, 'click', '[aria-expanded] > *:first-child', click )
60
+
61
+ // Set states according to cookies
62
+ //restoreNav()
63
+ })
64
+ }
65
+
66
+ module.exports = {
67
+ setup: setup
68
+ }
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Add dataset support to elements
3
+ * No globals, no overriding prototype with non-standard methods,
4
+ * handles CamelCase properly, attempts to use standard
5
+ * Object.defineProperty() (and Function bind()) methods,
6
+ * falls back to native implementation when existing
7
+ * Inspired by http://code.eligrey.com/html5/dataset/
8
+ * (via https://github.com/adalgiso/html5-dataset/blob/master/html5-dataset.js )
9
+ * Depends on Function.bind and Object.defineProperty/Object.getOwnPropertyDescriptor (shims below)
10
+ * Licensed under the X11/MIT License
11
+ */
12
+
13
+ // Inspired by https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
14
+ if (!Function.prototype.bind) {
15
+ Function.prototype.bind = function (oThis) {
16
+ 'use strict';
17
+ if (typeof this !== "function") {
18
+ // closest thing possible to the ECMAScript 5 internal IsCallable function
19
+ throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
20
+ }
21
+
22
+ var aArgs = Array.prototype.slice.call(arguments, 1),
23
+ fToBind = this,
24
+ FNOP = function () {},
25
+ fBound = function () {
26
+ return fToBind.apply(
27
+ this instanceof FNOP && oThis ? this : oThis,
28
+ aArgs.concat(Array.prototype.slice.call(arguments))
29
+ );
30
+ };
31
+
32
+ FNOP.prototype = this.prototype;
33
+ fBound.prototype = new FNOP();
34
+
35
+ return fBound;
36
+ };
37
+ }
38
+
39
+ /*
40
+ * Xccessors Standard: Cross-browser ECMAScript 5 accessors
41
+ * http://purl.eligrey.com/github/Xccessors
42
+ *
43
+ * 2010-06-21
44
+ *
45
+ * By Eli Grey, http://eligrey.com
46
+ *
47
+ * A shim that partially implements Object.defineProperty,
48
+ * Object.getOwnPropertyDescriptor, and Object.defineProperties in browsers that have
49
+ * legacy __(define|lookup)[GS]etter__ support.
50
+ *
51
+ * Licensed under the X11/MIT License
52
+ * See LICENSE.md
53
+ */
54
+
55
+ // Removed a few JSLint options as Notepad++ JSLint validator complaining and
56
+ // made comply with JSLint; also moved 'use strict' inside function
57
+ /*jslint white: true, undef: true, plusplus: true,
58
+ bitwise: true, regexp: true, newcap: true, maxlen: 90 */
59
+
60
+ /*! @source http://purl.eligrey.com/github/Xccessors/blob/master/xccessors-standard.js*/
61
+
62
+ (function () {
63
+ 'use strict';
64
+ var ObjectProto = Object.prototype,
65
+ defineGetter = ObjectProto.__defineGetter__,
66
+ defineSetter = ObjectProto.__defineSetter__,
67
+ lookupGetter = ObjectProto.__lookupGetter__,
68
+ lookupSetter = ObjectProto.__lookupSetter__,
69
+ hasOwnProp = ObjectProto.hasOwnProperty;
70
+
71
+ if (defineGetter && defineSetter && lookupGetter && lookupSetter) {
72
+
73
+ if (!Object.defineProperty) {
74
+ Object.defineProperty = function (obj, prop, descriptor) {
75
+ if (arguments.length < 3) { // all arguments required
76
+ throw new TypeError("Arguments not optional");
77
+ }
78
+
79
+ prop += ""; // convert prop to string
80
+
81
+ if (hasOwnProp.call(descriptor, "value")) {
82
+ if (!lookupGetter.call(obj, prop) && !lookupSetter.call(obj, prop)) {
83
+ // data property defined and no pre-existing accessors
84
+ obj[prop] = descriptor.value;
85
+ }
86
+
87
+ if ((hasOwnProp.call(descriptor, "get") ||
88
+ hasOwnProp.call(descriptor, "set")))
89
+ {
90
+ // descriptor has a value prop but accessor already exists
91
+ throw new TypeError("Cannot specify an accessor and a value");
92
+ }
93
+ }
94
+
95
+ // can't switch off these features in ECMAScript 3
96
+ // so throw a TypeError if any are false
97
+ if (!(descriptor.writable && descriptor.enumerable &&
98
+ descriptor.configurable))
99
+ {
100
+ throw new TypeError(
101
+ "This implementation of Object.defineProperty does not support" +
102
+ " false for configurable, enumerable, or writable."
103
+ );
104
+ }
105
+
106
+ if (descriptor.get) {
107
+ defineGetter.call(obj, prop, descriptor.get);
108
+ }
109
+ if (descriptor.set) {
110
+ defineSetter.call(obj, prop, descriptor.set);
111
+ }
112
+
113
+ return obj;
114
+ };
115
+ }
116
+
117
+ if (!Object.getOwnPropertyDescriptor) {
118
+ Object.getOwnPropertyDescriptor = function (obj, prop) {
119
+ if (arguments.length < 2) { // all arguments required
120
+ throw new TypeError("Arguments not optional.");
121
+ }
122
+
123
+ prop += ""; // convert prop to string
124
+
125
+ var descriptor = {
126
+ configurable: true,
127
+ enumerable : true,
128
+ writable : true
129
+ },
130
+ getter = lookupGetter.call(obj, prop),
131
+ setter = lookupSetter.call(obj, prop);
132
+
133
+ if (!hasOwnProp.call(obj, prop)) {
134
+ // property doesn't exist or is inherited
135
+ return descriptor;
136
+ }
137
+ if (!getter && !setter) { // not an accessor so return prop
138
+ descriptor.value = obj[prop];
139
+ return descriptor;
140
+ }
141
+
142
+ // there is an accessor, remove descriptor.writable;
143
+ // populate descriptor.get and descriptor.set (IE's behavior)
144
+ delete descriptor.writable;
145
+ descriptor.get = descriptor.set = undefined;
146
+
147
+ if (getter) {
148
+ descriptor.get = getter;
149
+ }
150
+ if (setter) {
151
+ descriptor.set = setter;
152
+ }
153
+
154
+ return descriptor;
155
+ };
156
+ }
157
+
158
+ if (!Object.defineProperties) {
159
+ Object.defineProperties = function (obj, props) {
160
+ var prop;
161
+ for (prop in props) {
162
+ if (hasOwnProp.call(props, prop)) {
163
+ Object.defineProperty(obj, prop, props[prop]);
164
+ }
165
+ }
166
+ };
167
+ }
168
+ }
169
+ }());
170
+
171
+ // Begin dataset code
172
+
173
+ if (!document.documentElement.dataset &&
174
+ // FF is empty while IE gives empty object
175
+ (!Object.getOwnPropertyDescriptor(Element.prototype, 'dataset') ||
176
+ !Object.getOwnPropertyDescriptor(Element.prototype, 'dataset').get)
177
+ ) {
178
+ var propDescriptor = {
179
+ enumerable: true,
180
+ get: function () {
181
+ 'use strict';
182
+ var i,
183
+ that = this,
184
+ HTML5_DOMStringMap,
185
+ attrVal, attrName, propName,
186
+ attribute,
187
+ attributes = this.attributes,
188
+ attsLength = attributes.length,
189
+ toUpperCase = function (n0) {
190
+ return n0.charAt(1).toUpperCase();
191
+ },
192
+ getter = function () {
193
+ return this;
194
+ },
195
+ setter = function (attrName, value) {
196
+ return (typeof value !== 'undefined') ?
197
+ this.setAttribute(attrName, value) :
198
+ this.removeAttribute(attrName);
199
+ };
200
+ try { // Simulate DOMStringMap w/accessor support
201
+ // Test setting accessor on normal object
202
+ ({}).__defineGetter__('test', function () {});
203
+ HTML5_DOMStringMap = {};
204
+ }
205
+ catch (e1) { // Use a DOM object for IE8
206
+ HTML5_DOMStringMap = document.createElement('div');
207
+ }
208
+ for (i = 0; i < attsLength; i++) {
209
+ attribute = attributes[i];
210
+ // Fix: This test really should allow any XML Name without
211
+ // colons (and non-uppercase for XHTML)
212
+ if (attribute && attribute.name &&
213
+ (/^data-\w[\w\-]*$/).test(attribute.name)) {
214
+ attrVal = attribute.value;
215
+ attrName = attribute.name;
216
+ // Change to CamelCase
217
+ propName = attrName.substr(5).replace(/-./g, toUpperCase);
218
+ try {
219
+ Object.defineProperty(HTML5_DOMStringMap, propName, {
220
+ enumerable: this.enumerable,
221
+ get: getter.bind(attrVal || ''),
222
+ set: setter.bind(that, attrName)
223
+ });
224
+ }
225
+ catch (e2) { // if accessors are not working
226
+ HTML5_DOMStringMap[propName] = attrVal;
227
+ }
228
+ }
229
+ }
230
+ return HTML5_DOMStringMap;
231
+ }
232
+ };
233
+ try {
234
+ // FF enumerates over element's dataset, but not
235
+ // Element.prototype.dataset; IE9 iterates over both
236
+ Object.defineProperty(Element.prototype, 'dataset', propDescriptor);
237
+ } catch (e) {
238
+ propDescriptor.enumerable = false; // IE8 does not allow setting to true
239
+ Object.defineProperty(Element.prototype, 'dataset', propDescriptor);
240
+ }
241
+ }