/** * Evaluate a compiled set of rules returned by compile(). Do not allow * the user to edit the compiled form, or else PHP errors may result. */ public static function evaluateCompiled($number, array $rules) { // The compiled form is RPN, with tokens strictly delimited by // spaces, so this is a simple RPN evaluator. foreach ($rules as $i => $rule) { $stack = array(); $zero = ord('0'); $nine = ord('9'); foreach (StringUtils::explode(' ', $rule) as $token) { $ord = ord($token); if ($token === 'n') { $stack[] = $number; } elseif ($ord >= $zero && $ord <= $nine) { $stack[] = intval($token); } else { $right = array_pop($stack); $left = array_pop($stack); $result = self::doOperation($token, $left, $right); $stack[] = $result; } } if ($stack[0]) { return $i; } } // None of the provided rules match. The number belongs to caregory // 'other' which comes last. return count($rules); }
/** * @desc Parse data from <hubspopularvideos /> tag * * @param String $input data within <hubspopularvideos /> tag */ protected function pullData($input) { wfProfileIn(__METHOD__); //use images passed inside <gallery> tag $lines = StringUtils::explode("\n", $input); foreach ($lines as $line) { if ($line == '') { continue; } //title|user|||wikiurl $parts = (array) StringUtils::explode('|', $line); $videoTitleText = !empty($parts[0]) ? $parts[0] : null; $username = !empty($parts[1]) ? $parts[1] : null; $wikiUrl = !empty($parts[4]) ? $parts[4] : null; if (in_array(true, array(is_null($videoTitleText), is_null($username), is_null($wikiUrl)))) { //we want all data given continue; } $this->data[] = array('videoTitleText' => $videoTitleText, 'username' => $username, 'wikiUrl' => $wikiUrl); } wfProfileOut(__METHOD__); }
/** * Parse flags with syntax -{FLAG| ... }- * @private */ function parseFlags() { $text = $this->mText; $flags = array(); $variantFlags = array(); $sepPos = strpos($text, '|'); if ($sepPos !== false) { $validFlags = $this->mConverter->mFlags; $f = StringUtils::explode(';', substr($text, 0, $sepPos)); foreach ($f as $ff) { $ff = trim($ff); if (isset($validFlags[$ff])) { $flags[$validFlags[$ff]] = true; } } $text = strval(substr($text, $sepPos + 1)); } if (!$flags) { $flags['S'] = true; } elseif (isset($flags['R'])) { $flags = array('R' => true); // remove other flags } elseif (isset($flags['N'])) { $flags = array('N' => true); // remove other flags } elseif (isset($flags['-'])) { $flags = array('-' => true); // remove other flags } elseif (count($flags) == 1 && isset($flags['T'])) { $flags['H'] = true; } elseif (isset($flags['H'])) { // replace A flag, and remove other flags except T $temp = array('+' => true, 'H' => true); if (isset($flags['T'])) { $temp['T'] = true; } if (isset($flags['D'])) { $temp['D'] = true; } $flags = $temp; } else { if (isset($flags['A'])) { $flags['+'] = true; $flags['S'] = true; } if (isset($flags['D'])) { unset($flags['S']); } // try to find flags like "zh-hans", "zh-hant" // allow syntaxes like "-{zh-hans;zh-hant|XXXX}-" $variantFlags = array_intersect(array_keys($flags), $this->mConverter->mVariants); if ($variantFlags) { $variantFlags = array_flip($variantFlags); $flags = array(); } } $this->mVariantFlags = $variantFlags; $this->mRules = $text; $this->mFlags = $flags; }
/** * Parse the conversion table stored in the cache. * * The tables should be in blocks of the following form: * -{ * word => word ; * word => word ; * ... * }- * * To make the tables more manageable, subpages are allowed * and will be parsed recursively if $recursive == true. * * @param string $code Language code * @param string $subpage Subpage name * @param bool $recursive Parse subpages recursively? Defaults to true. * * @return array */ function parseCachedTable($code, $subpage = '', $recursive = true) { static $parsed = []; $key = 'Conversiontable/' . $code; if ($subpage) { $key .= '/' . $subpage; } if (array_key_exists($key, $parsed)) { return []; } $parsed[$key] = true; if ($subpage === '') { $txt = MessageCache::singleton()->getMsgFromNamespace($key, $code); } else { $txt = false; $title = Title::makeTitleSafe(NS_MEDIAWIKI, $key); if ($title && $title->exists()) { $revision = Revision::newFromTitle($title); if ($revision) { if ($revision->getContentModel() == CONTENT_MODEL_WIKITEXT) { $txt = $revision->getContent(Revision::RAW)->getNativeData(); } // @todo in the future, use a specialized content model, perhaps based on json! } } } # Nothing to parse if there's no text if ($txt === false || $txt === null || $txt === '') { return []; } // get all subpage links of the form // [[MediaWiki:Conversiontable/zh-xx/...|...]] $linkhead = $this->mLangObj->getNsText(NS_MEDIAWIKI) . ':Conversiontable'; $subs = StringUtils::explode('[[', $txt); $sublinks = []; foreach ($subs as $sub) { $link = explode(']]', $sub, 2); if (count($link) != 2) { continue; } $b = explode('|', $link[0], 2); $b = explode('/', trim($b[0]), 3); if (count($b) == 3) { $sublink = $b[2]; } else { $sublink = ''; } if ($b[0] == $linkhead && $b[1] == $code) { $sublinks[] = $sublink; } } // parse the mappings in this page $blocks = StringUtils::explode('-{', $txt); $ret = []; $first = true; foreach ($blocks as $block) { if ($first) { // Skip the part before the first -{ $first = false; continue; } $mappings = explode('}-', $block, 2)[0]; $stripped = str_replace(["'", '"', '*', '#'], '', $mappings); $table = StringUtils::explode(';', $stripped); foreach ($table as $t) { $m = explode('=>', $t, 3); if (count($m) != 2) { continue; } // trim any trailling comments starting with '//' $tt = explode('//', $m[1], 2); $ret[trim($m[0])] = trim($tt[0]); } } // recursively parse the subpages if ($recursive) { foreach ($sublinks as $link) { $s = $this->parseCachedTable($code, $link, $recursive); $ret = $s + $ret; } } if ($this->mUcfirst) { foreach ($ret as $k => $v) { $ret[$this->mLangObj->ucfirst($k)] = $this->mLangObj->ucfirst($v); } } return $ret; }
/** * Parse image options text and use it to make an image * * @param $title Title * @param $options String * @param $holders LinkHolderArray|bool * @return string HTML */ function makeImage($title, $options, $holders = false) { # Check if the options text is of the form "options|alt text" # Options are: # * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang # * left no resizing, just left align. label is used for alt= only # * right same, but right aligned # * none same, but not aligned # * ___px scale to ___ pixels width, no aligning. e.g. use in taxobox # * center center the image # * frame Keep original image size, no magnify-button. # * framed Same as "frame" # * frameless like 'thumb' but without a frame. Keeps user preferences for width # * upright reduce width for upright images, rounded to full __0 px # * border draw a 1px border around the image # * alt Text for HTML alt attribute (defaults to empty) # * class Set a class for img node # * link Set the target of the image link. Can be external, interwiki, or local # vertical-align values (no % or length right now): # * baseline # * sub # * super # * top # * text-top # * middle # * bottom # * text-bottom $parts = StringUtils::explode("|", $options); # Give extensions a chance to select the file revision for us $options = array(); $descQuery = false; wfRunHooks('BeforeParserFetchFileAndTitle', array($this, $title, &$options, &$descQuery)); # Fetch and register the file (file title may be different via hooks) list($file, $title) = $this->fetchFileAndTitle($title, $options); # Get parameter map $handler = $file ? $file->getHandler() : false; list($paramMap, $mwArray) = $this->getImageParams($handler); if (!$file) { $this->addTrackingCategory('broken-file-category'); } # Process the input parameters $caption = ''; $params = array('frame' => array(), 'handler' => array(), 'horizAlign' => array(), 'vertAlign' => array()); foreach ($parts as $part) { $part = trim($part); list($magicName, $value) = $mwArray->matchVariableStartToEnd($part); $validated = false; if (isset($paramMap[$magicName])) { list($type, $paramName) = $paramMap[$magicName]; # Special case; width and height come in one variable together if ($type === 'handler' && $paramName === 'width') { $parsedWidthParam = $this->parseWidthParam($value); if (isset($parsedWidthParam['width'])) { $width = $parsedWidthParam['width']; if ($handler->validateParam('width', $width)) { $params[$type]['width'] = $width; $validated = true; } } if (isset($parsedWidthParam['height'])) { $height = $parsedWidthParam['height']; if ($handler->validateParam('height', $height)) { $params[$type]['height'] = $height; $validated = true; } } # else no validation -- bug 13436 } else { if ($type === 'handler') { # Validate handler parameter $validated = $handler->validateParam($paramName, $value); } else { # Validate internal parameters switch ($paramName) { case 'manualthumb': case 'alt': case 'class': # @todo FIXME: Possibly check validity here for # manualthumb? downstream behavior seems odd with # missing manual thumbs. $validated = true; $value = $this->stripAltText($value, $holders); break; case 'link': $chars = self::EXT_LINK_URL_CLASS; $prots = $this->mUrlProtocols; if ($value === '') { $paramName = 'no-link'; $value = true; $validated = true; } elseif (preg_match("/^(?i){$prots}/", $value)) { if (preg_match("/^((?i){$prots}){$chars}+\$/u", $value, $m)) { $paramName = 'link-url'; $this->mOutput->addExternalLink($value); if ($this->mOptions->getExternalLinkTarget()) { $params[$type]['link-target'] = $this->mOptions->getExternalLinkTarget(); } $validated = true; } } else { $linkTitle = Title::newFromText($value); if ($linkTitle) { $paramName = 'link-title'; $value = $linkTitle; $this->mOutput->addLink($linkTitle); $validated = true; } } break; default: # Most other things appear to be empty or numeric... $validated = $value === false || is_numeric(trim($value)); } } if ($validated) { $params[$type][$paramName] = $value; } } } if (!$validated) { $caption = $part; } } # Process alignment parameters if ($params['horizAlign']) { $params['frame']['align'] = key($params['horizAlign']); } if ($params['vertAlign']) { $params['frame']['valign'] = key($params['vertAlign']); } $params['frame']['caption'] = $caption; # Will the image be presented in a frame, with the caption below? $imageIsFramed = isset($params['frame']['frame']) || isset($params['frame']['framed']) || isset($params['frame']['thumbnail']) || isset($params['frame']['manualthumb']); # In the old days, [[Image:Foo|text...]] would set alt text. Later it # came to also set the caption, ordinary text after the image -- which # makes no sense, because that just repeats the text multiple times in # screen readers. It *also* came to set the title attribute. # # Now that we have an alt attribute, we should not set the alt text to # equal the caption: that's worse than useless, it just repeats the # text. This is the framed/thumbnail case. If there's no caption, we # use the unnamed parameter for alt text as well, just for the time be- # ing, if the unnamed param is set and the alt param is not. # # For the future, we need to figure out if we want to tweak this more, # e.g., introducing a title= parameter for the title; ignoring the un- # named parameter entirely for images without a caption; adding an ex- # plicit caption= parameter and preserving the old magic unnamed para- # meter for BC; ... if ($imageIsFramed) { # Framed image if ($caption === '' && !isset($params['frame']['alt'])) { # No caption or alt text, add the filename as the alt text so # that screen readers at least get some description of the image $params['frame']['alt'] = $title->getText(); } # Do not set $params['frame']['title'] because tooltips don't make sense # for framed images } else { # Inline image if (!isset($params['frame']['alt'])) { # No alt text, use the "caption" for the alt text if ($caption !== '') { $params['frame']['alt'] = $this->stripAltText($caption, $holders); } else { # No caption, fall back to using the filename for the # alt text $params['frame']['alt'] = $title->getText(); } } # Use the "caption" for the tooltip text $params['frame']['title'] = $this->stripAltText($caption, $holders); } wfRunHooks('ParserMakeImageParams', array($title, $file, &$params, $this)); # Linker does the rest $time = isset($options['time']) ? $options['time'] : false; $ret = Linker::makeImageLink($this, $title, $file, $params['frame'], $params['handler'], $time, $descQuery, $this->mOptions->getThumbSize()); # Give the handler a chance to modify the parser object if ($handler) { $handler->parserTransformHook($this, $file); } return $ret; }
/** * Evaluate a compiled set of rules returned by compile(). Do not allow * the user to edit the compiled form, or else PHP errors may result. * * @param string $number The number to be evaluated against the rules, in English, or it * may be a type convertible to string. * @param array $rules The associative array of plural rules in pluralform => rule format. * @return int The index of the plural form which passed the evaluation */ public static function evaluateCompiled($number, array $rules) { // Calculate the values of the operand symbols $number = strval($number); if (!preg_match('/^ -? ( ([0-9]+) (?: \\. ([0-9]+) )? )$/x', $number, $m)) { wfDebug(__METHOD__ . ": invalid number input, returning 'other'\n"); return count($rules); } if (!isset($m[3])) { $operandSymbols = array('n' => intval($m[1]), 'i' => intval($m[1]), 'v' => 0, 'w' => 0, 'f' => 0, 't' => 0); } else { $absValStr = $m[1]; $intStr = $m[2]; $fracStr = $m[3]; $operandSymbols = array('n' => floatval($absValStr), 'i' => intval($intStr), 'v' => strlen($fracStr), 'w' => strlen(rtrim($fracStr, '0')), 'f' => intval($fracStr), 't' => intval(rtrim($fracStr, '0'))); } // The compiled form is RPN, with tokens strictly delimited by // spaces, so this is a simple RPN evaluator. foreach ($rules as $i => $rule) { $stack = array(); $zero = ord('0'); $nine = ord('9'); foreach (StringUtils::explode(' ', $rule) as $token) { $ord = ord($token); if (isset($operandSymbols[$token])) { $stack[] = $operandSymbols[$token]; } elseif ($ord >= $zero && $ord <= $nine) { $stack[] = intval($token); } else { $right = array_pop($stack); $left = array_pop($stack); $result = self::doOperation($token, $left, $right); $stack[] = $result; } } if ($stack[0]) { return $i; } } // None of the provided rules match. The number belongs to category // 'other', which comes last. return count($rules); }
function generateFirstChars() { $file = fopen("{$this->dataDir}/allkeys.txt", 'r'); if (!$file) { $this->error("Unable to open allkeys.txt"); exit(1); } global $IP; $outFile = fopen("{$IP}/serialized/first-letters-root.ser", 'w'); if (!$outFile) { $this->error("Unable to open output file first-letters-root.ser"); exit(1); } $goodTertiaryChars = array(); // For each character with an entry in allkeys.txt, overwrite the implicit // entry in $this->weights that came from the UCD. // Also gather a list of tertiary weights, for use in selecting the group header while (false !== ($line = fgets($file))) { // We're only interested in single-character weights, pick them out with a regex $line = trim($line); if (!preg_match('/^([0-9A-F]+)\\s*;\\s*([^#]*)/', $line, $m)) { continue; } $cp = hexdec($m[1]); $allWeights = trim($m[2]); $primary = ''; $tertiary = ''; if (!isset($this->weights[$cp])) { // Non-printable, ignore continue; } foreach (StringUtils::explode('[', $allWeights) as $weightStr) { preg_match_all('/[*.]([0-9A-F]+)/', $weightStr, $m); if (!empty($m[1])) { if ($m[1][0] !== '0000') { $primary .= '.' . $m[1][0]; } if ($m[1][2] !== '0000') { $tertiary .= '.' . $m[1][2]; } } } $this->weights[$cp] = $primary; if ($tertiary === '.0008' || $tertiary === '.000E') { $goodTertiaryChars[$cp] = true; } } fclose($file); // Identify groups of characters with the same primary weight $this->groups = array(); asort($this->weights, SORT_STRING); $prevWeight = reset($this->weights); $group = array(); foreach ($this->weights as $cp => $weight) { if ($weight !== $prevWeight) { $this->groups[$prevWeight] = $group; $prevWeight = $weight; if (isset($this->groups[$weight])) { $group = $this->groups[$weight]; } else { $group = array(); } } $group[] = $cp; } if ($group) { $this->groups[$prevWeight] = $group; } // If one character has a given primary weight sequence, and a second // character has a longer primary weight sequence with an initial // portion equal to the first character, then remove the second // character. This avoids having characters like U+A732 (double A) // polluting the basic latin sort area. foreach ($this->groups as $weight => $group) { if (preg_match('/(\\.[0-9A-F]*)\\./', $weight, $m)) { if (isset($this->groups[$m[1]])) { unset($this->groups[$weight]); } } } ksort($this->groups, SORT_STRING); // Identify the header character in each group $headerChars = array(); $prevChar = ""; $tertiaryCollator = new Collator('root'); $primaryCollator = new Collator('root'); $primaryCollator->setStrength(Collator::PRIMARY); $numOutOfOrder = 0; foreach ($this->groups as $weight => $group) { $uncomposedChars = array(); $goodChars = array(); foreach ($group as $cp) { if (isset($goodTertiaryChars[$cp])) { $goodChars[] = $cp; } if (!isset($this->mappedChars[$cp])) { $uncomposedChars[] = $cp; } } $x = array_intersect($goodChars, $uncomposedChars); if (!$x) { $x = $uncomposedChars; if (!$x) { $x = $group; } } // Use ICU to pick the lowest sorting character in the selection $tertiaryCollator->sort($x); $cp = $x[0]; $char = UtfNormal\Utils::codepointToUtf8($cp); $headerChars[] = $char; if ($primaryCollator->compare($char, $prevChar) <= 0) { $numOutOfOrder++; /* printf( "Out of order: U+%05X > U+%05X\n", utf8ToCodepoint( $prevChar ), utf8ToCodepoint( $char ) ); */ } $prevChar = $char; if ($this->debugOutFile) { fwrite($this->debugOutFile, sprintf("%05X %s %s (%s)\n", $cp, $weight, $char, implode(' ', array_map('UtfNormal\\Utils::codepointToUtf8', $group)))); } } print "Out of order: {$numOutOfOrder} / " . count($headerChars) . "\n"; fwrite($outFile, serialize($headerChars)); }
/** * Parse image options text and use it to make an image * @param Title $title * @param string $options * @param LinkHolderArray $holders */ function makeImage($title, $options, $holders = false) { # Check if the options text is of the form "options|alt text" # Options are: # * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang # * left no resizing, just left align. label is used for alt= only # * right same, but right aligned # * none same, but not aligned # * ___px scale to ___ pixels width, no aligning. e.g. use in taxobox # * center center the image # * framed Keep original image size, no magnify-button. # * frameless like 'thumb' but without a frame. Keeps user preferences for width # * upright reduce width for upright images, rounded to full __0 px # * border draw a 1px border around the image # * alt Text for HTML alt attribute (defaults to empty) # vertical-align values (no % or length right now): # * baseline # * sub # * super # * top # * text-top # * middle # * bottom # * text-bottom $parts = StringUtils::explode("|", $options); $sk = $this->mOptions->getSkin(); # Give extensions a chance to select the file revision for us $skip = $time = $descQuery = false; wfRunHooks('BeforeParserMakeImageLinkObj', array(&$this, &$title, &$skip, &$time, &$descQuery)); if ($skip) { return $sk->link($title); } # Get the file $imagename = $title->getDBkey(); if (isset($this->mFileCache[$imagename][$time])) { $file = $this->mFileCache[$imagename][$time]; } else { $file = wfFindFile($title, $time); if (count($this->mFileCache) > 1000) { $this->mFileCache = array(); } $this->mFileCache[$imagename][$time] = $file; } # Get parameter map $handler = $file ? $file->getHandler() : false; list($paramMap, $mwArray) = $this->getImageParams($handler); # Process the input parameters $caption = ''; $params = array('frame' => array(), 'handler' => array(), 'horizAlign' => array(), 'vertAlign' => array()); foreach ($parts as $part) { $part = trim($part); list($magicName, $value) = $mwArray->matchVariableStartToEnd($part); $validated = false; if (isset($paramMap[$magicName])) { list($type, $paramName) = $paramMap[$magicName]; // Special case; width and height come in one variable together if ($type === 'handler' && $paramName === 'width') { $m = array(); # (bug 13500) In both cases (width/height and width only), # permit trailing "px" for backward compatibility. if (preg_match('/^([0-9]*)x([0-9]*)\\s*(?:px)?\\s*$/', $value, $m)) { $width = intval($m[1]); $height = intval($m[2]); if ($handler->validateParam('width', $width)) { $params[$type]['width'] = $width; $validated = true; } if ($handler->validateParam('height', $height)) { $params[$type]['height'] = $height; $validated = true; } } elseif (preg_match('/^[0-9]*\\s*(?:px)?\\s*$/', $value)) { $width = intval($value); if ($handler->validateParam('width', $width)) { $params[$type]['width'] = $width; $validated = true; } } // else no validation -- bug 13436 } else { if ($type === 'handler') { # Validate handler parameter $validated = $handler->validateParam($paramName, $value); } else { # Validate internal parameters switch ($paramName) { case 'manualthumb': case 'alt': // @fixme - possibly check validity here for // manualthumb? downstream behavior seems odd with // missing manual thumbs. $validated = true; $value = $this->stripAltText($value, $holders); break; case 'link': $chars = self::EXT_LINK_URL_CLASS; $prots = $this->mUrlProtocols; if ($value === '') { $paramName = 'no-link'; $value = true; $validated = true; } elseif (preg_match("/^{$prots}/", $value)) { if (preg_match("/^({$prots}){$chars}+\$/", $value, $m)) { $paramName = 'link-url'; $this->mOutput->addExternalLink($value); $validated = true; } } else { $linkTitle = Title::newFromText($value); if ($linkTitle) { $paramName = 'link-title'; $value = $linkTitle; $this->mOutput->addLink($linkTitle); $validated = true; } } break; default: // Most other things appear to be empty or numeric... $validated = $value === false || is_numeric(trim($value)); } } if ($validated) { $params[$type][$paramName] = $value; } } } if (!$validated) { $caption = $part; } } # Process alignment parameters if ($params['horizAlign']) { $params['frame']['align'] = key($params['horizAlign']); } if ($params['vertAlign']) { $params['frame']['valign'] = key($params['vertAlign']); } $params['frame']['caption'] = $caption; $params['frame']['title'] = $this->stripAltText($caption, $holders); # In the old days, [[Image:Foo|text...]] would set alt text. Later it # came to also set the caption, ordinary text after the image -- which # makes no sense, because that just repeats the text multiple times in # screen readers. It *also* came to set the title attribute. # # Now that we have an alt attribute, we should not set the alt text to # equal the caption: that's worse than useless, it just repeats the # text. This is the framed/thumbnail case. If there's no caption, we # use the unnamed parameter for alt text as well, just for the time be- # ing, if the unnamed param is set and the alt param is not. # # For the future, we need to figure out if we want to tweak this more, # e.g., introducing a title= parameter for the title; ignoring the un- # named parameter entirely for images without a caption; adding an ex- # plicit caption= parameter and preserving the old magic unnamed para- # meter for BC; ... if ($caption !== '' && !isset($params['frame']['alt']) && !isset($params['frame']['framed']) && !isset($params['frame']['thumbnail']) && !isset($params['frame']['manualthumb'])) { $params['frame']['alt'] = $params['frame']['title']; } wfRunHooks('ParserMakeImageParams', array($title, $file, &$params)); # Linker does the rest $ret = $sk->makeImageLink2($title, $file, $params['frame'], $params['handler'], $time, $descQuery); # Give the handler a chance to modify the parser object if ($handler) { $handler->parserTransformHook($this, $file); } return $ret; }
function highlight($text) { $lines = StringUtils::explode("\n", $text); foreach ($lines as $lineNum => $line) { if ($lineNum == $this->lineNum - 1) { return "{$line}\n" . str_repeat(' ', $this->colNum - 1) . "^\n"; } } return ''; }
/** * Parse content of <gallery> tag (add images with captions and links provided) */ public function parse(&$parser = null) { wfProfileIn(__METHOD__); //use images passed inside <gallery> tag $lines = StringUtils::explode("\n", $this->mText); foreach ($lines as $line) { if ($line == '') { continue; } $parts = (array) StringUtils::explode('|', $line); // get name of an image from current line and remove it from list of params $imageName = array_shift($parts); if (strpos($line, '%') !== false) { $imageName = urldecode($imageName); } // Allow <gallery> to accept image names without an Image: prefix $tp = Title::newFromText($imageName, NS_FILE); $nt =& $tp; if (is_null($nt)) { // Bogus title. Ignore these so we don't bomb out later. continue; } // search for caption and link= param $captionParts = array(); $link = $linktext = $shorttext = ''; foreach ($parts as $part) { if (substr($part, 0, 5) == 'link=') { $link = substr($part, 5); } else { if (substr($part, 0, 9) == 'linktext=') { $linktext = substr($part, 9); } else { if (substr($part, 0, 10) == 'shorttext=') { $shorttext = substr($part, 10); } else { $tempPart = trim($part); //If it looks like Gallery param don't treat it as a caption part if (!in_array($tempPart, $this->mAvailableUniqueParams)) { $captionParts[] = $tempPart; } } } } } // support captions with internal links with pipe (Foo.jpg|link=Bar|[[test|link]]) $caption = implode('|', $captionParts); $imageItem = array('name' => $imageName, 'caption' => $caption, 'link' => $link, 'linktext' => $linktext, 'shorttext' => $shorttext, 'data-caption' => Sanitizer::removeHTMLtags($caption)); // Get article link if it exists. If the href attribute is identical to the local // file URL, then there is no article URL. $localUrl = $tp->getLocalUrl(); $linkAttributes = $this->parseLink($localUrl, $tp->getText(), $link); if ($linkAttributes['href'] !== $localUrl) { $imageItem['linkhref'] = $linkAttributes['href']; } // store list of images from inner content of tag (to be used by front-end) $this->mData['images'][] = $imageItem; // store list of images actually shown (to be used by front-end) $this->mData['imagesShown'][] = $imageItem; $this->add($nt, $this->mParser->recursiveTagParse($caption), $link, $caption); // Only add real images (bug #5586) if ($nt->getNamespace() == NS_FILE) { $this->mParser->mOutput->addImage($nt->getDBkey()); } } // support "showrecentuploads" attribute (add 20 recently uploaded images at the end of slideshow) if (!empty($this->mShowRecentUploads)) { $this->addRecentlyUploaded(self::RECENT_UPLOADS_IMAGES); } // store ID of gallery if (empty($this->mData['id'])) { $this->mData['id'] = self::$galleriesCounter++; } if (!empty($parser)) { $this->recordParserOption($parser); } wfProfileOut(__METHOD__); }
/** * @param $in * @param array $param * @param Parser $parser * @param bool $frame * @return string */ public static function renderSlideshowTag($in, $param = array(), $parser = null, $frame = false) { $s = new CarZamSlideshow(); $s->setParser($parser); $s->setContextTitle($parser->getTitle()); $s->setHideBadImages(); if (isset($param['height'])) { $explosion = explode('px', strtolower($param['height'])); $s->setPhotoHeight($explosion[0]); } if (isset($param['width'])) { $explosion = explode('px', strtolower($param['width'])); $s->setPhotoWidth($explosion[0]); } if (isset($param['float'])) { $s->setFloat($param['float']); } # Reading inside the tag, right now takes arguments by order /** @todo make less ugly */ $lines = StringUtils::explode("\n", $in); foreach ($lines as $line) { $parameters = self::parseLine($line, $parser, $frame); if ($parameters === false) { continue; } else { $s->add($parameters['title'], $parameters['caption'], $parameters['alt'], $parameters['titleLink']); } } return $s->toHTML(); }
/** * Execute the pass. * @return string */ private function execute() { $text = $this->text; # Parsing through the text line by line. The main thing # happening here is handling of block-level elements p, pre, # and making lists from lines starting with * # : etc. $textLines = StringUtils::explode("\n", $text); $lastPrefix = $output = ''; $this->DTopen = $inBlockElem = false; $prefixLength = 0; $pendingPTag = false; $inBlockquote = false; foreach ($textLines as $inputLine) { # Fix up $lineStart if (!$this->lineStart) { $output .= $inputLine; $this->lineStart = true; continue; } # * = ul # # = ol # ; = dt # : = dd $lastPrefixLength = strlen($lastPrefix); $preCloseMatch = preg_match('/<\\/pre/i', $inputLine); $preOpenMatch = preg_match('/<pre/i', $inputLine); # If not in a <pre> element, scan for and figure out what prefixes are there. if (!$this->inPre) { # Multiple prefixes may abut each other for nested lists. $prefixLength = strspn($inputLine, '*#:;'); $prefix = substr($inputLine, 0, $prefixLength); # eh? # ; and : are both from definition-lists, so they're equivalent # for the purposes of determining whether or not we need to open/close # elements. $prefix2 = str_replace(';', ':', $prefix); $t = substr($inputLine, $prefixLength); $this->inPre = (bool) $preOpenMatch; } else { # Don't interpret any other prefixes in preformatted text $prefixLength = 0; $prefix = $prefix2 = ''; $t = $inputLine; } # List generation if ($prefixLength && $lastPrefix === $prefix2) { # Same as the last item, so no need to deal with nesting or opening stuff $output .= $this->nextItem(substr($prefix, -1)); $pendingPTag = false; if (substr($prefix, -1) === ';') { # The one nasty exception: definition lists work like this: # ; title : definition text # So we check for : in the remainder text to split up the # title and definition, without b0rking links. $term = $t2 = ''; if ($this->findColonNoLinks($t, $term, $t2) !== false) { $t = $t2; $output .= $term . $this->nextItem(':'); } } } elseif ($prefixLength || $lastPrefixLength) { # We need to open or close prefixes, or both. # Either open or close a level... $commonPrefixLength = $this->getCommon($prefix, $lastPrefix); $pendingPTag = false; # Close all the prefixes which aren't shared. while ($commonPrefixLength < $lastPrefixLength) { $output .= $this->closeList($lastPrefix[$lastPrefixLength - 1]); --$lastPrefixLength; } # Continue the current prefix if appropriate. if ($prefixLength <= $commonPrefixLength && $commonPrefixLength > 0) { $output .= $this->nextItem($prefix[$commonPrefixLength - 1]); } # Open prefixes where appropriate. if ($lastPrefix && $prefixLength > $commonPrefixLength) { $output .= "\n"; } while ($prefixLength > $commonPrefixLength) { $char = substr($prefix, $commonPrefixLength, 1); $output .= $this->openList($char); if (';' === $char) { # @todo FIXME: This is dupe of code above if ($this->findColonNoLinks($t, $term, $t2) !== false) { $t = $t2; $output .= $term . $this->nextItem(':'); } } ++$commonPrefixLength; } if (!$prefixLength && $lastPrefix) { $output .= "\n"; } $lastPrefix = $prefix2; } # If we have no prefixes, go to paragraph mode. if (0 == $prefixLength) { # No prefix (not in list)--go to paragraph mode # @todo consider using a stack for nestable elements like span, table and div $openMatch = preg_match('/(?:<table|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|' . '<p|<ul|<ol|<dl|<li|<\\/tr|<\\/td|<\\/th)/iS', $t); $closeMatch = preg_match('/(?:<\\/table|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|' . '<td|<th|<\\/?blockquote|<\\/?div|<hr|<\\/pre|<\\/p|<\\/mw:|' . Parser::MARKER_PREFIX . '-pre|<\\/li|<\\/ul|<\\/ol|<\\/dl|<\\/?center)/iS', $t); if ($openMatch || $closeMatch) { $pendingPTag = false; # @todo bug 5718: paragraph closed $output .= $this->closeParagraph(); if ($preOpenMatch && !$preCloseMatch) { $this->inPre = true; } $bqOffset = 0; while (preg_match('/<(\\/?)blockquote[\\s>]/i', $t, $bqMatch, PREG_OFFSET_CAPTURE, $bqOffset)) { $inBlockquote = !$bqMatch[1][0]; // is this a close tag? $bqOffset = $bqMatch[0][1] + strlen($bqMatch[0][0]); } $inBlockElem = !$closeMatch; } elseif (!$inBlockElem && !$this->inPre) { if (' ' == substr($t, 0, 1) && ($this->lastSection === 'pre' || trim($t) != '') && !$inBlockquote) { # pre if ($this->lastSection !== 'pre') { $pendingPTag = false; $output .= $this->closeParagraph() . '<pre>'; $this->lastSection = 'pre'; } $t = substr($t, 1); } else { # paragraph if (trim($t) === '') { if ($pendingPTag) { $output .= $pendingPTag . '<br />'; $pendingPTag = false; $this->lastSection = 'p'; } else { if ($this->lastSection !== 'p') { $output .= $this->closeParagraph(); $this->lastSection = ''; $pendingPTag = '<p>'; } else { $pendingPTag = '</p><p>'; } } } else { if ($pendingPTag) { $output .= $pendingPTag; $pendingPTag = false; $this->lastSection = 'p'; } elseif ($this->lastSection !== 'p') { $output .= $this->closeParagraph() . '<p>'; $this->lastSection = 'p'; } } } } } # somewhere above we forget to get out of pre block (bug 785) if ($preCloseMatch && $this->inPre) { $this->inPre = false; } if ($pendingPTag === false) { $output .= $t; if ($prefixLength === 0) { $output .= "\n"; } } } while ($prefixLength) { $output .= $this->closeList($prefix2[$prefixLength - 1]); --$prefixLength; if (!$prefixLength) { $output .= "\n"; } } if ($this->lastSection !== '') { $output .= '</' . $this->lastSection . '>'; $this->lastSection = ''; } return $output; }
/** * convert text to different variants of a language. the automatic * conversion is done in autoConvert(). here we parse the text * marked with -{}-, which specifies special conversions of the * text that can not be accomplished in autoConvert() * * syntax of the markup: * -{code1:text1;code2:text2;...}- or * -{flags|code1:text1;code2:text2;...}- or * -{text}- in which case no conversion should take place for text * * @param string $text text to be converted * @param bool $isTitle whether this conversion is for the article title * @return string converted text * @public */ function convert($text, $isTitle = false) { $mw =& MagicWord::get('notitleconvert'); if ($mw->matchAndRemove($text)) { $this->mDoTitleConvert = false; } $mw =& MagicWord::get('nocontentconvert'); if ($mw->matchAndRemove($text)) { $this->mDoContentConvert = false; } // no conversion if redirecting $mw =& MagicWord::get('redirect'); if ($mw->matchStart($text)) { return $text; } $plang = $this->getPreferredVariant(); // for title convertion if ($isTitle) { return $this->convertTitle($text, $plang); } $tarray = StringUtils::explode($this->mMarkup['end'], $text); $text = ''; $marks = array(); foreach ($tarray as $txt) { $marked = explode($this->mMarkup['begin'], $txt, 2); if (array_key_exists(1, $marked)) { $crule = new ConverterRule($marked[1], $this); $crule->parse($plang); $marked[1] = $crule->getDisplay(); $this->prepareManualConv($crule); } else { $marked[0] .= $this->mMarkup['end']; } array_push($marks, $marked); } $this->applyManualConv(); foreach ($marks as $marked) { if ($this->mDoContentConvert) { $text .= $this->autoConvert($marked[0], $plang); } else { $text .= $marked[0]; } if (array_key_exists(1, $marked)) { $text .= $marked[1]; } } // Remove the last delimiter (wasn't real) $text = substr($text, 0, -strlen($this->mMarkup['end'])); return $text; }
/** * Returns lines of text contained inside mosaic slider gallery tag * @param $articleText * @return array */ protected function extractMosaicGalleryImages($articleText) { $lines = array(); if (preg_match('/\\<gallery.+mosaic.+\\>([\\s\\S]+)\\<\\/gallery\\>/', $articleText, $matches)) { $lines = StringUtils::explode("\n", $matches[1]); } return $lines; }
/** * Parse content of <gallery> tag (add images with captions and links provided) */ public function parse(&$parser = null) { wfProfileIn(__METHOD__); //use images passed inside <gallery> tag $lines = StringUtils::explode("\n", $this->mText); foreach ($lines as $line) { if ($line == '') { continue; } $parts = (array) StringUtils::explode('|', $line); // get name of an image from current line and remove it from list of params $imageName = array_shift($parts); if (strpos($line, '%') !== false) { $imageName = urldecode($imageName); } // Allow <gallery> to accept image names without an Image: prefix $tp = Title::newFromText($imageName, NS_FILE); $nt =& $tp; if (is_null($nt)) { // Bogus title. Ignore these so we don't bomb out later. continue; } // search for caption and link= param $captionParts = array(); $link = $linktext = $shorttext = ''; foreach ($parts as $part) { if (substr($part, 0, 5) == 'link=') { $link = substr($part, 5); } else { if (substr($part, 0, 9) == 'linktext=') { $linktext = substr($part, 9); } else { if (substr($part, 0, 10) == 'shorttext=') { $shorttext = substr($part, 10); } else { $captionParts[] = trim($part); } } } } // support captions with internal links with pipe (Foo.jpg|link=Bar|[[test|link]]) $caption = implode('|', $captionParts); $imageItem = array('name' => $imageName, 'caption' => $caption, 'link' => $link, 'linktext' => $linktext, 'shorttext' => $shorttext, 'data-caption' => htmlspecialchars($caption)); // store list of images from inner content of tag (to be used by front-end) $this->mData['images'][] = $imageItem; // store list of images actually shown (to be used by front-end) $this->mData['imagesShown'][] = $imageItem; // use global instance of parser (RT #44689 / RT #44712) $caption = $this->mParser->recursiveTagParse($caption); $this->add($nt, $caption, $link); // Only add real images (bug #5586) if ($nt->getNamespace() == NS_FILE) { $this->mParser->mOutput->addImage($nt->getDBkey()); } } // support "showrecentuploads" attribute (add 20 recently uploaded images at the end of slideshow) if (!empty($this->mShowRecentUploads)) { $this->addRecentlyUploaded(self::RECENT_UPLOADS_IMAGES); } if (!empty($this->mFeedURL)) { $data = WikiaPhotoGalleryRSS::parseFeed($this->mFeedURL); //title of the feed - used by Lightbox $this->mData['feedTitle'] = $data['feedTitle']; //use images from feed $this->mExternalImages = $data['images']; // store list of images from inner content of tag (to be used by front-end) $this->mData['externalImages'] = $this->mExternalImages; // store list of images actually shown (to be used by front-end) $this->mData['imagesShown'] = $this->mExternalImages; } // store ID of gallery if (empty($this->mData['id'])) { $this->mData['id'] = self::$galleriesCounter++; } if (!empty($parser)) { $this->recordParserOption($parser); } wfProfileOut(__METHOD__); }
/** * convert text to different variants of a language. the automatic * conversion is done in autoConvert(). here we parse the text * marked with -{}-, which specifies special conversions of the * text that can not be accomplished in autoConvert() * * syntax of the markup: * -{code1:text1;code2:text2;...}- or * -{flags|code1:text1;code2:text2;...}- or * -{text}- in which case no conversion should take place for text * * @param string $text text to be converted * @param bool $isTitle whether this conversion is for the article title * @return string converted text * @public */ function convert($text, $isTitle = false) { $mw =& MagicWord::get('notitleconvert'); if ($mw->matchAndRemove($text)) { $this->mDoTitleConvert = false; } $mw =& MagicWord::get('nocontentconvert'); if ($mw->matchAndRemove($text)) { $this->mDoContentConvert = false; } // no conversion if redirecting $mw =& MagicWord::get('redirect'); if ($mw->matchStart($text)) { return $text; } // for title convertion if ($isTitle) { return $this->convertTitle($text); } $plang = $this->getPreferredVariant(); $tarray = StringUtils::explode($this->mMarkup['end'], $text); $text = ''; $lastDelim = false; foreach ($tarray as $txt) { $marked = explode($this->mMarkup['begin'], $txt, 2); if ($this->mDoContentConvert) { $text .= $this->autoConvert($marked[0], $plang); } else { $text .= $marked[0]; } if (array_key_exists(1, $marked)) { // strip the flags from syntax like -{T| ... }- $crule = new ConverterRule($marked[1], $this); $crule->parse($plang); $text .= $crule->getDisplay(); $this->applyManualConv($crule); $lastDelim = false; } else { // Reinsert the }- which wasn't part of anything $text .= $this->mMarkup['end']; $lastDelim = true; } } if ($lastDelim) { // Remove the last delimiter (wasn't real) $text = substr($text, 0, -strlen($this->mMarkup['end'])); } return $text; }