/** * 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 */ /** * 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; * })(jQuery); * ~~~ * * 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 * may be accessed outside of the module. For example, you can access "yii.sample.isActive". * * You must call "yii.initModule()" once for the root module of all your modules. */ yii = (function ($) { var pub = { /** * List of scripts that can be loaded multiple times via AJAX requests. Each script can be represented * as either an absolute URL or a relative one. */ reloadableScripts: [], /** * The selector for clickable elements that need to support confirmation and form submission. */ 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', /** * @return string|undefined the CSRF variable name. Undefined is returned is CSRF validation is not enabled. */ getCsrfVar: function () { return $('meta[name=csrf-var]').prop('content'); }, /** * @return string|undefined the CSRF token. Undefined is returned is CSRF validation is not enabled. */ getCsrfToken: function () { return $('meta[name=csrf-token]').prop('content'); }, /** * 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. * 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. * @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'); return message === undefined || pub.confirm(message); }, /** * 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. * * @param $e the jQuery representation of the element * @return boolean whether to execute the default action for the element. */ handleAction: function ($e) { var method = $e.data('method'); if (method === undefined) { return true; } var $form = $e.closest('form'); var newForm = !$form.length; if (newForm) { var action = $e.prop('href'); if (!action || !action.match(/(^\/|:\/\/)/)) { action = window.location.href; } $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'); if (newForm) { $form.remove(); } return false; }, initModule: function (module) { if (module.isActive === undefined || module.isActive) { if ($.isFunction(module.init)) { module.init(); } $.each(module, function () { if ($.isPlainObject(this)) { pub.initModule(this); } }); } }, init: function () { initCsrfHandler(); initRedirectHandler(); initScriptFilter(); initDataMethods(); } }; function initRedirectHandler() { // handle AJAX redirection $(document).ajaxComplete(function (event, xhr, settings) { var url = xhr.getResponseHeader('X-Redirect'); if (url) { window.location = url; } }); } function initCsrfHandler() { // automatically send CSRF token for all AJAX requests $.ajaxPrefilter(function (options, originalOptions, xhr) { if (!options.crossDomain && pub.getCsrfVar()) { xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken()); } }); } function initDataMethods() { var $document = $(document); // handle data-confirm and data-method for clickable elements $document.on('click.yii', pub.clickableSelector, function (event) { var $this = $(this); if (pub.allowAction($this)) { return pub.handleAction($this); } else { event.stopImmediatePropagation(); return false; } }); // handle data-confirm and data-method for changeable elements $document.on('change.yii', pub.changeableSelector, function (event) { var $this = $(this); if (pub.allowAction($this)) { return pub.handleAction($this); } else { event.stopImmediatePropagation(); return false; } }); } function initScriptFilter() { var hostInfo = location.protocol + '//' + location.host; var loadedScripts = $('script[src]').map(function () { return this.src.charAt(0) === '/' ? hostInfo + this.src : this.src; }).toArray(); $.ajaxPrefilter('script', function (options, originalOptions, xhr) { var url = options.url.charAt(0) === '/' ? hostInfo + options.url : options.url; if ($.inArray(url, loadedScripts) === -1) { loadedScripts.push(url); } else { var found = $.inArray(url, $.map(pub.reloadableScripts, function (script) { return script.charAt(0) === '/' ? hostInfo + script : script; })) !== -1; if (!found) { xhr.abort(); } } }); } return pub; })(jQuery); jQuery(document).ready(function () { yii.initModule(yii); });