* added in language mapping for commons upload form language hack (was supposed to...
[lhc/web/wiklou.git] / js2 / mwEmbed / jsScriptLoader.php
index 3a33a3d..580bf76 100644 (file)
  * This core jsScriptLoader class provides the script loader functionality
  * @file
  */
-// check if we are being invoked in MediaWiki context or stand alone usage:
-if ( !defined( 'MEDIAWIKI' ) ){
-       // load noMediaWiki helper:
-       require_once( realpath( dirname( __FILE__ ) ) . '/php/noMediaWikiConfig.php' );
 
-       // run the main action:
+
+//Setup the script local script cache directory (has to be hard coded rather than config based for fast non-mediawiki config hits
+$wgScriptCacheDirectory = realpath( dirname( __FILE__ ) ) . '/php/script-cache';
+
+// Check if we are being invoked in a MediaWiki context or stand alone usage:
+if ( !defined( 'MEDIAWIKI' ) && !defined( 'MW_CACHE_SCRIPT_CHECK' ) ){
+       // Load noMediaWiki helper for quick cache result
        $myScriptLoader = new jsScriptLoader();
-       // preset request values via normal $_GET operation:
+       if( $myScriptLoader->outputFromCache() )
+               exit();
+       //Else load up all the config and do normal stand alone ScriptLoader process:
+       require_once( realpath( dirname( __FILE__ ) ) . '/php/noMediaWikiConfig.php' );
        $myScriptLoader->doScriptLoader();
-} else {
-       $wgExtensionMessagesFiles['mwEmbed'] = realpath( dirname( __FILE__ ) ) . '/php/mwEmbed.i18n.php';
 }
 
-// setup page output hook
 class jsScriptLoader {
        var $jsFileList = array();
+       var $langCode = '';
        var $jsout = '';
        var $rKey = ''; // the request key
        var $error_msg = '';
        var $debug = false;
-       var $jsvarurl = false; // if we should include generated js (special class '-')
+       var $jsvarurl = false; // whether we should include generated JS (special class '-')
        var $doProcReqFlag = true;
 
-       function doScriptLoader(){
-               global  $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgEnableScriptLoaderJsFile, $IP,
-                               $wgEnableScriptMinify, $wgUseFileCache;
-
-               // process the request
-               $this->procRequestVars();
-
-               // if cache is on and file is present grab it from there:
-               if( $wgUseFileCache && !$this->debug ) {
-                       // setup file cache obj:
-                       $this->sFileCache = new simpleFileCache( $this->rKey );
-                       if( $this->sFileCache->isFileCached() ){
-                               // just output headers so we can use php "efficient" readfile
-                               $this->outputJsHeaders();
-                               $this->sFileCache->outputFromFileCache();
-                               die();
-                       }
+       function outputFromCache(){
+               // Process the request
+               $this->rKey = $this->preProcRequestVars();
+               // Setup file cache object
+               $this->sFileCache = new simpleFileCache( $this->rKey );
+               if ( $this->sFileCache->isFileCached() ) {
+                       // Just output headers so we can use PHP's @readfile::
+                       $this->outputJsHeaders();
+                       $this->sFileCache->outputFromFileCache();
+                       return true;
                }
+               return false;
+       }
 
-               // setup script loader header info
-               $this->jsout .= 'var mwSlScript = "' . $_SERVER['SCRIPT_NAME'] . '";' . "\n";
-               $this->jsout .= 'var mwSlGenISODate = "' . date( 'c' ) . '";'  ."\n";
-               $this->jsout .= 'var mwSlURID = "' . $this->urid . '";'  ."\n";
-               // Build the output:
-               // swap in the appropriate language per js_file
-               foreach( $this->jsFileList as $classKey => $file_name ){
-                       // special case: - title classes:
-                       if( substr( $classKey, 0, 3 ) == 'WT:' ){
-                               // get just the tile part:
-                               $title_block = substr( $classKey, 3 );
-                               if( $title_block[0] == '-' && strpos( $title_block, '|' ) !== false ){
-                                       // special case of "-" title with skin
-                                       $parts = explode( '|', $title_block );
-                                       $title = array_shift( $parts );
-                                       foreach( $parts as $tparam ){
-                                               list( $key, $val ) = explode( '=', $tparam );
-                                               if( $key == 'useskin' ){
-                                                       $skin = $val;
-                                               }
-                                       }
-                                       // make sure the skin name is valid
-                                       $skinNames = Skin::getSkinNames();
-                                       // get the lower case skin name (array keys)
-                                       $skinNames = array_keys( $skinNames );
-                                       if( in_array( strtolower( $skin ), $skinNames ) ){
-                                               $this->jsout .= Skin::generateUserJs( $skin ) . "\n";
-                                               // success continue:
-                                               continue;
-                                       }
-                               } else {
-                                       // it's a wikiTitle append the output of the wikitext:
-                                       $t = Title::newFromText( $title_block );
-                                       $a = new Article( $t );
-                                       // only get content if the page is not empty:
-                                       if( $a->getID() !== 0 ){
-                                               $this->jsout .= $a->getContent() . "\n";
-                                       }
-                                       continue;
-                               }
-                       }
-
-                       if( trim( $file_name ) != '' ){
-                               // if in debug add a comment with the file name:
-                               if( $this->debug )
-                                       $this->jsout .= "\n/**
-* File: $file_name
-*/\n";
-                               $this->jsout .= ( $this->doProccessJsFile( $file_name ) ) . "\n";
+       function doScriptLoader() {
+               global  $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $IP,
+               $wgEnableScriptMinify, $wgUseFileCache, $wgExtensionMessagesFiles;
+
+               //load the ExtensionMessagesFiles
+               $wgExtensionMessagesFiles['mwEmbed'] = realpath( dirname( __FILE__ ) ) . '/php/languages/mwEmbed.i18n.php';
+
+               //reset the rKey:
+               $this->rKey = '';
+               //do the post proc request with configuration vars:
+               $this->postProcRequestVars();
+               //update the filename (if gzip is on)
+               $this->sFileCache->getCacheFileName();
+
+               // Setup script loader header info
+               // @@todo we might want to put these into the mw var per class request set
+               // and or include a callback to avoid pulling in old browsers that don't support
+               // the onLoad attribute for script elements.
+               $this->jsout .= 'var mwSlScript = "' .  $_SERVER['SCRIPT_NAME']  . '";' . "\n";
+               $this->jsout .= 'var mwSlGenISODate = "' . date( 'c' ) . '";'  . "\n";
+               $this->jsout .= 'var mwSlURID = "' . htmlspecialchars( $this->urid ) . '";'  . "\n";
+               $this->jsout .= 'var mwLang = "' . htmlspecialchars( $this->langCode ) . '";' . "\n";
+               // Build the output
+
+               // Swap in the appropriate language per js_file
+               foreach ( $this->jsFileList as $classKey => $file_name ) {
+                       //get the script content
+                       $jstxt = $this->getScriptText($classKey, $file_name);
+                       if( $jstxt ){
+                               $this->jsout .= $this->doProcessJs( $jstxt );
                        }
                }
-               // check if we should minify :
-               if( $wgEnableScriptMinify && !$this->debug ){
+               // Check if we should minify the whole thing:
+               if ( !$this->debug ) {
                        // do the minification and output
-                       $this->jsout = JSMin::minify( $this->jsout);
+                       $this->jsout = JSMin::minify( $this->jsout );
                }
-               // save to the file cache:
-               if( $wgUseFileCache && !$this->debug ) {
+               // Save to the file cache
+               if ( $wgUseFileCache && !$this->debug ) {
                        $status = $this->sFileCache->saveToFileCache( $this->jsout );
-                       if( $status !== true )
-                               $this->error_msg.= $status;
+                       if ( $status !== true )
+                       $this->error_msg .= $status;
                }
-               // check for error msg:
-               if( $this->error_msg != ''){
-                       echo 'alert(\'Error With ScriptLoader.php ::' . str_replace( "\n", '\'+"\n"+'."\n'", $this->error_msg ) . '\');';
+               // Check for an error msg
+               if ( $this->error_msg != '' ) {
+                       //just set the content type (don't send cache header)
+                       header( 'Content-Type: text/javascript' );
+                       echo 'alert(\'Error With ScriptLoader.php ::' . str_replace( "\n", '\'+"\n"+' . "\n'", $this->error_msg ) . '\');';
                        echo trim( $this->jsout );
                } else {
-                       // all good lets output cache forever headers:
+                       // All good, let's output "cache" headers
                        $this->outputJsWithHeaders();
                }
        }
+       function getScriptText($classKey, $file_name=''){
+               $jsout = '';
+               // Special case: title classes
+               if ( substr( $classKey, 0, 3 ) == 'WT:' ) {
+                       global $wgUser;
+                       // Get just the title part
+                       $title_block = substr( $classKey, 3 );
+                       if ( $title_block[0] == '-' && strpos( $title_block, '|' ) !== false ) {
+                               // Special case of "-" title with skin
+                               $parts = explode( '|', $title_block );
+                               $title = array_shift( $parts );
+                               foreach ( $parts as $tparam ) {
+                                       list( $key, $val ) = explode( '=', $tparam );
+                                       if ( $key == 'useskin' ) {
+                                               $skin = $val;
+                                       }
+                               }
+                               $sk = $wgUser->getSkin();
+                               // Make sure the skin name is valid
+                               $skinNames = Skin::getSkinNames();
+                               $skinNames = array_keys( $skinNames );
+                               if ( in_array( strtolower( $skin ), $skinNames ) ) {
+                                       // If in debug mode, add a comment with wiki title and rev:
+                                       if ( $this->debug )
+                                       $jsout .= "\n/**\n* GenerateUserJs: \n*/\n";
+                                       return $jsout . $sk->generateUserJs( $skin ) . "\n";
+                               }
+                       } else {
+                               // Make sure the wiki title ends with .js
+                               if ( substr( $title_block, - 3 ) != '.js' ) {
+                                       $this->error_msg .= 'WikiTitle includes should end with .js';
+                                       return false;
+                               }
+                               // It's a wiki title, append the output of the wikitext:
+                               $t = Title::newFromText( $title_block );
+                               $a = new Article( $t );
+                               // Only get the content if the page is not empty:
+                               if ( $a->getID() !== 0 ) {
+                                       // If in debug mode, add a comment with wiki title and rev:
+                                       if ( $this->debug )
+                                       $jsout .= "\n/**\n* WikiJSPage: " . htmlspecialchars( $title_block ) . " rev: " . $a->getID() . " \n*/\n";
+
+                                       return $jsout . $a->getContent() . "\n";
+                               }
+                       }
+               }else{
+                       // Dealing with files
+
+                       // Check that the filename ends with .js and does not include ../ traversing
+                       if ( substr( $file_name, - 3 ) != '.js' ) {
+                               $this->error_msg .= "\nError file name must end with .js: " . htmlspecialchars( $file_name ) . " \n ";
+                               return false;
+                       }
+                       if ( strpos( $file_name, '../' ) !== false ) {
+                               $this->error_msg .= "\nError file name must not traverse paths: " . htmlspecialchars( $file_name ) . " \n ";
+                               return false;
+                       }
+
+                       if ( trim( $file_name ) != '' ) {
+                               if ( $this->debug )
+                               $jsout .= "\n/**\n* File: " . htmlspecialchars( $file_name ) . "\n*/\n";
 
-       function outputJsHeaders(){
-               global $wgJsMimeType;
-               // output js mime type:
-               header( 'Content-type: ' . $wgJsMimeType );
+                               $jsFileStr = $this->doGetJsFile( $file_name ) . "\n";
+                               if( $jsFileStr ){
+                                       return $jsout . $jsFileStr;
+                               }else{
+                                       $this->error_msg .= "\nError could not read file: ". htmlspecialchars( $file_name )  ."\n";
+                                       return false;
+                               }
+                       }
+               }
+               //if we did not return some js
+               $this->error_msg .= "\nUnknown error\n";
+               return false;
+       }
+       function outputJsHeaders() {
+               // Output JS MIME type:
+               header( 'Content-Type: text/javascript' );
                header( 'Pragma: public' );
-               // cache forever:
-               // (the point is we never have to revalidate since we should always change the request url based on the svn or article version)
-               $one_year = 60*60*24*365;
-               header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $one_year ) . " GM" );
+               // Cache for 1 day ( we should always change the request URL
+               // based on the SVN or article version.
+               $one_day = 60 * 60 * 24;
+               header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $one_day ) . " GM" );
        }
 
-       function outputJsWithHeaders(){
+       function outputJsWithHeaders() {
                global $wgUseGzip;
                $this->outputJsHeaders();
-               if( $wgUseGzip ) {
-                       if( wfClientAcceptsGzip() ) {
+               if ( $wgUseGzip ) {
+                       if ( $this->clientAcceptsGzip() ) {
                                header( 'Content-Encoding: gzip' );
                                echo gzencode( $this->jsout );
                        } else {
@@ -143,144 +197,320 @@ class jsScriptLoader {
                }
        }
 
-       /**
-        * updates the proc Request
+       function clientAcceptsGzip() {
+               $m = array();
+               if( preg_match(
+                       '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
+               $_SERVER['HTTP_ACCEPT_ENCODING'],
+               $m ) ) {
+                       if( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) )
+                       return false;
+                       //no gzip support found
+                       return true;
+               }
+               return false;
+       }
+       /*
+        * postProcRequestVars uses globals, configuration and mediaWiki to test wiki-titles and files exist etc.
         */
-       function procRequestVars(){
-               global $wgContLanguageCode, $wgEnableScriptMinify, $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgStyleVersion;
+       function postProcRequestVars(){
+               global $wgContLanguageCode, $wgEnableScriptMinify, $wgJSAutoloadClasses,
+               $wgJSAutoloadLocalClasses, $wgStyleVersion;
 
-               // set debug flag:
-               if( ( isset( $_GET['debug'] ) && $_GET['debug'] == 'true' ) || ( isset( $wgEnableScriptDebug ) && $wgEnableScriptDebug == true ) ){
+               // Set debug flag
+               if ( ( isset( $_GET['debug'] ) && $_GET['debug'] == 'true' ) || ( isset( $wgEnableScriptDebug ) && $wgEnableScriptDebug == true ) ) {
                        $this->debug = true;
                }
 
-               // set the urid: (be sure to escape it as it goes into our js output)
-               if( isset( $_GET['urid'] ) && $_GET['urid'] !=''){
+               // Set the urid. Be sure to escape it as it goes into our JS output.
+               if ( isset( $_GET['urid'] ) && $_GET['urid'] != '' ) {
                        $this->urid = htmlspecialchars( $_GET['urid'] );
                } else {
-                       // just give it the current style sheet id:
+                       // Just give it the current style sheet ID:
                        // @@todo read the svn version number
                        $this->urid = $wgStyleVersion;
                }
 
+               //get the language code (if not provided use the "default" language
+               if ( isset( $_GET['uselang'] ) && $_GET['uselang'] != '' ) {
+                       //make sure its a valid lang code:
+                       $this->langCode = preg_replace( "/[^A-Za-z]/", '', $_GET['uselang']);
+               }else{
+                       //set english as default
+                       $this->langCode = 'en';
+               }
+               $this->langCode = self::checkForCommonsLanguageFormHack( $this->langCode );
+
                $reqClassList = false;
-               if( isset( $_GET['class'] ) && $_GET['class'] != '' ){
+               if ( isset( $_GET['class'] ) && $_GET['class'] != '' ) {
                        $reqClassList = explode( ',', $_GET['class'] );
                }
 
-               // check for the requested classes
-               if( $reqClassList ){
-                       // clean the class list and populate jsFileList
-                       foreach( $reqClassList as $reqClass ){
-                               if( trim( $reqClass ) != '' ){
-                                       // check for special case '-' class for user generated js
-                                       if( substr( $reqClass, 0, 3 ) == 'WT:' ){
-                                               $this->jsFileList[$reqClass] = true;
-                                               $this->rKey .= $reqClass;
-                                               $this->jsvarurl = true;
+               // Check for the requested classes
+               if ( $reqClassList ) {
+                       // Clean the class list and populate jsFileList
+                       foreach ( $reqClassList as $reqClass ) {
+                               if ( trim( $reqClass ) != '' ) {
+                                       if ( substr( $reqClass, 0, 3 ) == 'WT:' ) {
+                                               $doAddWT = false;
+                                               // Check for special case '-' class for user-generated JS
+                                               if( substr( $reqClass, 3, 1) == '-'){
+                                                       $doAddWT = true;
+                                               }else{
+                                                       if( strtolower( substr( $reqClass, -3) ) == '.js'){
+                                                               //make sure its a valid wikipage before doing processing
+                                                               $t = Title::newFromDBkey( substr( $reqClass, 3) );
+                                                               if( $t->exists()
+                                                               && ( $t->getNamespace() == NS_MEDIAWIKI
+                                                               || $t->getNamespace() == NS_USER ) ){
+                                                                       $doAddWT = true;
+                                                               }
+                                                       }
+                                               }
+                                               if( $doAddWT ){
+                                                       $this->jsFileList[$reqClass] = true;
+                                                       $this->rKey .= $reqClass;
+                                                       $this->jsvarurl = true;
+                                               }
                                                continue;
                                        }
 
-                                       $reqClass = ereg_replace("[^A-Za-z0-9_\-\.]", '', $reqClass );
+                                       $reqClass = preg_replace( "/[^A-Za-z0-9_\-\.]/", '', $reqClass );
 
-                                       if( isset( $wgJSAutoloadLocalClasses[$reqClass] ) ){
-                                               $this->jsFileList[$reqClass] = $wgJSAutoloadLocalClasses[$reqClass];
-                                               $this->rKey.= $reqClass;
-                                       } else if( isset( $wgJSAutoloadClasses[$reqClass] ) ) {
-                                               $this->jsFileList[$reqClass] = $wgJSAutoloadClasses[$reqClass];
-                                               $this->rKey.= $reqClass;
-                                       } else {
-                                               $this->error_msg.= 'Requested class: ' . $reqClass . ' not found' . "\n";
+                                       $jsFilePath = self::getJsPathFromClass( $reqClass );
+                                       if(!$jsFilePath){
+                                               $this->error_msg .= 'Requested class: ' . htmlspecialchars( $reqClass ) . ' not found' . "\n";
+                                       }else{
+                                               $this->jsFileList[ $reqClass ] = $jsFilePath;
+                                               $this->rKey .= $reqClass;
                                        }
                                }
                        }
                }
 
-               // check for requested files if enabled:
-               if( $wgEnableScriptLoaderJsFile ){
-                       if( isset( $_GET['files'] ) ){
-                               $reqFileList = explode( ',', isset( $_GET['files'] ) );
-                               // clean the file list and populate jsFileList
-                               foreach( $reqFileList as $reqFile ){
-                                       // no jumping dirs:
-                                       $reqFile = str_replace( '../', '', $reqFile );
-                                       // only allow alphanumeric underscores periods and ending with .js
-                                       $reqFile = ereg_replace( "[^A-Za-z0-9_\-\/\.]", '', $reqFile );
-                                       if( substr( $reqFile, -3 ) == '.js' ){
-                                               // don't add it twice:
-                                               if( !in_array( $reqFile, $jsFileList ) ) {
-                                                       $this->jsFileList[] = $IP . $reqFile;
-                                                       $this->rKey.= $reqFile;
-                                               }
-                                       } else {
-                                               $this->error_msg.= 'Not valid requsted JavaScript file' . "\n";
-                                       }
-                               }
-                       }
-               }
 
-               // add the language code to the rKey:
+               // Add the language code to the rKey:
                $this->rKey .= '_' . $wgContLanguageCode;
 
-               // add the unique rid to the rKey
+               // Add the unique rid
                $this->rKey .= $this->urid;
 
-               // add a min flag:
-               if( $wgEnableScriptMinify ){
-                       $this->rKey.= '_min';
+               // Add a minify flag
+               if ( $wgEnableScriptMinify ) {
+                       $this->rKey .= '_min';
                }
        }
+       /**
+        * Pre-process request variables ~without configuration~ or much utility function~
+        *  This is to quickly get a rKey that we can check against the cache
+        */
+       function preProcRequestVars() {
+               $rKey = '';
+               // Check for debug (won't use the cache)
+               if ( ( isset( $_GET['debug'] ) && $_GET['debug'] == 'true' ) ) {
+                       //we are going to have to run postProcRequest
+                       return false;
+               }
+
+               // Check for the urid. Be sure to escape it as it goes into our JS output.
+               if ( isset( $_GET['urid'] ) && $_GET['urid'] != '' ) {
+                       $urid = htmlspecialchars( $_GET['urid'] );
+               }else{
+                       die( 'missing urid param');
+               }
 
-       function doProccessJsFile( $file_name ){
-               global $IP, $wgEnableScriptLocalization, $IP;
+               //get the language code (if not provided use the "default" language
+               if ( isset( $_GET['uselang'] ) && $_GET['uselang'] != '' ) {
+                       //make sure its a valid lang code:
+                       $langCode = preg_replace( "/[^A-Za-z]/", '', $_GET['uselang']);
+               }else{
+                       //set english as default
+                       $langCode = 'en';
+               }
 
-               // load the file:
-               $str = @file_get_contents( "{$IP}/{$file_name}" );
+               $langCode = self::checkForCommonsLanguageFormHack( $langCode );
 
-               if( $str === false ){
-                       // @@todo check php error level (don't want to expose paths if errors are hidden)
-                       $this->error_msg.= 'Requested File: ' . htmlspecialchars( $file_name ) . ' could not be read' . "\n";
-                       return '';
+
+               $reqClassList = false;
+               if ( isset( $_GET['class'] ) && $_GET['class'] != '' ) {
+                       $reqClassList = explode( ',', $_GET['class'] );
                }
-               $this->cur_file = $file_name;
 
-               // strip out js_log debug lines not much luck with this regExp yet:
-               //if( !$this->debug )
-               //       $str = preg_replace('/\n\s*js_log\s*\([^\)]([^;]|\n])*;/', "\n", $str);
+               // Check for the requested classes
+               if ( count( $reqClassList ) > 0 ) {
+                       // Clean the class list and populate jsFileList
+                       foreach (  $reqClassList as $reqClass ) {
+                               //do some simple checks:
+                               if ( trim( $reqClass ) != '' ){
+                                       if( substr( $reqClass, 0, 3 ) == 'WT:'  && strtolower( substr( $reqClass, -3) ) == '.js' ){
+                                               //wiki page requests (must end with .js):
+                                               $rKey .= $reqClass;
+                                       }else if( substr( $reqClass, 0, 3 ) != 'WT:' ){
+                                               //normal class requests:
+                                               $reqClass = preg_replace( "/[^A-Za-z0-9_\-\.]/", '', $reqClass );
+                                               $rKey .= $reqClass;
+                                       }else{
+                                               //not a valid class don't add it
+                                       }
+                               }
+                       }
+               }
+               // Add the language code to the rKey:
+               $rKey .= '_' . $langCode;
 
-               // do language swap
-               if( $wgEnableScriptLocalization )
-                       $str = preg_replace_callback(
-                               '/loadGM\s*\(\s*{(.*)}\s*\)\s*/siU',    // @@todo fix: will break down if someone does }) in their msg text
-                               array( $this, 'languageMsgReplace' ),
-                               $str
-                       );
+               // Add the unique rid
+               $rKey .= $urid;
 
+               return $rKey;
+       }
+       public static function checkForCommonsLanguageFormHack( $langKey){
+               $formNames = array( 'ownwork', 'fromflickr', 'fromwikimedia', 'fromgov');
+               foreach($formNames as $formName){
+                       // Directly reference a form Name then its "english"
+                       if( $formName == $langKey )
+                               return 'en';
+                       // If the langKey includes a form name (ie esownwork)
+                       // then strip the form name use that as the language key
+                       if( strpos($langKey, $formName)!==false){
+                               return str_replace($formName, '', $langKey);
+                       }
+               }
+               //else just return the key unchanged:
+               return $langKey;
+       }
+       public static function getJsPathFromClass( $reqClass ){
+               global $wgJSAutoloadLocalClasses, $wgJSAutoloadClasses;
+               if ( isset( $wgJSAutoloadLocalClasses[$reqClass] ) ) {
+                       return $wgJSAutoloadLocalClasses[$reqClass];
+               } else if ( isset( $wgJSAutoloadClasses[$reqClass] ) ) {
+                       return $wgJSAutoloadClasses[$reqClass];
+               } else {
+                       return false;
+               }
+       }
+       function doGetJsFile( $file_path ) {
+               global $IP;
+               // Load the file
+               $str = @file_get_contents( "{$IP}/{$file_path}" );
+
+               if ( $str === false ) {
+                       // @@todo check PHP error level. Don't want to expose paths if errors are hidden.
+                       $this->error_msg .= 'Requested File: ' . htmlspecialchars( $file_path ) . ' could not be read' . "\n";
+                       return false;
+               }
                return $str;
        }
+       function doProcessJs( $str ){
+               global $wgEnableScriptLocalization;
+               // Strip out js_log debug lines (if not in debug mode)
+               if( !$this->debug )
+                        $str = preg_replace('/\n\s*js_log\(([^\)]*\))*\s*[\;\n]/U', "\n", $str);
+
+               // Do language swap by index:
+               if ( $wgEnableScriptLocalization ){
+                       $inx = self::getLoadGmIndex( $str );
+                       if($inx){
+                               $translated = $this->languageMsgReplace( substr($str, $inx['s'], ($inx['e']-$inx['s']) ));
+                               //return the final string (without double {})
+                               return substr($str, 0, $inx['s']-1) . $translated . substr($str, $inx['e']+1);
+                       }
+               }
+               //return the js str unmodified if we did not transform with the localisation.
+               return $str;
+       }
+       static public function getLoadGmIndex( $str ){
+               $returnIndex = array();
+               preg_match('/loadGM\s*\(\s*\{/', $str, $matches, PREG_OFFSET_CAPTURE );
+               if( count($matches) == 0){
+                       return false;
+               }
+               if( count( $matches ) > 0 ){
+                       //offset + match str length gives startIndex:
+                       $returnIndex['s'] = strlen( $matches[0][0] ) + $matches[0][1];
+                       $foundMatch = true;
+               }
+               $ignorenext = false;
+               $inquote = false;
+               //look for closing } not inside quotes::
+               for ( $i = $returnIndex['s']; $i < strlen( $str ); $i++ ) {
+                       $char = $str[$i];
+                       if ( $ignorenext ) {
+                               $ignorenext = false;
+                       } else {
+                               //search for a close } that is not in quotes or escaped
+                               switch( $char ) {
+                                       case '"':
+                                               $inquote = !$inquote;
+                                               break;
+                                       case '}':
+                                               if( ! $inquote){
+                                                       $returnIndex['e'] =$i;
+                                                       return $returnIndex;
+                                               }
+                                               break;
+                                       case '\\':
+                                               if ( $inquote ) $ignorenext = true;
+                                               break;
+                               }
+                       }
+               }
+       }
 
-       function languageMsgReplace( $jvar ){
-               if( !isset( $jvar[1] ) )
-                       return;
+       function getInlineLoadGMFromClass( $class ){
+               $jsmsg = $this->getMsgKeysFromClass( $class );
+               if( $jsmsg ){
+                       self::getMsgKeys ( $jsmsg );
+                       return 'loadGM(' . FormatJson::encode( $jsmsg ) . ');';
+               }else{
+                       //if could not parse return empty string:
+                       return '';
+               }
+       }
+       function getMsgKeysFromClass( $class ){
+               $file_path = self::getJsPathFromClass( $class );
+               $str = $this->getScriptText($class,  $file_path);
+
+               $inx = self::getLoadGmIndex( $str );
+               if(!$inx)
+               return '';
+               return FormatJson::decode( '{' . substr($str, $inx['s'], ($inx['e']-$inx['s'])) . '}', true);
+       }
 
-               $jmsg = json_decode( '{' . $jvar[1] . '}', true );
-               // do the language lookup:
-               if( $jmsg ){
-                       foreach( $jmsg as $msgKey => $default_en_value ){
-                               $jmsg[$msgKey] = wfMsgNoTrans( $msgKey );
-                       }
-                       //return the updated loadGM json with fixed new lines:
-                       return 'loadGM( ' . json_encode( $jmsg ) . ')';
+       static public function getMsgKeys(& $jmsg, $langCode = false){
+               global $wgContLanguageCode;
+               //check the langCode
+               if(!$langCode)
+                       $langCode = $wgContLanguageCode;
+
+               // Get the msg keys for the a json array
+               foreach ( $jmsg as $msgKey => $default_en_value ) {
+                       $jmsg[$msgKey] = wfMsgGetKey( $msgKey, true, $langCode, false );
+               }
+       }
+       function languageMsgReplace( $json_str ) {
+               $jmsg = FormatJson::decode( '{' . $json_str . '}', true );
+               // Do the language lookup
+               if ( $jmsg ) {
+                       //see if any msgKey has the PLURAL template tag
+                       //package in PLURAL mapping
+                       self::getMsgKeys($jmsg, $this->langCode);
+
+                       // Return the updated loadGM JSON with updated msgs:
+                       return FormatJson::encode( $jmsg );
                } else {
-                       $this->error_msg.= "Could not parse JSON language msg in File:\n" .
-                                                               $this->cur_file . "\n";
+                       // Could not parse JSON return error: (maybe a alert?)
+                       //we just make a note in the code, visitors will get the fallback language,
+                       //developers will read the js source when its not behaving as expected.
+                       return "\n/*
+* Could not parse JSON language messages in this file,
+* Please check that loadGM call contains valid JSON (not javascript)
+*/\n\n" . $json_str; //include the original fallback loadGM
+
                }
-               // could not parse json (throw error?)
-               return $jvar[0];
        }
 }
 
-//a simple version of HTMLFileCache (@@todo abstract shared pieces)
+// A simple version of HTMLFileCache (@@todo abstract shared pieces)
 class simpleFileCache {
        var $mFileCache;
        var $filename = null;
@@ -288,78 +518,85 @@ class simpleFileCache {
 
        public function __construct( &$rKey ) {
                $this->rKey = $rKey;
-               $this->filename = $this->fileCacheName(); // init name
+               $this->getCacheFileName();
        }
 
-       public function fileCacheName() {
-               global $wgUseGzip;
-               if( !$this->mFileCache ) {
-                       global $wgFileCacheDirectory;
+       public function getCacheFileName() {
+               global $wgUseGzip, $wgScriptCacheDirectory;
 
-                       $hash = md5( $this->rKey );
-                       # Avoid extension confusion
-                       $key = str_replace( '.', '%2E', urlencode( $this->rKey ) );
+               $hash = md5( $this->rKey );
+               # Avoid extension confusion
+               $key = str_replace( '.', '%2E', urlencode( $this->rKey ) );
 
-                       $hash1 = substr( $hash, 0, 1 );
-                       $hash2 = substr( $hash, 0, 2 );
-                       $this->mFileCache = "{$wgFileCacheDirectory}/{$subdir}{$hash1}/{$hash2}/{$this->rKey}.js";
+               $hash1 = substr( $hash, 0, 1 );
+               $hash2 = substr( $hash, 0, 2 );
+               $this->filename = "{$wgScriptCacheDirectory}/{$hash1}/{$hash2}/{$this->rKey}.js";
 
-                       if( $wgUseGzip )
-                               $this->mFileCache .= '.gz';
+               // Check for defined files::
+               if( is_file( $this->filename ) )
+               return $this->filename;
 
-                       wfDebug( " fileCacheName() - {$this->mFileCache}\n" );
+               if( is_file(  $this->filename .'.gz') ){
+                       $this->filename.='.gz';
+                       return $this->filename;
                }
-               return $this->mFileCache;
+               //check the update the name based on the $wgUseGzip config var
+               if ( isset($wgUseGzip) && $wgUseGzip )
+               $this->filename.='.gz';
        }
 
        public function isFileCached() {
                return file_exists( $this->filename );
        }
 
-       public function outputFromFileCache(){
-               global $wgUseGzip;
-               if( $wgUseGzip ) {
-                       if( wfClientAcceptsGzip() ) {
-                               header( 'Content-Encoding: gzip' );
-                               readfile( $this->filename );
-                       } else {
-                               /* Send uncompressed  (check if fileCache is in compressed state (ends with .gz)
-                               * (unlikely to execute this since $wgUseGzip would have created a new file above.. but just in case:
-                               */
-                               if( substr( $this->filename, -3 ) == '.gz' ){
-                                       readgzfile( $this->filename );
-                               } else {
-                                       readfile( $this->filename );
-                               }
-                       }
+       public function outputFromFileCache() {
+               if ( $this->clientAcceptsGzip() && substr( $this->filename, - 3 ) == '.gz'  ) {
+                       header( 'Content-Encoding: gzip' );
+                       readfile( $this->filename );
+                       return true;
+               }
+               // Output without gzip:
+               if ( substr( $this->filename, - 3 ) == '.gz' ) {
+                       readgzfile( $this->filename );
                } else {
-                       // just output the file
                        readfile( $this->filename );
                }
-               //return true
                return true;
        }
+       public function clientAcceptsGzip(){
+               $m = array();
+               if ( preg_match(
+                       '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
+               $_SERVER['HTTP_ACCEPT_ENCODING'],
+               $m ) ) {
+                       if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) )
+                       return false;
 
+                       return true;
+               }
+               return false;
+       }
        public function saveToFileCache( &$text ) {
                global $wgUseFileCache, $wgUseGzip;
-               if( !$wgUseFileCache ) {
+               if ( !$wgUseFileCache ) {
                        return 'Error: Called saveToFileCache with $wgUseFileCache off';
                }
-               if( strcmp( $text, '' ) == 0 ) return 'saveToFileCache: empty output file';
-
-               // check the directories if we could not create them error out:
-               $status = $this->checkCacheDirs();
+               if ( strcmp( $text, '' ) == 0 )
+               return 'saveToFileCache: empty output file';
 
-               if( $wgUseGzip ){
+               if ( $wgUseGzip ) {
                        $outputText = gzencode( trim( $text ) );
                } else {
                        $outputText = trim( $text );
                }
 
-               if( $status !== true )
-                       return $status;
+               // Check the directories. If we could not create them, error out.
+               $status = $this->checkCacheDirs();
+
+               if ( $status !== true )
+               return $status;
                $f = fopen( $this->filename, 'w' );
-               if( $f ) {
+               if ( $f ) {
                        fwrite( $f, $outputText );
                        fclose( $f );
                } else {
@@ -372,10 +609,10 @@ class simpleFileCache {
                $mydir2 = substr( $this->filename, 0, strrpos( $this->filename, '/' ) ); # subdirectory level 2
                $mydir1 = substr( $mydir2, 0, strrpos( $mydir2, '/' ) ); # subdirectory level 1
 
-               if( wfMkdirParents( $mydir1 ) === false || wfMkdirParents( $mydir2 ) === false ){
+               if ( wfMkdirParents( $mydir1 ) === false || wfMkdirParents( $mydir2 ) === false ) {
                        return 'Could not create cache directory. Check your cache directory permissions?';
                } else {
                        return true;
                }
        }
-}
\ No newline at end of file
+}