lib/amd/src/prefetch.js

  1. // This file is part of Moodle - http://moodle.org/
  2. //
  3. // Moodle is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // Moodle is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  15. /**
  16. * Prefetch module to help lazily load content for use on the current page.
  17. *
  18. * @module core/prefetch
  19. * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
  20. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  21. *
  22. * @example <caption>Pre-fetching a set of strings to use later</caption>
  23. *
  24. * import prefetch from 'core/prefetch';
  25. *
  26. * // A single string prefetch.
  27. * prefetch.prefetchString('error', 'cannotfindteacher');
  28. *
  29. * // Prefetch multiple strings in the same component.
  30. * prefetch.prefetchStrings('core', [
  31. * 'yes',
  32. * 'no',
  33. * ]);
  34. *
  35. * // Use the strings.
  36. * import {get_string as getString, get_strings as getStrings} from 'core/str';
  37. * getString('cannotfindteacher', 'error')
  38. * .then(str => {
  39. * window.console.log(str); // Cannot find teacher
  40. * })
  41. * .catch();
  42. * getStrings([
  43. * {
  44. * key: 'cannotfindteacher',
  45. * component: 'error',
  46. * },
  47. * {
  48. * key: 'yes',
  49. * component: 'core',
  50. * },
  51. * {
  52. * key: 'no',
  53. * component: 'core',
  54. * },
  55. * ])
  56. * .then((cannotFindTeacher, yes, no) => {
  57. * window.console.log(cannotFindTeacher); // Cannot find teacher
  58. * window.console.log(yes); // Yes
  59. * window.console.log(no); // No
  60. * })
  61. * .catch();
  62. */
  63. import Config from 'core/config';
  64. // Keep track of whether the initial prefetch has occurred.
  65. let initialPrefetchComplete = false;
  66. // Prefetch templates.
  67. let templateList = [];
  68. // Prefetch strings.
  69. let stringList = {};
  70. let prefetchTimer;
  71. /**
  72. * Fetch all queued items in the queue.
  73. *
  74. * Should only be called via processQueue.
  75. * @private
  76. */
  77. const fetchQueue = () => {
  78. // Prefetch templates.
  79. if (templateList) {
  80. const templatesToLoad = templateList.slice();
  81. templateList = [];
  82. import('core/templates')
  83. .then(Templates => Templates.prefetchTemplates(templatesToLoad))
  84. .catch();
  85. }
  86. // Prefetch strings.
  87. const mappedStringsToFetch = stringList;
  88. stringList = {};
  89. const stringsToFetch = [];
  90. Object.keys(mappedStringsToFetch).forEach(component => {
  91. stringsToFetch.push(...mappedStringsToFetch[component].map(key => {
  92. return {component, key};
  93. }));
  94. });
  95. if (stringsToFetch) {
  96. import('core/str')
  97. .then(Str => Str.get_strings(stringsToFetch))
  98. .catch();
  99. }
  100. };
  101. /**
  102. * Process the prefetch queues as required.
  103. *
  104. * The initial call will queue the first fetch after a delay.
  105. * Subsequent fetches are immediate.
  106. *
  107. * @private
  108. */
  109. const processQueue = () => {
  110. if (prefetchTimer) {
  111. // There is a live prefetch timer. The initial prefetch has been scheduled but is not complete.
  112. return;
  113. }
  114. // The initial prefetch has compelted. Just queue as normal.
  115. if (initialPrefetchComplete) {
  116. fetchQueue();
  117. return;
  118. }
  119. // Queue the initial prefetch in a short while.
  120. prefetchTimer = setTimeout(() => {
  121. initialPrefetchComplete = true;
  122. prefetchTimer = null;
  123. // Ensure that the icon system is loaded.
  124. // This can be quite slow and delay UI interactions if it is loaded on demand.
  125. import(Config.iconsystemmodule)
  126. .then(IconSystem => {
  127. const iconSystem = new IconSystem();
  128. prefetchTemplate(iconSystem.getTemplateName());
  129. return iconSystem;
  130. })
  131. .then(iconSystem => {
  132. fetchQueue();
  133. iconSystem.init();
  134. return;
  135. })
  136. .catch();
  137. }, 500);
  138. };
  139. /**
  140. * Add a set of templates to the prefetch queue.
  141. *
  142. * @param {Array} templatesNames A list of the template names to fetch
  143. * @static
  144. */
  145. const prefetchTemplates = templatesNames => {
  146. templateList = templateList.concat(templatesNames);
  147. processQueue();
  148. };
  149. /**
  150. * Add a single template to the prefetch queue.
  151. *
  152. * @param {String} templateName The template names to fetch
  153. * @static
  154. */
  155. const prefetchTemplate = templateName => {
  156. prefetchTemplates([templateName]);
  157. };
  158. /**
  159. * Add a set of strings from the same component to the prefetch queue.
  160. *
  161. * @param {String} component The component that all of the strings belongs to
  162. * @param {String[]} keys An array of string identifiers.
  163. * @static
  164. */
  165. const prefetchStrings = (component, keys) => {
  166. if (!stringList[component]) {
  167. stringList[component] = [];
  168. }
  169. stringList[component] = stringList[component].concat(keys);
  170. processQueue();
  171. };
  172. /**
  173. * Add a single string to the prefetch queue.
  174. *
  175. * @param {String} component The component that the string belongs to
  176. * @param {String} key The string identifier
  177. * @static
  178. */
  179. const prefetchString = (component, key) => {
  180. if (!stringList[component]) {
  181. stringList[component] = [];
  182. }
  183. stringList[component].push(key);
  184. processQueue();
  185. };
  186. // Prefetch some commonly-used templates.
  187. prefetchTemplates([].concat(
  188. ['core/loading'],
  189. ['core/modal'],
  190. ['core/modal_backdrop'],
  191. ));
  192. // And some commonly used strings.
  193. prefetchStrings('core', [
  194. 'cancel',
  195. 'closebuttontitle',
  196. 'loading',
  197. 'savechanges',
  198. ]);
  199. prefetchStrings('core_form', [
  200. 'showless',
  201. 'showmore',
  202. ]);
  203. export default {
  204. prefetchTemplate,
  205. prefetchTemplates,
  206. prefetchString,
  207. prefetchStrings,
  208. };