ajax 1.0.8 → 1.1.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.
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));