yii.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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 <[email protected]>
  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 scripts 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 variable name. Undefined is returned if CSRF validation is not enabled.
  60. */
  61. getCsrfVar: function () {
  62. return $('meta[name=csrf-var]').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. * Displays a confirmation dialog.
  72. * The default implementation simply displays a js confirmation dialog.
  73. * You may override this by setting `yii.confirm`.
  74. * @param message the confirmation message.
  75. * @return boolean whether the user confirms with the message in the dialog
  76. */
  77. confirm: function (message) {
  78. return confirm(message);
  79. },
  80. /**
  81. * Returns a value indicating whether to allow executing the action defined for the specified element.
  82. * This method recognizes the `data-confirm` attribute of the element and uses it
  83. * as the message in a confirmation dialog. The method will return true if this special attribute
  84. * is not defined or if the user confirms the message.
  85. * @param $e the jQuery representation of the element
  86. * @return boolean whether to allow executing the action defined for the specified element.
  87. */
  88. allowAction: function ($e) {
  89. var message = $e.data('confirm');
  90. return message === undefined || pub.confirm(message);
  91. },
  92. /**
  93. * Handles the action triggered by user.
  94. * This method recognizes the `data-method` attribute of the element. If the attribute exists,
  95. * the method will submit the form containing this element. If there is no containing form, a form
  96. * will be created and submitted using the method given by this attribute value (e.g. "post", "put").
  97. * For hyperlinks, the form action will take the value of the "href" attribute of the link.
  98. * For other elements, either the containing form action or the current page URL will be used
  99. * as the form action URL.
  100. *
  101. * If the `data-method` attribute is not defined, the default element action will be performed.
  102. *
  103. * @param $e the jQuery representation of the element
  104. * @return boolean whether to execute the default action for the element.
  105. */
  106. handleAction: function ($e) {
  107. var method = $e.data('method');
  108. if (method === undefined) {
  109. return true;
  110. }
  111. var $form = $e.closest('form');
  112. var newForm = !$form.length;
  113. if (newForm) {
  114. var action = $e.prop('href');
  115. if (!action || !action.match(/(^\/|:\/\/)/)) {
  116. action = window.location.href;
  117. }
  118. $form = $('<form method="' + method + '" action="' + action + '"></form>');
  119. var target = $e.prop('target');
  120. if (target) {
  121. $form.attr('target', target);
  122. }
  123. if (!method.match(/(get|post)/i)) {
  124. $form.append('<input name="_method" value="' + method + '" type="hidden">');
  125. }
  126. var csrfVar = pub.getCsrfVar();
  127. if (csrfVar) {
  128. $form.append('<input name="' + csrfVar + '" value="' + pub.getCsrfToken() + '" type="hidden">');
  129. }
  130. $form.hide().appendTo('body');
  131. }
  132. var activeFormData = $form.data('yiiActiveForm');
  133. if (activeFormData) {
  134. // remember who triggers the form submission. This is used by yii.activeForm.js
  135. activeFormData.submitObject = $e;
  136. }
  137. $form.trigger('submit');
  138. if (newForm) {
  139. $form.remove();
  140. }
  141. return false;
  142. },
  143. initModule: function (module) {
  144. if (module.isActive === undefined || module.isActive) {
  145. if ($.isFunction(module.init)) {
  146. module.init();
  147. }
  148. $.each(module, function () {
  149. if ($.isPlainObject(this)) {
  150. pub.initModule(this);
  151. }
  152. });
  153. }
  154. },
  155. init: function () {
  156. initCsrfHandler();
  157. initRedirectHandler();
  158. initScriptFilter();
  159. initDataMethods();
  160. }
  161. };
  162. function initRedirectHandler() {
  163. // handle AJAX redirection
  164. $(document).ajaxComplete(function (event, xhr, settings) {
  165. var url = xhr.getResponseHeader('X-Redirect');
  166. if (url) {
  167. window.location = url;
  168. }
  169. });
  170. }
  171. function initCsrfHandler() {
  172. // automatically send CSRF token for all AJAX requests
  173. $.ajaxPrefilter(function (options, originalOptions, xhr) {
  174. if (!options.crossDomain && pub.getCsrfVar()) {
  175. xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());
  176. }
  177. });
  178. }
  179. function initDataMethods() {
  180. var $document = $(document);
  181. // handle data-confirm and data-method for clickable elements
  182. $document.on('click.yii', pub.clickableSelector, function (event) {
  183. var $this = $(this);
  184. if (pub.allowAction($this)) {
  185. return pub.handleAction($this);
  186. } else {
  187. event.stopImmediatePropagation();
  188. return false;
  189. }
  190. });
  191. // handle data-confirm and data-method for changeable elements
  192. $document.on('change.yii', pub.changeableSelector, function (event) {
  193. var $this = $(this);
  194. if (pub.allowAction($this)) {
  195. return pub.handleAction($this);
  196. } else {
  197. event.stopImmediatePropagation();
  198. return false;
  199. }
  200. });
  201. }
  202. function initScriptFilter() {
  203. var hostInfo = location.protocol + '//' + location.host;
  204. var loadedScripts = $('script[src]').map(function () {
  205. return this.src.charAt(0) === '/' ? hostInfo + this.src : this.src;
  206. }).toArray();
  207. $.ajaxPrefilter('script', function (options, originalOptions, xhr) {
  208. var url = options.url.charAt(0) === '/' ? hostInfo + options.url : options.url;
  209. if ($.inArray(url, loadedScripts) === -1) {
  210. loadedScripts.push(url);
  211. } else {
  212. var found = $.inArray(url, $.map(pub.reloadableScripts, function (script) {
  213. return script.charAt(0) === '/' ? hostInfo + script : script;
  214. })) !== -1;
  215. if (!found) {
  216. xhr.abort();
  217. }
  218. }
  219. });
  220. }
  221. return pub;
  222. })(jQuery);
  223. jQuery(document).ready(function () {
  224. yii.initModule(yii);
  225. });