Index: trunk/extensions/PatchOutputMobile/views/notices/_donate.html.php |
— | — | @@ -0,0 +1,9 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +$donate_html = <<<EOT |
| 5 | + <div class='mwm-message mwm-notice'> |
| 6 | + Text WIKI to <a href="sms:25383">25383</a> to donate $10. |
| 7 | + <br /> |
| 8 | + <a href='http://wikimediafoundation.org/wiki/Mobile_Giving'>Msg & Data Rates May Apply</a> |
| 9 | + </div> |
| 10 | +EOT; |
Index: trunk/extensions/PatchOutputMobile/views/layout/_footmenu_default.html.php |
— | — | @@ -0,0 +1,16 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +$footer_html = <<<EOD |
| 5 | + <div id='footer'> |
| 6 | + <div class='nav' id='footmenu'> |
| 7 | + <div class='mwm-notice'> |
| 8 | + <a href="http://en.wikipedia.org/w/mobileRedirect.php?to=">View this page on regular Wikipedia</a> |
| 9 | + <div id="perm"> |
| 10 | + <a href="https://www.mediawiki.org/disable/???">Permanently disable mobile site</a> |
| 11 | + </div> |
| 12 | + </div> |
| 13 | + </div> |
| 14 | + <div id='copyright'>Text is available under the <a href='http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License'>Creative Commons Attribution/Share-Alike License</a>; additional terms may apply. See <a href='http://wikimediafoundation.org/wiki/Terms_of_Use'>Terms of Use</a> for details. Wikipedia is a registered trademark of the <a href='http://www.wikimediafoundation.org/'>Wikimedia Foundation, Inc.</a>, a non-profit organization.</div> |
| 15 | + </div> |
| 16 | + |
| 17 | +EOD; |
Index: trunk/extensions/PatchOutputMobile/views/layout/application.html.php |
— | — | @@ -0,0 +1,33 @@ |
| 2 | +<?php |
| 3 | +$application_html = <<<EOT |
| 4 | +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| 5 | +<html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'> |
| 6 | + <head> |
| 7 | + <title>{$title}</title> |
| 8 | + <meta http-equiv="content-type" content="text/html; charset=utf-8" /> |
| 9 | + <link href='http://en.m.wikipedia.org/stylesheets/webkit.css' media='all' rel='Stylesheet' type='text/css' /> |
| 10 | + <meta name="ROBOTS" content="NOINDEX, NOFOLLOW" /> |
| 11 | + <meta name = "viewport" content = "width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" /> |
| 12 | + <link rel="apple-touch-icon" href="http://en.m.wikipedia.org/apple-touch-icon.png" /> |
| 13 | + <script type='text/javascript'> |
| 14 | + //<![CDATA[ |
| 15 | + var title = "{$title}"; |
| 16 | + var server = "http://en.wikipedia.org"; |
| 17 | + function shouldCache() { |
| 18 | + return true; |
| 19 | + } |
| 20 | + //]]> |
| 21 | + </script> |
| 22 | + <script type="text/javascript" language="javascript" SRC="http://en.m.wikipedia.org/javascripts/jquery.js"></script> |
| 23 | + <script type="text/javascript" language="javascript" SRC="http://en.m.wikipedia.org/javascripts/application.js"></script> |
| 24 | + </head> |
| 25 | + <body> |
| 26 | + {$search_webkit_html} |
| 27 | + <div class='show' id='content_wrapper'> |
| 28 | + {$donate_html} |
| 29 | + {$content_html} |
| 30 | + </div> |
| 31 | + {$footer_html} |
| 32 | + </body> |
| 33 | +</html> |
| 34 | +EOT; |
Index: trunk/extensions/PatchOutputMobile/views/layout/_search_webkit.html.php |
— | — | @@ -0,0 +1,41 @@ |
| 2 | +<?php |
| 3 | +/* |
| 4 | +$search_webkit_html = <<<EOD |
| 5 | +<div id='header'> |
| 6 | + <div id='searchbox'> |
| 7 | + <img alt='W logo' id='logo' src='http://en.m.wikipedia.org/images/w.gif' /> |
| 8 | + <form action='/wiki' class='search_bar' method='get'> |
| 9 | + <input id='searchField' name='search' size='27' type='search' value='' /> |
| 10 | + <div id='clearButton'></div> |
| 11 | + <button id='goButton' type='submit'></button> |
| 12 | + </form> |
| 13 | + </div> |
| 14 | + <div class='nav' id='nav'> |
| 15 | + <form method="get" action="/"><button type="submit" id="homeButton">Home</button></form> |
| 16 | + <form method="get" action="/wiki/::Random"><button type="submit" id="randomButton">Random</button></form> |
| 17 | + </div> |
| 18 | +</div> |
| 19 | +EOD; |
| 20 | +*/ |
| 21 | + |
| 22 | +$search_field = (!empty($_GET['search'])) ? $_GET['search'] : ''; |
| 23 | + |
| 24 | +$search_webkit_html = <<<EOD |
| 25 | +<div id='header'> |
| 26 | + <div id='searchbox'> |
| 27 | + <img alt='W logo' id='logo' src='http://en.m.wikipedia.org/images/w.gif' /> |
| 28 | + <form action='/index.php' class='search_bar' method='get'> |
| 29 | + <input type="hidden" value="Special:Search" name="title" /> |
| 30 | + <input type="hidden" value="Search" name="fulltext" /> |
| 31 | + <input type="hidden" value="0" name="redirs" /> |
| 32 | + <input id='searchField' name='search' size='27' type='search' value='{$search_field}' /> |
| 33 | + <div id='clearButton'></div> |
| 34 | + <button id='goButton' type='submit'></button> |
| 35 | + </form> |
| 36 | + </div> |
| 37 | + <div class='nav' id='nav'> |
| 38 | + <form method="get" action="/"><button type="submit" id="homeButton">Home</button></form> |
| 39 | + <form method="get" action="/wiki/::Random"><button type="submit" id="randomButton">Random</button></form> |
| 40 | + </div> |
| 41 | +</div> |
| 42 | +EOD; |
Index: trunk/extensions/PatchOutputMobile/PatchOutputMobile.php |
— | — | @@ -0,0 +1,252 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +# Needs to be called within MediaWiki; not standalone |
| 5 | +if ( !defined('MEDIAWIKI') ) { |
| 6 | + echo("This is an extension to the MediaWiki package and cannot be run standalone.\n" ); |
| 7 | + die(-1); |
| 8 | +} |
| 9 | + |
| 10 | +# Define the extension; allows us make sure the extension is used correctly |
| 11 | +DEFINE( 'PATCHOUTPUTMOBILE', 'PatchOutputMobile' ); |
| 12 | + |
| 13 | +$wgExtensionCredits['other'][] = array( |
| 14 | + 'name' => 'PatchOutputMobile', |
| 15 | + 'version' => ExtPatchOutputMobile::VERSION, |
| 16 | + 'author' => '[http://www.mediawiki.org/wiki/User:Preilly Preilly]', |
| 17 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:PatchOutputMobile', |
| 18 | + 'description' => 'patch html output for mobile' |
| 19 | +); |
| 20 | + |
| 21 | +// default settings |
| 22 | +ExtPatchOutputMobile::$mTable = array( |
| 23 | + 'accessed' => 'like viewed and stuff', |
| 24 | + 'information' => 'more stuff about it', |
| 25 | + 'Main Page' => 'First Page Dude'); |
| 26 | + |
| 27 | +$wgExtPatchOutputMobile = new ExtPatchOutputMobile(); |
| 28 | + |
| 29 | +$wgHooks['OutputPageBeforeHTML'][] = array(&$wgExtPatchOutputMobile, |
| 30 | + 'onOutputPageBeforeHTML'); |
| 31 | + |
| 32 | +class ExtPatchOutputMobile { |
| 33 | + const VERSION = '0.2'; |
| 34 | + |
| 35 | + public static $mTable; |
| 36 | + |
| 37 | + private $doc; |
| 38 | + |
| 39 | + public $items_to_remove = array("#contentSub", #redirection notice |
| 40 | + "div.messagebox", #cleanup data |
| 41 | + "#siteNotice", #site notice |
| 42 | + "#siteSub", #"From Wikipedia..." |
| 43 | + "#jump-to-nav", #jump-to-nav |
| 44 | + "div.editsection", #edit blocks |
| 45 | + "div.infobox", # Infoboxes in the article |
| 46 | + "table.toc", #table of contents |
| 47 | + "#catlinks", #category links |
| 48 | + "div.stub", #stub warnings |
| 49 | + "table.metadata", #ugly metadata |
| 50 | + "form", |
| 51 | + "div.sister-project", |
| 52 | + "script", |
| 53 | + "div.magnify", #stupid magnify thing |
| 54 | + ".editsection", |
| 55 | + "span.t", |
| 56 | + 'sup[style*="help"]', |
| 57 | + ".portal", |
| 58 | + "#protected-icon", |
| 59 | + ".printfooter", |
| 60 | + ".boilerplate", |
| 61 | + "#id-articulo-destacado", |
| 62 | + "#coordinates", |
| 63 | + "#top", |
| 64 | + ".hiddenStructure", |
| 65 | + ".noprint", |
| 66 | + ".medialist", |
| 67 | + ".mw-search-createlink"); |
| 68 | + |
| 69 | + public function onOutputPageBeforeHTML(&$out, &$text) { |
| 70 | + ob_start(array(&$this, 'parse')); |
| 71 | + return true; |
| 72 | + } |
| 73 | + |
| 74 | + public function javascriptize($s) { |
| 75 | + $s = preg_replace_callback('/<h2(.*)<span class="mw-headline" [^>]*>(.+)<\/span>\w*<\/h2>/', function ($matches) { |
| 76 | + static $headings = 0; |
| 77 | + $show = "Show"; |
| 78 | + $hide = "Hide"; |
| 79 | + $back_to_top = "Jump Back A Section"; |
| 80 | + ++$headings; |
| 81 | + // Back to top link |
| 82 | + $base = "<div class='section_anchors' id='anchor_" . intval($headings - 1) . "'><a href='#section_" . intval($headings - 1) . "' class='back_to_top'>↑ {$back_to_top}</a></div>"; |
| 83 | + // generate the HTML we are going to inject |
| 84 | + $buttons = "<button class='section_heading show' section_id='{$headings}'>{$show}</button><button class='section_heading hide' section_id='{$headings}'>{$hide}</button>"; |
| 85 | + $base .= "<h2 class='section_heading' id='section_{$headings}'{$matches[1]}{$buttons} <span>{$matches[2]}</span></h2><div class='content_block' id='content_{$headings}'>"; |
| 86 | + |
| 87 | + if ($headings > 1) { |
| 88 | + // Close it up here |
| 89 | + $base = "</div>" . $base; |
| 90 | + } |
| 91 | + |
| 92 | + $GLOBALS['headings'] = $headings; |
| 93 | + |
| 94 | + return $base; |
| 95 | + }, $s); |
| 96 | + |
| 97 | + // if we had any, make sure to close the whole thing! |
| 98 | + if (isset($GLOBALS['headings']) && $GLOBALS['headings'] > 0) { |
| 99 | + $s = str_replace('<div class="visualClear">', '</div><div class="visualClear">', $s); |
| 100 | + } |
| 101 | + |
| 102 | + return $s; |
| 103 | + } |
| 104 | + |
| 105 | + public function parse($s) { |
| 106 | + // foreach(self::$mTable as $from => $to) { |
| 107 | + // $s =& str_replace( $from, $to, $s ); |
| 108 | + // } |
| 109 | + |
| 110 | + return $this->DOMParse($s); |
| 111 | + } |
| 112 | + |
| 113 | + private function parse_items_to_remove() { |
| 114 | + $item_to_remove_records = array(); |
| 115 | + |
| 116 | + foreach ($this->items_to_remove as $item_to_remove) { |
| 117 | + $type = ''; |
| 118 | + $raw_name = ''; |
| 119 | + CSS_detection::detect_id_css_or_tag($item_to_remove, $type, $raw_name); |
| 120 | + $item_to_remove_records[$type][] = $raw_name; |
| 121 | + } |
| 122 | + |
| 123 | + return $item_to_remove_records; |
| 124 | + } |
| 125 | + |
| 126 | + public function DOMParse($html) { |
| 127 | + |
| 128 | + libxml_use_internal_errors(true); |
| 129 | + $this->doc = DOMDocument::loadHTML($html); |
| 130 | + libxml_use_internal_errors(false); |
| 131 | + $this->doc->preserveWhiteSpace = false; |
| 132 | + $this->doc->strictErrorChecking = false; |
| 133 | + |
| 134 | + $item_to_remove_records = $this->parse_items_to_remove(); |
| 135 | + |
| 136 | + //Tags |
| 137 | + |
| 138 | + //You can't remove DOMNodes from a DOMNodeList as you're iterating over them |
| 139 | + // in a foreach loop. It will seemingly leave the internal iterator on the foreach out of wack |
| 140 | + // and results will be quite strange. Though, making a queue of items to remove |
| 141 | + // seems to work. For example: |
| 142 | + |
| 143 | + $title_node = $this->doc->getElementsByTagName('title'); |
| 144 | + |
| 145 | + if ($title_node->length > 0) { |
| 146 | + $title = $title_node->item(0)->nodeValue; |
| 147 | + } |
| 148 | + |
| 149 | + $domElemsToRemove = array(); |
| 150 | + foreach ($item_to_remove_records['TAG'] as $tag_to_remove) { |
| 151 | + $tag_to_remove_nodes = $this->doc->getElementsByTagName($tag_to_remove); |
| 152 | + |
| 153 | + foreach($tag_to_remove_nodes as $tag_to_remove_node) { |
| 154 | + if ($tag_to_remove_node) { |
| 155 | + $domElemsToRemove[] = $tag_to_remove_node; |
| 156 | + } |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + foreach($domElemsToRemove as $domElement){ |
| 161 | + $domElement->parentNode->removeChild($domElement); |
| 162 | + } |
| 163 | + |
| 164 | + //Elements with named Ids |
| 165 | + foreach ($item_to_remove_records['ID'] as $item_to_remove) { |
| 166 | + $item_to_remove_node = $this->doc->getElementById($item_to_remove); |
| 167 | + if ($item_to_remove_node) { |
| 168 | + $removed_item_to_remove = $item_to_remove_node->parentNode->removeChild($item_to_remove_node); |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + // CSS Classes |
| 173 | + $xpath = new DOMXpath($this->doc); |
| 174 | + foreach ($item_to_remove_records['CLASS'] as $class_to_remove) { |
| 175 | + |
| 176 | + $elements = $xpath->query('//*[@class="'.$class_to_remove.'"]'); |
| 177 | + |
| 178 | + foreach($elements as $element) { |
| 179 | + $removed_element = $element->parentNode->removeChild($element); |
| 180 | + } |
| 181 | + } |
| 182 | + |
| 183 | + //Tags with CSS Classes |
| 184 | + foreach ($item_to_remove_records['TAG_CLASS'] as $class_to_remove) { |
| 185 | + |
| 186 | + $parts = explode(".", $class_to_remove); |
| 187 | + |
| 188 | + $elements = $xpath->query('//'.$parts[0].'[@class="'.$parts[1].'"]'); |
| 189 | + |
| 190 | + foreach($elements as $element) { |
| 191 | + $removed_element = $element->parentNode->removeChild($element); |
| 192 | + } |
| 193 | + } |
| 194 | + |
| 195 | + $content = $this->doc->getElementById('content'); |
| 196 | + |
| 197 | + $content_html = $this->doc->saveXML($content, LIBXML_NOEMPTYTAG); |
| 198 | + |
| 199 | + if (empty($title)) { |
| 200 | + $title = 'Wikipedia'; |
| 201 | + } |
| 202 | + |
| 203 | + require('views/notices/_donate.html.php'); |
| 204 | + require('views/layout/_search_webkit.html.php'); |
| 205 | + require('views/layout/_footmenu_default.html.php'); |
| 206 | + require('views/layout/application.html.php'); |
| 207 | + |
| 208 | + return (strlen($content_html) > 4000) ? $this->javascriptize($application_html) : $application_html; //$content_html; |
| 209 | + } |
| 210 | +} |
| 211 | + |
| 212 | +class CSS_detection { |
| 213 | + |
| 214 | + public static function detect_id_css_or_tag($snippet, &$type, &$raw_name) { |
| 215 | + $output = ''; |
| 216 | + |
| 217 | + if (strpos($snippet, '.') === 0) { |
| 218 | + $output = 'Class found: '; |
| 219 | + $type = 'CLASS'; |
| 220 | + $raw_name = substr($snippet, 1); |
| 221 | + } |
| 222 | + |
| 223 | + if (strpos($snippet, '#') === 0) { |
| 224 | + $output = 'ID found: '; |
| 225 | + $type = 'ID'; |
| 226 | + $raw_name = substr($snippet, 1); |
| 227 | + } |
| 228 | + |
| 229 | + if (strpos($snippet, '.') !== 0 && |
| 230 | + strpos($snippet, '.') !== false) { |
| 231 | + $output = 'Tag with Class found: '; |
| 232 | + $type = 'TAG_CLASS'; |
| 233 | + $raw_name = $snippet; |
| 234 | + } |
| 235 | + |
| 236 | + if (strpos($snippet, '.') === false && |
| 237 | + strpos($snippet, '#') === false && |
| 238 | + strpos($snippet, '[') === false && |
| 239 | + strpos($snippet, ']') === false) { |
| 240 | + $output = 'Tag found: '; |
| 241 | + $type = 'TAG'; |
| 242 | + $raw_name = $snippet; |
| 243 | + } |
| 244 | + |
| 245 | + if (empty($output)) { |
| 246 | + $output = 'Unknown HTML snippet found: '; |
| 247 | + $type = 'UNKNOWN'; |
| 248 | + $raw_name = $snippet; |
| 249 | + } |
| 250 | + |
| 251 | + return $output; |
| 252 | + } |
| 253 | +} |
\ No newline at end of file |