Commit 747462c0 authored by Aran Dunkley's avatar Aran Dunkley

restructure

git-svn-id: svn+ssh://organicdesign.co.nz/[email protected] 2ad5491c-c162-49ae-9d3b-afa169216a3e
parents
<?php
/**
* Internationalization file for magic words.
*/
$magicWords = array();
$magicWords['en'] = array(
'menu' => array( 0, 'menu' ),
'tree' => array( 0, 'tree' ),
'_tree' => array( 0, '_tree' ),
);
This diff is collapsed.
<?php
/**
* TreeAndMenu extension - Adds #tree and #menu parser functions for collapsible treeview's and dropdown menus
*
* See http://www.mediawiki.org/wiki/Extension:TreeAndMenu for installation and usage details
* - Tree component uses the FancyTree jQuery plugin, see http://wwwendt.de/tech/fancytree (changed from dTree to FancyTree in version 4, March 2015)
* - Menu component uses Son of Suckerfish, see http://alistapart.com/article/dropdowns
*
* @file
* @ingroup Extensions
* @author Aran Dunkley [http://www.organicdesign.co.nz/nad User:Nad]
* @copyright © 2007-2015 Aran Dunkley
* @licence GNU General Public Licence 2.0 or later
*/
if( !defined( 'MEDIAWIKI' ) ) die( 'Not an entry point.' );
define( 'TREEANDMENU_VERSION','4.1.0, 2015-04-04' );
define( 'TREEANDMENU_TREE', 1 );
define( 'TREEANDMENU_MENU', 2 );
$wgTreeAndMenuPersistIfId = false; // Makes trees with id attributes have persistent state
$wgExtensionCredits['parserhook'][] = array(
'path' => __FILE__,
'name' => 'TreeAndMenu',
'author' => '[http://www.organicdesign.co.nz/aran Aran Dunkley]',
'url' => 'http://www.mediawiki.org/wiki/Extension:TreeAndMenu',
'descriptionmsg' => 'treeandmenu-desc',
'version' => TREEANDMENU_VERSION,
);
$wgExtensionMessagesFiles['TreeAndMenu'] = __DIR__ . '/TreeAndMenu.i18n.php';
$wgExtensionMessagesFiles['TreeAndMenuMagic'] = __DIR__ . '/TreeAndMenu.i18n.magic.php';
require_once( __DIR__ . '/TreeAndMenu_body.php' );
new TreeAndMenu();
<?php
class TreeAndMenu {
public static $instance;
function __construct() {
global $wgExtensionFunctions;
$wgExtensionFunctions[] = 'TreeAndMenu::setup';
self::$instance = $this;
}
/**
* Called at extension setup time, install hooks and module resources
*/
public static function setup() {
global $wgOut, $wgParser, $wgExtensionAssetsPath, $wgResourceModules;
// Add hooks
$wgParser->setFunctionHook( 'tree', array( self::$instance, 'expandTree' ) );
$wgParser->setFunctionHook( 'menu', array( self::$instance, 'expandMenu' ) );
// Add the Fancy Tree scripts and styles
$path = $wgExtensionAssetsPath . '/' . basename( __DIR__ ) . '/fancytree';
$wgResourceModules['ext.fancytree'] = array(
'scripts' => array( 'jquery.fancytree.js', 'jquery.fancytree.persist.js', 'jquery.fancytree.mediawiki.js', 'fancytree.js' ),
'dependencies' => array( 'jquery.ui.core', 'jquery.effects.blind', 'jquery.cookie' ),
'remoteBasePath' => $path,
'localBasePath' => __DIR__ . '/fancytree',
);
$wgOut->addModules( 'ext.fancytree' );
$wgOut->addStyle( "$path/fancytree.css" );
$wgOut->addJsConfigVars( 'fancytree_path', $path );
// Add the Suckerfish scripts and styles
$path = $wgExtensionAssetsPath . '/' . basename( __DIR__ ) . '/suckerfish';
$wgResourceModules['ext.suckerfish'] = array(
'scripts' => array( 'suckerfish.js' ),
'dependencies' => array( 'jquery.client' ),
'remoteBasePath' => $path,
'localBasePath' => __DIR__ . '/suckerfish',
);
$wgOut->addModules( 'ext.suckerfish' );
$wgOut->addStyle( "$path/suckerfish.css" );
}
/**
* Expand #tree parser-functions
*/
public function expandTree() {
$args = func_get_args();
return $this->expandTreeAndMenu( TREEANDMENU_TREE, $args );
}
/**
* Expand #menu parser-functions
*/
public function expandMenu() {
$args = func_get_args();
return $this->expandTreeAndMenu( TREEANDMENU_MENU, $args );
}
/**
* Render a bullet list for either a tree or menu structure
*/
private function expandTreeAndMenu( $type, $args ) {
global $wgJsMimeType, $wgTreeAndMenuPersistIfId;
// First arg is parser, last is the structure
$parser = array_shift( $args );
$bullets = array_pop( $args );
// Convert other args (except class, id, root) into named opts to pass to JS (JSON values are allowed, name-only treated as bool)
$opts = array();
$atts = array();
foreach( $args as $arg ) {
if( preg_match( '/^(\\w+?)\\s*=\\s*(.+)$/s', $arg, $m ) ) {
if( $m[1] == 'class' || $m[1] == 'id' || $m[1] == 'root' ) $atts[$m[1]] = $m[2];
else $opts[$m[1]] = preg_match( '|^[\[\{]|', $m[2] ) ? json_decode( $m[2] ) : $m[2];
} else $opts[$arg] = true;
}
// If the $wgTreeAndMenuPersistIfId global is set and an ID is present, add the persist extension
if( array_key_exists( 'id', $atts ) && $wgTreeAndMenuPersistIfId ) {
if( array_key_exists( 'extensions', $opts ) ) $opts['extensions'][] = 'persist';
else $opts['extensions'] = array( 'persist' );
}
// Sanitise the bullet structure (remove empty lines and empty bullets)
$bullets = preg_replace( '|^\*+\s*$|m', '', $bullets );
$bullets = preg_replace( '|\n+|', "\n", $bullets );
// If it's a tree, wrap the item in a span so FancyTree treats it as HTML and put nowiki tags around any JSON props
if( $type == TREEANDMENU_TREE ) {
$bullets = preg_replace( '|^(\*+)(.+?)$|m', '$1<span>$2</span>', $bullets );
$bullets = preg_replace( '|^(.*?)(\{.+\})|m', '$1<nowiki>$2</nowiki>', $bullets );
}
// Parse the bullets to HTML
$html = $parser->parse( $bullets, $parser->getTitle(), $parser->getOptions(), true, false )->getText();
// Determine the class and id attributes
$class = $type == TREEANDMENU_TREE ? 'fancytree' : 'suckerfish';
if( array_key_exists( 'class', $atts ) ) $class .= ' ' . $atts['class'];
$id = array_key_exists( 'id', $atts ) ? ' id="' . $atts['id'] . '"' : '';
// If its a tree, we need to add some code to the ul structure
if( $type == TREEANDMENU_TREE ) {
// Mark the structure as tree data, wrap in an unclosable top level if root arg passed (and parse root content)
$tree = '<ul id="treeData" style="display:none">';
if( array_key_exists( 'root', $atts ) ) {
$root = $parser->parse( $atts['root'], $parser->getTitle(), $parser->getOptions(), false, false )->getText();
$html = $tree . '<li class="root">' . $root . $html . '</li></ul>';
$opts['minExpandLevel'] = 2;
} else $html = preg_replace( '|<ul>|', $tree, $html, 1 );
// Replace any json: markup in nodes into the li
$html = preg_replace( '|<li(>\s*\{.*?\"class\":\s*"(.+?)")|', "<li class='$2'$1", $html );
$html = preg_replace( '|<(li[^>]*)(>\s*\{.*?\"id\":\s*"(.+?)")|', "<$1 id='$3'$2", $html );
$html = preg_replace( '|<(li[^>]*)>\s*(.+?)\s*(\{.+\})\s*|', "<$1 data-json='$3'>$2", $html );
// Incorporate options as json encoded data in a div
$opts = count( $opts ) > 0 ? '<div class="opts" style="display:none">' . json_encode( $opts, JSON_NUMERIC_CHECK ) . '</div>' : '';
// Assemble it all into a single div
$html = "<div class=\"$class todo\"$id>$opts$html</div>";
}
// If its a menu, just add the class and id attributes to the ul
else $html = preg_replace( '|<ul>|', "<ul class=\"$class todo\"$id>", $html, 1 );
// Append script to prepare this tree or menu if page is already loaded
$html .= "<script type=\"$wgJsMimeType\">if('prepareTAM' in window) window.prepareTAM();</script>";
return array( $html, 'isHTML' => true, 'noparse' => true );
}
}
This diff is collapsed.
/**
* This code pre-processes trees and menus to integrate the third-party code into mediawiki without changing it
*/
$(document).ready(function(){
var id = 0; // unique ID for all tree LI elements
// This can be called again later and any unprepared trees and menus will get prepared
// - this was done so that trees and menus can work when received via Ajax such as in a live preview
window.prepareTAM = function() {
var inner;
/**
* Prepare trees
*/
$('.fancytree.todo').each(function() {
// Remove the todo class from this tree (allows new trees laoded via ajax to be processed too)
$(this).removeClass('todo');
// Get options passed to the parser-function from span
var div = $('div.opts', $(this));
var opts = {};
if(div.length > 0) {
opts = $.parseJSON(div.text());
div.remove();
}
// Add the mediawiki extension
if('extensions' in opts) opts['extensions'].push('mediawiki');
else opts['extensions'] = ['mediawiki'];
// Activate the tree
$(this).fancytree(opts);
});
/**
* Prepare menus (add even and odd classes)
*/
$('.suckerfish.todo').each(function() {
$(this).removeClass('todo');
$('li', this).each(function() {
var li = $(this);
li.addClass( (li.index()&1) ? 'odd' : 'even' );
});
});
};
// Prepare any trees and menus loaded in page that need preparing now
window.prepareTAM();
});
// Preload the tree icons and loader
var path = mw.config.get('fancytree_path');
(new Image()).src = path + '/loading.gif';
(new Image()).src = path + '/icons.gif';
This diff is collapsed.
/*!
* jquery.fancytree.mediawiki.js
*
* Add mediawiki-specific ajax loading and some mediawiki helper functions
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
* - for samples of all events, see https://github.com/mar10/fancytree/blob/master/demo/sample-events.html
*
* Copyright (c) 2015, Aran Dunkley (http://www.organicdesign.co.nz/aran)
*
* Released under the GNU General Public Licence 2.0 or later
*
*/
(function($, window, document, mw, undefined) {
"use strict";
/**
* Open the tree to the node containing the passed title, or current page if none supplied
*/
$.ui.fancytree._FancytreeClass.prototype.makeTitleVisible = function(title) {
var local = this.ext.mediawiki;
if(typeof(title) === 'undefined') title = mw.config.get('wgTitle');
this.visit(function(node) {
if(node.title == title) {
node.makeVisible({ noAnimation: true, noEvents: true, scrollIntoView: false });
node.setActive({ noEvents: true });
return false;
}
});
};
/**
* Register the extension and set the lazy-loading up in a MediaWiki-friendly way
*/
$.ui.fancytree.registerExtension({
name: "mediawiki",
version: "1.0.0",
options: {},
// When a tree is initialised, do some modifications appropriate to mediawiki trees
treeInit: function(ctx) {
var tree = ctx.tree, opts = ctx.options;
// If there's "ajax" in this nodes data, then it needs to be marked as lazy and the "ajax" removed
opts.renderNode = function(event, data) {
var node = data.node;
if('ajax' in node.data) {
node.lazy = true;
node.children = null;
node.setFocus(true);
node.setFocus(false);
node.data.url = node.data.ajax;
delete node.data.ajax;
}
};
// Lazy load event to collect child data from the supplied URL via ajax
opts.lazyLoad = function(event, data) {
var url = data.node.data.url;
// Set result to a jQuery ajax options object
data.result = {
type: 'GET',
dataType: 'text',
};
// If the ajax option is an URL, split it into main part and query-string
if(url.match(/^(https?:\/\/|\/)/)) {
var parts = url.split('?');
data.result.url = parts[0];
data.result.data = parts[1];
}
// Otherwise treat it as an article title to be read with action=render
else {
data.result.url = mw.util.wikiScript();
data.result.data = { title: url, action: 'render' };
}
};
// Parse the data collected from the Ajax response and make it into child nodes
opts.postProcess = function(event, data) {
var response = data.response, m;
// If there's a UL section in it, parse it into nodes
if(m = response.match(/^.*?(<ul[\s\S]+<\/ul>)/i)) data.result = $.ui.fancytree.parseHtml($(m[1]));
// Otherwise see if it's as a JSON list of node data (need to extract as MediaWiki adds parser info)
else if(m = response.match(/^.*?(\[[\s\S]+\])/i)) data.result = $.parseJSON(m[1]);
// Otherwise just return an empty node set (should raise an error)
else data.result = [];
};
return this._superApply(arguments);
},
});
}(jQuery, window, document, mw));
This diff is collapsed.
/**
* Son of Suckerfish menu styles
* - see http://alistapart.com/article/dropdowns
*/
.suckerfish {
width: 180px;
}
.suckerfish, .suckerfish ul {
padding: 0;
margin: 0;
list-style: none;
}
.suckerfish .selflink {
font-weight: normal;
}
.suckerfish li {
position: relative;
float: left;
width: 172px;
padding: 4px;
z-index: 100;
}
.suckerfish li,
.suckerfish li.even a,
.suckerfish li.odd a,
.suckerfish li li,
.suckerfish li li a,
.suckerfish li li li,
.suckerfish li li li a {
color : black;
}
.suckerfish li li {
margin: 0;
}
.suckerfish li ul {
position: absolute;
left: -999em;
z-index: 101;
border: 1px solid #ccc;
}
.suckerfish li ul ul {
position: absolute;
left: -999em;
margin: 0 0 0 0;
z-index: 102;
}
.suckerfish li.odd .submenu {
width: 12px;
height: 15px;
float: right;
background: url(img/Arr_r.png) 0 3px no-repeat;
}
.suckerfish li.even .submenu {
width: 12px;
height: 15px;
float: right;
background: url(img/Arr_r.png) 0 3px no-repeat;
}
.suckerfish li.odd {
background: #E9EDF4;
border: 2px solid #E9EDF4;
}
.suckerfish li.even {
background: #D0D8E8;
border: 2px solid #D0D8E8;
}
.suckerfish li:hover, .suckerfish li.sfhover {
border: 2px solid #385D8A;
}
.suckerfish li:hover ul ul,
.suckerfish li:hover ul ul ul,
.suckerfish li.sfhover ul ul,
.suckerfish li.sfhover ul ul ul {
left: -999em;
}
.suckerfish li:hover ul,
.suckerfish li li:hover ul,
.suckerfish li li li:hover ul,
.suckerfish li.sfhover ul,
.suckerfish li li.sfhover ul,
.suckerfish li li li.sfhover ul {
left: 182px;
top: -3px;
}
/**
* Minimal JS for Son of Suckerfish menus
* - see http://alistapart.com/article/dropdowns
*/
$(document).ready(function() {
// IE has problems with title attribute in suckerfish menus
if($.client.profile().name == 'msie') $('.suckerfish a').removeAttr('title');
// Suckerfish hover fix
if(window.attachEvent) {
$('.suckerfish li').mouseenter( function() { this.addClass('sfhover'); });
$('.suckerfish li').mouseleave( function() { this.removeClass('sfhover'); });
}
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment