2 //This core jsScriptLoader class provides the script loader functionality
3 //check if we are being invoked in mediaWiki context or stand alone usage:
4 if ( !defined( 'MEDIAWIKI' ) ){
5 //load noMediaWiki helper:
6 require_once( realpath( dirname(__FILE__
) ) . '/php/noMediaWikiConfig.php' );
9 $myScriptLoader = new jsScriptLoader();
10 //preset request values via normal $_GET operation:
11 $myScriptLoader->doScriptLoader();
13 $wgExtensionMessagesFiles['mwEmbed'] = realpath( dirname(__FILE__
) ) .'/php/mwEmbed.i18n.php';
16 //setup page output hook
18 var $jsFileList = array();
20 var $rKey = ''; // the request key
23 var $jsvarurl =false; // if we should include generated js (special class '-')
24 var $doProcReqFlag =true;
26 function doScriptLoader(){
27 global $wgJSAutoloadClasses,$wgJSAutoloadLocalClasses, $wgEnableScriptLoaderJsFile, $IP,
28 $wgEnableScriptMinify, $wgUseFileCache;
31 $this->procRequestVars();
33 //if cache is on and file is present grab it from there:
34 if( $wgUseFileCache && !$this->debug
) {
35 //setup file cache obj:
36 $this->sFileCache
= new simpleFileCache( $this->rKey
);
37 if( $this->sFileCache
->isFileCached() ){
38 //just output headers so we can use php "efficient" readfile
39 $this->outputJsHeaders();
40 $this->sFileCache
->outputFromFileCache();
45 //setup script loader header info
46 $this->jsout
.= 'var mwSlScript = "'. $_SERVER['SCRIPT_NAME'] . '";' . "\n";
47 $this->jsout
.= 'var mwSlGenISODate = "'. date('c') . '";' ."\n";
48 $this->jsout
.= 'var mwSlURID = "' . $this->urid
. '";' ."\n";
50 //swap in the appropriate language per js_file
51 foreach($this->jsFileList
as $classKey => $file_name){
52 //special case: - title classes:
53 if( substr( $classKey, 0, 3) == 'WT:' ){
54 //get just the tile part:
55 $title_block = substr( $classKey, 3);
56 if($title_block[0] == '-' && strpos($title_block, '|') !== false){
57 //special case of "-" title with skin
58 $parts = explode('|', $title_block);
59 $title = array_shift($parts);
60 foreach($parts as $tparam){
61 list($key, $val)= explode('=', $tparam);
62 if( $key=='useskin' ){
66 //make sure the skin name is valid
67 $skinNames = Skin
::getSkinNames();
68 //get the lower case skin name (array keys)
69 $skinNames = array_keys($skinNames);
70 if( in_array(strtolower($skin), $skinNames )){
71 $this->jsout
.= Skin
::generateUserJs( $skin ) . "\n";
76 //its a wikiTitle append the output of the wikitext:
77 $t = Title
::newFromText ( $title_block );
78 $a = new Article( $t );
79 //only get content if the page is not empty:
80 if($a->getID() !== 0 ){
81 $this->jsout
.= $a->getContent() . "\n";
87 if( trim( $file_name ) != ''){
88 //if in debug add a comment with the file name:
90 $this->jsout
.= "\n/**
93 $this->jsout
.= ( $this->doProccessJsFile( $file_name ) ). "\n";
96 //check if we should minify :
97 if( $wgEnableScriptMinify && !$this->debug
){
98 //do the minification and output
99 $this->jsout
= JSMin
::minify( $this->jsout
);
101 //save to the file cache:
102 if( $wgUseFileCache && !$this->debug
) {
103 $status = $this->sFileCache
->saveToFileCache($this->jsout
);
105 $this->error_msg
.= $status;
107 //check for error msg:
108 if( $this->error_msg
!= ''){
109 echo 'alert(\'Error With ScriptLoader.php ::' . str_replace("\n", '\'+"\n"+'."\n'", $this->error_msg
). '\');';
110 echo trim($this->jsout
);
112 //all good lets output cache forever headers:
113 $this->outputJsWithHeaders();
116 function outputJsHeaders(){
117 global $wgJsMimeType;
118 //output js mime type:
119 header( 'Content-type: '.$wgJsMimeType);
120 header( "Pragma: public" );
122 //(the point is we never have to re validate since we should always change the request url based on the svn or article version)
123 $one_year = 60*60*24*365;
124 header("Expires: " . gmdate( "D, d M Y H:i:s", time() +
$one_year ) . " GM");
126 function outputJsWithHeaders(){
128 $this->outputJsHeaders();
130 if( wfClientAcceptsGzip() ) {
131 header( 'Content-Encoding: gzip' );
132 echo gzencode( $this->jsout
);
141 * updates the proc Request
143 function procRequestVars(){
144 global $wgContLanguageCode, $wgEnableScriptMinify, $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgStyleVersion;
147 if( (isset($_GET['debug']) && $_GET['debug']=='true') ||
(isset($wgEnableScriptDebug) && $wgEnableScriptDebug==true )){
151 //set the urid: (be sure to escape it as it goes into our js output)
152 if( isset( $_GET['urid'] ) && $_GET['urid'] !=''){
153 $this->urid
= htmlspecialchars( $_GET['urid'] );
155 //just give it the current style sheet id:
156 //@@todo read the svn version number
157 $this->urid
= $wgStyleVersion;
160 $reqClassList = false;
161 if( isset($_GET['class']) && $_GET['class']!=''){
162 $reqClassList = explode( ',', $_GET['class'] );
164 //check for the requested classes
166 //clean the class list and populate jsFileList
167 foreach( $reqClassList as $reqClass ){
168 if(trim($reqClass) != ''){
169 //check for special case '-' class for user generated js
170 if( substr( $reqClass, 0, 3) == 'WT:' ){
171 $this->jsFileList
[ $reqClass ] = true;
172 $this->rKey
.= $reqClass;
173 $this->jsvarurl
= true;
177 $reqClass = ereg_replace("[^A-Za-z0-9_\-\.]", "", $reqClass );
179 if( isset( $wgJSAutoloadLocalClasses[$reqClass] ) ){
180 $this->jsFileList
[ $reqClass ] = $wgJSAutoloadLocalClasses[ $reqClass ];
181 $this->rKey
.=$reqClass;
182 }else if( isset($wgJSAutoloadClasses[$reqClass])) {
183 $this->jsFileList
[ $reqClass ] = $wgJSAutoloadClasses[ $reqClass ];
184 $this->rKey
.=$reqClass;
186 $this->error_msg
.= 'Requested class: ' . $reqClass . ' not found'."\n";
192 //check for requested files if enabled:
193 if( $wgEnableScriptLoaderJsFile ){
194 if( isset($_GET['files'])){
195 $reqFileList = explode(',', isset($_GET['files']));
196 //clean the file list and populate jsFileList
197 foreach($reqFileList as $reqFile){
199 $reqFile = str_replace('../','',$reqFile);
200 //only allow alphanumeric underscores periods and ending with .js
201 $reqFile = ereg_replace("[^A-Za-z0-9_\-\/\.]", "", $reqFile );
202 if( substr($reqFile, -3) == '.js' ){
203 //don't add it twice:
204 if( !in_array($reqFile, $jsFileList )) {
205 $this->jsFileList
[] = $IP . $reqFile;
206 $this->rKey
.=$reqFile;
209 $this->error_msg
.= 'Not valid requsted javascript file' . "\n";
215 //add the language code to the rKey:
216 $this->rKey
.= '_' . $wgContLanguageCode;
218 //add the unique rid to the rKey
219 $this->rKey
.= $this->urid
;
222 if($wgEnableScriptMinify){
226 function doProccessJsFile( $file_name ){
227 global $IP, $wgEnableScriptLocalization, $IP;
230 $str = @file_get_contents
("{$IP}/{$file_name}");
233 //@@todo check php error level (don't want to expose paths if errors are hidden)
234 $this->error_msg
.= 'Requested File: ' . htmlspecialchars( $file_name ) . ' could not be read' . "\n";
237 $this->cur_file
= $file_name;
239 //strip out js_log debug lines not much luck with this regExp yet:
240 //if( !$this->debug )
241 // $str = preg_replace('/\n\s*js_log\s*\([^\)]([^;]|\n])*;/', "\n", $str);
244 if($wgEnableScriptLocalization)
245 $str = preg_replace_callback('/loadGM\s*\(\s*{(.*)}\s*\)\s*/siU', //@@todo fix: will break down if someone does }) in their msg text
246 array($this, 'languageMsgReplace'),
251 function languageMsgReplace($jvar){
255 $jmsg = json_decode( '{' . $jvar[1] . '}', true );
256 //do the language lookup:
258 foreach($jmsg as $msgKey => $default_en_value){
259 $jmsg[$msgKey] = wfMsgNoTrans( $msgKey );
261 //return the updated loadGM json with fixed new lines:
262 return 'loadGM( ' . json_encode( $jmsg ) . ')';
264 $this->error_msg
.= "Could not parse JSON language msg in File:\n" .
265 $this->cur_file
."\n";
267 //could not parse json (throw error?)
271 //a simple version of HTMLFileCache (@@todo abstract shared pieces)
272 class simpleFileCache
{
276 public function __construct( &$rKey ) {
278 $this->filename
= $this->fileCacheName(); // init name
280 public function fileCacheName() {
282 if( !$this->mFileCache
) {
283 global $wgFileCacheDirectory;
285 $hash = md5( $this->rKey
);
286 # Avoid extension confusion
287 $key = str_replace( '.', '%2E', urlencode( $this->rKey
) );
289 $hash1 = substr( $hash, 0, 1 );
290 $hash2 = substr( $hash, 0, 2 );
291 $this->mFileCache
= "{$wgFileCacheDirectory}/{$subdir}{$hash1}/{$hash2}/{$this->rKey}.js";
294 $this->mFileCache
.= '.gz';
296 wfDebug( " fileCacheName() - {$this->mFileCache}\n" );
298 return $this->mFileCache
;
300 public function isFileCached() {
301 return file_exists( $this->filename
);
303 public function outputFromFileCache(){
306 if( wfClientAcceptsGzip() ) {
307 header( 'Content-Encoding: gzip' );
308 readfile( $this->filename
);
310 /* Send uncompressed (check if fileCache is in compressed state (ends with .gz)
311 * (unlikely to execute this since $wgUseGzip would have created a new file above.. but just in case:
313 if(substr($this->filename
, -3)=='.gz'){
314 readgzfile( $this->filename
);
316 readfile( $this->filename
);
320 //just output the file
321 readfile( $this->filename
);
326 public function saveToFileCache(& $text ) {
327 global $wgUseFileCache, $wgUseGzip;
328 if( !$wgUseFileCache ) {
329 return 'Error: Called saveToFileCache with $wgUseFileCache off';
331 if( strcmp($text,'') == 0 ) return 'saveToFileCache: empty output file';
333 //check the directories if we could not create them error out:
334 $status = $this->checkCacheDirs();
337 $outputText = gzencode( trim($text) );
339 $outputText = trim($text);
344 $f = fopen( $this->filename
, 'w' );
346 fwrite( $f, $outputText );
349 return 'Could not open file for writing. Check your cache directory permissions?';
353 protected function checkCacheDirs() {
354 $mydir2 = substr($this->filename
,0,strrpos($this->filename
,'/')); # subdirectory level 2
355 $mydir1 = substr($mydir2,0,strrpos($mydir2,'/')); # subdirectory level 1
357 if( wfMkdirParents( $mydir1 ) === false ||
wfMkdirParents( $mydir2 ) === false){
358 return 'Could not create cache directory. Check your cache directory permissions?';