1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @link http://www.yiiframework.com/ |
4
|
|
|
* @copyright Copyright (c) 2008 Yii Software LLC |
5
|
|
|
* @license http://www.yiiframework.com/license/ |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace yii\widgets; |
9
|
|
|
|
10
|
|
|
use Yii; |
11
|
|
|
use yii\base\Widget; |
12
|
|
|
use yii\helpers\ArrayHelper; |
13
|
|
|
use yii\helpers\Html; |
14
|
|
|
use yii\helpers\Json; |
15
|
|
|
use yii\web\Response; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Pjax is a widget integrating the [pjax](https://github.com/yiisoft/jquery-pjax) jQuery plugin. |
19
|
|
|
* |
20
|
|
|
* Pjax only deals with the content enclosed between its [[begin()]] and [[end()]] calls, called the *body content* of the widget. |
21
|
|
|
* By default, any link click or form submission (for those forms with `data-pjax` attribute) within the body content |
22
|
|
|
* will trigger an AJAX request. In responding to the AJAX request, Pjax will send the updated body content (based |
23
|
|
|
* on the AJAX request) to the client which will replace the old content with the new one. The browser's URL will then |
24
|
|
|
* be updated using pushState. The whole process requires no reloading of the layout or resources (js, css). |
25
|
|
|
* |
26
|
|
|
* You may configure [[linkSelector]] to specify which links should trigger pjax, and configure [[formSelector]] |
27
|
|
|
* to specify which form submission may trigger pjax. |
28
|
|
|
* |
29
|
|
|
* You may disable pjax for a specific link inside the container by adding `data-pjax="0"` attribute to this link. |
30
|
|
|
* |
31
|
|
|
* The following example shows how to use Pjax with the [[\yii\grid\GridView]] widget so that the grid pagination, |
32
|
|
|
* sorting and filtering can be done via pjax: |
33
|
|
|
* |
34
|
|
|
* ```php |
35
|
|
|
* use yii\widgets\Pjax; |
36
|
|
|
* |
37
|
|
|
* Pjax::begin(); |
38
|
|
|
* echo GridView::widget([...]); |
39
|
|
|
* Pjax::end(); |
40
|
|
|
* ``` |
41
|
|
|
* |
42
|
|
|
* @author Qiang Xue <[email protected]> |
43
|
|
|
* @since 2.0 |
44
|
|
|
*/ |
45
|
|
|
class Pjax extends Widget |
46
|
|
|
{ |
47
|
|
|
/** |
48
|
|
|
* @var array the HTML attributes for the widget container tag. The following special options are recognized: |
49
|
|
|
* |
50
|
|
|
* - `tag`: string, the tag name for the container. Defaults to `div` |
51
|
|
|
* This option is available since version 2.0.7. |
52
|
|
|
* See also [[\yii\helpers\Html::tag()]]. |
53
|
|
|
* |
54
|
|
|
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. |
55
|
|
|
*/ |
56
|
|
|
public $options = []; |
57
|
|
|
/** |
58
|
|
|
* @var string|false the jQuery selector of the links that should trigger pjax requests. |
59
|
|
|
* If not set, all links within the enclosed content of Pjax will trigger pjax requests. |
60
|
|
|
* If set to false, no code will be registered to handle links. |
61
|
|
|
* Note that if the response to the pjax request is a full page, a normal request will be sent again. |
62
|
|
|
*/ |
63
|
|
|
public $linkSelector; |
64
|
|
|
/** |
65
|
|
|
* @var string|false the jQuery selector of the forms whose submissions should trigger pjax requests. |
66
|
|
|
* If not set, all forms with `data-pjax` attribute within the enclosed content of Pjax will trigger pjax requests. |
67
|
|
|
* If set to false, no code will be registered to handle forms. |
68
|
|
|
* Note that if the response to the pjax request is a full page, a normal request will be sent again. |
69
|
|
|
*/ |
70
|
|
|
public $formSelector; |
71
|
|
|
/** |
72
|
|
|
* @var string The jQuery event that will trigger form handler. Defaults to "submit". |
73
|
|
|
* @since 2.0.9 |
74
|
|
|
*/ |
75
|
|
|
public $submitEvent = 'submit'; |
76
|
|
|
/** |
77
|
|
|
* @var bool whether to enable push state. |
78
|
|
|
*/ |
79
|
|
|
public $enablePushState = true; |
80
|
|
|
/** |
81
|
|
|
* @var bool whether to enable replace state. |
82
|
|
|
*/ |
83
|
|
|
public $enableReplaceState = false; |
84
|
|
|
/** |
85
|
|
|
* @var int pjax timeout setting (in milliseconds). This timeout is used when making AJAX requests. |
86
|
|
|
* Use a bigger number if your server is slow. If the server does not respond within the timeout, |
87
|
|
|
* a full page load will be triggered. |
88
|
|
|
*/ |
89
|
|
|
public $timeout = 1000; |
90
|
|
|
/** |
91
|
|
|
* @var bool|int how to scroll the page when pjax response is received. If false, no page scroll will be made. |
92
|
|
|
* Use a number if you want to scroll to a particular place. |
93
|
|
|
*/ |
94
|
|
|
public $scrollTo = false; |
95
|
|
|
/** |
96
|
|
|
* @var array additional options to be passed to the pjax JS plugin. Please refer to the |
97
|
|
|
* [pjax project page](https://github.com/yiisoft/jquery-pjax) for available options. |
98
|
|
|
*/ |
99
|
|
|
public $clientOptions; |
100
|
|
|
/** |
101
|
|
|
* @inheritdoc |
102
|
|
|
* @internal |
103
|
|
|
*/ |
104
|
|
|
public static $counter = 0; |
105
|
|
|
/** |
106
|
|
|
* @inheritdoc |
107
|
|
|
*/ |
108
|
|
|
public static $autoIdPrefix = 'p'; |
109
|
|
|
|
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* @inheritdoc |
113
|
|
|
*/ |
114
|
1 |
|
public function init() |
115
|
|
|
{ |
116
|
1 |
|
if (!isset($this->options['id'])) { |
117
|
1 |
|
$this->options['id'] = $this->getId(); |
118
|
1 |
|
} |
119
|
|
|
|
120
|
1 |
|
if ($this->requiresPjax()) { |
121
|
|
|
ob_start(); |
122
|
|
|
ob_implicit_flush(false); |
123
|
|
|
$view = $this->getView(); |
124
|
|
|
$view->clear(); |
125
|
|
|
$view->beginPage(); |
126
|
|
|
$view->head(); |
127
|
|
|
$view->beginBody(); |
128
|
|
|
if ($view->title !== null) { |
129
|
|
|
echo Html::tag('title', Html::encode($view->title)); |
130
|
|
|
} |
131
|
|
|
} else { |
132
|
1 |
|
$options = $this->options; |
133
|
1 |
|
$tag = ArrayHelper::remove($options, 'tag', 'div'); |
134
|
1 |
|
echo Html::beginTag($tag, array_merge([ |
135
|
1 |
|
'data-pjax-container' => '', |
136
|
1 |
|
'data-pjax-push-state' => $this->enablePushState, |
137
|
1 |
|
'data-pjax-replace-state' => $this->enableReplaceState, |
138
|
1 |
|
'data-pjax-timeout' => $this->timeout, |
139
|
1 |
|
'data-pjax-scrollto' => $this->scrollTo, |
140
|
1 |
|
], $options)); |
141
|
|
|
} |
142
|
1 |
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* @inheritdoc |
146
|
|
|
*/ |
147
|
|
|
public function run() |
148
|
|
|
{ |
149
|
|
|
if (!$this->requiresPjax()) { |
150
|
|
|
echo Html::endTag(ArrayHelper::remove($this->options, 'tag', 'div')); |
151
|
|
|
$this->registerClientScript(); |
152
|
|
|
|
153
|
|
|
return; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$view = $this->getView(); |
157
|
|
|
$view->endBody(); |
158
|
|
|
|
159
|
|
|
// Do not re-send css files as it may override the css files that were loaded after them. |
160
|
|
|
// This is a temporary fix for https://github.com/yiisoft/yii2/issues/2310 |
161
|
|
|
// It should be removed once pjax supports loading only missing css files |
162
|
|
|
$view->cssFiles = null; |
|
|
|
|
163
|
|
|
|
164
|
|
|
$view->endPage(true); |
165
|
|
|
|
166
|
|
|
$content = ob_get_clean(); |
167
|
|
|
|
168
|
|
|
// only need the content enclosed within this widget |
169
|
|
|
$response = Yii::$app->getResponse(); |
170
|
|
|
$response->clearOutputBuffers(); |
171
|
|
|
$response->setStatusCode(200); |
172
|
|
|
$response->format = Response::FORMAT_HTML; |
173
|
|
|
$response->content = $content; |
174
|
|
|
$response->send(); |
175
|
|
|
|
176
|
|
|
Yii::$app->end(); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* @return bool whether the current request requires pjax response from this widget |
181
|
|
|
*/ |
182
|
1 |
|
protected function requiresPjax() |
183
|
|
|
{ |
184
|
1 |
|
$headers = Yii::$app->getRequest()->getHeaders(); |
185
|
|
|
|
186
|
1 |
|
return $headers->get('X-Pjax') && explode(' ', $headers->get('X-Pjax-Container'))[0] === '#' . $this->options['id']; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Registers the needed JavaScript. |
191
|
|
|
*/ |
192
|
|
|
public function registerClientScript() |
193
|
|
|
{ |
194
|
|
|
$id = $this->options['id']; |
195
|
|
|
$this->clientOptions['push'] = $this->enablePushState; |
196
|
|
|
$this->clientOptions['replace'] = $this->enableReplaceState; |
197
|
|
|
$this->clientOptions['timeout'] = $this->timeout; |
198
|
|
|
$this->clientOptions['scrollTo'] = $this->scrollTo; |
199
|
|
|
if (!isset($this->clientOptions['container'])) { |
200
|
|
|
$this->clientOptions['container'] = "#$id"; |
201
|
|
|
} |
202
|
|
|
$options = Json::htmlEncode($this->clientOptions); |
203
|
|
|
$js = ''; |
204
|
|
|
if ($this->linkSelector !== false) { |
205
|
|
|
$linkSelector = Json::htmlEncode($this->linkSelector !== null ? $this->linkSelector : '#' . $id . ' a'); |
206
|
|
|
$js .= "jQuery(document).pjax($linkSelector, $options);"; |
207
|
|
|
} |
208
|
|
|
if ($this->formSelector !== false) { |
209
|
|
|
$formSelector = Json::htmlEncode($this->formSelector !== null ? $this->formSelector : '#' . $id . ' form[data-pjax]'); |
210
|
|
|
$submitEvent = Json::htmlEncode($this->submitEvent); |
211
|
|
|
$js .= "\njQuery(document).on($submitEvent, $formSelector, function (event) {jQuery.pjax.submit(event, $options);});"; |
212
|
|
|
} |
213
|
|
|
$view = $this->getView(); |
214
|
|
|
PjaxAsset::register($view); |
215
|
|
|
|
216
|
|
|
if ($js !== '') { |
217
|
|
|
$view->registerJs($js); |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..