ajax 1.0.8 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +5 -3
  2. data/Gemfile.lock +34 -8
  3. data/README.rdoc +76 -63
  4. data/Rakefile +3 -1
  5. data/VERSION +1 -1
  6. data/app/controllers/ajax_controller.rb +6 -0
  7. data/init.rb +6 -0
  8. data/lib/ajax.rb +13 -24
  9. data/lib/ajax/action_controller.rb +120 -65
  10. data/lib/ajax/application.rb +62 -0
  11. data/lib/ajax/helpers/request_helper.rb +2 -2
  12. data/lib/ajax/helpers/task_helper.rb +2 -2
  13. data/lib/ajax/railtie.rb +25 -1
  14. data/lib/ajax/routes.rb +7 -3
  15. data/lib/ajax/rspec.rb +2 -0
  16. data/lib/ajax/{spec → rspec}/extension.rb +4 -11
  17. data/lib/ajax/{spec → rspec}/helpers.rb +10 -10
  18. data/lib/ajax/rspec/integration.rb +24 -0
  19. data/lib/rack-ajax.rb +1 -1
  20. data/lib/rack-ajax/parser.rb +2 -2
  21. data/public/javascripts/{jquery.address-1.2rc.js → jquery.address-1.3.js} +340 -197
  22. data/public/javascripts/jquery.address-1.3.min.js +32 -0
  23. data/rails/init.rb +1 -4
  24. data/rails/install.rb +1 -1
  25. data/rails/routes.rb +6 -0
  26. data/tasks/ajax_tasks.rake +11 -2
  27. metadata +41 -25
  28. data/app/views/ajax/_redirect_with_fragment.html.erb +0 -19
  29. data/public/javascripts/jquery.address-1.1.js +0 -450
  30. data/public/javascripts/jquery.address-1.1.min.js +0 -11
  31. data/public/javascripts/jquery.address-1.2.js +0 -528
  32. data/public/javascripts/jquery.address-1.2.min.js +0 -25
  33. data/public/javascripts/jquery.address-1.2rc.min.js +0 -27
  34. data/rails/uninstall.rb +0 -1
  35. data/spec/ajax/ajax_spec.rb +0 -23
  36. data/spec/ajax/helpers_spec.rb +0 -121
  37. data/spec/ajax/request_helper_spec.rb +0 -52
  38. data/spec/ajax/tasks_spec.rb +0 -48
  39. data/spec/integration/ajax_spec.rb +0 -146
  40. data/spec/rack-ajax/parser_spec.rb +0 -62
  41. data/spec/spec.opts +0 -1
  42. data/spec/spec_helper.rb +0 -17
  43. data/spec/support/file_macros.rb +0 -28
  44. data/spec/support/query_count.rb +0 -0
  45. data/spec/support/response_helpers.rb +0 -7
@@ -0,0 +1,62 @@
1
+ require 'pathname'
2
+
3
+ module Ajax
4
+ # A class with framework/application related methods like discovering
5
+ # which version of Rails we are running under.
6
+ class Application
7
+ # Return a boolean indicating whether the Rails constant is defined.
8
+ # Pass a <tt>version</tt> integer to determine whether the major version
9
+ # of Rails matches <tt>version</tt>.
10
+ #
11
+ # Example: rails?(3) returns true when Rails.version.to_i == 3.
12
+ #
13
+ # Returns false if Rails is not defined or the major version does not match.
14
+ def rails?(version=nil)
15
+ !!(if version.nil?
16
+ defined?(::Rails)
17
+ elsif defined?(::Rails)
18
+ if ::Rails.respond_to?(:version)
19
+ ::Rails.version.to_i == version
20
+ else
21
+ version <= 2 # Rails.version defined in 2.1.0
22
+ end
23
+ else
24
+ false
25
+ end)
26
+ end
27
+
28
+ def root
29
+ Pathname.new(rails? && Rails.root || Dir.getwd)
30
+ end
31
+
32
+ # Include framework hooks for Rails
33
+ #
34
+ # This method is called by <tt>init.rb</tt>, which is run by Rails on startup.
35
+ #
36
+ # Customize rendering. Include custom headers and don't render the layout for AJAX.
37
+ # Insert the Rack::Ajax middleware to rewrite and handle requests.
38
+ # Add custom attributes to outgoing links.
39
+ #
40
+ # Hooks for Rails 3 are installed using Railties.
41
+ def init
42
+ return unless rails?
43
+ if rails?(3)
44
+ require 'ajax/railtie'
45
+ else
46
+ require 'ajax/action_controller'
47
+ require 'ajax/action_view'
48
+
49
+ Ajax.logger = ::Rails.logger
50
+
51
+ # Customize rendering. Include custom headers and don't render the layout for AJAX.
52
+ ::ActionController::Base.send(:include, Ajax::ActionController)
53
+
54
+ # Insert the Rack::Ajax middleware to rewrite and handle requests
55
+ ::ActionController::Dispatcher.middleware.insert_before(Rack::Head, Rack::Ajax)
56
+
57
+ # Add custom attributes to outgoing links
58
+ ::ActionView::Base.send(:include, Ajax::ActionView)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -24,7 +24,7 @@ module Ajax
24
24
  # All Hash and Array values are deep-merged.
25
25
  # Hash keys are converted to Strings.
26
26
  def set_header(object, key, value)
27
- headers = object.is_a?(::ActionController::Response) ? object.headers : object
27
+ headers = object.is_a?(Hash) ? object : object.headers # ::ActionController::Response
28
28
  key = key.to_s
29
29
 
30
30
  info = case headers["Ajax-Info"]
@@ -61,7 +61,7 @@ module Ajax
61
61
  # <tt>object</tt> can be a Hash or instance of <tt>ActionController::Request</tt>
62
62
  # <tt>key</tt> Symbol or String hash key, converted to String
63
63
  def get_header(object, key)
64
- headers = object.is_a?(::ActionController::Request) ? object.headers : object
64
+ headers = object.is_a?(Hash) ? object : object.headers # ::ActionController::Request
65
65
  key = key.to_s
66
66
 
67
67
  info = case headers["Ajax-Info"]
@@ -6,8 +6,8 @@ module Ajax
6
6
  app/views/ajax/framework.html.erb
7
7
  config/initializers/ajax.rb
8
8
  public/javascripts/ajax.js
9
- public/javascripts/jquery.address-1.2rc.js
10
- public/javascripts/jquery.address-1.2rc.min.js
9
+ public/javascripts/jquery.address-1.3.js
10
+ public/javascripts/jquery.address-1.3.min.js
11
11
  public/javascripts/jquery.json-2.2.min.js
12
12
  public/images/ajax-loading.gif]
13
13
 
@@ -1,7 +1,31 @@
1
+ require 'ajax/action_controller'
2
+ require 'ajax/action_view'
3
+
1
4
  module Ajax
2
5
  class Railtie < Rails::Railtie
3
6
  rake_tasks do
4
- load(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'tasks', 'ajax_tasks.rake')))
7
+ load(File.expand_path('../../../tasks/ajax_tasks.rake', __FILE__))
8
+ end
9
+
10
+ initializer 'ajax.action_integration' do
11
+ ActiveSupport.on_load :action_view do
12
+ include Ajax::ActionView
13
+ end
14
+ ActiveSupport.on_load :action_controller do
15
+ include Ajax::ActionController
16
+ end
17
+ end
18
+
19
+ initializer "ajax.middleware" do |app|
20
+ app.config.middleware.insert_before "ActionDispatch::Head", "Rack::Ajax"
21
+ end
22
+
23
+ initializer 'ajax.routes' do |app|
24
+ app.routes_reloader.paths << Ajax.root + 'rails/routes.rb'
25
+ end
26
+
27
+ initializer 'ajax.logger' do |app|
28
+ Ajax.logger = ::Rails.logger
5
29
  end
6
30
  end
7
31
  end
@@ -1,5 +1,3 @@
1
- require 'ajax'
2
-
3
1
  module Ajax
4
2
  module Routes
5
3
  # In your <tt>config/routes.rb</tt> file call:
@@ -8,8 +6,14 @@ module Ajax
8
6
  #
9
7
  # Adds an <tt>ajax_framework_path</tt> pointing to <tt>Ajax.framework_path</tt>
10
8
  # which is <tt>/ajax/framework</tt> by default.
9
+ #
10
+ # Only applies when installed as a gem in Rails 2 or less.
11
11
  def self.draw(map)
12
- map.ajax_framework Ajax.framework_path, :controller => 'ajax', :action => 'framework'
12
+ if Ajax.app.rails?(3) && map.respond_to?(:match)
13
+ map.match Ajax.framework_path, :to => 'ajax#framework', :as => 'ajax_framework'
14
+ else
15
+ map.ajax_framework Ajax.framework_path, :controller => 'ajax', :action => 'framework'
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -0,0 +1,2 @@
1
+ require 'ajax/rspec/extension'
2
+ require 'ajax/rspec/helpers'
@@ -1,6 +1,9 @@
1
1
  module Ajax
2
- module Spec
2
+ module RSpec
3
3
  module Extension
4
+ def included(base)
5
+ require 'ajax/rspec/integration'
6
+ end
4
7
 
5
8
  def integrate_ajax
6
9
  Ajax.enabled = true
@@ -21,14 +24,4 @@ module Ajax
21
24
  end
22
25
  end
23
26
  end
24
- end
25
-
26
- module ActiveSupport
27
- class TestCase
28
- include Ajax::Spec::Extension
29
-
30
- before(:all) do
31
- ::Ajax.enabled = false
32
- end if method_defined?(:before)
33
- end
34
27
  end
@@ -1,7 +1,7 @@
1
1
  require 'uri'
2
2
 
3
3
  module Ajax
4
- module Spec
4
+ module RSpec
5
5
  module Helpers
6
6
 
7
7
  def create_app
@@ -55,14 +55,14 @@ module Ajax
55
55
  # Response must be [code, {headers}, ['Response']]
56
56
  # Headers must contain the Content-Type header
57
57
  def should_be_a_valid_response
58
- return if @response.is_a?(::ActionController::Response)
59
- @response.should be_a_kind_of(Array)
58
+ return if !@response.is_a?(Array) # ::ActionController::Response
59
+ @response.should be_a(Array)
60
60
  @response.size.should == 3
61
- @response[0].should be_a_kind_of(Integer)
62
- @response[1].should be_a_kind_of(Hash)
61
+ @response[0].should be_a(Integer)
62
+ @response[1].should be_a(Hash)
63
63
  @response[1]['Content-Type'].should =~ %r[^text\/\w+]
64
- @response[2].should be_a_kind_of(Array)
65
- @response[2][0].should be_a_kind_of(String)
64
+ @response[2].should be_a(Array)
65
+ @response[2][0].should be_a(String)
66
66
  end
67
67
 
68
68
  def env(uri, request_method, options={})
@@ -76,15 +76,15 @@ module Ajax
76
76
  end
77
77
 
78
78
  def response_body
79
- @response.is_a?(::ActionController::Response) ? @response.body : @response[2][0]
79
+ @response.is_a?(Array) ? @response[2][0] : @response.body
80
80
  end
81
81
 
82
82
  def response_code
83
- @response.is_a?(::ActionController::Response) ? @response.status.to_i : @response[0]
83
+ @response.is_a?(Array) ? @response[0] : @response.status.to_i
84
84
  end
85
85
 
86
86
  def response_headers
87
- @response.is_a?(::ActionController::Response) ? @response.headers.to_hash : @response[1]
87
+ @response.is_a?(Array) ? @response[1] : @response.headers.to_hash
88
88
  end
89
89
 
90
90
  def response_body_as_hash
@@ -0,0 +1,24 @@
1
+ # Only require integration module when extensions are included.
2
+ # RSpec integration. Add a before filter to disable Ajax before all tests.
3
+ module Ajax::RSpec::Integration
4
+ if defined?(RSpec)
5
+ RSpec.configure do |c|
6
+ c.before do
7
+ ::Ajax.enabled = false
8
+ end
9
+ end
10
+ elsif defined?(Spec)
11
+ Spec::Runner.configure do |c|
12
+ c.before :all do
13
+ ::Ajax.enabled = false
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ # ActiveSupport::TestCase integration
20
+ module ActiveSupport
21
+ class TestCase
22
+ include Ajax::RSpec::Extension
23
+ end
24
+ end
@@ -49,7 +49,7 @@ module Rack
49
49
  # To test rewrites, return a 200 response with
50
50
  # the modified request environment encoded as Yaml.
51
51
  #
52
- # The Ajax::Spec::Helpers module includes a helper
52
+ # The Ajax::RSpec::Helpers module includes a helper
53
53
  # method to test the result of a rewrite.
54
54
  if ::Ajax.is_mocked?
55
55
  rack_response.nil? ? Rack::Ajax::Parser.rack_response(encode_env(env)) : rack_response
@@ -10,7 +10,7 @@ module Rack
10
10
  # easier to introspect the headers.
11
11
  def initialize(env)
12
12
  @env = env
13
- @request = ActionController::Request.new(env)
13
+ @request = Rack::Request.new(env)
14
14
  end
15
15
 
16
16
  protected
@@ -20,7 +20,7 @@ module Rack
20
20
  end
21
21
 
22
22
  def ajax_request?
23
- @request.xml_http_request?
23
+ @request.xhr?
24
24
  end
25
25
 
26
26
  def get_request?
@@ -1,12 +1,12 @@
1
1
  /*
2
- * jQuery Address Plugin v1.2rc
2
+ * jQuery Address Plugin v1.3
3
3
  * http://www.asual.com/jquery/address/
4
4
  *
5
5
  * Copyright (c) 2009-2010 Rostislav Hristov
6
6
  * Dual licensed under the MIT or GPL Version 2 licenses.
7
7
  * http://jquery.org/license
8
8
  *
9
- * Date: 2010-03-31 11:35:36 +0300 (Wed, 31 Mar 2010)
9
+ * Date: 2010-09-26 17:58:16 +0300 (Sun, 26 Sep 2010)
10
10
  */
11
11
  (function ($) {
12
12
 
@@ -34,14 +34,22 @@
34
34
  );
35
35
  },
36
36
  _bind = function(value, data, fn) {
37
- if (fn || data) {
38
- $($.address).bind(value, fn || data, fn && data);
39
- }
37
+ $($.address).bind(value, data, fn);
40
38
  return $.address;
41
39
  },
42
- _hash = function() {
40
+ _supportsState = function() {
41
+ return (_h.pushState && typeof _opts.state !== UNDEFINED);
42
+ },
43
+ _hrefState = function() {
44
+ return '/' + _l.pathname.replace(new RegExp(_opts.state), '') +
45
+ _l.search + (_hrefHash() ? '#' + _hrefHash() : '');
46
+ },
47
+ _hrefHash = function() {
43
48
  var index = _l.href.indexOf('#');
44
- return index != -1 ? _ec(_dc(_crawl(_l.href.substr(index + 1), FALSE))) : '';
49
+ return index != -1 ? _crawl(_l.href.substr(index + 1), FALSE) : '';
50
+ },
51
+ _href = function() {
52
+ return _supportsState() ? _hrefState() : _hrefHash();
45
53
  },
46
54
  _window = function() {
47
55
  try {
@@ -59,10 +67,6 @@
59
67
  }
60
68
  return value;
61
69
  },
62
- _local = function(value, direction) {
63
- return (_msie && _l.protocol == 'file:') ?
64
- (direction ? _value.replace(/\?/, '%3F') : _value.replace(/%253F/, '?')) : value;
65
- },
66
70
  _crawl = function(value, direction) {
67
71
  if (_opts.crawlable && direction) {
68
72
  return (value != '' ? '!' : '') + value;
@@ -87,9 +91,9 @@
87
91
  },
88
92
  _listen = function() {
89
93
  if (!_silent) {
90
- var hash = _hash(),
94
+ var hash = _href(),
91
95
  diff = _value != hash;
92
- if (_safari && _version < 523) {
96
+ if (_webkit && _version < 523) {
93
97
  if (_length != _h.length) {
94
98
  _length = _h.length;
95
99
  if (typeof _stack[_length - 1] != UNDEFINED) {
@@ -97,11 +101,16 @@
97
101
  }
98
102
  _update(FALSE);
99
103
  }
100
- } else if (_msie && _version < 7 && diff) {
101
- _l.reload();
102
104
  } else if (diff) {
103
- _value = hash;
104
- _update(FALSE);
105
+ if (_msie && _version < 7) {
106
+ _l.reload();
107
+ } else {
108
+ if (_msie && _version < 8 && _opts.history) {
109
+ _st(_html, 50);
110
+ }
111
+ _value = hash;
112
+ _update(FALSE);
113
+ }
105
114
  }
106
115
  }
107
116
  },
@@ -111,38 +120,67 @@
111
120
  _st(_track, 10);
112
121
  },
113
122
  _track = function() {
114
- var value = (_l.pathname + (/\/$/.test(_l.pathname) ? '' : '/') + $.address.value()).replace(/\/\//, '/').replace(/^\/$/, ''),
115
- fn = window[_opts.tracker];
116
- if (typeof fn == FUNCTION) {
117
- fn(value);
118
- } else if (typeof _gaq != UNDEFINED && typeof _gaq.push == FUNCTION) {
119
- _gaq.push(['_trackPageview', value]);
120
- } else if (typeof pageTracker != UNDEFINED && typeof pageTracker._trackPageview == FUNCTION) {
121
- pageTracker._trackPageview(value);
122
- } else if (typeof urchinTracker == FUNCTION) {
123
- urchinTracker(value);
123
+ if (_opts.tracker !== 'null' && _opts.tracker !== null) {
124
+ var fn = $.isFunction(_opts.tracker) ? _opts.tracker : _t[_opts.tracker],
125
+ value = (_l.pathname + _l.search +
126
+ ($.address && !_supportsState() ? $.address.value() : ''))
127
+ .replace(/\/\//, '/').replace(/^\/$/, '');
128
+ if ($.isFunction(fn)) {
129
+ fn(value);
130
+ } else if ($.isFunction(_t.urchinTracker)) {
131
+ _t.urchinTracker(value);
132
+ } else if (typeof _t.pageTracker != UNDEFINED && $.isFunction(_t.pageTracker._trackPageview)) {
133
+ _t.pageTracker._trackPageview(value);
134
+ } else if (typeof _t._gaq != UNDEFINED && $.isFunction(_t._gaq.push)) {
135
+ _t._gaq.push(['_trackPageview', value]);
136
+ }
124
137
  }
125
138
  },
126
139
  _html = function() {
127
- var doc = _frame.contentWindow.document;
128
- doc.open();
129
- doc.write('<html><head><title>' + _d.title + '</title><script>var ' + ID + ' = "' + _hash() + '";</' + 'script></head></html>');
130
- doc.close();
140
+ var src = _js() + ':' + FALSE + ';document.open();document.writeln(\'<html><head><title>' +
141
+ _d.title + '</title><script>var ' + ID + ' = "' + _href() +
142
+ (_d.domain != _l.host ? '";document.domain="' + _d.domain : '') +
143
+ '";</' + 'script></head></html>\');document.close();';
144
+ if (_version < 7) {
145
+ _frame.src = src;
146
+ } else {
147
+ _frame.contentWindow.location.replace(src);
148
+ }
149
+ },
150
+ _options = function() {
151
+ if (_url && _qi != -1) {
152
+ var param, params = _url.substr(_qi + 1).split('&');
153
+ for (i = 0; i < params.length; i++) {
154
+ param = params[i].split('=');
155
+ if (/^(autoUpdate|crawlable|history|strict|wrap)$/.test(param[0])) {
156
+ _opts[param[0]] = (isNaN(param[1]) ? /^(true|yes)$/i.test(param[1]) : (parseInt(param[1], 10) !== 0));
157
+ }
158
+ if (/^(state|tracker)$/.test(param[0])) {
159
+ _opts[param[0]] = param[1];
160
+ }
161
+ }
162
+ _url = null;
163
+ }
164
+ _value = _href();
131
165
  },
132
166
  _load = function() {
133
167
  if (!_loaded) {
134
168
  _loaded = TRUE;
169
+ _options();
170
+ var body = $('body').ajaxComplete(function() {
171
+ _enable.call(this);
172
+ _unescape.call(this);
173
+ }).trigger('ajaxComplete');
135
174
  if (_opts.wrap) {
136
- var body = $('body');
137
- wrap = $('body > *')
138
- .wrapAll('<div style="padding:' +
139
- (_cssint(body, 'marginTop') + _cssint(body, 'paddingTop')) + 'px ' +
140
- (_cssint(body, 'marginRight') + _cssint(body, 'paddingRight')) + 'px ' +
141
- (_cssint(body, 'marginBottom') + _cssint(body, 'paddingBottom')) + 'px ' +
142
- (_cssint(body, 'marginLeft') + _cssint(body, 'paddingLeft')) + 'px;" />')
143
- .parent()
144
- .wrap('<div id="' + ID + '" style="height:100%; overflow:auto;' +
145
- (_safari ? (window.statusbar.visible && !/chrome/i.test(_agent) ? '' : ' resize:both;') : '') + '" />');
175
+ var wrap = $('body > *')
176
+ .wrapAll('<div style="padding:' +
177
+ (_cssint(body, 'marginTop') + _cssint(body, 'paddingTop')) + 'px ' +
178
+ (_cssint(body, 'marginRight') + _cssint(body, 'paddingRight')) + 'px ' +
179
+ (_cssint(body, 'marginBottom') + _cssint(body, 'paddingBottom')) + 'px ' +
180
+ (_cssint(body, 'marginLeft') + _cssint(body, 'paddingLeft')) + 'px;" />')
181
+ .parent()
182
+ .wrap('<div id="' + ID + '" style="height:100%; overflow:auto;' +
183
+ (_webkit ? (window.statusbar.visible && !/chrome/i.test(_agent) ? '' : ' resize:both;') : '') + '" />');
146
184
  $('html, body')
147
185
  .css({
148
186
  height: '100%',
@@ -150,7 +188,7 @@
150
188
  padding: 0,
151
189
  overflow: 'hidden'
152
190
  });
153
- if (_safari) {
191
+ if (_webkit) {
154
192
  $('<style type="text/css" />')
155
193
  .appendTo('head')
156
194
  .text('#' + ID + '::-webkit-resizer { background-color: #fff; }');
@@ -162,12 +200,12 @@
162
200
  if (frameset) {
163
201
  frameset.insertAdjacentElement('beforeEnd', _frame);
164
202
  frameset[frameset.cols ? 'cols' : 'rows'] += ',0';
165
- _frame.src = _js() + ':' + FALSE;
166
203
  _frame.noResize = TRUE;
167
204
  _frame.frameBorder = _frame.frameSpacing = 0;
168
205
  } else {
169
- _frame.src = _js() + ':' + FALSE;
170
206
  _frame.style.display = 'none';
207
+ _frame.style.width = _frame.style.height = 0;
208
+ _frame.tabIndex = -1;
171
209
  _d.body.insertAdjacentElement('afterBegin', _frame);
172
210
  }
173
211
  _st(function() {
@@ -175,16 +213,16 @@
175
213
  var win = _frame.contentWindow;
176
214
  var src = win.location.href;
177
215
  _value = (typeof win[ID] != UNDEFINED ? win[ID] : '');
178
- if (_value != _hash()) {
216
+ if (_value != _href()) {
179
217
  _update(FALSE);
180
- _l.hash = _local(_crawl(_value, TRUE), TRUE);
218
+ _l.hash = _crawl(_value, TRUE);
181
219
  }
182
220
  });
183
221
  if (typeof _frame.contentWindow[ID] == UNDEFINED) {
184
222
  _html();
185
223
  }
186
224
  }, 50);
187
- } else if (_safari) {
225
+ } else if (_webkit) {
188
226
  if (_version < 418) {
189
227
  $(_d.body).append('<form id="' + ID + '" style="position:absolute;top:-9999px;" method="get"></form>');
190
228
  _form = _d.getElementById(ID);
@@ -201,17 +239,40 @@
201
239
  _trigger('init');
202
240
  _update(FALSE);
203
241
  }, 1);
204
-
205
- if ((_msie && _version > 7) || (!_msie && ('on' + HASH_CHANGE) in _t)) {
206
- if (_t.addEventListener) {
207
- _t.addEventListener(HASH_CHANGE, _listen, false);
208
- } else if (_t.attachEvent) {
209
- _t.attachEvent('on' + HASH_CHANGE, _listen);
242
+
243
+ if (!_supportsState()) {
244
+ if ((_msie && _version > 7) || (!_msie && ('on' + HASH_CHANGE) in _t)) {
245
+ if (_t.addEventListener) {
246
+ _t.addEventListener(HASH_CHANGE, _listen, false);
247
+ } else if (_t.attachEvent) {
248
+ _t.attachEvent('on' + HASH_CHANGE, _listen);
249
+ }
250
+ } else {
251
+ _si(_listen, 50);
210
252
  }
211
- } else {
212
- _si(_listen, 50);
213
253
  }
214
- $('a[rel*=address:]').address();
254
+ }
255
+ },
256
+ _enable = function() {
257
+ var el,
258
+ elements = $('a'),
259
+ length = elements.size(),
260
+ delay = 1,
261
+ index = -1;
262
+ _st(function() {
263
+ if (++index != length) {
264
+ el = $(elements.get(index));
265
+ if (el.is('[rel*=address:]')) {
266
+ el.address();
267
+ }
268
+ _st(arguments.callee, delay);
269
+ }
270
+ }, delay);
271
+ },
272
+ _popstate = function() {
273
+ if (_value != _href()) {
274
+ _value = _href();
275
+ _update(FALSE);
215
276
  }
216
277
  },
217
278
  _unload = function() {
@@ -221,8 +282,74 @@
221
282
  _t.detachEvent('on' + HASH_CHANGE, _listen);
222
283
  }
223
284
  },
285
+ _unescape = function() {
286
+ var base = _l.pathname.replace(/\/$/, ''),
287
+ fragment = '_escaped_fragment_';
288
+ if ($('body').html().indexOf(fragment) != -1) {
289
+ $('a[href]:not([href^=http]), , a[href*=' + document.domain + ']', this).each(function() {
290
+ var href = $(this).attr('href').replace(/^http:/, '').replace(new RegExp(base + '/?$'), '');
291
+ if (href == '' || href.indexOf(fragment) != -1) {
292
+ $(this).attr('href', '#' + this.decode(href.replace(new RegExp('/(.*)\\?' + fragment + '=(.*)$'), '!$2')));
293
+ }
294
+ });
295
+ }
296
+ },
297
+ _encode = function(value) {
298
+ return encodeURIComponent(value).replace(/%20/g, '+');
299
+ },
300
+ _path = function(value) {
301
+ return value.split('#')[0].split('?')[0];
302
+ },
303
+ _pathNames = function(value) {
304
+ var path = _path(value),
305
+ names = path.replace(/\/{2,9}/g, '/').split('/');
306
+ if (path.substr(0, 1) == '/' || path.length === 0) {
307
+ names.splice(0, 1);
308
+ }
309
+ if (path.substr(path.length - 1, 1) == '/') {
310
+ names.splice(names.length - 1, 1);
311
+ }
312
+ return names;
313
+ },
314
+ _queryString = function(value) {
315
+ var arr = value.split('?');
316
+ return arr.slice(1, arr.length).join('?').split('#')[0];
317
+ },
318
+ _parameter = function(name, value) {
319
+ value = _queryString(value);
320
+ if (value) {
321
+ params = value.split('&');
322
+ var r = [];
323
+ for (i = 0; i < params.length; i++) {
324
+ var p = params[i].split('=');
325
+ if (p[0] == name) {
326
+ r.push(p.slice(1).join('='));
327
+ }
328
+ }
329
+ if (r.length !== 0) {
330
+ return r.length != 1 ? r : r[0];
331
+ }
332
+ }
333
+ },
334
+ _parameterNames = function(value) {
335
+ var qs = _queryString(value),
336
+ names = [];
337
+ if (qs && qs.indexOf('=') != -1) {
338
+ var params = qs.split('&');
339
+ for (var i = 0; i < params.length; i++) {
340
+ var name = params[i].split('=')[0];
341
+ if ($.inArray(name, names) == -1) {
342
+ names.push(name);
343
+ }
344
+ }
345
+ }
346
+ return names;
347
+ },
348
+ _hash = function(value) {
349
+ var arr = value.split('#');
350
+ return arr.slice(1, arr.length).join('#');
351
+ },
224
352
  ID = 'jQueryAddress',
225
- FUNCTION = 'function',
226
353
  UNDEFINED = 'undefined',
227
354
  HASH_CHANGE = 'hashchange',
228
355
  INIT = 'init',
@@ -243,7 +370,7 @@
243
370
  _mozilla = _browser.mozilla,
244
371
  _msie = _browser.msie,
245
372
  _opera = _browser.opera,
246
- _safari = _browser.safari,
373
+ _webkit = _browser.webkit,
247
374
  _supported = FALSE,
248
375
  _t = _window(),
249
376
  _d = _t.document,
@@ -251,8 +378,6 @@
251
378
  _l = _t.location,
252
379
  _si = setInterval,
253
380
  _st = setTimeout,
254
- _dc = decodeURI,
255
- _ec = encodeURI,
256
381
  _agent = navigator.userAgent,
257
382
  _frame,
258
383
  _form,
@@ -267,7 +392,7 @@
267
392
  _updating = FALSE,
268
393
  _stack = [],
269
394
  _listeners = {},
270
- _value = _hash();
395
+ _value = _href();
271
396
 
272
397
  if (_msie) {
273
398
  _version = parseFloat(_agent.substr(_agent.indexOf('MSIE') + 4));
@@ -275,7 +400,7 @@
275
400
  _version = _d.documentMode != 8 ? 7 : 8;
276
401
  }
277
402
  $(document).bind('propertychange', function() {
278
- if (_d.title != _title && _d.title.indexOf('#' + _hash()) != -1) {
403
+ if (_d.title != _title && _d.title.indexOf('#' + _href()) != -1) {
279
404
  _d.title = _title;
280
405
  }
281
406
  });
@@ -285,69 +410,64 @@
285
410
  (_mozilla && _version >= 1) ||
286
411
  (_msie && _version >= 6) ||
287
412
  (_opera && _version >= 9.5) ||
288
- (_safari && _version >= 312);
413
+ (_webkit && _version >= 312);
289
414
 
290
415
  if (_supported) {
291
-
292
416
  for (var i = 1; i < _length; i++) {
293
417
  _stack.push('');
294
418
  }
295
-
296
419
  _stack.push(_value);
297
-
298
- if (_msie && _l.hash != _value) {
299
- _l.hash = '#' + _local(_crawl(_value, TRUE), TRUE);
300
- }
301
-
302
420
  if (_opera) {
303
- history.navigationMode = 'compatible';
421
+ history.navigationMode = 'compatible';
304
422
  }
305
-
306
- if (_url && _qi != -1) {
307
- var param, params = _url.substr(_qi + 1).split('&');
308
- for (i = 0; i < params.length; i++) {
309
- param = params[i].split('=');
310
- if (/^(autoUpdate|crawlable|history|strict|wrap)$/.test(param[0])) {
311
- _opts[param[0]] = (isNaN(param[1]) ? /^(true|yes)$/i.test(param[1]) : (parseInt(param[1], 10) !== 0));
423
+ if (document.readyState == 'complete') {
424
+ var interval = setInterval(function() {
425
+ if ($.address) {
426
+ _load();
427
+ clearInterval(interval);
312
428
  }
313
- if (/^tracker$/.test(param[0])) {
314
- _opts[param[0]] = param[1];
429
+ }, 50);
430
+ } else {
431
+ _options();
432
+ $(_load);
433
+ }
434
+ var hrefState = _hrefState();
435
+ if (typeof _opts.state !== UNDEFINED) {
436
+ if (_h.pushState) {
437
+ if (hrefState.substr(0, 3) == '/#/') {
438
+ _l.replace(_opts.state.replace(/^\/$/, '') + hrefState.substr(2));
315
439
  }
440
+ } else if (hrefState != '/' && hrefState.replace(/^\/#/, '') != _hrefHash()) {
441
+ _l.replace(_opts.state.replace(/^\/$/, '') + '/#' + hrefState);
316
442
  }
317
443
  }
318
-
319
- if (document.readyState == 'complete') {
320
- _load();
321
- }
322
- $(_load);
323
- $(window).bind('unload', _unload);
324
-
325
- } else if ((!_supported && _hash() != '') ||
326
- (_safari && _version < 418 && _hash() != '' && _l.search != '')) {
327
- _d.open();
328
- _d.write('<html><head><meta http-equiv="refresh" content="0;url=' +
329
- encodeURI(_l.href.substr(0, _l.href.indexOf('#'))) + '" /></head></html>');
330
- _d.close();
444
+ $(window).bind('popstate', _popstate).bind('unload', _unload);
445
+ } else if ((!_supported && _hrefHash() != '') ||
446
+ (_webkit && _version < 418 && _hrefHash() != '' && _l.search != '')) {
447
+ _l.replace(_l.href.substr(0, _l.href.indexOf('#')));
331
448
  } else {
332
449
  _track();
333
450
  }
334
451
 
335
452
  return {
336
- init: function(data, fn) {
337
- return _bind(INIT, data, fn);
453
+ bind: function(type, data, fn) {
454
+ return _bind(type, data, fn);
338
455
  },
339
- change: function(data, fn) {
340
- return _bind(CHANGE, data, fn);
456
+ init: function(fn) {
457
+ return _bind(INIT, fn);
341
458
  },
342
- internalChange: function(data, fn) {
343
- return _bind(INTERNAL_CHANGE, data, fn);
459
+ change: function(fn) {
460
+ return _bind(CHANGE, fn);
344
461
  },
345
- externalChange: function(data, fn) {
346
- return _bind(EXTERNAL_CHANGE, data, fn);
462
+ internalChange: function(fn) {
463
+ return _bind(INTERNAL_CHANGE, fn);
464
+ },
465
+ externalChange: function(fn) {
466
+ return _bind(EXTERNAL_CHANGE, fn);
347
467
  },
348
468
  baseURL: function() {
349
469
  var url = _l.href;
350
- if (_hash() != '') {
470
+ if (url.indexOf('#') != -1) {
351
471
  url = url.substr(0, url.indexOf('#'));
352
472
  }
353
473
  if (/\/$/.test(url)) {
@@ -376,6 +496,13 @@
376
496
  }
377
497
  return _opts.history;
378
498
  },
499
+ state: function(value) {
500
+ if (value !== undefined) {
501
+ _opts.state = value;
502
+ return this;
503
+ }
504
+ return _opts.state;
505
+ },
379
506
  strict: function(value) {
380
507
  if (value !== undefined) {
381
508
  _opts.strict = value;
@@ -403,9 +530,51 @@
403
530
  _updating = FALSE;
404
531
  return this;
405
532
  },
533
+ encode: function(value) {
534
+ var pathNames = _pathNames(value),
535
+ parameterNames = _parameterNames(value),
536
+ queryString = _queryString(value),
537
+ hash = _hash(value),
538
+ first = value.substr(0, 1),
539
+ last = value.substr(value.length - 1),
540
+ encoded = '';
541
+ $.each(pathNames, function(i, v) {
542
+ encoded += '/' + _encode(v);
543
+ });
544
+ if (queryString !== '') {
545
+ encoded += '?';
546
+ if (parameterNames.length === 0) {
547
+ encoded += queryString;
548
+ } else {
549
+ $.each(parameterNames, function(i, v) {
550
+ var pv = _parameter(v, value);
551
+ if (typeof pv !== 'string') {
552
+ $.each(pv, function(ni, nv) {
553
+ encoded += _encode(v) + '=' + _encode(nv) + '&';
554
+ });
555
+ } else {
556
+ encoded += _encode(v) + '=' + _encode(pv) + '&';
557
+ }
558
+ });
559
+ encoded = encoded.substr(0, encoded.length - 1);
560
+ }
561
+ }
562
+ if (hash !== '') {
563
+ encoded += '#' + _encode(hash);
564
+ }
565
+ if (first != '/' && encoded.substr(0, 1) == '/') {
566
+ encoded = encoded.substr(1);
567
+ }
568
+ if (/#|&|\?/.test(last)) {
569
+ encoded += last;
570
+ }
571
+ return encoded;
572
+ },
573
+ decode: function(value) {
574
+ return decodeURIComponent(value.replace(/\+/g, '%20'));
575
+ },
406
576
  title: function(value) {
407
577
  if (value !== undefined) {
408
- value = _dc(value);
409
578
  _st(function() {
410
579
  _title = _d.title = value;
411
580
  if (_juststart && _frame && _frame.contentWindow && _frame.contentWindow.document) {
@@ -423,7 +592,7 @@
423
592
  },
424
593
  value: function(value) {
425
594
  if (value !== undefined) {
426
- value = _ec(_dc(_strict(value, TRUE)));
595
+ value = _strict(this.encode(value), TRUE);
427
596
  if (value == '/') {
428
597
  value = '';
429
598
  }
@@ -433,52 +602,57 @@
433
602
  _justset = TRUE;
434
603
  _value = value;
435
604
  if (_opts.autoUpdate || _updating) {
436
- _silent = TRUE;
437
605
  _update(TRUE);
438
- _stack[_h.length] = _value;
439
- if (_safari) {
440
- if (_opts.history) {
441
- _l[ID][_l.pathname] = _stack.toString();
442
- _length = _h.length + 1;
443
- if (_version < 418) {
444
- if (_l.search == '') {
445
- _form.action = '#' + _crawl(_value, TRUE);
446
- _form.submit();
606
+ if (_supportsState()) {
607
+ _h[_opts.history ? 'pushState' : 'replaceState']({}, '',
608
+ _opts.state.replace(/\/$/, '') + (_value == '' ? '/' : _value));
609
+ } else {
610
+ _silent = TRUE;
611
+ _stack[_h.length] = _value;
612
+ if (_webkit) {
613
+ if (_opts.history) {
614
+ _l[ID][_l.pathname] = _stack.toString();
615
+ _length = _h.length + 1;
616
+ if (_version < 418) {
617
+ if (_l.search == '') {
618
+ _form.action = '#' + _crawl(_value, TRUE);
619
+ _form.submit();
620
+ }
621
+ } else if (_version < 523 || _value == '') {
622
+ var evt = _d.createEvent('MouseEvents');
623
+ evt.initEvent('click', TRUE, TRUE);
624
+ var anchor = _d.createElement('a');
625
+ anchor.href = '#' + _crawl(_value, TRUE);
626
+ anchor.dispatchEvent(evt);
627
+ } else {
628
+ _l.hash = '#' + _crawl(_value, TRUE);
447
629
  }
448
- } else if (_version < 523 || _value == '') {
449
- var evt = _d.createEvent('MouseEvents');
450
- evt.initEvent('click', TRUE, TRUE);
451
- var anchor = _d.createElement('a');
452
- anchor.href = '#' + _crawl(_value, TRUE);
453
- anchor.dispatchEvent(evt);
454
630
  } else {
631
+ _l.replace('#' + _crawl(_value, TRUE));
632
+ }
633
+ } else if (_value != _href()) {
634
+ if (_opts.history) {
455
635
  _l.hash = '#' + _crawl(_value, TRUE);
636
+ } else {
637
+ _l.replace('#' + _crawl(_value, TRUE));
456
638
  }
457
- } else {
458
- _l.replace('#' + _crawl(_value, TRUE));
459
639
  }
460
- } else if (_value != _hash()) {
461
- if (_opts.history) {
462
- _l.hash = '#' + _local(_crawl(_value, TRUE), TRUE);
640
+ if ((_msie && _version < 8) && _opts.history) {
641
+ _st(_html, 50);
642
+ }
643
+ if (_webkit) {
644
+ _st(function(){ _silent = FALSE; }, 1);
463
645
  } else {
464
- _l.replace('#' + _crawl(_value, TRUE));
646
+ _silent = FALSE;
465
647
  }
466
648
  }
467
- if ((_msie && _version < 8) && _opts.history) {
468
- _st(_html, 50);
469
- }
470
- if (_safari) {
471
- _st(function(){ _silent = FALSE; }, 1);
472
- } else {
473
- _silent = FALSE;
474
- }
475
649
  }
476
650
  return this;
477
651
  }
478
652
  if (!_supported) {
479
653
  return null;
480
654
  }
481
- return _dc(_strict(_local(_value, FALSE), FALSE));
655
+ return _strict(this.decode(_value), FALSE);
482
656
  },
483
657
  path: function(value) {
484
658
  if (value !== undefined) {
@@ -487,7 +661,10 @@
487
661
  this.value(value + (qs ? '?' + qs : '') + (hash ? '#' + hash : ''));
488
662
  return this;
489
663
  }
490
- return this.value().split('#')[0].split('?')[0];
664
+ return _path(this.value());
665
+ },
666
+ pathNames: function() {
667
+ return _pathNames(this.value());
491
668
  },
492
669
  queryString: function(value) {
493
670
  if (value !== undefined) {
@@ -495,8 +672,7 @@
495
672
  this.value(this.path() + (value ? '?' + value : '') + (hash ? '#' + hash : ''));
496
673
  return this;
497
674
  }
498
- var arr = this.value().split('?');
499
- return arr.slice(1, arr.length).join('?').split('#')[0];
675
+ return _queryString(this.value());
500
676
  },
501
677
  parameter: function(name, value, append) {
502
678
  var i, params;
@@ -510,90 +686,57 @@
510
686
  v = [v];
511
687
  }
512
688
  if (n == name) {
513
- v = (value === null || value == '') ? [] :
689
+ v = (value === null || value === '') ? [] :
514
690
  (append ? v.concat([value]) : [value]);
515
691
  }
516
692
  for (var j = 0; j < v.length; j++) {
517
693
  params.push(n + '=' + v[j]);
518
694
  }
519
695
  }
520
- if ($.inArray(name, names) == -1) {
696
+ if ($.inArray(name, names) == -1 && value !== null && value !== '') {
521
697
  params.push(name + '=' + value);
522
698
  }
523
699
  this.queryString(params.join('&'));
524
700
  return this;
525
701
  }
526
- value = this.queryString();
527
- if (value) {
528
- params = value.split('&');
529
- var r = [];
530
- for (i = 0; i < params.length; i++) {
531
- var p = params[i].split('=');
532
- if (p[0] == name) {
533
- r.push(p[1]);
534
- }
535
- }
536
- if (r.length !== 0) {
537
- return r.length != 1 ? r : r[0];
538
- }
539
- }
540
- },
541
- pathNames: function() {
542
- var path = this.path(),
543
- names = path.replace(/\/{2,9}/g, '/').split('/');
544
- if (path.substr(0, 1) == '/' || path.length === 0) {
545
- names.splice(0, 1);
546
- }
547
- if (path.substr(path.length - 1, 1) == '/') {
548
- names.splice(names.length - 1, 1);
549
- }
550
- return names;
702
+ return _parameter(name, this.value());
551
703
  },
552
704
  parameterNames: function() {
553
- var qs = this.queryString(),
554
- names = [];
555
- if (qs && qs.indexOf('=') != -1) {
556
- var params = qs.split('&');
557
- for (var i = 0; i < params.length; i++) {
558
- var name = params[i].split('=')[0];
559
- if ($.inArray(name, names) == -1) {
560
- names.push(name);
561
- }
562
- }
563
- }
564
- return names;
705
+ return _parameterNames(this.value());
565
706
  },
566
707
  hash: function(value) {
567
708
  if (value !== undefined) {
568
709
  this.value(this.value().split('#')[0] + (value ? '#' + value : ''));
569
710
  return this;
570
711
  }
571
- var arr = this.value().split('#');
572
- return arr.slice(1, arr.length).join('#');
573
- }
712
+ return _hash(this.value());
713
+ }
574
714
  };
575
-
715
+
576
716
  })();
577
717
 
578
- $.fn.address = function (fn) {
579
- this.each(function() {
580
- if (this.tagName.toLowerCase() == 'form') {
581
- $('form').live('submit', function() {
582
- var value = fn ? fn.call(this) : $(this).attr('action') + '?' + $(this).serialize();
583
- $.address.value(value);
584
- return false;
585
- });
586
- } else {
587
- var f = function() {
718
+ $.fn.address = function(fn) {
719
+ if (!$(this).attr('address')) {
720
+ var f = function(e) {
721
+ if ($(this).is('a')) {
588
722
  var value = fn ? fn.call(this) :
589
723
  /address:/.test($(this).attr('rel')) ? $(this).attr('rel').split('address:')[1].split(' ')[0] :
590
- $(this).attr('href').replace(/^#\!?/, '');
724
+ typeof $.address.state() !== 'undefined' && $.address.state() != '/' ?
725
+ $(this).attr('href').replace(new RegExp('^(.*' + $.address.state() + '|\\.)'), '') :
726
+ $(this).attr('href').replace(/^(#\!?|\.)/, '');
591
727
  $.address.value(value);
592
- return false;
593
- };
594
- $(this).click(f).live('click', f);
595
- }
596
- });
728
+ e.preventDefault();
729
+ }
730
+ };
731
+ $(this).click(f).live('click', f).submit(function(e) {
732
+ if ($(this).is('form')) {
733
+ var value = fn ? fn.call(this) : $(this).attr('action') + '?' + $.address.decode($(this).serialize());
734
+ $.address.value(value);
735
+ e.preventDefault();
736
+ }
737
+ }).attr('address', true);
738
+ }
739
+ return this;
597
740
  };
598
741
 
599
742
  }(jQuery));