static function processArticleHTML($body, $opts = array())
 {
     global $wgUser, $wgTitle, $wgLanguageCode;
     $doc = PHPQuery::newDocument($body);
     $featurestar = pq("div#featurestar");
     if ($featurestar) {
         $clearelement = pq($featurestar)->next();
         $clearelement->remove();
         $featurestar->remove();
     }
     $ads = $wgUser->isAnon() && !@$opts['no-ads'] && wikihowAds::isEligibleForAds();
     // Remove __TOC__ resulting html from all pages other than User pages
     if (@$opts['ns'] != NS_USER && pq('div#toc')->length) {
         $toc = pq('div#toc');
         //in upgrade, it's no longer preceded by an h2, so deleting the intro instead :(
         //maybe this will change so leaving in for now.
         //$toc->prev()->remove();
         $toc->remove();
     }
     $sticky = "";
     if (@$opts['sticky-headers']) {
         $sticky = " sticky ";
     }
     // Remove originators for titles that don't exist
     if ($wgTitle->getArticleId() == 0) {
         pq('#originators')->remove();
     }
     //move firstHeading to inside the intro
     $firstH2 = pq("h2:first");
     if (pq($firstH2)->length() == 0) {
         try {
             pq("#bodycontents")->children(":first")->wrapAll("<div class='section wh_block'></div>");
         } catch (Exception $e) {
         }
     } else {
         try {
             pq($firstH2)->prevAll()->reverse()->wrapAll("<div id='intro' class='section {$sticky}'></div>");
         } catch (Exception $e) {
         }
     }
     //add a clearall to the end of the intro
     pq("#intro")->append("<div class='clearall'></div>");
     //removing any stray br tags at the start of the intro
     foreach (pq("#intro #originators")->next()->children() as $child) {
         if (pq($child)->is("br")) {
             pq($child)->remove();
         } else {
             break;
         }
     }
     //add the pimpedheader to our h3s!
     pq('h3, h4')->prepend('<div class="altblock"></div>');
     foreach (pq("h2") as $node) {
         $h2Parent = pq($node)->parent();
         if (@$opts['ns'] == NS_USER && pq($h2Parent)->attr("id") == "toctitle") {
             pq("#toc")->wrapAll("<div class='section'></div>");
             pq("#toc ul:first")->addClass("section_text");
             continue;
         }
         //find each section
         //first grab the name
         $sectionName = mb_strtolower(pq("span.mw-headline", $node)->html());
         //Remove all non-letters and numbers in all languges
         if ($wgLanguageCode == 'en') {
             $sectionName = preg_replace("/[^A-Za-z0-9]/u", '', $sectionName);
         } elseif ($wgLanguageCode == 'hi') {
             $sectionName = str_replace(' ', '', $sectionName);
         } else {
             $sectionName = preg_replace("/[^\\p{L}\\p{N}]/u", '', $sectionName);
         }
         //now find all the elements prior to the next h2
         $set = array();
         $h3Tags = array();
         $h3Elements = array();
         $priorToH3Set = array();
         $h3Count = 0;
         foreach (pq($node)->nextAll() as $sibling) {
             if (pq($sibling)->is("h2")) {
                 break;
             }
             if (pq($sibling)->is("h3")) {
                 $h3Count++;
                 $h3Tags[$h3Count] = $sibling;
                 $h3Elements[$h3Count] = array();
             } else {
                 if ($h3Count > 0) {
                     $h3Elements[$h3Count][] = $sibling;
                 } else {
                     $priorToH3Set[] = $sibling;
                 }
             }
             $set[] = $sibling;
         }
         if (mb_strtolower($sectionName) == mb_strtolower(wfMessage('steps')->text())) {
             if ($h3Count > 0) {
                 //has alternate methods
                 $altMethodNames = array();
                 $altMethodAnchors = array();
                 if (count($priorToH3Set) > 0) {
                     //needs to have a steps section prior to the
                     //alt method
                     try {
                         pq($priorToH3Set)->wrapAll("<div id='{$sectionName}' class='section_text'></div>");
                     } catch (Exception $e) {
                     }
                     $overallSet = array();
                     $overallSet[] = $node;
                     foreach (pq("div#{$sectionName}:first") as $temp) {
                         $overallSet[] = $temp;
                     }
                     try {
                         pq($overallSet)->wrapAll("<div class='section steps {$sticky}'></div>");
                     } catch (Exception $e) {
                     }
                 } else {
                     //hide the h2 tag
                     pq($node)->addClass("hidden");
                 }
                 $stepsEditUrl = pq('.editsection', $node)->attr("href");
                 $displayMethodCount = $h3Count;
                 $isSample = array();
                 for ($i = 1; $i <= $h3Count; $i++) {
                     $isSampleItem = false;
                     if (!is_array($h3Elements[$i]) || count($h3Elements[$i]) < 1) {
                         $isSampleItem = false;
                     } else {
                         //the sd_container isn't always the first element, need to look through all
                         foreach ($h3Elements[$i] as $node) {
                             //not the most efficient way to do this, but couldn't get the find function to work.
                             if (pq($node)->attr("id") == "sd_container") {
                                 $isSampleItem = true;
                                 break;
                             }
                         }
                     }
                     if ($isSampleItem) {
                         $isSample[$i] = true;
                         $displayMethodCount--;
                     } else {
                         $isSample[$i] = false;
                     }
                 }
                 if ($ads) {
                     wikihowAds::setAltMethods($displayMethodCount > 1);
                 }
                 //[sc] ***INTERMEDIATE STEP (remove line below)
                 $wikihowArticle = WikihowArticleEditor::newFromTitle($wgTitle);
                 $hasParts = $opts['magic-word'] == MagicWord::get('parts');
                 $displayMethod = 1;
                 for ($i = 1; $i <= $h3Count; $i++) {
                     //change the method title
                     $methodTitle = pq("span.mw-headline", $h3Tags[$i])->html();
                     //[sc] ***INTERMEDIATE STEP (remove line below)
                     $removeRet = WikihowArticleEditor::removeMethodNamePrefix($methodTitle);
                     $altMethodNames[] = $methodTitle;
                     $altMethodAnchors[] = pq("span.mw-headline", $h3Tags[$i])->attr("id");
                     //[sc] ***INTERMEDIATE STEP (swap if logic below)
                     //if ($displayMethodCount > 1 && $hasParts && $opts['ns'] == NS_MAIN) {
                     if ($displayMethodCount > 1 && !$isSample[$i] && ($removeRet['has_parts'] || $hasParts) && $opts['ns'] == NS_MAIN) {
                         if ($methodTitle) {
                             $methodTitle = wfMessage("part_2", $displayMethod, $displayMethodCount, $methodTitle)->text();
                         } else {
                             $methodTitle = wfMessage("part_1", $displayMethod, $displayMethodCount)->text();
                         }
                         $displayMethod++;
                     } elseif ($displayMethodCount > 1 && !$isSample[$i] && $opts['ns'] == NS_MAIN) {
                         if ($methodTitle) {
                             $methodTitle = wfMessage("method_2", $displayMethod, $displayMethodCount, $methodTitle)->text();
                         } else {
                             $methodTitle = wfMessage("method_1", $displayMethod, $displayMethodCount)->text();
                         }
                         $displayMethod++;
                     }
                     pq("span.mw-headline", $h3Tags[$i])->html($methodTitle);
                     //add our custom anchors
                     $anchor_name = pq("span.mw-headline", $h3Tags[$i])->attr('id') . '_sub';
                     try {
                         pq($h3Tags[$i])->before('<a name="' . $anchor_name . '" class="anchor"></a>');
                     } catch (Exception $e) {
                     }
                     //want to change the url for the edit link to
                     //edit the whole steps section, not just the
                     //alternate method
                     pq(".editsection", $h3Tags[$i])->attr("href", $stepsEditUrl);
                     $sample = $isSample[$i] ? "sample" : "";
                     //only wrap if there's stuff there to wrap.
                     //This happens when people put two sub methods on top of each other without
                     //any content between.
                     if (count($h3Elements[$i]) > 0) {
                         pq($h3Elements[$i])->wrapAll("<div id='{$sectionName}_{$i}' class='section_text'></div>");
                     }
                     $overallSet = array();
                     $overallSet[] = $h3Tags[$i];
                     foreach (pq("div#{$sectionName}_{$i}:first") as $temp) {
                         $overallSet[] = $temp;
                     }
                     try {
                         pq($overallSet)->wrapAll("<div class='section steps {$sample} {$sticky}'></div>");
                     } catch (Exception $e) {
                     }
                 }
                 //BEBETH - not sure we need this anymore, but not sure yet
                 //fix for Chrome -- wrap first anchor name so it detects the spacing
                 try {
                     pq(".section.steps:first")->prev()->children(".anchor")->after('<br class="clearall" />')->wrapAll('<div></div>');
                 } catch (Exception $e) {
                 }
                 //now we should have all the alt methods,
                 //let's create the links to them under the headline
                 $charCount = 0;
                 $maxCount = 80000;
                 //temporarily turning off hidden headers
                 $hiddenCount = 0;
                 $anchorList = "";
                 for ($i = 0; $i < count($altMethodAnchors); $i++) {
                     $methodName = pq('<div>' . $altMethodNames[$i] . '</div>')->text();
                     // remove any reference notes
                     $methodName = preg_replace("@\\[\\d{1,3}\\]\$@", "", $methodName);
                     $charCount += strlen($methodName);
                     $class = "";
                     if ($charCount > $maxCount) {
                         $class = "hidden excess";
                         $hiddenCount++;
                     }
                     if ($methodName == "") {
                         continue;
                     }
                     $anchorList .= "<a href='#{$altMethodAnchors[$i]}_sub' class='{$class}'>{$methodName}</a>";
                 }
                 $hiddentext = "";
                 if ($hiddenCount > 0) {
                     $hiddenText = "<a href='#' id='method_toc_unhide'>{$hiddenCount} more method" . ($hiddenCount > 1 ? "s" : "") . "</a>";
                     $hiddenText .= "<a href='#' id='method_toc_hide' class='hidden'>show less methods</a>";
                 } else {
                     $hiddenText = '';
                 }
                 //add our little list header
                 if ($hasParts) {
                     //ucwords
                     $anchorList = '<span>' . ucwords(Misc::numToWord(count($altMethodAnchors), 10)) . ' ' . wfMessage('part_3')->text() . ':</span>' . $anchorList;
                 } else {
                     $anchorList = '<span>' . ucwords(Misc::numToWord(count($altMethodAnchors), 10)) . ' ' . wfMessage('method_3')->text() . ':</span>' . $anchorList;
                 }
                 //chance to reformat the alt method_toc before output
                 //using for running tests
                 $bAfter = false;
                 wfRunHooks('BeforeOutputAltMethodTOC', array($wgTitle, &$anchorList, &$bAfter));
                 $bAfter = true;
                 if ($bAfter) {
                     pq("#originators")->after("<p id='method_toc' style='margin-top:-10px'>{$anchorList}{$hiddenText}</p>");
                 } else {
                     pq(".firstHeading")->after("<p id='method_toc'>{$anchorList}{$hiddenText}</p>");
                 }
             } else {
                 //only 1 method
                 if ($ads) {
                     wikihowAds::setAltMethods(false);
                 }
                 if ($set) {
                     try {
                         pq($set)->wrapAll("<div id='{$sectionName}' class='section_text'></div>");
                     } catch (Exception $e) {
                     }
                 }
                 $overallSet = array();
                 $overallSet[] = $node;
                 foreach (pq("div#{$sectionName}:first") as $temp) {
                     $overallSet[] = $temp;
                 }
                 try {
                     pq($overallSet)->wrapAll("<div class='section steps {$sticky}'></div>");
                 } catch (Exception $e) {
                 }
             }
         } else {
             //not a steps section
             if ($set) {
                 $sec_id = @$opts['list-page'] ? '' : 'id="' . $sectionName . '"';
                 try {
                     $new_set = pq($set)->wrapAll("<div {$sec_id} class='section_text'></div>");
                 } catch (Exception $e) {
                 }
             }
             $overallSet = array();
             $overallSet[] = $node;
             foreach (pq("div#{$sectionName}:first") as $temp) {
                 $overallSet[] = $temp;
             }
             try {
                 pq($overallSet)->wrapAll("<div class='section {$sectionName} {$sticky}'></div>");
             } catch (Exception $e) {
             }
             if (@$opts['list-page']) {
                 //gotta pull those dangling divs into the same space as the h2
                 try {
                     pq($overallSet)->parent()->append(pq($new_set));
                 } catch (Exception $e) {
                 }
             }
             // commenting this out because it's causing the following error:
             // "Couldn't add newnode as the previous sibling of refnode"
             // // format edit links for non-steps sections
             // // pq('span', $node)->prepend(pq('a.edit', $node));
             //remove the edit link from subheaders if we're not in the steps section
             try {
                 pq(".{$sectionName} h3 .editsection")->remove();
             } catch (Exception $e) {
             }
         }
     }
     //add a clear to the end of each section_text to make sure
     //images don't bleed across the bottom
     pq(".section_text")->append("<div class='clearall'></div>");
     // Add checkboxes to Ingredients and 'Things You Need' sections, but only to the top-most li
     $lis = pq('#ingredients > ul > li, #thingsyoullneed > ul > li');
     foreach ($lis as $li) {
         $id = md5(pq($li)->html() . mt_rand(1, 100));
         pq($li)->html("<input id='item_{$id}' class='css-checkbox' type='checkbox'/><label for='item_{$id}' name='item_{$id}_lbl' class='css-checkbox-label'></label><div class='checkbox-text'>" . pq($li)->html() . '</div>');
     }
     // Move templates above article body contents and style appropriately
     foreach (pq('.template_top') as $template) {
         pq($template)->addClass('sidebox');
         if (pq($template)->parent()->hasClass('tmp_li')) {
             pq($template)->addClass('tmp_li');
         }
         if ($wgUser->isAnon()) {
             pq($template)->addClass('notice_bgcolor_lo');
         } else {
             pq($template)->addClass('notice_bgcolor_important');
         }
     }
     // put templates after the intro div
     pq('.template_top')->insertAfter('#intro');
     //now put the step numbers in
     foreach (pq("div.steps .section_text > ol") as $list) {
         pq($list)->addClass("steps_list_2");
         $stepNum = 1;
         foreach (pq($list)->children() as $step) {
             $boldStep = WikihowArticleHTML::boldFirstSentence(pq($step)->html());
             pq($step)->html($boldStep);
             pq($step)->prepend("<div class='step_num'>{$stepNum}</div>");
             pq($step)->append("<div class='clearall'></div>");
             $stepNum++;
         }
     }
     foreach (pq(".steps:last .steps_list_2")->children(":last-child") as $step) {
         pq($step)->addClass("final_li");
     }
     //move each of the large images to the top
     foreach (pq(".steps_list_2 li .mwimg.largeimage") as $image) {
         //delete any previous <br>
         foreach (pq($image)->prevAll() as $node) {
             if (pq($node)->is("br")) {
                 pq($node)->remove();
             } else {
                 break;
             }
         }
         //first handle the special case where the image
         //ends up inside the bold tag by accident
         if (pq($image)->parent()->is("b")) {
             pq($image)->insertBefore(pq($image)->parent());
         }
         if (pq($image)->parent()->parent()->is(".steps_list_2")) {
             pq($image)->parent()->prepend($image);
         }
     }
     //move each of the large images to the top
     foreach (pq(".steps_list_2 li .whvid_cont") as $vid) {
         //delete any previous <br>
         foreach (pq($vid)->prevAll() as $node) {
             if (pq($node)->is("br")) {
                 pq($node)->remove();
             } else {
                 break;
             }
         }
         if (pq($vid)->parent()->parent()->is(".steps_list_2")) {
             pq($vid)->parent()->prepend($vid);
         }
     }
     //if there's a related articles section, make it have images
     $relatedSection = pq("#relatedwikihows");
     if ($relatedSection) {
         foreach (pq("li a", $relatedSection) as $related) {
             $titleText = pq($related)->attr("title");
             $title = Title::newFromText($titleText);
             if ($title) {
                 $image = SkinWikihowskin::getArticleThumb($title, 127, 120);
                 pq($relatedSection)->prepend($image);
             }
             pq($related)->remove();
         }
         pq("ul", $relatedSection)->remove();
         pq($relatedSection)->append("<div class='clearall'></div>");
     }
     //remove all images in the intro that aren't
     //marked with the class "introimage"
     pq("#intro .mwimg:not(.introimage)")->remove();
     //let's mark all the <p> tags that aren't inside a step.
     //they need special padding
     foreach (pq(".section.steps p") as $p) {
         if (pq($p)->parents(".steps_list_2")->count() == 0 && pq($p)->children(".anchor")->count() == 0) {
             pq($p)->addClass("lone_p");
         }
     }
     // Add alt method adder cta
     if (class_exists("AltMethodAdder") && $wgTitle && $wgUser && $wgUser->isAnon()) {
         $cta = AltMethodAdder::getCTA($wgTitle);
         if (!is_null($cta)) {
             pq("div.steps:last")->after($cta);
         }
     }
     //add line breaks between the p tags
     foreach (pq("p") as $paragraph) {
         $sibling = pq($paragraph)->next();
         if (!pq($sibling)->is("p")) {
             continue;
         }
         if (pq($sibling)->children(":first")->hasClass("anchor")) {
             continue;
         }
         $id = pq($paragraph)->attr("id");
         if ($id == "originators" || $id == "method_toc") {
             continue;
         }
         pq($paragraph)->after("<br />");
     }
     if ($ads) {
         pq(".#intro")->append(wikihowAds::getAdUnitPlaceholder('intro'));
         pq(".steps_list_2:first li:first")->append(wikihowAds::getAdUnitPlaceholder(0));
         pq(".final_li")->append(wikihowAds::getAdUnitPlaceholder(1));
         $tipsClass = mb_strtolower(wfMessage("tips")->text());
         //grabs the tips section by name, but internationalized
         pq(".{$tipsClass} .section_text")->children()->filter("ul:last")->after(wikihowAds::getAdUnitPlaceholder('2a'));
         //add in the Taboola ads
         if ($wgLanguageCode == "en") {
             $sourcesClass = mb_strtolower(wfMessage("sources")->text());
             //grabs the sources and citations section by name, but internationalized
             $sourcesClass = str_replace(' ', '', $sourcesClass);
             if (pq(".section.{$sourcesClass}")->length) {
                 //put above Sources & Citations if it exists
                 pq(".section.{$sourcesClass}")->before(wikihowAds::getAdUnitTaboola());
             } else {
                 // put above Article Info
                 pq("#bodycontents")->after(wikihowAds::getAdUnitTaboola());
             }
         }
     }
     $markPatrolledLink = self::getMarkPatrolledLink();
     if ($markPatrolledLink) {
         pq('#bodycontents')->append($markPatrolledLink);
     }
     // //TEST - no intro
     // global $wgRequest;
     // $article_ids = explode("\n",ConfigStorage::dbGetConfig('wikihow-nointro-test'));
     // if ($wgTitle && $wgTitle->getNamespace() == NS_MAIN && $wgRequest->getVal('action') == '' && in_array($wgTitle->getArticleID(),$article_ids)) {
     // pq('#intro p:last')->remove();
     // }
     return $doc->htmlOuter();
 }
Esempio n. 2
0
 static function processArticleHTML($body, $opts = array())
 {
     global $wgUser, $wgTitle, $wgLanguageCode;
     $skin = $wgUser->getSkin();
     $doc = PHPQuery::newDocument($body);
     $featurestar = pq("div#featurestar");
     if ($featurestar) {
         $clearelement = pq($featurestar)->next();
         $clearelement->remove();
         $featurestar->remove();
     }
     $ads = $wgUser->isAnon() && !@$opts['no-ads'] && wikihowAds::isEligibleForAds();
     // Remove __TOC__ resulting html from all pages other than User pages
     if (@$opts['ns'] != NS_USER && pq('table#toc')->length) {
         $toc = pq('table#toc');
         $toc->prev()->remove();
         $toc->remove();
     }
     $sticky = "";
     if (@$opts['sticky-headers']) {
         $sticky = " sticky ";
     }
     // Remove originators for titles that don't exist
     if ($wgTitle->getArticleId() == 0) {
         pq('#originators')->remove();
     }
     //move firstHeading to inside the intro
     $firstH2 = pq("h2:first");
     if (pq($firstH2)->length() == 0) {
         try {
             pq("#bodycontents")->children(":first")->wrapAll("<div class='section wh_block'></div>");
         } catch (Exception $e) {
         }
     } else {
         try {
             pq($firstH2)->prevAll()->reverse()->wrapAll("<div id='intro' class='section {$sticky}'></div>");
         } catch (Exception $e) {
         }
     }
     //add a clearall to the end of the intro
     pq("#intro")->append("<div class='clearall'></div>");
     //removing any stray br tags at the start of the intro
     foreach (pq("#intro #originators")->next()->children() as $child) {
         if (pq($child)->is("br")) {
             pq($child)->remove();
         } else {
             break;
         }
     }
     //add the pimpedheader to our h3s!
     pq('h3, h4')->prepend('<div class="altblock"></div>');
     foreach (pq("h2") as $node) {
         //find each section
         //first grab the name
         $sectionName = mb_strtolower(pq("span", $node)->html());
         //Remove all non-letters and numbers in all languges
         if ($wgLanguageCode == 'en') {
             $sectionName = preg_replace("/[^A-Za-z0-9]/u", '', $sectionName);
         } elseif ($wgLanguageCode == 'hi') {
             $sectionName = str_replace(' ', '', $sectionName);
         } else {
             $sectionName = preg_replace("/[^\\p{L}\\p{N}]/u", '', $sectionName);
         }
         //now find all the elements prior to the next h2
         $set = array();
         $h3Tags = array();
         $h3Elements = array();
         $priorToH3Set = array();
         $h3Count = 0;
         foreach (pq($node)->nextAll() as $sibling) {
             if (pq($sibling)->is("h2")) {
                 break;
             }
             if (pq($sibling)->is("h3")) {
                 $h3Count++;
                 $h3Tags[$h3Count] = $sibling;
                 $h3Elements[$h3Count] = array();
             } else {
                 if ($h3Count > 0) {
                     $h3Elements[$h3Count][] = $sibling;
                 } else {
                     $priorToH3Set[] = $sibling;
                 }
             }
             $set[] = $sibling;
         }
         if (mb_strtolower($sectionName) == mb_strtolower(wfMsg('steps'))) {
             if ($h3Count > 0) {
                 //has alternate methods
                 $altMethodNames = array();
                 $altMethodAnchors = array();
                 if (count($priorToH3Set) > 1) {
                     //if there are alt methods, then there will at least be a <p> tag with a link inside, so that doesn't count
                     //needs to have a steps section prior to the
                     //alt method
                     try {
                         pq($priorToH3Set)->wrapAll("<div id='{$sectionName}' class='section_text'></div>");
                     } catch (Exception $e) {
                     }
                     $overallSet = array();
                     $overallSet[] = $node;
                     foreach (pq("div#{$sectionName}:first") as $temp) {
                         $overallSet[] = $temp;
                     }
                     try {
                         pq($overallSet)->wrapAll("<div class='section steps {$sticky}'></div>");
                     } catch (Exception $e) {
                     }
                 } else {
                     //hide the h2 tag
                     pq($node)->addClass("hidden");
                 }
                 $displayMethodCount = $h3Count;
                 $isSample = array();
                 for ($i = 1; $i <= $h3Count; $i++) {
                     $isSampleItem = false;
                     if (!is_array($h3Elements[$i]) || count($h3Elements[$i]) < 1) {
                         $isSampleItem = false;
                     } else {
                         //the sd_container isn't always the first element, need to look through all
                         foreach ($h3Elements[$i] as $node) {
                             //not the most efficient way to do this, but couldn't get the find function to work.
                             if (pq($node)->attr("id") == "sd_container") {
                                 $isSampleItem = true;
                                 break;
                             }
                         }
                     }
                     if ($isSampleItem) {
                         $isSample[$i] = true;
                         $displayMethodCount--;
                     } else {
                         $isSample[$i] = false;
                     }
                 }
                 if ($ads) {
                     wikihowAds::setAltMethods($displayMethodCount > 1);
                 }
                 $wikihowArticle = WikihowArticleEditor::newFromTitle($wgTitle);
                 $editLink = $skin->editSectionLink($wgTitle, $wikihowArticle->getSectionNumber("steps"));
                 $displayMethod = 1;
                 for ($i = 1; $i <= $h3Count; $i++) {
                     //change the method title
                     $methodTitle = pq("span", $h3Tags[$i])->html();
                     $removeRet = WikihowArticleEditor::removeMethodNamePrefix($methodTitle);
                     $altMethodNames[] = $methodTitle;
                     if ($displayMethodCount > 1 && !$isSample[$i] && $removeRet['has_parts'] && $opts['ns'] == NS_MAIN) {
                         if ($methodTitle) {
                             $methodTitle = wfMsg("part_2", $displayMethod, $displayMethodCount, $methodTitle);
                         } else {
                             $methodTitle = wfMsg("part_1", $displayMethod, $displayMethodCount);
                         }
                         $displayMethod++;
                     } elseif ($displayMethodCount > 1 && !$isSample[$i] && $opts['ns'] == NS_MAIN) {
                         if ($methodTitle) {
                             $methodTitle = wfMsg("method_2", $displayMethod, $displayMethodCount, $methodTitle);
                         } else {
                             $methodTitle = wfMsg("method_1", $displayMethod, $displayMethodCount);
                         }
                         $displayMethod++;
                     }
                     pq("span", $h3Tags[$i])->html($methodTitle);
                     //add an edit link to each sub method
                     pq("span", $h3Tags[$i])->prepend($editLink);
                     $sample = $isSample[$i] ? "sample" : "";
                     pq($h3Elements[$i])->wrapAll("<div id='{$sectionName}_{$i}' class='section_text'></div>");
                     $overallSet = array();
                     $overallSet[] = $h3Tags[$i];
                     foreach (pq("div#{$sectionName}_{$i}:first") as $temp) {
                         $overallSet[] = $temp;
                     }
                     try {
                         pq($overallSet)->wrapAll("<div class='section steps {$sample} {$sticky}'></div>");
                     } catch (Exception $e) {
                     }
                 }
                 $i = 1;
                 // Pull out the top-level anchors
                 foreach (pq(".section.steps") as $steps) {
                     // Only grab last anchor. There may be other anchors for subsections
                     // but we'll ignore those
                     $anchor = pq('.anchor:last', $steps);
                     $altMethodAnchors[$i] = pq($anchor)->attr("name");
                     $i++;
                 }
                 //now grab the one prior to the first step
                 $altMethodAnchors[0] = pq(".section.steps:first")->prev()->children(".anchor")->attr("name");
                 // Sometimes there isn't an anchor prior to first step, so get rid of it
                 if (empty($altMethodAnchors[0])) {
                     $altMethodAnchors = array_splice($altMethodAnchors, 0);
                 }
                 //fix for Chrome -- wrap first anchor name so it detects the spacing
                 try {
                     pq(".section.steps:first")->prev()->children(".anchor")->after('<br class="clearall" />')->wrapAll('<div></div>');
                 } catch (Exception $e) {
                 }
                 //now we should have all the alt methods,
                 //let's create the links to them under the headline
                 $charCount = 0;
                 $maxCount = 80000;
                 //temporairily turning off hidden headers
                 $hiddenCount = 0;
                 $anchorList = "";
                 //don't use the last altMethodAnchor b/c it links to the first non-step section
                 for ($i = 0; $i < count($altMethodAnchors) - 1; $i++) {
                     $methodName = pq('<div>' . $altMethodNames[$i] . '</div>')->text();
                     // remove any reference notes
                     $methodName = preg_replace("@\\[\\d{1,3}\\]\$@", "", $methodName);
                     $charCount += strlen($methodName);
                     $class = "";
                     if ($charCount > $maxCount) {
                         $class = "hidden excess";
                         $hiddenCount++;
                     }
                     if ($methodName == "") {
                         continue;
                     }
                     $anchorList .= "<a href='#{$altMethodAnchors[$i]}' class='{$class}'>{$methodName}</a>";
                 }
                 $hiddentext = "";
                 if ($hiddenCount > 0) {
                     $hiddenText = "<a href='#' id='method_toc_unhide'>{$hiddenCount} more method" . ($hiddenCount > 1 ? "s" : "") . "</a>";
                     $hiddenText .= "<a href='#' id='method_toc_hide' class='hidden'>show less methods</a>";
                 }
                 pq(".firstHeading")->after("<p id='method_toc'>{$anchorList}{$hiddenText}</p>");
             } else {
                 //only 1 method
                 if ($ads) {
                     wikihowAds::setAltMethods(false);
                 }
                 if ($set) {
                     try {
                         pq($set)->wrapAll("<div id='{$sectionName}' class='section_text'></div>");
                     } catch (Exception $e) {
                     }
                 }
                 $overallSet = array();
                 $overallSet[] = $node;
                 foreach (pq("div#{$sectionName}:first") as $temp) {
                     $overallSet[] = $temp;
                 }
                 try {
                     pq($overallSet)->wrapAll("<div class='section steps {$sticky}'></div>");
                 } catch (Exception $e) {
                 }
             }
         } else {
             //not a steps section
             if ($set) {
                 $sec_id = @$opts['list-page'] ? '' : 'id="' . $sectionName . '"';
                 try {
                     $new_set = pq($set)->wrapAll("<div {$sec_id} class='section_text'></div>");
                 } catch (Exception $e) {
                 }
             }
             $overallSet = array();
             $overallSet[] = $node;
             foreach (pq("div#{$sectionName}:first") as $temp) {
                 $overallSet[] = $temp;
             }
             try {
                 pq($overallSet)->wrapAll("<div class='section {$sectionName} {$sticky}'></div>");
             } catch (Exception $e) {
             }
             if (@$opts['list-page']) {
                 //gotta pull those dangling divs into the same space as the h2
                 pq($overallSet)->parent()->append(pq($new_set));
             }
             // commenting this out because it's causing the following error:
             // "Couldn't add newnode as the previous sibling of refnode"
             // // format edit links for non-steps sections
             // // pq('span', $node)->prepend(pq('a.edit', $node));
         }
     }
     //add a clear to the end of each section_text to make sure
     //images don't bleed across the bottom
     pq(".section_text")->append("<div class='clearall'></div>");
     // Add checkboxes to Ingredients and 'Things You Need' sections, but only to the top-most li
     $lis = pq('#ingredients > ul > li, #thingsyoullneed > ul > li');
     foreach ($lis as $li) {
         $id = md5(pq($li)->html() . mt_rand(1, 100));
         pq($li)->html("<input id='item_{$id}' class='css-checkbox' type='checkbox'/><label for='item_{$id}' name='item_{$id}_lbl' class='css-checkbox-label'></label><div class='checkbox-text'>" . pq($li)->html() . '</div>');
     }
     // Move templates above article body contents and style appropriately
     foreach (pq('.template_top') as $template) {
         pq($template)->addClass('sidebox');
         if (pq($template)->parent()->hasClass('tmp_li')) {
             pq($template)->addClass('tmp_li');
         }
         if ($wgUser->isAnon()) {
             pq($template)->addClass('notice_bgcolor_lo');
         } else {
             pq($template)->addClass('notice_bgcolor_important');
         }
     }
     // put templates after the intro div
     pq('.template_top')->insertAfter('#intro');
     //now put the step numbers in
     foreach (pq("div.steps .section_text > ol") as $list) {
         pq($list)->addClass("steps_list_2");
         $stepNum = 1;
         foreach (pq($list)->children() as $step) {
             $boldStep = WikihowArticleHTML::boldFirstSentence(pq($step)->html());
             pq($step)->html($boldStep);
             pq($step)->prepend("<div class='step_num'>{$stepNum}</div>");
             pq($step)->append("<div class='clearall'></div>");
             $stepNum++;
         }
     }
     foreach (pq(".steps:last .steps_list_2")->children(":last-child") as $step) {
         pq($step)->addClass("final_li");
     }
     //move each of the large images to the top
     foreach (pq(".steps_list_2 li .mwimg.largeimage") as $image) {
         //delete any previous <br>
         foreach (pq($image)->prevAll() as $node) {
             if (pq($node)->is("br")) {
                 pq($node)->remove();
             } else {
                 break;
             }
         }
         //first handle the special case where the image
         //ends up inside the bold tag by accident
         if (pq($image)->parent()->is("b")) {
             pq($image)->insertBefore(pq($image)->parent());
         }
         if (pq($image)->parent()->parent()->is(".steps_list_2")) {
             pq($image)->parent()->prepend($image);
         }
     }
     //move each of the large images to the top
     foreach (pq(".steps_list_2 li .whvid_cont") as $vid) {
         //delete any previous <br>
         foreach (pq($vid)->prevAll() as $node) {
             if (pq($node)->is("br")) {
                 pq($node)->remove();
             } else {
                 break;
             }
         }
         if (pq($vid)->parent()->parent()->is(".steps_list_2")) {
             pq($vid)->parent()->prepend($vid);
         }
     }
     //if there's a related articles section, make it have images
     $relatedSection = pq("#relatedwikihows");
     if ($relatedSection) {
         foreach (pq("li a", $relatedSection) as $related) {
             $titleText = pq($related)->attr("title");
             $title = Title::newFromText($titleText);
             if ($title) {
                 $image = $skin->getArticleThumb($title, 127, 120);
                 pq($relatedSection)->prepend($image);
             }
             pq($related)->remove();
         }
         pq("ul", $relatedSection)->remove();
         pq($relatedSection)->append("<div class='clearall'></div>");
     }
     //remove all images in the intro that aren't
     //marked with the class "introimage"
     pq("#intro .mwimg:not(.introimage)")->remove();
     //let's mark all the <p> tags that aren't inside a step.
     //they need special padding
     foreach (pq(".section.steps p") as $p) {
         if (pq($p)->parents(".steps_list_2")->count() == 0 && pq($p)->children(".anchor")->count() == 0) {
             pq($p)->addClass("lone_p");
         }
     }
     // Add alt method adder cta
     if (class_exists("AltMethodAdder") && $wgTitle && $wgUser && $wgUser->isAnon()) {
         $cta = AltMethodAdder::getCTA($wgTitle);
         if (!is_null($cta)) {
             pq("div.steps:last")->after($cta);
         }
     }
     //add line breaks between the p tags
     foreach (pq("p") as $paragraph) {
         $sibling = pq($paragraph)->next();
         if (!pq($sibling)->is("p")) {
             continue;
         }
         if (pq($sibling)->children(":first")->hasClass("anchor")) {
             continue;
         }
         $id = pq($paragraph)->attr("id");
         if ($id == "originators" || $id == "method_toc") {
             continue;
         }
         pq($paragraph)->after("<br />");
     }
     if ($ads) {
         pq("#intro")->append(wikihowAds::getAdUnitPlaceholder('intro'));
         pq(".steps_list_2:first li:first")->append(wikihowAds::getAdUnitPlaceholder(0));
         pq(".final_li")->append(wikihowAds::getAdUnitPlaceholder(1));
         $tipsClass = mb_strtolower(wfMsg("tips"));
         //grabs the tips section by name, but internationalized
         pq(".{$tipsClass} ul:last")->after(wikihowAds::getAdUnitPlaceholder('2a'));
     }
     return $doc->htmlOuter();
 }