Skip to content

Commit 8a09f00

Browse files
committed
Merge pull request #22 from vojtech-dobes/history
History API extension
2 parents 1366355 + ad925c2 commit 8a09f00

File tree

5 files changed

+244
-21
lines changed

5 files changed

+244
-21
lines changed

extensions/history.ajax.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
(function($, undefined) {
2+
3+
// Is History API reliably supported? (based on Modernizr & PJAX)
4+
if (!(window.history && history.pushState && window.history.replaceState && !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/))) return;
5+
6+
$.nette.ext('redirect', false);
7+
8+
var findSnippets = function () {
9+
var result = [];
10+
$('[id^="snippet--"]').each(function () {
11+
var $el = $(this);
12+
result.push({
13+
id: $el.attr('id'),
14+
html: $el.html()
15+
});
16+
});
17+
return result;
18+
};
19+
var handleState = function (context, name, args) {
20+
var handler = context['handle' + name.substring(0, 1).toUpperCase() + name.substring(1)];
21+
if (handler) {
22+
handler.apply(context, args);
23+
}
24+
};
25+
26+
$.nette.ext('history', {
27+
init: function () {
28+
var snippetsExt;
29+
if (this.cache && (snippetsExt = $.nette.ext('snippets'))) {
30+
this.handleUI = function (domCache) {
31+
$.each(domCache, function () {
32+
snippetsExt.updateSnippet(this.id, this.html);
33+
});
34+
$.nette.load();
35+
};
36+
}
37+
38+
history.replaceState(this.initialState = {
39+
nette: true,
40+
href: window.location.href,
41+
title: document.title,
42+
ui: findSnippets()
43+
}, document.title, window.location.href);
44+
45+
$(window).on('popstate.nette', $.proxy(function (e) {
46+
var state = e.originalEvent.state || this.initialState;
47+
if (window.history.ready || !state || !state.nette) return;
48+
if (this.cache && state.ui) {
49+
handleState(this, 'UI', [state.ui]);
50+
handleState(this, 'title', [state.title]);
51+
} else {
52+
$.nette.ajax({
53+
url: state.href,
54+
off: ['history']
55+
});
56+
}
57+
}, this));
58+
},
59+
before: function (xhr, settings) {
60+
if (!settings.nette) {
61+
this.href = null;
62+
} else if (!settings.nette.form) {
63+
this.href = settings.nette.ui.href;
64+
} else if (settings.nette.form.method == 'get') {
65+
this.href = settings.nette.ui.action || window.location.href;
66+
} else {
67+
this.href = null;
68+
}
69+
},
70+
success: function (payload) {
71+
var redirect = payload.redirect || payload.url; // backwards compatibility for 'url'
72+
if (redirect) {
73+
var regexp = new RegExp('//' + window.location.host + '($|/)');
74+
if ((redirect.substring(0,4) === 'http') ? regexp.test(redirect) : true) {
75+
this.href = redirect;
76+
} else {
77+
window.location.href = redirect;
78+
}
79+
}
80+
if (this.href && this.href != window.location.href) {
81+
history.pushState({
82+
nette: true,
83+
href: this.href,
84+
title: document.title,
85+
ui: findSnippets()
86+
}, document.title, this.href);
87+
}
88+
this.href = null;
89+
}
90+
}, {
91+
href: null,
92+
cache: true,
93+
handleTitle: function (title) {
94+
document.title = title;
95+
}
96+
});
97+
98+
})(jQuery);

nette.ajax.js

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -375,27 +375,6 @@ $.nette.ext('redirect', {
375375
}
376376
});
377377

378-
// change URL (requires HTML5)
379-
if (!!(window.history && history.pushState)) { // check borrowed from Modernizr
380-
$.nette.ext('history', {
381-
start: function (xhr, settings) {
382-
if (!settings.nette) return;
383-
var $el = settings.nette.el;
384-
if ($el.is('a')) {
385-
this.href = settings.nette.ui.href;
386-
}
387-
},
388-
success: function (payload) {
389-
if (payload.url) {
390-
this.href = payload.url;
391-
}
392-
if (!payload.signal && this.href) {
393-
history.pushState({href: this.href}, '', this.href);
394-
}
395-
}
396-
}, {href: null});
397-
}
398-
399378
// current page state
400379
$.nette.ext('state', {
401380
success: function (payload) {

src/Extension.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace VojtechDobes\NetteAjax;
4+
5+
use Nette\Config\CompilerExtension;
6+
7+
8+
/**
9+
* Provides support for History API
10+
*/
11+
class Extension extends CompilerExtension
12+
{
13+
14+
public function loadConfiguration()
15+
{
16+
$container = $this->getContainerBuilder();
17+
18+
$container->addDefinition($this->prefix('onRequestHandler'))
19+
->setClass('VojtechDobes\NetteAjax\OnRequestHandler');
20+
21+
$container->addDefinition($this->prefix('onResponseHandler'))
22+
->setClass('VojtechDobes\NetteAjax\OnResponseHandler');
23+
24+
$application = $container->getDefinition('application');
25+
$application->addSetup('$service->onRequest[] = ?', array('@' . $this->prefix('onRequestHandler')));
26+
$application->addSetup('$service->onResponse[] = ?', array('@' . $this->prefix('onResponseHandler')));
27+
}
28+
29+
}

src/OnRequestHandler.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace VojtechDobes\NetteAjax;
4+
5+
use Nette\Http;
6+
7+
8+
/**
9+
* Listens for forward calls
10+
*/
11+
class OnRequestHandler
12+
{
13+
14+
/** @var Http\IRequest */
15+
private $httpRequest;
16+
17+
/** @var OnResponseHandler */
18+
private $onResponseHandler;
19+
20+
21+
22+
/**
23+
* @param Http\IRequest
24+
* @param OnResponseHandler
25+
*/
26+
public function __construct(Http\IRequest $httpRequest, OnResponseHandler $onResponseHandler)
27+
{
28+
$this->httpRequest = $httpRequest;
29+
$this->onResponseHandler = $onResponseHandler;
30+
}
31+
32+
33+
34+
public function __invoke($application, $request)
35+
{
36+
if ($this->httpRequest->isAjax() && count($application->getRequests()) > 1) {
37+
$this->onResponseHandler->markForward();
38+
}
39+
}
40+
41+
}

src/OnResponseHandler.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
namespace VojtechDobes\NetteAjax;
4+
5+
use Nette\Application\Responses\ForwardResponse;
6+
use Nette\Application\Responses\JsonResponse;
7+
use Nette\Application\IRouter;
8+
use Nette\Http;
9+
use Nette\Reflection\Property;
10+
11+
12+
/**
13+
* Automatically adds 'redirect' to payload when forward happens
14+
* to simplify userland code in presenters
15+
*
16+
* @author Vojtěch Dobeš
17+
*/
18+
class OnResponseHandler
19+
{
20+
21+
/** @var Http\IRequest */
22+
private $httpRequest;
23+
24+
/** @var IRouter */
25+
private $router;
26+
27+
/** @var bool */
28+
private $forwardHasHappened = FALSE;
29+
30+
31+
32+
/**
33+
* @param Http\IRequest
34+
* @param IRouter
35+
*/
36+
public function __construct(Http\IRequest $httpRequest, IRouter $router)
37+
{
38+
$this->httpRequest = $httpRequest;
39+
$this->router = $router;
40+
}
41+
42+
43+
44+
/**
45+
* Stores information about ocurring forward() call
46+
*/
47+
public function markForward()
48+
{
49+
$this->forwardHasHappened = TRUE;
50+
}
51+
52+
53+
54+
public function __invoke($application, $response)
55+
{
56+
if ($response instanceof JsonResponse && ($payload = $response->getPayload()) instanceof \stdClass) {
57+
if (!$this->forwardHasHappened && isset($payload->redirect)) {
58+
$url = new Http\UrlScript($payload->redirect);
59+
$url->setScriptPath($this->httpRequest->url->scriptPath);
60+
$httpRequest = new Http\Request($url);
61+
62+
if ($this->router->match($httpRequest) !== NULL) {
63+
$prop = new Property($application, 'httpRequest');
64+
$prop->setAccessible(TRUE);
65+
$prop->setValue($application, $httpRequest);
66+
67+
$application->run();
68+
exit;
69+
}
70+
} elseif ($this->forwardHasHappened && !isset($payload->redirect)) {
71+
$payload->redirect = $application->getPresenter()->link('this');
72+
}
73+
}
74+
}
75+
76+
}

0 commit comments

Comments
 (0)