Index: branches/resourceloader/phase3/includes/ResourceLoader.php |
— | — | @@ -55,6 +55,8 @@ |
56 | 56 | * ), |
57 | 57 | * // List of keys of messages to include |
58 | 58 | * 'messages' => array( 'foo-hello', 'foo-goodbye' ), |
| 59 | + * // Subclass of ResourceLoaderModule to use for custom modules |
| 60 | + * 'class' => 'ResourceLoaderSiteJSModule', |
59 | 61 | * ) ); |
60 | 62 | * @example |
61 | 63 | * // Responds to a resource loading request |
— | — | @@ -64,7 +66,7 @@ |
65 | 67 | |
66 | 68 | /* Protected Static Members */ |
67 | 69 | |
68 | | - // List of modules and their options |
| 70 | + // array ( modulename => ResourceLoaderModule ) |
69 | 71 | protected static $modules = array(); |
70 | 72 | |
71 | 73 | /* Protected Static Methods */ |
— | — | @@ -78,6 +80,7 @@ |
79 | 81 | * @return {string} filtered data |
80 | 82 | */ |
81 | 83 | protected static function filter( $filter, $data, $file = null ) { |
| 84 | + // FIXME: $file is not used by any callers as path rewriting is currently kinda broken |
82 | 85 | global $wgMemc; |
83 | 86 | $key = wfMemcKey( 'resourceloader', $filter, md5( $data ) ); |
84 | 87 | $cached = $wgMemc->get( $key ); |
— | — | @@ -105,69 +108,7 @@ |
106 | 109 | $wgMemc->set( $key, $result ); |
107 | 110 | return $result; |
108 | 111 | } |
109 | | - /** |
110 | | - * Converts a multi-level array into a flat array containing the unique values of all leaf nodes |
111 | | - * |
112 | | - * @param {array} $deep mutli-level array to flatten |
113 | | - * @param {array} $flat array to append flattened values to (used internally) |
114 | | - * @return {array} flattened array of leaf nodes |
115 | | - */ |
116 | | - protected static function flatten( $deep, $flat = array() ) { |
117 | | - foreach ( $deep as $value ) { |
118 | | - if ( is_array( $value ) ) { |
119 | | - $flat = self::flatten( $value, $flat ); |
120 | | - } else { |
121 | | - if ( $value ) { |
122 | | - $flat[] = $value; |
123 | | - } |
124 | | - } |
125 | | - } |
126 | | - return array_unique( $flat ); |
127 | | - } |
128 | | - /** |
129 | | - * Validates a file or list of files as existing |
130 | | - * |
131 | | - * @param {mixed} string file name or array of any depth containing string file names as leaf nodes |
132 | | - * @throws {MWException} if one or more files do not exist |
133 | | - */ |
134 | | - protected static function validate( $files ) { |
135 | | - if ( is_array( $files ) ) { |
136 | | - $files = self::flatten( $files ); |
137 | | - foreach ( $files as $file ) { |
138 | | - self::validate( $file ); |
139 | | - } |
140 | | - } else { |
141 | | - if ( !file_exists( $files ) ) { |
142 | | - throw new MWException( 'File does not exist: ' . $files ); |
143 | | - } |
144 | | - } |
145 | | - } |
146 | | - /** |
147 | | - * Reads a file or list of files and returns them as a string or outputs them into the current output buffer |
148 | | - * |
149 | | - * @param {mixed} $files string file name or array of any depth containing string file names as leaf nodes |
150 | | - * @param {bool} $passthrough whether to return read data as a string or to output it directly to the current buffer |
151 | | - * @return {mixed} string of read data or null if $passthrough is true |
152 | | - */ |
153 | | - protected static function read( $files, $passthrough = false ) { |
154 | | - if ( is_array( $files ) ) { |
155 | | - $files = self::flatten( $files ); |
156 | | - $contents = ''; |
157 | | - foreach ( $files as $file ) { |
158 | | - $contents .= self::read( $file ); |
159 | | - } |
160 | | - return $contents; |
161 | | - } else { |
162 | | - if ( $passthrough ) { |
163 | | - readfile( $files ); |
164 | | - echo "\n"; |
165 | | - return null; |
166 | | - } else { |
167 | | - return file_get_contents( $files ) . "\n"; |
168 | | - } |
169 | | - } |
170 | | - } |
171 | | - |
| 112 | + |
172 | 113 | /* Static Methods */ |
173 | 114 | |
174 | 115 | /** |
— | — | @@ -199,6 +140,7 @@ |
200 | 141 | * ... |
201 | 142 | * ), |
202 | 143 | * 'messages' => array( 'message1', 'message2' ... ), |
| 144 | + * 'class' => 'classname', // Optional, defaults to ResourceLoaderModule |
203 | 145 | * ) |
204 | 146 | * |
205 | 147 | * @todo We need much more clever error reporting, not just in detailing what happened, but in bringing errors to |
— | — | @@ -217,69 +159,31 @@ |
218 | 160 | // A module has already been registered by this name |
219 | 161 | throw new MWException( 'Another module has already been registered as ' . $module ); |
220 | 162 | } |
221 | | - // Always include a set of default options in each registration so we need not exaustively mark all options for |
222 | | - // all modules when registering and also don't need to worry if the options are set or not later on |
223 | | - $options = array_merge( array( |
224 | | - 'script' => null, |
225 | | - 'locales' => null, |
226 | | - 'raw' => false, |
227 | | - // An empty array is used for needs to make FormatJson::encode output [] instead of null which is shorted and |
228 | | - // results in easier to work with data on the client |
229 | | - 'needs' => array(), |
230 | | - 'loader' => null, |
231 | | - 'debug' => null, |
232 | | - 'style' => null, |
233 | | - 'themes' => null, |
234 | | - 'messages' => null, |
235 | | - 'callback' => null, |
236 | | - ), $options ); |
237 | | - // Validate script option - which is required and must reference files that exist |
238 | | - if ( !is_string( $options['script'] ) && is_null( $options['callback'] ) ) { |
239 | | - throw new MWException( 'Module does not include a script or a callback: ' . $module ); |
240 | | - } |
241 | | - if ( is_string( $options['script'] ) && !is_null( $options['callback'] ) ) { |
242 | | - throw new MWException( 'Module has both a script and a callback: ' . $module ); |
243 | | - } |
244 | | - // Validate options that reference files |
245 | | - foreach ( array( 'script', 'locales', 'loader', 'debug', 'style', 'themes' ) as $option ) { |
246 | | - if ( $options[$option] !== null ) { |
247 | | - self::validate( $options[$option] ); |
248 | | - } |
249 | | - } |
| 163 | + |
| 164 | + // Determine class |
| 165 | + $class = isset( $options['class'] ) ? $options['class'] : 'ResourceLoaderModule'; |
| 166 | + |
250 | 167 | // Attach module |
251 | | - self::$modules[$module] = $options; |
| 168 | + self::$modules[$module] = new $class( $options ); |
252 | 169 | } |
253 | 170 | /** |
254 | 171 | * Gets a map of all modules and their options |
255 | 172 | * |
256 | | - * @return {array} list of modules and their options |
| 173 | + * @return {array} array( modulename => ResourceLoaderModule ) |
257 | 174 | */ |
258 | 175 | public static function getModules() { |
259 | 176 | return self::$modules; |
260 | 177 | } |
| 178 | + |
261 | 179 | /** |
262 | | - * Get the timestamp at which the module was last modified. This is the |
263 | | - * maximum of the last modification timestamps of its constituent files |
264 | | - * @param $module string Module name |
265 | | - * @return int UNIX timestamp |
| 180 | + * Get the ResourceLoaderModule object for a given module name |
| 181 | + * @param $name string Module name |
| 182 | + * @return mixed ResourceLoaderModule or null if not registered |
266 | 183 | */ |
267 | | - public static function getModuleTimestamp( $module ) { |
268 | | - $m = self::$modules[$module]; |
269 | | - $mtime = filemtime( $m['script'] ); |
270 | | - if ( !is_null( $m['style'] ) ) { |
271 | | - $mtime = max( $mtime, filemtime( $m['style'] ) ); |
272 | | - } |
273 | | - if( !is_null( $m['loader'] ) ) { |
274 | | - $mtime = max( $mtime, filemtime( $m['loader'] ) ); |
275 | | - } |
276 | | - return $mtime; |
| 184 | + public static function getModule( $name ) { |
| 185 | + return isset( self::$modules[$name] ) ? self::$modules[$name] : null; |
277 | 186 | } |
278 | 187 | |
279 | | - public static function siteJSCallback( $parameters ) { |
280 | | - // FIXME: Does this belong in this class? |
281 | | - return Skin::newFromKey( $parameters['skin'] )->generateUserJs(); |
282 | | - } |
283 | | - |
284 | 188 | /* |
285 | 189 | * Outputs a response to a resource load-request, including a content-type header |
286 | 190 | * |
— | — | @@ -298,6 +202,7 @@ |
299 | 203 | public static function respond( WebRequest $request, $server ) { |
300 | 204 | global $wgUser, $wgLang, $wgDefaultSkin; |
301 | 205 | // Fallback on system settings |
| 206 | + // FIXME: Unnecessary unstubbing going on here, work around that |
302 | 207 | $parameters = array( |
303 | 208 | 'user' => $request->getVal( 'user', $wgUser->isLoggedIn() ), |
304 | 209 | 'lang' => $request->getVal( 'lang', $wgLang->getCode() ), |
— | — | @@ -310,31 +215,39 @@ |
311 | 216 | $parameters['debug'] = $parameters['debug'] === 'true' || $parameters['debug']; |
312 | 217 | // Get the direction from the requested language |
313 | 218 | if ( !isset( $parameters['dir'] ) ) { |
314 | | - $lang = $wgLang->factory( $parameters['lang'] ); |
| 219 | + $lang = Language::factory( $parameters['lang'] ); |
315 | 220 | $parameters['dir'] = $lang->getDir(); |
316 | 221 | } |
| 222 | + |
| 223 | + // Set parameters on all modules |
| 224 | + // FIXME: This sucks |
| 225 | + foreach ( self::$modules as $module ) { |
| 226 | + $module->setParameters( $parameters ); |
| 227 | + } |
| 228 | + |
317 | 229 | // Build a list of requested modules excluding unrecognized ones which are collected into a list used to |
318 | 230 | // register the unrecognized modules with error status later on |
319 | 231 | $modules = array(); |
320 | 232 | $missing = array(); |
321 | | - foreach ( explode( '|', $request->getVal( 'modules' ) ) as $module ) { |
322 | | - if ( isset( self::$modules[$module] ) ) { |
323 | | - $modules[] = $module; |
| 233 | + foreach ( explode( '|', $request->getVal( 'modules' ) ) as $name ) { |
| 234 | + if ( self::getModule( $name ) ) { |
| 235 | + $modules[] = $name; |
324 | 236 | } else { |
325 | | - $missing[] = $module; |
| 237 | + $missing[] = $name; |
326 | 238 | } |
327 | 239 | } |
328 | 240 | // Use output buffering |
329 | 241 | ob_start(); |
330 | 242 | // Output raw modules first and build a list of raw modules to be registered with ready status later on |
331 | 243 | $ready = array(); |
332 | | - foreach ( $modules as $module ) { |
333 | | - if ( self::$modules[$module]['raw'] ) { |
334 | | - self::read( self::$modules[$module]['script'], true ); |
335 | | - if ( $parameters['debug'] && self::$modules[$module]['debug'] ) { |
336 | | - self::read( self::$modules[$module]['debug'], true ); |
| 244 | + foreach ( $modules as $name ) { |
| 245 | + $module = self::getModule( $name ); |
| 246 | + if ( $module->isRaw() ) { |
| 247 | + echo $module->getScript(); |
| 248 | + if ( $parameters['debug'] ) { |
| 249 | + echo $module->getDebugScript(); |
337 | 250 | } |
338 | | - $ready[] = $module; |
| 251 | + $ready[] = $name; |
339 | 252 | } |
340 | 253 | } |
341 | 254 | // Special meta-information for the 'mediawiki' module |
— | — | @@ -344,14 +257,16 @@ |
345 | 258 | // Generate list of registrations and collect all loader scripts |
346 | 259 | $loaders = array(); |
347 | 260 | $registrations = array(); |
348 | | - foreach ( self::$modules as $name => $options ) { |
349 | | - if ( $options['loader'] ) { |
350 | | - $loaders[] = $options['loader']; |
| 261 | + foreach ( self::$modules as $name => $module ) { |
| 262 | + $loader = $module->getLoaderScript(); |
| 263 | + if ( $loader !== false ) { |
| 264 | + echo $loader; |
351 | 265 | } else { |
352 | | - if ( empty( $options['needs'] ) && !in_array( $name, $ready ) && !in_array( $name, $missing ) ) { |
| 266 | + if ( !count( $module->getDependencies() ) && |
| 267 | + !in_array( $name, $ready ) && !in_array( $name, $missing ) ) { |
353 | 268 | $registrations[$name] = $name; |
354 | 269 | } else { |
355 | | - $registrations[$name] = array( $name, $options['needs'] ); |
| 270 | + $registrations[$name] = array( $name, $module->getDependencies() ); |
356 | 271 | if ( in_array( $name, $ready ) ) { |
357 | 272 | $registrations[$name][] = 'ready'; |
358 | 273 | } else if ( in_array( $name, $missing ) ) { |
— | — | @@ -360,44 +275,34 @@ |
361 | 276 | } |
362 | 277 | } |
363 | 278 | } |
364 | | - // Include loaders |
365 | | - self::read( $loaders, true ); |
366 | 279 | // Register modules without loaders |
367 | 280 | echo "mediaWiki.loader.register( " . FormatJson::encode( array_values( $registrations ) ) . " );\n"; |
368 | 281 | } |
369 | 282 | // Output non-raw modules |
370 | 283 | $blobs = MessageBlobStore::get( $modules, $parameters['lang'] ); |
371 | | - foreach ( $modules as $module ) { |
372 | | - if ( !self::$modules[$module]['raw'] ) { |
| 284 | + foreach ( $modules as $name ) { |
| 285 | + $module = self::getModule( $name ); |
| 286 | + if ( !$module->isRaw() ) { |
373 | 287 | // Script |
374 | | - if ( !is_null( self::$modules[$module]['callback'] ) ) { |
375 | | - $script = call_user_func( self::$modules[$module]['callback'], $parameters ); |
376 | | - } else { |
377 | | - $script = self::read( self::$modules[$module]['script'] ); |
378 | | - } |
| 288 | + $script = $module->getScript(); |
379 | 289 | // Debug |
380 | | - if ( $parameters['debug'] && self::$modules[$module]['debug'] ) { |
381 | | - $script .= self::read( self::$modules[$module]['debug'] ); |
| 290 | + if ( $parameters['debug'] ) { |
| 291 | + $script .= $module->getDebugScript(); |
382 | 292 | } |
383 | | - // Locale |
384 | | - if ( isset( self::$modules[$module]['locales'][$parameters['lang']] ) ) { |
385 | | - $script .= self::read( self::$modules[$module]['locales'][$parameters['lang']] ); |
386 | | - } |
| 293 | + // Language-specific scripts |
| 294 | + $script .= $module->getLanguageScript( $parameters['lang'] ); |
387 | 295 | // Style |
388 | | - $style = self::$modules[$module]['style'] ? self::read( self::$modules[$module]['style'] ) : ''; |
389 | | - // Theme |
390 | | - if ( isset( self::$modules[$module]['themes'][$parameters['skin']] ) ) { |
391 | | - $style .= self::read( self::$modules[$module]['themes'][$parameters['skin']] ); |
392 | | - } else if ( isset( self::$modules[$module]['themes']['default'] ) ) { |
393 | | - $style .= self::read( self::$modules[$module]['themes']['default'] ); |
394 | | - } |
| 296 | + $style = $module->getStyle(); |
| 297 | + // Skin-specific styles |
| 298 | + $style .= $module->getSkinStyle( $parameters['skin'] ); |
| 299 | + |
395 | 300 | if ( $style !== '' ) { |
396 | 301 | if ( $parameters['dir'] == 'rtl' ) { |
397 | 302 | $style = self::filter( 'flip-css', $style ); |
398 | 303 | } |
399 | 304 | $style = Xml::escapeJsString( |
400 | 305 | $parameters['debug'] ? |
401 | | - $style : self::filter( 'minify-css', $style, self::$modules[$module]['style'] ) |
| 306 | + $style : self::filter( 'minify-css', $style ) |
402 | 307 | ); |
403 | 308 | } |
404 | 309 | // Messages |
Index: branches/resourceloader/phase3/includes/AutoLoader.php |
— | — | @@ -190,6 +190,8 @@ |
191 | 191 | 'ReplacementArray' => 'includes/StringUtils.php', |
192 | 192 | 'Replacer' => 'includes/StringUtils.php', |
193 | 193 | 'ResourceLoader' => 'includes/ResourceLoader.php', |
| 194 | + 'ResourceLoaderModule' => 'includes/ResourceLoaderModule.php', |
| 195 | + 'ResourceLoaderSiteJSModule' => 'includes/ResourceLoaderModule.php', |
194 | 196 | 'ReverseChronologicalPager' => 'includes/Pager.php', |
195 | 197 | 'Revision' => 'includes/Revision.php', |
196 | 198 | 'RevisionDelete' => 'includes/RevisionDelete.php', |
Index: branches/resourceloader/phase3/includes/MessageBlobStore.php |
— | — | @@ -93,8 +93,8 @@ |
94 | 94 | } else { |
95 | 95 | // Update msg_resource_links |
96 | 96 | $rows = array(); |
97 | | - $allModules = ResourceLoader::getModules(); |
98 | | - foreach ( $allModules[$module]['messages'] as $key ) { |
| 97 | + $mod = ResourceLoader::getModule( $module ); |
| 98 | + foreach ( $mod->getMessages() as $key ) { |
99 | 99 | $rows[] = array( |
100 | 100 | 'mrl_resource' => $module, |
101 | 101 | 'mrl_message' => $key |
— | — | @@ -292,12 +292,9 @@ |
293 | 293 | * @return string JSON object |
294 | 294 | */ |
295 | 295 | private static function generateMessageBlob( $module, $lang ) { |
296 | | - $allModules = ResourceLoader::getModules(); |
297 | | - if ( !isset ( $allModules[$module]['messages'] ) ) { |
298 | | - return false; |
299 | | - } |
| 296 | + $mod = ResourceLoader::getModule( $module ); |
300 | 297 | $messages = array(); |
301 | | - foreach ( $allModules[$module]['messages'] as $key ) { |
| 298 | + foreach ( $mod->getMessages() as $key ) { |
302 | 299 | $messages[$key] = wfMsgExt( $key, array( 'language' => $lang ) ); |
303 | 300 | } |
304 | 301 | return FormatJson::encode( $messages ); |
Index: branches/resourceloader/phase3/includes/ResourceLoaderModule.php |
— | — | @@ -0,0 +1,329 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * This program is free software; you can redistribute it and/or modify |
| 5 | + * it under the terms of the GNU General Public License as published by |
| 6 | + * the Free Software Foundation; either version 2 of the License, or |
| 7 | + * (at your option) any later version. |
| 8 | + * |
| 9 | + * This program is distributed in the hope that it will be useful, |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | + * GNU General Public License for more details. |
| 13 | + * |
| 14 | + * You should have received a copy of the GNU General Public License along |
| 15 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 16 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 17 | + * http://www.gnu.org/copyleft/gpl.html |
| 18 | + * |
| 19 | + * @author Roan Kattouw |
| 20 | + */ |
| 21 | + |
| 22 | +// TODO: Class comment |
| 23 | +// TODO: Add an interface to inherit from rather than having to subclass this class, or add an empty-returning superclass |
| 24 | +class ResourceLoaderModule { |
| 25 | + private $scripts = array(); |
| 26 | + private $styles = array(); |
| 27 | + private $messages = array(); |
| 28 | + private $dependencies = array(); |
| 29 | + private $debugScripts = array(); |
| 30 | + private $languageScripts = array(); |
| 31 | + private $skinStyles = array(); |
| 32 | + private $loaders = array(); |
| 33 | + private $raw = false; |
| 34 | + private $parameters = array(); |
| 35 | + |
| 36 | + /* Public methods */ |
| 37 | + |
| 38 | + /** |
| 39 | + * Construct a new module from an options array. |
| 40 | + * |
| 41 | + * Keys recognized in options array are 'script', 'debug', 'locales', |
| 42 | + * 'themes', 'raw', 'needs', 'style' and 'messages'. For more |
| 43 | + * information, see their respective setters: addScripts(), |
| 44 | + * addDebugScripts(), addLanguageScripts(), addSkinStyles(), setRaw(), |
| 45 | + * addDependencies(), addStyles() and addMessages() |
| 46 | + * @param $options array Options array. If empty, an empty module will be constructed |
| 47 | + */ |
| 48 | + public function __construct( $options = array() ) { |
| 49 | + foreach ( $options as $option => $value ) { |
| 50 | + switch ( $option ) { |
| 51 | + case 'script': |
| 52 | + $this->scripts = (array)$value; |
| 53 | + break; |
| 54 | + case 'style': |
| 55 | + $this->styles = $value; |
| 56 | + break; |
| 57 | + case 'messages': |
| 58 | + $this->messages = $value; |
| 59 | + break; |
| 60 | + case 'needs': |
| 61 | + $this->dependencies = $value; |
| 62 | + break; |
| 63 | + case 'debug': |
| 64 | + $this->debugScripts = (array)$value; |
| 65 | + break; |
| 66 | + case 'locales': |
| 67 | + $this->languageScripts = (array)$value; |
| 68 | + break; |
| 69 | + case 'themes': |
| 70 | + $this->skinStyles = (array)$value; |
| 71 | + break; |
| 72 | + case 'loader': |
| 73 | + $this->loaders = (array)$value; |
| 74 | + break; |
| 75 | + case 'raw': |
| 76 | + $this->raw = (bool)$value; |
| 77 | + break; |
| 78 | + } |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + /** |
| 83 | + * Add script files to this module. In order to be valid, a module |
| 84 | + * must contain at least one script file. |
| 85 | + * @param $scripts mixed Path to script file (string) or array of paths |
| 86 | + */ |
| 87 | + public function addScripts( $scripts ) { |
| 88 | + $this->scripts = array_merge( $this->scripts, (array)$scripts ); |
| 89 | + } |
| 90 | + |
| 91 | + /** |
| 92 | + * Add style (CSS) files to this module. |
| 93 | + * @param $styles mixed Path to CSS file (string) or array of paths |
| 94 | + */ |
| 95 | + public function addStyles( $styles ) { |
| 96 | + $this->styles = array_merge( $this->styles, (array)$styles ); |
| 97 | + } |
| 98 | + |
| 99 | + /** |
| 100 | + * Add messages to this module. |
| 101 | + * @param $messages mixed Message key (string) or array of message keys |
| 102 | + */ |
| 103 | + public function addMessages( $messages ) { |
| 104 | + $this->messages = array_merge( $this->messages, (array)$messages ); |
| 105 | + } |
| 106 | + |
| 107 | + /** |
| 108 | + * Add dependencies. Dependency information is taken into account when |
| 109 | + * loading a module on the client side. When adding a module on the |
| 110 | + * server side, dependency information is NOT taken into account and |
| 111 | + * YOU are responsible for adding dependent modules as well. If you |
| 112 | + * don't do this, the client side loader will send a second request |
| 113 | + * back to the server to fetch the missing modules, which kind of |
| 114 | + * defeats the point of using the resource loader in the first place. |
| 115 | + * |
| 116 | + * To add dependencies dynamically on the client side, use a custom |
| 117 | + * loader (see addLoaders()) |
| 118 | + * |
| 119 | + * @param $dependencies mixed Module name (string) or array of module names |
| 120 | + */ |
| 121 | + public function addDependencies( $dependencies ) { |
| 122 | + $this->dependencies = array_merge( $this->dependencies, (array)$dependencies ); |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * Add debug scripts to the module. These scripts are only included |
| 127 | + * in debug mode. |
| 128 | + * @param $scripts mixed Path to script file (string) or array of paths |
| 129 | + */ |
| 130 | + public function addDebugScripts( $scripts ) { |
| 131 | + $this->debugScripts = array_merge( $this->debugScripts, (array)$scripts ); |
| 132 | + } |
| 133 | + |
| 134 | + /** |
| 135 | + * Add language-specific scripts. These scripts are only included for |
| 136 | + * a given language. |
| 137 | + * @param $lang string Language code |
| 138 | + * @param $scripts mixed Path to script file (string) or array of paths |
| 139 | + */ |
| 140 | + public function addLanguageScripts( $lang, $scripts ) { |
| 141 | + $this->languageScripts = array_merge_recursive( |
| 142 | + $this->languageScripts, |
| 143 | + array( $lang => $scripts ) |
| 144 | + ); |
| 145 | + } |
| 146 | + |
| 147 | + /** |
| 148 | + * Add skin-specific CSS. These CSS files are only included for a |
| 149 | + * given skin. If there are no skin-specific CSS files for a skin, |
| 150 | + * the files defined for 'default' will be used, if any. |
| 151 | + * @param $skin string Skin name, or 'default' |
| 152 | + * @param $scripts mixed Path to CSS file (string) or array of paths |
| 153 | + */ |
| 154 | + public function addSkinStyles( $skin, $scripts ) { |
| 155 | + $this->skinStyles = array_merge_recursive( |
| 156 | + $this->skinStyles, |
| 157 | + array( $skin => $scripts ) |
| 158 | + ); |
| 159 | + } |
| 160 | + |
| 161 | + /** |
| 162 | + * Add loader scripts. These scripts are loaded on every page and are |
| 163 | + * responsible for registering this module using |
| 164 | + * mediaWiki.loader.register(). If there are no loader scripts defined, |
| 165 | + * the resource loader will register the module itself. |
| 166 | + * |
| 167 | + * Loader scripts are used to determine a module's dependencies |
| 168 | + * dynamically on the client side (e.g. based on browser type/version). |
| 169 | + * Note that loader scripts are included on every page, so they should |
| 170 | + * be lightweight and use mediaWiki.loader.register()'s callback |
| 171 | + * feature to defer dependency calculation. |
| 172 | + * @param $scripts mixed Path to script file (string) or array of paths |
| 173 | + */ |
| 174 | + public function addLoaders( $scripts ) { |
| 175 | + $this->loaders = array_merge( $this->loaders, (array)$scripts ); |
| 176 | + } |
| 177 | + |
| 178 | + /** |
| 179 | + * Set this module's raw flag. Raw modules are loaded before the |
| 180 | + * mediaWiki object and are not wrapped in any loading code. |
| 181 | + * They can only have scripts and debug scripts, nothing else. |
| 182 | + * Raw module can still be used as dependencies for other modules. |
| 183 | + * |
| 184 | + * Modules are non-raw by default. |
| 185 | + * |
| 186 | + * @param $raw bool If true, mark this module as raw, if false, mark it non-raw |
| 187 | + */ |
| 188 | + public function setRaw( $raw = true ) { |
| 189 | + $this->raw = (bool)$raw; |
| 190 | + } |
| 191 | + |
| 192 | + /** |
| 193 | + * Set parameters used for obtaining this module. Ignored in this class |
| 194 | + * but used in subclasses. |
| 195 | + * |
| 196 | + * FIXME: This is ugly and should ideally be killed with fire. I just don't know how else to do skin JS ATM. |
| 197 | + */ |
| 198 | + public function setParameters( $params ) { |
| 199 | + $this->parameters = $params; |
| 200 | + } |
| 201 | + |
| 202 | + /** |
| 203 | + * Get the primary JS for this module. This is pulled from the |
| 204 | + * script files added through addScripts() |
| 205 | + * @return string JS |
| 206 | + */ |
| 207 | + public function getScript() { |
| 208 | + return self::concatFiles( $this->scripts ); |
| 209 | + } |
| 210 | + |
| 211 | + /** |
| 212 | + * Get the CSS for this module. This is pulled from the CSS files |
| 213 | + * added through addStyles() |
| 214 | + * @return string JS |
| 215 | + */ |
| 216 | + public function getStyle() { |
| 217 | + return self::concatFiles( $this->styles ); |
| 218 | + } |
| 219 | + |
| 220 | + /** |
| 221 | + * Get the messages added to this module with addMessages(). |
| 222 | + * |
| 223 | + * To get a JSON blob with messages, use MessageBlobStore::get() |
| 224 | + * @return array of message keys. Keys may occur more than once |
| 225 | + */ |
| 226 | + public function getMessages() { |
| 227 | + return $this->messages; |
| 228 | + } |
| 229 | + |
| 230 | + /** |
| 231 | + * Get the dependencies added to this module with addDependencies() |
| 232 | + * @return array of module names (strings) |
| 233 | + */ |
| 234 | + public function getDependencies() { |
| 235 | + return $this->dependencies; |
| 236 | + } |
| 237 | + |
| 238 | + /** |
| 239 | + * Get the debug JS for this module. This is pulled from the script |
| 240 | + * files added through addDebugScripts() |
| 241 | + * @return string JS |
| 242 | + */ |
| 243 | + public function getDebugScript() { |
| 244 | + return self::concatFiles( $this->debugScripts ); |
| 245 | + } |
| 246 | + |
| 247 | + /** |
| 248 | + * Get the language-specific JS for a given language. This is pulled |
| 249 | + * from the language-specific script files added through addLanguageScripts() |
| 250 | + * @return string JS |
| 251 | + */ |
| 252 | + public function getLanguageScript( $lang ) { |
| 253 | + if ( !isset( $this->languageScripts[$lang] ) ) { |
| 254 | + return ''; |
| 255 | + } |
| 256 | + return self::concatFiles( $this->languageScripts[$lang] ); |
| 257 | + } |
| 258 | + |
| 259 | + /** |
| 260 | + * Get the skin-specific CSS for a given skin. This is pulled from the |
| 261 | + * skin-specific CSS files added through addSkinStyles() |
| 262 | + * @return string CSS |
| 263 | + */ |
| 264 | + public function getSkinStyle( $skin ) { |
| 265 | + $styles = array(); |
| 266 | + if ( isset( $this->skinStyles[$skin] ) && count( $this->skinStyles[$skin] ) ) { |
| 267 | + $styles = $this->skinStyles[$skin]; |
| 268 | + } else if ( isset( $this->skinStyles['default'] ) ) { |
| 269 | + $styles = $this->skinStyles['default']; |
| 270 | + } |
| 271 | + return self::concatFiles( $this->skinStyles[$skin] ); |
| 272 | + } |
| 273 | + |
| 274 | + /** |
| 275 | + * Get the custom loader JS, if set. This is pulled from the script |
| 276 | + * files added through addLoaders() |
| 277 | + * @return mixed Loader JS (string) or false if no custom loader set |
| 278 | + */ |
| 279 | + public function getLoaderScript() { |
| 280 | + if ( count( $this->loaderScripts ) == 0 ) { |
| 281 | + return false; |
| 282 | + } |
| 283 | + return self::concatFiles( $this->loaderScripts ); |
| 284 | + } |
| 285 | + |
| 286 | + /** |
| 287 | + * Check whether this module is in raw mode. See setRaw() for details |
| 288 | + * on what raw mode is. |
| 289 | + * @return bool |
| 290 | + */ |
| 291 | + public function isRaw() { |
| 292 | + return $this->raw; |
| 293 | + } |
| 294 | + |
| 295 | + /** |
| 296 | + * Get a parameter previously set through setParameters() |
| 297 | + * @param $param string Parameter name |
| 298 | + * @return mixed Parameter value or null if not set |
| 299 | + */ |
| 300 | + public function getParameter( $param ) { |
| 301 | + return isset( $this->parameters[$param] ? $this->parameters[$param] : null; |
| 302 | + } |
| 303 | + |
| 304 | + /** |
| 305 | + * Get the contents of a set of files and concatenate them, with |
| 306 | + * newlines in between. Each file is used only once. |
| 307 | + * @param $files array Array of file names |
| 308 | + * @return string Concatenated contents of $files |
| 309 | + */ |
| 310 | + protected static function concatFiles( $files ) { |
| 311 | + return implode( "\n", array_map( 'file_get_contents', array_unique( $files ) ) ); |
| 312 | + } |
| 313 | + |
| 314 | +} |
| 315 | + |
| 316 | +class ResourceLoaderSiteJSModule extends ResourceLoaderModule { |
| 317 | + public function getScript( $skin ) { |
| 318 | + return Skin::newFromKey( $this->getParameter( 'skin' ) )->generateUserJs(); |
| 319 | + } |
| 320 | + |
| 321 | + // Dummy overrides to return emptyness |
| 322 | + public function getStyle() { return ''; } |
| 323 | + public function getMessages() { return array(); } |
| 324 | + public function getDependencies() { return array(); } |
| 325 | + public function getDebugScript() { return ''; } |
| 326 | + public function getLanguageScript( $lang ) { return ''; } |
| 327 | + public function getSkinStyle( $skin ) { return ''; } |
| 328 | + public function getLoaderScript() { return false; } |
| 329 | + public function isRaw() { return true; } |
| 330 | +} |
Property changes on: branches/resourceloader/phase3/includes/ResourceLoaderModule.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 331 | + native |
Index: branches/resourceloader/phase3/resources/Resources.php |
— | — | @@ -2,8 +2,8 @@ |
3 | 3 | |
4 | 4 | ResourceLoader::register( array( |
5 | 5 | |
6 | | - /* Special resources (callbacks) */ |
7 | | - 'sitejs' => array( 'callback' => array( 'ResourceLoader', 'siteJSCallback' ) ), |
| 6 | + /* Special resources who have their own classes */ |
| 7 | + 'sitejs' => array( 'class' => 'ResourceLoaderSiteJSModule' ), |
8 | 8 | |
9 | 9 | /* jQuery */ |
10 | 10 | |