/** * JS stuff to put at the 'bottom', which goes at the bottom of the `<body>`. * These are modules marked with position 'bottom', legacy scripts ($this->mScripts), * site JS, and user JS. * * @param bool $unused Previously used to let this method change its output based * on whether it was called by getExternalHeadScripts() or getBottomScripts(). * @return string */ function getScriptsForBottomQueue($unused = null) { // Scripts "only" requests marked for bottom inclusion // If we're in the <head>, use load() calls rather than <script src="..."> tags $links = array(); $links[] = $this->makeResourceLoaderLink($this->getModuleScripts(true, 'bottom'), ResourceLoaderModule::TYPE_SCRIPTS); // Modules requests - let the client calculate dependencies and batch requests as it likes // Only load modules that have marked themselves for loading at the bottom $modules = $this->getModules(true, 'bottom'); if ($modules) { $links[] = ResourceLoader::makeInlineScript(Xml::encodeJsCall('mw.loader.load', array($modules))); } // Legacy Scripts $links[] = $this->mScripts; // Add user JS if enabled // This must use TYPE_COMBINED instead of only=scripts so that its request is handled by // mw.loader.implement() which ensures that execution is scheduled after the "site" module. if ($this->getConfig()->get('AllowUserJs') && $this->getUser()->isLoggedIn() && $this->getTitle() && $this->getTitle()->isJsSubpage() && $this->userCanPreview()) { // We're on a preview of a JS subpage. Exclude this page from the user module (T28283) // and include the draft contents as a raw script instead. $links[] = $this->makeResourceLoaderLink('user', ResourceLoaderModule::TYPE_COMBINED, array('excludepage' => $this->getTitle()->getPrefixedDBkey())); // Load the previewed JS $links[] = ResourceLoader::makeInlineScript(Xml::encodeJsCall('mw.loader.using', array(array('user', 'site'), new XmlJsCode('function () {' . Xml::encodeJsCall('$.globalEval', array($this->getRequest()->getText('wpTextbox1'))) . '}')))); // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded // asynchronously and may arrive *after* the inline script here. So the previewed code // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js. // Similarly, when previewing ./common.js and the user module does arrive first, // it will arrive without common.js and the inline script runs after. // Thus running common after the excluded subpage. } else { // Include the user module normally, i.e., raw to avoid it being wrapped in a closure. $links[] = $this->makeResourceLoaderLink('user', ResourceLoaderModule::TYPE_COMBINED); } // Group JS is only enabled if site JS is enabled. $links[] = $this->makeResourceLoaderLink('user.groups', ResourceLoaderModule::TYPE_COMBINED); return self::getHtmlFromLoaderLinks($links); }
/** * JS stuff to put at the 'bottom', which can either be the bottom of the * "<body>" or the bottom of the "<head>" depending on * $wgResourceLoaderExperimentalAsyncLoading: modules marked with position * 'bottom', legacy scripts ($this->mScripts), user preferences, site JS * and user JS. * * @param bool $inHead If true, this HTML goes into the "<head>", * if false it goes into the "<body>". * @return string */ function getScriptsForBottomQueue($inHead) { // Scripts "only" requests marked for bottom inclusion // If we're in the <head>, use load() calls rather than <script src="..."> tags $links = array(); $links[] = $this->makeResourceLoaderLink($this->getModuleScripts(true, 'bottom'), ResourceLoaderModule::TYPE_SCRIPTS, false, array(), $inHead); $links[] = $this->makeResourceLoaderLink($this->getModuleStyles(true, 'bottom'), ResourceLoaderModule::TYPE_STYLES, false, array(), $inHead); // Modules requests - let the client calculate dependencies and batch requests as it likes // Only load modules that have marked themselves for loading at the bottom $modules = $this->getModules(true, 'bottom'); if ($modules) { $links[] = ResourceLoader::makeInlineScript(Xml::encodeJsCall('mw.loader.load', array($modules, null, true))); } // Legacy Scripts $links[] = "\n" . $this->mScripts; // Add site JS if enabled $links[] = $this->makeResourceLoaderLink('site', ResourceLoaderModule::TYPE_SCRIPTS, false, array(), $inHead); // Add user JS if enabled if ($this->getConfig()->get('AllowUserJs') && $this->getUser()->isLoggedIn() && $this->getTitle() && $this->getTitle()->isJsSubpage() && $this->userCanPreview()) { # XXX: additional security check/prompt? // We're on a preview of a JS subpage // Exclude this page from the user module in case it's in there (bug 26283) $links[] = $this->makeResourceLoaderLink('user', ResourceLoaderModule::TYPE_SCRIPTS, false, array('excludepage' => $this->getTitle()->getPrefixedDBkey()), $inHead); // Load the previewed JS $links[] = Html::inlineScript("\n" . $this->getRequest()->getText('wpTextbox1') . "\n") . "\n"; // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded // asynchronously and may arrive *after* the inline script here. So the previewed code // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js... } else { // Include the user module normally, i.e., raw to avoid it being wrapped in a closure. $links[] = $this->makeResourceLoaderLink('user', ResourceLoaderModule::TYPE_SCRIPTS, false, array(), $inHead); } // Group JS is only enabled if site JS is enabled. $links[] = $this->makeResourceLoaderLink('user.groups', ResourceLoaderModule::TYPE_COMBINED, false, array(), $inHead); return self::getHtmlFromLoaderLinks($links); }
/** * Returns the HTML to add to the page for the toolbar * * @since 1.19 * @param IContextSource $context * @return string */ public static function getDebugHTML(IContextSource $context) { global $wgDebugComments; $html = ''; if (self::$enabled) { MWDebug::log('MWDebug output complete'); $debugInfo = self::getDebugInfo($context); // Cannot use OutputPage::addJsConfigVars because those are already outputted // by the time this method is called. $html = ResourceLoader::makeInlineScript(ResourceLoader::makeConfigSetScript(array('debugInfo' => $debugInfo))); } if ($wgDebugComments) { $html .= "<!-- Debug output:\n" . htmlspecialchars(implode("\n", self::$debug)) . "\n\n-->"; } return $html; }
function printPage($form_name, $embedded = false) { global $wgOut, $wgRequest, $sfgFormPrinter, $wgParser, $sfgRunQueryFormAtTop; global $wgUser; // Get contents of form-definition page. $form_title = Title::makeTitleSafe(SF_NS_FORM, $form_name); if (!$form_title || !$form_title->exists()) { if ($form_name === '') { $text = Html::element('p', array('class' => 'error'), wfMessage('sf_runquery_badurl')->text()) . "\n"; } else { $text = Html::rawElement('p', array('class' => 'error'), wfMessage('sf_formstart_badform', SFUtils::linkText(SF_NS_FORM, $form_name))->parse()) . "\n"; } $wgOut->addHTML($text); return; } // Initialize variables. $form_definition = SFUtils::getPageText($form_title); if ($embedded) { $run_query = false; $content = null; $raw = false; } else { $run_query = $wgRequest->getCheck('wpRunQuery'); $content = $wgRequest->getVal('wpTextbox1'); $raw = $wgRequest->getBool('raw', false); } $form_submitted = $run_query; if ($raw) { $wgOut->setArticleBodyOnly(true); } // If user already made some action, ignore the edited // page and just get data from the query string. if (!$embedded && $wgRequest->getVal('query') == 'true') { $edit_content = null; $is_text_source = false; } elseif ($content != null) { $edit_content = $content; $is_text_source = true; } else { $edit_content = null; $is_text_source = true; } list($form_text, $javascript_text, $data_text, $form_page_title) = $sfgFormPrinter->formHTML($form_definition, $form_submitted, $is_text_source, $form_title->getArticleID(), $edit_content, null, null, true, $embedded); $text = ""; // Get the text of the results. $resultsText = ''; if ($form_submitted) { // @TODO - fix RunQuery's parsing so that this check // isn't needed. if ($wgParser->getOutput() == null) { $headItems = array(); } else { $headItems = $wgParser->getOutput()->getHeadItems(); } foreach ($headItems as $key => $item) { $wgOut->addHeadItem($key, "\t\t" . $item . "\n"); } $wgParser->mOptions = ParserOptions::newFromUser($wgUser); $resultsText = $wgParser->parse($data_text, $this->getTitle(), $wgParser->mOptions)->getText(); } // Get the full text of the form. $fullFormText = ''; $additionalQueryHeader = ''; $dividerText = ''; if (!$raw) { // Create the "additional query" header, and the // divider text - one of these (depending on whether // the query form is at the top or bottom) is displayed // if the form has already been submitted. if ($form_submitted) { $additionalQueryHeader = "\n" . Html::element('h2', null, wfMessage('sf_runquery_additionalquery')->text()) . "\n"; $dividerText = "\n<hr style=\"margin: 15px 0;\" />\n"; } $action = htmlspecialchars($this->getTitle($form_name)->getLocalURL()); $fullFormText .= <<<END \t<form id="sfForm" name="createbox" action="{$action}" method="post" class="createbox"> END; $fullFormText .= Html::hidden('query', 'true'); $fullFormText .= $form_text; } // Either don't display a query form at all, or display the // query form at the top, and the results at the bottom, or the // other way around, depending on the settings. if ($wgRequest->getVal('additionalquery') == 'false') { $text .= $resultsText; } elseif ($sfgRunQueryFormAtTop) { $text .= Html::openElement('div', array('class' => 'sf-runquery-formcontent')); $text .= $fullFormText; $text .= $dividerText; $text .= Html::closeElement('div'); $text .= $resultsText; } else { $text .= $resultsText; $text .= Html::openElement('div', array('class' => 'sf-runquery-formcontent')); $text .= $additionalQueryHeader; $text .= $fullFormText; $text .= Html::closeElement('div'); } if ($embedded) { $text = "<div class='runQueryEmbedded'>{$text}</div>"; } // Armor against doBlockLevels() $text = preg_replace('/^ +/m', '', $text); // Now write everything to the screen. $wgOut->addHTML($text); SFUtils::addFormRLModules($embedded ? $wgParser : null); $script = "\t\t" . '<script type="text/javascript">' . "\n" . $javascript_text . '</script>' . "\n"; if ($embedded) { if (method_exists('ResourceLoader', 'makeInlineScript')) { // MW 1.25+ $wgParser->getOutput()->addHeadItem(ResourceLoader::makeInlineScript($javascript_text)); } else { $wgParser->getOutput()->addHeadItem($script); } } else { if (method_exists('ResourceLoader', 'makeInlineScript')) { // MW 1.25+ $wgOut->addScript(ResourceLoader::makeInlineScript($javascript_text)); } else { $wgOut->addScript($script); } $po = $wgParser->getOutput(); if ($po) { // addParserOutputMetadata was introduced in 1.24 when addParserOutputNoText was deprecated if (method_exists($wgOut, 'addParserOutputMetadata')) { $wgOut->addParserOutputMetadata($po); } else { $wgOut->addParserOutputNoText($po); } } } // Finally, set the page title - previously, this had to be // called after addParserOutputNoText() for it to take effect; // now the order doesn't matter. if (!$embedded) { if ($form_page_title != null) { $wgOut->setPageTitle($form_page_title); } else { $s = wfMessage('sf_runquery_title', $form_title->getText())->text(); $wgOut->setPageTitle($s); } } }
/** * Run the test suite on the Special page. * * Rendered by OutputPage and Skin. */ private function viewQUnit() { $out = $this->getOutput(); $modules = $out->getResourceLoader()->getTestModuleNames('qunit'); $summary = $this->msg('javascripttest-qunit-intro')->params('https://www.mediawiki.org/wiki/Manual:JavaScript_unit_testing')->parseAsBlock(); $baseHtml = <<<HTML <div class="mw-content-ltr"> <div id="qunit"></div> </div> HTML; $out->addHtml($this->wrapSummaryHtml($summary) . $baseHtml); // The testrunner configures QUnit and essentially depends on it. However, test suites // are reusable in environments that preload QUnit (or a compatibility interface to // another framework). Therefore we have to load it ourselves. $out->addHtml(ResourceLoader::makeInlineScript(Xml::encodeJsCall('mw.loader.using', array(array('jquery.qunit', 'jquery.qunit.completenessTest'), new XmlJsCode('function () {' . Xml::encodeJsCall('mw.loader.load', array($modules)) . '}'))))); }
/** * @param array $data * @return string */ static function makeVariablesScript($data) { if ($data) { return ResourceLoader::makeInlineScript(ResourceLoader::makeConfigSetScript($data)); } else { return ''; } }
/** * Shows a bulletin board style toolbar for common editing functions. * It can be disabled in the user preferences. * * @param $title Title object for the page being edited (optional) * @return string */ static function getEditToolbar($title = null) { global $wgContLang, $wgOut; global $wgEnableUploads, $wgForeignFileRepos; $imagesAvailable = $wgEnableUploads || count($wgForeignFileRepos); $showSignature = true; if ($title) { $showSignature = MWNamespace::wantSignatures($title->getNamespace()); } /** * $toolarray is an array of arrays each of which includes the * opening tag, the closing tag, optionally a sample text that is * inserted between the two when no selection is highlighted * and. The tip text is shown when the user moves the mouse * over the button. * * Images are defined in ResourceLoaderEditToolbarModule. */ $toolarray = array(array('id' => 'mw-editbutton-bold', 'open' => '\'\'\'', 'close' => '\'\'\'', 'sample' => wfMessage('bold_sample')->text(), 'tip' => wfMessage('bold_tip')->text()), array('id' => 'mw-editbutton-italic', 'open' => '\'\'', 'close' => '\'\'', 'sample' => wfMessage('italic_sample')->text(), 'tip' => wfMessage('italic_tip')->text()), array('id' => 'mw-editbutton-link', 'open' => '[[', 'close' => ']]', 'sample' => wfMessage('link_sample')->text(), 'tip' => wfMessage('link_tip')->text()), array('id' => 'mw-editbutton-extlink', 'open' => '[', 'close' => ']', 'sample' => wfMessage('extlink_sample')->text(), 'tip' => wfMessage('extlink_tip')->text()), array('id' => 'mw-editbutton-headline', 'open' => "\n== ", 'close' => " ==\n", 'sample' => wfMessage('headline_sample')->text(), 'tip' => wfMessage('headline_tip')->text()), $imagesAvailable ? array('id' => 'mw-editbutton-image', 'open' => '[[' . $wgContLang->getNsText(NS_FILE) . ':', 'close' => ']]', 'sample' => wfMessage('image_sample')->text(), 'tip' => wfMessage('image_tip')->text()) : false, $imagesAvailable ? array('id' => 'mw-editbutton-media', 'open' => '[[' . $wgContLang->getNsText(NS_MEDIA) . ':', 'close' => ']]', 'sample' => wfMessage('media_sample')->text(), 'tip' => wfMessage('media_tip')->text()) : false, array('id' => 'mw-editbutton-nowiki', 'open' => "<nowiki>", 'close' => "</nowiki>", 'sample' => wfMessage('nowiki_sample')->text(), 'tip' => wfMessage('nowiki_tip')->text()), $showSignature ? array('id' => 'mw-editbutton-signature', 'open' => '--~~~~', 'close' => '', 'sample' => '', 'tip' => wfMessage('sig_tip')->text()) : false, array('id' => 'mw-editbutton-hr', 'open' => "\n----\n", 'close' => '', 'sample' => '', 'tip' => wfMessage('hr_tip')->text())); $script = 'mw.loader.using("mediawiki.toolbar", function () {'; foreach ($toolarray as $tool) { if (!$tool) { continue; } $params = array(false, $tool['tip'], $tool['open'], $tool['close'], $tool['sample'], $tool['id']); $script .= Xml::encodeJsCall('mw.toolbar.addButton', $params, ResourceLoader::inDebugMode()); } $script .= '});'; $wgOut->addScript(ResourceLoader::makeInlineScript($script)); $toolbar = '<div id="toolbar"></div>'; Hooks::run('EditPageBeforeEditToolbar', array(&$toolbar)); return $toolbar; }
/** * JS stuff to put at the bottom of the `<body>`. These are modules with position 'bottom', * legacy scripts ($this->mScripts), and user JS. * * @return string|WrappedStringList HTML */ public function getBottomScripts() { $chunks = []; $chunks[] = $this->getRlClient()->getBodyHtml(); // Legacy non-ResourceLoader scripts $chunks[] = $this->mScripts; // Exempt 'user' module // - May need excludepages for live preview. (T28283) // - Must use TYPE_COMBINED so its response is handled by mw.loader.implement() which // ensures execution is scheduled after the "site" module. // - Don't load if module state is already resolved as "ready". if ($this->rlUserModuleState === 'loading') { if ($this->isUserJsPreview()) { $chunks[] = $this->makeResourceLoaderLink('user', ResourceLoaderModule::TYPE_COMBINED, ['excludepage' => $this->getTitle()->getPrefixedDBkey()]); $chunks[] = ResourceLoader::makeInlineScript(Xml::encodeJsCall('mw.loader.using', [['user', 'site'], new XmlJsCode('function () {' . Xml::encodeJsCall('$.globalEval', [$this->getRequest()->getText('wpTextbox1')]) . '}')])); // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded // asynchronously and may arrive *after* the inline script here. So the previewed code // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js. // Similarly, when previewing ./common.js and the user module does arrive first, // it will arrive without common.js and the inline script runs after. // Thus running common after the excluded subpage. } else { // Load normally $chunks[] = $this->makeResourceLoaderLink('user', ResourceLoaderModule::TYPE_COMBINED); } } if ($this->limitReportData) { $chunks[] = ResourceLoader::makeInlineScript(ResourceLoader::makeConfigSetScript(['wgPageParseReport' => $this->limitReportData], true)); } return self::combineWrappedStrings($chunks); }
function printForm($form_name, $targetName, $alt_forms = array()) { $out = $this->getOutput(); $req = $this->getRequest(); $module = new SFAutoeditAPI(new ApiMain(), 'sfautoedit'); $module->setOption('form', $form_name); $module->setOption('target', $targetName); if ($req->getCheck('wpSave') || $req->getCheck('wpPreview') || $req->getCheck('wpDiff')) { // If the page was submitted, form data should be // complete => do not preload (unless it's a partial // form). if ($req->getCheck('partial')) { $module->setOption('preload', true); } else { $module->setOption('preload', false); } } else { if (!empty($targetName) && Title::newFromText($targetName)->exists()) { // If target page exists, do not overwrite it with // preload data; just preload the page's data. $module->setOption('preload', true); } else { if ($req->getCheck('preload')) { // if page does not exist and preload parameter is set, pass that on $module->setOption('preload', $req->getText('preload')); } else { // nothing set, so do not set preload } } } $module->execute(); $text = ''; // if action was successful and action was a Save, return if ($module->getStatus() === 200) { if ($module->getAction() === SFAutoeditAPI::ACTION_SAVE) { return; } } else { $resultData = $module->getResultData(); if (array_key_exists('errors', $resultData)) { foreach ($resultData['errors'] as $error) { // FIXME: This should probably not be hard-coded to WARNING but put into a setting if ($error['level'] <= SFAutoeditAPI::WARNING) { $text .= Html::rawElement('p', array('class' => 'error'), $error['message']) . "\n"; } } } } // Override the default title for this page if a title was // specified in the form. $result = $module->getOptions(); $targetTitle = Title::newFromText($result['target']); // Set page title depending on whether an explicit title was // specified in the form definition. if (array_key_exists('formtitle', $result)) { // set page title depending on whether the target page exists if (empty($targetName)) { $pageTitle = $result['formtitle']; } else { $pageTitle = $result['formtitle'] . ': ' . $targetName; } } elseif ($result['form'] !== '') { // Set page title depending on whether the target page // exists. if (empty($targetName)) { $pageTitle = wfMessage('sf_formedit_createtitlenotarget', $result['form'])->text(); } elseif ($targetTitle->exists()) { $pageTitle = wfMessage('sf_formedit_edittitle', $result['form'], $targetName)->text(); } else { $pageTitle = wfMessage('sf_formedit_createtitle', $result['form'], $targetName)->text(); } } elseif (count($alt_forms) > 0) { // We use the 'creating' message here, instead of // 'sf_formedit_createtitlenotarget', to differentiate // between a page with no (default) form, and one with // no target; in English they'll show up as // "Creating ..." and "Create ...", respectively. // Does this make any difference? Who knows. $pageTitle = wfMessage('creating', $targetName)->text(); } elseif ($result['form'] == '') { //FIXME: This looks weird; a simple else should be enough, right? // display error message if the form is not specified in the URL $pageTitle = wfMessage('formedit')->text(); $text .= Html::element('p', array('class' => 'error'), wfMessage('sf_formedit_badurl')->text()) . "\n"; $out->addHTML($text); } $out->setPageTitle($pageTitle); if (count($alt_forms) > 0) { $text .= '<div class="infoMessage">'; if ($result['form'] != '') { $text .= wfMessage('sf_formedit_altforms')->escaped(); } else { $text .= wfMessage('sf_formedit_altformsonly')->escaped(); } $text .= ' ' . $this->printAltFormsList($alt_forms, $targetName); $text .= "</div>\n"; } $text .= '<form name="createbox" id="sfForm" method="post" class="createbox">'; $pre_form_html = ''; Hooks::run('sfHTMLBeforeForm', array(&$targetTitle, &$pre_form_html)); $text .= $pre_form_html; if (isset($result['formHTML'])) { $text .= $result['formHTML']; } SFUtils::addFormRLModules(); if (isset($result['formJS'])) { if (method_exists('ResourceLoader', 'makeInlineScript')) { // MW 1.25+ $out->addScript(ResourceLoader::makeInlineScript($result['formJS'])); } else { $out->addScript(' <script type="text/javascript">' . "\n{$result['formJS']}\n" . '</script>' . "\n"); } } $out->addHTML($text); return null; }
private static function makeLegacyWarning($id) { $special = SpecialPage::getTitleFor('Gadgets'); return ResourceLoader::makeInlineScript(Xml::encodeJsCall('mw.log.warn', array("Gadget \"{$id}\" was not loaded. Please migrate it to use ResourceLoader. " . ' See <' . $special->getCanonicalURL() . '>.'))); }
/** * Explicily load or embed modules on a page. * * @param ResourceLoaderContext $mainContext * @param array $modules One or more module names * @param string $only ResourceLoaderModule TYPE_ class constant * @param array $extraQuery [optional] Array with extra query parameters for the request * @return string|WrappedStringList HTML */ public static function makeLoad(ResourceLoaderContext $mainContext, array $modules, $only, array $extraQuery = []) { $rl = $mainContext->getResourceLoader(); $chunks = []; if ($mainContext->getDebug() && count($modules) > 1) { $chunks = []; // Recursively call us for every item foreach ($modules as $name) { $chunks[] = self::makeLoad($mainContext, [$name], $only, $extraQuery); } return new WrappedStringList("\n", $chunks); } // Sort module names so requests are more uniform sort($modules); // Create keyed-by-source and then keyed-by-group list of module objects from modules list $sortedModules = []; foreach ($modules as $name) { $module = $rl->getModule($name); if (!$module) { $rl->getLogger()->warning('Unknown module "{module}"', ['module' => $name]); continue; } $sortedModules[$module->getSource()][$module->getGroup()][$name] = $module; } foreach ($sortedModules as $source => $groups) { foreach ($groups as $group => $grpModules) { $context = self::makeContext($mainContext, $group, $only, $extraQuery); $context->setModules(array_keys($grpModules)); if ($group === 'private') { // Decide whether to use style or script element if ($only == ResourceLoaderModule::TYPE_STYLES) { $chunks[] = Html::inlineStyle($rl->makeModuleResponse($context, $grpModules)); } else { $chunks[] = ResourceLoader::makeInlineScript($rl->makeModuleResponse($context, $grpModules)); } continue; } // See if we have one or more raw modules $isRaw = false; foreach ($grpModules as $key => $module) { $isRaw |= $module->isRaw(); } // Special handling for the user group; because users might change their stuff // on-wiki like user pages, or user preferences; we need to find the highest // timestamp of these user-changeable modules so we can ensure cache misses on change // This should NOT be done for the site group (bug 27564) because anons get that too // and we shouldn't be putting timestamps in CDN-cached HTML if ($group === 'user') { // Must setModules() before makeVersionQuery() $context->setVersion($rl->makeVersionQuery($context)); } $url = $rl->createLoaderURL($source, $context, $extraQuery); // Decide whether to use 'style' or 'script' element if ($only === ResourceLoaderModule::TYPE_STYLES) { $chunk = Html::linkedStyle($url); } else { if ($context->getRaw() || $isRaw) { $chunk = Html::element('script', ['async' => !isset($extraQuery['sync']), 'src' => $url]); } else { $chunk = ResourceLoader::makeInlineScript(Xml::encodeJsCall('mw.loader.load', [$url])); } } if ($group == 'noscript') { $chunks[] = Html::rawElement('noscript', [], $chunk); } else { $chunks[] = $chunk; } } } return new WrappedStringList("\n", $chunks); }