javascript_auto_include 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +7 -3
- data/Gemfile.lock +69 -19
- data/README.markdown +44 -12
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/javascript_auto_include.rb +5 -1
- data/lib/javascript_auto_include/dependency.rb +13 -0
- data/lib/javascript_auto_include/helpers/action_view.rb +72 -19
- data/lib/javascript_auto_include/source_file.rb +61 -0
- data/spec/dependency_spec.rb +19 -0
- data/spec/javascript_auto_include_spec.rb +11 -0
- data/spec/javascript_auto_include_tag_spec.rb +9 -0
- data/spec/source_file_spec.rb +52 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/testbed/Gemfile +7 -0
- data/spec/testbed/Gemfile.lock +86 -0
- data/spec/testbed/Rakefile +7 -0
- data/spec/testbed/config.ru +4 -0
- data/spec/testbed/config/application.rb +45 -0
- data/spec/testbed/config/boot.rb +6 -0
- data/spec/testbed/config/dependencies/another_fake_controller.yml +3 -0
- data/spec/testbed/config/dependencies/application.yml +2 -0
- data/spec/testbed/config/dependencies/fake_controller.yml +4 -0
- data/spec/testbed/config/dependencies/jquery.yml +2 -0
- data/spec/testbed/config/dependencies/rails.yml +2 -0
- data/spec/testbed/config/environment.rb +5 -0
- data/spec/testbed/config/environments/development.rb +26 -0
- data/spec/testbed/config/environments/production.rb +49 -0
- data/spec/testbed/config/environments/test.rb +35 -0
- data/spec/testbed/config/routes.rb +58 -0
- data/spec/testbed/log/development.log +0 -0
- data/spec/testbed/log/production.log +0 -0
- data/spec/testbed/log/server.log +0 -0
- data/spec/testbed/log/test.log +8 -0
- data/spec/testbed/public/javascripts/another_fake_controller.js +14 -0
- data/spec/testbed/public/javascripts/application.js +1 -0
- data/spec/testbed/public/javascripts/fake_controller.js +15 -0
- data/spec/testbed/public/javascripts/jquery.js +8316 -0
- data/spec/testbed/public/javascripts/jquery.ui.core.js +312 -0
- data/spec/testbed/public/javascripts/jquery.ui.core.min.js +17 -0
- data/spec/testbed/public/javascripts/jquery.ui.mouse.js +160 -0
- data/spec/testbed/public/javascripts/jquery.ui.mouse.min.js +17 -0
- data/spec/testbed/public/javascripts/jquery.ui.widget.js +262 -0
- data/spec/testbed/public/javascripts/jquery.ui.widget.min.js +15 -0
- data/spec/testbed/public/javascripts/rails.js +289 -0
- metadata +95 -88
- data/.document +0 -5
- data/javascript_auto_include.gemspec +0 -60
@@ -0,0 +1,17 @@
|
|
1
|
+
/*!
|
2
|
+
* jQuery UI Mouse 1.8.13
|
3
|
+
*
|
4
|
+
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
5
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
6
|
+
* http://jquery.org/license
|
7
|
+
*
|
8
|
+
* http://docs.jquery.com/UI/Mouse
|
9
|
+
*
|
10
|
+
* Depends:
|
11
|
+
* jquery.ui.widget.js
|
12
|
+
*/
|
13
|
+
(function(b){var d=false;b(document).mousedown(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
|
14
|
+
this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"?b(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=
|
15
|
+
this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
|
16
|
+
!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
|
17
|
+
false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
|
@@ -0,0 +1,262 @@
|
|
1
|
+
/*!
|
2
|
+
* jQuery UI Widget 1.8.13
|
3
|
+
*
|
4
|
+
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
5
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
6
|
+
* http://jquery.org/license
|
7
|
+
*
|
8
|
+
* http://docs.jquery.com/UI/Widget
|
9
|
+
*/
|
10
|
+
(function( $, undefined ) {
|
11
|
+
|
12
|
+
// jQuery 1.4+
|
13
|
+
if ( $.cleanData ) {
|
14
|
+
var _cleanData = $.cleanData;
|
15
|
+
$.cleanData = function( elems ) {
|
16
|
+
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
|
17
|
+
$( elem ).triggerHandler( "remove" );
|
18
|
+
}
|
19
|
+
_cleanData( elems );
|
20
|
+
};
|
21
|
+
} else {
|
22
|
+
var _remove = $.fn.remove;
|
23
|
+
$.fn.remove = function( selector, keepData ) {
|
24
|
+
return this.each(function() {
|
25
|
+
if ( !keepData ) {
|
26
|
+
if ( !selector || $.filter( selector, [ this ] ).length ) {
|
27
|
+
$( "*", this ).add( [ this ] ).each(function() {
|
28
|
+
$( this ).triggerHandler( "remove" );
|
29
|
+
});
|
30
|
+
}
|
31
|
+
}
|
32
|
+
return _remove.call( $(this), selector, keepData );
|
33
|
+
});
|
34
|
+
};
|
35
|
+
}
|
36
|
+
|
37
|
+
$.widget = function( name, base, prototype ) {
|
38
|
+
var namespace = name.split( "." )[ 0 ],
|
39
|
+
fullName;
|
40
|
+
name = name.split( "." )[ 1 ];
|
41
|
+
fullName = namespace + "-" + name;
|
42
|
+
|
43
|
+
if ( !prototype ) {
|
44
|
+
prototype = base;
|
45
|
+
base = $.Widget;
|
46
|
+
}
|
47
|
+
|
48
|
+
// create selector for plugin
|
49
|
+
$.expr[ ":" ][ fullName ] = function( elem ) {
|
50
|
+
return !!$.data( elem, name );
|
51
|
+
};
|
52
|
+
|
53
|
+
$[ namespace ] = $[ namespace ] || {};
|
54
|
+
$[ namespace ][ name ] = function( options, element ) {
|
55
|
+
// allow instantiation without initializing for simple inheritance
|
56
|
+
if ( arguments.length ) {
|
57
|
+
this._createWidget( options, element );
|
58
|
+
}
|
59
|
+
};
|
60
|
+
|
61
|
+
var basePrototype = new base();
|
62
|
+
// we need to make the options hash a property directly on the new instance
|
63
|
+
// otherwise we'll modify the options hash on the prototype that we're
|
64
|
+
// inheriting from
|
65
|
+
// $.each( basePrototype, function( key, val ) {
|
66
|
+
// if ( $.isPlainObject(val) ) {
|
67
|
+
// basePrototype[ key ] = $.extend( {}, val );
|
68
|
+
// }
|
69
|
+
// });
|
70
|
+
basePrototype.options = $.extend( true, {}, basePrototype.options );
|
71
|
+
$[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
|
72
|
+
namespace: namespace,
|
73
|
+
widgetName: name,
|
74
|
+
widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
|
75
|
+
widgetBaseClass: fullName
|
76
|
+
}, prototype );
|
77
|
+
|
78
|
+
$.widget.bridge( name, $[ namespace ][ name ] );
|
79
|
+
};
|
80
|
+
|
81
|
+
$.widget.bridge = function( name, object ) {
|
82
|
+
$.fn[ name ] = function( options ) {
|
83
|
+
var isMethodCall = typeof options === "string",
|
84
|
+
args = Array.prototype.slice.call( arguments, 1 ),
|
85
|
+
returnValue = this;
|
86
|
+
|
87
|
+
// allow multiple hashes to be passed on init
|
88
|
+
options = !isMethodCall && args.length ?
|
89
|
+
$.extend.apply( null, [ true, options ].concat(args) ) :
|
90
|
+
options;
|
91
|
+
|
92
|
+
// prevent calls to internal methods
|
93
|
+
if ( isMethodCall && options.charAt( 0 ) === "_" ) {
|
94
|
+
return returnValue;
|
95
|
+
}
|
96
|
+
|
97
|
+
if ( isMethodCall ) {
|
98
|
+
this.each(function() {
|
99
|
+
var instance = $.data( this, name ),
|
100
|
+
methodValue = instance && $.isFunction( instance[options] ) ?
|
101
|
+
instance[ options ].apply( instance, args ) :
|
102
|
+
instance;
|
103
|
+
// TODO: add this back in 1.9 and use $.error() (see #5972)
|
104
|
+
// if ( !instance ) {
|
105
|
+
// throw "cannot call methods on " + name + " prior to initialization; " +
|
106
|
+
// "attempted to call method '" + options + "'";
|
107
|
+
// }
|
108
|
+
// if ( !$.isFunction( instance[options] ) ) {
|
109
|
+
// throw "no such method '" + options + "' for " + name + " widget instance";
|
110
|
+
// }
|
111
|
+
// var methodValue = instance[ options ].apply( instance, args );
|
112
|
+
if ( methodValue !== instance && methodValue !== undefined ) {
|
113
|
+
returnValue = methodValue;
|
114
|
+
return false;
|
115
|
+
}
|
116
|
+
});
|
117
|
+
} else {
|
118
|
+
this.each(function() {
|
119
|
+
var instance = $.data( this, name );
|
120
|
+
if ( instance ) {
|
121
|
+
instance.option( options || {} )._init();
|
122
|
+
} else {
|
123
|
+
$.data( this, name, new object( options, this ) );
|
124
|
+
}
|
125
|
+
});
|
126
|
+
}
|
127
|
+
|
128
|
+
return returnValue;
|
129
|
+
};
|
130
|
+
};
|
131
|
+
|
132
|
+
$.Widget = function( options, element ) {
|
133
|
+
// allow instantiation without initializing for simple inheritance
|
134
|
+
if ( arguments.length ) {
|
135
|
+
this._createWidget( options, element );
|
136
|
+
}
|
137
|
+
};
|
138
|
+
|
139
|
+
$.Widget.prototype = {
|
140
|
+
widgetName: "widget",
|
141
|
+
widgetEventPrefix: "",
|
142
|
+
options: {
|
143
|
+
disabled: false
|
144
|
+
},
|
145
|
+
_createWidget: function( options, element ) {
|
146
|
+
// $.widget.bridge stores the plugin instance, but we do it anyway
|
147
|
+
// so that it's stored even before the _create function runs
|
148
|
+
$.data( element, this.widgetName, this );
|
149
|
+
this.element = $( element );
|
150
|
+
this.options = $.extend( true, {},
|
151
|
+
this.options,
|
152
|
+
this._getCreateOptions(),
|
153
|
+
options );
|
154
|
+
|
155
|
+
var self = this;
|
156
|
+
this.element.bind( "remove." + this.widgetName, function() {
|
157
|
+
self.destroy();
|
158
|
+
});
|
159
|
+
|
160
|
+
this._create();
|
161
|
+
this._trigger( "create" );
|
162
|
+
this._init();
|
163
|
+
},
|
164
|
+
_getCreateOptions: function() {
|
165
|
+
return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
|
166
|
+
},
|
167
|
+
_create: function() {},
|
168
|
+
_init: function() {},
|
169
|
+
|
170
|
+
destroy: function() {
|
171
|
+
this.element
|
172
|
+
.unbind( "." + this.widgetName )
|
173
|
+
.removeData( this.widgetName );
|
174
|
+
this.widget()
|
175
|
+
.unbind( "." + this.widgetName )
|
176
|
+
.removeAttr( "aria-disabled" )
|
177
|
+
.removeClass(
|
178
|
+
this.widgetBaseClass + "-disabled " +
|
179
|
+
"ui-state-disabled" );
|
180
|
+
},
|
181
|
+
|
182
|
+
widget: function() {
|
183
|
+
return this.element;
|
184
|
+
},
|
185
|
+
|
186
|
+
option: function( key, value ) {
|
187
|
+
var options = key;
|
188
|
+
|
189
|
+
if ( arguments.length === 0 ) {
|
190
|
+
// don't return a reference to the internal hash
|
191
|
+
return $.extend( {}, this.options );
|
192
|
+
}
|
193
|
+
|
194
|
+
if (typeof key === "string" ) {
|
195
|
+
if ( value === undefined ) {
|
196
|
+
return this.options[ key ];
|
197
|
+
}
|
198
|
+
options = {};
|
199
|
+
options[ key ] = value;
|
200
|
+
}
|
201
|
+
|
202
|
+
this._setOptions( options );
|
203
|
+
|
204
|
+
return this;
|
205
|
+
},
|
206
|
+
_setOptions: function( options ) {
|
207
|
+
var self = this;
|
208
|
+
$.each( options, function( key, value ) {
|
209
|
+
self._setOption( key, value );
|
210
|
+
});
|
211
|
+
|
212
|
+
return this;
|
213
|
+
},
|
214
|
+
_setOption: function( key, value ) {
|
215
|
+
this.options[ key ] = value;
|
216
|
+
|
217
|
+
if ( key === "disabled" ) {
|
218
|
+
this.widget()
|
219
|
+
[ value ? "addClass" : "removeClass"](
|
220
|
+
this.widgetBaseClass + "-disabled" + " " +
|
221
|
+
"ui-state-disabled" )
|
222
|
+
.attr( "aria-disabled", value );
|
223
|
+
}
|
224
|
+
|
225
|
+
return this;
|
226
|
+
},
|
227
|
+
|
228
|
+
enable: function() {
|
229
|
+
return this._setOption( "disabled", false );
|
230
|
+
},
|
231
|
+
disable: function() {
|
232
|
+
return this._setOption( "disabled", true );
|
233
|
+
},
|
234
|
+
|
235
|
+
_trigger: function( type, event, data ) {
|
236
|
+
var callback = this.options[ type ];
|
237
|
+
|
238
|
+
event = $.Event( event );
|
239
|
+
event.type = ( type === this.widgetEventPrefix ?
|
240
|
+
type :
|
241
|
+
this.widgetEventPrefix + type ).toLowerCase();
|
242
|
+
data = data || {};
|
243
|
+
|
244
|
+
// copy original event properties over to the new event
|
245
|
+
// this would happen if we could call $.event.fix instead of $.Event
|
246
|
+
// but we don't have a way to force an event to be fixed multiple times
|
247
|
+
if ( event.originalEvent ) {
|
248
|
+
for ( var i = $.event.props.length, prop; i; ) {
|
249
|
+
prop = $.event.props[ --i ];
|
250
|
+
event[ prop ] = event.originalEvent[ prop ];
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
this.element.trigger( event, data );
|
255
|
+
|
256
|
+
return !( $.isFunction(callback) &&
|
257
|
+
callback.call( this.element[0], event, data ) === false ||
|
258
|
+
event.isDefaultPrevented() );
|
259
|
+
}
|
260
|
+
};
|
261
|
+
|
262
|
+
})( jQuery );
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*!
|
2
|
+
* jQuery UI Widget 1.8.13
|
3
|
+
*
|
4
|
+
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
5
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
6
|
+
* http://jquery.org/license
|
7
|
+
*
|
8
|
+
* http://docs.jquery.com/UI/Widget
|
9
|
+
*/
|
10
|
+
(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
|
11
|
+
a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.charAt(0)==="_")return h;
|
12
|
+
e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,
|
13
|
+
this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},
|
14
|
+
widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},
|
15
|
+
enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
|
@@ -0,0 +1,289 @@
|
|
1
|
+
/**
|
2
|
+
* Unobtrusive scripting adapter for jQuery
|
3
|
+
*
|
4
|
+
* Requires jQuery 1.4.4 or later.
|
5
|
+
* https://github.com/rails/jquery-ujs
|
6
|
+
|
7
|
+
* Uploading file using rails.js
|
8
|
+
* =============================
|
9
|
+
*
|
10
|
+
* By default, browsers do not allow files to be uploaded via AJAX. As a result, if there are any non-blank file fields
|
11
|
+
* in the remote form, this adapter aborts the AJAX submission and allows the form to submit through standard means.
|
12
|
+
*
|
13
|
+
* The `ajax:aborted:file` event allows you to bind your own handler to process the form submission however you wish.
|
14
|
+
*
|
15
|
+
* Ex:
|
16
|
+
* $('form').live('ajax:aborted:file', function(event, elements){
|
17
|
+
* // Implement own remote file-transfer handler here for non-blank file inputs passed in `elements`.
|
18
|
+
* // Returning false in this handler tells rails.js to disallow standard form submission
|
19
|
+
* return false;
|
20
|
+
* });
|
21
|
+
*
|
22
|
+
* The `ajax:aborted:file` event is fired when a file-type input is detected with a non-blank value.
|
23
|
+
*
|
24
|
+
* Third-party tools can use this hook to detect when an AJAX file upload is attempted, and then use
|
25
|
+
* techniques like the iframe method to upload the file instead.
|
26
|
+
*
|
27
|
+
* Required fields in rails.js
|
28
|
+
* ===========================
|
29
|
+
*
|
30
|
+
* If any blank required inputs (required="required") are detected in the remote form, the whole form submission
|
31
|
+
* is canceled. Note that this is unlike file inputs, which still allow standard (non-AJAX) form submission.
|
32
|
+
*
|
33
|
+
* The `ajax:aborted:required` event allows you to bind your own handler to inform the user of blank required inputs.
|
34
|
+
*
|
35
|
+
* !! Note that Opera does not fire the form's submit event if there are blank required inputs, so this event may never
|
36
|
+
* get fired in Opera. This event is what causes other browsers to exhibit the same submit-aborting behavior.
|
37
|
+
*
|
38
|
+
* Ex:
|
39
|
+
* $('form').live('ajax:aborted:required', function(event, elements){
|
40
|
+
* // Returning false in this handler tells rails.js to submit the form anyway.
|
41
|
+
* // The blank required inputs are passed to this function in `elements`.
|
42
|
+
* return ! confirm("Would you like to submit the form with missing info?");
|
43
|
+
* });
|
44
|
+
*/
|
45
|
+
|
46
|
+
(function($) {
|
47
|
+
// Shorthand to make it a little easier to call public rails functions from within rails.js
|
48
|
+
var rails;
|
49
|
+
|
50
|
+
$.rails = rails = {
|
51
|
+
// Link elements bound by jquery-ujs
|
52
|
+
linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]',
|
53
|
+
|
54
|
+
// Form elements bound by jquery-ujs
|
55
|
+
formSubmitSelector: 'form',
|
56
|
+
|
57
|
+
// Form input elements bound by jquery-ujs
|
58
|
+
formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])',
|
59
|
+
|
60
|
+
// Form input elements disabled during form submission
|
61
|
+
disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]',
|
62
|
+
|
63
|
+
// Form input elements re-enabled after form submission
|
64
|
+
enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled',
|
65
|
+
|
66
|
+
// Form required input elements
|
67
|
+
requiredInputSelector: 'input[name][required],textarea[name][required]',
|
68
|
+
|
69
|
+
// Form file input elements
|
70
|
+
fileInputSelector: 'input:file',
|
71
|
+
|
72
|
+
// Make sure that every Ajax request sends the CSRF token
|
73
|
+
CSRFProtection: function(xhr) {
|
74
|
+
var token = $('meta[name="csrf-token"]').attr('content');
|
75
|
+
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
|
76
|
+
},
|
77
|
+
|
78
|
+
// Triggers an event on an element and returns false if the event result is false
|
79
|
+
fire: function(obj, name, data) {
|
80
|
+
var event = $.Event(name);
|
81
|
+
obj.trigger(event, data);
|
82
|
+
return event.result !== false;
|
83
|
+
},
|
84
|
+
|
85
|
+
// Submits "remote" forms and links with ajax
|
86
|
+
handleRemote: function(element) {
|
87
|
+
var method, url, data,
|
88
|
+
dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);
|
89
|
+
|
90
|
+
if (rails.fire(element, 'ajax:before')) {
|
91
|
+
|
92
|
+
if (element.is('form')) {
|
93
|
+
method = element.attr('method');
|
94
|
+
url = element.attr('action');
|
95
|
+
data = element.serializeArray();
|
96
|
+
// memoized value from clicked submit button
|
97
|
+
var button = element.data('ujs:submit-button');
|
98
|
+
if (button) {
|
99
|
+
data.push(button);
|
100
|
+
element.data('ujs:submit-button', null);
|
101
|
+
}
|
102
|
+
} else {
|
103
|
+
method = element.data('method');
|
104
|
+
url = element.attr('href');
|
105
|
+
data = null;
|
106
|
+
}
|
107
|
+
|
108
|
+
$.ajax({
|
109
|
+
url: url, type: method || 'GET', data: data, dataType: dataType,
|
110
|
+
// stopping the "ajax:beforeSend" event will cancel the ajax request
|
111
|
+
beforeSend: function(xhr, settings) {
|
112
|
+
if (settings.dataType === undefined) {
|
113
|
+
xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
|
114
|
+
}
|
115
|
+
return rails.fire(element, 'ajax:beforeSend', [xhr, settings]);
|
116
|
+
},
|
117
|
+
success: function(data, status, xhr) {
|
118
|
+
element.trigger('ajax:success', [data, status, xhr]);
|
119
|
+
},
|
120
|
+
complete: function(xhr, status) {
|
121
|
+
element.trigger('ajax:complete', [xhr, status]);
|
122
|
+
},
|
123
|
+
error: function(xhr, status, error) {
|
124
|
+
element.trigger('ajax:error', [xhr, status, error]);
|
125
|
+
}
|
126
|
+
});
|
127
|
+
}
|
128
|
+
},
|
129
|
+
|
130
|
+
// Handles "data-method" on links such as:
|
131
|
+
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
|
132
|
+
handleMethod: function(link) {
|
133
|
+
var href = link.attr('href'),
|
134
|
+
method = link.data('method'),
|
135
|
+
csrf_token = $('meta[name=csrf-token]').attr('content'),
|
136
|
+
csrf_param = $('meta[name=csrf-param]').attr('content'),
|
137
|
+
form = $('<form method="post" action="' + href + '"></form>'),
|
138
|
+
metadata_input = '<input name="_method" value="' + method + '" type="hidden" />';
|
139
|
+
|
140
|
+
if (csrf_param !== undefined && csrf_token !== undefined) {
|
141
|
+
metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />';
|
142
|
+
}
|
143
|
+
|
144
|
+
form.hide().append(metadata_input).appendTo('body');
|
145
|
+
form.submit();
|
146
|
+
},
|
147
|
+
|
148
|
+
/* Disables form elements:
|
149
|
+
- Caches element value in 'ujs:enable-with' data store
|
150
|
+
- Replaces element text with value of 'data-disable-with' attribute
|
151
|
+
- Adds disabled=disabled attribute
|
152
|
+
*/
|
153
|
+
disableFormElements: function(form) {
|
154
|
+
form.find(rails.disableSelector).each(function() {
|
155
|
+
var element = $(this), method = element.is('button') ? 'html' : 'val';
|
156
|
+
element.data('ujs:enable-with', element[method]());
|
157
|
+
element[method](element.data('disable-with'));
|
158
|
+
element.attr('disabled', 'disabled');
|
159
|
+
});
|
160
|
+
},
|
161
|
+
|
162
|
+
/* Re-enables disabled form elements:
|
163
|
+
- Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
|
164
|
+
- Removes disabled attribute
|
165
|
+
*/
|
166
|
+
enableFormElements: function(form) {
|
167
|
+
form.find(rails.enableSelector).each(function() {
|
168
|
+
var element = $(this), method = element.is('button') ? 'html' : 'val';
|
169
|
+
if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
|
170
|
+
element.removeAttr('disabled');
|
171
|
+
});
|
172
|
+
},
|
173
|
+
|
174
|
+
// If message provided in 'data-confirm' attribute, fires `confirm` event and returns result of confirm dialog.
|
175
|
+
// Attaching a handler to the element's `confirm` event that returns false cancels the confirm dialog.
|
176
|
+
allowAction: function(element) {
|
177
|
+
var message = element.data('confirm');
|
178
|
+
return !message || (rails.fire(element, 'confirm') && confirm(message));
|
179
|
+
},
|
180
|
+
|
181
|
+
// Helper function which checks for blank inputs in a form that match the specified CSS selector
|
182
|
+
blankInputs: function(form, specifiedSelector, nonBlank) {
|
183
|
+
var inputs = $(), input,
|
184
|
+
selector = specifiedSelector || 'input,textarea';
|
185
|
+
form.find(selector).each(function() {
|
186
|
+
input = $(this);
|
187
|
+
// Collect non-blank inputs if nonBlank option is true, otherwise, collect blank inputs
|
188
|
+
if (nonBlank ? input.val() : !input.val()) {
|
189
|
+
inputs = inputs.add(input);
|
190
|
+
}
|
191
|
+
});
|
192
|
+
return inputs.length ? inputs : false;
|
193
|
+
},
|
194
|
+
|
195
|
+
// Helper function which checks for non-blank inputs in a form that match the specified CSS selector
|
196
|
+
nonBlankInputs: function(form, specifiedSelector) {
|
197
|
+
return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
|
198
|
+
},
|
199
|
+
|
200
|
+
// Helper function, needed to provide consistent behavior in IE
|
201
|
+
stopEverything: function(e) {
|
202
|
+
e.stopImmediatePropagation();
|
203
|
+
return false;
|
204
|
+
},
|
205
|
+
|
206
|
+
// find all the submit events directly bound to the form and
|
207
|
+
// manually invoke them. If anyone returns false then stop the loop
|
208
|
+
callFormSubmitBindings: function(form) {
|
209
|
+
var events = form.data('events'), continuePropagation = true;
|
210
|
+
if (events !== undefined && events['submit'] !== undefined) {
|
211
|
+
$.each(events['submit'], function(i, obj){
|
212
|
+
if (typeof obj.handler === 'function') return continuePropagation = obj.handler(obj.data);
|
213
|
+
});
|
214
|
+
}
|
215
|
+
return continuePropagation;
|
216
|
+
}
|
217
|
+
};
|
218
|
+
|
219
|
+
// ajaxPrefilter is a jQuery 1.5 feature
|
220
|
+
if ('ajaxPrefilter' in $) {
|
221
|
+
$.ajaxPrefilter(function(options, originalOptions, xhr){ rails.CSRFProtection(xhr); });
|
222
|
+
} else {
|
223
|
+
$(document).ajaxSend(function(e, xhr){ rails.CSRFProtection(xhr); });
|
224
|
+
}
|
225
|
+
|
226
|
+
$(rails.linkClickSelector).live('click.rails', function(e) {
|
227
|
+
var link = $(this);
|
228
|
+
if (!rails.allowAction(link)) return rails.stopEverything(e);
|
229
|
+
|
230
|
+
if (link.data('remote') !== undefined) {
|
231
|
+
rails.handleRemote(link);
|
232
|
+
return false;
|
233
|
+
} else if (link.data('method')) {
|
234
|
+
rails.handleMethod(link);
|
235
|
+
return false;
|
236
|
+
}
|
237
|
+
});
|
238
|
+
|
239
|
+
$(rails.formSubmitSelector).live('submit.rails', function(e) {
|
240
|
+
var form = $(this),
|
241
|
+
remote = form.data('remote') !== undefined,
|
242
|
+
blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector),
|
243
|
+
nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
|
244
|
+
|
245
|
+
if (!rails.allowAction(form)) return rails.stopEverything(e);
|
246
|
+
|
247
|
+
// skip other logic when required values are missing or file upload is present
|
248
|
+
if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
|
249
|
+
return !remote;
|
250
|
+
}
|
251
|
+
|
252
|
+
if (remote) {
|
253
|
+
if (nonBlankFileInputs) {
|
254
|
+
return rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
|
255
|
+
}
|
256
|
+
|
257
|
+
// If browser does not support submit bubbling, then this live-binding will be called before direct
|
258
|
+
// bindings. Therefore, we should directly call any direct bindings before remotely submitting form.
|
259
|
+
if (!$.support.submitBubbles && rails.callFormSubmitBindings(form) === false) return rails.stopEverything(e);
|
260
|
+
|
261
|
+
rails.handleRemote(form);
|
262
|
+
return false;
|
263
|
+
} else {
|
264
|
+
// slight timeout so that the submit button gets properly serialized
|
265
|
+
setTimeout(function(){ rails.disableFormElements(form); }, 13);
|
266
|
+
}
|
267
|
+
});
|
268
|
+
|
269
|
+
$(rails.formInputClickSelector).live('click.rails', function(event) {
|
270
|
+
var button = $(this);
|
271
|
+
|
272
|
+
if (!rails.allowAction(button)) return rails.stopEverything(event);
|
273
|
+
|
274
|
+
// register the pressed submit button
|
275
|
+
var name = button.attr('name'),
|
276
|
+
data = name ? {name:name, value:button.val()} : null;
|
277
|
+
|
278
|
+
button.closest('form').data('ujs:submit-button', data);
|
279
|
+
});
|
280
|
+
|
281
|
+
$(rails.formSubmitSelector).live('ajax:beforeSend.rails', function(event) {
|
282
|
+
if (this == event.target) rails.disableFormElements($(this));
|
283
|
+
});
|
284
|
+
|
285
|
+
$(rails.formSubmitSelector).live('ajax:complete.rails', function(event) {
|
286
|
+
if (this == event.target) rails.enableFormElements($(this));
|
287
|
+
});
|
288
|
+
|
289
|
+
})( jQuery );
|