Version 3.17.2
Show:

File: pjax/js/pjax-content.js

  1. /**
  2. `Y.Router` extension that provides the content fetching and handling needed to
  3. implement the standard pjax (HTMP5 pushState + Ajax) functionality.
  4.  
  5. @module pjax
  6. @submodule pjax-content
  7. @since 3.7.0
  8. **/
  9.  
  10. /**
  11. `Y.Router` extension that provides the content fetching and handling needed to
  12. implement the standard pjax (HTMP5 pushState + Ajax) functionality.
  13.  
  14. This makes it easy to fetch server rendered content for URLs using Ajax. By
  15. helping the router to fulfill the "request" for the content you can avoid full
  16. page loads.
  17.  
  18. The `PjaxContent` class isn't useful on its own, but can be mixed into a
  19. `Router`-based class along with the `PjaxBase` class to add Pjax functionality
  20. to that Router. For a pre-made standalone Pjax router, see the `Pjax` class.
  21.  
  22. var MyRouter = Y.Base.create('myRouter', Y.Router, [
  23. Y.PjaxBase,
  24. Y.PjaxContent
  25. ], {
  26. // ...
  27. });
  28.  
  29. @class PjaxContent
  30. @extensionfor Router
  31. @since 3.7.0
  32. **/
  33. function PjaxContent() {}
  34.  
  35. PjaxContent.prototype = {
  36. // -- Public Methods -------------------------------------------------------
  37.  
  38. /**
  39. Extracts and returns the relevant HTML content from an Ajax response. The
  40. content is extracted using the `contentSelector` attribute as a CSS
  41. selector. If `contentSelector` is `null`, the entire response will be
  42. returned.
  43.  
  44. The return value is an object containing two properties:
  45.  
  46. * `node`: A `Y.Node` instance for a document fragment containing the
  47. extracted HTML content.
  48.  
  49. * `title`: The title of the HTML page, if any, extracted using the
  50. `titleSelector` attribute (which defaults to looking for a `<title>`
  51. element). If `titleSelector` is not set or if a title could not be
  52. found, this property will be `undefined`.
  53.  
  54. @method getContent
  55. @param {String} responseText Raw Ajax response text.
  56. @return {Object} Content object with the properties described above.
  57. @since 3.5.0
  58. **/
  59. getContent: function (responseText) {
  60. var content = {},
  61. contentSelector = this.get('contentSelector'),
  62. frag = Y.Node.create(responseText || ''),
  63. titleSelector = this.get('titleSelector'),
  64. titleNode;
  65.  
  66. if (contentSelector && frag) {
  67. content.node = frag.all(contentSelector).toFrag();
  68. } else {
  69. content.node = frag;
  70. }
  71.  
  72. if (titleSelector && frag) {
  73. titleNode = frag.one(titleSelector);
  74.  
  75. if (titleNode) {
  76. content.title = titleNode.get('text');
  77. }
  78. }
  79.  
  80. return content;
  81. },
  82.  
  83. /**
  84. Pjax route middleware to load content from a server. This makes an Ajax
  85. request for the requested URL, parses the returned content and puts it on
  86. the route's response object.
  87.  
  88. This is route middleware and not intended to be the final callback for a
  89. route. This will add the following information to the route's request and
  90. response objects:
  91.  
  92. - `req.ioURL`: The full URL that was used to make the `Y.io()` XHR. This
  93. may contain `"pjax=1"` if the `addPjaxParam` option is set.
  94.  
  95. - `res.content`: An object containing `node` and `title` properties for
  96. the content extracted from the server's response. See `getContent()` for
  97. more details.
  98.  
  99. - `res.ioResponse`: The full `Y.io()` response object. This is useful if
  100. you need access to the XHR's response `status` or HTTP headers.
  101.  
  102. @example
  103. router.route('/foo/', 'loadContent', function (req, res, next) {
  104. Y.one('container').setHTML(res.content.node);
  105. Y.config.doc.title = res.content.title;
  106. });
  107.  
  108. @method loadContent
  109. @param {Object} req Request object.
  110. @param {Object} res Response Object.
  111. @param {Function} next Function to pass control to the next route callback.
  112. @since 3.7.0
  113. @see Router.route()
  114. **/
  115. loadContent: function (req, res, next) {
  116. var url = req.url;
  117.  
  118. // If there's an outstanding request, abort it.
  119. if (this._request) {
  120. this._request.abort();
  121. }
  122.  
  123. // Add a 'pjax=1' query parameter if enabled.
  124. if (this.get('addPjaxParam')) {
  125. // Captures the path with query, and hash parts of the URL. Then
  126. // properly injects the "pjax=1" query param in the right place,
  127. // before any hash fragment, and returns the updated URL.
  128. url = url.replace(/([^#]*)(#.*)?$/, function (match, path, hash) {
  129. path += (path.indexOf('?') > -1 ? '&' : '?') + 'pjax=1';
  130. return path + (hash || '');
  131. });
  132. }
  133.  
  134. // Send a request.
  135. this._request = Y.io(url, {
  136. 'arguments': {
  137. route: {
  138. req : req,
  139. res : res,
  140. next: next
  141. },
  142.  
  143. url: url
  144. },
  145.  
  146. context: this,
  147. headers: {'X-PJAX': 'true'},
  148. timeout: this.get('timeout'),
  149.  
  150. on: {
  151. complete: this._onPjaxIOComplete,
  152. end : this._onPjaxIOEnd
  153. }
  154. });
  155. },
  156.  
  157. // -- Event Handlers -------------------------------------------------------
  158.  
  159. /**
  160. Handles IO complete events.
  161.  
  162. This parses the content from the `Y.io()` response and puts it on the
  163. route's response object.
  164.  
  165. @method _onPjaxIOComplete
  166. @param {String} id The `Y.io` transaction id.
  167. @param {Object} ioResponse The `Y.io` response object.
  168. @param {Object} details Extra details carried through from `loadContent()`.
  169. @protected
  170. @since 3.7.0
  171. **/
  172. _onPjaxIOComplete: function (id, ioResponse, details) {
  173. var content = this.getContent(ioResponse.responseText),
  174. route = details.route,
  175. req = route.req,
  176. res = route.res;
  177.  
  178. // Put the URL requested through `Y.io` on the route's `req` object.
  179. req.ioURL = details.url;
  180.  
  181. // Put the parsed content and `Y.io` response object on the route's
  182. // `res` object.
  183. res.content = content;
  184. res.ioResponse = ioResponse;
  185.  
  186. route.next();
  187. },
  188.  
  189. /**
  190. Handles IO end events.
  191.  
  192. @method _onPjaxIOEnd
  193. @param {String} id The `Y.io` transaction id.
  194. @param {Object} details Extra details carried through from `loadContent()`.
  195. @protected
  196. @since 3.5.0
  197. **/
  198. _onPjaxIOEnd: function () {
  199. this._request = null;
  200. }
  201. };
  202.  
  203. PjaxContent.ATTRS = {
  204. /**
  205. If `true`, a "pjax=1" query parameter will be appended to all URLs
  206. requested via Pjax.
  207.  
  208. Browsers ignore HTTP request headers when caching content, so if the
  209. same URL is used to request a partial Pjax page and a full page, the
  210. browser will cache them under the same key and may later load the
  211. cached partial page when the user actually requests a full page (or vice
  212. versa).
  213.  
  214. To prevent this, we can add a bogus query parameter to the URL so that
  215. Pjax URLs will always be cached separately from non-Pjax URLs.
  216.  
  217. @attribute addPjaxParam
  218. @type Boolean
  219. @default true
  220. @since 3.5.0
  221. **/
  222. addPjaxParam: {
  223. value: true
  224. },
  225.  
  226. /**
  227. CSS selector used to extract a specific portion of the content of a page
  228. loaded via Pjax.
  229.  
  230. For example, if you wanted to load the page `example.html` but only use
  231. the content within an element with the id "pjax-content", you'd set
  232. `contentSelector` to "#pjax-content".
  233.  
  234. If not set, the entire page will be used.
  235.  
  236. @attribute contentSelector
  237. @type String
  238. @default null
  239. @since 3.5.0
  240. **/
  241. contentSelector: {
  242. value: null
  243. },
  244.  
  245. /**
  246. CSS selector used to extract a page title from the content of a page
  247. loaded via Pjax.
  248.  
  249. By default this is set to extract the title from the `<title>` element,
  250. but you could customize it to extract the title from an `<h1>`, or from
  251. any other element, if that's more appropriate for the content you're
  252. loading.
  253.  
  254. @attribute titleSelector
  255. @type String
  256. @default "title"
  257. @since 3.5.0
  258. **/
  259. titleSelector: {
  260. value: 'title'
  261. },
  262.  
  263. /**
  264. Time in milliseconds after which an Ajax request should time out.
  265.  
  266. @attribute timeout
  267. @type Number
  268. @default 30000
  269. @since 3.5.0
  270. **/
  271. timeout: {
  272. value: 30000
  273. }
  274. };
  275.  
  276. Y.PjaxContent = PjaxContent;
  277.