yii.js 6.7 KB
Newer Older
Qiang Xue committed
1 2 3 4 5 6 7 8 9
/**
 * Yii JavaScript module.
 *
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
Qiang Xue committed
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

/**
 * yii is the root module for all Yii JavaScript modules.
 * It implements a mechanism of organizing JavaScript code in modules through the function "yii.initModule()".
 *
 * Each module should be named as "x.y.z", where "x" stands for the root module (for the Yii core code, this is "yii").
 *
 * A module may be structured as follows:
 *
 * ~~~
 * yii.sample = (function($) {
 *     var pub = {
 *         // whether this module is currently active. If false, init() will not be called for this module
 *         // it will also not be called for all its child modules. If this property is undefined, it means true.
 *         isActive: true,
 *         init: function() {
 *             // ... module initialization code go here ...
 *         },
 *
 *         // ... other public functions and properties go here ...
 *     };
 *
 *     // ... private functions and properties go here ...
 *
 *     return pub;
Qiang Xue committed
35
 * })(jQuery);
Qiang Xue committed
36 37 38 39
 * ~~~
 *
 * Using this structure, you can define public and private functions/properties for a module.
 * Private functions/properties are only visible within the module, while public functions/properties
40
 * may be accessed outside of the module. For example, you can access "yii.sample.isActive".
Qiang Xue committed
41 42 43
 *
 * You must call "yii.initModule()" once for the root module of all your modules.
 */
Qiang Xue committed
44 45
yii = (function ($) {
	var pub = {
46
		/**
47
		 * The selector for clickable elements that need to support confirmation and form submission.
48
		 */
49 50 51 52 53
		clickableSelector: 'a, button, input[type="submit"], input[type="button"], input[type="reset"], input[type="image"]',
		/**
		 * The selector for changeable elements that need to support confirmation and form submission.
		 */
		changeableSelector: 'select, input, textarea',
54

55 56 57
		/**
		 * @return string|undefined the CSRF variable name. Undefined is returned is CSRF validation is not enabled.
		 */
58
		getCsrfVar: function () {
Qiang Xue committed
59
			return $('meta[name=csrf-var]').prop('content');
60 61 62 63 64
		},

		/**
		 * @return string|undefined the CSRF token. Undefined is returned is CSRF validation is not enabled.
		 */
65
		getCsrfToken: function () {
Qiang Xue committed
66
			return $('meta[name=csrf-token]').prop('content');
67
		},
68

69 70 71 72 73 74 75 76 77 78 79 80 81
		/**
		 * Displays a confirmation dialog.
		 * The default implementation simply displays a js confirmation dialog.
		 * You may override this by setting `yii.confirm`.
		 * @param message the confirmation message.
		 * @return boolean whether the user confirms with the message in the dialog
		 */
		confirm: function (message) {
			return confirm(message);
		},

		/**
		 * Returns a value indicating whether to allow executing the action defined for the specified element.
82 83 84
		 * This method recognizes the `data-confirm` attribute of the element and uses it
		 * as the message in a confirmation dialog. The method will return true if this special attribute
		 * is not defined or if the user confirms the message.
85 86 87 88 89
		 * @param $e the jQuery representation of the element
		 * @return boolean whether to allow executing the action defined for the specified element.
		 */
		allowAction: function ($e) {
			var message = $e.data('confirm');
90
			return message === undefined || pub.confirm(message);
91 92 93
		},

		/**
94 95 96 97 98 99 100 101 102 103
		 * Handles the action triggered by user.
		 * This method recognizes the `data-method` attribute of the element. If the attribute exists,
		 * the method will submit the form containing this element. If there is no containing form, a form
		 * will be created and submitted using the method given by this attribute value (e.g. "post", "put").
		 * For hyperlinks, the form action will take the value of the "href" attribute of the link.
		 * For other elements, either the containing form action or the current page URL will be used
		 * as the form action URL.
		 *
		 * If the `data-method` attribute is not defined, the default element action will be performed.
		 *
104
		 * @param $e the jQuery representation of the element
105
		 * @return boolean whether to execute the default action for the element.
106
		 */
107
		handleAction: function ($e) {
108 109
			var method = $e.data('method');
			if (method === undefined) {
110
				return true;
111 112 113
			}

			var $form = $e.closest('form');
114 115 116 117 118
			var newForm = !$form.length;
			if (newForm) {
				var action = $e.prop('href');
				if (!action || !action.match(/(^\/|:\/\/)/)) {
					action = window.location.href;
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
				}
				$form = $('<form method="' + method + '" action="' + action + '"></form>');
				var target = $e.prop('target');
				if (target) {
					$form.attr('target', target);
				}
				if (!method.match(/(get|post)/i)) {
					$form.append('<input name="_method" value="' + method + '" type="hidden">');
				}
				var csrfVar = pub.getCsrfVar();
				if (csrfVar) {
					$form.append('<input name="' + csrfVar + '" value="' + pub.getCsrfToken() + '" type="hidden">');
				}
				$form.hide().appendTo('body');
			}

			var activeFormData = $form.data('yiiActiveForm');
			if (activeFormData) {
				// remember who triggers the form submission. This is used by yii.activeForm.js
				activeFormData.submitObject = $e;
			}

			$form.trigger('submit');
142 143 144 145 146 147

			if (newForm) {
				$form.remove();
			}

			return false;
148 149
		},

Qiang Xue committed
150 151 152 153 154 155 156 157 158 159 160
		initModule: function (module) {
			if (module.isActive === undefined || module.isActive) {
				if ($.isFunction(module.init)) {
					module.init();
				}
				$.each(module, function () {
					if ($.isPlainObject(this)) {
						pub.initModule(this);
					}
				});
			}
161 162 163 164 165
		},

		init: function () {
			var $document = $(document);

Qiang Xue committed
166
			// automatically send CSRF token for all AJAX requests
167 168
			$.ajaxPrefilter(function (options, originalOptions, xhr) {
				if (!options.crossDomain && pub.getCsrfVar()) {
Qiang Xue committed
169
					xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());
170 171 172
				}
			});

Qiang Xue committed
173 174 175 176 177 178 179 180 181
			// handle AJAX redirection
			$document.ajaxComplete(function (event, xhr, settings) {
				var url = xhr.getResponseHeader('X-Redirect');
				if (url) {
					window.location = url;
				}
			});

			// handle data-confirm and data-method for clickable elements
182
			$document.on('click.yii', pub.clickableSelector, function (event) {
183
				var $this = $(this);
184 185 186 187
				if (pub.allowAction($this)) {
					return pub.handleAction($this);
				} else {
					event.stopImmediatePropagation();
188
					return false;
189 190
				}
			});
Qiang Xue committed
191 192

			// handle data-confirm and data-method for changeable elements
193 194 195 196
			$document.on('change.yii', pub.changeableSelector, function (event) {
				var $this = $(this);
				if (pub.allowAction($this)) {
					return pub.handleAction($this);
197
				} else {
198 199
					event.stopImmediatePropagation();
					return false;
200 201
				}
			});
Qiang Xue committed
202
		}
Qiang Xue committed
203
	};
Qiang Xue committed
204
	return pub;
Qiang Xue committed
205
})(jQuery);
Qiang Xue committed
206

Qiang Xue committed
207 208
jQuery(document).ready(function () {
	yii.initModule(yii);
Qiang Xue committed
209
});