You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. namespace yii\widgets;
  8. use Yii;
  9. use yii\base\Widget;
  10. use yii\helpers\Html;
  11. use yii\helpers\Json;
  12. use yii\web\Response;
  13. /**
  14. * Pjax is a widget integrating the [pjax](https://github.com/yiisoft/jquery-pjax) jQuery plugin.
  15. *
  16. * Pjax only deals with the content enclosed between its [[begin()]] and [[end()]] calls, called the *body content* of the widget.
  17. * By default, any link click or form submission (for those forms with `data-pjax` attribute) within the body content
  18. * will trigger an AJAX request. In responding to the AJAX request, Pjax will send the updated body content (based
  19. * on the AJAX request) to the client which will replace the old content with the new one. The browser's URL will then
  20. * be updated using pushState. The whole process requires no reloading of the layout or resources (js, css).
  21. *
  22. * You may configure [[linkSelector]] to specify which links should trigger pjax, and configure [[formSelector]]
  23. * to specify which form submission may trigger pjax.
  24. *
  25. * You may disable pjax for a specific link inside the container by adding `data-pjax="0"` attribute to this link.
  26. *
  27. * The following example shows how to use Pjax with the [[\yii\grid\GridView]] widget so that the grid pagination,
  28. * sorting and filtering can be done via pjax:
  29. *
  30. * ```php
  31. * use yii\widgets\Pjax;
  32. *
  33. * Pjax::begin();
  34. * echo GridView::widget([...]);
  35. * Pjax::end();
  36. * ```
  37. *
  38. * @author Qiang Xue <qiang.xue@gmail.com>
  39. * @since 2.0
  40. */
  41. class Pjax extends Widget
  42. {
  43. /**
  44. * @var array the HTML attributes for the widget container tag.
  45. * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
  46. */
  47. public $options = [];
  48. /**
  49. * @var string the jQuery selector of the links that should trigger pjax requests.
  50. * If not set, all links within the enclosed content of Pjax will trigger pjax requests.
  51. * Note that if the response to the pjax request is a full page, a normal request will be sent again.
  52. */
  53. public $linkSelector;
  54. /**
  55. * @var string the jQuery selector of the forms whose submissions should trigger pjax requests.
  56. * If not set, all forms with `data-pjax` attribute within the enclosed content of Pjax will trigger pjax requests.
  57. * Note that if the response to the pjax request is a full page, a normal request will be sent again.
  58. */
  59. public $formSelector;
  60. /**
  61. * @var boolean whether to enable push state.
  62. */
  63. public $enablePushState = true;
  64. /**
  65. * @var boolean whether to enable replace state.
  66. */
  67. public $enableReplaceState = false;
  68. /**
  69. * @var integer pjax timeout setting (in milliseconds). This timeout is used when making AJAX requests.
  70. * Use a bigger number if your server is slow. If the server does not respond within the timeout,
  71. * a full page load will be triggered.
  72. */
  73. public $timeout = 1000;
  74. /**
  75. * @var boolean|integer how to scroll the page when pjax response is received. If false, no page scroll will be made.
  76. * Use a number if you want to scroll to a particular place.
  77. */
  78. public $scrollTo = false;
  79. /**
  80. * @var array additional options to be passed to the pjax JS plugin. Please refer to the
  81. * [pjax project page](https://github.com/yiisoft/jquery-pjax) for available options.
  82. */
  83. public $clientOptions;
  84. /**
  85. * @inheritdoc
  86. */
  87. public function init()
  88. {
  89. if (!isset($this->options['id'])) {
  90. $this->options['id'] = $this->getId();
  91. }
  92. if ($this->requiresPjax()) {
  93. ob_start();
  94. ob_implicit_flush(false);
  95. $view = $this->getView();
  96. $view->clear();
  97. $view->beginPage();
  98. $view->head();
  99. $view->beginBody();
  100. if ($view->title !== null) {
  101. echo Html::tag('title', Html::encode($view->title));
  102. }
  103. } else {
  104. echo Html::beginTag('div', $this->options);
  105. }
  106. }
  107. /**
  108. * @inheritdoc
  109. */
  110. public function run()
  111. {
  112. if (!$this->requiresPjax()) {
  113. echo Html::endTag('div');
  114. $this->registerClientScript();
  115. return;
  116. }
  117. $view = $this->getView();
  118. $view->endBody();
  119. // Do not re-send css files as it may override the css files that were loaded after them.
  120. // This is a temporary fix for https://github.com/yiisoft/yii2/issues/2310
  121. // It should be removed once pjax supports loading only missing css files
  122. $view->cssFiles = null;
  123. $view->endPage(true);
  124. $content = ob_get_clean();
  125. // only need the content enclosed within this widget
  126. $response = Yii::$app->getResponse();
  127. $response->clearOutputBuffers();
  128. $response->setStatusCode(200);
  129. $response->format = Response::FORMAT_HTML;
  130. $response->content = $content;
  131. $response->send();
  132. Yii::$app->end();
  133. }
  134. /**
  135. * @return boolean whether the current request requires pjax response from this widget
  136. */
  137. protected function requiresPjax()
  138. {
  139. $headers = Yii::$app->getRequest()->getHeaders();
  140. return $headers->get('X-Pjax') && $headers->get('X-Pjax-Container') === '#' . $this->options['id'];
  141. }
  142. /**
  143. * Registers the needed JavaScript.
  144. */
  145. public function registerClientScript()
  146. {
  147. $id = $this->options['id'];
  148. $this->clientOptions['push'] = $this->enablePushState;
  149. $this->clientOptions['replace'] = $this->enableReplaceState;
  150. $this->clientOptions['timeout'] = $this->timeout;
  151. $this->clientOptions['scrollTo'] = $this->scrollTo;
  152. $options = Json::encode($this->clientOptions);
  153. $linkSelector = Json::encode($this->linkSelector !== null ? $this->linkSelector : '#' . $id . ' a');
  154. $formSelector = Json::encode($this->formSelector !== null ? $this->formSelector : '#' . $id . ' form[data-pjax]');
  155. $view = $this->getView();
  156. PjaxAsset::register($view);
  157. $js = "jQuery(document).pjax($linkSelector, \"#$id\", $options);";
  158. $js .= "\njQuery(document).on('submit', $formSelector, function (event) {jQuery.pjax.submit(event, '#$id', $options);});";
  159. $view->registerJs($js);
  160. }
  161. }