wiselinks 0.7.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  #= require_tree ./lib
2
+ # require_tree ./lib_old
2
3
 
3
4
  #= require _page
4
5
  #= require _link
@@ -10,6 +11,7 @@ class Wiselinks
10
11
 
11
12
  @options = $.extend(this._defaults(), @options)
12
13
  if this.enabled()
14
+ if @options.disable_suid then History.options.disableSuid = true
13
15
  @page = new _Wiselinks.Page($target, @options)
14
16
 
15
17
  enabled: ->
@@ -20,8 +22,9 @@ class Wiselinks
20
22
 
21
23
  reload: () ->
22
24
  @page.reload()
23
-
25
+
24
26
  _defaults: ->
27
+ disable_suid: true
25
28
  html4: true
26
29
  html4_root_path: '/'
27
30
  html4_normalize_path: true
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Wiselinks
4
4
  module Version
5
- MAJOR = 0
6
- MINOR = 7
7
- PATCH = 3
5
+ MAJOR = 1
6
+ MINOR = 0
7
+ PATCH = 0
8
8
  BUILD = nil
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
data/wiselinks.gemspec CHANGED
@@ -7,13 +7,13 @@ require 'wiselinks/version'
7
7
  Gem::Specification.new do |gem|
8
8
  gem.name = 'wiselinks'
9
9
  gem.version = Wiselinks::Version::STRING
10
- gem.authors = ['Igor Alexandrov', 'Alexey Solilin', 'Julia Egorova', 'Alexandr Borisov']
11
- gem.email = 'igor.alexandrov@gmail.com'
10
+ gem.authors = ['Igor Alexandrov', 'Alexey Solilin', 'Julia Egorova', 'Alexandr Borisov']
11
+ gem.email = 'igor.alexandrov@gmail.com'
12
12
  gem.summary = 'Wiselinks makes following links and submitting some forms in your web application smarter and faster'
13
13
  gem.homepage = 'http://github.com/igor-alexandrov/wiselinks'
14
14
  gem.licenses = ['MIT']
15
15
 
16
- gem.files = `git ls-files`.split($/)
16
+ gem.files = `git ls-files | grep -v 'build/*'`.split($/)
17
17
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wiselinks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 1.0.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Igor Alexandrov
@@ -11,25 +12,28 @@ authors:
11
12
  autorequire:
12
13
  bindir: bin
13
14
  cert_chain: []
14
- date: 2013-07-04 00:00:00.000000000 Z
15
+ date: 2013-07-10 00:00:00.000000000 Z
15
16
  dependencies:
16
17
  - !ruby/object:Gem::Dependency
17
18
  name: rspec
18
19
  requirement: !ruby/object:Gem::Requirement
20
+ none: false
19
21
  requirements:
20
- - - '>='
22
+ - - ! '>='
21
23
  - !ruby/object:Gem::Version
22
24
  version: '0'
23
25
  type: :development
24
26
  prerelease: false
25
27
  version_requirements: !ruby/object:Gem::Requirement
28
+ none: false
26
29
  requirements:
27
- - - '>='
30
+ - - ! '>='
28
31
  - !ruby/object:Gem::Version
29
32
  version: '0'
30
33
  - !ruby/object:Gem::Dependency
31
34
  name: webmock
32
35
  requirement: !ruby/object:Gem::Requirement
36
+ none: false
33
37
  requirements:
34
38
  - - ~>
35
39
  - !ruby/object:Gem::Version
@@ -37,6 +41,7 @@ dependencies:
37
41
  type: :development
38
42
  prerelease: false
39
43
  version_requirements: !ruby/object:Gem::Requirement
44
+ none: false
40
45
  requirements:
41
46
  - - ~>
42
47
  - !ruby/object:Gem::Version
@@ -44,160 +49,183 @@ dependencies:
44
49
  - !ruby/object:Gem::Dependency
45
50
  name: shoulda
46
51
  requirement: !ruby/object:Gem::Requirement
52
+ none: false
47
53
  requirements:
48
- - - '>='
54
+ - - ! '>='
49
55
  - !ruby/object:Gem::Version
50
56
  version: '0'
51
57
  type: :development
52
58
  prerelease: false
53
59
  version_requirements: !ruby/object:Gem::Requirement
60
+ none: false
54
61
  requirements:
55
- - - '>='
62
+ - - ! '>='
56
63
  - !ruby/object:Gem::Version
57
64
  version: '0'
58
65
  - !ruby/object:Gem::Dependency
59
66
  name: simplecov
60
67
  requirement: !ruby/object:Gem::Requirement
68
+ none: false
61
69
  requirements:
62
- - - '>='
70
+ - - ! '>='
63
71
  - !ruby/object:Gem::Version
64
72
  version: '0'
65
73
  type: :development
66
74
  prerelease: false
67
75
  version_requirements: !ruby/object:Gem::Requirement
76
+ none: false
68
77
  requirements:
69
- - - '>='
78
+ - - ! '>='
70
79
  - !ruby/object:Gem::Version
71
80
  version: '0'
72
81
  - !ruby/object:Gem::Dependency
73
82
  name: coveralls
74
83
  requirement: !ruby/object:Gem::Requirement
84
+ none: false
75
85
  requirements:
76
- - - '>='
86
+ - - ! '>='
77
87
  - !ruby/object:Gem::Version
78
88
  version: '0'
79
89
  type: :development
80
90
  prerelease: false
81
91
  version_requirements: !ruby/object:Gem::Requirement
92
+ none: false
82
93
  requirements:
83
- - - '>='
94
+ - - ! '>='
84
95
  - !ruby/object:Gem::Version
85
96
  version: '0'
86
97
  - !ruby/object:Gem::Dependency
87
98
  name: rake
88
99
  requirement: !ruby/object:Gem::Requirement
100
+ none: false
89
101
  requirements:
90
- - - '>='
102
+ - - ! '>='
91
103
  - !ruby/object:Gem::Version
92
104
  version: '0'
93
105
  type: :development
94
106
  prerelease: false
95
107
  version_requirements: !ruby/object:Gem::Requirement
108
+ none: false
96
109
  requirements:
97
- - - '>='
110
+ - - ! '>='
98
111
  - !ruby/object:Gem::Version
99
112
  version: '0'
100
113
  - !ruby/object:Gem::Dependency
101
114
  name: bundler
102
115
  requirement: !ruby/object:Gem::Requirement
116
+ none: false
103
117
  requirements:
104
- - - '>='
118
+ - - ! '>='
105
119
  - !ruby/object:Gem::Version
106
120
  version: '0'
107
121
  type: :development
108
122
  prerelease: false
109
123
  version_requirements: !ruby/object:Gem::Requirement
124
+ none: false
110
125
  requirements:
111
- - - '>='
126
+ - - ! '>='
112
127
  - !ruby/object:Gem::Version
113
128
  version: '0'
114
129
  - !ruby/object:Gem::Dependency
115
130
  name: sqlite3
116
131
  requirement: !ruby/object:Gem::Requirement
132
+ none: false
117
133
  requirements:
118
- - - '>='
134
+ - - ! '>='
119
135
  - !ruby/object:Gem::Version
120
136
  version: '0'
121
137
  type: :development
122
138
  prerelease: false
123
139
  version_requirements: !ruby/object:Gem::Requirement
140
+ none: false
124
141
  requirements:
125
- - - '>='
142
+ - - ! '>='
126
143
  - !ruby/object:Gem::Version
127
144
  version: '0'
128
145
  - !ruby/object:Gem::Dependency
129
146
  name: rspec
130
147
  requirement: !ruby/object:Gem::Requirement
148
+ none: false
131
149
  requirements:
132
- - - '>='
150
+ - - ! '>='
133
151
  - !ruby/object:Gem::Version
134
152
  version: '0'
135
153
  type: :development
136
154
  prerelease: false
137
155
  version_requirements: !ruby/object:Gem::Requirement
156
+ none: false
138
157
  requirements:
139
- - - '>='
158
+ - - ! '>='
140
159
  - !ruby/object:Gem::Version
141
160
  version: '0'
142
161
  - !ruby/object:Gem::Dependency
143
162
  name: rspec-rails
144
163
  requirement: !ruby/object:Gem::Requirement
164
+ none: false
145
165
  requirements:
146
- - - '>='
166
+ - - ! '>='
147
167
  - !ruby/object:Gem::Version
148
168
  version: '0'
149
169
  type: :development
150
170
  prerelease: false
151
171
  version_requirements: !ruby/object:Gem::Requirement
172
+ none: false
152
173
  requirements:
153
- - - '>='
174
+ - - ! '>='
154
175
  - !ruby/object:Gem::Version
155
176
  version: '0'
156
177
  - !ruby/object:Gem::Dependency
157
178
  name: factory_girl
158
179
  requirement: !ruby/object:Gem::Requirement
180
+ none: false
159
181
  requirements:
160
- - - '>='
182
+ - - ! '>='
161
183
  - !ruby/object:Gem::Version
162
184
  version: '0'
163
185
  type: :development
164
186
  prerelease: false
165
187
  version_requirements: !ruby/object:Gem::Requirement
188
+ none: false
166
189
  requirements:
167
- - - '>='
190
+ - - ! '>='
168
191
  - !ruby/object:Gem::Version
169
192
  version: '0'
170
193
  - !ruby/object:Gem::Dependency
171
194
  name: faker
172
195
  requirement: !ruby/object:Gem::Requirement
196
+ none: false
173
197
  requirements:
174
- - - '>='
198
+ - - ! '>='
175
199
  - !ruby/object:Gem::Version
176
200
  version: '0'
177
201
  type: :development
178
202
  prerelease: false
179
203
  version_requirements: !ruby/object:Gem::Requirement
204
+ none: false
180
205
  requirements:
181
- - - '>='
206
+ - - ! '>='
182
207
  - !ruby/object:Gem::Version
183
208
  version: '0'
184
209
  - !ruby/object:Gem::Dependency
185
210
  name: capybara
186
211
  requirement: !ruby/object:Gem::Requirement
212
+ none: false
187
213
  requirements:
188
- - - '>='
214
+ - - ! '>='
189
215
  - !ruby/object:Gem::Version
190
216
  version: '0'
191
217
  type: :development
192
218
  prerelease: false
193
219
  version_requirements: !ruby/object:Gem::Requirement
220
+ none: false
194
221
  requirements:
195
- - - '>='
222
+ - - ! '>='
196
223
  - !ruby/object:Gem::Version
197
224
  version: '0'
198
225
  - !ruby/object:Gem::Dependency
199
226
  name: rails
200
227
  requirement: !ruby/object:Gem::Requirement
228
+ none: false
201
229
  requirements:
202
230
  - - ~>
203
231
  - !ruby/object:Gem::Version
@@ -205,6 +233,7 @@ dependencies:
205
233
  type: :development
206
234
  prerelease: false
207
235
  version_requirements: !ruby/object:Gem::Requirement
236
+ none: false
208
237
  requirements:
209
238
  - - ~>
210
239
  - !ruby/object:Gem::Version
@@ -212,43 +241,49 @@ dependencies:
212
241
  - !ruby/object:Gem::Dependency
213
242
  name: coffee-rails
214
243
  requirement: !ruby/object:Gem::Requirement
244
+ none: false
215
245
  requirements:
216
- - - '>='
246
+ - - ! '>='
217
247
  - !ruby/object:Gem::Version
218
248
  version: '0'
219
249
  type: :development
220
250
  prerelease: false
221
251
  version_requirements: !ruby/object:Gem::Requirement
252
+ none: false
222
253
  requirements:
223
- - - '>='
254
+ - - ! '>='
224
255
  - !ruby/object:Gem::Version
225
256
  version: '0'
226
257
  - !ruby/object:Gem::Dependency
227
258
  name: closure-compiler
228
259
  requirement: !ruby/object:Gem::Requirement
260
+ none: false
229
261
  requirements:
230
- - - '>='
262
+ - - ! '>='
231
263
  - !ruby/object:Gem::Version
232
264
  version: '0'
233
265
  type: :development
234
266
  prerelease: false
235
267
  version_requirements: !ruby/object:Gem::Requirement
268
+ none: false
236
269
  requirements:
237
- - - '>='
270
+ - - ! '>='
238
271
  - !ruby/object:Gem::Version
239
272
  version: '0'
240
273
  - !ruby/object:Gem::Dependency
241
274
  name: coffeelint
242
275
  requirement: !ruby/object:Gem::Requirement
276
+ none: false
243
277
  requirements:
244
- - - '>='
278
+ - - ! '>='
245
279
  - !ruby/object:Gem::Version
246
280
  version: '0'
247
281
  type: :development
248
282
  prerelease: false
249
283
  version_requirements: !ruby/object:Gem::Requirement
284
+ none: false
250
285
  requirements:
251
- - - '>='
286
+ - - ! '>='
252
287
  - !ruby/object:Gem::Version
253
288
  version: '0'
254
289
  description:
@@ -267,21 +302,14 @@ files:
267
302
  - README.md
268
303
  - Rakefile
269
304
  - app/views/layouts/wiselinks.html.erb
270
- - build/wiselinks-0.7.3.js
271
- - build/wiselinks-0.7.3.min.js
272
- - build/wiselinks-0.7.3.min.js.gz
273
305
  - compiler.jar
274
306
  - lib/assets/javascripts/_form.js.coffee
275
307
  - lib/assets/javascripts/_link.js.coffee
276
308
  - lib/assets/javascripts/_page.js.coffee
277
309
  - lib/assets/javascripts/_request_manager.js.coffee
278
- - lib/assets/javascripts/lib/_history.adapter.jquery.js
279
- - lib/assets/javascripts/lib/_history.html4.js
280
- - lib/assets/javascripts/lib/_history.js
281
- - lib/assets/javascripts/lib/_json2.js
310
+ - lib/assets/javascripts/lib/native.history.js
282
311
  - lib/assets/javascripts/wiselinks.js.coffee
283
312
  - lib/wiselinks.rb
284
- - lib/wiselinks/builder.rb
285
313
  - lib/wiselinks/controller_methods.rb
286
314
  - lib/wiselinks/helpers.rb
287
315
  - lib/wiselinks/logger.rb
@@ -359,26 +387,33 @@ files:
359
387
  homepage: http://github.com/igor-alexandrov/wiselinks
360
388
  licenses:
361
389
  - MIT
362
- metadata: {}
363
390
  post_install_message:
364
391
  rdoc_options: []
365
392
  require_paths:
366
393
  - lib
367
394
  required_ruby_version: !ruby/object:Gem::Requirement
395
+ none: false
368
396
  requirements:
369
- - - '>='
397
+ - - ! '>='
370
398
  - !ruby/object:Gem::Version
371
399
  version: '0'
400
+ segments:
401
+ - 0
402
+ hash: 3217867254501092990
372
403
  required_rubygems_version: !ruby/object:Gem::Requirement
404
+ none: false
373
405
  requirements:
374
- - - '>='
406
+ - - ! '>='
375
407
  - !ruby/object:Gem::Version
376
408
  version: '0'
409
+ segments:
410
+ - 0
411
+ hash: 3217867254501092990
377
412
  requirements: []
378
413
  rubyforge_project:
379
- rubygems_version: 2.0.3
414
+ rubygems_version: 1.8.24
380
415
  signing_key:
381
- specification_version: 4
416
+ specification_version: 3
382
417
  summary: Wiselinks makes following links and submitting some forms in your web application
383
418
  smarter and faster
384
419
  test_files:
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: ff45b38e288d992f9f0caac5e9c421fa55f89d08
4
- data.tar.gz: 267a33bf4cace68a13da6d39293fe0f80b147691
5
- SHA512:
6
- metadata.gz: e52123b5f8965db4e49edca5571f9117dfc7308cc83211e665016167e15dae3e59f881c38c17a89e78f2b8068e0488b65edf82ad5476922255b336aca21cdbde
7
- data.tar.gz: 89e98116b5d6073612380e2a720b491597501b650d68dd2d6c442589690b102c0d81f33b278443473b59a4acd9ee20b0d7840d31bff97230980f170d8876bb75
@@ -1,3541 +0,0 @@
1
- /**
2
- * Wiselinks-0.7.3
3
- * @copyright 2012-2013 Igor Alexandrov, Alexey Solilin, Julia Egorova, Alexandr Borisov
4
- * @preserve https://github.com/igor-alexandrov/wiselinks
5
- */
6
-
7
- // Generated by CoffeeScript 1.6.3
8
- (function() {
9
- var Form, Link, Page, RequestManager, Wiselinks;
10
-
11
- Form = (function() {
12
- function Form(page, $form) {
13
- this.page = page;
14
- this.$form = $form;
15
- }
16
-
17
- Form.prototype.process = function() {
18
- var self;
19
- self = this;
20
- if (self._include_blank_url_params()) {
21
- return self.page.load(self._url(), self._target(), self._type());
22
- } else {
23
- return self._without_blank_url_params(function() {
24
- return self.page.load(self._url(), self._target(), self._type());
25
- });
26
- }
27
- };
28
-
29
- Form.prototype._without_blank_url_params = function(callback) {
30
- var $disable, selector;
31
- selector = 'select:not(:disabled),input:not(:disabled)';
32
- $disable = this.$form.find(selector).filter(function() {
33
- return !$(this).val();
34
- });
35
- $disable.attr('disabled', true);
36
- callback();
37
- return $disable.attr('disabled', false);
38
- };
39
-
40
- Form.prototype._params = function() {
41
- var hash, item, name, _i, _len, _ref;
42
- hash = {};
43
- _ref = this.$form.serializeArray();
44
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
45
- item = _ref[_i];
46
- if (item.name !== 'utf8') {
47
- name = item.name.indexOf('[]', item.name.length - '[]'.length) !== -1 ? item.name.substr(0, item.name.length - 2) : item.name;
48
- if (hash[name] != null) {
49
- hash[name] = hash[name] + ("," + item.value);
50
- } else {
51
- hash[name] = item.value;
52
- }
53
- }
54
- }
55
- return hash;
56
- };
57
-
58
- Form.prototype._include_blank_url_params = function() {
59
- return this.$form.data('include-blank-url-params') === true;
60
- };
61
-
62
- Form.prototype._optimize_url_params = function() {
63
- return this.$form.data('optimize-url-params') !== false;
64
- };
65
-
66
- Form.prototype._type = function() {
67
- if (this.$form.data('push') === 'partial') {
68
- return 'partial';
69
- } else {
70
- return 'template';
71
- }
72
- };
73
-
74
- Form.prototype._target = function() {
75
- return this.$form.data('target');
76
- };
77
-
78
- Form.prototype._url = function() {
79
- var key, params, self, serialized, url, value;
80
- self = this;
81
- serialized = (function() {
82
- var _ref;
83
- if (self._optimize_url_params()) {
84
- params = [];
85
- _ref = this._params();
86
- for (key in _ref) {
87
- value = _ref[key];
88
- params.push("" + key + "=" + (encodeURIComponent(value)));
89
- }
90
- return params.join('&');
91
- } else {
92
- return this.$form.serialize();
93
- }
94
- }).call(this);
95
- url = this.$form.attr("action");
96
- if (serialized.length > 0) {
97
- url += "?" + serialized;
98
- }
99
- return url;
100
- };
101
-
102
- return Form;
103
-
104
- })();
105
-
106
- if (window._Wiselinks == null) {
107
- window._Wiselinks = {};
108
- }
109
-
110
- window._Wiselinks.Form = Form;
111
-
112
- Link = (function() {
113
- function Link(page, $link) {
114
- this.page = page;
115
- this.$link = $link;
116
- }
117
-
118
- Link.prototype.allows_process = function(event) {
119
- return !(this._cross_origin_link(event.currentTarget) || this._non_standard_click(event));
120
- };
121
-
122
- Link.prototype.process = function() {
123
- var type;
124
- type = this.$link.data('push') === 'partial' ? 'partial' : 'template';
125
- return this.page.load(this.$link.attr("href"), this.$link.data('target'), type);
126
- };
127
-
128
- Link.prototype._cross_origin_link = function(link) {
129
- return (location.protocol !== link.protocol) || (location.port !== link.port) || (location.host.split(':')[0] !== link.host.split(':')[0]);
130
- };
131
-
132
- Link.prototype._non_standard_click = function(event) {
133
- return event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
134
- };
135
-
136
- return Link;
137
-
138
- })();
139
-
140
- if (window._Wiselinks === void 0) {
141
- window._Wiselinks = {};
142
- }
143
-
144
- window._Wiselinks.Link = Link;
145
-
146
- Page = (function() {
147
- function Page($target, options) {
148
- var self;
149
- this.$target = $target;
150
- this.options = options;
151
- self = this;
152
- this.template_id = new Date().getTime();
153
- this.request_manager = new _Wiselinks.RequestManager(this.options);
154
- self._try_target(this.$target);
155
- if (History.emulated.pushState && this.options.html4 === true) {
156
- if (window.location.href.indexOf('#!') === -1 && this.options.html4_normalize_path === true && window.location.pathname !== this.options.html4_root_path) {
157
- window.location.href = "" + window.location.protocol + "//" + window.location.host + this.options.html4_root_path + "#!" + window.location.pathname;
158
- }
159
- if (window.location.hash.indexOf('#!') !== -1) {
160
- self._call(self._make_state(window.location.hash.substring(2)));
161
- }
162
- }
163
- History.Adapter.bind(window, "statechange", function(event, data) {
164
- var state;
165
- state = History.getState();
166
- if (self._template_id_changed(state)) {
167
- return self._call(self._reset_state(state));
168
- } else {
169
- return self._call(state);
170
- }
171
- });
172
- $(document).on('click', 'a[data-push], a[data-replace]', function(event) {
173
- var link;
174
- if ((link = new _Wiselinks.Link(self, $(this))).allows_process(event)) {
175
- event.preventDefault();
176
- link.process();
177
- return false;
178
- }
179
- });
180
- $(document).on('submit', 'form[data-push], form[data-replace]', function(event) {
181
- var form;
182
- if ((form = new _Wiselinks.Form(self, $(this)))) {
183
- event.preventDefault();
184
- form.process();
185
- return false;
186
- }
187
- });
188
- }
189
-
190
- Page.prototype.load = function(url, target, render) {
191
- if (render == null) {
192
- render = 'template';
193
- }
194
- if (render !== 'partial') {
195
- this.template_id = new Date().getTime();
196
- }
197
- if (target != null) {
198
- this._try_target($(target));
199
- }
200
- return History.pushState({
201
- timestamp: new Date().getTime(),
202
- template_id: this.template_id,
203
- render: render,
204
- target: target,
205
- referer: window.location.href
206
- }, document.title, url);
207
- };
208
-
209
- Page.prototype.reload = function() {
210
- return History.replaceState({
211
- timestamp: new Date().getTime(),
212
- template_id: this.template_id,
213
- render: 'template',
214
- referer: window.location.href
215
- }, document.title, History.getState().url);
216
- };
217
-
218
- Page.prototype._call = function(state) {
219
- var $target;
220
- $target = state.data.target != null ? $(state.data.target) : this.$target;
221
- return this.request_manager.call($target, state);
222
- };
223
-
224
- Page.prototype._template_id_changed = function(state) {
225
- return (state.data.template_id == null) || state.data.template_id !== this.template_id;
226
- };
227
-
228
- Page.prototype._make_state = function(url, target, render, referer) {
229
- if (render == null) {
230
- render = 'template';
231
- }
232
- return {
233
- url: url,
234
- data: {
235
- target: target,
236
- render: render,
237
- referer: referer
238
- }
239
- };
240
- };
241
-
242
- Page.prototype._reset_state = function(state) {
243
- if (state.data == null) {
244
- state.data = {};
245
- }
246
- state.data.target = null;
247
- state.data.render = 'template';
248
- return state;
249
- };
250
-
251
- Page.prototype._try_target = function($target) {
252
- if ($target.length === 0 && this.options.target_missing === 'exception') {
253
- throw new Error("[Wiselinks] Target missing: `" + $target.selector + "`");
254
- }
255
- };
256
-
257
- return Page;
258
-
259
- })();
260
-
261
- if (window._Wiselinks === void 0) {
262
- window._Wiselinks = {};
263
- }
264
-
265
- window._Wiselinks.Page = Page;
266
-
267
- RequestManager = (function() {
268
- function RequestManager(options) {
269
- this.options = options != null ? options : {};
270
- }
271
-
272
- RequestManager.prototype.call = function($target, state) {
273
- var self;
274
- self = this;
275
- if (this.redirected != null) {
276
- this.redirected = null;
277
- return;
278
- }
279
- self._loading($target, state);
280
- return $.ajax({
281
- url: state.url,
282
- headers: {
283
- 'X-Wiselinks': state.data.render,
284
- 'X-Wiselinks-Referer': state.data.referer
285
- },
286
- dataType: "html"
287
- }).done(function(data, status, xhr) {
288
- var assets_digest, url;
289
- url = self._normalize(xhr.getResponseHeader('X-Wiselinks-Url'));
290
- assets_digest = xhr.getResponseHeader('X-Wiselinks-Assets-Digest');
291
- if (self._assets_changed(assets_digest)) {
292
- return window.location.reload(true);
293
- } else {
294
- state = History.getState();
295
- if ((url != null) && (url !== self._normalize(window.location.href))) {
296
- self._redirect_to(url, $target, state, xhr);
297
- }
298
- $target.html(data);
299
- self._title(xhr.getResponseHeader('X-Wiselinks-Title'));
300
- return self._done($target, status, state.url, data);
301
- }
302
- }).fail(function(xhr, status, error) {
303
- return self._fail($target, status, state.url, error);
304
- }).always(function(data_or_xhr, status, xhr_or_error) {
305
- return self._always($target, status, state.url);
306
- });
307
- };
308
-
309
- RequestManager.prototype._normalize = function(url) {
310
- if (url == null) {
311
- return;
312
- }
313
- url = url.replace(/\/+$/, '');
314
- return url;
315
- };
316
-
317
- RequestManager.prototype._assets_changed = function(assets_digest) {
318
- return (this.options.assets_digest != null) && this.options.assets_digest !== assets_digest;
319
- };
320
-
321
- RequestManager.prototype._redirect_to = function(url, $target, state, xhr) {
322
- if (xhr && xhr.readyState < 4) {
323
- xhr.onreadystatechange = $.noop;
324
- xhr.abort();
325
- }
326
- this.redirected = true;
327
- $(document).trigger('page:redirected', [$target, state.data.render, url]);
328
- return History.replaceState(state.data, document.title, url);
329
- };
330
-
331
- RequestManager.prototype._loading = function($target, state) {
332
- return $(document).trigger('page:loading', [$target, state.data.render, state.url]);
333
- };
334
-
335
- RequestManager.prototype._done = function($target, status, url, data) {
336
- return $(document).trigger('page:done', [$target, status, url, data]);
337
- };
338
-
339
- RequestManager.prototype._fail = function($target, status, url, error) {
340
- return $(document).trigger('page:fail', [$target, status, url, error]);
341
- };
342
-
343
- RequestManager.prototype._always = function($target, status, url) {
344
- return $(document).trigger('page:always', [$target, status, url]);
345
- };
346
-
347
- RequestManager.prototype._title = function(value) {
348
- if (value != null) {
349
- $(document).trigger('page:title', decodeURI(value));
350
- return document.title = decodeURI(value);
351
- }
352
- };
353
-
354
- return RequestManager;
355
-
356
- })();
357
-
358
- if (window._Wiselinks === void 0) {
359
- window._Wiselinks = {};
360
- }
361
-
362
- window._Wiselinks.RequestManager = RequestManager;
363
-
364
- Wiselinks = (function() {
365
- function Wiselinks($target, options) {
366
- if ($target == null) {
367
- $target = $('body');
368
- }
369
- this.options = options != null ? options : {};
370
- this._try_jquery();
371
- this.options = $.extend(this._defaults(), this.options);
372
- if (this.enabled()) {
373
- this.page = new _Wiselinks.Page($target, this.options);
374
- }
375
- }
376
-
377
- Wiselinks.prototype.enabled = function() {
378
- return !History.emulated.pushState || this.options.html4 === true;
379
- };
380
-
381
- Wiselinks.prototype.load = function(url, target, render) {
382
- if (render == null) {
383
- render = 'template';
384
- }
385
- return this.page.load(url, target, render);
386
- };
387
-
388
- Wiselinks.prototype.reload = function() {
389
- return this.page.reload();
390
- };
391
-
392
- Wiselinks.prototype._defaults = function() {
393
- return {
394
- html4: true,
395
- html4_root_path: '/',
396
- html4_normalize_path: true,
397
- target_missing: null,
398
- assets_digest: $("meta[name='assets-digest']").attr("content")
399
- };
400
- };
401
-
402
- Wiselinks.prototype._try_jquery = function() {
403
- if (window.jQuery == null) {
404
- throw new Error("[Wiselinks] jQuery is not loaded");
405
- }
406
- };
407
-
408
- return Wiselinks;
409
-
410
- })();
411
-
412
- window.Wiselinks = Wiselinks;
413
-
414
- }).call(this);
415
- /**
416
- * History.js jQuery Adapter
417
- * @author Benjamin Arthur Lupton <contact@balupton.com>
418
- * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
419
- * @license New BSD License <http://creativecommons.org/licenses/BSD/>
420
- */
421
-
422
- // Closure
423
- (function(window,undefined){
424
- "use strict";
425
-
426
- // Localise Globals
427
- var
428
- History = window.History = window.History||{},
429
- jQuery = window.jQuery;
430
-
431
- // Check Existence
432
- if ( typeof History.Adapter !== 'undefined' ) {
433
- throw new Error('History.js Adapter has already been loaded...');
434
- }
435
-
436
- // Add the Adapter
437
- History.Adapter = {
438
- /**
439
- * History.Adapter.bind(el,event,callback)
440
- * @param {Element|string} el
441
- * @param {string} event - custom and standard events
442
- * @param {function} callback
443
- * @return {void}
444
- */
445
- bind: function(el,event,callback){
446
- jQuery(el).bind(event,callback);
447
- },
448
-
449
- /**
450
- * History.Adapter.trigger(el,event)
451
- * @param {Element|string} el
452
- * @param {string} event - custom and standard events
453
- * @param {Object=} extra - a object of extra event data (optional)
454
- * @return {void}
455
- */
456
- trigger: function(el,event,extra){
457
- jQuery(el).trigger(event,extra);
458
- },
459
-
460
- /**
461
- * History.Adapter.extractEventData(key,event,extra)
462
- * @param {string} key - key for the event data to extract
463
- * @param {string} event - custom and standard events
464
- * @param {Object=} extra - a object of extra event data (optional)
465
- * @return {mixed}
466
- */
467
- extractEventData: function(key,event,extra){
468
- // jQuery Native then jQuery Custom
469
- var result = (event && event.originalEvent && event.originalEvent[key]) || (extra && extra[key]) || undefined;
470
-
471
- // Return
472
- return result;
473
- },
474
-
475
- /**
476
- * History.Adapter.onDomLoad(callback)
477
- * @param {function} callback
478
- * @return {void}
479
- */
480
- onDomLoad: function(callback) {
481
- jQuery(callback);
482
- }
483
- };
484
-
485
- // Try and Initialise History
486
- if ( typeof History.init !== 'undefined' ) {
487
- History.init();
488
- }
489
-
490
- })(window);
491
- /**
492
- * History.js HTML4 Support
493
- * Depends on the HTML5 Support
494
- * @author Benjamin Arthur Lupton <contact@balupton.com>
495
- * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
496
- * @license New BSD License <http://creativecommons.org/licenses/BSD/>
497
- */
498
-
499
- (function(window,undefined){
500
- "use strict";
501
-
502
- // ========================================================================
503
- // Initialise
504
-
505
- // Localise Globals
506
- var
507
- document = window.document, // Make sure we are using the correct document
508
- setTimeout = window.setTimeout||setTimeout,
509
- clearTimeout = window.clearTimeout||clearTimeout,
510
- setInterval = window.setInterval||setInterval,
511
- History = window.History = window.History||{}; // Public History Object
512
-
513
- // Check Existence
514
- if ( typeof History.initHtml4 !== 'undefined' ) {
515
- throw new Error('History.js HTML4 Support has already been loaded...');
516
- }
517
-
518
-
519
- // ========================================================================
520
- // Initialise HTML4 Support
521
-
522
- // Initialise HTML4 Support
523
- History.initHtml4 = function(){
524
- // Initialise
525
- if ( typeof History.initHtml4.initialized !== 'undefined' ) {
526
- // Already Loaded
527
- return false;
528
- }
529
- else {
530
- History.initHtml4.initialized = true;
531
- }
532
-
533
-
534
- // ====================================================================
535
- // Properties
536
-
537
- /**
538
- * History.enabled
539
- * Is History enabled?
540
- */
541
- History.enabled = true;
542
-
543
-
544
- // ====================================================================
545
- // Hash Storage
546
-
547
- /**
548
- * History.savedHashes
549
- * Store the hashes in an array
550
- */
551
- History.savedHashes = [];
552
-
553
- /**
554
- * History.isLastHash(newHash)
555
- * Checks if the hash is the last hash
556
- * @param {string} newHash
557
- * @return {boolean} true
558
- */
559
- History.isLastHash = function(newHash){
560
- // Prepare
561
- var oldHash = History.getHashByIndex(),
562
- isLast;
563
-
564
- // Check
565
- isLast = newHash === oldHash;
566
-
567
- // Return isLast
568
- return isLast;
569
- };
570
-
571
- /**
572
- * History.saveHash(newHash)
573
- * Push a Hash
574
- * @param {string} newHash
575
- * @return {boolean} true
576
- */
577
- History.saveHash = function(newHash){
578
- // Check Hash
579
- if ( History.isLastHash(newHash) ) {
580
- return false;
581
- }
582
-
583
- // Push the Hash
584
- History.savedHashes.push(newHash);
585
-
586
- // Return true
587
- return true;
588
- };
589
-
590
- /**
591
- * History.getHashByIndex()
592
- * Gets a hash by the index
593
- * @param {integer} index
594
- * @return {string}
595
- */
596
- History.getHashByIndex = function(index){
597
- // Prepare
598
- var hash = null;
599
-
600
- // Handle
601
- if ( typeof index === 'undefined' ) {
602
- // Get the last inserted
603
- hash = History.savedHashes[History.savedHashes.length-1];
604
- }
605
- else if ( index < 0 ) {
606
- // Get from the end
607
- hash = History.savedHashes[History.savedHashes.length+index];
608
- }
609
- else {
610
- // Get from the beginning
611
- hash = History.savedHashes[index];
612
- }
613
-
614
- // Return hash
615
- return hash;
616
- };
617
-
618
-
619
- // ====================================================================
620
- // Discarded States
621
-
622
- /**
623
- * History.discardedHashes
624
- * A hashed array of discarded hashes
625
- */
626
- History.discardedHashes = {};
627
-
628
- /**
629
- * History.discardedStates
630
- * A hashed array of discarded states
631
- */
632
- History.discardedStates = {};
633
-
634
- /**
635
- * History.discardState(State)
636
- * Discards the state by ignoring it through History
637
- * @param {object} State
638
- * @return {true}
639
- */
640
- History.discardState = function(discardedState,forwardState,backState){
641
- //History.debug('History.discardState', arguments);
642
- // Prepare
643
- var discardedStateHash = History.getHashByState(discardedState),
644
- discardObject;
645
-
646
- // Create Discard Object
647
- discardObject = {
648
- 'discardedState': discardedState,
649
- 'backState': backState,
650
- 'forwardState': forwardState
651
- };
652
-
653
- // Add to DiscardedStates
654
- History.discardedStates[discardedStateHash] = discardObject;
655
-
656
- // Return true
657
- return true;
658
- };
659
-
660
- /**
661
- * History.discardHash(hash)
662
- * Discards the hash by ignoring it through History
663
- * @param {string} hash
664
- * @return {true}
665
- */
666
- History.discardHash = function(discardedHash,forwardState,backState){
667
- //History.debug('History.discardState', arguments);
668
- // Create Discard Object
669
- var discardObject = {
670
- 'discardedHash': discardedHash,
671
- 'backState': backState,
672
- 'forwardState': forwardState
673
- };
674
-
675
- // Add to discardedHash
676
- History.discardedHashes[discardedHash] = discardObject;
677
-
678
- // Return true
679
- return true;
680
- };
681
-
682
- /**
683
- * History.discardState(State)
684
- * Checks to see if the state is discarded
685
- * @param {object} State
686
- * @return {bool}
687
- */
688
- History.discardedState = function(State){
689
- // Prepare
690
- var StateHash = History.getHashByState(State),
691
- discarded;
692
-
693
- // Check
694
- discarded = History.discardedStates[StateHash]||false;
695
-
696
- // Return true
697
- return discarded;
698
- };
699
-
700
- /**
701
- * History.discardedHash(hash)
702
- * Checks to see if the state is discarded
703
- * @param {string} State
704
- * @return {bool}
705
- */
706
- History.discardedHash = function(hash){
707
- // Check
708
- var discarded = History.discardedHashes[hash]||false;
709
-
710
- // Return true
711
- return discarded;
712
- };
713
-
714
- /**
715
- * History.recycleState(State)
716
- * Allows a discarded state to be used again
717
- * @param {object} data
718
- * @param {string} title
719
- * @param {string} url
720
- * @return {true}
721
- */
722
- History.recycleState = function(State){
723
- //History.debug('History.recycleState', arguments);
724
- // Prepare
725
- var StateHash = History.getHashByState(State);
726
-
727
- // Remove from DiscardedStates
728
- if ( History.discardedState(State) ) {
729
- delete History.discardedStates[StateHash];
730
- }
731
-
732
- // Return true
733
- return true;
734
- };
735
-
736
-
737
- // ====================================================================
738
- // HTML4 HashChange Support
739
-
740
- if ( History.emulated.hashChange ) {
741
- /*
742
- * We must emulate the HTML4 HashChange Support by manually checking for hash changes
743
- */
744
-
745
- /**
746
- * History.hashChangeInit()
747
- * Init the HashChange Emulation
748
- */
749
- History.hashChangeInit = function(){
750
- // Define our Checker Function
751
- History.checkerFunction = null;
752
-
753
- // Define some variables that will help in our checker function
754
- var lastDocumentHash = '',
755
- iframeId, iframe,
756
- lastIframeHash, checkerRunning;
757
-
758
- // Handle depending on the browser
759
- if ( History.isInternetExplorer() ) {
760
- // IE6 and IE7
761
- // We need to use an iframe to emulate the back and forward buttons
762
-
763
- // Create iFrame
764
- iframeId = 'historyjs-iframe';
765
- iframe = document.createElement('iframe');
766
-
767
- // Adjust iFarme
768
- iframe.setAttribute('id', iframeId);
769
- iframe.style.display = 'none';
770
-
771
- // Append iFrame
772
- document.body.appendChild(iframe);
773
-
774
- // Create initial history entry
775
- iframe.contentWindow.document.open();
776
- iframe.contentWindow.document.close();
777
-
778
- // Define some variables that will help in our checker function
779
- lastIframeHash = '';
780
- checkerRunning = false;
781
-
782
- // Define the checker function
783
- History.checkerFunction = function(){
784
- // Check Running
785
- if ( checkerRunning ) {
786
- return false;
787
- }
788
-
789
- // Update Running
790
- checkerRunning = true;
791
-
792
- // Fetch
793
- var documentHash = History.getHash()||'',
794
- iframeHash = History.unescapeHash(iframe.contentWindow.document.location.hash)||'';
795
-
796
- // The Document Hash has changed (application caused)
797
- if ( documentHash !== lastDocumentHash ) {
798
- // Equalise
799
- lastDocumentHash = documentHash;
800
-
801
- // Create a history entry in the iframe
802
- if ( iframeHash !== documentHash ) {
803
- //History.debug('hashchange.checker: iframe hash change', 'documentHash (new):', documentHash, 'iframeHash (old):', iframeHash);
804
-
805
- // Equalise
806
- lastIframeHash = iframeHash = documentHash;
807
-
808
- // Create History Entry
809
- iframe.contentWindow.document.open();
810
- iframe.contentWindow.document.close();
811
-
812
- // Update the iframe's hash
813
- iframe.contentWindow.document.location.hash = History.escapeHash(documentHash);
814
- }
815
-
816
- // Trigger Hashchange Event
817
- History.Adapter.trigger(window,'hashchange');
818
- }
819
-
820
- // The iFrame Hash has changed (back button caused)
821
- else if ( iframeHash !== lastIframeHash ) {
822
- //History.debug('hashchange.checker: iframe hash out of sync', 'iframeHash (new):', iframeHash, 'documentHash (old):', documentHash);
823
-
824
- // Equalise
825
- lastIframeHash = iframeHash;
826
-
827
- // Update the Hash
828
- History.setHash(iframeHash,false);
829
- }
830
-
831
- // Reset Running
832
- checkerRunning = false;
833
-
834
- // Return true
835
- return true;
836
- };
837
- }
838
- else {
839
- // We are not IE
840
- // Firefox 1 or 2, Opera
841
-
842
- // Define the checker function
843
- History.checkerFunction = function(){
844
- // Prepare
845
- var documentHash = History.getHash();
846
-
847
- // The Document Hash has changed (application caused)
848
- if ( documentHash !== lastDocumentHash ) {
849
- // Equalise
850
- lastDocumentHash = documentHash;
851
-
852
- // Trigger Hashchange Event
853
- History.Adapter.trigger(window,'hashchange');
854
- }
855
-
856
- // Return true
857
- return true;
858
- };
859
- }
860
-
861
- // Apply the checker function
862
- History.intervalList.push(setInterval(History.checkerFunction, History.options.hashChangeInterval));
863
-
864
- // Done
865
- return true;
866
- }; // History.hashChangeInit
867
-
868
- // Bind hashChangeInit
869
- History.Adapter.onDomLoad(History.hashChangeInit);
870
-
871
- } // History.emulated.hashChange
872
-
873
-
874
- // ====================================================================
875
- // HTML5 State Support
876
-
877
- // Non-Native pushState Implementation
878
- if ( History.emulated.pushState ) {
879
- /*
880
- * We must emulate the HTML5 State Management by using HTML4 HashChange
881
- */
882
-
883
- /**
884
- * History.onHashChange(event)
885
- * Trigger HTML5's window.onpopstate via HTML4 HashChange Support
886
- */
887
- History.onHashChange = function(event){
888
- //History.debug('History.onHashChange', arguments);
889
-
890
- // Prepare
891
- var currentUrl = ((event && event.newURL) || document.location.href),
892
- currentHash = History.getHashByUrl(currentUrl),
893
- currentState = null,
894
- currentStateHash = null,
895
- currentStateHashExits = null,
896
- discardObject;
897
-
898
- // Check if we are the same state
899
- if ( History.isLastHash(currentHash) ) {
900
- // There has been no change (just the page's hash has finally propagated)
901
- //History.debug('History.onHashChange: no change');
902
- History.busy(false);
903
- return false;
904
- }
905
-
906
- // Reset the double check
907
- History.doubleCheckComplete();
908
-
909
- // Store our location for use in detecting back/forward direction
910
- History.saveHash(currentHash);
911
-
912
- // Expand Hash
913
- if ( currentHash && History.isTraditionalAnchor(currentHash) ) {
914
- //History.debug('History.onHashChange: traditional anchor', currentHash);
915
- // Traditional Anchor Hash
916
- History.Adapter.trigger(window,'anchorchange');
917
- History.busy(false);
918
- return false;
919
- }
920
-
921
- // Create State
922
- currentState = History.extractState(History.getFullUrl(currentHash||document.location.href,false),true);
923
-
924
- // Check if we are the same state
925
- if ( History.isLastSavedState(currentState) ) {
926
- //History.debug('History.onHashChange: no change');
927
- // There has been no change (just the page's hash has finally propagated)
928
- History.busy(false);
929
- return false;
930
- }
931
-
932
- // Create the state Hash
933
- currentStateHash = History.getHashByState(currentState);
934
-
935
- // Check if we are DiscardedState
936
- discardObject = History.discardedState(currentState);
937
- if ( discardObject ) {
938
- // Ignore this state as it has been discarded and go back to the state before it
939
- if ( History.getHashByIndex(-2) === History.getHashByState(discardObject.forwardState) ) {
940
- // We are going backwards
941
- //History.debug('History.onHashChange: go backwards');
942
- History.back(false);
943
- } else {
944
- // We are going forwards
945
- //History.debug('History.onHashChange: go forwards');
946
- History.forward(false);
947
- }
948
- return false;
949
- }
950
-
951
- // Push the new HTML5 State
952
- //History.debug('History.onHashChange: success hashchange');
953
- History.pushState(currentState.data,currentState.title,currentState.url,false);
954
-
955
- // End onHashChange closure
956
- return true;
957
- };
958
- History.Adapter.bind(window,'hashchange',History.onHashChange);
959
-
960
- /**
961
- * History.pushState(data,title,url)
962
- * Add a new State to the history object, become it, and trigger onpopstate
963
- * We have to trigger for HTML4 compatibility
964
- * @param {object} data
965
- * @param {string} title
966
- * @param {string} url
967
- * @return {true}
968
- */
969
- History.pushState = function(data,title,url,queue){
970
- //History.debug('History.pushState: called', arguments);
971
-
972
- // Check the State
973
- if ( History.getHashByUrl(url) ) {
974
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
975
- }
976
-
977
- // Handle Queueing
978
- if ( queue !== false && History.busy() ) {
979
- // Wait + Push to Queue
980
- //History.debug('History.pushState: we must wait', arguments);
981
- History.pushQueue({
982
- scope: History,
983
- callback: History.pushState,
984
- args: arguments,
985
- queue: queue
986
- });
987
- return false;
988
- }
989
-
990
- // Make Busy
991
- History.busy(true);
992
-
993
- // Fetch the State Object
994
- var newState = History.createStateObject(data,title,url),
995
- newStateHash = History.getHashByState(newState),
996
- oldState = History.getState(false),
997
- oldStateHash = History.getHashByState(oldState),
998
- html4Hash = History.getHash();
999
-
1000
- // Store the newState
1001
- History.storeState(newState);
1002
- History.expectedStateId = newState.id;
1003
-
1004
- // Recycle the State
1005
- History.recycleState(newState);
1006
-
1007
- // Force update of the title
1008
- // History.setTitle(newState);
1009
-
1010
- // Check if we are the same State
1011
- if ( newStateHash === oldStateHash ) {
1012
- //History.debug('History.pushState: no change', newStateHash);
1013
- History.busy(false);
1014
- return false;
1015
- }
1016
-
1017
- // Update HTML4 Hash
1018
- if ( newStateHash !== html4Hash && newStateHash !== History.getShortUrl(document.location.href) ) {
1019
- //History.debug('History.pushState: update hash', newStateHash, html4Hash);
1020
- History.setHash(newStateHash,false);
1021
- return false;
1022
- }
1023
-
1024
- // Update HTML5 State
1025
- History.saveState(newState);
1026
-
1027
- // Fire HTML5 Event
1028
- //History.debug('History.pushState: trigger popstate');
1029
- History.Adapter.trigger(window,'statechange');
1030
- History.busy(false);
1031
-
1032
- // End pushState closure
1033
- return true;
1034
- };
1035
-
1036
- /**
1037
- * History.replaceState(data,title,url)
1038
- * Replace the State and trigger onpopstate
1039
- * We have to trigger for HTML4 compatibility
1040
- * @param {object} data
1041
- * @param {string} title
1042
- * @param {string} url
1043
- * @return {true}
1044
- */
1045
- History.replaceState = function(data,title,url,queue){
1046
- //History.debug('History.replaceState: called', arguments);
1047
-
1048
- // Check the State
1049
- if ( History.getHashByUrl(url) ) {
1050
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1051
- }
1052
-
1053
- // Handle Queueing
1054
- if ( queue !== false && History.busy() ) {
1055
- // Wait + Push to Queue
1056
- //History.debug('History.replaceState: we must wait', arguments);
1057
- History.pushQueue({
1058
- scope: History,
1059
- callback: History.replaceState,
1060
- args: arguments,
1061
- queue: queue
1062
- });
1063
- return false;
1064
- }
1065
-
1066
- // Make Busy
1067
- History.busy(true);
1068
-
1069
- // Fetch the State Objects
1070
- var newState = History.createStateObject(data,title,url),
1071
- oldState = History.getState(false),
1072
- previousState = History.getStateByIndex(-2);
1073
-
1074
- // Discard Old State
1075
- History.discardState(oldState,newState,previousState);
1076
-
1077
- // Alias to PushState
1078
- History.pushState(newState.data,newState.title,newState.url,false);
1079
-
1080
- // End replaceState closure
1081
- return true;
1082
- };
1083
-
1084
- } // History.emulated.pushState
1085
-
1086
-
1087
-
1088
- // ====================================================================
1089
- // Initialise
1090
-
1091
- // Non-Native pushState Implementation
1092
- if ( History.emulated.pushState ) {
1093
- /**
1094
- * Ensure initial state is handled correctly
1095
- */
1096
- if ( History.getHash() && !History.emulated.hashChange ) {
1097
- History.Adapter.onDomLoad(function(){
1098
- History.Adapter.trigger(window,'hashchange');
1099
- });
1100
- }
1101
-
1102
- } // History.emulated.pushState
1103
-
1104
- }; // History.initHtml4
1105
-
1106
- // Try and Initialise History
1107
- if ( typeof History.init !== 'undefined' ) {
1108
- History.init();
1109
- }
1110
-
1111
- })(window);/**
1112
- * History.js Core
1113
- * @author Benjamin Arthur Lupton <contact@balupton.com>
1114
- * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
1115
- * @license New BSD License <http://creativecommons.org/licenses/BSD/>
1116
- */
1117
-
1118
- (function(window,undefined){
1119
- "use strict";
1120
-
1121
- // ========================================================================
1122
- // Initialise
1123
-
1124
- // Localise Globals
1125
- var
1126
- console = window.console||undefined, // Prevent a JSLint complain
1127
- document = window.document, // Make sure we are using the correct document
1128
- navigator = window.navigator, // Make sure we are using the correct navigator
1129
- sessionStorage = window.sessionStorage||false, // sessionStorage
1130
- setTimeout = window.setTimeout,
1131
- clearTimeout = window.clearTimeout,
1132
- setInterval = window.setInterval,
1133
- clearInterval = window.clearInterval,
1134
- JSON = window.JSON,
1135
- alert = window.alert,
1136
- History = window.History = window.History||{}, // Public History Object
1137
- history = window.history; // Old History Object
1138
-
1139
- // MooTools Compatibility
1140
- JSON.stringify = JSON.stringify||JSON.encode;
1141
- JSON.parse = JSON.parse||JSON.decode;
1142
-
1143
- // Check Existence
1144
- if ( typeof History.init !== 'undefined' ) {
1145
- throw new Error('History.js Core has already been loaded...');
1146
- }
1147
-
1148
- // Initialise History
1149
- History.init = function(){
1150
- // Check Load Status of Adapter
1151
- if ( typeof History.Adapter === 'undefined' ) {
1152
- return false;
1153
- }
1154
-
1155
- // Check Load Status of Core
1156
- if ( typeof History.initCore !== 'undefined' ) {
1157
- History.initCore();
1158
- }
1159
-
1160
- // Check Load Status of HTML4 Support
1161
- if ( typeof History.initHtml4 !== 'undefined' ) {
1162
- History.initHtml4();
1163
- }
1164
-
1165
- // Return true
1166
- return true;
1167
- };
1168
-
1169
-
1170
- // ========================================================================
1171
- // Initialise Core
1172
-
1173
- // Initialise Core
1174
- History.initCore = function(){
1175
- // Initialise
1176
- if ( typeof History.initCore.initialized !== 'undefined' ) {
1177
- // Already Loaded
1178
- return false;
1179
- }
1180
- else {
1181
- History.initCore.initialized = true;
1182
- }
1183
-
1184
-
1185
- // ====================================================================
1186
- // Options
1187
-
1188
- /**
1189
- * History.options
1190
- * Configurable options
1191
- */
1192
- History.options = History.options||{};
1193
-
1194
- /**
1195
- * History.options.hashChangeInterval
1196
- * How long should the interval be before hashchange checks
1197
- */
1198
- History.options.hashChangeInterval = History.options.hashChangeInterval || 100;
1199
-
1200
- /**
1201
- * History.options.safariPollInterval
1202
- * How long should the interval be before safari poll checks
1203
- */
1204
- History.options.safariPollInterval = History.options.safariPollInterval || 500;
1205
-
1206
- /**
1207
- * History.options.doubleCheckInterval
1208
- * How long should the interval be before we perform a double check
1209
- */
1210
- History.options.doubleCheckInterval = History.options.doubleCheckInterval || 500;
1211
-
1212
- /**
1213
- * History.options.storeInterval
1214
- * How long should we wait between store calls
1215
- */
1216
- History.options.storeInterval = History.options.storeInterval || 1000;
1217
-
1218
- /**
1219
- * History.options.busyDelay
1220
- * How long should we wait between busy events
1221
- */
1222
- History.options.busyDelay = History.options.busyDelay || 250;
1223
-
1224
- /**
1225
- * History.options.debug
1226
- * If true will enable debug messages to be logged
1227
- */
1228
- History.options.debug = History.options.debug || false;
1229
-
1230
- /**
1231
- * History.options.initialTitle
1232
- * What is the title of the initial state
1233
- */
1234
- History.options.initialTitle = History.options.initialTitle || document.title;
1235
-
1236
-
1237
- // ====================================================================
1238
- // Interval record
1239
-
1240
- /**
1241
- * History.intervalList
1242
- * List of intervals set, to be cleared when document is unloaded.
1243
- */
1244
- History.intervalList = [];
1245
-
1246
- /**
1247
- * History.clearAllIntervals
1248
- * Clears all setInterval instances.
1249
- */
1250
- History.clearAllIntervals = function(){
1251
- var i, il = History.intervalList;
1252
- if (typeof il !== "undefined" && il !== null) {
1253
- for (i = 0; i < il.length; i++) {
1254
- clearInterval(il[i]);
1255
- }
1256
- History.intervalList = null;
1257
- }
1258
- };
1259
-
1260
-
1261
- // ====================================================================
1262
- // Debug
1263
-
1264
- /**
1265
- * History.debug(message,...)
1266
- * Logs the passed arguments if debug enabled
1267
- */
1268
- History.debug = function(){
1269
- if ( (History.options.debug||false) ) {
1270
- History.log.apply(History,arguments);
1271
- }
1272
- };
1273
-
1274
- /**
1275
- * History.log(message,...)
1276
- * Logs the passed arguments
1277
- */
1278
- History.log = function(){
1279
- // Prepare
1280
- var
1281
- consoleExists = !(typeof console === 'undefined' || typeof console.log === 'undefined' || typeof console.log.apply === 'undefined'),
1282
- textarea = document.getElementById('log'),
1283
- message,
1284
- i,n,
1285
- args,arg
1286
- ;
1287
-
1288
- // Write to Console
1289
- if ( consoleExists ) {
1290
- args = Array.prototype.slice.call(arguments);
1291
- message = args.shift();
1292
- if ( typeof console.debug !== 'undefined' ) {
1293
- console.debug.apply(console,[message,args]);
1294
- }
1295
- else {
1296
- console.log.apply(console,[message,args]);
1297
- }
1298
- }
1299
- else {
1300
- message = ("\n"+arguments[0]+"\n");
1301
- }
1302
-
1303
- // Write to log
1304
- for ( i=1,n=arguments.length; i<n; ++i ) {
1305
- arg = arguments[i];
1306
- if ( typeof arg === 'object' && typeof JSON !== 'undefined' ) {
1307
- try {
1308
- arg = JSON.stringify(arg);
1309
- }
1310
- catch ( Exception ) {
1311
- // Recursive Object
1312
- }
1313
- }
1314
- message += "\n"+arg+"\n";
1315
- }
1316
-
1317
- // Textarea
1318
- if ( textarea ) {
1319
- textarea.value += message+"\n-----\n";
1320
- textarea.scrollTop = textarea.scrollHeight - textarea.clientHeight;
1321
- }
1322
- // No Textarea, No Console
1323
- else if ( !consoleExists ) {
1324
- alert(message);
1325
- }
1326
-
1327
- // Return true
1328
- return true;
1329
- };
1330
-
1331
-
1332
- // ====================================================================
1333
- // Emulated Status
1334
-
1335
- /**
1336
- * History.getInternetExplorerMajorVersion()
1337
- * Get's the major version of Internet Explorer
1338
- * @return {integer}
1339
- * @license Public Domain
1340
- * @author Benjamin Arthur Lupton <contact@balupton.com>
1341
- * @author James Padolsey <https://gist.github.com/527683>
1342
- */
1343
- History.getInternetExplorerMajorVersion = function(){
1344
- var result = History.getInternetExplorerMajorVersion.cached =
1345
- (typeof History.getInternetExplorerMajorVersion.cached !== 'undefined')
1346
- ? History.getInternetExplorerMajorVersion.cached
1347
- : (function(){
1348
- var v = 3,
1349
- div = document.createElement('div'),
1350
- all = div.getElementsByTagName('i');
1351
- while ( (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->') && all[0] ) {}
1352
- return (v > 4) ? v : false;
1353
- })()
1354
- ;
1355
- return result;
1356
- };
1357
-
1358
- /**
1359
- * History.isInternetExplorer()
1360
- * Are we using Internet Explorer?
1361
- * @return {boolean}
1362
- * @license Public Domain
1363
- * @author Benjamin Arthur Lupton <contact@balupton.com>
1364
- */
1365
- History.isInternetExplorer = function(){
1366
- var result =
1367
- History.isInternetExplorer.cached =
1368
- (typeof History.isInternetExplorer.cached !== 'undefined')
1369
- ? History.isInternetExplorer.cached
1370
- : Boolean(History.getInternetExplorerMajorVersion())
1371
- ;
1372
- return result;
1373
- };
1374
-
1375
- /**
1376
- * History.emulated
1377
- * Which features require emulating?
1378
- */
1379
- History.emulated = {
1380
- pushState: !Boolean(
1381
- window.history && window.history.pushState && window.history.replaceState
1382
- && !(
1383
- (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent) /* disable for versions of iOS before version 4.3 (8F190) */
1384
- || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent) /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
1385
- )
1386
- ),
1387
- hashChange: Boolean(
1388
- !(('onhashchange' in window) || ('onhashchange' in document))
1389
- ||
1390
- (History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8)
1391
- )
1392
- };
1393
-
1394
- /**
1395
- * History.enabled
1396
- * Is History enabled?
1397
- */
1398
- History.enabled = !History.emulated.pushState;
1399
-
1400
- /**
1401
- * History.bugs
1402
- * Which bugs are present
1403
- */
1404
- History.bugs = {
1405
- /**
1406
- * Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a `replaceState` call
1407
- * https://bugs.webkit.org/show_bug.cgi?id=56249
1408
- */
1409
- setHash: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
1410
-
1411
- /**
1412
- * Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions
1413
- * https://bugs.webkit.org/show_bug.cgi?id=42940
1414
- */
1415
- safariPoll: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
1416
-
1417
- /**
1418
- * MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function)
1419
- */
1420
- ieDoubleCheck: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8),
1421
-
1422
- /**
1423
- * MSIE 6 requires the entire hash to be encoded for the hashes to trigger the onHashChange event
1424
- */
1425
- hashEscape: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 7)
1426
- };
1427
-
1428
- /**
1429
- * History.isEmptyObject(obj)
1430
- * Checks to see if the Object is Empty
1431
- * @param {Object} obj
1432
- * @return {boolean}
1433
- */
1434
- History.isEmptyObject = function(obj) {
1435
- for ( var name in obj ) {
1436
- return false;
1437
- }
1438
- return true;
1439
- };
1440
-
1441
- /**
1442
- * History.cloneObject(obj)
1443
- * Clones a object and eliminate all references to the original contexts
1444
- * @param {Object} obj
1445
- * @return {Object}
1446
- */
1447
- History.cloneObject = function(obj) {
1448
- var hash,newObj;
1449
- if ( obj ) {
1450
- hash = JSON.stringify(obj);
1451
- newObj = JSON.parse(hash);
1452
- }
1453
- else {
1454
- newObj = {};
1455
- }
1456
- return newObj;
1457
- };
1458
-
1459
-
1460
- // ====================================================================
1461
- // URL Helpers
1462
-
1463
- /**
1464
- * History.getRootUrl()
1465
- * Turns "http://mysite.com/dir/page.html?asd" into "http://mysite.com"
1466
- * @return {String} rootUrl
1467
- */
1468
- History.getRootUrl = function(){
1469
- // Create
1470
- var rootUrl = document.location.protocol+'//'+(document.location.hostname||document.location.host);
1471
- if ( document.location.port||false ) {
1472
- rootUrl += ':'+document.location.port;
1473
- }
1474
- rootUrl += '/';
1475
-
1476
- // Return
1477
- return rootUrl;
1478
- };
1479
-
1480
- /**
1481
- * History.getBaseHref()
1482
- * Fetches the `href` attribute of the `<base href="...">` element if it exists
1483
- * @return {String} baseHref
1484
- */
1485
- History.getBaseHref = function(){
1486
- // Create
1487
- var
1488
- baseElements = document.getElementsByTagName('base'),
1489
- baseElement = null,
1490
- baseHref = '';
1491
-
1492
- // Test for Base Element
1493
- if ( baseElements.length === 1 ) {
1494
- // Prepare for Base Element
1495
- baseElement = baseElements[0];
1496
- baseHref = baseElement.href.replace(/[^\/]+$/,'');
1497
- }
1498
-
1499
- // Adjust trailing slash
1500
- baseHref = baseHref.replace(/\/+$/,'');
1501
- if ( baseHref ) baseHref += '/';
1502
-
1503
- // Return
1504
- return baseHref;
1505
- };
1506
-
1507
- /**
1508
- * History.getBaseUrl()
1509
- * Fetches the baseHref or basePageUrl or rootUrl (whichever one exists first)
1510
- * @return {String} baseUrl
1511
- */
1512
- History.getBaseUrl = function(){
1513
- // Create
1514
- var baseUrl = History.getBaseHref()||History.getBasePageUrl()||History.getRootUrl();
1515
-
1516
- // Return
1517
- return baseUrl;
1518
- };
1519
-
1520
- /**
1521
- * History.getPageUrl()
1522
- * Fetches the URL of the current page
1523
- * @return {String} pageUrl
1524
- */
1525
- History.getPageUrl = function(){
1526
- // Fetch
1527
- var
1528
- State = History.getState(false,false),
1529
- stateUrl = (State||{}).url||document.location.href,
1530
- pageUrl;
1531
-
1532
- // Create
1533
- pageUrl = stateUrl.replace(/\/+$/,'').replace(/[^\/]+$/,function(part,index,string){
1534
- return (/\!/).test(part) ? part : part+'/';
1535
- });
1536
-
1537
- // Return
1538
- return pageUrl;
1539
- };
1540
-
1541
- /**
1542
- * History.getBasePageUrl()
1543
- * Fetches the Url of the directory of the current page
1544
- * @return {String} basePageUrl
1545
- */
1546
- History.getBasePageUrl = function(){
1547
- // Create
1548
- var basePageUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
1549
- return (/[^\/]$/).test(part) ? '' : part;
1550
- }).replace(/\/+$/,'')+'/';
1551
-
1552
- // Return
1553
- return basePageUrl;
1554
- };
1555
-
1556
- /**
1557
- * History.getFullUrl(url)
1558
- * Ensures that we have an absolute URL and not a relative URL
1559
- * @param {string} url
1560
- * @param {Boolean} allowBaseHref
1561
- * @return {string} fullUrl
1562
- */
1563
- History.getFullUrl = function(url,allowBaseHref){
1564
- // Prepare
1565
- var fullUrl = url, firstChar = url.substring(0,1);
1566
- allowBaseHref = (typeof allowBaseHref === 'undefined') ? true : allowBaseHref;
1567
-
1568
- // Check
1569
- if ( /[a-z]+\:\/\//.test(url) ) {
1570
- // Full URL
1571
- }
1572
- else if ( firstChar === '/' ) {
1573
- // Root URL
1574
- fullUrl = History.getRootUrl()+url.replace(/^\/+/,'');
1575
- }
1576
- else if ( firstChar === '#' ) {
1577
- // Anchor URL
1578
- fullUrl = History.getPageUrl().replace(/#!*/,'')+url;
1579
- }
1580
- else if ( firstChar === '?' ) {
1581
- // Query URL
1582
- fullUrl = History.getPageUrl().replace(/[\?#]!*/,'')+url;
1583
- }
1584
- else {
1585
- // Relative URL
1586
- if ( allowBaseHref ) {
1587
- fullUrl = History.getBaseUrl()+url.replace(/^(\!\/)+/,'');
1588
- } else {
1589
- fullUrl = History.getBasePageUrl()+url.replace(/^(\!\/)+/,'');
1590
- }
1591
- // We have an if condition above as we do not want hashes
1592
- // which are relative to the baseHref in our URLs
1593
- // as if the baseHref changes, then all our bookmarks
1594
- // would now point to different locations
1595
- // whereas the basePageUrl will always stay the same
1596
- }
1597
-
1598
- // Return
1599
- return fullUrl.replace(/\#$/,'');
1600
- };
1601
-
1602
- /**
1603
- * History.getShortUrl(url)
1604
- * Ensures that we have a relative URL and not a absolute URL
1605
- * @param {string} url
1606
- * @return {string} url
1607
- */
1608
- History.getShortUrl = function(url){
1609
- // Prepare
1610
- var shortUrl = url, baseUrl = History.getBaseUrl(), rootUrl = History.getRootUrl();
1611
-
1612
- // Trim baseUrl
1613
- if ( History.emulated.pushState ) {
1614
- // We are in a if statement as when pushState is not emulated
1615
- // The actual url these short urls are relative to can change
1616
- // So within the same session, we the url may end up somewhere different
1617
- shortUrl = shortUrl.replace(baseUrl,'');
1618
- }
1619
-
1620
- // Trim rootUrl
1621
- shortUrl = shortUrl.replace(rootUrl,'/');
1622
-
1623
- // Ensure we can still detect it as a state
1624
- // if ( History.isTraditionalAnchor(shortUrl) ) {
1625
- // shortUrl = './'+shortUrl;
1626
- // // shortUrl = '/'+shortUrl;
1627
- // }
1628
-
1629
- shortUrl = '!/'+shortUrl;
1630
-
1631
- // Clean It
1632
- shortUrl = shortUrl.replace(/^(\!\/)+/g,'!/').replace(/\#$/,'');
1633
-
1634
- // Return
1635
- return shortUrl;
1636
- };
1637
-
1638
-
1639
- // ====================================================================
1640
- // State Storage
1641
-
1642
- /**
1643
- * History.store
1644
- * The store for all session specific data
1645
- */
1646
- History.store = {};
1647
-
1648
- /**
1649
- * History.idToState
1650
- * 1-1: State ID to State Object
1651
- */
1652
- History.idToState = History.idToState||{};
1653
-
1654
- /**
1655
- * History.stateToId
1656
- * 1-1: State String to State ID
1657
- */
1658
- History.stateToId = History.stateToId||{};
1659
-
1660
- /**
1661
- * History.urlToId
1662
- * 1-1: State URL to State ID
1663
- */
1664
- History.urlToId = History.urlToId||{};
1665
-
1666
- /**
1667
- * History.storedStates
1668
- * Store the states in an array
1669
- */
1670
- History.storedStates = History.storedStates||[];
1671
-
1672
- /**
1673
- * History.savedStates
1674
- * Saved the states in an array
1675
- */
1676
- History.savedStates = History.savedStates||[];
1677
-
1678
- /**
1679
- * History.noramlizeStore()
1680
- * Noramlize the store by adding necessary values
1681
- */
1682
- History.normalizeStore = function(){
1683
- History.store.idToState = History.store.idToState||{};
1684
- History.store.urlToId = History.store.urlToId||{};
1685
- History.store.stateToId = History.store.stateToId||{};
1686
- };
1687
-
1688
- /**
1689
- * History.getState()
1690
- * Get an object containing the data, title and url of the current state
1691
- * @param {Boolean} friendly
1692
- * @param {Boolean} create
1693
- * @return {Object} State
1694
- */
1695
- History.getState = function(friendly,create){
1696
- // Prepare
1697
- if ( typeof friendly === 'undefined' ) { friendly = true; }
1698
- if ( typeof create === 'undefined' ) { create = true; }
1699
-
1700
- // Fetch
1701
- var State = History.getLastSavedState();
1702
-
1703
- // Create
1704
- if ( !State && create ) {
1705
- State = History.createStateObject();
1706
- }
1707
-
1708
- // Adjust
1709
- if ( friendly ) {
1710
- State = History.cloneObject(State);
1711
- State.url = State.cleanUrl||State.url;
1712
- }
1713
-
1714
- // Return
1715
- return State;
1716
- };
1717
-
1718
- /**
1719
- * History.getIdByState(State)
1720
- * Gets a ID for a State
1721
- * @param {State} newState
1722
- * @return {String} id
1723
- */
1724
- History.getIdByState = function(newState){
1725
-
1726
- // Fetch ID
1727
- var id = History.extractId(newState.url),
1728
- str;
1729
-
1730
- if ( !id ) {
1731
- // Find ID via State String
1732
- str = History.getStateString(newState);
1733
- if ( typeof History.stateToId[str] !== 'undefined' ) {
1734
- id = History.stateToId[str];
1735
- }
1736
- else if ( typeof History.store.stateToId[str] !== 'undefined' ) {
1737
- id = History.store.stateToId[str];
1738
- }
1739
- else {
1740
- // Generate a new ID
1741
- while ( true ) {
1742
- id = (new Date()).getTime() + String(Math.random()).replace(/\D/g,'');
1743
- if ( typeof History.idToState[id] === 'undefined' && typeof History.store.idToState[id] === 'undefined' ) {
1744
- break;
1745
- }
1746
- }
1747
-
1748
- // Apply the new State to the ID
1749
- History.stateToId[str] = id;
1750
- History.idToState[id] = newState;
1751
- }
1752
- }
1753
-
1754
- // Return ID
1755
- return id;
1756
- };
1757
-
1758
- /**
1759
- * History.normalizeState(State)
1760
- * Expands a State Object
1761
- * @param {object} State
1762
- * @return {object}
1763
- */
1764
- History.normalizeState = function(oldState){
1765
- // Variables
1766
- var newState, dataNotEmpty;
1767
-
1768
- // Prepare
1769
- if ( !oldState || (typeof oldState !== 'object') ) {
1770
- oldState = {};
1771
- }
1772
-
1773
- // Check
1774
- if ( typeof oldState.normalized !== 'undefined' ) {
1775
- return oldState;
1776
- }
1777
-
1778
- // Adjust
1779
- if ( !oldState.data || (typeof oldState.data !== 'object') ) {
1780
- oldState.data = {};
1781
- }
1782
-
1783
- // ----------------------------------------------------------------
1784
-
1785
- // Create
1786
- newState = {};
1787
- newState.normalized = true;
1788
- newState.title = oldState.title||'';
1789
- newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href));
1790
- newState.hash = History.getShortUrl(newState.url);
1791
- newState.data = History.cloneObject(oldState.data);
1792
-
1793
- // Fetch ID
1794
- newState.id = History.getIdByState(newState);
1795
-
1796
- // ----------------------------------------------------------------
1797
-
1798
- // Clean the URL
1799
- newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,'');
1800
- newState.url = newState.cleanUrl;
1801
-
1802
- // Check to see if we have more than just a url
1803
- dataNotEmpty = !History.isEmptyObject(newState.data);
1804
-
1805
- // Apply
1806
- if ( newState.title || dataNotEmpty ) {
1807
- // Add ID to Hash
1808
- newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
1809
- if ( !/\?/.test(newState.hash) ) {
1810
- newState.hash += '?';
1811
- }
1812
- newState.hash += '&_suid='+newState.id;
1813
- }
1814
-
1815
- // Create the Hashed URL
1816
- newState.hashedUrl = History.getFullUrl(newState.hash);
1817
-
1818
- // ----------------------------------------------------------------
1819
-
1820
- // Update the URL if we have a duplicate
1821
- if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) {
1822
- newState.url = newState.hashedUrl;
1823
- }
1824
-
1825
- // ----------------------------------------------------------------
1826
-
1827
- // Return
1828
- return newState;
1829
- };
1830
-
1831
- /**
1832
- * History.createStateObject(data,title,url)
1833
- * Creates a object based on the data, title and url state params
1834
- * @param {object} data
1835
- * @param {string} title
1836
- * @param {string} url
1837
- * @return {object}
1838
- */
1839
- History.createStateObject = function(data,title,url){
1840
- // Hashify
1841
- var State = {
1842
- 'data': data,
1843
- 'title': title,
1844
- 'url': url
1845
- };
1846
-
1847
- // Expand the State
1848
- State = History.normalizeState(State);
1849
-
1850
- // Return object
1851
- return State;
1852
- };
1853
-
1854
- /**
1855
- * History.getStateById(id)
1856
- * Get a state by it's UID
1857
- * @param {String} id
1858
- */
1859
- History.getStateById = function(id){
1860
- // Prepare
1861
- id = String(id);
1862
-
1863
- // Retrieve
1864
- var State = History.idToState[id] || History.store.idToState[id] || undefined;
1865
-
1866
- // Return State
1867
- return State;
1868
- };
1869
-
1870
- /**
1871
- * Get a State's String
1872
- * @param {State} passedState
1873
- */
1874
- History.getStateString = function(passedState){
1875
- // Prepare
1876
- var State, cleanedState, str;
1877
-
1878
- // Fetch
1879
- State = History.normalizeState(passedState);
1880
-
1881
- // Clean
1882
- cleanedState = {
1883
- data: State.data,
1884
- title: passedState.title,
1885
- url: passedState.url
1886
- };
1887
-
1888
- // Fetch
1889
- str = JSON.stringify(cleanedState);
1890
-
1891
- // Return
1892
- return str;
1893
- };
1894
-
1895
- /**
1896
- * Get a State's ID
1897
- * @param {State} passedState
1898
- * @return {String} id
1899
- */
1900
- History.getStateId = function(passedState){
1901
- // Prepare
1902
- var State, id;
1903
-
1904
- // Fetch
1905
- State = History.normalizeState(passedState);
1906
-
1907
- // Fetch
1908
- id = State.id;
1909
-
1910
- // Return
1911
- return id;
1912
- };
1913
-
1914
- /**
1915
- * History.getHashByState(State)
1916
- * Creates a Hash for the State Object
1917
- * @param {State} passedState
1918
- * @return {String} hash
1919
- */
1920
- History.getHashByState = function(passedState){
1921
- // Prepare
1922
- var State, hash;
1923
-
1924
- // Fetch
1925
- State = History.normalizeState(passedState);
1926
-
1927
- // Hash
1928
- hash = State.hash;
1929
-
1930
- // Return
1931
- return hash;
1932
- };
1933
-
1934
- /**
1935
- * History.extractId(url_or_hash)
1936
- * Get a State ID by it's URL or Hash
1937
- * @param {string} url_or_hash
1938
- * @return {string} id
1939
- */
1940
- History.extractId = function ( url_or_hash ) {
1941
- // Prepare
1942
- var id,parts,url;
1943
-
1944
- // Extract
1945
- parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash);
1946
- url = parts ? (parts[1]||url_or_hash) : url_or_hash;
1947
- id = parts ? String(parts[2]||'') : '';
1948
-
1949
- // Return
1950
- return id||false;
1951
- };
1952
-
1953
- /**
1954
- * History.isTraditionalAnchor
1955
- * Checks to see if the url is a traditional anchor or not
1956
- * @param {String} url_or_hash
1957
- * @return {Boolean}
1958
- */
1959
- History.isTraditionalAnchor = function(url_or_hash){
1960
- // Check
1961
- var isTraditional = !(/[\/\?\.]/.test(url_or_hash));
1962
-
1963
- // Return
1964
- return isTraditional;
1965
- };
1966
-
1967
- /**
1968
- * History.extractState
1969
- * Get a State by it's URL or Hash
1970
- * @param {String} url_or_hash
1971
- * @return {State|null}
1972
- */
1973
- History.extractState = function(url_or_hash,create){
1974
- // Prepare
1975
- var State = null, id, url;
1976
- create = create||false;
1977
-
1978
- // Fetch SUID
1979
- id = History.extractId(url_or_hash);
1980
- if ( id ) {
1981
- State = History.getStateById(id);
1982
- }
1983
-
1984
- // Fetch SUID returned no State
1985
- if ( !State ) {
1986
- // Fetch URL
1987
- url = History.getFullUrl(url_or_hash);
1988
-
1989
- // Check URL
1990
- id = History.getIdByUrl(url)||false;
1991
- if ( id ) {
1992
- State = History.getStateById(id);
1993
- }
1994
-
1995
- // Create State
1996
- if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) {
1997
- State = History.createStateObject(null,null,url);
1998
- }
1999
- }
2000
-
2001
- // Return
2002
- return State;
2003
- };
2004
-
2005
- /**
2006
- * History.getIdByUrl()
2007
- * Get a State ID by a State URL
2008
- */
2009
- History.getIdByUrl = function(url){
2010
- // Fetch
2011
- var id = History.urlToId[url] || History.store.urlToId[url] || undefined;
2012
-
2013
- // Return
2014
- return id;
2015
- };
2016
-
2017
- /**
2018
- * History.getLastSavedState()
2019
- * Get an object containing the data, title and url of the current state
2020
- * @return {Object} State
2021
- */
2022
- History.getLastSavedState = function(){
2023
- return History.savedStates[History.savedStates.length-1]||undefined;
2024
- };
2025
-
2026
- /**
2027
- * History.getLastStoredState()
2028
- * Get an object containing the data, title and url of the current state
2029
- * @return {Object} State
2030
- */
2031
- History.getLastStoredState = function(){
2032
- return History.storedStates[History.storedStates.length-1]||undefined;
2033
- };
2034
-
2035
- /**
2036
- * History.hasUrlDuplicate
2037
- * Checks if a Url will have a url conflict
2038
- * @param {Object} newState
2039
- * @return {Boolean} hasDuplicate
2040
- */
2041
- History.hasUrlDuplicate = function(newState) {
2042
- // Prepare
2043
- var hasDuplicate = false,
2044
- oldState;
2045
-
2046
- // Fetch
2047
- oldState = History.extractState(newState.url);
2048
-
2049
- // Check
2050
- hasDuplicate = oldState && oldState.id !== newState.id;
2051
-
2052
- // Return
2053
- return hasDuplicate;
2054
- };
2055
-
2056
- /**
2057
- * History.storeState
2058
- * Store a State
2059
- * @param {Object} newState
2060
- * @return {Object} newState
2061
- */
2062
- History.storeState = function(newState){
2063
- // Store the State
2064
- History.urlToId[newState.url] = newState.id;
2065
-
2066
- // Push the State
2067
- History.storedStates.push(History.cloneObject(newState));
2068
-
2069
- // Return newState
2070
- return newState;
2071
- };
2072
-
2073
- /**
2074
- * History.isLastSavedState(newState)
2075
- * Tests to see if the state is the last state
2076
- * @param {Object} newState
2077
- * @return {boolean} isLast
2078
- */
2079
- History.isLastSavedState = function(newState){
2080
- // Prepare
2081
- var isLast = false,
2082
- newId, oldState, oldId;
2083
-
2084
- // Check
2085
- if ( History.savedStates.length ) {
2086
- newId = newState.id;
2087
- oldState = History.getLastSavedState();
2088
- oldId = oldState.id;
2089
-
2090
- // Check
2091
- isLast = (newId === oldId);
2092
- }
2093
-
2094
- // Return
2095
- return isLast;
2096
- };
2097
-
2098
- /**
2099
- * History.saveState
2100
- * Push a State
2101
- * @param {Object} newState
2102
- * @return {boolean} changed
2103
- */
2104
- History.saveState = function(newState){
2105
- // Check Hash
2106
- if ( History.isLastSavedState(newState) ) {
2107
- return false;
2108
- }
2109
-
2110
- // Push the State
2111
- History.savedStates.push(History.cloneObject(newState));
2112
-
2113
- // Return true
2114
- return true;
2115
- };
2116
-
2117
- /**
2118
- * History.getStateByIndex()
2119
- * Gets a state by the index
2120
- * @param {integer} index
2121
- * @return {Object}
2122
- */
2123
- History.getStateByIndex = function(index){
2124
- // Prepare
2125
- var State = null;
2126
-
2127
- // Handle
2128
- if ( typeof index === 'undefined' ) {
2129
- // Get the last inserted
2130
- State = History.savedStates[History.savedStates.length-1];
2131
- }
2132
- else if ( index < 0 ) {
2133
- // Get from the end
2134
- State = History.savedStates[History.savedStates.length+index];
2135
- }
2136
- else {
2137
- // Get from the beginning
2138
- State = History.savedStates[index];
2139
- }
2140
-
2141
- // Return State
2142
- return State;
2143
- };
2144
-
2145
-
2146
- // ====================================================================
2147
- // Hash Helpers
2148
-
2149
- /**
2150
- * History.getHash()
2151
- * Gets the current document hash
2152
- * @return {string}
2153
- */
2154
- History.getHash = function(){
2155
- var hash = History.unescapeHash(document.location.hash);
2156
- return hash;
2157
- };
2158
-
2159
- /**
2160
- * History.unescapeString()
2161
- * Unescape a string
2162
- * @param {String} str
2163
- * @return {string}
2164
- */
2165
- History.unescapeString = function(str){
2166
- // Prepare
2167
- var result = str,
2168
- tmp;
2169
-
2170
- // Unescape hash
2171
- while ( true ) {
2172
- tmp = window.decodeURI(result);
2173
- if ( tmp === result ) {
2174
- break;
2175
- }
2176
- result = tmp;
2177
- }
2178
-
2179
- // Return result
2180
- return result;
2181
- };
2182
-
2183
- /**
2184
- * History.unescapeHash()
2185
- * normalize and Unescape a Hash
2186
- * @param {String} hash
2187
- * @return {string}
2188
- */
2189
- History.unescapeHash = function(hash){
2190
- // Prepare
2191
- var result = History.normalizeHash(hash);
2192
-
2193
- // Unescape hash
2194
- result = History.unescapeString(result);
2195
-
2196
- // Return result
2197
- return result;
2198
- };
2199
-
2200
- /**
2201
- * History.normalizeHash()
2202
- * normalize a hash across browsers
2203
- * @return {string}
2204
- */
2205
- History.normalizeHash = function(hash){
2206
- // Prepare
2207
- var result = hash.replace(/[^#]*#/,'').replace(/#!*/, '');
2208
-
2209
- // Return result
2210
- return result;
2211
- };
2212
-
2213
- /**
2214
- * History.setHash(hash)
2215
- * Sets the document hash
2216
- * @param {string} hash
2217
- * @return {History}
2218
- */
2219
- History.setHash = function(hash,queue){
2220
- // Prepare
2221
- var adjustedHash, State, pageUrl;
2222
-
2223
- // Handle Queueing
2224
- if ( queue !== false && History.busy() ) {
2225
- // Wait + Push to Queue
2226
- //History.debug('History.setHash: we must wait', arguments);
2227
- History.pushQueue({
2228
- scope: History,
2229
- callback: History.setHash,
2230
- args: arguments,
2231
- queue: queue
2232
- });
2233
- return false;
2234
- }
2235
-
2236
- // Log
2237
- //History.debug('History.setHash: called',hash);
2238
-
2239
- // Prepare
2240
- adjustedHash = History.escapeHash(hash);
2241
-
2242
- // Make Busy + Continue
2243
- History.busy(true);
2244
-
2245
- // Check if hash is a state
2246
- State = History.extractState(hash,true);
2247
- if ( State && !History.emulated.pushState ) {
2248
- // Hash is a state so skip the setHash
2249
- //History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);
2250
-
2251
- // PushState
2252
- History.pushState(State.data,State.title,State.url,false);
2253
- }
2254
- else if ( document.location.hash !== adjustedHash ) {
2255
- // Hash is a proper hash, so apply it
2256
-
2257
- // Handle browser bugs
2258
- if ( History.bugs.setHash ) {
2259
- // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249
2260
-
2261
- // Fetch the base page
2262
- pageUrl = History.getPageUrl();
2263
-
2264
- // Safari hash apply
2265
- History.pushState(null,null,pageUrl+'#'+adjustedHash,false);
2266
- }
2267
- else {
2268
- // Normal hash apply
2269
- document.location.hash = adjustedHash;
2270
- }
2271
- }
2272
-
2273
- // Chain
2274
- return History;
2275
- };
2276
-
2277
- /**
2278
- * History.escape()
2279
- * normalize and Escape a Hash
2280
- * @return {string}
2281
- */
2282
- History.escapeHash = function(hash){
2283
- // Prepare
2284
- var result = History.normalizeHash(hash);
2285
-
2286
- // Escape hash
2287
- result = window.encodeURI(result);
2288
-
2289
- // IE6 Escape Bug
2290
- if ( !History.bugs.hashEscape ) {
2291
- // Restore common parts
2292
- result = result
2293
- .replace(/\%21/g,'!')
2294
- .replace(/\%26/g,'&')
2295
- .replace(/\%3D/g,'=')
2296
- .replace(/\%3F/g,'?');
2297
- }
2298
-
2299
- // Return result
2300
- return result;
2301
- };
2302
-
2303
- /**
2304
- * History.getHashByUrl(url)
2305
- * Extracts the Hash from a URL
2306
- * @param {string} url
2307
- * @return {string} url
2308
- */
2309
- History.getHashByUrl = function(url){
2310
- // Extract the hash
2311
- var hash = String(url)
2312
- .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
2313
- ;
2314
-
2315
- // Unescape hash
2316
- hash = History.unescapeHash(hash);
2317
-
2318
- // Return hash
2319
- return hash;
2320
- };
2321
-
2322
- /**
2323
- * History.setTitle(title)
2324
- * Applies the title to the document
2325
- * @param {State} newState
2326
- * @return {Boolean}
2327
- */
2328
- History.setTitle = function(newState){
2329
- // Prepare
2330
- var title = newState.title,
2331
- firstState;
2332
-
2333
- // Initial
2334
- if ( !title ) {
2335
- firstState = History.getStateByIndex(0);
2336
- if ( firstState && firstState.url === newState.url ) {
2337
- title = firstState.title||History.options.initialTitle;
2338
- }
2339
- }
2340
-
2341
- // Apply
2342
- try {
2343
- document.getElementsByTagName('title')[0].innerHTML = title.replace('<','&lt;').replace('>','&gt;').replace(' & ',' &amp; ');
2344
- }
2345
- catch ( Exception ) { }
2346
- document.title = title;
2347
-
2348
- // Chain
2349
- return History;
2350
- };
2351
-
2352
-
2353
- // ====================================================================
2354
- // Queueing
2355
-
2356
- /**
2357
- * History.queues
2358
- * The list of queues to use
2359
- * First In, First Out
2360
- */
2361
- History.queues = [];
2362
-
2363
- /**
2364
- * History.busy(value)
2365
- * @param {boolean} value [optional]
2366
- * @return {boolean} busy
2367
- */
2368
- History.busy = function(value){
2369
- // Apply
2370
- if ( typeof value !== 'undefined' ) {
2371
- //History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length);
2372
- History.busy.flag = value;
2373
- }
2374
- // Default
2375
- else if ( typeof History.busy.flag === 'undefined' ) {
2376
- History.busy.flag = false;
2377
- }
2378
-
2379
- // Queue
2380
- if ( !History.busy.flag ) {
2381
- // Execute the next item in the queue
2382
- clearTimeout(History.busy.timeout);
2383
- var fireNext = function(){
2384
- var i, queue, item;
2385
- if ( History.busy.flag ) return;
2386
- for ( i=History.queues.length-1; i >= 0; --i ) {
2387
- queue = History.queues[i];
2388
- if ( queue.length === 0 ) continue;
2389
- item = queue.shift();
2390
- History.fireQueueItem(item);
2391
- History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
2392
- }
2393
- };
2394
- History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
2395
- }
2396
-
2397
- // Return
2398
- return History.busy.flag;
2399
- };
2400
-
2401
- /**
2402
- * History.busy.flag
2403
- */
2404
- History.busy.flag = false;
2405
-
2406
- /**
2407
- * History.fireQueueItem(item)
2408
- * Fire a Queue Item
2409
- * @param {Object} item
2410
- * @return {Mixed} result
2411
- */
2412
- History.fireQueueItem = function(item){
2413
- return item.callback.apply(item.scope||History,item.args||[]);
2414
- };
2415
-
2416
- /**
2417
- * History.pushQueue(callback,args)
2418
- * Add an item to the queue
2419
- * @param {Object} item [scope,callback,args,queue]
2420
- */
2421
- History.pushQueue = function(item){
2422
- // Prepare the queue
2423
- History.queues[item.queue||0] = History.queues[item.queue||0]||[];
2424
-
2425
- // Add to the queue
2426
- History.queues[item.queue||0].push(item);
2427
-
2428
- // Chain
2429
- return History;
2430
- };
2431
-
2432
- /**
2433
- * History.queue (item,queue), (func,queue), (func), (item)
2434
- * Either firs the item now if not busy, or adds it to the queue
2435
- */
2436
- History.queue = function(item,queue){
2437
- // Prepare
2438
- if ( typeof item === 'function' ) {
2439
- item = {
2440
- callback: item
2441
- };
2442
- }
2443
- if ( typeof queue !== 'undefined' ) {
2444
- item.queue = queue;
2445
- }
2446
-
2447
- // Handle
2448
- if ( History.busy() ) {
2449
- History.pushQueue(item);
2450
- } else {
2451
- History.fireQueueItem(item);
2452
- }
2453
-
2454
- // Chain
2455
- return History;
2456
- };
2457
-
2458
- /**
2459
- * History.clearQueue()
2460
- * Clears the Queue
2461
- */
2462
- History.clearQueue = function(){
2463
- History.busy.flag = false;
2464
- History.queues = [];
2465
- return History;
2466
- };
2467
-
2468
-
2469
- // ====================================================================
2470
- // IE Bug Fix
2471
-
2472
- /**
2473
- * History.stateChanged
2474
- * States whether or not the state has changed since the last double check was initialised
2475
- */
2476
- History.stateChanged = false;
2477
-
2478
- /**
2479
- * History.doubleChecker
2480
- * Contains the timeout used for the double checks
2481
- */
2482
- History.doubleChecker = false;
2483
-
2484
- /**
2485
- * History.doubleCheckComplete()
2486
- * Complete a double check
2487
- * @return {History}
2488
- */
2489
- History.doubleCheckComplete = function(){
2490
- // Update
2491
- History.stateChanged = true;
2492
-
2493
- // Clear
2494
- History.doubleCheckClear();
2495
-
2496
- // Chain
2497
- return History;
2498
- };
2499
-
2500
- /**
2501
- * History.doubleCheckClear()
2502
- * Clear a double check
2503
- * @return {History}
2504
- */
2505
- History.doubleCheckClear = function(){
2506
- // Clear
2507
- if ( History.doubleChecker ) {
2508
- clearTimeout(History.doubleChecker);
2509
- History.doubleChecker = false;
2510
- }
2511
-
2512
- // Chain
2513
- return History;
2514
- };
2515
-
2516
- /**
2517
- * History.doubleCheck()
2518
- * Create a double check
2519
- * @return {History}
2520
- */
2521
- History.doubleCheck = function(tryAgain){
2522
- // Reset
2523
- History.stateChanged = false;
2524
- History.doubleCheckClear();
2525
-
2526
- // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does)
2527
- // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940
2528
- if ( History.bugs.ieDoubleCheck ) {
2529
- // Apply Check
2530
- History.doubleChecker = setTimeout(
2531
- function(){
2532
- History.doubleCheckClear();
2533
- if ( !History.stateChanged ) {
2534
- //History.debug('History.doubleCheck: State has not yet changed, trying again', arguments);
2535
- // Re-Attempt
2536
- tryAgain();
2537
- }
2538
- return true;
2539
- },
2540
- History.options.doubleCheckInterval
2541
- );
2542
- }
2543
-
2544
- // Chain
2545
- return History;
2546
- };
2547
-
2548
-
2549
- // ====================================================================
2550
- // Safari Bug Fix
2551
-
2552
- /**
2553
- * History.safariStatePoll()
2554
- * Poll the current state
2555
- * @return {History}
2556
- */
2557
- History.safariStatePoll = function(){
2558
- // Poll the URL
2559
-
2560
- // Get the Last State which has the new URL
2561
- var
2562
- urlState = History.extractState(document.location.href),
2563
- newState;
2564
-
2565
- // Check for a difference
2566
- if ( !History.isLastSavedState(urlState) ) {
2567
- newState = urlState;
2568
- }
2569
- else {
2570
- return;
2571
- }
2572
-
2573
- // Check if we have a state with that url
2574
- // If not create it
2575
- if ( !newState ) {
2576
- //History.debug('History.safariStatePoll: new');
2577
- newState = History.createStateObject();
2578
- }
2579
-
2580
- // Apply the New State
2581
- //History.debug('History.safariStatePoll: trigger');
2582
- History.Adapter.trigger(window,'popstate');
2583
-
2584
- // Chain
2585
- return History;
2586
- };
2587
-
2588
-
2589
- // ====================================================================
2590
- // State Aliases
2591
-
2592
- /**
2593
- * History.back(queue)
2594
- * Send the browser history back one item
2595
- * @param {Integer} queue [optional]
2596
- */
2597
- History.back = function(queue){
2598
- //History.debug('History.back: called', arguments);
2599
-
2600
- // Handle Queueing
2601
- if ( queue !== false && History.busy() ) {
2602
- // Wait + Push to Queue
2603
- //History.debug('History.back: we must wait', arguments);
2604
- History.pushQueue({
2605
- scope: History,
2606
- callback: History.back,
2607
- args: arguments,
2608
- queue: queue
2609
- });
2610
- return false;
2611
- }
2612
-
2613
- // Make Busy + Continue
2614
- History.busy(true);
2615
-
2616
- // Fix certain browser bugs that prevent the state from changing
2617
- History.doubleCheck(function(){
2618
- History.back(false);
2619
- });
2620
-
2621
- // Go back
2622
- history.go(-1);
2623
-
2624
- // End back closure
2625
- return true;
2626
- };
2627
-
2628
- /**
2629
- * History.forward(queue)
2630
- * Send the browser history forward one item
2631
- * @param {Integer} queue [optional]
2632
- */
2633
- History.forward = function(queue){
2634
- //History.debug('History.forward: called', arguments);
2635
-
2636
- // Handle Queueing
2637
- if ( queue !== false && History.busy() ) {
2638
- // Wait + Push to Queue
2639
- //History.debug('History.forward: we must wait', arguments);
2640
- History.pushQueue({
2641
- scope: History,
2642
- callback: History.forward,
2643
- args: arguments,
2644
- queue: queue
2645
- });
2646
- return false;
2647
- }
2648
-
2649
- // Make Busy + Continue
2650
- History.busy(true);
2651
-
2652
- // Fix certain browser bugs that prevent the state from changing
2653
- History.doubleCheck(function(){
2654
- History.forward(false);
2655
- });
2656
-
2657
- // Go forward
2658
- history.go(1);
2659
-
2660
- // End forward closure
2661
- return true;
2662
- };
2663
-
2664
- /**
2665
- * History.go(index,queue)
2666
- * Send the browser history back or forward index times
2667
- * @param {Integer} queue [optional]
2668
- */
2669
- History.go = function(index,queue){
2670
- //History.debug('History.go: called', arguments);
2671
-
2672
- // Prepare
2673
- var i;
2674
-
2675
- // Handle
2676
- if ( index > 0 ) {
2677
- // Forward
2678
- for ( i=1; i<=index; ++i ) {
2679
- History.forward(queue);
2680
- }
2681
- }
2682
- else if ( index < 0 ) {
2683
- // Backward
2684
- for ( i=-1; i>=index; --i ) {
2685
- History.back(queue);
2686
- }
2687
- }
2688
- else {
2689
- throw new Error('History.go: History.go requires a positive or negative integer passed.');
2690
- }
2691
-
2692
- // Chain
2693
- return History;
2694
- };
2695
-
2696
-
2697
- // ====================================================================
2698
- // HTML5 State Support
2699
-
2700
- // Non-Native pushState Implementation
2701
- if ( History.emulated.pushState ) {
2702
- /*
2703
- * Provide Skeleton for HTML4 Browsers
2704
- */
2705
-
2706
- // Prepare
2707
- var emptyFunction = function(){};
2708
- History.pushState = History.pushState||emptyFunction;
2709
- History.replaceState = History.replaceState||emptyFunction;
2710
- } // History.emulated.pushState
2711
-
2712
- // Native pushState Implementation
2713
- else {
2714
- /*
2715
- * Use native HTML5 History API Implementation
2716
- */
2717
-
2718
- /**
2719
- * History.onPopState(event,extra)
2720
- * Refresh the Current State
2721
- */
2722
- History.onPopState = function(event,extra){
2723
- // Prepare
2724
- var stateId = false, newState = false, currentHash, currentState;
2725
-
2726
- // Reset the double check
2727
- History.doubleCheckComplete();
2728
-
2729
- // Check for a Hash, and handle apporiatly
2730
- currentHash = History.getHash();
2731
- if ( currentHash ) {
2732
- // Expand Hash
2733
- currentState = History.extractState(currentHash||document.location.href,true);
2734
- if ( currentState ) {
2735
- // We were able to parse it, it must be a State!
2736
- // Let's forward to replaceState
2737
- //History.debug('History.onPopState: state anchor', currentHash, currentState);
2738
- History.replaceState(currentState.data, currentState.title, currentState.url, false);
2739
- }
2740
- else {
2741
- // Traditional Anchor
2742
- //History.debug('History.onPopState: traditional anchor', currentHash);
2743
- History.Adapter.trigger(window,'anchorchange');
2744
- History.busy(false);
2745
- }
2746
-
2747
- // We don't care for hashes
2748
- History.expectedStateId = false;
2749
- return false;
2750
- }
2751
-
2752
- // Ensure
2753
- stateId = History.Adapter.extractEventData('state',event,extra) || false;
2754
-
2755
- // Fetch State
2756
- if ( stateId ) {
2757
- // Vanilla: Back/forward button was used
2758
- newState = History.getStateById(stateId);
2759
- }
2760
- else if ( History.expectedStateId ) {
2761
- // Vanilla: A new state was pushed, and popstate was called manually
2762
- newState = History.getStateById(History.expectedStateId);
2763
- }
2764
- else {
2765
- // Initial State
2766
- newState = History.extractState(document.location.href);
2767
- }
2768
-
2769
- // The State did not exist in our store
2770
- if ( !newState ) {
2771
- // Regenerate the State
2772
- newState = History.createStateObject(null,null,document.location.href);
2773
- }
2774
-
2775
- // Clean
2776
- History.expectedStateId = false;
2777
-
2778
- // Check if we are the same state
2779
- if ( History.isLastSavedState(newState) ) {
2780
- // There has been no change (just the page's hash has finally propagated)
2781
- //History.debug('History.onPopState: no change', newState, History.savedStates);
2782
- History.busy(false);
2783
- return false;
2784
- }
2785
-
2786
- // Store the State
2787
- History.storeState(newState);
2788
- History.saveState(newState);
2789
-
2790
- // Force update of the title
2791
- History.setTitle(newState);
2792
-
2793
- // Fire Our Event
2794
- History.Adapter.trigger(window,'statechange');
2795
- History.busy(false);
2796
-
2797
- // Return true
2798
- return true;
2799
- };
2800
- History.Adapter.bind(window,'popstate',History.onPopState);
2801
-
2802
- /**
2803
- * History.pushState(data,title,url)
2804
- * Add a new State to the history object, become it, and trigger onpopstate
2805
- * We have to trigger for HTML4 compatibility
2806
- * @param {object} data
2807
- * @param {string} title
2808
- * @param {string} url
2809
- * @return {true}
2810
- */
2811
- History.pushState = function(data,title,url,queue){
2812
- //History.debug('History.pushState: called', arguments);
2813
-
2814
- // Check the State
2815
- if ( History.getHashByUrl(url) && History.emulated.pushState ) {
2816
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
2817
- }
2818
-
2819
- // Handle Queueing
2820
- if ( queue !== false && History.busy() ) {
2821
- // Wait + Push to Queue
2822
- //History.debug('History.pushState: we must wait', arguments);
2823
- History.pushQueue({
2824
- scope: History,
2825
- callback: History.pushState,
2826
- args: arguments,
2827
- queue: queue
2828
- });
2829
- return false;
2830
- }
2831
-
2832
- // Make Busy + Continue
2833
- History.busy(true);
2834
-
2835
- // Create the newState
2836
- var newState = History.createStateObject(data,title,url);
2837
-
2838
- // Check it
2839
- if ( History.isLastSavedState(newState) ) {
2840
- // Won't be a change
2841
- History.busy(false);
2842
- }
2843
- else {
2844
- // Store the newState
2845
- History.storeState(newState);
2846
- History.expectedStateId = newState.id;
2847
-
2848
- // Push the newState
2849
- history.pushState(newState.id,newState.title,newState.url);
2850
-
2851
- // Fire HTML5 Event
2852
- History.Adapter.trigger(window,'popstate');
2853
- }
2854
-
2855
- // End pushState closure
2856
- return true;
2857
- };
2858
-
2859
- /**
2860
- * History.replaceState(data,title,url)
2861
- * Replace the State and trigger onpopstate
2862
- * We have to trigger for HTML4 compatibility
2863
- * @param {object} data
2864
- * @param {string} title
2865
- * @param {string} url
2866
- * @return {true}
2867
- */
2868
- History.replaceState = function(data,title,url,queue){
2869
- //History.debug('History.replaceState: called', arguments);
2870
-
2871
- // Check the State
2872
- if ( History.getHashByUrl(url) && History.emulated.pushState ) {
2873
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
2874
- }
2875
-
2876
- // Handle Queueing
2877
- if ( queue !== false && History.busy() ) {
2878
- // Wait + Push to Queue
2879
- //History.debug('History.replaceState: we must wait', arguments);
2880
- History.pushQueue({
2881
- scope: History,
2882
- callback: History.replaceState,
2883
- args: arguments,
2884
- queue: queue
2885
- });
2886
- return false;
2887
- }
2888
-
2889
- // Make Busy + Continue
2890
- History.busy(true);
2891
-
2892
- // Create the newState
2893
- var newState = History.createStateObject(data,title,url);
2894
-
2895
- // Check it
2896
- if ( History.isLastSavedState(newState) ) {
2897
- // Won't be a change
2898
- History.busy(false);
2899
- }
2900
- else {
2901
- // Store the newState
2902
- History.storeState(newState);
2903
- History.expectedStateId = newState.id;
2904
-
2905
- // Push the newState
2906
- history.replaceState(newState.id,newState.title,newState.url);
2907
-
2908
- // Fire HTML5 Event
2909
- History.Adapter.trigger(window,'popstate');
2910
- }
2911
-
2912
- // End replaceState closure
2913
- return true;
2914
- };
2915
-
2916
- } // !History.emulated.pushState
2917
-
2918
-
2919
- // ====================================================================
2920
- // Initialise
2921
-
2922
- /**
2923
- * Load the Store
2924
- */
2925
- if ( sessionStorage ) {
2926
- // Fetch
2927
- try {
2928
- History.store = JSON.parse(sessionStorage.getItem('History.store'))||{};
2929
- }
2930
- catch ( err ) {
2931
- History.store = {};
2932
- }
2933
-
2934
- // Normalize
2935
- History.normalizeStore();
2936
- }
2937
- else {
2938
- // Default Load
2939
- History.store = {};
2940
- History.normalizeStore();
2941
- }
2942
-
2943
- /**
2944
- * Clear Intervals on exit to prevent memory leaks
2945
- */
2946
- History.Adapter.bind(window,"beforeunload",History.clearAllIntervals);
2947
- History.Adapter.bind(window,"unload",History.clearAllIntervals);
2948
-
2949
- /**
2950
- * Create the initial State
2951
- */
2952
- History.saveState(History.storeState(History.extractState(document.location.href,true)));
2953
-
2954
- /**
2955
- * Bind for Saving Store
2956
- */
2957
- if ( sessionStorage ) {
2958
- // When the page is closed
2959
- History.onUnload = function(){
2960
- // Prepare
2961
- var currentStore, item;
2962
-
2963
- // Fetch
2964
- try {
2965
- currentStore = JSON.parse(sessionStorage.getItem('History.store'))||{};
2966
- }
2967
- catch ( err ) {
2968
- currentStore = {};
2969
- }
2970
-
2971
- // Ensure
2972
- currentStore.idToState = currentStore.idToState || {};
2973
- currentStore.urlToId = currentStore.urlToId || {};
2974
- currentStore.stateToId = currentStore.stateToId || {};
2975
-
2976
- // Sync
2977
- for ( item in History.idToState ) {
2978
- if ( !History.idToState.hasOwnProperty(item) ) {
2979
- continue;
2980
- }
2981
- currentStore.idToState[item] = History.idToState[item];
2982
- }
2983
- for ( item in History.urlToId ) {
2984
- if ( !History.urlToId.hasOwnProperty(item) ) {
2985
- continue;
2986
- }
2987
- currentStore.urlToId[item] = History.urlToId[item];
2988
- }
2989
- for ( item in History.stateToId ) {
2990
- if ( !History.stateToId.hasOwnProperty(item) ) {
2991
- continue;
2992
- }
2993
- currentStore.stateToId[item] = History.stateToId[item];
2994
- }
2995
-
2996
- // Update
2997
- History.store = currentStore;
2998
- History.normalizeStore();
2999
-
3000
- // Store
3001
- sessionStorage.setItem('History.store',JSON.stringify(currentStore));
3002
- };
3003
-
3004
- // For Internet Explorer
3005
- History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval));
3006
-
3007
- // For Other Browsers
3008
- History.Adapter.bind(window,'beforeunload',History.onUnload);
3009
- History.Adapter.bind(window,'unload',History.onUnload);
3010
-
3011
- // Both are enabled for consistency
3012
- }
3013
-
3014
- // Non-Native pushState Implementation
3015
- if ( !History.emulated.pushState ) {
3016
- // Be aware, the following is only for native pushState implementations
3017
- // If you are wanting to include something for all browsers
3018
- // Then include it above this if block
3019
-
3020
- /**
3021
- * Setup Safari Fix
3022
- */
3023
- if ( History.bugs.safariPoll ) {
3024
- History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval));
3025
- }
3026
-
3027
- /**
3028
- * Ensure Cross Browser Compatibility
3029
- */
3030
- if ( navigator.vendor === 'Apple Computer, Inc.' || (navigator.appCodeName||'') === 'Mozilla' ) {
3031
- /**
3032
- * Fix Safari HashChange Issue
3033
- */
3034
-
3035
- // Setup Alias
3036
- History.Adapter.bind(window,'hashchange',function(){
3037
- History.Adapter.trigger(window,'popstate');
3038
- });
3039
-
3040
- // Initialise Alias
3041
- if ( History.getHash() ) {
3042
- History.Adapter.onDomLoad(function(){
3043
- History.Adapter.trigger(window,'hashchange');
3044
- });
3045
- }
3046
- }
3047
-
3048
- } // !History.emulated.pushState
3049
-
3050
-
3051
- }; // History.initCore
3052
-
3053
- // Try and Initialise History
3054
- History.init();
3055
-
3056
- })(window);/*
3057
- json2.js
3058
- 2012-10-08
3059
-
3060
- Public Domain.
3061
-
3062
- NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
3063
-
3064
- See http://www.JSON.org/js.html
3065
-
3066
-
3067
- This code should be minified before deployment.
3068
- See http://javascript.crockford.com/jsmin.html
3069
-
3070
- USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
3071
- NOT CONTROL.
3072
-
3073
-
3074
- This file creates a global JSON object containing two methods: stringify
3075
- and parse.
3076
-
3077
- JSON.stringify(value, replacer, space)
3078
- value any JavaScript value, usually an object or array.
3079
-
3080
- replacer an optional parameter that determines how object
3081
- values are stringified for objects. It can be a
3082
- function or an array of strings.
3083
-
3084
- space an optional parameter that specifies the indentation
3085
- of nested structures. If it is omitted, the text will
3086
- be packed without extra whitespace. If it is a number,
3087
- it will specify the number of spaces to indent at each
3088
- level. If it is a string (such as '\t' or '&nbsp;'),
3089
- it contains the characters used to indent at each level.
3090
-
3091
- This method produces a JSON text from a JavaScript value.
3092
-
3093
- When an object value is found, if the object contains a toJSON
3094
- method, its toJSON method will be called and the result will be
3095
- stringified. A toJSON method does not serialize: it returns the
3096
- value represented by the name/value pair that should be serialized,
3097
- or undefined if nothing should be serialized. The toJSON method
3098
- will be passed the key associated with the value, and this will be
3099
- bound to the value
3100
-
3101
- For example, this would serialize Dates as ISO strings.
3102
-
3103
- Date.prototype.toJSON = function (key) {
3104
- function f(n) {
3105
- // Format integers to have at least two digits.
3106
- return n < 10 ? '0' + n : n;
3107
- }
3108
-
3109
- return this.getUTCFullYear() + '-' +
3110
- f(this.getUTCMonth() + 1) + '-' +
3111
- f(this.getUTCDate()) + 'T' +
3112
- f(this.getUTCHours()) + ':' +
3113
- f(this.getUTCMinutes()) + ':' +
3114
- f(this.getUTCSeconds()) + 'Z';
3115
- };
3116
-
3117
- You can provide an optional replacer method. It will be passed the
3118
- key and value of each member, with this bound to the containing
3119
- object. The value that is returned from your method will be
3120
- serialized. If your method returns undefined, then the member will
3121
- be excluded from the serialization.
3122
-
3123
- If the replacer parameter is an array of strings, then it will be
3124
- used to select the members to be serialized. It filters the results
3125
- such that only members with keys listed in the replacer array are
3126
- stringified.
3127
-
3128
- Values that do not have JSON representations, such as undefined or
3129
- functions, will not be serialized. Such values in objects will be
3130
- dropped; in arrays they will be replaced with null. You can use
3131
- a replacer function to replace those with JSON values.
3132
- JSON.stringify(undefined) returns undefined.
3133
-
3134
- The optional space parameter produces a stringification of the
3135
- value that is filled with line breaks and indentation to make it
3136
- easier to read.
3137
-
3138
- If the space parameter is a non-empty string, then that string will
3139
- be used for indentation. If the space parameter is a number, then
3140
- the indentation will be that many spaces.
3141
-
3142
- Example:
3143
-
3144
- text = JSON.stringify(['e', {pluribus: 'unum'}]);
3145
- // text is '["e",{"pluribus":"unum"}]'
3146
-
3147
-
3148
- text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
3149
- // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
3150
-
3151
- text = JSON.stringify([new Date()], function (key, value) {
3152
- return this[key] instanceof Date ?
3153
- 'Date(' + this[key] + ')' : value;
3154
- });
3155
- // text is '["Date(---current time---)"]'
3156
-
3157
-
3158
- JSON.parse(text, reviver)
3159
- This method parses a JSON text to produce an object or array.
3160
- It can throw a SyntaxError exception.
3161
-
3162
- The optional reviver parameter is a function that can filter and
3163
- transform the results. It receives each of the keys and values,
3164
- and its return value is used instead of the original value.
3165
- If it returns what it received, then the structure is not modified.
3166
- If it returns undefined then the member is deleted.
3167
-
3168
- Example:
3169
-
3170
- // Parse the text. Values that look like ISO date strings will
3171
- // be converted to Date objects.
3172
-
3173
- myData = JSON.parse(text, function (key, value) {
3174
- var a;
3175
- if (typeof value === 'string') {
3176
- a =
3177
- /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
3178
- if (a) {
3179
- return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
3180
- +a[5], +a[6]));
3181
- }
3182
- }
3183
- return value;
3184
- });
3185
-
3186
- myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
3187
- var d;
3188
- if (typeof value === 'string' &&
3189
- value.slice(0, 5) === 'Date(' &&
3190
- value.slice(-1) === ')') {
3191
- d = new Date(value.slice(5, -1));
3192
- if (d) {
3193
- return d;
3194
- }
3195
- }
3196
- return value;
3197
- });
3198
-
3199
-
3200
- This is a reference implementation. You are free to copy, modify, or
3201
- redistribute.
3202
- */
3203
-
3204
- /*jslint evil: true, regexp: true */
3205
-
3206
- /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
3207
- call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
3208
- getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
3209
- lastIndex, length, parse, prototype, push, replace, slice, stringify,
3210
- test, toJSON, toString, valueOf
3211
- */
3212
-
3213
-
3214
- // Create a JSON object only if one does not already exist. We create the
3215
- // methods in a closure to avoid creating global variables.
3216
-
3217
- if (typeof JSON !== 'object') {
3218
- JSON = {};
3219
- }
3220
-
3221
- (function () {
3222
- 'use strict';
3223
-
3224
- function f(n) {
3225
- // Format integers to have at least two digits.
3226
- return n < 10 ? '0' + n : n;
3227
- }
3228
-
3229
- if (typeof Date.prototype.toJSON !== 'function') {
3230
-
3231
- Date.prototype.toJSON = function (key) {
3232
-
3233
- return isFinite(this.valueOf())
3234
- ? this.getUTCFullYear() + '-' +
3235
- f(this.getUTCMonth() + 1) + '-' +
3236
- f(this.getUTCDate()) + 'T' +
3237
- f(this.getUTCHours()) + ':' +
3238
- f(this.getUTCMinutes()) + ':' +
3239
- f(this.getUTCSeconds()) + 'Z'
3240
- : null;
3241
- };
3242
-
3243
- String.prototype.toJSON =
3244
- Number.prototype.toJSON =
3245
- Boolean.prototype.toJSON = function (key) {
3246
- return this.valueOf();
3247
- };
3248
- }
3249
-
3250
- var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
3251
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
3252
- gap,
3253
- indent,
3254
- meta = { // table of character substitutions
3255
- '\b': '\\b',
3256
- '\t': '\\t',
3257
- '\n': '\\n',
3258
- '\f': '\\f',
3259
- '\r': '\\r',
3260
- '"' : '\\"',
3261
- '\\': '\\\\'
3262
- },
3263
- rep;
3264
-
3265
-
3266
- function quote(string) {
3267
-
3268
- // If the string contains no control characters, no quote characters, and no
3269
- // backslash characters, then we can safely slap some quotes around it.
3270
- // Otherwise we must also replace the offending characters with safe escape
3271
- // sequences.
3272
-
3273
- escapable.lastIndex = 0;
3274
- return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
3275
- var c = meta[a];
3276
- return typeof c === 'string'
3277
- ? c
3278
- : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
3279
- }) + '"' : '"' + string + '"';
3280
- }
3281
-
3282
-
3283
- function str(key, holder) {
3284
-
3285
- // Produce a string from holder[key].
3286
-
3287
- var i, // The loop counter.
3288
- k, // The member key.
3289
- v, // The member value.
3290
- length,
3291
- mind = gap,
3292
- partial,
3293
- value = holder[key];
3294
-
3295
- // If the value has a toJSON method, call it to obtain a replacement value.
3296
-
3297
- if (value && typeof value === 'object' &&
3298
- typeof value.toJSON === 'function') {
3299
- value = value.toJSON(key);
3300
- }
3301
-
3302
- // If we were called with a replacer function, then call the replacer to
3303
- // obtain a replacement value.
3304
-
3305
- if (typeof rep === 'function') {
3306
- value = rep.call(holder, key, value);
3307
- }
3308
-
3309
- // What happens next depends on the value's type.
3310
-
3311
- switch (typeof value) {
3312
- case 'string':
3313
- return quote(value);
3314
-
3315
- case 'number':
3316
-
3317
- // JSON numbers must be finite. Encode non-finite numbers as null.
3318
-
3319
- return isFinite(value) ? String(value) : 'null';
3320
-
3321
- case 'boolean':
3322
- case 'null':
3323
-
3324
- // If the value is a boolean or null, convert it to a string. Note:
3325
- // typeof null does not produce 'null'. The case is included here in
3326
- // the remote chance that this gets fixed someday.
3327
-
3328
- return String(value);
3329
-
3330
- // If the type is 'object', we might be dealing with an object or an array or
3331
- // null.
3332
-
3333
- case 'object':
3334
-
3335
- // Due to a specification blunder in ECMAScript, typeof null is 'object',
3336
- // so watch out for that case.
3337
-
3338
- if (!value) {
3339
- return 'null';
3340
- }
3341
-
3342
- // Make an array to hold the partial results of stringifying this object value.
3343
-
3344
- gap += indent;
3345
- partial = [];
3346
-
3347
- // Is the value an array?
3348
-
3349
- if (Object.prototype.toString.apply(value) === '[object Array]') {
3350
-
3351
- // The value is an array. Stringify every element. Use null as a placeholder
3352
- // for non-JSON values.
3353
-
3354
- length = value.length;
3355
- for (i = 0; i < length; i += 1) {
3356
- partial[i] = str(i, value) || 'null';
3357
- }
3358
-
3359
- // Join all of the elements together, separated with commas, and wrap them in
3360
- // brackets.
3361
-
3362
- v = partial.length === 0
3363
- ? '[]'
3364
- : gap
3365
- ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
3366
- : '[' + partial.join(',') + ']';
3367
- gap = mind;
3368
- return v;
3369
- }
3370
-
3371
- // If the replacer is an array, use it to select the members to be stringified.
3372
-
3373
- if (rep && typeof rep === 'object') {
3374
- length = rep.length;
3375
- for (i = 0; i < length; i += 1) {
3376
- if (typeof rep[i] === 'string') {
3377
- k = rep[i];
3378
- v = str(k, value);
3379
- if (v) {
3380
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
3381
- }
3382
- }
3383
- }
3384
- } else {
3385
-
3386
- // Otherwise, iterate through all of the keys in the object.
3387
-
3388
- for (k in value) {
3389
- if (Object.prototype.hasOwnProperty.call(value, k)) {
3390
- v = str(k, value);
3391
- if (v) {
3392
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
3393
- }
3394
- }
3395
- }
3396
- }
3397
-
3398
- // Join all of the member texts together, separated with commas,
3399
- // and wrap them in braces.
3400
-
3401
- v = partial.length === 0
3402
- ? '{}'
3403
- : gap
3404
- ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
3405
- : '{' + partial.join(',') + '}';
3406
- gap = mind;
3407
- return v;
3408
- }
3409
- }
3410
-
3411
- // If the JSON object does not yet have a stringify method, give it one.
3412
-
3413
- if (typeof JSON.stringify !== 'function') {
3414
- JSON.stringify = function (value, replacer, space) {
3415
-
3416
- // The stringify method takes a value and an optional replacer, and an optional
3417
- // space parameter, and returns a JSON text. The replacer can be a function
3418
- // that can replace values, or an array of strings that will select the keys.
3419
- // A default replacer method can be provided. Use of the space parameter can
3420
- // produce text that is more easily readable.
3421
-
3422
- var i;
3423
- gap = '';
3424
- indent = '';
3425
-
3426
- // If the space parameter is a number, make an indent string containing that
3427
- // many spaces.
3428
-
3429
- if (typeof space === 'number') {
3430
- for (i = 0; i < space; i += 1) {
3431
- indent += ' ';
3432
- }
3433
-
3434
- // If the space parameter is a string, it will be used as the indent string.
3435
-
3436
- } else if (typeof space === 'string') {
3437
- indent = space;
3438
- }
3439
-
3440
- // If there is a replacer, it must be a function or an array.
3441
- // Otherwise, throw an error.
3442
-
3443
- rep = replacer;
3444
- if (replacer && typeof replacer !== 'function' &&
3445
- (typeof replacer !== 'object' ||
3446
- typeof replacer.length !== 'number')) {
3447
- throw new Error('JSON.stringify');
3448
- }
3449
-
3450
- // Make a fake root object containing our value under the key of ''.
3451
- // Return the result of stringifying the value.
3452
-
3453
- return str('', {'': value});
3454
- };
3455
- }
3456
-
3457
-
3458
- // If the JSON object does not yet have a parse method, give it one.
3459
-
3460
- if (typeof JSON.parse !== 'function') {
3461
- JSON.parse = function (text, reviver) {
3462
-
3463
- // The parse method takes a text and an optional reviver function, and returns
3464
- // a JavaScript value if the text is a valid JSON text.
3465
-
3466
- var j;
3467
-
3468
- function walk(holder, key) {
3469
-
3470
- // The walk method is used to recursively walk the resulting structure so
3471
- // that modifications can be made.
3472
-
3473
- var k, v, value = holder[key];
3474
- if (value && typeof value === 'object') {
3475
- for (k in value) {
3476
- if (Object.prototype.hasOwnProperty.call(value, k)) {
3477
- v = walk(value, k);
3478
- if (v !== undefined) {
3479
- value[k] = v;
3480
- } else {
3481
- delete value[k];
3482
- }
3483
- }
3484
- }
3485
- }
3486
- return reviver.call(holder, key, value);
3487
- }
3488
-
3489
-
3490
- // Parsing happens in four stages. In the first stage, we replace certain
3491
- // Unicode characters with escape sequences. JavaScript handles many characters
3492
- // incorrectly, either silently deleting them, or treating them as line endings.
3493
-
3494
- text = String(text);
3495
- cx.lastIndex = 0;
3496
- if (cx.test(text)) {
3497
- text = text.replace(cx, function (a) {
3498
- return '\\u' +
3499
- ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
3500
- });
3501
- }
3502
-
3503
- // In the second stage, we run the text against regular expressions that look
3504
- // for non-JSON patterns. We are especially concerned with '()' and 'new'
3505
- // because they can cause invocation, and '=' because it can cause mutation.
3506
- // But just to be safe, we want to reject all unexpected forms.
3507
-
3508
- // We split the second stage into 4 regexp operations in order to work around
3509
- // crippling inefficiencies in IE's and Safari's regexp engines. First we
3510
- // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
3511
- // replace all simple value tokens with ']' characters. Third, we delete all
3512
- // open brackets that follow a colon or comma or that begin the text. Finally,
3513
- // we look to see that the remaining characters are only whitespace or ']' or
3514
- // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
3515
-
3516
- if (/^[\],:{}\s]*$/
3517
- .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
3518
- .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
3519
- .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
3520
-
3521
- // In the third stage we use the eval function to compile the text into a
3522
- // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
3523
- // in JavaScript: it can begin a block or an object literal. We wrap the text
3524
- // in parens to eliminate the ambiguity.
3525
-
3526
- j = eval('(' + text + ')');
3527
-
3528
- // In the optional fourth stage, we recursively walk the new structure, passing
3529
- // each name/value pair to a reviver function for possible transformation.
3530
-
3531
- return typeof reviver === 'function'
3532
- ? walk({'': j}, '')
3533
- : j;
3534
- }
3535
-
3536
- // If the text is not JSON parseable, then a SyntaxError is thrown.
3537
-
3538
- throw new SyntaxError('JSON.parse');
3539
- };
3540
- }
3541
- }());