fleetio_spark 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/app/assets/javascripts/spark/_cookie.js +23 -0
- data/app/assets/javascripts/spark/_esvg.js +73 -0
- data/app/assets/javascripts/spark/_icons.js +19 -0
- data/app/assets/javascripts/spark/_modal.js +421 -0
- data/app/assets/javascripts/spark/_search.js +79 -0
- data/app/assets/javascripts/spark/_sidebar.js +13 -0
- data/app/assets/javascripts/spark/_stack.js +248 -0
- data/app/assets/javascripts/spark/_tree.js +68 -0
- data/app/assets/javascripts/spark/shims/_dataset.js +241 -0
- data/app/assets/javascripts/spark/spark.js +31 -0
- data/app/assets/stylesheets/spark/_icons.scss +15 -0
- data/app/assets/stylesheets/spark/_index.scss +5 -0
- data/app/assets/stylesheets/spark/components/_index.scss +6 -0
- data/app/assets/stylesheets/spark/components/_modal.scss +73 -0
- data/app/assets/stylesheets/spark/components/_nav-menu.scss +102 -0
- data/app/assets/stylesheets/spark/components/_sidebar.scss +280 -0
- data/app/assets/stylesheets/spark/components/_tooltip.scss +42 -0
- data/app/assets/stylesheets/spark/components/_tree-nav.scss +13 -0
- data/app/assets/stylesheets/spark/components/header/_app-admin-header.scss +82 -0
- data/app/assets/stylesheets/spark/components/header/_app-nav-header.scss +154 -0
- data/app/assets/stylesheets/spark/components/header/_index.scss +5 -0
- data/app/assets/stylesheets/spark/components/header/_search-results.scss +29 -0
- data/app/assets/stylesheets/spark/components/header/_search.scss +195 -0
- data/app/assets/stylesheets/spark/components/header/_trial-status.scss +43 -0
- data/app/assets/stylesheets/spark/core/_animations.scss +154 -0
- data/app/assets/stylesheets/spark/core/_base.scss +11 -0
- data/app/assets/stylesheets/spark/core/_colors.scss +94 -0
- data/app/assets/stylesheets/spark/core/_index.scss +7 -0
- data/app/assets/stylesheets/spark/core/_layout.scss +39 -0
- data/app/assets/stylesheets/spark/core/_mixins.scss +86 -0
- data/app/assets/stylesheets/spark/core/_text.scss +5 -0
- data/app/assets/stylesheets/spark/core/_vars.scss +76 -0
- data/app/assets/stylesheets/spark/form/_base.scss +124 -0
- data/app/assets/stylesheets/spark/form/_check-switch.scss +99 -0
- data/app/assets/stylesheets/spark/form/_index.scss +2 -0
- data/app/assets/stylesheets/spark/spark.scss +1 -0
- data/app/assets/svgs/spark/add.svg +3 -0
- data/app/assets/svgs/spark/admin-user.svg +5 -0
- data/app/assets/svgs/spark/chevron-down.svg +3 -0
- data/app/assets/svgs/spark/chevron-up.svg +3 -0
- data/app/assets/svgs/spark/close.svg +3 -0
- data/app/assets/svgs/spark/contact.svg +6 -0
- data/app/assets/svgs/spark/dashboard.svg +3 -0
- data/app/assets/svgs/spark/fuel.svg +3 -0
- data/app/assets/svgs/spark/inspection.svg +3 -0
- data/app/assets/svgs/spark/issue.svg +3 -0
- data/app/assets/svgs/spark/leaderboard.svg +5 -0
- data/app/assets/svgs/spark/logo.svg +1 -0
- data/app/assets/svgs/spark/map.svg +4 -0
- data/app/assets/svgs/spark/nav-menu.svg +5 -0
- data/app/assets/svgs/spark/part.svg +6 -0
- data/app/assets/svgs/spark/place.svg +5 -0
- data/app/assets/svgs/spark/question.svg +10 -0
- data/app/assets/svgs/spark/reminder.svg +3 -0
- data/app/assets/svgs/spark/report.svg +5 -0
- data/app/assets/svgs/spark/search.svg +3 -0
- data/app/assets/svgs/spark/service.svg +3 -0
- data/app/assets/svgs/spark/settings.svg +3 -0
- data/app/assets/svgs/spark/trip.svg +4 -0
- data/app/assets/svgs/spark/vehicle.svg +3 -0
- data/app/assets/svgs/spark/vendor.svg +4 -0
- data/app/helpers/spark/application_helper.rb +42 -0
- data/app/helpers/spark/icon_helper.rb +40 -0
- data/app/helpers/spark/input_helper.rb +163 -0
- data/app/helpers/spark/menu_helper.rb +113 -0
- data/app/helpers/spark/modal_helper.rb +52 -0
- data/app/helpers/spark/nav_menu_helper.rb +167 -0
- data/app/helpers/spark/tag_helper.rb +27 -0
- data/app/helpers/spark/trial_helper.rb +23 -0
- data/app/views/layouts/spark/application.html.slim +28 -0
- data/app/views/layouts/spark/blank.html.slim +9 -0
- data/app/views/layouts/spark/default.html.slim +13 -0
- data/config/autoprefixer.yml +4 -0
- data/config/esvg.yml +20 -0
- data/lib/fleetio_spark.rb +14 -0
- data/lib/fleetio_spark/helper.rb +163 -0
- data/lib/fleetio_spark/version.rb +3 -0
- data/public/spark-0.1.0.css +1442 -0
- data/public/spark-0.1.0.css.gz +0 -0
- data/public/spark-0.1.0.js +11927 -0
- data/public/spark-0.1.0.js.gz +0 -0
- 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
|
+
}
|