mera 0.1.9 → 0.3.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.
- data/lib/assets/javascripts/mera.js +912 -103
- data/lib/assets/javascripts/mera.min.js +58 -0
- metadata +8 -22
|
@@ -1,141 +1,950 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* My Easy Rails Ajax
|
|
4
|
-
* Open under GPL (General Public License)
|
|
5
|
-
* Author Richard Hutta 2012
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
1
|
|
|
2
|
+
(function($, undefined) {
|
|
9
3
|
|
|
10
|
-
|
|
4
|
+
/**
|
|
5
|
+
* = Jquery_Ujs
|
|
6
|
+
*
|
|
7
|
+
* Unobtrusive scripting adapter for jQuery
|
|
8
|
+
*
|
|
9
|
+
* Requires jQuery 1.6.0 or later.
|
|
10
|
+
* https://github.com/rails/jquery-ujs
|
|
11
|
+
*
|
|
12
|
+
* =========================================
|
|
13
|
+
*
|
|
14
|
+
* = Pjax
|
|
15
|
+
*
|
|
16
|
+
* Pjax, loads HTML from your server into the current page without a full reload. It's ajax with real
|
|
17
|
+
* permalinks, page titles, and a working back button that fully degrades.
|
|
18
|
+
*
|
|
19
|
+
* Requires jQuery 1.7.0 or later.
|
|
20
|
+
* https://github.com/defunkt/jquery-pjax
|
|
21
|
+
*
|
|
22
|
+
* =========================================
|
|
23
|
+
*
|
|
24
|
+
* = Mera
|
|
25
|
+
*
|
|
26
|
+
* Is technology what combine classic Jquery_Ujs (only core) and Pjax (only core), together
|
|
27
|
+
*
|
|
28
|
+
* https://github.com/huttarichard/Mera
|
|
29
|
+
*
|
|
30
|
+
* =========================================
|
|
31
|
+
*
|
|
32
|
+
* Each project is open under Mit license
|
|
33
|
+
*
|
|
34
|
+
*
|
|
35
|
+
* Copyright (c) Mera 2012 & Pjax 2012 & Jquery_Ujs 2012
|
|
36
|
+
*
|
|
37
|
+
* Permission is hereby granted, free of charge, to any person
|
|
38
|
+
* obtaining a copy of this software and associated documentation
|
|
39
|
+
* files (the "Software"), to deal in the Software without
|
|
40
|
+
* restriction, including without limitation the rights to use,
|
|
41
|
+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
42
|
+
* copies of the Software, and to permit persons to whom the
|
|
43
|
+
* Software is furnished to do so, subject to the following
|
|
44
|
+
* conditions:
|
|
45
|
+
*
|
|
46
|
+
* The above copyright notice and this permission notice shall be
|
|
47
|
+
* included in all copies or substantial portions of the Software.
|
|
48
|
+
*
|
|
49
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
50
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
51
|
+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
52
|
+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
53
|
+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
54
|
+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
55
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
56
|
+
* OTHER DEALINGS IN THE SOFTWARE.
|
|
57
|
+
*
|
|
58
|
+
*/
|
|
11
59
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
60
|
+
// Cut down on the number if issues from people inadvertently including jquery_ujs twice
|
|
61
|
+
// by detecting and raising an error when it happens.
|
|
62
|
+
var alreadyInitialized = function() {
|
|
63
|
+
var events = $._data(document, 'events');
|
|
64
|
+
return events && events.click && $.grep(events.click, function(e) { return e.namespace === 'rails'; }).length;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if ( alreadyInitialized() ) {
|
|
68
|
+
$.error('jquery-ujs has already been loaded!');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Shorthand to make it a little easier to call public rails functions from within rails.js
|
|
72
|
+
var rails;
|
|
73
|
+
var cacheMapping = {};
|
|
74
|
+
var cacheForwardStack = [];
|
|
75
|
+
var cacheBackStack = [];
|
|
76
|
+
var mera = {};
|
|
77
|
+
|
|
78
|
+
$.rails = rails = {
|
|
79
|
+
// Link elements bound by jquery-ujs
|
|
80
|
+
linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]',
|
|
81
|
+
|
|
82
|
+
// Select elements bound by jquery-ujs
|
|
83
|
+
inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
|
|
84
|
+
|
|
85
|
+
// Form elements bound by jquery-ujs
|
|
86
|
+
formSubmitSelector: 'form',
|
|
87
|
+
|
|
88
|
+
// Form input elements bound by jquery-ujs
|
|
89
|
+
formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])',
|
|
90
|
+
|
|
91
|
+
// Form input elements disabled during form submission
|
|
92
|
+
disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]',
|
|
93
|
+
|
|
94
|
+
// Form input elements re-enabled after form submission
|
|
95
|
+
enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled',
|
|
96
|
+
|
|
97
|
+
// Form required input elements
|
|
98
|
+
requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',
|
|
99
|
+
|
|
100
|
+
// Form file input elements
|
|
101
|
+
fileInputSelector: 'input:file',
|
|
102
|
+
|
|
103
|
+
// Link onClick disable selector with possible reenable after remote submission
|
|
104
|
+
linkDisableSelector: 'a[data-disable-with]',
|
|
105
|
+
|
|
106
|
+
// Define location path
|
|
107
|
+
locationPath: location.pathname,
|
|
108
|
+
|
|
109
|
+
// Define location url
|
|
110
|
+
locationUrl: location.href,
|
|
111
|
+
|
|
112
|
+
// Define default container what will be updated
|
|
113
|
+
meraContainer: "body",
|
|
114
|
+
|
|
115
|
+
// Make sure that every Ajax request sends the CSRF token
|
|
116
|
+
CSRFProtection: function(xhr) {
|
|
117
|
+
var token = $('meta[name="csrf-token"]').attr('content');
|
|
118
|
+
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
|
|
15
119
|
},
|
|
16
120
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
121
|
+
// Triggers an event on an element and returns false if the event result is false
|
|
122
|
+
fire: function(obj, name, data) {
|
|
123
|
+
var event = $.Event(name);
|
|
124
|
+
obj.trigger(event, data);
|
|
125
|
+
return event.result !== false;
|
|
126
|
+
},
|
|
21
127
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
$absoluteUrl = location.href;
|
|
27
|
-
//get this identifier
|
|
28
|
-
$this = $(this);
|
|
29
|
-
//get default options
|
|
30
|
-
$default = {
|
|
31
|
-
before : function(){$('body').hide();},
|
|
32
|
-
after : function(){$('body').fadeIn();},
|
|
33
|
-
error : function(xhr, status, error){alert(error);},
|
|
34
|
-
confirme : function(question){return confirm(question);},
|
|
35
|
-
compress: true
|
|
36
|
-
};
|
|
37
|
-
//get user options
|
|
38
|
-
$options = $.extend({}, $default, options);
|
|
39
|
-
//get if data-update-only
|
|
40
|
-
$dataUpdateOnly = false;
|
|
41
|
-
|
|
42
|
-
$this.click(function(){
|
|
43
|
-
window.location.href = $(this).attr('href');
|
|
44
|
-
});
|
|
128
|
+
// Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
|
|
129
|
+
confirm: function(message) {
|
|
130
|
+
return confirm(message);
|
|
131
|
+
},
|
|
45
132
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
133
|
+
// Default ajax function, may be overridden with custom function in $.rails.ajax
|
|
134
|
+
ajax: function(options) {
|
|
135
|
+
return $.ajax(options);
|
|
136
|
+
},
|
|
49
137
|
|
|
50
|
-
|
|
51
|
-
|
|
138
|
+
// Default options for rails.ajax
|
|
139
|
+
ajaxDefault: {
|
|
140
|
+
timeout: 650,
|
|
141
|
+
push: true,
|
|
142
|
+
replace: false,
|
|
143
|
+
type: 'GET',
|
|
144
|
+
dataType: 'html',
|
|
145
|
+
scrollTo: 0,
|
|
146
|
+
maxCacheLength: 20
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// Default way to get an element's href. May be overridden at $.rails.href.
|
|
150
|
+
href: function(element) {
|
|
151
|
+
return element.attr('href');
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Is mera supported?
|
|
155
|
+
// from pjax + custom mera
|
|
156
|
+
supportMera: function(){
|
|
157
|
+
return (window.history &&
|
|
158
|
+
window.history.pushState &&
|
|
159
|
+
window.history.replaceState &&
|
|
160
|
+
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/));
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// Get container for added element
|
|
164
|
+
// from pjax + custom mera
|
|
165
|
+
findContainerFor: function (container) {
|
|
166
|
+
container = $(container);
|
|
167
|
+
|
|
168
|
+
if (!container.length) {
|
|
169
|
+
throw "Mera have not container for '" + container.selector + "'!";
|
|
170
|
+
} else if (container.selector !== '' && container.context === document) {
|
|
171
|
+
return container;
|
|
172
|
+
} else if (container.attr('id')) {
|
|
173
|
+
return $('#' + container.attr('id'));
|
|
52
174
|
} else {
|
|
53
|
-
|
|
175
|
+
throw "Can not get selector for Mera container!"
|
|
54
176
|
}
|
|
177
|
+
},
|
|
55
178
|
|
|
56
|
-
|
|
179
|
+
// Find data in for or in element
|
|
180
|
+
// custom mera
|
|
181
|
+
findDataFor: function(element){
|
|
182
|
+
var object, method, _method, params, _params;
|
|
57
183
|
|
|
58
|
-
|
|
59
|
-
|
|
184
|
+
if(element.is('form')){
|
|
185
|
+
object = element.find('input[name=ajaxParams]').val();
|
|
186
|
+
if(object && object.length)
|
|
187
|
+
eval('object = ' + object);
|
|
188
|
+
else
|
|
189
|
+
object = {};
|
|
60
190
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
191
|
+
object = $.extend(object, {
|
|
192
|
+
url: element.attr('action'),
|
|
193
|
+
method: element.attr('method'),
|
|
194
|
+
data: element.serialize()
|
|
195
|
+
});
|
|
196
|
+
} else {
|
|
197
|
+
object = {};
|
|
198
|
+
object.crossDomain = element.data('cross-domain');
|
|
199
|
+
object.withCredentials = element.data('with-credentials');
|
|
200
|
+
object.container = element.data('container');
|
|
201
|
+
object.event = element.data('event');
|
|
202
|
+
object.push = element.data('push');
|
|
203
|
+
|
|
204
|
+
method = element.data('method');
|
|
205
|
+
_method = method ? "_method=" + method + "&" : "";
|
|
206
|
+
|
|
207
|
+
params = element.data('params');
|
|
208
|
+
_params = params ? params : "";
|
|
209
|
+
|
|
210
|
+
object = $.extend(object, {
|
|
211
|
+
url: (rails.href(element) || element.data('url')),
|
|
212
|
+
method: method,
|
|
213
|
+
data: _method + _params
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
object.crossDomain = object.crossDomain || false;
|
|
218
|
+
object.withCredentials = object.withCredentials || false;
|
|
219
|
+
object.push = object.push == undefined ? object.url : object.push;
|
|
220
|
+
object.container = object.container !== undefined ? object.container : rails.meraContainer;
|
|
221
|
+
object.event = object.event ? 'ajax:' + object.event : false;
|
|
222
|
+
|
|
223
|
+
return object;
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
// Get link from url
|
|
227
|
+
// custom mera
|
|
228
|
+
getUrlElement: function (url) {
|
|
229
|
+
return $('<a/>', {href: url}).get(0);
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
// Filter data
|
|
233
|
+
// from pjax
|
|
234
|
+
findAll: function findAll(element, selector) {
|
|
235
|
+
return element.filter(selector).add(element.find(selector));
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
// Location replace
|
|
239
|
+
// from pjax + custom mera
|
|
240
|
+
locationReplace: function(url) {
|
|
241
|
+
window.history.replaceState(null, "", "/");
|
|
242
|
+
window.location.replace(url);
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
// Extract container for update
|
|
246
|
+
// from pjax + custom mera
|
|
247
|
+
extractContainer: function (data, xhr, options) {
|
|
248
|
+
var obj, $head, $body, $fragment;
|
|
66
249
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
250
|
+
obj = {};
|
|
251
|
+
|
|
252
|
+
// Prefer X-MERA-URL header if it was set, otherwise fallback to
|
|
253
|
+
// using the original requested url.
|
|
254
|
+
obj.url = (xhr.getResponseHeader('X-MERA-URL') || options.requestUrl);
|
|
255
|
+
|
|
256
|
+
// Attempt to parse response html into elements
|
|
257
|
+
if (/<html/i.test(data)) {
|
|
258
|
+
$head = $(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]);
|
|
259
|
+
$body = $(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]);
|
|
260
|
+
} else {
|
|
261
|
+
$head = $body = $(data);
|
|
70
262
|
}
|
|
71
|
-
});
|
|
72
263
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
264
|
+
// If response data is empty, return fast
|
|
265
|
+
if ($body.length === 0)
|
|
266
|
+
return obj;
|
|
76
267
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
});
|
|
268
|
+
// If there's a <title> tag in the header, use it as
|
|
269
|
+
// the page's title.
|
|
270
|
+
obj.title = rails.findAll($head, 'title').last().text();
|
|
81
271
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
272
|
+
if (options.container) {
|
|
273
|
+
// If they specified a fragment, look for it in the response
|
|
274
|
+
// and pull it out.
|
|
275
|
+
if (options.container === 'body') {
|
|
276
|
+
$fragment = $body;
|
|
277
|
+
} else {
|
|
278
|
+
$fragment = rails.findAll($body, options.container).first().contents();
|
|
279
|
+
}
|
|
86
280
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
281
|
+
if ($fragment.length) {
|
|
282
|
+
obj.contents = $fragment;
|
|
283
|
+
// If there's no title, look for data-title and title attributes
|
|
284
|
+
// on the fragment
|
|
285
|
+
if (!obj.title)
|
|
286
|
+
obj.title = $fragment.attr('title') || $fragment.data('title');
|
|
287
|
+
}
|
|
90
288
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
289
|
+
} else if (!/<html/i.test(data)) {
|
|
290
|
+
obj.contents = $body;
|
|
291
|
+
}
|
|
94
292
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
293
|
+
// Clean up any <title> tags
|
|
294
|
+
if (obj.contents) {
|
|
295
|
+
// Remove any parent title elements
|
|
296
|
+
obj.contents = obj.contents.not('title');
|
|
297
|
+
|
|
298
|
+
// Then scrub any titles from their descendents
|
|
299
|
+
obj.contents.find('title').remove();
|
|
98
300
|
}
|
|
99
301
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
};
|
|
302
|
+
// Trim any whitespace off the title
|
|
303
|
+
if (obj.title) obj.title = $.trim(obj.title);
|
|
103
304
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
data = data.replace(/\n/g,'');
|
|
107
|
-
data = $.trim(data);
|
|
305
|
+
return obj;
|
|
306
|
+
},
|
|
108
307
|
|
|
109
|
-
|
|
110
|
-
|
|
308
|
+
// Get number / random
|
|
309
|
+
// from pjax core
|
|
310
|
+
uniqueId: function() {
|
|
311
|
+
return (new Date).getTime();
|
|
312
|
+
},
|
|
111
313
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
314
|
+
// Get / Save cache of page handled of mera
|
|
315
|
+
// from pjax core
|
|
316
|
+
cachePush: function (id, value) {
|
|
317
|
+
cacheMapping[id] = value;
|
|
318
|
+
cacheBackStack.push(id);
|
|
319
|
+
|
|
320
|
+
// Remove all entires in forward history stack after pushing
|
|
321
|
+
// a new page.
|
|
322
|
+
while (cacheForwardStack.length)
|
|
323
|
+
delete cacheMapping[cacheForwardStack.shift()];
|
|
324
|
+
|
|
325
|
+
// Trim back history stack to max cache length.
|
|
326
|
+
while (cacheBackStack.length > rails.ajaxDefault.maxCacheLength)
|
|
327
|
+
delete cacheMapping[cacheBackStack.shift()];
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
cachePop: function (direction, id, value) {
|
|
331
|
+
var pushStack, popStack;
|
|
332
|
+
|
|
333
|
+
cacheMapping[id] = value;
|
|
334
|
+
|
|
335
|
+
if (direction === 'forward') {
|
|
336
|
+
pushStack = cacheBackStack;
|
|
337
|
+
popStack = cacheForwardStack;
|
|
338
|
+
} else {
|
|
339
|
+
pushStack = cacheForwardStack;
|
|
340
|
+
popStack = cacheBackStack;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
pushStack.push(id);
|
|
344
|
+
|
|
345
|
+
if (id = popStack.pop())
|
|
346
|
+
delete cacheMapping[id];
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
// Handle back/forward button in MERA
|
|
350
|
+
// from pjax core
|
|
351
|
+
onMeraPopstate: function (event) {
|
|
352
|
+
var state, container, contents, direction, popstateEvent, options;
|
|
353
|
+
|
|
354
|
+
state = event.state;
|
|
355
|
+
|
|
356
|
+
if (state && state.container) {
|
|
357
|
+
container = $(state.container);
|
|
358
|
+
if (container.length) {
|
|
359
|
+
contents = cacheMapping[state.id];
|
|
360
|
+
|
|
361
|
+
if (mera.state) {
|
|
362
|
+
// Since state ids always increase, we can deduce the history
|
|
363
|
+
// direction from the previous state.
|
|
364
|
+
direction = mera.state.id < state.id ? 'forward' : 'back';
|
|
365
|
+
|
|
366
|
+
// Cache current container before replacement and inform the
|
|
367
|
+
// cache which direction the history shifted.
|
|
368
|
+
rails.cachePop(direction, mera.state.id, container.clone().contents())
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
popstateEvent = $.Event('ajax:popstate', {
|
|
372
|
+
state: state,
|
|
373
|
+
direction: direction
|
|
374
|
+
});
|
|
375
|
+
container.trigger(popstateEvent);
|
|
376
|
+
|
|
377
|
+
options = {
|
|
378
|
+
id: state.id,
|
|
379
|
+
url: state.url,
|
|
380
|
+
container: container,
|
|
381
|
+
push: false,
|
|
382
|
+
timeout: state.timeout,
|
|
383
|
+
scrollTo: false
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
if (contents) {
|
|
387
|
+
container.trigger('ajax:start', [null, options]);
|
|
388
|
+
|
|
389
|
+
if (state.title){
|
|
390
|
+
document.title = state.title;
|
|
391
|
+
container.html(contents);
|
|
392
|
+
mera.state = state;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
container.trigger('ajax:end', [null, options]);
|
|
396
|
+
} else {
|
|
397
|
+
rails.mjax(options);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Force reflow/relayout before the browser tries to restore the
|
|
401
|
+
// scroll position.
|
|
402
|
+
container[0].offsetHeight;
|
|
403
|
+
} else {
|
|
404
|
+
rails.locationReplace(location.href);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
// Core of PJAX and MERA => Mjax
|
|
410
|
+
// Options will be merge with $.ajaxDefault, and user options
|
|
411
|
+
// from pjax + custom mera
|
|
412
|
+
mjax: function (options) {
|
|
413
|
+
var target, hash, origin, context,timeoutTimer, xhr;
|
|
414
|
+
|
|
415
|
+
options = $.extend(true, {}, $.ajaxSettings, rails.ajaxDefault, options);
|
|
416
|
+
|
|
417
|
+
if ($.isFunction(options.url)) {
|
|
418
|
+
options.url = options.url();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
target = options.target;
|
|
422
|
+
hash = rails.getUrlElement(options.url).hash;
|
|
423
|
+
|
|
424
|
+
if(!options.container){
|
|
425
|
+
origin = options.container;
|
|
426
|
+
options.c = origin;
|
|
427
|
+
options.container = rails.meraContainer;
|
|
428
|
+
} else {
|
|
429
|
+
options.c = options.container;
|
|
430
|
+
origin = true;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
context = options.context = rails.findContainerFor(options.container);
|
|
434
|
+
|
|
435
|
+
function fire(type, args) {
|
|
436
|
+
var event = $.Event(type, { relatedTarget: target });
|
|
437
|
+
context.trigger(event, args);
|
|
438
|
+
return !event.isDefaultPrevented();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
options.beforeSend = function(xhr, settings) {
|
|
442
|
+
// No timeout for non-GET requests
|
|
443
|
+
// Its not safe to request the resource again with a fallback method.
|
|
444
|
+
if (settings.type !== 'GET')
|
|
445
|
+
settings.timeout = 0;
|
|
446
|
+
|
|
447
|
+
if (settings.timeout > 0) {
|
|
448
|
+
timeoutTimer = setTimeout(function() {
|
|
449
|
+
if (fire('ajax:timeout', [xhr, options]))
|
|
450
|
+
xhr.abort('timeout');
|
|
451
|
+
}, settings.timeout);
|
|
452
|
+
|
|
453
|
+
// Clear timeout setting so jquerys internal timeout isn't invoked
|
|
454
|
+
settings.timeout = 0;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
xhr.setRequestHeader('X-MERA', 'true');
|
|
458
|
+
xhr.setRequestHeader('X-MERA-CONTAINER', context.selector);
|
|
459
|
+
|
|
460
|
+
if (!fire('ajax:beforeSend', [xhr, settings, options]))
|
|
461
|
+
return false;
|
|
462
|
+
|
|
463
|
+
options.requestUrl = rails.getUrlElement(settings.url).href;
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
options.complete = function(xhr, textStatus) {
|
|
467
|
+
if (timeoutTimer)
|
|
468
|
+
clearTimeout(timeoutTimer);
|
|
469
|
+
|
|
470
|
+
if(options.customEvent)
|
|
471
|
+
fire(options.customEvent, [xhr, textStatus, options]);
|
|
472
|
+
|
|
473
|
+
fire('ajax:complete', [xhr, textStatus, options]);
|
|
474
|
+
fire('ajax:end', [xhr, options]);
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
options.error = function(xhr, textStatus, errorThrown) {
|
|
478
|
+
var container, allowed;
|
|
479
|
+
|
|
480
|
+
container = rails.extractContainer("", xhr, options);
|
|
481
|
+
allowed = fire('ajax:error', [xhr, textStatus, errorThrown, options]);
|
|
482
|
+
|
|
483
|
+
if (textStatus !== 'abort' && allowed)
|
|
484
|
+
rails.locationReplace(container.url);
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
options.success = function(data, status, xhr) {
|
|
488
|
+
var container, url, target;
|
|
489
|
+
|
|
490
|
+
container = rails.extractContainer(data, xhr, options);
|
|
491
|
+
|
|
492
|
+
if (!container.contents) {
|
|
493
|
+
rails.locationReplace(container.url);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
mera.state = {
|
|
498
|
+
id: options.id || rails.uniqueId(),
|
|
499
|
+
url: container.url,
|
|
500
|
+
title: container.title,
|
|
501
|
+
container: context.selector,
|
|
502
|
+
timeout: options.timeout
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
if (options.push || options.replace) {
|
|
506
|
+
window.history.replaceState(mera.state, container.title, options.push)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (container.title){
|
|
510
|
+
document.title = container.title;
|
|
511
|
+
if(origin)
|
|
512
|
+
context.html(container.contents);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
// Scroll to top by default
|
|
517
|
+
if (typeof options.scrollTo === 'number')
|
|
518
|
+
$(window).scrollTop(options.scrollTo);
|
|
519
|
+
|
|
520
|
+
// Google Analytics support
|
|
521
|
+
if ( (options.replace || options.push) && window._gaq )
|
|
522
|
+
_gaq.push(['_trackPageview']);
|
|
523
|
+
|
|
524
|
+
// If the URL has a hash in it, make sure the browser
|
|
525
|
+
// knows to navigate to the hash.
|
|
526
|
+
if ( hash !== '' ) {
|
|
527
|
+
// Avoid using simple hash set here. Will add another history
|
|
528
|
+
// entry. Replace the url with replaceState and scroll to target
|
|
529
|
+
// by hand.
|
|
530
|
+
//
|
|
531
|
+
// window.location.hash = hash
|
|
532
|
+
url = rails.getUrlElement(container.url);
|
|
533
|
+
url.hash = hash;
|
|
534
|
+
|
|
535
|
+
mera.state.url = url.href;
|
|
536
|
+
window.history.replaceState(mera.state, container.title, url.href);
|
|
537
|
+
|
|
538
|
+
target = $(url.hash);
|
|
539
|
+
|
|
540
|
+
if (target.length)
|
|
541
|
+
$(window).scrollTop(target.offset().top);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
fire('ajax:success', [data, status, xhr, options]);
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
options.xhrFields = {
|
|
548
|
+
withCredentials: options.credentials
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
// Initialize mera.state for the initial page load. Assume we're
|
|
552
|
+
// using the container and options of the link we're loading for the
|
|
553
|
+
// back button to the initial page. This ensures good back button
|
|
554
|
+
// behavior.
|
|
555
|
+
if (!mera.state) {
|
|
556
|
+
mera.state = {
|
|
557
|
+
id: rails.uniqueId(),
|
|
558
|
+
url: window.location.href,
|
|
559
|
+
title: document.title,
|
|
560
|
+
container: context.selector,
|
|
561
|
+
timeout: options.timeout
|
|
562
|
+
};
|
|
563
|
+
window.history.replaceState(mera.state, document.title);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Cancel the current request if we're already meraing
|
|
567
|
+
xhr = mera.xhr;
|
|
568
|
+
|
|
569
|
+
if (xhr && xhr.readyState < 4) {
|
|
570
|
+
xhr.onreadystatechange = $.noop;
|
|
571
|
+
xhr.abort();
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
mera.options = options;
|
|
575
|
+
|
|
576
|
+
xhr = mera.xhr = rails.ajax(options);
|
|
577
|
+
|
|
578
|
+
if (xhr.readyState > 0) {
|
|
579
|
+
if (options.push && !options.replace) {
|
|
580
|
+
// Cache current container element before replacing it
|
|
581
|
+
rails.cachePush(mera.state.id, context.clone().contents());
|
|
582
|
+
|
|
583
|
+
if(options.push !== false)
|
|
584
|
+
window.history.pushState(null, "", options.push);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
fire('ajax:start', [xhr, options]);
|
|
588
|
+
fire('ajax:send', [xhr, options]);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return mera.xhr;
|
|
592
|
+
},
|
|
593
|
+
|
|
594
|
+
// Submits "remote" forms and links with ajax
|
|
595
|
+
// This method handle links, forms and other elements with ajax
|
|
596
|
+
|
|
597
|
+
handleRemote: function(element, dataType) {
|
|
598
|
+
if (rails.fire(element, 'ajax:before')) {
|
|
599
|
+
var obj = rails.findDataFor(element);
|
|
600
|
+
|
|
601
|
+
var options = {
|
|
602
|
+
type: obj.method ? 'POST' : 'GET',
|
|
603
|
+
dataType: dataType ? dataType : 'html',
|
|
604
|
+
container: obj.container,
|
|
605
|
+
url: obj.url,
|
|
606
|
+
data: obj.data,
|
|
607
|
+
push: obj.push,
|
|
608
|
+
credentials: obj.withCredentials,
|
|
609
|
+
crossDomain: obj.crossDomain,
|
|
610
|
+
customEvent: obj.event
|
|
611
|
+
};
|
|
612
|
+
return options.url ? rails.mjax(options) : false;
|
|
613
|
+
} else {
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
|
|
618
|
+
// Handles "data-method" on links such as:
|
|
619
|
+
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
|
|
620
|
+
handleMethod: function(link, remote) {
|
|
621
|
+
var href = rails.href(link),
|
|
622
|
+
method = link.data('method'),
|
|
623
|
+
target = link.attr('target'),
|
|
117
624
|
csrf_token = $('meta[name=csrf-token]').attr('content'),
|
|
118
625
|
csrf_param = $('meta[name=csrf-param]').attr('content'),
|
|
119
|
-
form
|
|
120
|
-
|
|
626
|
+
form = $('<form/>', {method: "post", action: href});
|
|
627
|
+
|
|
628
|
+
$('<input>', {name:"_method", value: method, type: "hidden"}).appendTo(form);
|
|
629
|
+
|
|
630
|
+
if (csrf_param && csrf_token)
|
|
631
|
+
$('<input>', {name: csrf_param, value: csrf_token, type: "hidden"}).appendTo(form);
|
|
632
|
+
|
|
633
|
+
if (target)
|
|
634
|
+
form.attr('target', target);
|
|
635
|
+
|
|
636
|
+
form.hide().appendTo('body');
|
|
637
|
+
return form.submit();
|
|
638
|
+
},
|
|
639
|
+
|
|
640
|
+
// Rails ajax method
|
|
641
|
+
// $.rails.mera({url: "/example/1", method: "put", update: "#response"})
|
|
642
|
+
mera: function(opt){
|
|
643
|
+
var a = $('<a/>', {
|
|
644
|
+
"data-push": opt.push,
|
|
645
|
+
"data-container": opt.update,
|
|
646
|
+
"data-event": opt.customEvent,
|
|
647
|
+
"data-method": opt.method,
|
|
648
|
+
"data-cross-domain": opt.crossDomain,
|
|
649
|
+
"data-with-credentials": opt.withCredentials,
|
|
650
|
+
href: opt.url
|
|
651
|
+
});
|
|
652
|
+
return opt.url ? rails.handleRemote(a, opt.dataType) : false;
|
|
653
|
+
},
|
|
654
|
+
|
|
655
|
+
/* Disables form elements:
|
|
656
|
+
- Caches element value in 'ujs:enable-with' data store
|
|
657
|
+
- Replaces element text with value of 'data-disable-with' attribute
|
|
658
|
+
- Sets disabled property to true
|
|
659
|
+
*/
|
|
660
|
+
disableFormElements: function(form) {
|
|
661
|
+
form.find(rails.disableSelector).each(function() {
|
|
662
|
+
var element = $(this), method = element.is('button') ? 'html' : 'val';
|
|
663
|
+
element.data('ujs:enable-with', element[method]());
|
|
664
|
+
element[method](element.data('disable-with'));
|
|
665
|
+
element.prop('disabled', true);
|
|
666
|
+
});
|
|
667
|
+
},
|
|
668
|
+
|
|
669
|
+
/* Re-enables disabled form elements:
|
|
670
|
+
- Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
|
|
671
|
+
- Sets disabled property to false
|
|
672
|
+
*/
|
|
673
|
+
enableFormElements: function(form) {
|
|
674
|
+
form.find(rails.enableSelector).each(function() {
|
|
675
|
+
var element = $(this), method = element.is('button') ? 'html' : 'val';
|
|
676
|
+
if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
|
|
677
|
+
element.prop('disabled', false);
|
|
678
|
+
});
|
|
679
|
+
},
|
|
680
|
+
|
|
681
|
+
/* For 'data-confirm' attribute:
|
|
682
|
+
- Fires `confirm` event
|
|
683
|
+
- Shows the confirmation dialog
|
|
684
|
+
- Fires the `confirm:complete` event
|
|
685
|
+
|
|
686
|
+
Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
|
|
687
|
+
Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
|
|
688
|
+
Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
|
|
689
|
+
return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
|
|
690
|
+
*/
|
|
691
|
+
allowAction: function(element) {
|
|
692
|
+
var message = element.data('confirm'),
|
|
693
|
+
answer = false, callback;
|
|
694
|
+
if (!message) { return true; }
|
|
121
695
|
|
|
122
|
-
if (
|
|
123
|
-
|
|
696
|
+
if (rails.fire(element, 'confirm')) {
|
|
697
|
+
answer = rails.confirm(message);
|
|
698
|
+
callback = rails.fire(element, 'confirm:complete', [answer]);
|
|
124
699
|
}
|
|
700
|
+
return answer && callback;
|
|
701
|
+
},
|
|
702
|
+
|
|
703
|
+
// Helper function which checks for blank inputs in a form that match the specified CSS selector
|
|
704
|
+
blankInputs: function(form, specifiedSelector, nonBlank) {
|
|
705
|
+
var inputs = $(), input, valueToCheck,
|
|
706
|
+
selector = specifiedSelector || 'input,textarea',
|
|
707
|
+
allInputs = form.find(selector);
|
|
125
708
|
|
|
126
|
-
|
|
709
|
+
allInputs.each(function() {
|
|
710
|
+
input = $(this);
|
|
711
|
+
valueToCheck = input.is(':checkbox,:radio') ? input.is(':checked') : input.val();
|
|
712
|
+
// If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey
|
|
713
|
+
if (!valueToCheck === !nonBlank) {
|
|
127
714
|
|
|
128
|
-
|
|
715
|
+
// Don't count unchecked required radio if other radio with same name is checked
|
|
716
|
+
if (input.is(':radio') && allInputs.filter('input:radio:checked[name="' + input.attr('name') + '"]').length) {
|
|
717
|
+
return true; // Skip to next input
|
|
718
|
+
}
|
|
129
719
|
|
|
130
|
-
|
|
131
|
-
if($options.confirme(confirmLink)){
|
|
132
|
-
$.rails.handleRemote(form);
|
|
720
|
+
inputs = inputs.add(input);
|
|
133
721
|
}
|
|
134
|
-
}
|
|
135
|
-
|
|
722
|
+
});
|
|
723
|
+
return inputs.length ? inputs : false;
|
|
724
|
+
},
|
|
725
|
+
|
|
726
|
+
// Helper function which checks for non-blank inputs in a form that match the specified CSS selector
|
|
727
|
+
nonBlankInputs: function(form, specifiedSelector) {
|
|
728
|
+
return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
// Helper function, needed to provide consistent behavior in IE
|
|
732
|
+
stopEverything: function(e) {
|
|
733
|
+
$(e.target).trigger('ujs:everythingStopped');
|
|
734
|
+
e.stopImmediatePropagation();
|
|
735
|
+
return false;
|
|
736
|
+
},
|
|
737
|
+
|
|
738
|
+
// find all the submit events directly bound to the form and
|
|
739
|
+
// manually invoke them. If anyone returns false then stop the loop
|
|
740
|
+
callFormSubmitBindings: function(form, event) {
|
|
741
|
+
var events = form.data('events'), continuePropagation = true;
|
|
742
|
+
if (events !== undefined && events['submit'] !== undefined) {
|
|
743
|
+
$.each(events['submit'], function(i, obj){
|
|
744
|
+
if (typeof obj.handler === 'function') return continuePropagation = obj.handler(event);
|
|
745
|
+
});
|
|
136
746
|
}
|
|
137
|
-
|
|
747
|
+
return continuePropagation;
|
|
748
|
+
},
|
|
749
|
+
|
|
750
|
+
// replace element's html with the 'data-disable-with' after storing original html
|
|
751
|
+
// and prevent clicking on it
|
|
752
|
+
disableElement: function(element) {
|
|
753
|
+
element.data('ujs:enable-with', element.html()); // store enabled state
|
|
754
|
+
element.html(element.data('disable-with')); // set to disabled state
|
|
755
|
+
element.bind('click.railsDisable', function(e) { // prevent further clicking
|
|
756
|
+
return rails.stopEverything(e);
|
|
757
|
+
});
|
|
758
|
+
},
|
|
759
|
+
|
|
760
|
+
// restore element to its original state which was disabled by 'disableElement' above
|
|
761
|
+
enableElement: function(element) {
|
|
762
|
+
if (element.data('ujs:enable-with') !== undefined) {
|
|
763
|
+
element.html(element.data('ujs:enable-with')); // set to old enabled state
|
|
764
|
+
// this should be element.removeData('ujs:enable-with')
|
|
765
|
+
// but, there is currently a bug in jquery which makes hyphenated data attributes not get removed
|
|
766
|
+
element.data('ujs:enable-with', false); // clean up cache
|
|
767
|
+
}
|
|
768
|
+
element.unbind('click.railsDisable'); // enable element
|
|
769
|
+
},
|
|
770
|
+
|
|
771
|
+
// Handle cookies
|
|
772
|
+
// Can use with get, set and remove
|
|
773
|
+
cookie: {
|
|
774
|
+
get: function(key){
|
|
775
|
+
var name = new String();
|
|
776
|
+
var splitedCookies = document.cookie.split('; ');
|
|
777
|
+
|
|
778
|
+
for(var key in splitedCookies){
|
|
779
|
+
var cookie = splitedCookies[key].split('=');
|
|
780
|
+
if(cookie[0] == key){
|
|
781
|
+
name = decodeURIComponent(cookie[1]);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return name;
|
|
785
|
+
},
|
|
786
|
+
|
|
787
|
+
set: function(name, value, days){
|
|
788
|
+
var today = new Date();
|
|
789
|
+
var expire = new Date();
|
|
790
|
+
|
|
791
|
+
if (days == null || days == 0) {
|
|
792
|
+
days = 1;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
expire.setTime(today.getTime() + (3600000 * 24 * days));
|
|
796
|
+
document.cookie = (
|
|
797
|
+
name + "=" + encodeURIComponent(value)
|
|
798
|
+
+ ";expires=" + expire.toGMTString()
|
|
799
|
+
);
|
|
800
|
+
},
|
|
801
|
+
|
|
802
|
+
remove: function(key){
|
|
803
|
+
document.cookie = key + '=; expires=Thu, 01-Jan-70 00:00:01 GMT;';
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
|
|
807
|
+
//clean white spaces in code
|
|
808
|
+
cleanSpaces: function(data){
|
|
809
|
+
if(data !== undefined)
|
|
810
|
+
return $.trim(data.replace(/ /g,'').replace(/\n/g,''));
|
|
811
|
+
}
|
|
138
812
|
|
|
139
813
|
};
|
|
140
|
-
});
|
|
141
814
|
|
|
815
|
+
if (rails.fire($(document), 'rails:attachBindings')) {
|
|
816
|
+
|
|
817
|
+
$.ajaxPrefilter(function(options, originalOptions, xhr){
|
|
818
|
+
if (!options.crossDomain) rails.CSRFProtection(xhr);
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
$(document).delegate(rails.linkDisableSelector, 'ajax:complete', function() {
|
|
822
|
+
rails.enableElement($(this));
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
$(document).delegate(rails.linkClickSelector, 'click.rails', function(e) {
|
|
826
|
+
var link = $(this),
|
|
827
|
+
method = link.data('method'),
|
|
828
|
+
data = link.data('params');
|
|
829
|
+
|
|
830
|
+
if (!rails.allowAction(link))
|
|
831
|
+
return rails.stopEverything(e);
|
|
832
|
+
|
|
833
|
+
if (link.is(rails.linkDisableSelector))
|
|
834
|
+
rails.disableElement(link);
|
|
835
|
+
|
|
836
|
+
if (link.data('remote')) {
|
|
837
|
+
if ((e.metaKey || e.ctrlKey) && (!method || method === 'GET') && !data )
|
|
838
|
+
return true;
|
|
839
|
+
|
|
840
|
+
var handleRemote = rails.handleRemote(link);
|
|
841
|
+
// response from rails.handleRemote() will either be false or a deferred object promise.
|
|
842
|
+
if (handleRemote === false) {
|
|
843
|
+
rails.enableElement(link);
|
|
844
|
+
} else {
|
|
845
|
+
handleRemote.error(function() {
|
|
846
|
+
rails.enableElement(link);
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
return false;
|
|
850
|
+
} else if (link.data('method')) {
|
|
851
|
+
rails.handleMethod(link);
|
|
852
|
+
return false;
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
$(document).delegate(rails.inputChangeSelector, 'change.rails', function(e) {
|
|
857
|
+
var link = $(this);
|
|
858
|
+
if (!rails.allowAction(link))
|
|
859
|
+
return rails.stopEverything(e);
|
|
860
|
+
|
|
861
|
+
rails.handleRemote(link);
|
|
862
|
+
return false;
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
$(document).delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
|
|
866
|
+
var form = $(this),
|
|
867
|
+
remote = form.data('remote') !== undefined,
|
|
868
|
+
blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector),
|
|
869
|
+
nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
|
|
870
|
+
|
|
871
|
+
if (!rails.allowAction(form))
|
|
872
|
+
return rails.stopEverything(e);
|
|
873
|
+
|
|
874
|
+
// skip other logic when required values are missing or file upload is present
|
|
875
|
+
if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs]))
|
|
876
|
+
return rails.stopEverything(e);
|
|
877
|
+
|
|
878
|
+
if (remote) {
|
|
879
|
+
if (nonBlankFileInputs) {
|
|
880
|
+
// slight timeout so that the submit button gets properly serialized
|
|
881
|
+
// (make it easy for event handler to serialize form without disabled values)
|
|
882
|
+
setTimeout(function(){
|
|
883
|
+
rails.disableFormElements(form);
|
|
884
|
+
}, 13);
|
|
885
|
+
|
|
886
|
+
var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
|
|
887
|
+
|
|
888
|
+
// re-enable form elements if event bindings return false (canceling normal form submission)
|
|
889
|
+
if (!aborted) {setTimeout(function(){
|
|
890
|
+
rails.enableFormElements(form);
|
|
891
|
+
}, 13); }
|
|
892
|
+
|
|
893
|
+
return aborted;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// If browser does not support submit bubbling, then this live-binding will be called before direct
|
|
897
|
+
// bindings. Therefore, we should directly call any direct bindings before remotely submitting form.
|
|
898
|
+
if (!$.support.submitBubbles && $().jquery < '1.7' && rails.callFormSubmitBindings(form, e) === false)
|
|
899
|
+
return rails.stopEverything(e);
|
|
900
|
+
|
|
901
|
+
rails.handleRemote(form);
|
|
902
|
+
return false;
|
|
903
|
+
|
|
904
|
+
} else {
|
|
905
|
+
// slight timeout so that the submit button gets properly serialized
|
|
906
|
+
setTimeout(function(){
|
|
907
|
+
rails.disableFormElements(form);
|
|
908
|
+
}, 13);
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
$(document).delegate(rails.formInputClickSelector, 'click.rails', function(event) {
|
|
913
|
+
var button = $(this);
|
|
914
|
+
|
|
915
|
+
if (!rails.allowAction(button))
|
|
916
|
+
return rails.stopEverything(event);
|
|
917
|
+
|
|
918
|
+
// register the pressed submit button
|
|
919
|
+
var name = button.attr('name'),
|
|
920
|
+
data = name ? {name:name, value:button.val()} : null;
|
|
921
|
+
|
|
922
|
+
button.closest('form').data('ujs:submit-button', data);
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
$(document).delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) {
|
|
926
|
+
if (this == event.target) rails.disableFormElements($(this));
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
$(document).delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) {
|
|
930
|
+
if (this == event.target) rails.enableFormElements($(this));
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
$(function(){
|
|
934
|
+
// making sure that all forms have actual up-to-date token(cached forms contain old one)
|
|
935
|
+
csrf_token = $('meta[name=csrf-token]').attr('content');
|
|
936
|
+
csrf_param = $('meta[name=csrf-param]').attr('content');
|
|
937
|
+
$('form input[name="' + csrf_param + '"]').val(csrf_token);
|
|
938
|
+
|
|
939
|
+
if ($.inArray('state', $.event.props) < 0)
|
|
940
|
+
$.event.props.push('state');
|
|
941
|
+
|
|
942
|
+
if(rails.supportMera()){
|
|
943
|
+
$(window).bind('popstate.ajax', rails.onMeraPopstate);
|
|
944
|
+
window.location.redirect = function(url){
|
|
945
|
+
return $.rails.mera({url: url});
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
})( jQuery );
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* = Jquery_Ujs
|
|
3
|
+
*
|
|
4
|
+
* Unobtrusive scripting adapter for jQuery
|
|
5
|
+
*
|
|
6
|
+
* Requires jQuery 1.6.0 or later.
|
|
7
|
+
* https://github.com/rails/jquery-ujs
|
|
8
|
+
*
|
|
9
|
+
* =========================================
|
|
10
|
+
*
|
|
11
|
+
* = Pjax
|
|
12
|
+
*
|
|
13
|
+
* Pjax, loads HTML from your server into the current page without a full reload. It's ajax with real
|
|
14
|
+
* permalinks, page titles, and a working back button that fully degrades.
|
|
15
|
+
*
|
|
16
|
+
* Requires jQuery 1.7.0 or later.
|
|
17
|
+
* https://github.com/defunkt/jquery-pjax
|
|
18
|
+
*
|
|
19
|
+
* =========================================
|
|
20
|
+
*
|
|
21
|
+
* = Mera
|
|
22
|
+
*
|
|
23
|
+
* Is technology what combine classic Jquery_Ujs (only core) and Pjax (only core), together
|
|
24
|
+
*
|
|
25
|
+
* https://github.com/huttarichard/Mera
|
|
26
|
+
*
|
|
27
|
+
* =========================================
|
|
28
|
+
*
|
|
29
|
+
* Each project is open under Mit license
|
|
30
|
+
*
|
|
31
|
+
*
|
|
32
|
+
* Copyright (c) Mera 2012 & Pjax 2012 & Jquery_Ujs 2012
|
|
33
|
+
*
|
|
34
|
+
* Permission is hereby granted, free of charge, to any person
|
|
35
|
+
* obtaining a copy of this software and associated documentation
|
|
36
|
+
* files (the "Software"), to deal in the Software without
|
|
37
|
+
* restriction, including without limitation the rights to use,
|
|
38
|
+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
39
|
+
* copies of the Software, and to permit persons to whom the
|
|
40
|
+
* Software is furnished to do so, subject to the following
|
|
41
|
+
* conditions:
|
|
42
|
+
*
|
|
43
|
+
* The above copyright notice and this permission notice shall be
|
|
44
|
+
* included in all copies or substantial portions of the Software.
|
|
45
|
+
*
|
|
46
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
47
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
48
|
+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
49
|
+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
50
|
+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
51
|
+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
52
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
53
|
+
* OTHER DEALINGS IN THE SOFTWARE.
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
(function($,undefined){var alreadyInitialized=function(){var events=$._data(document,'events');return events&&events.click&&$.grep(events.click,function(e){return e.namespace==='rails';}).length;};if(alreadyInitialized()){$.error('jquery-ujs has already been loaded!');}var rails;var cacheMapping={};var cacheForwardStack=[];var cacheBackStack=[];var mera={};$.rails=rails={linkClickSelector:'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]',inputChangeSelector:'select[data-remote], input[data-remote], textarea[data-remote]',formSubmitSelector:'form',formInputClickSelector:'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])',disableSelector:'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]',enableSelector:'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled',requiredInputSelector:'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',fileInputSelector:'input:file',linkDisableSelector:'a[data-disable-with]',locationPath:location.pathname,locationUrl:location.href,meraContainer:"body",CSRFProtection:function(xhr){var token=$('meta[name="csrf-token"]').attr('content');if(token)xhr.setRequestHeader('X-CSRF-Token',token);},fire:function(obj,name,data){var event=$.Event(name);obj.trigger(event,data);return event.result!==false;},confirm:function(message){return confirm(message);},ajax:function(options){return $.ajax(options);},ajaxDefault:{timeout:650,push:true,replace:false,type:'GET',dataType:'html',scrollTo:0,maxCacheLength:20},href:function(element){return element.attr('href');},supportMera:function(){return(window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/));},findContainerFor:function(container){container=$(container);if(!container.length){throw"Mera have not container for '"+container.selector+"'!";}else if(container.selector!==''&&container.context===document){return container;}else if(container.attr('id')){return $('#'+container.attr('id'));}else{throw"Can not get selector for Mera container!"}},findDataFor:function(element){var object,method,_method,params,_params;if(element.is('form')){object=element.find('input[name=ajaxParams]').val();if(object&&object.length)eval('object = '+object);else
|
|
58
|
+
object={};object=$.extend(object,{url:element.attr('action'),method:element.attr('method'),data:element.serialize()});}else{object={};object.crossDomain=element.data('cross-domain');object.withCredentials=element.data('with-credentials');object.container=element.data('container');object.event=element.data('event');object.push=element.data('push');method=element.data('method');_method=method?"_method="+method+"&":"";params=element.data('params');_params=params?params:"";object=$.extend(object,{url:(rails.href(element)||element.data('url')),method:method,data:_method+_params});}object.crossDomain=object.crossDomain||false;object.withCredentials=object.withCredentials||false;object.push=object.push==undefined?object.url:object.push;object.container=object.container!==undefined?object.container:rails.meraContainer;object.event=object.event?'ajax:'+object.event:false;return object;},getUrlElement:function(url){return $('<a/>',{href:url}).get(0);},findAll:function findAll(element,selector){return element.filter(selector).add(element.find(selector));},locationReplace:function(url){window.history.replaceState(null,"","/");window.location.replace(url);},extractContainer:function(data,xhr,options){var obj,$head,$body,$fragment;obj={};obj.url=(xhr.getResponseHeader('X-MERA-URL')||options.requestUrl);if(/<html/i.test(data)){$head=$(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]);$body=$(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]);}else{$head=$body=$(data);}if($body.length===0)return obj;obj.title=rails.findAll($head,'title').last().text();if(options.container){if(options.container==='body'){$fragment=$body;}else{$fragment=rails.findAll($body,options.container).first().contents();}if($fragment.length){obj.contents=$fragment;if(!obj.title)obj.title=$fragment.attr('title')||$fragment.data('title');}}else if(!/<html/i.test(data)){obj.contents=$body;}if(obj.contents){obj.contents=obj.contents.not('title');obj.contents.find('title').remove();}if(obj.title)obj.title=$.trim(obj.title);return obj;},uniqueId:function(){return(new Date).getTime();},cachePush:function(id,value){cacheMapping[id]=value;cacheBackStack.push(id);while(cacheForwardStack.length)delete cacheMapping[cacheForwardStack.shift()];while(cacheBackStack.length>rails.ajaxDefault.maxCacheLength)delete cacheMapping[cacheBackStack.shift()];},cachePop:function(direction,id,value){var pushStack,popStack;cacheMapping[id]=value;if(direction==='forward'){pushStack=cacheBackStack;popStack=cacheForwardStack;}else{pushStack=cacheForwardStack;popStack=cacheBackStack;}pushStack.push(id);if(id=popStack.pop())delete cacheMapping[id];},onMeraPopstate:function(event){var state,container,contents,direction,popstateEvent,options;state=event.state;if(state&&state.container){container=$(state.container);if(container.length){contents=cacheMapping[state.id];if(mera.state){direction=mera.state.id<state.id?'forward':'back';rails.cachePop(direction,mera.state.id,container.clone().contents())}popstateEvent=$.Event('ajax:popstate',{state:state,direction:direction});container.trigger(popstateEvent);options={id:state.id,url:state.url,container:container,push:false,timeout:state.timeout,scrollTo:false};if(contents){container.trigger('ajax:start',[null,options]);if(state.title){document.title=state.title;container.html(contents);mera.state=state;}container.trigger('ajax:end',[null,options]);}else{rails.mjax(options);}container[0].offsetHeight;}else{rails.locationReplace(location.href);}}},mjax:function(options){var target,hash,origin,context,timeoutTimer,xhr;options=$.extend(true,{},$.ajaxSettings,rails.ajaxDefault,options);if($.isFunction(options.url)){options.url=options.url();}target=options.target;hash=rails.getUrlElement(options.url).hash;if(!options.container){origin=options.container;options.c=origin;options.container=rails.meraContainer;}else{options.c=options.container;origin=true;}context=options.context=rails.findContainerFor(options.container);function fire(type,args){var event=$.Event(type,{relatedTarget:target});context.trigger(event,args);return!event.isDefaultPrevented();}options.beforeSend=function(xhr,settings){if(settings.type!=='GET')settings.timeout=0;if(settings.timeout>0){timeoutTimer=setTimeout(function(){if(fire('ajax:timeout',[xhr,options]))xhr.abort('timeout');},settings.timeout);settings.timeout=0;}xhr.setRequestHeader('X-MERA','true');xhr.setRequestHeader('X-MERA-CONTAINER',context.selector);if(!fire('ajax:beforeSend',[xhr,settings,options]))return false;options.requestUrl=rails.getUrlElement(settings.url).href;};options.complete=function(xhr,textStatus){if(timeoutTimer)clearTimeout(timeoutTimer);if(options.customEvent)fire(options.customEvent,[xhr,textStatus,options]);fire('ajax:complete',[xhr,textStatus,options]);fire('ajax:end',[xhr,options]);};options.error=function(xhr,textStatus,errorThrown){var container,allowed;container=rails.extractContainer("",xhr,options);allowed=fire('ajax:error',[xhr,textStatus,errorThrown,options]);if(textStatus!=='abort'&&allowed)rails.locationReplace(container.url);};options.success=function(data,status,xhr){var container,url,target;container=rails.extractContainer(data,xhr,options);if(!container.contents){rails.locationReplace(container.url);return;}mera.state={id:options.id||rails.uniqueId(),url:container.url,title:container.title,container:context.selector,timeout:options.timeout};if(options.push||options.replace){window.history.replaceState(mera.state,container.title,options.push)}if(container.title){document.title=container.title;if(origin)context.html(container.contents);}if(typeof options.scrollTo==='number')$(window).scrollTop(options.scrollTo);if((options.replace||options.push)&&window._gaq)_gaq.push(['_trackPageview']);if(hash!==''){url=rails.getUrlElement(container.url);url.hash=hash;mera.state.url=url.href;window.history.replaceState(mera.state,container.title,url.href);target=$(url.hash);if(target.length)$(window).scrollTop(target.offset().top);}fire('ajax:success',[data,status,xhr,options]);};options.xhrFields={withCredentials:options.credentials};if(!mera.state){mera.state={id:rails.uniqueId(),url:window.location.href,title:document.title,container:context.selector,timeout:options.timeout};window.history.replaceState(mera.state,document.title);}xhr=mera.xhr;if(xhr&&xhr.readyState<4){xhr.onreadystatechange=$.noop;xhr.abort();}mera.options=options;xhr=mera.xhr=rails.ajax(options);if(xhr.readyState>0){if(options.push&&!options.replace){rails.cachePush(mera.state.id,context.clone().contents());if(options.push!==false)window.history.pushState(null,"",options.push);}fire('ajax:start',[xhr,options]);fire('ajax:send',[xhr,options]);}return mera.xhr;},handleRemote:function(element,dataType){if(rails.fire(element,'ajax:before')){var obj=rails.findDataFor(element);var options={type:obj.method?'POST':'GET',dataType:dataType?dataType:'html',container:obj.container,url:obj.url,data:obj.data,push:obj.push,credentials:obj.withCredentials,crossDomain:obj.crossDomain,customEvent:obj.event};return options.url?rails.mjax(options):false;}else{return false;}},handleMethod:function(link,remote){var href=rails.href(link),method=link.data('method'),target=link.attr('target'),csrf_token=$('meta[name=csrf-token]').attr('content'),csrf_param=$('meta[name=csrf-param]').attr('content'),form=$('<form/>',{method:"post",action:href});$('<input>',{name:"_method",value:method,type:"hidden"}).appendTo(form);if(csrf_param&&csrf_token)$('<input>',{name:csrf_param,value:csrf_token,type:"hidden"}).appendTo(form);if(target)form.attr('target',target);form.hide().appendTo('body');return form.submit();},mera:function(opt){var a=$('<a/>',{"data-push":opt.push,"data-container":opt.update,"data-event":opt.customEvent,"data-method":opt.method,"data-cross-domain":opt.crossDomain,"data-with-credentials":opt.withCredentials,href:opt.url});return opt.url?rails.handleRemote(a,opt.dataType):false;},disableFormElements:function(form){form.find(rails.disableSelector).each(function(){var element=$(this),method=element.is('button')?'html':'val';element.data('ujs:enable-with',element[method]());element[method](element.data('disable-with'));element.prop('disabled',true);});},enableFormElements:function(form){form.find(rails.enableSelector).each(function(){var element=$(this),method=element.is('button')?'html':'val';if(element.data('ujs:enable-with'))element[method](element.data('ujs:enable-with'));element.prop('disabled',false);});},allowAction:function(element){var message=element.data('confirm'),answer=false,callback;if(!message){return true;}if(rails.fire(element,'confirm')){answer=rails.confirm(message);callback=rails.fire(element,'confirm:complete',[answer]);}return answer&&callback;},blankInputs:function(form,specifiedSelector,nonBlank){var inputs=$(),input,valueToCheck,selector=specifiedSelector||'input,textarea',allInputs=form.find(selector);allInputs.each(function(){input=$(this);valueToCheck=input.is(':checkbox,:radio')?input.is(':checked'):input.val();if(!valueToCheck===!nonBlank){if(input.is(':radio')&&allInputs.filter('input:radio:checked[name="'+input.attr('name')+'"]').length){return true;}inputs=inputs.add(input);}});return inputs.length?inputs:false;},nonBlankInputs:function(form,specifiedSelector){return rails.blankInputs(form,specifiedSelector,true);},stopEverything:function(e){$(e.target).trigger('ujs:everythingStopped');e.stopImmediatePropagation();return false;},callFormSubmitBindings:function(form,event){var events=form.data('events'),continuePropagation=true;if(events!==undefined&&events['submit']!==undefined){$.each(events['submit'],function(i,obj){if(typeof obj.handler==='function')return continuePropagation=obj.handler(event);});}return continuePropagation;},disableElement:function(element){element.data('ujs:enable-with',element.html());element.html(element.data('disable-with'));element.bind('click.railsDisable',function(e){return rails.stopEverything(e);});},enableElement:function(element){if(element.data('ujs:enable-with')!==undefined){element.html(element.data('ujs:enable-with'));element.data('ujs:enable-with',false);}element.unbind('click.railsDisable');},cookie:{get:function(key){var name=new String();var splitedCookies=document.cookie.split('; ');for(var key in splitedCookies){var cookie=splitedCookies[key].split('=');if(cookie[0]==key){name=decodeURIComponent(cookie[1]);}}return name;},set:function(name,value,days){var today=new Date();var expire=new Date();if(days==null||days==0){days=1;}expire.setTime(today.getTime()+(3600000*24*days));document.cookie=(name+"="+encodeURIComponent(value)+";expires="+expire.toGMTString());},remove:function(key){document.cookie=key+'=; expires=Thu, 01-Jan-70 00:00:01 GMT;';}},cleanSpaces:function(data){if(data!==undefined)return $.trim(data.replace(/ /g,'').replace(/\n/g,''));}};if(rails.fire($(document),'rails:attachBindings')){$.ajaxPrefilter(function(options,originalOptions,xhr){if(!options.crossDomain)rails.CSRFProtection(xhr);});$(document).delegate(rails.linkDisableSelector,'ajax:complete',function(){rails.enableElement($(this));});$(document).delegate(rails.linkClickSelector,'click.rails',function(e){var link=$(this),method=link.data('method'),data=link.data('params');if(!rails.allowAction(link))return rails.stopEverything(e);if(link.is(rails.linkDisableSelector))rails.disableElement(link);if(link.data('remote')){if((e.metaKey||e.ctrlKey)&&(!method||method==='GET')&&!data)return true;var handleRemote=rails.handleRemote(link);if(handleRemote===false){rails.enableElement(link);}else{handleRemote.error(function(){rails.enableElement(link);});}return false;}else if(link.data('method')){rails.handleMethod(link);return false;}});$(document).delegate(rails.inputChangeSelector,'change.rails',function(e){var link=$(this);if(!rails.allowAction(link))return rails.stopEverything(e);rails.handleRemote(link);return false;});$(document).delegate(rails.formSubmitSelector,'submit.rails',function(e){var form=$(this),remote=form.data('remote')!==undefined,blankRequiredInputs=rails.blankInputs(form,rails.requiredInputSelector),nonBlankFileInputs=rails.nonBlankInputs(form,rails.fileInputSelector);if(!rails.allowAction(form))return rails.stopEverything(e);if(blankRequiredInputs&&form.attr("novalidate")==undefined&&rails.fire(form,'ajax:aborted:required',[blankRequiredInputs]))return rails.stopEverything(e);if(remote){if(nonBlankFileInputs){setTimeout(function(){rails.disableFormElements(form);},13);var aborted=rails.fire(form,'ajax:aborted:file',[nonBlankFileInputs]);if(!aborted){setTimeout(function(){rails.enableFormElements(form);},13);}return aborted;}if(!$.support.submitBubbles&&$().jquery<'1.7'&&rails.callFormSubmitBindings(form,e)===false)return rails.stopEverything(e);rails.handleRemote(form);return false;}else{setTimeout(function(){rails.disableFormElements(form);},13);}});$(document).delegate(rails.formInputClickSelector,'click.rails',function(event){var button=$(this);if(!rails.allowAction(button))return rails.stopEverything(event);var name=button.attr('name'),data=name?{name:name,value:button.val()}:null;button.closest('form').data('ujs:submit-button',data);});$(document).delegate(rails.formSubmitSelector,'ajax:beforeSend.rails',function(event){if(this==event.target)rails.disableFormElements($(this));});$(document).delegate(rails.formSubmitSelector,'ajax:complete.rails',function(event){if(this==event.target)rails.enableFormElements($(this));});$(function(){csrf_token=$('meta[name=csrf-token]').attr('content');csrf_param=$('meta[name=csrf-param]').attr('content');$('form input[name="'+csrf_param+'"]').val(csrf_token);if($.inArray('state',$.event.props)<0)$.event.props.push('state');if(rails.supportMera()){$(window).bind('popstate.ajax',rails.onMeraPopstate);window.location.redirect=function(url){return $.rails.mera({url:url});}}});}})(jQuery);
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mera
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,33 +9,19 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
13
|
-
dependencies:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
|
17
|
-
none: false
|
|
18
|
-
requirements:
|
|
19
|
-
- - ! '>='
|
|
20
|
-
- !ruby/object:Gem::Version
|
|
21
|
-
version: '0'
|
|
22
|
-
type: :runtime
|
|
23
|
-
prerelease: false
|
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
-
none: false
|
|
26
|
-
requirements:
|
|
27
|
-
- - ! '>='
|
|
28
|
-
- !ruby/object:Gem::Version
|
|
29
|
-
version: '0'
|
|
30
|
-
description:
|
|
12
|
+
date: 2012-10-07 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: Simple way how to make project with ajax. Mera handle pushState, updating
|
|
15
|
+
and buttons in browser!
|
|
31
16
|
email: huttarichard@gmail.com
|
|
32
17
|
executables: []
|
|
33
18
|
extensions: []
|
|
34
19
|
extra_rdoc_files: []
|
|
35
20
|
files:
|
|
36
21
|
- lib/assets/javascripts/mera.js
|
|
22
|
+
- lib/assets/javascripts/mera.min.js
|
|
37
23
|
- lib/mera.rb
|
|
38
|
-
homepage:
|
|
24
|
+
homepage: https://github.com/huttarichard/Mera
|
|
39
25
|
licenses: []
|
|
40
26
|
post_install_message:
|
|
41
27
|
rdoc_options: []
|
|
@@ -58,5 +44,5 @@ rubyforge_project:
|
|
|
58
44
|
rubygems_version: 1.8.24
|
|
59
45
|
signing_key:
|
|
60
46
|
specification_version: 3
|
|
61
|
-
summary: My Easy Rails Ajax
|
|
47
|
+
summary: My Easy Rails Ajax
|
|
62
48
|
test_files: []
|