page-cache.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. define([
  6. 'jquery',
  7. 'domReady',
  8. 'consoleLogger',
  9. 'jquery/ui',
  10. 'mage/cookies'
  11. ], function ($, domReady, consoleLogger) {
  12. 'use strict';
  13. /**
  14. * Helper. Generate random string
  15. * TODO: Merge with mage/utils
  16. * @param {String} chars - list of symbols
  17. * @param {Number} length - length for need string
  18. * @returns {String}
  19. */
  20. function generateRandomString(chars, length) {
  21. var result = '';
  22. length = length > 0 ? length : 1;
  23. while (length--) {
  24. result += chars[Math.round(Math.random() * (chars.length - 1))];
  25. }
  26. return result;
  27. }
  28. /**
  29. * Nodes tree to flat list converter
  30. * @returns {Array}
  31. */
  32. $.fn.comments = function () {
  33. var elements = [],
  34. contents,
  35. elementContents;
  36. /**
  37. * @param {jQuery} element - Comment holder
  38. */
  39. (function lookup(element) {
  40. var iframeHostName;
  41. // prevent cross origin iframe content reading
  42. if ($(element).prop('tagName') === 'IFRAME') {
  43. iframeHostName = $('<a>').prop('href', $(element).prop('src'))
  44. .prop('hostname');
  45. if (window.location.hostname !== iframeHostName) {
  46. return [];
  47. }
  48. }
  49. /**
  50. * Rewrite jQuery contents().
  51. *
  52. * @param {jQuery} elem
  53. */
  54. contents = function (elem) {
  55. return $.map(elem, function (el) {
  56. try {
  57. return $.nodeName(el, 'iframe') ?
  58. el.contentDocument || (el.contentWindow ? el.contentWindow.document : []) :
  59. $.merge([], el.childNodes);
  60. } catch (e) {
  61. consoleLogger.error(e);
  62. return [];
  63. }
  64. });
  65. };
  66. elementContents = contents($(element));
  67. $.each(elementContents, function (index, el) {
  68. switch (el.nodeType) {
  69. case 1: // ELEMENT_NODE
  70. lookup(el);
  71. break;
  72. case 8: // COMMENT_NODE
  73. elements.push(el);
  74. break;
  75. case 9: // DOCUMENT_NODE
  76. lookup($(el).find('body'));
  77. break;
  78. }
  79. });
  80. })(this);
  81. return elements;
  82. };
  83. /**
  84. * FormKey Widget - this widget is generating from key, saves it to cookie and
  85. */
  86. $.widget('mage.formKey', {
  87. options: {
  88. inputSelector: 'input[name="form_key"]',
  89. allowedCharacters: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
  90. length: 16
  91. },
  92. /**
  93. * Creates widget 'mage.formKey'
  94. * @private
  95. */
  96. _create: function () {
  97. var formKey = $.mage.cookies.get('form_key');
  98. if (!formKey) {
  99. formKey = generateRandomString(this.options.allowedCharacters, this.options.length);
  100. $.mage.cookies.set('form_key', formKey);
  101. }
  102. $(this.options.inputSelector).val(formKey);
  103. }
  104. });
  105. /**
  106. * PageCache Widget
  107. * Handles additional ajax request for rendering user private content.
  108. */
  109. $.widget('mage.pageCache', {
  110. options: {
  111. url: '/',
  112. patternPlaceholderOpen: /^ BLOCK (.+) $/,
  113. patternPlaceholderClose: /^ \/BLOCK (.+) $/,
  114. versionCookieName: 'private_content_version',
  115. handles: []
  116. },
  117. /**
  118. * Creates widget 'mage.pageCache'
  119. * @private
  120. */
  121. _create: function () {
  122. var placeholders,
  123. version = $.mage.cookies.get(this.options.versionCookieName);
  124. if (!version) {
  125. return;
  126. }
  127. placeholders = this._searchPlaceholders(this.element.comments());
  128. if (placeholders && placeholders.length) {
  129. this._ajax(placeholders, version);
  130. }
  131. },
  132. /**
  133. * Parse page for placeholders.
  134. * @param {Array} elements
  135. * @returns {Array}
  136. * @private
  137. */
  138. _searchPlaceholders: function (elements) {
  139. var placeholders = [],
  140. tmp = {},
  141. ii,
  142. len,
  143. el, matches, name;
  144. if (!(elements && elements.length)) {
  145. return placeholders;
  146. }
  147. for (ii = 0, len = elements.length; ii < len; ii++) {
  148. el = elements[ii];
  149. matches = this.options.patternPlaceholderOpen.exec(el.nodeValue);
  150. name = null;
  151. if (matches) {
  152. name = matches[1];
  153. tmp[name] = {
  154. name: name,
  155. openElement: el
  156. };
  157. } else {
  158. matches = this.options.patternPlaceholderClose.exec(el.nodeValue);
  159. if (matches) { //eslint-disable-line max-depth
  160. name = matches[1];
  161. if (tmp[name]) { //eslint-disable-line max-depth
  162. tmp[name].closeElement = el;
  163. placeholders.push(tmp[name]);
  164. delete tmp[name];
  165. }
  166. }
  167. }
  168. }
  169. return placeholders;
  170. },
  171. /**
  172. * Parse for page and replace placeholders
  173. * @param {Object} placeholder
  174. * @param {Object} html
  175. * @protected
  176. */
  177. _replacePlaceholder: function (placeholder, html) {
  178. var startReplacing = false,
  179. prevSibling = null,
  180. parent, contents, yy, len, element;
  181. if (!placeholder || !html) {
  182. return;
  183. }
  184. parent = $(placeholder.openElement).parent();
  185. contents = parent.contents();
  186. for (yy = 0, len = contents.length; yy < len; yy++) {
  187. element = contents[yy];
  188. if (element == placeholder.openElement) { //eslint-disable-line eqeqeq
  189. startReplacing = true;
  190. }
  191. if (startReplacing) {
  192. $(element).remove();
  193. } else if (element.nodeType != 8) { //eslint-disable-line eqeqeq
  194. //due to comment tag doesn't have siblings we try to find it manually
  195. prevSibling = element;
  196. }
  197. if (element == placeholder.closeElement) { //eslint-disable-line eqeqeq
  198. break;
  199. }
  200. }
  201. if (prevSibling) {
  202. $(prevSibling).after(html);
  203. } else {
  204. $(parent).prepend(html);
  205. }
  206. // trigger event to use mage-data-init attribute
  207. $(parent).trigger('contentUpdated');
  208. },
  209. /**
  210. * AJAX helper
  211. * @param {Object} placeholders
  212. * @param {String} version
  213. * @private
  214. */
  215. _ajax: function (placeholders, version) {
  216. var ii,
  217. data = {
  218. blocks: [],
  219. handles: this.options.handles,
  220. originalRequest: this.options.originalRequest,
  221. version: version
  222. };
  223. for (ii = 0; ii < placeholders.length; ii++) {
  224. data.blocks.push(placeholders[ii].name);
  225. }
  226. data.blocks = JSON.stringify(data.blocks.sort());
  227. data.handles = JSON.stringify(data.handles);
  228. data.originalRequest = JSON.stringify(data.originalRequest);
  229. $.ajax({
  230. url: this.options.url,
  231. data: data,
  232. type: 'GET',
  233. cache: true,
  234. dataType: 'json',
  235. context: this,
  236. /**
  237. * Response handler
  238. * @param {Object} response
  239. */
  240. success: function (response) {
  241. var placeholder, i;
  242. for (i = 0; i < placeholders.length; i++) {
  243. placeholder = placeholders[i];
  244. if (response.hasOwnProperty(placeholder.name)) {
  245. this._replacePlaceholder(placeholder, response[placeholder.name]);
  246. }
  247. }
  248. }
  249. });
  250. }
  251. });
  252. domReady(function () {
  253. $('body')
  254. .formKey();
  255. });
  256. return {
  257. 'pageCache': $.mage.pageCache,
  258. 'formKey': $.mage.formKey
  259. };
  260. });