From 44c12545fc8632d166cb8c759ef92d8c94535dc4 Mon Sep 17 00:00:00 2001 From: Rouslan Placella Date: Mon, 17 Dec 2012 14:23:31 +0000 Subject: [PATCH 1/4] Generate a single js file server-side for any number of requested files IMPLEMENTS feature request #3583340 --- js/ajax.js | 54 +++++++++++++--------------- js/get_scripts.js.php | 38 ++++++++++++++++++++ libraries/Scripts.class.php | 72 ++++++++++++++++++------------------- 3 files changed, 98 insertions(+), 66 deletions(-) create mode 100644 js/get_scripts.js.php diff --git a/js/ajax.js b/js/ajax.js index 875303d361..ba0cb090ae 100644 --- a/js/ajax.js +++ b/js/ajax.js @@ -255,7 +255,7 @@ var AJAX = { $('#selflink > a').attr('href', data._selflink); } if (data._scripts) { - AJAX.scriptHandler.load(data._scripts, 1); + AJAX.scriptHandler.load(data._scripts); } if (data._selflink && data._scripts && data._menuHash && data._params) { AJAX.cache.add( @@ -329,46 +329,41 @@ var AJAX = { return this; }, /** - * Queues up an array of files to be downloaded + * Download a list of js files in one request * * @param array files An array of filenames and flags * * @return void */ - load: function (files, reset) { - if (reset) { - this._scriptsToBeLoaded = []; - this._scriptsToBeFired = []; - } + load: function (files) { + var self = this; + self._scriptsToBeLoaded = []; + self._scriptsToBeFired = []; for (var i in files) { - this._scriptsToBeLoaded.push(files[i].name); + self._scriptsToBeLoaded.push(files[i].name); if (files[i].fire) { - this._scriptsToBeFired.push(files[i].name); + self._scriptsToBeFired.push(files[i].name); } } - this.callback(); - }, - /** - * Called whenever a file is loaded. - * Will queue up another file, or call done(); - * - * @return void - */ - callback: function () { - var scripts = this._scriptsToBeLoaded; - if (scripts.length > 0) { - var script = scripts.shift(); - if ($.inArray(script, this._scripts) == -1) { + // Generate a request string + var request = []; + var needRequest = false; + for (var index in self._scriptsToBeLoaded) { + var script = self._scriptsToBeLoaded[index]; + // Only for scripts that we don't already have + if ($.inArray(script, self._scripts) == -1) { + needRequest = true; this.add(script); - var self = this; - $.getScript('js/' + script, function () { - self.callback(); - }); - } else { - this.callback(); + request.push("scripts[]=" + script); } + } + // Download the composite js file, if necessary + if (needRequest) { + $.getScript("js/get_scripts.js.php?" + request.join("&"), function () { + self.done(); + }); } else { - this.done(); + self.done(); } }, /** @@ -386,7 +381,6 @@ var AJAX = { * Fires all the teardown event handlers for the current page * and rebinds all forms and links to the request handler * - * @param object ctx Context for the callback * @param function callback The callback to call after resetting * * @return void diff --git a/js/get_scripts.js.php b/js/get_scripts.js.php new file mode 100644 index 0000000000..d10532fc67 --- /dev/null +++ b/js/get_scripts.js.php @@ -0,0 +1,38 @@ + $filename) { + if (! preg_match("@^\.+$@", $filename) + && preg_match("@^[\w\.-]+$@", $filename) + ) { + // Disallow "." and ".." alone + // Allow alphanumeric, "." and "-" chars only + $script_name .= "/" . $filename; + } + } + // Output file contents + if (preg_match("@\.js$@", $script_name) && is_readable($script_name)) { + readfile($script_name); + echo ";\n\n"; + } + } +} + +?> diff --git a/libraries/Scripts.class.php b/libraries/Scripts.class.php index 7b09b197de..20b9f196c5 100644 --- a/libraries/Scripts.class.php +++ b/libraries/Scripts.class.php @@ -44,32 +44,38 @@ class PMA_Scripts /** * Returns HTML code to include javascript file. * - * @param string $url Location of javascript, relative to js/ folder. - * @param int $timestamp The date when the file was last modified - * @param string $ie_conditional true - wrap with IE conditional comment - * 'lt 9' etc. - wrap for specific IE version + * @param array $files The list of js file to include * * @return string HTML code for javascript inclusion. */ - private function _includeFile($url, $timestamp = null, $ie_conditional = false) + private function _includeFiles($files) { - $include = ''; - if ($ie_conditional !== false) { - if ($ie_conditional === true) { - $include .= '' . "\n"; - } - return $include; + $static_scripts = sprintf( + "", + implode("&", $params) + ); + return $static_scripts . $dynamic_scripts; } /** @@ -99,14 +105,9 @@ class PMA_Scripts $hash = md5($filename); if (empty($this->_files[$hash])) { $has_onload = $this->_eventBlacklist($filename); - $timestamp = null; - if (strpos($filename, '?') === false) { - $timestamp = filemtime('js/' . $filename); - } $this->_files[$hash] = array( 'has_onload' => $has_onload, 'filename' => $filename, - 'timestamp' => $timestamp, 'conditional_ie' => $conditional_ie ); } @@ -175,11 +176,13 @@ class PMA_Scripts { $retval = array(); foreach ($this->_files as $file) { - if (! $file['conditional_ie'] || PMA_USR_BROWSER_AGENT == 'IE') { - $retval[] = array( - 'name' => $file['filename'], - 'fire' => $file['has_onload'] - ); + if (strpos($file['filename'], "?") === false) { + if (! $file['conditional_ie'] || PMA_USR_BROWSER_AGENT == 'IE') { + $retval[] = array( + 'name' => $file['filename'], + 'fire' => $file['has_onload'] + ); + } } } return $retval; @@ -194,13 +197,10 @@ class PMA_Scripts { $retval = ''; - foreach ($this->_files as $file) { - $retval .= $this->_includeFile( - 'js/' . $file['filename'], - $file['timestamp'], - $file['conditional_ie'] - ); - } + $retval .= $this->_includeFiles( + $this->_files + ); + $code = 'AJAX.scriptHandler'; foreach ($this->_files as $file) { $code .= sprintf( From 9f8de953167af9767fb92977d9cbe02f0e43328b Mon Sep 17 00:00:00 2001 From: Rouslan Placella Date: Tue, 18 Dec 2012 15:11:11 +0000 Subject: [PATCH 2/4] Fixed path for Windows machines --- js/get_scripts.js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/get_scripts.js.php b/js/get_scripts.js.php index d10532fc67..564fda7dd2 100644 --- a/js/get_scripts.js.php +++ b/js/get_scripts.js.php @@ -24,7 +24,7 @@ if (! empty($_GET['scripts']) && is_array($_GET['scripts'])) { ) { // Disallow "." and ".." alone // Allow alphanumeric, "." and "-" chars only - $script_name .= "/" . $filename; + $script_name .= DIRECTORY_SEPARATOR . $filename; } } // Output file contents From c3d8eba07de08b3258956cc1c4f1559d8bec7650 Mon Sep 17 00:00:00 2001 From: Rouslan Placella Date: Tue, 18 Dec 2012 15:22:57 +0000 Subject: [PATCH 3/4] Prevent pointless requests for js files --- libraries/Scripts.class.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/Scripts.class.php b/libraries/Scripts.class.php index 20b9f196c5..58cfbe89ce 100644 --- a/libraries/Scripts.class.php +++ b/libraries/Scripts.class.php @@ -197,9 +197,11 @@ class PMA_Scripts { $retval = ''; - $retval .= $this->_includeFiles( - $this->_files - ); + if (count($this->_files) > 0) { + $retval .= $this->_includeFiles( + $this->_files + ); + } $code = 'AJAX.scriptHandler'; foreach ($this->_files as $file) { From 360a2a64db388f251a72b737abf337b4bf3ea4af Mon Sep 17 00:00:00 2001 From: Rouslan Placella Date: Wed, 19 Dec 2012 13:09:57 +0000 Subject: [PATCH 4/4] Disable jQuery's nocache feature when fetching js scripts --- js/ajax.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/js/ajax.js b/js/ajax.js index ba0cb090ae..0ac45f9bdb 100644 --- a/js/ajax.js +++ b/js/ajax.js @@ -359,8 +359,13 @@ var AJAX = { } // Download the composite js file, if necessary if (needRequest) { - $.getScript("js/get_scripts.js.php?" + request.join("&"), function () { - self.done(); + $.ajax({ + url: "js/get_scripts.js.php?" + request.join("&"), + cache: true, + success: function () { + self.done(); + }, + dataType: "script" }); } else { self.done();