ajax 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <b>A Ruby on Rails plugin to augment a traditional Rails application with a completely AJAX frontend, while transparently handling issues important to both the enterprise and end users, such as testing, SEO and browser history.</b>
4
4
 
5
- The Ajax philosophy is that you should not have to develop for AJAX: Your code shouldn't change; your tests shouldn't change; and the way Google sees your site shouldn't change. (Coming soon, this link {AJAX site crawling specification}[http://code.google.com/web/ajaxcrawling/docs/getting-started.html] may obviate that.)
5
+ The Ajax philosophy is that you shouldn't have to develop for AJAX: Your code shouldn't change; your tests shouldn't change; and the way Google sees your site shouldn't change.
6
6
 
7
7
  The beauty of Ajax is that your Rails application only ever sees traditional requests, so it does not have to be "Ajax aware".
8
8
 
@@ -10,8 +10,6 @@ Ajax is being used live in production on altnet.com[http://altnet.com], if you w
10
10
 
11
11
  == Install
12
12
 
13
- After installing, create layouts in <tt>app/views/layouts/ajax/</tt> that mimic your existing layouts. See <b>Request Handling -> Layouts</b>.
14
-
15
13
  === Rails 3
16
14
 
17
15
  1. Add the gem to your <tt>Gemspec</tt>
@@ -47,20 +45,31 @@ After installing, create layouts in <tt>app/views/layouts/ajax/</tt> that mimic
47
45
 
48
46
  === Sample Output
49
47
 
50
- $ rake ajax:install
51
- created: app/controllers/ajax_controller.rb
52
- created: app/views/ajax/framework.html.erb
53
- created: config/initializers/ajax.rb
54
- created: public/javascripts/ajax.js
55
- created: public/javascripts/jquery.address-1.2rc.js
56
- created: public/javascripts/jquery.address-1.2rc.min.js
57
- created: public/javascripts/jquery.json-2.2.min.js
58
- created: public/images/loading-icon-small.gif
59
-
60
- $ rake ajax:install
61
- skipped: app/controllers/ajax_controller.rb exists!
62
- skipped: app/views/ajax/framework.html.erb exists!
63
- ...
48
+ $ rake ajax:install
49
+ created: app/controllers/ajax_controller.rb
50
+ created: app/views/ajax/framework.html.erb
51
+ created: config/initializers/ajax.rb
52
+ created: public/javascripts/ajax.js
53
+ created: public/javascripts/jquery.address-1.2rc.js
54
+ created: public/javascripts/jquery.address-1.2rc.min.js
55
+ created: public/javascripts/jquery.json-2.2.min.js
56
+ created: public/images/ajax-loading.gif
57
+
58
+ $ rake ajax:install
59
+ skipped: app/controllers/ajax_controller.rb exists!
60
+ skipped: app/views/ajax/framework.html.erb exists!
61
+ ...
62
+
63
+ === Post Install
64
+
65
+ 1. Create layouts in <tt>app/views/layouts/ajax/</tt> that mimic your existing layouts. See <b>Request Handling -> Layouts</b>.
66
+ 2. Instantiate an instance of the Ajax class in <tt>application.js</tt>. For example:
67
+
68
+ window.ajax = new Ajax({
69
+ default_container: '#main',
70
+ enabled: true,
71
+ lazy_load_assets: false
72
+ });
64
73
 
65
74
  == Introduction
66
75
 
@@ -156,6 +165,10 @@ Please browse the {API documentation at rDoc.info}[http://rdoc.info/projects/kjv
156
165
 
157
166
  It is important to be able to disable the plugin when you don't want it interfering, like when you are testing. You will also want to ensure that your site's JavaScript still works when the plugin is disabled.
158
167
 
168
+ <b>If Ajax is disabled</b>, your site will act like a traditional Rails application. Because each request will be a traditional request, <b>callbacks specified in the Ajax-Info header will not be parsed by the browser, and so will not execute.</b>
169
+
170
+ <b>Callbacks added directly to the <tt>window.ajax</tt> instance will still be executed, and they will execute immediately.</b>
171
+
159
172
  To disable the plugin in your environment file:
160
173
 
161
174
  # config/environments/test.rb
@@ -252,40 +265,118 @@ See {ajax/two_column.html.haml}[http://gist.github.com/373133#file_two_column.ht
252
265
 
253
266
  == Lazy-loading Assets
254
267
 
268
+ <b>KJV 2010-04-22:</b> Browser support for callbacks (specifically the problem of calling them only *after* all assets have loaded) is patchy/inconsistent at this time so lazy-loading is not recommended. It has been disabled by default. Once all browsers can be supported this may change.
269
+
270
+ <b>The recommended way of dynamically enabling/disabling lazy loading:</b>
271
+
272
+ # environment/initializer
273
+ Ajax.lazy_load_assets = false # or true
274
+
275
+ # application layout (HAML example)
276
+ :javascript
277
+ var AJAX_LAZY_LOAD_ASSETS = #{Ajax.lazy_load_assets?};
278
+
279
+ if !Ajax.lazy_load_assets
280
+ include_all_assets
281
+ end
282
+
283
+ # application.js
284
+ window.ajax = new Ajax({
285
+ lazy_load_assets: window.AJAX_LAZY_LOAD_ASSETS !== undefined ? window.AJAX_LAZY_LOAD_ASSETS : false
286
+ });
287
+
255
288
  Use <code>ajax_header :assets { :stylesheets => [], :javascripts => [] }</code> to define assets that a page depends on. These assets will be loaded before the response content is inserted into the DOM.
256
289
 
257
- Assets that have already been loaded are not loaded again, so it is safe to always include the assets header.
290
+ 1. Assets that have already been loaded are not loaded again
291
+ 2. Assets that are loaded, remain loaded (watch out for CSS conflicts and JS memory leaks)
292
+ 3. If lazy-loading assets is disabled, assets in the <tt>Ajax-Info</tt> header are ignored, but callbacks are still executed.
258
293
 
259
- If you need to perform operations on the new content, or instantiate objects defined in the dependent assets, use <b>JavaScript Callbacks</b>.
294
+ Often you will need to perform some DOM manipulations on the newly inserted content, or instantiate JavaScript objects that are defined in a lazy-loaded JS file. To execute some JavaScript after all assets have been loaded and the new content has been inserted, use <b>JavaScript Callbacks</b>.
260
295
 
261
- == <a name="callbacks">JavaScript Callbacks</a>
296
+ == JavaScript Callbacks
262
297
 
263
- To add a JavaScript callback(s) that will be executed <b>after any assets in <tt>Ajax-Info['assets']</tt> have been loaded</b> you can pass it in the header with the following. These callbacks are executed in the global scope:
298
+ JavaScript callbacks can be added to the response and will be executed after any assets in <tt>Ajax-Info['assets']</tt> have been loaded. (If lazy loading assets is disabled, they are executed immediately.)
264
299
 
265
- ajax_header, :callbacks, 'window.player.init();'
266
-
267
- Alternatively you may want to bind callbacks directly to the <tt>window.ajax</tt> object in your view:
300
+ You can bind callbacks directly to the <tt>window.ajax</tt> object in your view, for example, in HAML we could have:
301
+
302
+ :javascript
303
+ window.ajax.onLoad(function() {
304
+ window.juggernaut = new window.Juggernaut(#{juggernaut_options.to_json});
305
+ window.liveFeed.init();
306
+ });
268
307
 
269
- # app/views/activity/recent.html.haml
270
- window.ajax.onLoad('window.juggernaut = new Juggernaut(#{juggernaut_options.to_json}); window.liveFeed.init();');
271
- window.ajax.prependOnLoad("$(document).trigger('player.init');");
308
+ window.ajax.prependOnLoad(function() {
309
+ $(document).trigger('player.init');
310
+ });
272
311
 
273
- When we add the juggernaut callback it must be a string because Juggernaut is defined in one of the assets that hasn't been loaded yet.
312
+ In the <tt>onLoad</tt> callback I'm scoping everything to <tt>window</tt> to <b>avoid scoping issues</b> in different browsers.
274
313
 
275
- <tt>window.ajax.prependOnLoad</tt> adds the callback to the front of the queue. Callbacks can also be functions, for example:
314
+ <b><tt>window.ajax.prependOnLoad</tt><b> adds the callback to the front of the queue.
315
+
316
+ Alternatively callbacks can be passed as a list of Strings in the <tt>Ajax-Info</tt> header using the <tt>ajax_header</tt> helper:
317
+
318
+ ajax_header, :callbacks, 'window.player.init();'
319
+
320
+ These callbacks are executed in the global scope. This method of adding callbacks is not recommended for two reasons:
321
+
322
+ 1. Safari has trouble with some String callbacks.
323
+ 2. If Ajax is disabled, these callbacks will not be executed, because the <tt>Ajax-Info</tt> header will not be set.
324
+
325
+ However, callbacks added directly to the <tt>window.ajax</tt> instance will still be executed, and they will execute immediately, so your code continues to work as expected.
326
+
327
+ == JavaScript Gotchas
328
+
329
+ Most of the problems you will likely encounter from a change to Ajax will be JavaScript related. These problems become more noticeable for the following reasons:
330
+
331
+ 1. JavaScript that has been loaded, remains loaded for a very long time. This can lead to:
332
+ 1. Memory leaks
333
+ 2. Callbacks executing ad infinitum, likely on content that has since been replaced.
334
+ 2. Inconsistent browser handling of JavaScript returned via AJAX:
335
+ 1. JavaScript in AJAX response is executed in local scope
336
+ 1. Safari {scoping issues}[http://forum.jquery.com/topic/dealing-with-globaleval-and-safari-suggestion-for-a-better-approach]
337
+ 3. {Inconsistent support for <tt>script.onload</tt>}[http://unixpapa.com/js/dyna.html]
338
+ 3. Badly written JavaScript libraries
339
+
340
+ To ease some of the pain, observe some of the following advice:
341
+
342
+ 1. Never use {<tt>document.write</tt>}[http://javascript.crockford.com/script.html]
343
+ 2. Use <tt>window</tt> to avoid scoping issues.
344
+ 3. Modify your third-party JavaScript libraries to also assign classes etc to <tt>window</tt>.
345
+ 4. Use jQuery {live events}[http://api.jquery.com/live/]
346
+ 5. Dynamically turn off repeating callbacks e.g.
347
+
348
+ function my_repetitive_callback() {
349
+ if ($(selector).size() == 0) {
350
+ // Turn off the interval
351
+ if (object.interval_id !== undefined) {
352
+ clearInterval(object.interval_id);
353
+ object.interval_id = undefined;
354
+ }
355
+ } else {
356
+ $(selector).do().some().jquery().kung().foo();
357
+ }
358
+ }
359
+
360
+ // Start the interval. Do this whenever a page is rendered
361
+ // that has content we want to work with. This will start
362
+ // the interval running. When we change the page, the
363
+ // content will disappear and the interval will turn itself off.
364
+ object.interval_id = setInterval(my_repetitive_callback, 5000);
365
+
366
+ == Testing
367
+
368
+ * We use RSpec
369
+ * See <tt>Ajax::Spec::Helpers</tt> and <tt>Ajax::Spec::Extension</tt> {in the rdocs}[http://rdoc.info/projects/kjvarga/ajax]
370
+ * Copy <tt>ajax/spec/integration/ajax_spec.rb</tt> into your project to ensure that the Ajax integration always works.
276
371
 
277
- window.ajax.onLoad(function() {
278
- alert('All your asset are ours!');
279
- });
280
-
281
372
  == Contributions
282
373
 
283
374
  Contributions are welcome. Please fork the project and send me a pull request with your changes and Spec tests.
284
375
 
285
376
  == Useful Resources
286
377
 
287
- * AjaxPatters[http://ajaxpatterns.org/Unique_URLs] useful discussion of techniques for maintaining state using JavaScript to poll for changes to <tt>window.location.hash</tt>.
288
- * link jQuery Address[http://www.asual.com/jquery/address/] JavaScript library for managing the URL.
289
- * SWFAddress[http://www.asual.com/swfaddress/] deep linking solution for Flash.
378
+ * {AJAX site crawling specification}[http://code.google.com/web/ajaxcrawling/docs/getting-started.html].
379
+ * AjaxPatters[http://ajaxpatterns.org/] useful discussion of AJAX-related problems and their solutions.
380
+ * {jQuery Address}[http://www.asual.com/jquery/address/] JavaScript library for managing the URL and deep-linking.
290
381
 
291
382
  Copyright (c) 2010 Karl Varga, released under the MIT license
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.1.4
@@ -5,36 +5,40 @@ module Ajax
5
5
  # Return a boolean indicating whether the given URL points to the
6
6
  # root path.
7
7
  def url_is_root?(url)
8
- !!(URI.parse(url).path =~ %r[^\/?$])
8
+ !!(encode_and_parse_url(url).path =~ %r[^\/?$])
9
9
  end
10
10
 
11
11
  # The URL is hashed if the fragment part starts with a /
12
12
  #
13
13
  # For example, http://lol.com#/Rihanna
14
14
  def is_hashed_url?(url)
15
- !!(URI.parse(url).fragment =~ %r[^\/])
15
+ !!(encode_and_parse_url(url).fragment=~ %r[^\/])
16
16
  end
17
-
17
+
18
18
  # Return a hashed URL using the fragment of <tt>url</tt>
19
19
  def hashed_url_from_fragment(url)
20
- url_host(url) + ('/#/' + (URI.parse(url).fragment || '')).gsub(/\/\//, '/')
20
+ url_host(url) + ('/#/' + (encode_and_parse_url(url).fragment || '')).gsub(/\/\//, '/')
21
21
  end
22
22
 
23
23
  # Return a traditional URL from the fragment of <tt>url</tt>
24
24
  def traditional_url_from_fragment(url)
25
- url_host(url) + ('/' + (URI.parse(url).fragment || '')).gsub(/\/\//, '/')
25
+ url_host(url) + ('/' + (encode_and_parse_url(url).fragment || '')).gsub(/\/\//, '/')
26
26
  end
27
27
 
28
28
  # Return a hashed URL formed from a traditional <tt>url</tt>
29
29
  def hashed_url_from_traditional(url)
30
- uri = URI.parse(url)
30
+ uri = encode_and_parse_url(url)
31
31
  hashed_url = url_host(url) + ('/#/' + (uri.path || '')).gsub(/\/\//, '/')
32
32
  hashed_url += ('?' + uri.query) unless uri.query.nil?
33
33
  hashed_url
34
34
  end
35
35
 
36
36
  protected
37
-
37
+
38
+ def encode_and_parse_url(url)
39
+ URI.parse(URI.encode(url).gsub("%23", "#"))
40
+ end
41
+
38
42
  def url_host(url)
39
43
  if url.match(/^(\w+\:\/\/[^\/]+)\/?/)
40
44
  $1
Binary file
@@ -176,50 +176,77 @@ var AjaxAssets = function(array, type) {
176
176
  *
177
177
  * Options:
178
178
  * <tt>enabled</tt> boolean indicating whether the plugin is enabled.
179
- * This must be set if you are using Ajax callbacks in your code,
180
- * and you want them to still fire if Ajax is not enabled.
179
+ * Callbacks that you set in the Ajax-Info header or directly on
180
+ * this instance will still be executed. They will not be queued,
181
+ * the will be executed immediately.
181
182
  *
182
183
  * <tt>default_container</tt> string jQuery selector of the default
183
184
  * container element to receive content.
184
185
  *
186
+ * <tt>lazy_load_assets</tt> boolean indicating whether to enable
187
+ * lazy loading assets. If this is disabled, callbacks will be
188
+ * executed immediately.
189
+ *
190
+ * <tt>show_loading_image</tt> (default true) boolean indicating whether
191
+ * to show the loading image.
192
+ *
193
+ * <tt>loading_image</tt> (optional) string jQuery selector of an
194
+ * existing image to show while pages are loading. If not set the default
195
+ * selector is: img#ajax-loading
196
+ *
197
+ * <tt>loading_image_path</tt> (optional) string full path to the loading
198
+ * image. Used to append an image tag to the body element
199
+ * if an existing image is not found. Default: /images/ajax-loading.gif
200
+ *
201
+ * To customize image handling, override the <tt>showLoadingImage</tt> and
202
+ * <tt>hideLoadingImage</tt> methods.
203
+ *
185
204
  * Callbacks:
186
205
  *
187
- * Callbacks can be specified using Ajax-Info{ callbacks: 'javascript to eval' },
188
- * or by adding callbacks directly to the Ajax instance:
206
+ * Callbacks can be specified using Ajax-Info{ callbacks: 'javascript to eval.' },
207
+ * or by adding callbacks directly to the Ajax instance.
208
+ *
209
+ * 'onLoad' callbacks are executed once new content has been inserted into the DOM,
210
+ * and after all assets have been loaded (if using lazy-loading). I.e. "on page load".
211
+ *
212
+ * For example:
189
213
  *
190
214
  * window.ajax.onLoad(function() { doSomething(args); });
191
215
  *
192
- * Order of execution:
216
+ * To add a callback to the front of the queue use:
193
217
  *
218
+ * window.ajax.prependOnLoad(function() { doSomething(args); });
194
219
  *
220
+ * KJV 2010-04-22: I've experienced problems with Safari using String callbacks. YMMV.
221
+ * Browser support for callbacks is patchy at this time so lazy-loading is
222
+ * not recommended.
195
223
  */
196
224
  var Ajax = function(options) {
197
225
  var self = this;
198
-
199
- self.enabled = true;
200
- self.default_container = undefined;
201
- self.loaded_by_framework = false;
202
- self.loading_icon = $('#loading-icon-small');
203
- self.javascripts = undefined;
204
- self.stylesheets = new AjaxAssets([], 'css');
205
- self.callbacks = [];
206
- self.loaded = false;
207
- self.lazy_load_assets = false;
208
-
209
- // For initial position of the loading icon. Often the mouse does not
210
- // move so position it by the link that was clicked.
211
- self.last_click_coords = undefined;
212
-
213
- // Parse options
214
- self.options = options;
215
- self.default_container = options.default_container;
216
- if (options.enabled !== undefined) {
217
- self.enabled = options.enabled;
218
- }
219
- if (options.lazy_load_assets !== undefined) {
220
- self.lazy_load_assets = options.lazy_load_assets;
221
- }
222
226
 
227
+ /**
228
+ * Options
229
+ */
230
+ self.options = {
231
+ enabled: true,
232
+ default_container: undefined,
233
+ loaded_by_framework: false,
234
+ show_loading_image: true,
235
+ loading_image: 'img#ajax-loading',
236
+ loading_image_path: '/images/ajax-loading.gif',
237
+ javascripts: undefined,
238
+ stylesheets: new AjaxAssets([], 'css'),
239
+ callbacks: [],
240
+ loaded: false,
241
+ lazy_load_assets: false,
242
+
243
+ // For initial position of the loading icon. Often the mouse does not
244
+ // move so position it by the link that was clicked.
245
+ last_click_coords: undefined
246
+ };
247
+ jQuery.extend(self.options, options);
248
+ jQuery.extend(self, self.options);
249
+
223
250
  // Initialize on DOM ready
224
251
  $(function() { self.init() });
225
252
 
@@ -234,10 +261,6 @@ var Ajax = function(options) {
234
261
  $.address.history(true);
235
262
  $.address.change = self.addressChanged;
236
263
 
237
- // Insert loading image
238
- var image = '<img src="/images/loading-icon-small.gif" id="loading-icon-small" alt="Loading..." />'
239
- $(image).hide().appendTo($('body'));
240
-
241
264
  // Bind a live event to all ajax-enabled links
242
265
  $('a[data-deep-link]').live('click', self.linkClicked);
243
266
 
@@ -328,9 +351,9 @@ var Ajax = function(options) {
328
351
  beforeSend: self.setRequestHeaders,
329
352
  success: self.responseHandler,
330
353
  complete: function(XMLHttpRequest, responseText) {
331
- // Stop watching the mouse position and scroll to the top of the page.
332
- $(document).unbind('mousemove', self.updateImagePosition).scrollTop(0);
333
- $('#loading-icon-small').hide();
354
+ // Scroll to the top of the page.
355
+ $(document).scrollTop(0);
356
+ self.hideLoadingImage();
334
357
  self.loaded = true;
335
358
  },
336
359
  error: function(XMLHttpRequest, textStatus, errorThrown) {
@@ -504,11 +527,30 @@ var Ajax = function(options) {
504
527
  return data;
505
528
  };
506
529
 
530
+ /**
531
+ * Hide the loading image.
532
+ *
533
+ * Stop watching the mouse position.
534
+ */
535
+ self.hideLoadingImage = function() {
536
+ if (!self.show_loading_image) { return; }
537
+ $(document).unbind('mousemove', self.updateImagePosition);
538
+ $(self.loading_image).hide();
539
+ };
540
+
507
541
  /**
508
542
  * Show the loading image.
509
543
  */
510
544
  self.showLoadingImage = function() {
511
- var icon = $('#loading-icon-small');
545
+ if (!self.show_loading_image) { return; }
546
+
547
+ var icon = $(self.loading_image);
548
+
549
+ // Create the image if it doesn't exist
550
+ if (icon.size() == 0) {
551
+ $('<img src="'+ self.loading_image_path +'" id="ajax-loading" alt="Loading..." />').hide().appendTo($('body'));
552
+ icon = $(self.loading_image);
553
+ }
512
554
 
513
555
  // Follow the mouse pointer
514
556
  $(document).bind('mousemove', self.updateImagePosition);
@@ -540,7 +582,7 @@ var Ajax = function(options) {
540
582
  * Update the position of the loading icon.
541
583
  */
542
584
  self.updateImagePosition = function(e) {
543
- $('#loading-icon-small').css({
585
+ $(self.loading_image).css({
544
586
  zIndex: 99,
545
587
  position: 'absolute',
546
588
  top: e.pageY + 14,
@@ -548,7 +590,6 @@ var Ajax = function(options) {
548
590
  });
549
591
  };
550
592
 
551
-
552
593
  /**
553
594
  * onLoad
554
595
  *
@@ -625,4 +666,26 @@ var Ajax = function(options) {
625
666
  self.teaser = function(callback) {
626
667
  return new String(callback).slice(0,50);
627
668
  };
669
+
670
+ /**
671
+ * Escape all special jQuery CSS selector characters in *selector*.
672
+ * Useful when you have a class or id which contains special characters
673
+ * which you need to include in a selector.
674
+ */
675
+ self.escapeSelector = (function() {
676
+ var specials = [
677
+ '#', '&', '~', '=', '>',
678
+ "'", ':', '"', '!', ';', ','
679
+ ];
680
+ var regexSpecials = [
681
+ '.', '*', '+', '|', '[', ']', '(', ')', '/', '^', '$'
682
+ ];
683
+ var sRE = new RegExp(
684
+ '(' + specials.join('|') + '|\\' + regexSpecials.join('|\\') + ')', 'g'
685
+ );
686
+
687
+ return function(selector) {
688
+ return selector.replace(sRE, '\\$1');
689
+ }
690
+ })();
628
691
  };
data/rails/install.rb CHANGED
@@ -8,7 +8,7 @@ AJAX_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
8
8
  public/javascripts/jquery.address-1.2rc.js
9
9
  public/javascripts/jquery.address-1.2rc.min.js
10
10
  public/javascripts/jquery.json-2.2.min.js
11
- public/images/loading-icon-small.gif
11
+ public/images/ajax-loading.gif
12
12
  ].each do |file|
13
13
  if File.exist?(File.join(Rails.root, file))
14
14
  puts "skipped: #{file} exists!"
@@ -16,6 +16,11 @@ context 'Ajax::UrlHelpers' do
16
16
  Ajax.hashed_url_from_traditional('/Beyonce').should == '/#/Beyonce'
17
17
  end
18
18
 
19
+ it "should handle special characters" do
20
+ Ajax.hashed_url_from_traditional('/beyoncé').should == '/#/beyonc%C3%A9'
21
+ Ajax.hashed_url_from_traditional('/red hot').should == '/#/red%20hot'
22
+ end
23
+
19
24
  DOMAINS.each do |domain|
20
25
  it "should work for domain #{domain}" do
21
26
  Ajax.hashed_url_from_traditional("http://#{domain}/playlists").should == "http://#{domain}/#/playlists"
@@ -28,6 +33,11 @@ context 'Ajax::UrlHelpers' do
28
33
  Ajax.hashed_url_from_fragment('/Beyonce#/Akon').should == '/#/Akon'
29
34
  Ajax.hashed_url_from_fragment('/Beyonce#Akon').should == '/#/Akon'
30
35
  end
36
+
37
+ it "should handle special characters" do
38
+ Ajax.hashed_url_from_fragment('/#/beyoncé').should == '/#/beyonc%C3%A9'
39
+ Ajax.hashed_url_from_fragment('/#/red hot').should == '/#/red%20hot'
40
+ end
31
41
 
32
42
  it "should handle no fragment" do
33
43
  Ajax.hashed_url_from_fragment('/Beyonce').should == '/#/'
@@ -46,6 +56,7 @@ context 'Ajax::UrlHelpers' do
46
56
  it "should detect root urls" do
47
57
  Ajax.url_is_root?('/#/Beyonce?query2').should be(true)
48
58
  Ajax.url_is_root?('/').should be(true)
59
+ Ajax.url_is_root?('/#/beyoncé'). should be(true)
49
60
  end
50
61
 
51
62
  it "should detect non-root urls" do
@@ -55,6 +66,10 @@ context 'Ajax::UrlHelpers' do
55
66
  it "should support full URLs" do
56
67
  Ajax.is_hashed_url?('http://musicsocial.com.local/#/playlists').should be(true)
57
68
  end
69
+
70
+ it "should support special characters" do
71
+ Ajax.is_hashed_url?('http://musicsocial.com.local/#/beyoncé').should be(true)
72
+ end
58
73
  end
59
74
 
60
75
  describe "(boolean) is_hashed_url?" do
@@ -88,7 +103,12 @@ context 'Ajax::UrlHelpers' do
88
103
  end
89
104
 
90
105
  it "should handle no fragment" do
91
- Ajax.traditional_url_from_fragment('/Beyonce').should == '/'
106
+ Ajax.traditional_url_from_fragment('/Beyonce#/beyoncé').should == '/beyonc%C3%A9'
107
+ Ajax.traditional_url_from_fragment('/#/red hot').should == '/red%20hot'
108
+ end
109
+
110
+ it "should handle special characters" do
111
+ Ajax.traditional_url_from_fragment('/Beyonce#/Akon').should == '/Akon'
92
112
  end
93
113
 
94
114
  DOMAINS.each do |domain|
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.3
4
+ version: 0.1.4
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-21 00:00:00 -07:00
12
+ date: 2010-04-22 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -53,8 +53,7 @@ files:
53
53
  - lib/rack-ajax.rb
54
54
  - lib/rack-ajax/decision_tree.rb
55
55
  - lib/rack-ajax/parser.rb
56
- - public/images/loading-icon-large.gif
57
- - public/images/loading-icon-small.gif
56
+ - public/images/ajax-loading.gif
58
57
  - public/javascripts/ajax.js
59
58
  - public/javascripts/jquery.address-1.1.js
60
59
  - public/javascripts/jquery.address-1.1.min.js
Binary file
Binary file