Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

349 lines
13KB

  1. /**
  2. * Yii JavaScript module.
  3. *
  4. * @link http://www.yiiframework.com/
  5. * @copyright Copyright (c) 2008 Yii Software LLC
  6. * @license http://www.yiiframework.com/license/
  7. * @author Qiang Xue <qiang.xue@gmail.com>
  8. * @since 2.0
  9. */
  10. /**
  11. * yii is the root module for all Yii JavaScript modules.
  12. * It implements a mechanism of organizing JavaScript code in modules through the function "yii.initModule()".
  13. *
  14. * Each module should be named as "x.y.z", where "x" stands for the root module (for the Yii core code, this is "yii").
  15. *
  16. * A module may be structured as follows:
  17. *
  18. * ~~~
  19. * yii.sample = (function($) {
  20. * var pub = {
  21. * // whether this module is currently active. If false, init() will not be called for this module
  22. * // it will also not be called for all its child modules. If this property is undefined, it means true.
  23. * isActive: true,
  24. * init: function() {
  25. * // ... module initialization code go here ...
  26. * },
  27. *
  28. * // ... other public functions and properties go here ...
  29. * };
  30. *
  31. * // ... private functions and properties go here ...
  32. *
  33. * return pub;
  34. * })(jQuery);
  35. * ~~~
  36. *
  37. * Using this structure, you can define public and private functions/properties for a module.
  38. * Private functions/properties are only visible within the module, while public functions/properties
  39. * may be accessed outside of the module. For example, you can access "yii.sample.isActive".
  40. *
  41. * You must call "yii.initModule()" once for the root module of all your modules.
  42. */
  43. yii = (function ($) {
  44. var pub = {
  45. /**
  46. * List of JS or CSS URLs that can be loaded multiple times via AJAX requests. Each script can be represented
  47. * as either an absolute URL or a relative one.
  48. */
  49. reloadableScripts: [],
  50. /**
  51. * The selector for clickable elements that need to support confirmation and form submission.
  52. */
  53. clickableSelector: 'a, button, input[type="submit"], input[type="button"], input[type="reset"], input[type="image"]',
  54. /**
  55. * The selector for changeable elements that need to support confirmation and form submission.
  56. */
  57. changeableSelector: 'select, input, textarea',
  58. /**
  59. * @return string|undefined the CSRF parameter name. Undefined is returned if CSRF validation is not enabled.
  60. */
  61. getCsrfParam: function () {
  62. return $('meta[name=csrf-param]').prop('content');
  63. },
  64. /**
  65. * @return string|undefined the CSRF token. Undefined is returned if CSRF validation is not enabled.
  66. */
  67. getCsrfToken: function () {
  68. return $('meta[name=csrf-token]').prop('content');
  69. },
  70. /**
  71. * Sets the CSRF token in the meta elements.
  72. * This method is provided so that you can update the CSRF token with the latest one you obtain from the server.
  73. * @param name the CSRF token name
  74. * @param value the CSRF token value
  75. */
  76. setCsrfToken: function (name, value) {
  77. $('meta[name=csrf-param]').prop('content', name);
  78. $('meta[name=csrf-token]').prop('content', value)
  79. },
  80. /**
  81. * Updates all form CSRF input fields with the latest CSRF token.
  82. * This method is provided to avoid cached forms containing outdated CSRF tokens.
  83. */
  84. refreshCsrfToken: function () {
  85. var token = pub.getCsrfToken();
  86. if (token) {
  87. $('form input[name="' + pub.getCsrfParam() + '"]').val(token);
  88. }
  89. },
  90. /**
  91. * Displays a confirmation dialog.
  92. * The default implementation simply displays a js confirmation dialog.
  93. * You may override this by setting `yii.confirm`.
  94. * @param message the confirmation message.
  95. * @param ok a callback to be called when the user confirms the message
  96. * @param cancel a callback to be called when the user cancels the confirmation
  97. */
  98. confirm: function (message, ok, cancel) {
  99. if (confirm(message)) {
  100. !ok || ok();
  101. } else {
  102. !cancel || cancel();
  103. }
  104. },
  105. /**
  106. * Handles the action triggered by user.
  107. * This method recognizes the `data-method` attribute of the element. If the attribute exists,
  108. * the method will submit the form containing this element. If there is no containing form, a form
  109. * will be created and submitted using the method given by this attribute value (e.g. "post", "put").
  110. * For hyperlinks, the form action will take the value of the "href" attribute of the link.
  111. * For other elements, either the containing form action or the current page URL will be used
  112. * as the form action URL.
  113. *
  114. * If the `data-method` attribute is not defined, the `href` attribute (if any) of the element
  115. * will be assigned to `window.location`.
  116. *
  117. * Starting from version 2.0.3, the `data-params` attribute is also recognized when you specify
  118. * `data-method`. The value of `data-params` should be a JSON representation of the data (name-value pairs)
  119. * that should be submitted as hidden inputs. For example, you may use the following code to generate
  120. * such a link:
  121. *
  122. * ```php
  123. * use yii\helpers\Html;
  124. * use yii\helpers\Json;
  125. *
  126. * echo Html::a('submit', ['site/foobar'], [
  127. * 'data' => [
  128. * 'method' => 'post',
  129. * 'params' => [
  130. * 'name1' => 'value1',
  131. * 'name2' => 'value2',
  132. * ],
  133. * ],
  134. * ];
  135. * ```
  136. *
  137. * @param $e the jQuery representation of the element
  138. */
  139. handleAction: function ($e) {
  140. var method = $e.data('method'),
  141. $form = $e.closest('form'),
  142. action = $e.attr('href'),
  143. params = $e.data('params');
  144. if (method === undefined) {
  145. if (action && action != '#') {
  146. window.location = action;
  147. } else if ($e.is(':submit') && $form.length) {
  148. $form.trigger('submit');
  149. }
  150. return;
  151. }
  152. var newForm = !$form.length;
  153. if (newForm) {
  154. if (!action || !action.match(/(^\/|:\/\/)/)) {
  155. action = window.location.href;
  156. }
  157. $form = $('<form method="' + method + '"></form>');
  158. $form.prop('action', action);
  159. var target = $e.prop('target');
  160. if (target) {
  161. $form.attr('target', target);
  162. }
  163. if (!method.match(/(get|post)/i)) {
  164. $form.append('<input name="_method" value="' + method + '" type="hidden">');
  165. method = 'POST';
  166. }
  167. if (!method.match(/(get|head|options)/i)) {
  168. var csrfParam = pub.getCsrfParam();
  169. if (csrfParam) {
  170. $form.append('<input name="' + csrfParam + '" value="' + pub.getCsrfToken() + '" type="hidden">');
  171. }
  172. }
  173. $form.hide().appendTo('body');
  174. }
  175. var activeFormData = $form.data('yiiActiveForm');
  176. if (activeFormData) {
  177. // remember who triggers the form submission. This is used by yii.activeForm.js
  178. activeFormData.submitObject = $e;
  179. }
  180. // temporarily add hidden inputs according to data-params
  181. if (params && $.isPlainObject(params)) {
  182. $.each(params, function (idx, obj) {
  183. $form.append('<input name="' + idx + '" value="' + obj + '" type="hidden">');
  184. });
  185. }
  186. var oldMethod = $form.prop('method');
  187. $form.prop('method', method);
  188. var oldAction = null;
  189. if (action && action != '#') {
  190. oldAction = $form.prop('action');
  191. $form.prop('action', action);
  192. }
  193. $form.trigger('submit');
  194. if (oldAction != null) {
  195. $form.prop('action', oldAction);
  196. }
  197. $form.prop('method', oldMethod);
  198. // remove the temporarily added hidden inputs
  199. if (params && $.isPlainObject(params)) {
  200. $.each(params, function (idx, obj) {
  201. $('input[name="' + idx + '"]', $form).remove();
  202. });
  203. }
  204. if (newForm) {
  205. $form.remove();
  206. }
  207. },
  208. getQueryParams: function (url) {
  209. var pos = url.indexOf('?');
  210. if (pos < 0) {
  211. return {};
  212. }
  213. var qs = url.substring(pos + 1).split('&');
  214. for (var i = 0, result = {}; i < qs.length; i++) {
  215. qs[i] = qs[i].split('=');
  216. result[decodeURIComponent(qs[i][0])] = decodeURIComponent(qs[i][1]);
  217. }
  218. return result;
  219. },
  220. initModule: function (module) {
  221. if (module.isActive === undefined || module.isActive) {
  222. if ($.isFunction(module.init)) {
  223. module.init();
  224. }
  225. $.each(module, function () {
  226. if ($.isPlainObject(this)) {
  227. pub.initModule(this);
  228. }
  229. });
  230. }
  231. },
  232. init: function () {
  233. initCsrfHandler();
  234. initRedirectHandler();
  235. initScriptFilter();
  236. initDataMethods();
  237. }
  238. };
  239. function initRedirectHandler() {
  240. // handle AJAX redirection
  241. $(document).ajaxComplete(function (event, xhr, settings) {
  242. var url = xhr.getResponseHeader('X-Redirect');
  243. if (url) {
  244. window.location = url;
  245. }
  246. });
  247. }
  248. function initCsrfHandler() {
  249. // automatically send CSRF token for all AJAX requests
  250. $.ajaxPrefilter(function (options, originalOptions, xhr) {
  251. if (!options.crossDomain && pub.getCsrfParam()) {
  252. xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());
  253. }
  254. });
  255. pub.refreshCsrfToken();
  256. }
  257. function initDataMethods() {
  258. var handler = function (event) {
  259. var $this = $(this),
  260. method = $this.data('method'),
  261. message = $this.data('confirm');
  262. if (method === undefined && message === undefined) {
  263. return true;
  264. }
  265. if (message !== undefined) {
  266. pub.confirm(message, function () {
  267. pub.handleAction($this);
  268. });
  269. } else {
  270. pub.handleAction($this);
  271. }
  272. event.stopImmediatePropagation();
  273. return false;
  274. };
  275. // handle data-confirm and data-method for clickable and changeable elements
  276. $(document).on('click.yii', pub.clickableSelector, handler)
  277. .on('change.yii', pub.changeableSelector, handler);
  278. }
  279. function initScriptFilter() {
  280. var hostInfo = location.protocol + '//' + location.host;
  281. var loadedScripts = $('script[src]').map(function () {
  282. return this.src.charAt(0) === '/' ? hostInfo + this.src : this.src;
  283. }).toArray();
  284. $.ajaxPrefilter('script', function (options, originalOptions, xhr) {
  285. if (options.dataType == 'jsonp') {
  286. return;
  287. }
  288. var url = options.url.charAt(0) === '/' ? hostInfo + options.url : options.url;
  289. if ($.inArray(url, loadedScripts) === -1) {
  290. loadedScripts.push(url);
  291. } else {
  292. var found = $.inArray(url, $.map(pub.reloadableScripts, function (script) {
  293. return script.charAt(0) === '/' ? hostInfo + script : script;
  294. })) !== -1;
  295. if (!found) {
  296. xhr.abort();
  297. }
  298. }
  299. });
  300. $(document).ajaxComplete(function (event, xhr, settings) {
  301. var styleSheets = [];
  302. $('link[rel=stylesheet]').each(function () {
  303. if ($.inArray(this.href, pub.reloadableScripts) !== -1) {
  304. return;
  305. }
  306. if ($.inArray(this.href, styleSheets) == -1) {
  307. styleSheets.push(this.href)
  308. } else {
  309. $(this).remove();
  310. }
  311. })
  312. });
  313. }
  314. return pub;
  315. })(jQuery);
  316. jQuery(document).ready(function () {
  317. yii.initModule(yii);
  318. });