ladda-rails 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +19 -0
- data/README.md +28 -0
- data/ladda-rails.gemspec +19 -0
- data/lib/ladda-rails.rb +6 -0
- data/vendor/assets/javascripts/ladda.jquery.js +46 -0
- data/vendor/assets/javascripts/ladda.js +423 -0
- data/vendor/assets/javascripts/spin.js +349 -0
- data/vendor/assets/stylesheets/ladda-themed.scss +81 -0
- data/vendor/assets/stylesheets/ladda.scss +486 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
N2U2NmJmNGJmYzdjYjc5YTczNDI5MTU0ZDAxOTk3MmVjYmQxMGM2Mw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YTQyOWRhMDBhMTMzNzc4YzNhZTY0YTA5MTU2NWM5ZWZkYWU2NjJhMg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ODdlODI1ODE1MGU2MWY5NGFkM2RhNmZhYzBkOTQxYzBjMzVkNWI3NmI4Mzkx
|
10
|
+
ZWU0ZjQ0ZTdiMDRkYmNhNzdkMGU2ZTQ1YzY4ZGIyOTljMjgyZWMzYjMxMDMz
|
11
|
+
ODU2YjU3OWM3NDMyY2Y0ZTZhOGI1OTIyZGQ0ZWNiYjJlZjFlNDk=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MDdiYTcwYzI2MzVmNmRhNmUzY2Q1OWViMjkyYTIzMWQ2MjE3NmYzYzM1NWUw
|
14
|
+
MWY2YzBiYWY0ZWUyNGUxN2Y0ZTMyZmMyMjhmZmJmODZlZjYyNTQ2MjI4MzZm
|
15
|
+
ZTE1ZmIxZjQ5MjZlMGMyYTRiMWM5NDhhZDA4ODcxOTNjNDQ1ZGQ=
|
data/.gitignore
ADDED
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
ladda-rails
|
2
|
+
===========
|
3
|
+
|
4
|
+
A thin wrapper around https://github.com/hakimel/Ladda .
|
5
|
+
|
6
|
+
If you copy files from the interwebs into your project, kittens die.
|
7
|
+
|
8
|
+
Do this instead (in your Gemfile):
|
9
|
+
|
10
|
+
gem "ladda-rails"
|
11
|
+
|
12
|
+
And, in your application.js and application.css:
|
13
|
+
|
14
|
+
//= require "spin"
|
15
|
+
//= require "ladda"
|
16
|
+
//= require "ladda.jquery" // only when you use jQuery already, see Ladda docs for usage
|
17
|
+
|
18
|
+
*= require ladda
|
19
|
+
*= require ladda-themed // this is optional - when you leave this out, it's equivalent to the "ladda-themeless.min.css from the Ladda project
|
20
|
+
|
21
|
+
|
22
|
+
====================================================================
|
23
|
+
|
24
|
+
original source https://github.com/Promptus/ladda-rails
|
25
|
+
|
26
|
+
I have forked that cause I am in a hurry to build a gem on rubygems.org
|
27
|
+
|
28
|
+
And I have updated the ladda version to 0.9.7
|
data/ladda-rails.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "ladda-rails"
|
5
|
+
s.version = "0.9.7"
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ["Martin Tepper"]
|
8
|
+
s.email = ["mt@promptus-partners.de"]
|
9
|
+
s.homepage = "https://github.com/Promptus/ladda-rails"
|
10
|
+
s.summary = "Use Ladda with Rails 3 and 4"
|
11
|
+
s.description = "Ladda CSS and JS in a convenient fashion."
|
12
|
+
s.license = "MIT"
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
18
|
+
s.require_path = 'lib'
|
19
|
+
end
|
data/lib/ladda-rails.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
/*!
|
2
|
+
* Ladda for jQuery
|
3
|
+
* http://lab.hakim.se/ladda
|
4
|
+
* MIT licensed
|
5
|
+
*
|
6
|
+
* Copyright (C) 2015 Hakim El Hattab, http://hakim.se
|
7
|
+
*/
|
8
|
+
|
9
|
+
(function( Ladda, $ ) {
|
10
|
+
if ($ === undefined)
|
11
|
+
return console.error( 'jQuery required for Ladda.jQuery' );
|
12
|
+
|
13
|
+
var arr = [];
|
14
|
+
|
15
|
+
$ = $.extend( $, {
|
16
|
+
ladda: function( arg ) {
|
17
|
+
if( arg === 'stopAll' )
|
18
|
+
Ladda.stopAll();
|
19
|
+
}
|
20
|
+
});
|
21
|
+
|
22
|
+
$.fn = $.extend( $.fn, {
|
23
|
+
ladda: function( arg ) {
|
24
|
+
var args = arr.slice.call( arguments, 1 );
|
25
|
+
|
26
|
+
if( arg === 'bind' ) {
|
27
|
+
args.unshift( $( this ).selector );
|
28
|
+
Ladda.bind.apply( Ladda, args );
|
29
|
+
}
|
30
|
+
else {
|
31
|
+
$( this ).each( function() {
|
32
|
+
var $this = $( this ), ladda;
|
33
|
+
|
34
|
+
if( arg === undefined )
|
35
|
+
$this.data( 'ladda', Ladda.create( this ) );
|
36
|
+
else {
|
37
|
+
ladda = $this.data( 'ladda' );
|
38
|
+
ladda[arg].apply( ladda, args );
|
39
|
+
}
|
40
|
+
});
|
41
|
+
}
|
42
|
+
|
43
|
+
return this;
|
44
|
+
}
|
45
|
+
});
|
46
|
+
}( this.Ladda, this.jQuery ));
|
@@ -0,0 +1,423 @@
|
|
1
|
+
/*!
|
2
|
+
* Ladda
|
3
|
+
* http://lab.hakim.se/ladda
|
4
|
+
* MIT licensed
|
5
|
+
*
|
6
|
+
* Copyright (C) 2015 Hakim El Hattab, http://hakim.se
|
7
|
+
*/
|
8
|
+
/* jshint node:true, browser:true */
|
9
|
+
(function( root, factory ) {
|
10
|
+
|
11
|
+
// CommonJS
|
12
|
+
if( typeof exports === 'object' ) {
|
13
|
+
module.exports = factory(require('spin.js'));
|
14
|
+
}
|
15
|
+
// AMD module
|
16
|
+
else if( typeof define === 'function' && define.amd ) {
|
17
|
+
define( [ 'spin' ], factory );
|
18
|
+
}
|
19
|
+
// Browser global
|
20
|
+
else {
|
21
|
+
root.Ladda = factory( root.Spinner );
|
22
|
+
}
|
23
|
+
|
24
|
+
}
|
25
|
+
(this, function( Spinner ) {
|
26
|
+
'use strict';
|
27
|
+
|
28
|
+
// All currently instantiated instances of Ladda
|
29
|
+
var ALL_INSTANCES = [];
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Creates a new instance of Ladda which wraps the
|
33
|
+
* target button element.
|
34
|
+
*
|
35
|
+
* @return An API object that can be used to control
|
36
|
+
* the loading animation state.
|
37
|
+
*/
|
38
|
+
function create( button ) {
|
39
|
+
|
40
|
+
if( typeof button === 'undefined' ) {
|
41
|
+
console.warn( "Ladda button target must be defined." );
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
|
45
|
+
// The text contents must be wrapped in a ladda-label
|
46
|
+
// element, create one if it doesn't already exist
|
47
|
+
if( !button.querySelector( '.ladda-label' ) ) {
|
48
|
+
button.innerHTML = '<span class="ladda-label">'+ button.innerHTML +'</span>';
|
49
|
+
}
|
50
|
+
|
51
|
+
// The spinner component
|
52
|
+
var spinner,
|
53
|
+
spinnerWrapper = button.querySelector( '.ladda-spinner' );
|
54
|
+
|
55
|
+
// Wrapper element for the spinner
|
56
|
+
if( !spinnerWrapper ) {
|
57
|
+
spinnerWrapper = document.createElement( 'span' );
|
58
|
+
spinnerWrapper.className = 'ladda-spinner';
|
59
|
+
}
|
60
|
+
|
61
|
+
button.appendChild( spinnerWrapper );
|
62
|
+
|
63
|
+
// Timer used to delay starting/stopping
|
64
|
+
var timer;
|
65
|
+
|
66
|
+
var instance = {
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Enter the loading state.
|
70
|
+
*/
|
71
|
+
start: function() {
|
72
|
+
|
73
|
+
// Create the spinner if it doesn't already exist
|
74
|
+
if( !spinner ) spinner = createSpinner( button );
|
75
|
+
|
76
|
+
button.setAttribute( 'disabled', '' );
|
77
|
+
button.setAttribute( 'data-loading', '' );
|
78
|
+
|
79
|
+
clearTimeout( timer );
|
80
|
+
spinner.spin( spinnerWrapper );
|
81
|
+
|
82
|
+
this.setProgress( 0 );
|
83
|
+
|
84
|
+
return this; // chain
|
85
|
+
|
86
|
+
},
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Enter the loading state, after a delay.
|
90
|
+
*/
|
91
|
+
startAfter: function( delay ) {
|
92
|
+
|
93
|
+
clearTimeout( timer );
|
94
|
+
timer = setTimeout( function() { instance.start(); }, delay );
|
95
|
+
|
96
|
+
return this; // chain
|
97
|
+
|
98
|
+
},
|
99
|
+
|
100
|
+
/**
|
101
|
+
* Exit the loading state.
|
102
|
+
*/
|
103
|
+
stop: function() {
|
104
|
+
|
105
|
+
button.removeAttribute( 'disabled' );
|
106
|
+
button.removeAttribute( 'data-loading' );
|
107
|
+
|
108
|
+
// Kill the animation after a delay to make sure it
|
109
|
+
// runs for the duration of the button transition
|
110
|
+
clearTimeout( timer );
|
111
|
+
|
112
|
+
if( spinner ) {
|
113
|
+
timer = setTimeout( function() { spinner.stop(); }, 1000 );
|
114
|
+
}
|
115
|
+
|
116
|
+
return this; // chain
|
117
|
+
|
118
|
+
},
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Toggle the loading state on/off.
|
122
|
+
*/
|
123
|
+
toggle: function() {
|
124
|
+
|
125
|
+
if( this.isLoading() ) {
|
126
|
+
this.stop();
|
127
|
+
}
|
128
|
+
else {
|
129
|
+
this.start();
|
130
|
+
}
|
131
|
+
|
132
|
+
return this; // chain
|
133
|
+
|
134
|
+
},
|
135
|
+
|
136
|
+
/**
|
137
|
+
* Sets the width of the visual progress bar inside of
|
138
|
+
* this Ladda button
|
139
|
+
*
|
140
|
+
* @param {Number} progress in the range of 0-1
|
141
|
+
*/
|
142
|
+
setProgress: function( progress ) {
|
143
|
+
|
144
|
+
// Cap it
|
145
|
+
progress = Math.max( Math.min( progress, 1 ), 0 );
|
146
|
+
|
147
|
+
var progressElement = button.querySelector( '.ladda-progress' );
|
148
|
+
|
149
|
+
// Remove the progress bar if we're at 0 progress
|
150
|
+
if( progress === 0 && progressElement && progressElement.parentNode ) {
|
151
|
+
progressElement.parentNode.removeChild( progressElement );
|
152
|
+
}
|
153
|
+
else {
|
154
|
+
if( !progressElement ) {
|
155
|
+
progressElement = document.createElement( 'div' );
|
156
|
+
progressElement.className = 'ladda-progress';
|
157
|
+
button.appendChild( progressElement );
|
158
|
+
}
|
159
|
+
|
160
|
+
progressElement.style.width = ( ( progress || 0 ) * button.offsetWidth ) + 'px';
|
161
|
+
}
|
162
|
+
|
163
|
+
},
|
164
|
+
|
165
|
+
enable: function() {
|
166
|
+
|
167
|
+
this.stop();
|
168
|
+
|
169
|
+
return this; // chain
|
170
|
+
|
171
|
+
},
|
172
|
+
|
173
|
+
disable: function () {
|
174
|
+
|
175
|
+
this.stop();
|
176
|
+
button.setAttribute( 'disabled', '' );
|
177
|
+
|
178
|
+
return this; // chain
|
179
|
+
|
180
|
+
},
|
181
|
+
|
182
|
+
isLoading: function() {
|
183
|
+
|
184
|
+
return button.hasAttribute( 'data-loading' );
|
185
|
+
|
186
|
+
},
|
187
|
+
|
188
|
+
remove: function() {
|
189
|
+
|
190
|
+
clearTimeout( timer );
|
191
|
+
|
192
|
+
button.removeAttribute( 'disabled', '' );
|
193
|
+
button.removeAttribute( 'data-loading', '' );
|
194
|
+
|
195
|
+
if( spinner ) {
|
196
|
+
spinner.stop();
|
197
|
+
spinner = null;
|
198
|
+
}
|
199
|
+
|
200
|
+
for( var i = 0, len = ALL_INSTANCES.length; i < len; i++ ) {
|
201
|
+
if( instance === ALL_INSTANCES[i] ) {
|
202
|
+
ALL_INSTANCES.splice( i, 1 );
|
203
|
+
break;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
}
|
208
|
+
|
209
|
+
};
|
210
|
+
|
211
|
+
ALL_INSTANCES.push( instance );
|
212
|
+
|
213
|
+
return instance;
|
214
|
+
|
215
|
+
}
|
216
|
+
|
217
|
+
/**
|
218
|
+
* Get the first ancestor node from an element, having a
|
219
|
+
* certain type.
|
220
|
+
*
|
221
|
+
* @param elem An HTML element
|
222
|
+
* @param type an HTML tag type (uppercased)
|
223
|
+
*
|
224
|
+
* @return An HTML element
|
225
|
+
*/
|
226
|
+
function getAncestorOfTagType( elem, type ) {
|
227
|
+
|
228
|
+
while ( elem.parentNode && elem.tagName !== type ) {
|
229
|
+
elem = elem.parentNode;
|
230
|
+
}
|
231
|
+
|
232
|
+
return ( type === elem.tagName ) ? elem : undefined;
|
233
|
+
|
234
|
+
}
|
235
|
+
|
236
|
+
/**
|
237
|
+
* Returns a list of all inputs in the given form that
|
238
|
+
* have their `required` attribute set.
|
239
|
+
*
|
240
|
+
* @param form The from HTML element to look in
|
241
|
+
*
|
242
|
+
* @return A list of elements
|
243
|
+
*/
|
244
|
+
function getRequiredFields( form ) {
|
245
|
+
|
246
|
+
var requirables = [ 'input', 'textarea', 'select' ];
|
247
|
+
var inputs = [];
|
248
|
+
|
249
|
+
for( var i = 0; i < requirables.length; i++ ) {
|
250
|
+
var candidates = form.getElementsByTagName( requirables[i] );
|
251
|
+
for( var j = 0; j < candidates.length; j++ ) {
|
252
|
+
if ( candidates[j].hasAttribute( 'required' ) ) {
|
253
|
+
inputs.push( candidates[j] );
|
254
|
+
}
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
return inputs;
|
259
|
+
|
260
|
+
}
|
261
|
+
|
262
|
+
|
263
|
+
/**
|
264
|
+
* Binds the target buttons to automatically enter the
|
265
|
+
* loading state when clicked.
|
266
|
+
*
|
267
|
+
* @param target Either an HTML element or a CSS selector.
|
268
|
+
* @param options
|
269
|
+
* - timeout Number of milliseconds to wait before
|
270
|
+
* automatically cancelling the animation.
|
271
|
+
*/
|
272
|
+
function bind( target, options ) {
|
273
|
+
|
274
|
+
options = options || {};
|
275
|
+
|
276
|
+
var targets = [];
|
277
|
+
|
278
|
+
if( typeof target === 'string' ) {
|
279
|
+
targets = toArray( document.querySelectorAll( target ) );
|
280
|
+
}
|
281
|
+
else if( typeof target === 'object' && typeof target.nodeName === 'string' ) {
|
282
|
+
targets = [ target ];
|
283
|
+
}
|
284
|
+
|
285
|
+
for( var i = 0, len = targets.length; i < len; i++ ) {
|
286
|
+
|
287
|
+
(function() {
|
288
|
+
var element = targets[i];
|
289
|
+
|
290
|
+
// Make sure we're working with a DOM element
|
291
|
+
if( typeof element.addEventListener === 'function' ) {
|
292
|
+
var instance = create( element );
|
293
|
+
var timeout = -1;
|
294
|
+
|
295
|
+
element.addEventListener( 'click', function( event ) {
|
296
|
+
|
297
|
+
// If the button belongs to a form, make sure all the
|
298
|
+
// fields in that form are filled out
|
299
|
+
var valid = true;
|
300
|
+
var form = getAncestorOfTagType( element, 'FORM' );
|
301
|
+
|
302
|
+
if( typeof form !== 'undefined' ) {
|
303
|
+
var requireds = getRequiredFields( form );
|
304
|
+
for( var i = 0; i < requireds.length; i++ ) {
|
305
|
+
|
306
|
+
// Alternatively to this trim() check,
|
307
|
+
// we could have use .checkValidity() or .validity.valid
|
308
|
+
if( requireds[i].value.replace( /^\s+|\s+$/g, '' ) === '' ) {
|
309
|
+
valid = false;
|
310
|
+
}
|
311
|
+
|
312
|
+
// Radiobuttons and Checkboxes need to be checked for the "checked" attribute
|
313
|
+
if( (requireds[i].type === 'checkbox' || requireds[i].type === 'radio' ) && !requireds[i].checked ) {
|
314
|
+
valid = false;
|
315
|
+
}
|
316
|
+
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
320
|
+
if( valid ) {
|
321
|
+
// This is asynchronous to avoid an issue where setting
|
322
|
+
// the disabled attribute on the button prevents forms
|
323
|
+
// from submitting
|
324
|
+
instance.startAfter( 1 );
|
325
|
+
|
326
|
+
// Set a loading timeout if one is specified
|
327
|
+
if( typeof options.timeout === 'number' ) {
|
328
|
+
clearTimeout( timeout );
|
329
|
+
timeout = setTimeout( instance.stop, options.timeout );
|
330
|
+
}
|
331
|
+
|
332
|
+
// Invoke callbacks
|
333
|
+
if( typeof options.callback === 'function' ) {
|
334
|
+
options.callback.apply( null, [ instance ] );
|
335
|
+
}
|
336
|
+
}
|
337
|
+
|
338
|
+
}, false );
|
339
|
+
}
|
340
|
+
})();
|
341
|
+
|
342
|
+
}
|
343
|
+
|
344
|
+
}
|
345
|
+
|
346
|
+
/**
|
347
|
+
* Stops ALL current loading animations.
|
348
|
+
*/
|
349
|
+
function stopAll() {
|
350
|
+
|
351
|
+
for( var i = 0, len = ALL_INSTANCES.length; i < len; i++ ) {
|
352
|
+
ALL_INSTANCES[i].stop();
|
353
|
+
}
|
354
|
+
|
355
|
+
}
|
356
|
+
|
357
|
+
function createSpinner( button ) {
|
358
|
+
|
359
|
+
var height = button.offsetHeight,
|
360
|
+
spinnerColor;
|
361
|
+
|
362
|
+
if( height === 0 ) {
|
363
|
+
// We may have an element that is not visible so
|
364
|
+
// we attempt to get the height in a different way
|
365
|
+
height = parseFloat( window.getComputedStyle( button ).height );
|
366
|
+
}
|
367
|
+
|
368
|
+
// If the button is tall we can afford some padding
|
369
|
+
if( height > 32 ) {
|
370
|
+
height *= 0.8;
|
371
|
+
}
|
372
|
+
|
373
|
+
// Prefer an explicit height if one is defined
|
374
|
+
if( button.hasAttribute( 'data-spinner-size' ) ) {
|
375
|
+
height = parseInt( button.getAttribute( 'data-spinner-size' ), 10 );
|
376
|
+
}
|
377
|
+
|
378
|
+
// Allow buttons to specify the color of the spinner element
|
379
|
+
if( button.hasAttribute( 'data-spinner-color' ) ) {
|
380
|
+
spinnerColor = button.getAttribute( 'data-spinner-color' );
|
381
|
+
}
|
382
|
+
|
383
|
+
var lines = 12,
|
384
|
+
radius = height * 0.2,
|
385
|
+
length = radius * 0.6,
|
386
|
+
width = radius < 7 ? 2 : 3;
|
387
|
+
|
388
|
+
return new Spinner( {
|
389
|
+
color: spinnerColor || '#fff',
|
390
|
+
lines: lines,
|
391
|
+
radius: radius,
|
392
|
+
length: length,
|
393
|
+
width: width,
|
394
|
+
zIndex: 'auto',
|
395
|
+
top: 'auto',
|
396
|
+
left: 'auto',
|
397
|
+
className: ''
|
398
|
+
} );
|
399
|
+
|
400
|
+
}
|
401
|
+
|
402
|
+
function toArray( nodes ) {
|
403
|
+
|
404
|
+
var a = [];
|
405
|
+
|
406
|
+
for ( var i = 0; i < nodes.length; i++ ) {
|
407
|
+
a.push( nodes[ i ] );
|
408
|
+
}
|
409
|
+
|
410
|
+
return a;
|
411
|
+
|
412
|
+
}
|
413
|
+
|
414
|
+
// Public API
|
415
|
+
return {
|
416
|
+
|
417
|
+
bind: bind,
|
418
|
+
create: create,
|
419
|
+
stopAll: stopAll
|
420
|
+
|
421
|
+
};
|
422
|
+
|
423
|
+
}));
|