Index: trunk/phase3/includes/ResourceLoaderContext.php |
— | — | @@ -33,6 +33,7 @@ |
34 | 34 | protected $skin; |
35 | 35 | protected $debug; |
36 | 36 | protected $only; |
| 37 | + protected $media; |
37 | 38 | protected $hash; |
38 | 39 | |
39 | 40 | /* Methods */ |
— | — | @@ -49,6 +50,7 @@ |
50 | 51 | $this->skin = $request->getVal( 'skin' ); |
51 | 52 | $this->debug = $request->getVal( 'debug' ) === 'true' || $request->getBool( 'debug' ); |
52 | 53 | $this->only = $request->getVal( 'only' ); |
| 54 | + $this->media = $request->getVal( 'media', 'all' ); |
53 | 55 | |
54 | 56 | // Fallback on system defaults |
55 | 57 | if ( !$this->language ) { |
— | — | @@ -95,6 +97,10 @@ |
96 | 98 | public function getOnly() { |
97 | 99 | return $this->only; |
98 | 100 | } |
| 101 | + |
| 102 | + public function getMedia() { |
| 103 | + return $this->media; |
| 104 | + } |
99 | 105 | |
100 | 106 | public function shouldIncludeScripts() { |
101 | 107 | return is_null( $this->only ) || $this->only === 'scripts'; |
— | — | @@ -110,6 +116,7 @@ |
111 | 117 | |
112 | 118 | public function getHash() { |
113 | 119 | return isset( $this->hash ) ? |
114 | | - $this->hash : $this->hash = implode( '|', array( $this->language, $this->skin, $this->debug ) ); |
| 120 | + $this->hash : $this->hash = |
| 121 | + implode( '|', array( $this->language, $this->skin, $this->debug, $this->only, $this->media ) ); |
115 | 122 | } |
116 | 123 | } |
Index: trunk/phase3/includes/OutputPage.php |
— | — | @@ -2198,7 +2198,7 @@ |
2199 | 2199 | global $wgUser, $wgRequest, $wgLang; |
2200 | 2200 | |
2201 | 2201 | if ( $sk->commonPrintStylesheet() ) { |
2202 | | - $this->addStyle( 'common/wikiprintable.css', 'print' ); |
| 2202 | + $this->addModuleStyles( 'mediawiki.legacy.wikiprintable' ); |
2203 | 2203 | } |
2204 | 2204 | $sk->setupUserCss( $this ); |
2205 | 2205 | |
— | — | @@ -2280,7 +2280,7 @@ |
2281 | 2281 | return $ret; |
2282 | 2282 | } |
2283 | 2283 | |
2284 | | - static function makeResourceLoaderLink( $skin, $modules, $only ) { |
| 2284 | + static function makeResourceLoaderLink( $skin, $modules, $only, $media = 'all' ) { |
2285 | 2285 | global $wgUser, $wgLang, $wgRequest; |
2286 | 2286 | // TODO: Should this be a static function of ResourceLoader instead? |
2287 | 2287 | $query = array( |
— | — | @@ -2292,7 +2292,8 @@ |
2293 | 2293 | ); |
2294 | 2294 | // Automatically select style/script elements |
2295 | 2295 | if ( $only === 'styles' ) { |
2296 | | - return Html::linkedStyle( wfAppendQuery( wfScript( 'load' ), $query ) ); |
| 2296 | + $query['media'] = $media; |
| 2297 | + return Html::linkedStyle( wfAppendQuery( wfScript( 'load' ), $query ), $media ); |
2297 | 2298 | } else { |
2298 | 2299 | return Html::linkedScript( wfAppendQuery( wfScript( 'load' ), $query ) ); |
2299 | 2300 | } |
— | — | @@ -2488,11 +2489,15 @@ |
2489 | 2490 | // Support individual script requests in debug mode |
2490 | 2491 | if ( $wgRequest->getBool( 'debug' ) && $wgRequest->getVal( 'debug' ) !== 'false' ) { |
2491 | 2492 | foreach ( $this->getModuleStyles() as $name ) { |
2492 | | - $tags[] = self::makeResourceLoaderLink( $sk, $name, 'styles' ); |
| 2493 | + $tags[] = self::makeResourceLoaderLink( $sk, $name, 'styles', 'all' ); |
| 2494 | + $tags[] = self::makeResourceLoaderLink( $sk, $name, 'styles', 'screen' ); |
| 2495 | + $tags[] = self::makeResourceLoaderLink( $sk, $name, 'styles', 'print' ); |
2493 | 2496 | } |
2494 | 2497 | } else { |
2495 | 2498 | if ( count( $this->getModuleStyles() ) ) { |
2496 | | - $tags[] = self::makeResourceLoaderLink( $sk, $this->getModuleStyles(), 'styles' ); |
| 2499 | + $tags[] = self::makeResourceLoaderLink( $sk, $this->getModuleStyles(), 'styles', 'all' ); |
| 2500 | + $tags[] = self::makeResourceLoaderLink( $sk, $this->getModuleStyles(), 'styles', 'screen' ); |
| 2501 | + $tags[] = self::makeResourceLoaderLink( $sk, $this->getModuleStyles(), 'styles', 'print' ); |
2497 | 2502 | } |
2498 | 2503 | } |
2499 | 2504 | |
Index: trunk/phase3/includes/ResourceLoader.php |
— | — | @@ -287,16 +287,20 @@ |
288 | 288 | } |
289 | 289 | |
290 | 290 | // Styles |
291 | | - $styles = ''; |
| 291 | + $styles = array(); |
292 | 292 | |
293 | 293 | if ( |
294 | 294 | $context->shouldIncludeStyles() && |
295 | | - ( $styles .= self::$modules[$name]->getStyle( $context ) ) !== '' |
| 295 | + ( count( $styles = self::$modules[$name]->getStyles( $context ) ) ) |
296 | 296 | ) { |
297 | | - if ( self::$modules[$name]->getFlip( $context ) ) { |
298 | | - $styles = self::filter( 'flip-css', $styles ); |
| 297 | + foreach ( $styles as $media => $style ) { |
| 298 | + if ( self::$modules[$name]->getFlip( $context ) ) { |
| 299 | + $styles[$media] = self::filter( 'flip-css', $style ); |
| 300 | + } |
| 301 | + if ( !$context->getDebug() ) { |
| 302 | + $styles[$media] = self::filter( 'minify-css', $style ); |
| 303 | + } |
299 | 304 | } |
300 | | - $styles = $context->getDebug() ? $styles : self::filter( 'minify-css', $styles ); |
301 | 305 | } |
302 | 306 | |
303 | 307 | // Messages |
— | — | @@ -304,14 +308,16 @@ |
305 | 309 | |
306 | 310 | // Output |
307 | 311 | if ( $context->getOnly() === 'styles' ) { |
308 | | - echo $styles; |
| 312 | + if ( isset( $styles[$context->getMedia()] ) ) { |
| 313 | + echo $styles[$context->getMedia()]; |
| 314 | + } |
309 | 315 | } else if ( $context->getOnly() === 'scripts' ) { |
310 | 316 | echo $scripts; |
311 | 317 | } else if ( $context->getOnly() === 'messages' ) { |
312 | 318 | echo "mediaWiki.msg.set( $messages );\n"; |
313 | 319 | } else { |
314 | | - $styles = Xml::escapeJsString( $styles ); |
315 | | - echo "mediaWiki.loader.implement( '$name', function() {{$scripts}},\n'$styles',\n$messages );\n"; |
| 320 | + $styles = FormatJson::encode( $styles ); |
| 321 | + echo "mediaWiki.loader.implement( '$name', function() {{$scripts}},\n$styles,\n$messages );\n"; |
316 | 322 | } |
317 | 323 | } |
318 | 324 | |
Index: trunk/phase3/includes/SkinTemplate.php |
— | — | @@ -107,9 +107,7 @@ |
108 | 108 | * @param $out OutputPage |
109 | 109 | */ |
110 | 110 | function setupSkinUserCss( OutputPage $out ){ |
111 | | - $out->addModuleStyles( 'mediawiki.legacy.shared' ); |
112 | | - // ResourceLoader does not support CSS media types yet, so we must include this on it's own the old way |
113 | | - $out->addStyle( 'common/commonPrint.css', 'print' ); |
| 111 | + $out->addModuleStyles( array( 'mediawiki.legacy.shared', 'mediawiki.legacy.commonPrint' ) ); |
114 | 112 | } |
115 | 113 | |
116 | 114 | /** |
Index: trunk/phase3/includes/ResourceLoaderModule.php |
— | — | @@ -96,9 +96,9 @@ |
97 | 97 | * Get all CSS for this module for a given skin. |
98 | 98 | * |
99 | 99 | * @param $context ResourceLoaderContext object |
100 | | - * @return String: CSS |
| 100 | + * @return array: strings of CSS keyed by media type |
101 | 101 | */ |
102 | | - public abstract function getStyle( ResourceLoaderContext $context ); |
| 102 | + public abstract function getStyles( ResourceLoaderContext $context ); |
103 | 103 | |
104 | 104 | /** |
105 | 105 | * Get the messages needed for this module. |
— | — | @@ -191,9 +191,11 @@ |
192 | 192 | * // Non-raw module options |
193 | 193 | * 'dependencies' => 'module' | array( 'module1', 'module2' ... ) |
194 | 194 | * 'loaderScripts' => 'dir/loader.js' | array( 'dir/loader1.js', 'dir/loader2.js' ... ), |
195 | | - * 'styles' => 'dir/file.css' | array( 'dir/file1.css', 'dir/file2.css' ... ), |
| 195 | + * 'styles' => 'dir/file.css' | array( 'dir/file1.css', 'dir/file2.css' ... ), | |
| 196 | + * array( 'dir/file1.css' => array( 'media' => 'print' ) ), |
196 | 197 | * 'skinStyles' => array( |
197 | | - * '[skin name]' => 'dir/skin.css' | '[skin name]' => array( 'dir/skin1.css', 'dir/skin2.css' ... ) |
| 198 | + * '[skin name]' => 'dir/skin.css' | array( 'dir/skin1.css', 'dir/skin2.css' ... ) | |
| 199 | + * array( 'dir/file1.css' => array( 'media' => 'print' ) |
198 | 200 | * ... |
199 | 201 | * ), |
200 | 202 | * 'messages' => array( 'message1', 'message2' ... ), |
— | — | @@ -362,12 +364,28 @@ |
363 | 365 | return $retval; |
364 | 366 | } |
365 | 367 | |
366 | | - public function getStyle( ResourceLoaderContext $context ) { |
367 | | - $style = $this->getPrimaryStyle() . "\n" . $this->getSkinStyle( $context->getSkin() ); |
368 | | - |
369 | | - // Extract and store the list of referenced files |
370 | | - $files = CSSMin::getLocalFileReferences( $style ); |
371 | | - |
| 368 | + public function getStyles( ResourceLoaderContext $context ) { |
| 369 | + $styles = array(); |
| 370 | + foreach ( $this->getPrimaryStyles() as $media => $style ) { |
| 371 | + if ( !isset( $styles[$media] ) ) { |
| 372 | + $styles[$media] = ''; |
| 373 | + } |
| 374 | + $styles[$media] .= $style; |
| 375 | + } |
| 376 | + foreach ( $this->getPrimaryStyles() as $media => $style ) { |
| 377 | + if ( !isset( $styles[$media] ) ) { |
| 378 | + $styles[$media] = ''; |
| 379 | + } |
| 380 | + $styles[$media] .= $this->getSkinStyles( $context->getSkin() ); |
| 381 | + } |
| 382 | + |
| 383 | + // Collect referenced files |
| 384 | + $files = array(); |
| 385 | + foreach ( $styles as $media => $style ) { |
| 386 | + // Extract and store the list of referenced files |
| 387 | + $files = array_merge( $files, CSSMin::getLocalFileReferences( $style ) ); |
| 388 | + } |
| 389 | + |
372 | 390 | // Only store if modified |
373 | 391 | if ( $files !== $this->getFileDependencies( $context->getSkin() ) ) { |
374 | 392 | $encFiles = FormatJson::encode( $files ); |
— | — | @@ -379,15 +397,15 @@ |
380 | 398 | 'md_deps' => $encFiles, |
381 | 399 | ) |
382 | 400 | ); |
383 | | - |
| 401 | + |
384 | 402 | // Save into memcached |
385 | 403 | global $wgMemc; |
386 | | - |
| 404 | + |
387 | 405 | $key = wfMemcKey( 'resourceloader', 'module_deps', $this->getName(), $context->getSkin() ); |
388 | 406 | $wgMemc->set( $key, $encFiles ); |
389 | 407 | } |
390 | | - |
391 | | - return $style; |
| 408 | + |
| 409 | + return $styles; |
392 | 410 | } |
393 | 411 | |
394 | 412 | public function getMessages() { |
— | — | @@ -421,19 +439,31 @@ |
422 | 440 | if ( isset( $this->modifiedTime[$context->getHash()] ) ) { |
423 | 441 | return $this->modifiedTime[$context->getHash()]; |
424 | 442 | } |
425 | | - |
| 443 | + |
| 444 | + // Sort of nasty way we can get a flat list of files depended on by all styles |
| 445 | + $styles = array(); |
| 446 | + foreach ( self::organizeFilesByOption( $this->styles, 'media', 'all' ) as $media => $styleFiles ) { |
| 447 | + $styles = array_merge( $styles, $styleFiles ); |
| 448 | + } |
| 449 | + $skinFiles = (array) self::getSkinFiles( |
| 450 | + $context->getSkin(), self::organizeFilesByOption( $this->skinStyles, 'media', 'all' ) |
| 451 | + ); |
| 452 | + foreach ( $skinFiles as $media => $styleFiles ) { |
| 453 | + $styles = array_merge( $styles, $styleFiles ); |
| 454 | + } |
| 455 | + |
| 456 | + // Final merge, this should result in a master list of dependent files |
426 | 457 | $files = array_merge( |
427 | 458 | $this->scripts, |
428 | | - $this->styles, |
| 459 | + $styles, |
429 | 460 | $context->getDebug() ? $this->debugScripts : array(), |
430 | 461 | isset( $this->languageScripts[$context->getLanguage()] ) ? |
431 | 462 | (array) $this->languageScripts[$context->getLanguage()] : array(), |
432 | 463 | (array) self::getSkinFiles( $context->getSkin(), $this->skinScripts ), |
433 | | - (array) self::getSkinFiles( $context->getSkin(), $this->skinStyles ), |
434 | 464 | $this->loaders, |
435 | 465 | $this->getFileDependencies( $context->getSkin() ) |
436 | 466 | ); |
437 | | - |
| 467 | + |
438 | 468 | $filesMtime = max( array_map( 'filemtime', array_map( array( __CLASS__, 'remapFilename' ), $files ) ) ); |
439 | 469 | |
440 | 470 | // Get the mtime of the message blob |
— | — | @@ -468,7 +498,7 @@ |
469 | 499 | * |
470 | 500 | * @return String: JS |
471 | 501 | */ |
472 | | - protected function getPrimaryStyle() { |
| 502 | + protected function getPrimaryStyles() { |
473 | 503 | return self::concatStyles( $this->styles ); |
474 | 504 | } |
475 | 505 | |
— | — | @@ -509,9 +539,9 @@ |
510 | 540 | * Get the skin-specific CSS for a given skin. This is pulled from the |
511 | 541 | * skin-specific CSS files added through addSkinStyles() |
512 | 542 | * |
513 | | - * @return String: CSS |
| 543 | + * @return Array: list of CSS strings keyed by media type |
514 | 544 | */ |
515 | | - protected function getSkinStyle( $skin ) { |
| 545 | + protected function getSkinStyles( $skin ) { |
516 | 546 | return self::concatStyles( self::getSkinFiles( $skin, $this->skinStyles ) ); |
517 | 547 | } |
518 | 548 | |
— | — | @@ -582,15 +612,41 @@ |
583 | 613 | return implode( "\n", array_map( 'file_get_contents', array_map( array( __CLASS__, 'remapFilename' ), array_unique( (array) $files ) ) ) ); |
584 | 614 | } |
585 | 615 | |
| 616 | + protected static function organizeFilesByOption( $files, $option, $default ) { |
| 617 | + $organizedFiles = array(); |
| 618 | + foreach ( (array) $files as $key => $value ) { |
| 619 | + if ( is_int( $key ) ) { |
| 620 | + // File name as the value |
| 621 | + if ( !isset( $organizedFiles[$default] ) ) { |
| 622 | + $organizedFiles[$default] = array(); |
| 623 | + } |
| 624 | + $organizedFiles[$default][] = $value; |
| 625 | + } else if ( is_array( $value ) ) { |
| 626 | + // File name as the key, options array as the value |
| 627 | + $media = isset( $value[$option] ) ? $value[$option] : $default; |
| 628 | + if ( !isset( $organizedFiles[$media] ) ) { |
| 629 | + $organizedFiles[$media] = array(); |
| 630 | + } |
| 631 | + $organizedFiles[$media][] = $key; |
| 632 | + } |
| 633 | + } |
| 634 | + return $organizedFiles; |
| 635 | + } |
| 636 | + |
586 | 637 | /** |
587 | 638 | * Get the contents of a set of CSS files, remap then and concatenate |
588 | 639 | * them, with newlines in between. Each file is used only once. |
589 | 640 | * |
590 | 641 | * @param $files Array of file names |
591 | | - * @return String: concatenated and remapped contents of $files |
| 642 | + * @return Array: list of concatenated and remapped contents of $files keyed by media type |
592 | 643 | */ |
593 | | - protected static function concatStyles( $files ) { |
594 | | - return implode( "\n", array_map( array( __CLASS__, 'remapStyle' ), array_unique( (array) $files ) ) ); |
| 644 | + protected static function concatStyles( $styles ) { |
| 645 | + $styles = self::organizeFilesByOption( $styles, 'media', 'all' ); |
| 646 | + foreach ( $styles as $media => $files ) { |
| 647 | + $styles[$media] = |
| 648 | + implode( "\n", array_map( array( __CLASS__, 'remapStyle' ), array_unique( (array) $files ) ) ); |
| 649 | + } |
| 650 | + return $styles; |
595 | 651 | } |
596 | 652 | |
597 | 653 | /** |
— | — | @@ -662,7 +718,7 @@ |
663 | 719 | return $this->modifiedTime; |
664 | 720 | } |
665 | 721 | |
666 | | - public function getStyle( ResourceLoaderContext $context ) { return ''; } |
| 722 | + public function getStyles( ResourceLoaderContext $context ) { return array(); } |
667 | 723 | public function getMessages() { return array(); } |
668 | 724 | public function getLoaderScript() { return ''; } |
669 | 725 | public function getDependencies() { return array(); } |
— | — | @@ -738,7 +794,7 @@ |
739 | 795 | return 300; // 5 minutes |
740 | 796 | } |
741 | 797 | |
742 | | - public function getStyle( ResourceLoaderContext $context ) { return ''; } |
| 798 | + public function getStyles( ResourceLoaderContext $context ) { return array(); } |
743 | 799 | |
744 | 800 | public function getFlip( $context ) { |
745 | 801 | global $wgContLang; |
Index: trunk/phase3/resources/Resources.php |
— | — | @@ -9,7 +9,9 @@ |
10 | 10 | |
11 | 11 | /* Skins */ |
12 | 12 | |
13 | | - 'vector' => new ResourceLoaderFileModule( array( 'styles' => 'skins/vector/main-ltr.css' ) ), |
| 13 | + 'vector' => new ResourceLoaderFileModule( |
| 14 | + array( 'styles' => array( 'skins/vector/main-ltr.css' => array( 'media' => 'screen' ) ) ) |
| 15 | + ), |
14 | 16 | |
15 | 17 | /* jQuery */ |
16 | 18 | |
— | — | @@ -315,7 +317,7 @@ |
316 | 318 | 'dependencies' => 'mediawiki.legacy.wikibits', |
317 | 319 | ) ), |
318 | 320 | 'mediawiki.legacy.commonPrint' => new ResourceLoaderFileModule( array( |
319 | | - 'styles' => 'skins/common/commonPrint.css', |
| 321 | + 'styles' => array( 'skins/common/commonPrint.css' => array( 'media' => 'print' ) ), |
320 | 322 | ) ), |
321 | 323 | 'mediawiki.legacy.config' => new ResourceLoaderFileModule( array( |
322 | 324 | 'scripts' => 'skins/common/config.js', |
— | — | @@ -381,10 +383,10 @@ |
382 | 384 | 'dependencies' => 'mediawiki.legacy.wikibits', |
383 | 385 | ) ), |
384 | 386 | 'mediawiki.legacy.shared' => new ResourceLoaderFileModule( array( |
385 | | - 'styles' => 'skins/common/shared.css', |
| 387 | + 'styles' => array( 'skins/common/shared.css' => array( 'media' => 'screen' ) ), |
386 | 388 | ) ), |
387 | 389 | 'mediawiki.legacy.oldshared' => new ResourceLoaderFileModule( array( |
388 | | - 'styles' => 'skins/common/oldshared.css', |
| 390 | + 'styles' => array( 'skins/common/oldshared.css' => array( 'media' => 'screen' ) ), |
389 | 391 | ) ), |
390 | 392 | 'mediawiki.legacy.upload' => new ResourceLoaderFileModule( array( |
391 | 393 | 'scripts' => 'skins/common/upload.js', |
— | — | @@ -394,4 +396,7 @@ |
395 | 397 | 'scripts' => 'skins/common/wikibits.js', |
396 | 398 | 'messages' => array( 'showtoc', 'hidetoc' ), |
397 | 399 | ) ), |
| 400 | + 'mediawiki.legacy.wikiprintable' => new ResourceLoaderFileModule( array( |
| 401 | + 'styles' => array( 'skins/common/wikiprintable.css' => array( 'media' => 'print' ) ), |
| 402 | + ) ), |
398 | 403 | ) ); |
Index: trunk/phase3/resources/mediawiki/mediawiki.js |
— | — | @@ -323,6 +323,12 @@ |
324 | 324 | // Add style sheet to document |
325 | 325 | if ( typeof registry[module].style === 'string' && registry[module].style.length ) { |
326 | 326 | $( 'head' ).append( '<style type="text/css">' + registry[module].style + '</style>' ); |
| 327 | + } else if ( typeof registry[module].style === 'object' ) { |
| 328 | + for ( var media in registry[module].style ) { |
| 329 | + $( 'head' ).append( |
| 330 | + '<style type="text/css" media="' + media + '">' + registry[module].style[media] + '</style>' |
| 331 | + ); |
| 332 | + } |
327 | 333 | } |
328 | 334 | // Add localizations to message system |
329 | 335 | if ( typeof registry[module].messages === 'object' ) { |
— | — | @@ -526,8 +532,8 @@ |
527 | 533 | if ( typeof script !== 'function' ) { |
528 | 534 | throw new Error( 'script must be a function, not a ' + typeof script ); |
529 | 535 | } |
530 | | - if ( typeof style !== 'undefined' && typeof style !== 'string' ) { |
531 | | - throw new Error( 'style must be a string, not a ' + typeof style ); |
| 536 | + if ( typeof style !== 'undefined' && typeof style !== 'string' && typeof style !== 'object' ) { |
| 537 | + throw new Error( 'style must be a string or object, not a ' + typeof style ); |
532 | 538 | } |
533 | 539 | if ( typeof localization !== 'undefined' && typeof localization !== 'object' ) { |
534 | 540 | throw new Error( 'localization must be an object, not a ' + typeof localization ); |