ajax 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.1.3
data/lib/ajax.rb CHANGED
@@ -25,7 +25,10 @@ module Ajax
25
25
  def self.is_enabled?
26
26
  @enabled.nil? ? true : !!@enabled
27
27
  end
28
-
28
+ class << self
29
+ alias_method :enabled?, :is_enabled?
30
+ end
31
+
29
32
  # Set to false to disable this plugin completely.
30
33
  #
31
34
  # ActionController and ActionView helpers are still mixed in but
@@ -35,6 +38,24 @@ module Ajax
35
38
  @enabled = !!value
36
39
  end
37
40
 
41
+ # Return a boolean indicating whether to enable lazy loading assets.
42
+ # There are currently issues with some browsers when using this feature.
43
+ #
44
+ # Disabled by default.
45
+ def self.lazy_load_assets?
46
+ @lazy_load_assets.nil? ? false : !!@lazy_load_assets
47
+ end
48
+
49
+ # Set to false to disable lazy loading assets. Callbacks will
50
+ # be executed immediately.
51
+ #
52
+ # ActionController and ActionView helpers are still mixed in but
53
+ # they are effectively disabled, which means your code will still
54
+ # run.
55
+ def self.lazy_load_assets=(value)
56
+ @lazy_load_assets = !!value
57
+ end
58
+
38
59
  # Return a boolean indicating whether the plugin is being mock tested.
39
60
  #
40
61
  # Mocking forces the environment to be returned after Ajax processing
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  module Ajax
2
4
  module Helpers
3
5
  module RequestHelper
@@ -15,33 +17,45 @@ module Ajax
15
17
  # Hash and/or Array values are merged so you can set multiple values
16
18
  def set_header(object, key, value)
17
19
  headers = object.is_a?(::ActionController::Response) ? object.headers : object
18
- unless headers["Ajax-Info"].is_a?(Hash)
19
- headers["Ajax-Info"] = {}
20
+
21
+ info = case headers["Ajax-Info"]
22
+ when String
23
+ JSON.parse(headers["Ajax-Info"])
24
+ when Hash
25
+ headers["Ajax-Info"]
26
+ else
27
+ {}
20
28
  end
21
29
 
22
30
  # Deep merge hashes
23
- if headers["Ajax-Info"].has_key?(key.to_s) &&
31
+ if info.has_key?(key.to_s) &&
24
32
  value.is_a?(Hash) &&
25
- headers["Ajax-Info"][key.to_s].is_a?(Hash)
26
- value = headers["Ajax-Info"][key.to_s].merge(value, &DEEP_MERGE)
33
+ info[key.to_s].is_a?(Hash)
34
+ value = info[key.to_s].merge(value, &DEEP_MERGE)
27
35
  end
28
36
 
29
37
  # Concat arrays
30
- if headers["Ajax-Info"].has_key?(key.to_s) &&
38
+ if info.has_key?(key.to_s) &&
31
39
  value.is_a?(Array) &&
32
- headers["Ajax-Info"][key.to_s].is_a?(Array)
33
- value = headers["Ajax-Info"][key.to_s].concat(value)
40
+ info[key.to_s].is_a?(Array)
41
+ value = info[key.to_s].concat(value)
34
42
  end
35
43
 
36
- headers["Ajax-Info"][key.to_s] = value
44
+ info[key.to_s] = value
45
+ headers["Ajax-Info"] = info.to_json
37
46
  end
38
47
 
39
48
  def get_header(object, key)
40
49
  headers = object.is_a?(::ActionController::Request) ? object.headers : object
41
- unless headers["Ajax-Info"].is_a?(Hash)
42
- headers["Ajax-Info"] = {}
50
+ info = case headers["Ajax-Info"]
51
+ when String
52
+ JSON.parse(headers["Ajax-Info"])
53
+ when Hash
54
+ headers["Ajax-Info"]
55
+ else
56
+ {}
43
57
  end
44
- headers['Ajax-Info'][key.to_s]
58
+ info[key.to_s]
45
59
  end
46
60
 
47
61
  # Set one or more paths that can be accessed directly without the AJAX framework.
@@ -1,3 +1,64 @@
1
+ /**
2
+ * Script lazy loader 0.5
3
+ * Copyright (c) 2008 Bob Matsuoka
4
+ *
5
+ * This program is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU General Public License
7
+ * as published by the Free Software Foundation; either version 2
8
+ * of the License, or (at your option) any later version.
9
+ */
10
+
11
+ var LazyLoader = {}; //namespace
12
+ LazyLoader.timer = {}; // contains timers for scripts
13
+ LazyLoader.scripts = []; // contains called script references
14
+ LazyLoader.load = function(url, callback) {
15
+ // handle object or path
16
+ var classname = null;
17
+ var properties = null;
18
+ try {
19
+ // make sure we only load once
20
+ if (LazyLoader.scripts.indexOf(url) == -1) {
21
+ // note that we loaded already
22
+ LazyLoader.scripts.push(url);
23
+ var script = document.createElement("script");
24
+ script.src = url;
25
+ script.type = "text/javascript";
26
+ $(script).appendTo("head"); // add script tag to head element
27
+
28
+ // was a callback requested
29
+ if (callback) {
30
+ // test for onreadystatechange to trigger callback
31
+ script.onreadystatechange = function () {
32
+ if (script.readyState == 'loaded' || script.readyState == 'complete') {
33
+ callback();
34
+ }
35
+ };
36
+
37
+ // test for onload to trigger callback
38
+ script.onload = function () {
39
+ callback();
40
+ return;
41
+ };
42
+
43
+ // safari doesn't support either onload or readystate, create a timer
44
+ // only way to do this in safari
45
+ if (($.browser.webkit && !navigator.userAgent.match(/Version\/3/)) || $.browser.opera) { // sniff
46
+ LazyLoader.timer[url] = setInterval(function() {
47
+ if (/loaded|complete/.test(document.readyState)) {
48
+ clearInterval(LazyLoader.timer[url]);
49
+ callback(); // call the callback handler
50
+ }
51
+ }, 10);
52
+ }
53
+ }
54
+ } else {
55
+ if (callback) { callback(); }
56
+ }
57
+ } catch (e) {
58
+ alert(e);
59
+ }
60
+ }
61
+
1
62
  /**
2
63
  * AjaxAssets
3
64
  *
@@ -28,22 +89,28 @@ var AjaxAssets = function(array, type) {
28
89
  addAsset: function(path) {
29
90
  this.push(this.sanitizePath(path));
30
91
  },
31
-
92
+
32
93
  /**
33
94
  * Load and add an asset. The asset is loaded using the
34
95
  * unsanitized path should you need to put something in the
35
96
  * query string.
36
97
  */
37
- loadAsset: function(path) {
98
+ loadAsset: function(path, callback) {
38
99
  console.log('[ajax] loading', type, path);
39
100
  this.push(this.sanitizePath(path));
40
- this.appendScriptTag(path);
101
+ if (type == 'css') {
102
+ this.appendScriptTag(path, callback);
103
+ } else if ($.browser.msie || $.browser.mozilla) {
104
+ this.appendScriptTag(path, callback);
105
+ } else {
106
+ LazyLoader.load(path, callback);
107
+ }
41
108
  },
42
109
 
43
110
  /**
44
111
  * Return a boolean indicating whether an asset has
45
112
  * already been loaded.
46
- */
113
+ */
47
114
  loadedAsset: function(path) {
48
115
  path = this.sanitizePath(path);
49
116
  for (var i=0; i < this.length; i++) {
@@ -51,47 +118,45 @@ var AjaxAssets = function(array, type) {
51
118
  return true;
52
119
  }
53
120
  }
54
- return false;
121
+ return false;
55
122
  },
56
123
 
57
124
  /**
58
125
  * Remove query strings and otherwise cleanup paths
59
126
  * before adding them.
60
- */
127
+ */
61
128
  sanitizePath: function(path) {
62
129
  return path.replace(/\?.*/, '');
63
130
  },
64
-
131
+
65
132
  /**
66
133
  * Supports debugging and references the script files as external resources
67
134
  * rather than inline.
68
135
  *
69
136
  * @see http://stackoverflow.com/questions/690781/debugging-scripts-added-via-jquery-getscript-function
70
- */
137
+ */
71
138
  appendScriptTag: function(url, callback) {
72
139
  if (type == 'js') {
73
140
  var head = document.getElementsByTagName("head")[0];
74
141
  var script = document.createElement("script");
75
142
  script.src = url;
76
143
  script.type = 'text/javascript'
77
-
78
- { // Handle Script loading
144
+ head.appendChild(script);
145
+ // Handle Script loading
146
+ if (callback) {
79
147
  var done = false;
80
-
81
- // Attach handlers for all browsers
82
148
  script.onload = script.onreadystatechange = function(){
83
149
  if ( !done && (!this.readyState ||
84
150
  this.readyState == "loaded" || this.readyState == "complete") ) {
85
151
  done = true;
86
152
  if (callback)
87
153
  callback();
88
-
154
+
89
155
  // Handle memory leak in IE
90
156
  script.onload = script.onreadystatechange = null;
91
157
  }
92
158
  };
93
- }
94
- head.appendChild(script);
159
+ }
95
160
  } else if (type == 'css') {
96
161
  if (url.match(/datauri/)) {
97
162
  $(DATA_URI_START + '<link type="text/css" rel="stylesheet" href="'+ url +'">' + DATA_URI_END).appendTo('head');
@@ -102,7 +167,7 @@ var AjaxAssets = function(array, type) {
102
167
  }
103
168
  }
104
169
  return undefined;
105
- }
170
+ }
106
171
  });
107
172
  };
108
173
 
@@ -114,20 +179,20 @@ var AjaxAssets = function(array, type) {
114
179
  * This must be set if you are using Ajax callbacks in your code,
115
180
  * and you want them to still fire if Ajax is not enabled.
116
181
  *
117
- * <tt>default_container</tt> string jQuery selector of the default
182
+ * <tt>default_container</tt> string jQuery selector of the default
118
183
  * container element to receive content.
119
184
  *
120
185
  * Callbacks:
121
186
  *
122
187
  * Callbacks can be specified using Ajax-Info{ callbacks: 'javascript to eval' },
123
188
  * or by adding callbacks directly to the Ajax instance:
124
- *
189
+ *
125
190
  * window.ajax.onLoad(function() { doSomething(args); });
126
191
  *
127
192
  * Order of execution:
128
193
  *
129
194
  *
130
- */
195
+ */
131
196
  var Ajax = function(options) {
132
197
  var self = this;
133
198
 
@@ -139,28 +204,32 @@ var Ajax = function(options) {
139
204
  self.stylesheets = new AjaxAssets([], 'css');
140
205
  self.callbacks = [];
141
206
  self.loaded = false;
207
+ self.lazy_load_assets = false;
142
208
 
143
209
  // For initial position of the loading icon. Often the mouse does not
144
210
  // move so position it by the link that was clicked.
145
211
  self.last_click_coords = undefined;
146
212
 
147
213
  // Parse options
148
- self.options = options;
214
+ self.options = options;
149
215
  self.default_container = options.default_container;
150
216
  if (options.enabled !== undefined) {
151
217
  self.enabled = options.enabled;
152
218
  }
153
-
219
+ if (options.lazy_load_assets !== undefined) {
220
+ self.lazy_load_assets = options.lazy_load_assets;
221
+ }
222
+
154
223
  // Initialize on DOM ready
155
224
  $(function() { self.init() });
156
-
225
+
157
226
  /**
158
227
  * Initializations run on DOM ready.
159
228
  *
160
229
  * Bind event handlers and setup jQuery Address.
161
230
  */
162
231
  self.init = function() {
163
-
232
+
164
233
  // Configure jQuery Address
165
234
  $.address.history(true);
166
235
  $.address.change = self.addressChanged;
@@ -168,21 +237,21 @@ var Ajax = function(options) {
168
237
  // Insert loading image
169
238
  var image = '<img src="/images/loading-icon-small.gif" id="loading-icon-small" alt="Loading..." />'
170
239
  $(image).hide().appendTo($('body'));
171
-
240
+
172
241
  // Bind a live event to all ajax-enabled links
173
242
  $('a[data-deep-link]').live('click', self.linkClicked);
174
-
243
+
175
244
  // Initialize the list of javascript assets
176
245
  if (self.javascripts === undefined) {
177
246
  self.javascripts = new AjaxAssets([], 'js');
178
-
247
+
179
248
  $(document).find('script[type=text/javascript][src!=]').each(function() {
180
249
  var script = $(this);
181
250
  var src = script.attr('src');
182
-
251
+
183
252
  // Local scripts only
184
253
  if (src.match(/^\//)) {
185
-
254
+
186
255
  // Parse parameters passed to the script via the query string.
187
256
  // TODO: Untested. It's difficult for us to use this with Jammit.
188
257
  if (src.match(/\Wajax.js\?.+/)) {
@@ -190,7 +259,7 @@ var Ajax = function(options) {
190
259
  jQuery.each(params, function(idx, param) {
191
260
  param = param.split('=');
192
261
  if (param.length == 1) { return true; }
193
-
262
+
194
263
  switch(param[0]) {
195
264
  case 'enabled':
196
265
  self.enabled = param[1] == 'false' ? false : true;
@@ -203,33 +272,33 @@ var Ajax = function(options) {
203
272
  }
204
273
  });
205
274
  }
206
-
275
+
207
276
  self.javascripts.addAsset(script.attr('src'));
208
277
  }
209
278
  });
210
279
  }
211
280
  self.initialized = true;
212
-
281
+
213
282
  // Run onInit() callbacks
214
283
  };
215
-
284
+
216
285
  /**
217
286
  * jQuery Address callback triggered when the address changes.
218
287
  */
219
288
  self.addressChanged = function() {
220
289
  if (document.location.pathname != '/') { return false; }
221
-
222
- if (typeof(self.loaded_by_framework) == 'undefined' || self.loaded_by_framework != true) {
290
+ if (window.ajax.disable_address_intercept == true) {return false;}
291
+ if (typeof(self.loaded_by_framework) == 'undefined' || self.loaded_by_framework != true) {
223
292
  self.loaded_by_framework = true;
224
- return false;
225
- }
226
-
227
- self.loadPage({
293
+ return false;
294
+ }
295
+
296
+ self.loadPage({
228
297
  url: $.address.value().replace(/\/\//, '/')
229
298
  });
230
299
  return true;
231
300
  };
232
-
301
+
233
302
  /**
234
303
  * loadPage
235
304
  *
@@ -246,7 +315,7 @@ var Ajax = function(options) {
246
315
  * Cookies in the response are automatically set on the document.cookie.
247
316
  */
248
317
  self.loadPage = function(options) {
249
- if (!self.enabled) {
318
+ if (!self.enabled) {
250
319
  document.location = options.url;
251
320
  return true;
252
321
  }
@@ -343,16 +412,16 @@ var Ajax = function(options) {
343
412
  console.log('Using page title '+data.title);
344
413
  $.address.title(data.title);
345
414
  }
346
-
415
+
347
416
  if (data.tab !== undefined) {
348
417
  console.log('Activating tab '+data.tab);
349
418
  $(data.tab).trigger('activate');
350
419
  }
351
-
420
+
352
421
  /**
353
- * Load assets
422
+ * Load stylesheets
354
423
  */
355
- if (data.assets !== undefined && data.assets.stylesheets !== undefined) {
424
+ if (self.lazy_load_assets && data.assets && data.assets.stylesheets !== undefined) {
356
425
  jQuery.each(jQuery.makeArray(data.assets.stylesheets), function(idx, url) {
357
426
  if (self.stylesheets.loadedAsset(url)) {
358
427
  console.log('[ajax] skipping css', url);
@@ -362,17 +431,6 @@ var Ajax = function(options) {
362
431
  }
363
432
  });
364
433
  }
365
-
366
- if (data.assets !== undefined && data.assets.javascripts !== undefined) {
367
- jQuery.each(jQuery.makeArray(data.assets.javascripts), function(idx, url) {
368
- if (self.javascripts.loadedAsset(url)) {
369
- console.log('[ajax] skipping js', url);
370
- return true;
371
- } else {
372
- self.javascripts.loadAsset(url);
373
- }
374
- });
375
- }
376
434
 
377
435
  /**
378
436
  * Insert response
@@ -381,30 +439,49 @@ var Ajax = function(options) {
381
439
  console.log('Set data ',data);
382
440
  container.data('ajax-info', data)
383
441
  container.html(responseText);
384
-
442
+
385
443
  /**
386
- * Execute callbacks
444
+ * Include callbacks from Ajax-Info
387
445
  */
388
- if (data.callbacks !== undefined) {
389
- jQuery.each(jQuery.makeArray(data.callbacks), function(idx, callback) {
390
- self.executeCallback(callback);
391
- });
446
+ if (data.callbacks) {
447
+ data.callbacks = jQuery.makeArray(data.callbacks);
448
+ self.callbacks.concat(data.callbacks);
392
449
  }
393
-
394
- if (self.callbacks.length > 0) {
395
- jQuery.each(self.callbacks, function(idx, callback) {
396
- self.executeCallback(callback);
450
+
451
+ /**
452
+ * Load javascipts
453
+ */
454
+ if (self.lazy_load_assets && data.assets && data.assets.javascripts !== undefined) {
455
+ var count = data.assets.javascripts.length;
456
+ var callback;
457
+
458
+ jQuery.each(jQuery.makeArray(data.assets.javascripts), function(idx, url) {
459
+ if (self.javascripts.loadedAsset(url)) {
460
+ console.log('[ajax] skipping js', url);
461
+ return true;
462
+ }
463
+
464
+ // Execute callbacks once the last asset has loaded
465
+ callback = (idx == count - 1) ? undefined : self.executeCallbacks;
466
+ self.javascripts.loadAsset(url, callback);
397
467
  });
398
- self.callbacks = [];
468
+ } else {
469
+ // Execute callbacks immediately
470
+ self.executeCallbacks();
399
471
  }
400
-
472
+
473
+ $(document).trigger('ajax.onload');
474
+
401
475
  /**
402
- * Set cookies
476
+ * Set cookies - browsers don't seem to allow this
403
477
  */
404
- var cookie = XMLHttpRequest.getResponseHeader('Set-Cookie');
405
- if (cookie !== null) {
406
- console.log('Setting cookie');
407
- document.cookie = cookie;
478
+ try {
479
+ var cookie = XMLHttpRequest.getResponseHeader('Set-Cookie');
480
+ if (cookie !== null) {
481
+ console.log('Setting cookie');
482
+ document.cookie = cookie;
483
+ }
484
+ } catch(e) {
408
485
  }
409
486
  };
410
487
 
@@ -432,28 +509,33 @@ var Ajax = function(options) {
432
509
  */
433
510
  self.showLoadingImage = function() {
434
511
  var icon = $('#loading-icon-small');
435
-
512
+
436
513
  // Follow the mouse pointer
437
514
  $(document).bind('mousemove', self.updateImagePosition);
438
-
515
+
439
516
  // Display at last click coords initially
440
517
  if (self.last_click_coords !== undefined) {
441
518
  self.updateImagePosition(self.last_click_coords);
442
-
519
+
443
520
  // Center it
444
521
  } else {
522
+ var marginTop = parseInt(icon.css('marginTop'), 10);
523
+ var marginLeft = parseInt(icon.css('marginLeft'), 10);
524
+ marginTop = isNaN(marginTop) ? 0 : marginTop;
525
+ marginLeft = isNaN(marginLeft) ? 0 : marginLeft;
526
+
445
527
  icon.css({
446
- position: 'absolute',
447
- left: '50%',
448
- top: '50%',
449
- zIndex: '99',
450
- marginTop: parseInt(icon.css('marginTop'), 10) + jQuery(window).scrollTop(),
451
- marginLeft: parseInt(icon.css('marginLeft'), 10) + jQuery(window).scrollLeft()
452
- });
453
- }
454
- icon.show();
528
+ position: 'absolute',
529
+ left: '50%',
530
+ top: '50%',
531
+ zIndex: '99',
532
+ marginTop: marginTop + jQuery(window).scrollTop(),
533
+ marginLeft: marginLeft + jQuery(window).scrollLeft()
534
+ });
535
+ }
536
+ icon.show();
455
537
  };
456
-
538
+
457
539
  /**
458
540
  * Update the position of the loading icon.
459
541
  */
@@ -466,7 +548,7 @@ var Ajax = function(options) {
466
548
  });
467
549
  };
468
550
 
469
-
551
+
470
552
  /**
471
553
  * onLoad
472
554
  *
@@ -478,9 +560,9 @@ var Ajax = function(options) {
478
560
  * on DOM ready.
479
561
  */
480
562
  self.onLoad = function(callback) {
481
- if (self.enabled && !self.loaded) {
563
+ if (self.enabled && (self.lazy_load_assets && !self.loaded)) {
482
564
  self.callbacks.push(callback);
483
- console.log('[ajax] appending callback', callback);
565
+ console.log('[ajax] appending callback', self.teaser(callback));
484
566
  } else {
485
567
  self.executeCallback(callback, true);
486
568
  }
@@ -494,14 +576,27 @@ var Ajax = function(options) {
494
576
  * @see onLoad
495
577
  */
496
578
  self.prependOnLoad = function(callback) {
497
- if (self.enabled && !self.loaded) {
579
+ if (self.enabled && (self.lazy_load_assets && !self.loaded)) {
498
580
  self.callbacks.unshift(callback);
499
- console.log('[ajax] prepending callback', callback);
581
+ console.log('[ajax] prepending callback', self.teaser(callback));
500
582
  } else {
501
583
  self.executeCallback(callback, true);
502
584
  }
503
585
  };
504
-
586
+
587
+ /**
588
+ * Execute callbacks
589
+ */
590
+ self.executeCallbacks = function() {
591
+ var callbacks = jQuery.makeArray(self.callbacks);
592
+ if (callbacks.length > 0) {
593
+ jQuery.each(callbacks, function(idx, callback) {
594
+ self.executeCallback(callback);
595
+ });
596
+ self.callbacks = [];
597
+ }
598
+ };
599
+
505
600
  /**
506
601
  * Execute a callback given as a string or function reference.
507
602
  *
@@ -513,8 +608,8 @@ var Ajax = function(options) {
513
608
  $(function() {
514
609
  self.executeCallback(callback);
515
610
  })
516
- } else {
517
- console.log('[ajax] executing callback', callback);
611
+ } else {
612
+ console.log('[ajax] executing callback', self.teaser(callback));
518
613
  try {
519
614
  if (jQuery.isFunction(callback)) {
520
615
  callback();
@@ -524,6 +619,10 @@ var Ajax = function(options) {
524
619
  } catch(e) {
525
620
  console.log('[ajax] callback failed with exception', e);
526
621
  }
527
- }
528
- };
622
+ }
623
+ };
624
+
625
+ self.teaser = function(callback) {
626
+ return new String(callback).slice(0,50);
627
+ };
529
628
  };
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ajax
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karl Varga
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-04-20 00:00:00 -07:00
12
+ date: 2010-04-21 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency