Index: branches/resourceloader/phase3/includes/ResourceLoader.php |
— | — | @@ -15,315 +15,310 @@ |
16 | 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | 17 | * http://www.gnu.org/copyleft/gpl.html |
18 | 18 | * |
19 | | - * @author Roan Kattouw |
20 | | - * |
| 19 | + * @author Roan Kattouw, Trevor Parscal |
21 | 20 | */ |
22 | 21 | |
23 | | -/** |
24 | | - * TODO: Class description |
| 22 | +/* |
| 23 | + * Dynamic JavaScript and CSS resource loading system |
| 24 | + * |
| 25 | + * @example |
| 26 | + * // Registers a module with the resource loading system |
| 27 | + * ResourceLoader::register( 'foo', array( |
| 28 | + * // At minimum you must have a script file |
| 29 | + * 'script' => 'resources/foo/foo.js', |
| 30 | + * // Optionally you can have a style file as well |
| 31 | + * 'style' => 'resources/foo/foo.css', |
| 32 | + * // Only needed if you are doing something fancy with your loader, otherwise one will be generated for you |
| 33 | + * 'loader' => 'resources/foo/loader.js', |
| 34 | + * // If you need any localized messages brought into the JavaScript environment, list the keys here |
| 35 | + * 'messages' => array( 'foo-hello', 'foo-goodbye' ), |
| 36 | + * // Base-only scripts are special scripts loaded in the base-package |
| 37 | + * 'base' => false, |
| 38 | + * // Debug-only scripts are special scripts that are only loaded when requested and while in debug mode |
| 39 | + * 'debug' => false, |
| 40 | + * ) ); |
| 41 | + * @example |
| 42 | + * // Responds to a resource loading request |
| 43 | + * ResourceLoader::respond( $wgRequest ); |
25 | 44 | */ |
26 | 45 | class ResourceLoader { |
27 | | - /** |
28 | | - * List of core scripts to include if the "core" module is specified - it's like a bucket |
29 | | - */ |
30 | | - private static $coreScripts = array( |
31 | | - 'jquery' => 'resources/core/jquery-1.4.2.js', |
32 | | - 'mw' => 'resources/core/mw.js', |
33 | | - 'mw.config' => 'resources/core/mw/mw.config.js', |
34 | | - 'mw.loader' => 'resources/core/mw/mw.loader.js', |
35 | | - 'mw.msg' => 'resources/core/mw/mw.msg.js', |
36 | | - 'mw.util' => 'resources/core/mw/mw.util.js', |
37 | | - ); |
38 | | - private static $debugScripts = array( |
39 | | - 'mw.debug' => 'resources/core/mw/mw.debug.js', |
40 | | - 'mw.log' => 'resources/core/mw/mw.log.js', |
41 | | - ); |
42 | | - /** |
43 | | - * List of modules. |
44 | | - * |
45 | | - * Format: |
46 | | - * 'modulename' => array( |
47 | | - * 'script' => 'resources/foo/bar.js', |
48 | | - * 'loader' => 'resources/foo/loader.js', |
49 | | - * 'style' => 'resources/foo/bar.css', |
50 | | - * 'messages' => array( 'messagekey1', 'messagekey2' ) |
51 | | - * ); |
52 | | - * 'script' and 'loader' are mandatory. |
53 | | - */ |
54 | | - public static $modules = array( |
55 | | - 'test' => array( |
56 | | - 'script' => 'resources/test/test.js', |
57 | | - 'loader' => 'resources/test/loader.js', |
58 | | - 'style' => 'resources/test/test.css', |
59 | | - ), |
60 | | - 'foo' => array( |
61 | | - 'script' => 'resources/test/foo.js', |
62 | | - 'loader' => 'resources/test/loader.js', |
63 | | - 'style' => 'resources/test/foo.css', |
64 | | - ), |
65 | | - 'bar' => array( |
66 | | - 'script' => 'resources/test/bar.js', |
67 | | - 'loader' => 'resources/test/loader.js', |
68 | | - 'style' => 'resources/test/bar.css', |
69 | | - ), |
70 | | - 'buz' => array( |
71 | | - 'script' => 'resources/test/buz.js', |
72 | | - 'loader' => 'resources/test/loader.js', |
73 | | - 'style' => 'resources/test/buz.css', |
74 | | - ), |
75 | | - 'baz' => array( |
76 | | - 'script' => 'resources/test/baz.js', |
77 | | - 'loader' => 'resources/test/loader.js', |
78 | | - 'style' => 'resources/test/baz.css', |
79 | | - ), |
80 | | - 'wikibits' => array( |
81 | | - 'script' => 'skins/common/wikibits.js', |
82 | | - 'loader' => 'skins/common/loader.js', |
83 | | - ), |
84 | | - ); |
85 | 46 | |
86 | | - private $loadedModules = array(); |
87 | | - private $includeCore = false; |
| 47 | + /* Protected Static Members */ |
88 | 48 | |
89 | | - private $useJSMin = false; |
90 | | - private $useCSSMin = true; |
91 | | - private $useCSSJanus = true; |
92 | | - private $useDebugMode = false; |
| 49 | + protected static $modules = array(); |
93 | 50 | |
94 | | - private $lang; |
| 51 | + /* Protected Static Methods */ |
95 | 52 | |
96 | | - public function __construct( $lang ) { |
97 | | - $this->lang = $lang; |
98 | | - } |
99 | | - |
100 | 53 | /** |
101 | | - * Add a module to the output. This includes the module's |
102 | | - * JS itself, its style and its messages. |
103 | | - * @param $module string Module name |
| 54 | + * Runs text through a filter, caching the filtered result for future calls |
| 55 | + * |
| 56 | + * @param string $filter name of filter to run |
| 57 | + * @param string $data text to filter, such as JavaScript or CSS text |
| 58 | + * @param string $file path to file being filtered, (optional: only required for CSS to resolve paths) |
| 59 | + * @return string filtered data |
104 | 60 | */ |
105 | | - public function addModule( $module ) { |
106 | | - if ( $module == 'core' ) { |
107 | | - $this->includeCore = true; |
108 | | - } else if ( isset( self::$modules[$module] ) ) { |
109 | | - $this->loadedModules[] = $module; |
110 | | - } else { |
111 | | - // We have a problem, they've asked for something we don't have! |
112 | | - } |
113 | | - } |
114 | | - |
115 | | - public function setUseJSMin( $use ) { |
116 | | - $this->useJSMin = $use; |
117 | | - } |
118 | | - |
119 | | - public function setUseCSSMin( $use ) { |
120 | | - $this->useCSSMin = $use; |
121 | | - } |
122 | | - |
123 | | - public function setUseCSSJanus( $use ) { |
124 | | - $this->useCSSJanus = $use; |
125 | | - } |
126 | | - |
127 | | - public function setUseDebugMode( $use ) { |
128 | | - $this->useDebugMode = $use; |
129 | | - } |
130 | | - |
131 | | - public function getOutput() { |
132 | | - // Because these are keyed by module, in the case that more than one module asked for the same script only the |
133 | | - // first will end up being registered - the client loader can't handle multiple modules per implementation yet, |
134 | | - // so this is fine, but causes silent failure it strange abusive cases |
135 | | - $this->loadedModules = array_unique( $this->loadedModules ); |
136 | | - $retval = ''; |
137 | | - |
138 | | - if ( $this->includeCore ) { |
139 | | - // TODO: file_get_contents() errors? |
140 | | - // TODO: CACHING! |
141 | | - foreach ( self::$coreScripts as $script ) { |
142 | | - if ( file_exists( $script ) ) { |
143 | | - $retval .= file_get_contents( $script ); |
144 | | - } |
145 | | - } |
146 | | - if ( $this->useDebugMode ) { |
147 | | - // TODO: file_get_contents() errors? |
148 | | - // TODO: CACHING! |
149 | | - foreach ( self::$debugScripts as $script ) { |
150 | | - if ( file_exists( $script ) ) { |
151 | | - $retval .= file_get_contents( $script ); |
152 | | - } |
153 | | - } |
154 | | - } |
155 | | - $retval .= $this->getLoaderJS(); |
156 | | - } |
157 | | - |
158 | | - /* |
159 | | - * Skin::makeGlobalVariablesScript needs to be modified so that we still output the globals for now, but also |
160 | | - * put them into the initial payload like this: |
161 | | - * |
162 | | - * // Sets the inital configuration |
163 | | - * mw.config.set( { 'name': 'value', ... } ); |
164 | | - * |
165 | | - * Also, the naming of these variables is horrible and sad, hopefully this can be worked on |
166 | | - */ |
167 | | - |
168 | | - // Get messages in one go |
169 | | - $blobs = MessageBlobStore::get( $this->lang, $this->loadedModules ); |
170 | | - |
171 | | - // TODO: file_get_contents() errors? |
172 | | - // TODO: CACHING! |
173 | | - foreach ( $this->loadedModules as $module ) { |
174 | | - $mod = self::$modules[$module]; |
175 | | - $script = $style = ''; |
176 | | - $messages = isset( $blobs[$module] ) ? $blobs[$module] : ''; |
177 | | - if ( file_exists( $mod['script'] ) ) { |
178 | | - $script = file_get_contents( $mod['script'] ); |
179 | | - } |
180 | | - if ( !$this->useDebugMode ) { |
181 | | - $script = preg_replace( '/\n\s*mw\.log\(([^\)]*\))*\s*[\;\n]/U', "\n", $script ); |
182 | | - } |
183 | | - if ( isset( $mod['style'] ) && file_exists( $mod['style'] ) ) { |
184 | | - $css = file_get_contents( $mod['style'] ); |
185 | | - if ( $this->useCSSJanus ) { |
186 | | - $css = $this->cssJanus( $css ); |
187 | | - } |
188 | | - if ( $this->useCSSMin ) { |
189 | | - $css = $this->cssMin( $css, $mod['style'] ); |
190 | | - } |
191 | | - $style = Xml::escapeJsString( $css ); |
192 | | - } |
193 | | - |
194 | | - $retval .= "mw.loader.implement( '$module', function() { $script }, '$style', { $messages } );\n"; |
195 | | - } |
196 | | - |
197 | | - if ( !$this->useDebugMode && $this->useJSMin ) { |
198 | | - $retval = $this->jsMin( $retval ); |
199 | | - } |
200 | | - return $retval; |
201 | | - } |
202 | | - |
203 | | - public function getLoaderJS() { |
204 | | - $retval = ''; |
205 | | - // Only add each file once (just in case there are multiple modules in a single loader, which is common) |
206 | | - $loaders = array(); |
207 | | - foreach ( self::$modules as $name => $module ) { |
208 | | - // TODO: file_get_contents() errors? |
209 | | - // TODO: CACHING! |
210 | | - if ( !in_array( $module['loader'], $loaders ) && file_exists( $module['loader'] ) ) { |
211 | | - $retval .= file_get_contents( $module['loader'] ); |
212 | | - $loaders[] = $module['loader']; |
213 | | - } |
214 | | - } |
215 | | - // FIXME: Duplicated; centralize in doJSTransforms() or something? |
216 | | - if ( $this->useJSMin ) { |
217 | | - $retval = $this->jsMin( $retval ); |
218 | | - } |
219 | | - return $retval; |
220 | | - } |
221 | | - |
222 | | - public function jsMin( $js ) { |
| 61 | + protected static function filter( $filter, $data, $file = null ) { |
223 | 62 | global $wgMemc; |
224 | | - $key = wfMemcKey( 'resourceloader', 'jsmin', md5( $js ) ); |
| 63 | + $key = wfMemcKey( 'resourceloader', $filter, md5( $data ) ); |
225 | 64 | $cached = $wgMemc->get( $key ); |
226 | 65 | if ( $cached !== false && $cached !== null ) { |
227 | 66 | return $cached; |
228 | 67 | } |
229 | | - $retval = JSMin::minify( $js ); |
230 | | - $wgMemc->set( $key, $retval ); |
231 | | - return $retval; |
232 | | - } |
233 | | - |
234 | | - public function cssMin( $css, $file ) { |
235 | | - global $wgMemc; |
236 | | - $key = wfMemcKey( 'resourceloader', 'cssmin', md5( $css ) ); |
237 | | - $cached = $wgMemc->get( $key ); |
238 | | - if( $cached !== false && $cached !== null ) { |
239 | | - return $cached; |
| 68 | + switch ( $filter ) { |
| 69 | + case 'minify-js': |
| 70 | + $result = JSMin::minify( $data ); |
| 71 | + break; |
| 72 | + case 'minify-css': |
| 73 | + $result = Minify_CSS::minify( $data, array( 'currentDir' => dirname( $file ), 'docRoot' => '.' ) ); |
| 74 | + break; |
| 75 | + case 'flip-css': |
| 76 | + $cssJanus = new CSSJanus(); |
| 77 | + $result = $cssJanus::transform( $data, true, false ); |
| 78 | + break; |
| 79 | + case 'strip-debug': |
| 80 | + $result = preg_replace( '/\n\s*mw\.log\(([^\)]*\))*\s*[\;\n]/U', "\n", $data ); |
| 81 | + break; |
| 82 | + default: |
| 83 | + // Don't cache anything, just pass right through |
| 84 | + return $data; |
240 | 85 | } |
241 | | - // TODO: Test how well this path rewriting stuff works with various setups |
242 | | - $retval = Minify_CSS::minify( $css, array( 'currentDir' => dirname( $file ), 'docRoot' => '.' ) ); |
243 | | - $wgMemc->set( $key, $retval ); |
244 | | - return $retval; |
| 86 | + $wgMemc->set( $key, $result ); |
| 87 | + return $result; |
245 | 88 | } |
246 | | - |
247 | | - public function cssJanus( $css ) { |
248 | | - global $wgMemc; |
249 | | - $key = wfMemcKey( 'resourceloader', 'cssjanus', md5( $css ) ); |
250 | | - $cached = $wgMemc->get( $key ); |
251 | | - if ( $cached !== false && $cached !== null ) { |
252 | | - return $cached; |
253 | | - } |
254 | | - $retval = $css; // TODO: Actually flip |
255 | | - $wgMemc->set( $key, $retval ); |
256 | | - return $retval; |
257 | | - } |
258 | | -} |
259 | | - |
260 | | -class MessageBlobStore { |
261 | 89 | /** |
262 | | - * Get the message blobs for a set of modules |
263 | | - * @param $lang string Language code |
264 | | - * @param $modules array Array of module names |
265 | | - * @return array An array of incomplete JSON objects (i.e. without the {} ) containing messages keys and their values. Array keys are module names. |
| 90 | + * Get a list of JSON encoded message lists for a set of modules, automatically caching JSON blobs in the database |
| 91 | + * |
| 92 | + * @param string $lang language code |
| 93 | + * @param array $modules module names |
| 94 | + * @return array JSON objects containing message keys and values for each module, keyed by module name |
266 | 95 | */ |
267 | | - public static function get( $lang, $modules ) { |
| 96 | + protected static function messages( $lang, $modules ) { |
268 | 97 | // TODO: Invalidate blob when module touched |
269 | | - if ( !count( $modules ) ) { |
| 98 | + if ( empty( $modules ) ) { |
270 | 99 | return array(); |
271 | 100 | } |
272 | 101 | // Try getting from the DB first |
273 | | - $blobs = self::getFromDB( $lang, $modules ); |
274 | | - |
| 102 | + $blobs = array(); |
| 103 | + $dbr = wfGetDB( DB_SLAVE ); |
| 104 | + $result = $dbr->select( |
| 105 | + 'msg_resource', |
| 106 | + array( 'mr_blob', 'mr_resource' ), |
| 107 | + array( 'mr_resource' => $modules, 'mr_lang' => $lang ), |
| 108 | + __METHOD__ |
| 109 | + ); |
| 110 | + foreach ( $result as $row ) { |
| 111 | + $blobs[$row->mr_resource] = $row->mr_blob; |
| 112 | + } |
275 | 113 | // Generate blobs for any missing modules and store them in the DB |
276 | 114 | $missing = array_diff( $modules, array_keys( $blobs ) ); |
277 | 115 | foreach ( $missing as $module ) { |
278 | | - $blob = self::generateMessageBlob( $lang, $module ); |
279 | | - if ( $blob ) { |
280 | | - self::set( $lang, $module, $blob ); |
| 116 | + // Build message blob for module messages |
| 117 | + $messages = isset( static::$modules[$module]['messages'] ) ? |
| 118 | + array_keys( static::$modules[$module]['messages'] ) : false; |
| 119 | + if ( $messages ) { |
| 120 | + foreach ( ResourceLoader::$modules[$module]['messages'] as $key ) { |
| 121 | + $messages[$encKey] = wfMsgExt( $key, array( 'language' => $lang ) ); |
| 122 | + } |
| 123 | + $blob = json_encode( $messages ); |
| 124 | + // Store message blob |
| 125 | + $dbw = wfGetDb( DB_MASTER ); |
| 126 | + $dbw->replace( |
| 127 | + 'msg_resource', |
| 128 | + array( array( 'mr_lang', 'mr_resource' ) ), |
| 129 | + array( array( |
| 130 | + 'mr_lang' => $lang, |
| 131 | + 'mr_module' => $module, |
| 132 | + 'mr_blob' => $blob, |
| 133 | + 'mr_timestamp' => wfTimestampNow(), |
| 134 | + ) ) |
| 135 | + ); |
| 136 | + // Add message blob to result |
281 | 137 | $blobs[$module] = $blob; |
282 | 138 | } |
283 | 139 | } |
284 | 140 | return $blobs; |
285 | 141 | } |
286 | 142 | |
| 143 | + /* Static Methods */ |
| 144 | + |
287 | 145 | /** |
288 | | - * Set the message blob for a given module in a given language |
289 | | - * @param $lang string Language code |
290 | | - * @param $module string Module name |
291 | | - * @param $blob string Incomplete JSON object, see get() |
| 146 | + * Registers a module with the ResourceLoader system |
| 147 | + * |
| 148 | + * @param mixed $module string of name of module or array of name/options pairs |
| 149 | + * @param array $options module options (optional when using multiple-registration calling style) |
| 150 | + * @return boolean false if there were any errors, in which case one or more modules were not registered |
| 151 | + * |
| 152 | + * $options format: |
| 153 | + * array( |
| 154 | + * 'script' => [string: path to file], |
| 155 | + * 'style' => [string: path to file, optional], |
| 156 | + * 'loader' => [string: path to file, optional], |
| 157 | + * 'messages' => [array: message keys, optional], |
| 158 | + * 'base' => [boolean: include in base package only, optional], |
| 159 | + * 'debug' => [boolean: include in debug mode only, optional], |
| 160 | + * ) |
| 161 | + * |
| 162 | + * @todo We need much more clever error reporting, not just in detailing what happened, but in bringing errors to |
| 163 | + * the client in a way that they can easily see them if they want to, such as by using FireBug |
292 | 164 | */ |
293 | | - public static function set( $lang, $module, $blob ) { |
294 | | - $dbw = wfGetDb( DB_MASTER ); |
295 | | - $dbw->replace( 'msg_resource', array( array( 'mr_lang', 'mr_resource' ) ), |
296 | | - array( array( |
297 | | - 'mr_lang' => $lang, |
298 | | - 'mr_module' => $module, |
299 | | - 'mr_blob' => $blob, |
300 | | - 'mr_timestamp' => wfTimestampNow(), |
301 | | - ) ) |
302 | | - ); |
303 | | - } |
304 | | - |
305 | | - private static function getFromDB( $lang, $modules ) { |
306 | | - $retval = array(); |
307 | | - $dbr = wfGetDB( DB_SLAVE ); |
308 | | - $res = $dbr->select( 'msg_resource', array( 'mr_blob', 'mr_resource' ), |
309 | | - array( 'mr_resource' => $modules, 'mr_lang' => $lang ), |
310 | | - __METHOD__ |
311 | | - ); |
312 | | - foreach ( $res as $row ) { |
313 | | - $retval[$row->mr_resource] = $row->mr_blob; |
| 165 | + public static function register( $module, $options = array() ) { |
| 166 | + // Allow multiple modules to be registered in one call |
| 167 | + if ( is_array( $module ) && empty( $options ) ) { |
| 168 | + $success = true; |
| 169 | + foreach ( $module as $name => $options ) { |
| 170 | + if ( !static::register( $name, $options ) ) { |
| 171 | + $success = false; |
| 172 | + } |
| 173 | + } |
| 174 | + return $success; |
314 | 175 | } |
315 | | - return $retval; |
316 | | - } |
317 | | - |
318 | | - private static function generateMessageBlob( $lang, $module ) { |
319 | | - if ( !isset ( ResourceLoader::$modules[$module]['messages'] ) ) { |
| 176 | + // Disallow duplicate registrations |
| 177 | + if ( isset( static::$modules[$module] ) ) { |
| 178 | + // A module has already been registered by this name |
320 | 179 | return false; |
321 | 180 | } |
322 | | - $messages = array(); |
323 | | - foreach ( ResourceLoader::$modules[$module]['messages'] as $key ) { |
324 | | - $encKey = Xml::escapeJsString( $key ); |
325 | | - $encValue = Xml::escapeJsString( wfMsg( $key ) ); // TODO: Use something rawer than wfMsg()? |
326 | | - $messages[] = "'$encKey': '$encValue'"; |
| 181 | + // Always include a set of default options in each registration - more data, less isset() checks |
| 182 | + $options = array_merge( array( |
| 183 | + 'script' => null, |
| 184 | + 'style' => null, |
| 185 | + 'messages' => null, |
| 186 | + 'loader' => null, |
| 187 | + 'base' => false, |
| 188 | + 'debug' => false, |
| 189 | + ), $options ); |
| 190 | + // Validate script option |
| 191 | + if ( !is_string( $options['script'] ) ) { |
| 192 | + // Module does not include a script |
| 193 | + return false; |
327 | 194 | } |
328 | | - return implode( ",", $messages ); |
| 195 | + if ( !file_exists( $options['script'] ) ) { |
| 196 | + // Script file does not exist |
| 197 | + return false; |
| 198 | + } |
| 199 | + if ( $options['loader'] !== null && !file_exists( $options['loader'] ) ) { |
| 200 | + // Loader file does not exist |
| 201 | + return false; |
| 202 | + } |
| 203 | + if ( $options['style'] !== null && !file_exists( $options['style'] ) ) { |
| 204 | + // Style file does not exist |
| 205 | + return false; |
| 206 | + } |
| 207 | + static::$modules[$module] = $options; |
329 | 208 | } |
| 209 | + /* |
| 210 | + * Outputs a response to a resource load-request, including a content-type header |
| 211 | + * |
| 212 | + * @param array $modules module names to include in the request |
| 213 | + * @param array $options options which affect the content of the response (optional) |
| 214 | + * |
| 215 | + * $options format: |
| 216 | + * array( |
| 217 | + * 'user' => [boolean: true for logged in, false for anon, optional, state of current user by default], |
| 218 | + * 'lang' => [string: language code, optional, code of default language by default], |
| 219 | + * 'skin' => [string: name of skin, optional, name of default skin by default], |
| 220 | + * 'dir' => [string: 'ltr' or 'rtl', optional, direction of lang by default], |
| 221 | + * 'base' => [boolean: true to include base-only scripts, optional, false by default], |
| 222 | + * 'debug' => [boolean: true to include debug-only scripts, optional, false by default], |
| 223 | + * ) |
| 224 | + */ |
| 225 | + public static function respond( WebRequest $request ) { |
| 226 | + global $wgUser, $wgLang, $wgDefaultSkin; |
| 227 | + // Fallback on system settings |
| 228 | + $parameters = array_merge( array( |
| 229 | + 'user' => $request->getBool( 'user', $wgUser->isLoggedIn() ), |
| 230 | + 'lang' => $request->getVal( 'lang', $wgLang->getCode() ), |
| 231 | + 'skin' => $request->getVal( 'skin', $wgDefaultSkin ), |
| 232 | + 'base' => $request->getBool( 'base' ), |
| 233 | + 'debug' => $request->getBool( 'debug' ), |
| 234 | + ) ); |
| 235 | + $modules = explode( '|', $request->getVal( 'modules' ) ); |
| 236 | + // Get the direction from the requested language |
| 237 | + if ( !isset( $parameters['dir'] ) ) { |
| 238 | + $lang = $wgLang->factory( $parameters['lang'] ); |
| 239 | + $parameters['dir'] = $lang->getDir(); |
| 240 | + } |
| 241 | + // Optionally include all base-only scripts |
| 242 | + $base = array(); |
| 243 | + if ( $parameters['base'] ) { |
| 244 | + foreach ( static::$modules as $name => $options ) { |
| 245 | + if ( $options['base'] ) { |
| 246 | + // Only include debug scripts in debug mode |
| 247 | + if ( $options['debug'] ) { |
| 248 | + if ( $parameters['debug'] ) { |
| 249 | + $base[] = $name; |
| 250 | + } |
| 251 | + } else { |
| 252 | + $base[] = $name; |
| 253 | + } |
| 254 | + } |
| 255 | + } |
| 256 | + } |
| 257 | + // Include requested modules which have been registered - ignoring any which have not been |
| 258 | + $other = array(); |
| 259 | + foreach ( static::$modules as $name => $options ) { |
| 260 | + if ( in_array( $name, $modules ) && !in_array( $name, $base )) { |
| 261 | + $other[] = $name; |
| 262 | + } |
| 263 | + } |
| 264 | + // Use output buffering |
| 265 | + ob_start(); |
| 266 | + // Optionally include base modules |
| 267 | + if ( $parameters['base'] ) { |
| 268 | + // Base modules |
| 269 | + foreach ( $base as $module ) { |
| 270 | + readfile( static::$modules[$module]['script'] ); |
| 271 | + } |
| 272 | + // All module loaders - keep track of which loaders have been included to prevent multiple modules with a |
| 273 | + // single loader causing the loader to be included more than once |
| 274 | + $loaders = array(); |
| 275 | + foreach ( self::$modules as $name => $options ) { |
| 276 | + if ( $options['loader'] !== null && !in_array( $options['loader'], $loaders ) ) { |
| 277 | + readfile( $options['loader'] ); |
| 278 | + $loaders[] = $options['loader']; |
| 279 | + } |
| 280 | + } |
| 281 | + } |
| 282 | + |
| 283 | + /* |
| 284 | + * Skin::makeGlobalVariablesScript needs to be modified so that we still output the globals for now, but also |
| 285 | + * put them into the initial payload like this: |
| 286 | + * |
| 287 | + * // Sets the inital configuration |
| 288 | + * mw.config.set( { 'name': 'value', ... } ); |
| 289 | + * |
| 290 | + * Also, the naming of these variables is horrible and sad, hopefully this can be worked on |
| 291 | + */ |
| 292 | + |
| 293 | + // Other modules |
| 294 | + $blobs = static::messages( $parameters['lang'], $other ); |
| 295 | + foreach ( $other as $module ) { |
| 296 | + // Script |
| 297 | + $script = file_get_contents( static::$modules[$module]['script'] ); |
| 298 | + if ( !$parameters['debug'] ) { |
| 299 | + $script = static::filter( 'strip-debug', $script ); |
| 300 | + } |
| 301 | + // Style |
| 302 | + $style = static::$modules[$module]['style'] ? file_get_contents( static::$modules[$module]['style'] ) : ''; |
| 303 | + if ( $style !== '' ) { |
| 304 | + if ( $parameters['dir'] == 'rtl' ) { |
| 305 | + $style = static::filter( 'flip-css', $style ); |
| 306 | + } |
| 307 | + $style = Xml::escapeJsString( |
| 308 | + static::filter( 'minify-css', $style, static::$modules[$module]['style'] ) |
| 309 | + ); |
| 310 | + } |
| 311 | + // Messages |
| 312 | + $messages = isset( $blobs[$module] ) ? $blobs[$module] : '{}'; |
| 313 | + // Output |
| 314 | + echo "mw.loader.implement( '{$module}', function() { {$script} }, '{$style}', {$messages} );\n"; |
| 315 | + } |
| 316 | + // Set headers -- when we support CSS only mode, this might change! |
| 317 | + header( 'Content-Type: text/javascript' ); |
| 318 | + // Final processing |
| 319 | + if ( $parameters['debug'] ) { |
| 320 | + ob_end_flush(); |
| 321 | + } else { |
| 322 | + echo static::filter( 'minify-js', ob_get_clean() ); |
| 323 | + } |
| 324 | + } |
330 | 325 | } |
\ No newline at end of file |
Index: branches/resourceloader/phase3/load.php |
— | — | @@ -15,7 +15,7 @@ |
16 | 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | 17 | * http://www.gnu.org/copyleft/gpl.html |
18 | 18 | * |
19 | | - * @author Roan Kattouw |
| 19 | + * @author Roan Kattouw, Trevor Parscal |
20 | 20 | * |
21 | 21 | */ |
22 | 22 | |
— | — | @@ -45,23 +45,11 @@ |
46 | 46 | // FIXME: Doesn't this execute the rest of the request anyway? |
47 | 47 | // Was taken from api.php so I guess it's maybe OK but it doesn't look good. |
48 | 48 | } |
| 49 | +// Include core resource list |
| 50 | +require_once "$IP/resources/Resources.php"; |
| 51 | +// Respond to resource loading request |
| 52 | +ResourceLoader::respond( $wgRequest ); |
49 | 53 | |
50 | | -$loader = new ResourceLoader( $wgRequest->getVal( 'lang', 'en' ) ); |
51 | | -$loader->setUseJSMin( $wgRequest->getBool( 'jsmin', true ) ); |
52 | | -$loader->setUseCSSMin( $wgRequest->getBool( 'cssmin', true ) ); |
53 | | -$loader->setUseCSSJanus( $wgRequest->getVal( 'dir', 'ltr' ) == 'rtl' ); |
54 | | -$loader->setUseDebugMode( $wgRequest->getBool( 'debug', false ) ); |
55 | | -$moduleParam = $wgRequest->getVal( 'modules' ); |
56 | | -$modules = $moduleParam ? explode( '|', $moduleParam ) : array(); |
57 | | -foreach ( $modules as $module ) { |
58 | | - $loader->addModule( $module ); |
59 | | -} |
60 | | - |
61 | | -// TODO: Cache-Control header |
62 | | -// TODO: gziphandler |
63 | | -header( 'Content-Type', 'text/javascript' ); |
64 | | -echo $loader->getOutput(); |
65 | | - |
66 | 54 | wfProfileOut( 'loader.php' ); |
67 | 55 | wfLogProfilingData(); |
68 | 56 | |
Index: branches/resourceloader/phase3/resources/test/index.html |
— | — | @@ -8,7 +8,7 @@ |
9 | 9 | color: white; |
10 | 10 | } |
11 | 11 | </style> |
12 | | - <script type="text/javascript" src="../../load.php?modules=core|test&debug=0"></script> |
| 12 | + <script type="text/javascript" src="../../load.php?modules=test&base=1&debug=0"></script> |
13 | 13 | <script> |
14 | 14 | mw.config.set( 'wgScriptPath', '../..' ); |
15 | 15 | </script> |
Index: branches/resourceloader/phase3/resources/Resources.php |
— | — | @@ -0,0 +1,67 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +ResourceLoader::register( array( |
| 5 | + 'jquery' => array( |
| 6 | + 'script' => 'resources/base/jquery-1.4.2.js', |
| 7 | + 'base' => true, |
| 8 | + ), |
| 9 | + 'mw' => array( |
| 10 | + 'script' => 'resources/base/mw.js', |
| 11 | + 'base' => true, |
| 12 | + ), |
| 13 | + 'mw.config' => array( |
| 14 | + 'script' => 'resources/base/mw/mw.config.js', |
| 15 | + 'base' => true, |
| 16 | + ), |
| 17 | + 'mw.loader' => array( |
| 18 | + 'script' => 'resources/base/mw/mw.loader.js', |
| 19 | + 'base' => true, |
| 20 | + ), |
| 21 | + 'mw.msg' => array( |
| 22 | + 'script' => 'resources/base/mw/mw.msg.js', |
| 23 | + 'base' => true, |
| 24 | + ), |
| 25 | + 'mw.util' => array( |
| 26 | + 'script' => 'resources/base/mw/mw.util.js', |
| 27 | + 'base' => true, |
| 28 | + ), |
| 29 | + 'mw.debug' => array( |
| 30 | + 'script' => 'resources/base/mw/mw.debug.js', |
| 31 | + 'base' => true, |
| 32 | + 'debug' => true |
| 33 | + ), |
| 34 | + 'mw.log' => array( |
| 35 | + 'script' => 'resources/base/mw/mw.log.js', |
| 36 | + 'base' => true, |
| 37 | + 'debug' => true |
| 38 | + ), |
| 39 | + 'test' => array( |
| 40 | + 'script' => 'resources/test/test.js', |
| 41 | + 'loader' => 'resources/test/loader.js', |
| 42 | + 'style' => 'resources/test/test.css', |
| 43 | + ), |
| 44 | + 'foo' => array( |
| 45 | + 'script' => 'resources/test/foo.js', |
| 46 | + 'loader' => 'resources/test/loader.js', |
| 47 | + 'style' => 'resources/test/foo.css', |
| 48 | + ), |
| 49 | + 'bar' => array( |
| 50 | + 'script' => 'resources/test/bar.js', |
| 51 | + 'loader' => 'resources/test/loader.js', |
| 52 | + 'style' => 'resources/test/bar.css', |
| 53 | + ), |
| 54 | + 'buz' => array( |
| 55 | + 'script' => 'resources/test/buz.js', |
| 56 | + 'loader' => 'resources/test/loader.js', |
| 57 | + 'style' => 'resources/test/buz.css', |
| 58 | + ), |
| 59 | + 'baz' => array( |
| 60 | + 'script' => 'resources/test/baz.js', |
| 61 | + 'loader' => 'resources/test/loader.js', |
| 62 | + 'style' => 'resources/test/baz.css', |
| 63 | + ), |
| 64 | + 'wikibits' => array( |
| 65 | + 'script' => 'skins/common/wikibits.js', |
| 66 | + 'loader' => 'skins/common/loader.js', |
| 67 | + ), |
| 68 | +) ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/Resources.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 69 | + native |