From b6cc8c0066aa926c6428d7ad39242a86855ea831 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 18 Nov 2013 20:38:04 -0500 Subject: [PATCH] Fixes #1149: Filter out already included javascript files requested via ajax. --- framework/yii/assets/yii.js | 112 +++++++++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 37 deletions(-) diff --git a/framework/yii/assets/yii.js b/framework/yii/assets/yii.js index add3a02..dc1d859 100644 --- a/framework/yii/assets/yii.js +++ b/framework/yii/assets/yii.js @@ -44,6 +44,11 @@ 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"]', @@ -161,46 +166,79 @@ yii = (function ($) { }, init: function () { - var $document = $(document); + initCsrfHandler(); + initRedirectHandler(); + initScriptFilter(); + initDataMethods(); + } + }; - // 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 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').filter(function () { + return this.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 (loadedScripts.indexOf(url) < 0) { + loadedScripts.push(url); + } else { + var found = pub.reloadableScripts.map(function () { + return this.charAt(0) === '/' ? hostInfo + this : this; + }).indexOf(url) >= 0; + if (!found) { + xhr.abort(); } - }); + } + }); + } - // 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 - $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; - } - }); - } - }; return pub; })(jQuery);