|
|
| Line 1: |
Line 1: |
| − | <?php | + | <html> |
| − | /**
| + | <script> |
| − | * TreeAndMenu extension - Adds #tree, #menu and #star parser functions which contain bullet-lists to be rendered as collapsible treeview's or dropdown menus
| + | $('.firstHeading').animate( |
| − | *
| + | { 'font-size': '50px' }, |
| − | * See http://www.mediawiki.org/wiki/Extension:TreeAndMenu for installation and usage details
| + | { duration: 100, easing: 'linear', step: function(now,fx) { alert(now); } } |
| − | * See http://www.organicdesign.co.nz/Extension_talk:TreeAndMenu.php for development notes and disucssion
| |
| − | *
| |
| − | * @file
| |
| − | * @ingroup Extensions
| |
| − | * @author Aran Dunkley [http://www.organicdesign.co.nz/nad User:Nad]
| |
| − | * @copyright © 2007 Aran Dunkley
| |
| − | * @licence GNU General Public Licence 2.0 or later
| |
| − | */
| |
| − | | |
| − | if( !defined( 'MEDIAWIKI' ) ) die( 'Not an entry point.' );
| |
| − | | |
| − | define( 'TREEANDMENU_VERSION','2.0.1, 2012-01-28' );
| |
| − | | |
| − | // Set any unset images to default titles
| |
| − | if( !isset( $wgTreeViewImages ) || !is_array( $wgTreeViewImages ) ) $wgTreeViewImages = array();
| |
| − | | |
| − | $wgTreeMagic = "tree"; // the parser-function name for trees
| |
| − | $wgMenuMagic = "menu"; // the parser-function name for dropdown menus
| |
| − | $wgStarMagic = "star"; // the parser-function name for radial star menus
| |
| − | $wgTreeViewShowLines = false; // whether to render the dotted lines joining nodes
| |
| − | $wgExtensionFunctions[] = 'wfSetupTreeAndMenu';
| |
| − | $wgHooks['LanguageGetMagic'][] = 'wfTreeAndMenuLanguageGetMagic';
| |
| − | | |
| − | $wgExtensionCredits['parserhook'][] = array(
| |
| − | 'path' => __FILE__,
| |
| − | 'name' => 'TreeAndMenu',
| |
| − | 'author' => array( '[http://www.organicdesign.co.nz/User:Nad Nad]', '[http://www.organicdesign.co.nz/User:Sven Sven]' ),
| |
| − | 'url' => 'http://www.mediawiki.org/wiki/Extension:TreeAndMenu',
| |
| − | 'description' => 'Adds #tree, #menu and #star parser functions which contain bullet-lists to be rendered as collapsible treeview\'s or dropdown menus.
| |
| − | The treeview\'s use the [http://www.destroydrop.com/javascripts/tree dTree] JavaScript tree menu,
| |
| − | and the dropdown menu\'s use [http://www.htmldog.com/articles/suckerfish/dropdowns/ Son of Suckerfish]',
| |
| − | 'version' => TREEANDMENU_VERSION
| |
| | ); | | ); |
| − | | + | </script> |
| − | class TreeAndMenu {
| + | </html> |
| − | | |
| − | var $version = TREEANDMENU_VERSION;
| |
| − | var $uniq = ''; // uniq part of all tree id's
| |
| − | var $uniqname = 'tam'; // input name for uniqid
| |
| − | var $id = ''; // id for specific tree
| |
| − | var $baseDir = ''; // internal absolute path to treeview directory
| |
| − | var $baseUrl = ''; // external URL to treeview directory (relative to domain)
| |
| − | var $images = ''; // internal JS to update dTree images
| |
| − | var $useLines = true; // internal variable determining whether to render connector lines
| |
| − | var $args = array(); // args for each tree
| |
| − | | |
| − | /**
| |
| − | * Constructor
| |
| − | */
| |
| − | function __construct() {
| |
| − | global $wgOut, $wgHooks, $wgParser, $wgJsMimeType, $wgExtensionAssetsPath, $wgResourceModules,
| |
| − | $wgTreeMagic, $wgMenuMagic, $wgStarMagic, $wgTreeViewImages, $wgTreeViewShowLines;
| |
| − | | |
| − | // Add hooks
| |
| − | $wgParser->setFunctionHook( $wgTreeMagic, array( $this,'expandTree' ) );
| |
| − | $wgParser->setFunctionHook( $wgMenuMagic, array( $this,'expandMenu' ) );
| |
| − | $wgParser->setFunctionHook( $wgStarMagic, array( $this,'expandStar' ) );
| |
| − | $wgHooks['ParserAfterTidy'][] = array( $this, 'renderTreeAndMenu' );
| |
| − | | |
| − | // Update general tree paths and properties
| |
| − | $this->baseDir = dirname( __FILE__ );
| |
| − | $this->baseUrl = $wgExtensionAssetsPath . '/' . basename( dirname( __FILE__ ) );
| |
| − | $this->useLines = $wgTreeViewShowLines ? 'true' : 'false';
| |
| − | $this->uniq = uniqid( $this->uniqname );
| |
| − | | |
| − | // Convert image titles to file paths and store as JS to update dTree
| |
| − | foreach( $wgTreeViewImages as $k => $v ) {
| |
| − | $image = wfLocalFile( $v );
| |
| − | $v = ( is_object( $image ) && $image->exists() ) ? $image->getURL() : $wgTreeViewImages[$k];
| |
| − | $this->images .= "tree.icon['$k'] = '$v';";
| |
| − | }
| |
| − | }
| |
| − | | |
| − | /**
| |
| − | * Expand #tree parser-functions
| |
| − | */
| |
| − | public function expandTree() {
| |
| − | $args = func_get_args();
| |
| − | return $this->expandTreeAndMenu( 'tree', $args );
| |
| − | }
| |
| − | | |
| − | /**
| |
| − | * Expand #menu parser-functions
| |
| − | */
| |
| − | public function expandMenu() {
| |
| − | $args = func_get_args();
| |
| − | return $this->expandTreeAndMenu( 'menu', $args );
| |
| − | }
| |
| − | | |
| − | /**
| |
| − | * Expand #star parser-functions
| |
| − | */
| |
| − | public function expandStar() {
| |
| − | $args = func_get_args();
| |
| − | return 'not done yet';
| |
| − | }
| |
| − | | |
| − | /**
| |
| − | * Expand either kind of parser-function (reformats tree rows for matching later) and store args
| |
| − | */
| |
| − | private function expandTreeAndMenu( $magic, $args ) {
| |
| − | $parser = array_shift( $args );
| |
| − | | |
| − | // Store args for this tree for later use
| |
| − | $text = '';
| |
| − | foreach( $args as $arg ) if ( preg_match( '/^(\\w+?)\\s*=\\s*(.+)$/s', $arg, $m ) ) $args[$m[1]] = $m[2]; else $text = $arg;
| |
| − | | |
| − | // If root, parse as wikitext
| |
| − | if( isset( $args['root'] ) ) {
| |
| − | $p = clone $parser;
| |
| − | $o = clone $parser->mOptions;
| |
| − | $o->mTidy = $o->mEnableLimitReport = false;
| |
| − | $html = $p->parse( $args['root'], $parser->mTitle, $o, false, true )->getText();
| |
| − | $args['root'] = addslashes( $html );
| |
| − | }
| |
| − | | |
| − | // Create a unique id for this tree or use id supplied in args and store args wrt id
| |
| − | $this->id = isset($args['id']) ? $args['id'] : uniqid( '' );
| |
| − | $args['type'] = $magic;
| |
| − | $this->args[$this->id] = $args;
| |
| − | | |
| − | // Reformat tree rows for matching in ParserAfterStrip
| |
| − | $text = preg_replace( '/(?<=\\*)\\s*\\[\\[Image:(.+?)\\]\\]/', "{$this->uniq}3$1{$this->uniq}4", $text );
| |
| − | $text = preg_replace_callback( '/^(\\*+)(.*?)$/m', array( $this, 'formatRow' ), $text );
| |
| − | | |
| − | return $text;
| |
| − | }
| |
| − | | |
| − | | |
| − | /**
| |
| − | * Reformat tree bullet structure recording row, depth and id in a format which is not altered by wiki-parsing
| |
| − | * - format is: 1{uniq}-{id}-{depth}-{item}-2{uniq}
| |
| − | * - sequences of this format will be matched in ParserAfterTidy and converted into dTree JavaScript
| |
| − | * - NOTE: we can't encode a unique row-id because if the same tree instranscluded twice a cached version
| |
| − | * may be used (even if parser-cache disabled) this also means that tree id's may be repeated
| |
| − | */
| |
| − | private function formatRow( $m ) {
| |
| − | return "\x7f1{$this->uniq}\x7f{$this->id}\x7f" . ( strlen( $m[1] )-1 ) . "\x7f$m[2]\x7f2{$this->uniq}";
| |
| − | }
| |
| − | | |
| − | | |
| − | /**
| |
| − | * Called after parser has finished (ParserAfterTidy) so all transcluded parts can be assembled into final trees
| |
| − | */
| |
| − | public function renderTreeAndMenu( &$parser, &$text ) {
| |
| − | global $wgJsMimeType, $wgOut;
| |
| − | $u = $this->uniq;
| |
| − | | |
| − | // Determine which trees are sub trees
| |
| − | // - there should be a more robust way to do this,
| |
| − | // it's just based on the fact that all sub-tree's have a minus preceding their row data
| |
| − | if( !preg_match_all( "/\x7f\x7f1$u\x7f(.+?)\x7f/", $text, $subs ) ) $subs = array( 1 => array() );
| |
| − | | |
| − | // Extract all the formatted tree rows in the page and if any, replace with dTree JavaScript
| |
| − | if( preg_match_all( "/\x7f1$u\x7f(.+?)\x7f([0-9]+)\x7f({$u}3(.+?){$u}4)?(.*?)(?=\x7f[12]$u)/", $text, $matches, PREG_SET_ORDER ) ) {
| |
| − | | |
| − | // PASS-1: build $rows array containing depth, and tree start/end information
| |
| − | $rows = array();
| |
| − | $depths = array( '' => 0 ); // depth of each tree root
| |
| − | $rootId = ''; // the id of the current root-tree (used as tree id in PASS2)
| |
| − | $lastId = '';
| |
| − | $lastDepth = 0;
| |
| − | foreach( $matches as $match ) {
| |
| − | list( , $id, $depth,, $icon, $item ) = $match;
| |
| − | $start = false;
| |
| − | if( $id != $lastId ) {
| |
| − | if( !isset( $depths[$id] ) ) $depths[$id] = $depths[$lastId]+$lastDepth;
| |
| − | if( $start = $rootId != $id && !in_array( $id, $subs[1] ) ) $depths[$rootId = $id] = 0;
| |
| − | }
| |
| − | if( $item ) $rows[] = array( $rootId, $depth + $depths[$id], $icon, addslashes( $item ), $start );
| |
| − | $lastId = $id;
| |
| − | $lastDepth = $depth;
| |
| − | }
| |
| − | | |
| − | // PASS-2: build the JavaScript and replace into $text
| |
| − | $parents = array(); // parent node for each depth
| |
| − | $parity = array(); // keep track of odd/even rows for each depth
| |
| − | $node = 0;
| |
| − | $last = -1;
| |
| − | $nodes = '';
| |
| − | $opennodes = array();
| |
| − | foreach( $rows as $i => $info ) {
| |
| − | $node++;
| |
| − | list( $id, $depth, $icon, $item, $start ) = $info;
| |
| − | $objid = $this->uniqname . preg_replace( '/\W/', '', $id );
| |
| − | $args = $this->args[$id];
| |
| − | $type = $args['type'];
| |
| − | $end = $i == count( $rows )-1 || $rows[$i+1][4];
| |
| − | if( !isset( $args['root'] ) ) $args['root'] = ''; // tmp - need to handle rootless trees
| |
| − | $openlevels = isset( $args['openlevels'] ) ? $args['openlevels']+1 : 0;
| |
| − | if( $start ) $node = 1;
| |
| − | | |
| − | // Append node script for this row
| |
| − | if( $depth > $last ) $parents[$depth] = $node-1;
| |
| − | $parent = $parents[$depth];
| |
| − | if( $type == 'tree' ) {
| |
| − | $nodes .= "$objid.add($node, $parent, '$item');\n";
| |
| − | if( $depth > 0 && $openlevels > $depth ) $opennodes[$parent] = true;
| |
| − | }
| |
| − | else {
| |
| − | if( !$start ) {
| |
| − | if( $depth < $last ) $nodes .= str_repeat( '</ul></li>', $last - $depth );
| |
| − | elseif( $depth > $last ) $nodes .= "\n<ul>";
| |
| − | }
| |
| − | $parity[$depth] = isset( $parity[$depth] ) ? $parity[$depth]^1 : 0;
| |
| − | $class = $parity[$depth] ? 'odd' : 'even';
| |
| − | $nodes .= "<li class=\"$class\">$item";
| |
| − | if( $depth >= $rows[$node][1] ) $nodes .= "</li>\n";
| |
| − | }
| |
| − | $last = $depth;
| |
| − | | |
| − | // Last row of current root, surround nodes dtree or menu script and div etc
| |
| − | if( $end ) {
| |
| − | $class = isset( $args['class'] ) ? $args['class'] : "d$type";
| |
| − | if( $type == 'tree' ) {
| |
| − | | |
| − | static $dtree = false;
| |
| − | if( !$dtree ) {
| |
| − | $wgOut->addScriptFile( $this->baseUrl . '/dtree.js' );
| |
| − | $dtree = true;
| |
| − | }
| |
| − | | |
| − | // Finalise a tree
| |
| − | $add = isset( $args['root'] ) ? "tree.add(0,-1,'".$args['root']."');" : '';
| |
| − | $top = $bottom = $root = $opennodesjs = '';
| |
| − | foreach( array_keys( $opennodes ) as $i ) $opennodesjs .= "$objid.o($i);";
| |
| − | foreach( $args as $arg => $pos )
| |
| − | if( ( $pos == 'top' || $pos == 'bottom' || $pos == 'root' ) && ( $arg == 'open' || $arg == 'close' ) )
| |
| − | $$pos .= "<a href=\"javascript: $objid.{$arg}All();\"> {$arg} all</a> ";
| |
| − | if( $top ) $top = "<p> $top</p>";
| |
| − | if( $bottom ) $bottom = "<p> $bottom</p>";
| |
| − | | |
| − | // Define the script to build this tree
| |
| − | $script = "// TreeAndMenu-{$this->version}\ntree = new dTree('$objid');
| |
| − | for (i in tree.icon) tree.icon[i] = '{$this->baseUrl}/'+tree.icon[i];{$this->images}
| |
| − | tree.config.useLines = {$this->useLines};
| |
| − | $add
| |
| − | $objid = tree;
| |
| − | $nodes
| |
| − | document.getElementById('$id').innerHTML = $objid.toString();
| |
| − | $opennodesjs
| |
| − | for(i in window.tamOnload_$objid) { window.tamOnload_{$objid}[i](); }";
| |
| − | $wgOut->addScript( "<script type=\"$wgJsMimeType\">$(function(){\n$script\n});</script>" );
| |
| − | $html = "$top<div class='$class' id='$id'></div>$bottom";
| |
| − | $html .= "<script type=\"$wgJsMimeType\">window.tamOnload_$objid=[]</script>";
| |
| − | | |
| − | } else {
| |
| − | | |
| − | // Finalise a menu
| |
| − | if( $depth > 0 ) $nodes .= str_repeat( '</ul></li>', $depth );
| |
| − | $nodes = preg_replace( "/<(a.*? )title=\".+?\".*?>/", "<$1>", $nodes ); // IE has problems with title attribute in suckerfish menus
| |
| − | $html = "<ul class='$class' id='$id'>\n$nodes</ul><div style=\"clear:both\"></div>";
| |
| − | $script = "if (window.attachEvent) {
| |
| − | var sfEls = document.getElementById('$id').getElementsByTagName('li');
| |
| − | for (var i=0; i<sfEls.length; i++) {
| |
| − | sfEls[i].onmouseover=function() { this.className+=' sfhover'; }
| |
| − | sfEls[i].onmouseout=function() { this.className=this.className.replace(new RegExp(' sfhover *'),''); }
| |
| − | }
| |
| − | }";
| |
| − | $wgOut->addScript( "<script type=\"$wgJsMimeType\">$(function(){\n$script\n});</script>" );
| |
| − | }
| |
| − | | |
| − | $text = preg_replace( "/\x7f1$u\x7f$id\x7f.+?$/m", $html, $text, 1 ); // replace first occurence of this trees root-id
| |
| − | $nodes = '';
| |
| − | $last = -1;
| |
| − | }
| |
| − | }
| |
| − | }
| |
| − | $text = preg_replace( "/\x7f1$u\x7f.+?[\\r\\n]+/m", '', $text ); // Remove all unreplaced row information
| |
| − | return true;
| |
| − | }
| |
| − | | |
| − | }
| |
| − | | |
| − | /**
| |
| − | * Called from $wgExtensionFunctions array when initialising extensions
| |
| − | */
| |
| − | function wfSetupTreeAndMenu() {
| |
| − | global $wgTreeAndMenu;
| |
| − | $wgTreeAndMenu = new TreeAndMenu();
| |
| − | }
| |
| − | | |
| − | /**
| |
| − | * Reserve magic words
| |
| − | */
| |
| − | function wfTreeAndMenuLanguageGetMagic( &$magicWords, $langCode = 0 ) {
| |
| − | global $wgTreeMagic, $wgMenuMagic, $wgStarMagic;
| |
| − | $magicWords[$wgTreeMagic] = array( 0, $wgTreeMagic );
| |
| − | $magicWords[$wgMenuMagic] = array( 0, $wgMenuMagic );
| |
| − | $magicWords[$wgStarMagic] = array( 0, $wgStarMagic );
| |
| − | return true;
| |
| − | }
| |