Kji has uploaded a new change for review.
https://gerrit.wikimedia.org/r/189494
Change subject: Implemented hierarchySelected parser function, upgraded
hierarchySubtree, bugfix for hierarchyParent.
......................................................................
Implemented hierarchySelected parser function, upgraded hierarchySubtree,
bugfix for hierarchyParent.
Implemented pruned version of hierarchySelected parser function.
Change-Id: I5d7f394c1147683574bfe28ee7ee8ba034ee81d7
Fixed a syntax error.
Change-Id: I4472500550f1ae7e561c673e0189ffdb1a637284
Added documentation for MST functions.
Change-Id: If0f9ee10bbc7bb28c10f3e1ec7054d5a9f25a2d0
Added basics of new renderer for hierarchySelected.
Change-Id: Iad7c5f8a1110b88f4e6a51f0f71e58d47dc172e2
Render selected now correctly marks selected items with checked boxes.
Change-Id: I547a407460e6bbbf014a81df55f45953d044a037
Implemented collapsed displaymode for hierarchySelected parser function.
Change-Id: I8225cd65763c3a64483086f82562ec52e304b853
Fixed empty parent bug in hierarchyParent parser function.
Change-Id: I246bd9ef4ba0a6ee744d3d611673f72e4bf92291
Implemented showroot and collapsed display modes for hierarchySubtree parser
function.
Change-Id: I26b4abf53b29f7f7b2afd00d172a4981a8a49435
Removed logging function.
Change-Id: I1ddf57997aad1923cdc9801f4dc455aab09f55d9
Modified hierarchySelected parser function so pruned displaymode is default
since that is the only supported display mode currently.
Change-Id: I964cc59534a29b60506df07a0a70c63a70d6ad31
Updated hierarchySelected so that it will allow the selected pages list to be
provided in link format.
Change-Id: I47cbee05fc6eaac42285531475baad87dd892e41
---
M HierarchyBuilder.class.php
M HierarchyBuilder.i18n.magic.php
M HierarchyBuilder.php
A HierarchyTree.php
A TreeNode.php
A renderHierarchySelected.js
6 files changed, 712 insertions(+), 11 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/HierarchyBuilder
refs/changes/94/189494/1
diff --git a/HierarchyBuilder.class.php b/HierarchyBuilder.class.php
index 371900c..b84cd57 100644
--- a/HierarchyBuilder.class.php
+++ b/HierarchyBuilder.class.php
@@ -39,6 +39,9 @@
const LINK = 'link';
const FORMAT = 'format';
const DISPLAYNAMEPROPERTY = 'displaynameproperty';
+ const DISPLAYMODE = 'displaymode';
+ const SHOWROOT = 'showroot';
+ const COLLAPSED = 'collapsed';
/**
* This function gives the section number for a target page within a
@@ -228,8 +231,9 @@
// Note that if there is no hierarchical
parent, then the parent
// will be empty.
$parent = self::getParent( $hierarchyRows,
$row, $i );
- array_push( $parents, $parent);
- //return $parent;
+ if ( $parent != '' ) {
+ array_push( $parents, $parent);
+ }
}
}
@@ -345,7 +349,7 @@
*
* @return string: The first page name found within $hierarchyRow.
*/
- private function getPageNameFromHierarchyRow( $hierarchyRow ) {
+ public function getPageNameFromHierarchyRow( $hierarchyRow ) {
$numMatches = preg_match_all( self::PAGENAMEPATTERN,
$hierarchyRow, $matches );
// give me the first subpattern match to be the name of the
previous page
$pageName = ( $numMatches > 0 ? $matches[1][0] : '' );
@@ -363,7 +367,7 @@
*
* @return number: The depth of $hierarchyRow.
*/
- private function getDepthOfHierarchyRow( $hierarchyRow ) {
+ public function getDepthOfHierarchyRow( $hierarchyRow ) {
$numMatches = preg_match_all( self::DEPTHPATTERN,
$hierarchyRow, $matches );
$depth = ( $numMatches > 0 ? strlen( $matches[1][0] ) : 0 );
return $depth;
@@ -525,6 +529,84 @@
$script = <<<END
mw.loader.using(['ext.HierarchyBuilder.render'], function () {
renderHierarchy("$hierarchyName", "$hierarchy", $collapsed, $numbered);
+});
+END;
+
+ global $wgOut;
+ $script = Html::inlineScript( $script );
+ $wgOut->addScript( $script );
+
+ return Html::element( 'div', array( 'id' => $hierarchyName ) );
+ }
+
+ public function renderHierarchySelected( $input, $attributes, $parser,
$frame ) {
+ $hierarchyName = 'HierarchyDiv' . self::$m_hierarchy_num;
+ self::$m_hierarchy_num++;
+
+ if ( isset( $attributes['collapsed'] ) ) {
+ $collapsed = htmlspecialchars( $attributes['collapsed']
);
+ if ( $collapsed === 'collapsed' ) {
+ $collapsed = 'true';
+ }
+ } else {
+ $collapsed = 'false';
+ }
+
+ if ( isset( $attributes['displaynameproperty'] ) ) {
+ $displayNameProperty =
+ htmlspecialchars(
$attributes['displaynameproperty'] );
+ } else {
+ $displayNameProperty = '';
+ }
+
+ if ( isset( $attributes['numbered'] ) ) {
+ $numbered = htmlspecialchars( $attributes['numbered'] );
+ if ( $numbered === 'numbered' ) {
+ $numbered = 'true';
+ }
+ } else {
+ $numbered = 'false';
+ }
+
+ if ( isset( $attributes['selected'] ) ) {
+ $selectedPages =
+ json_encode( explode( ',', urldecode(
$attributes['selected'] ) ) );
+ } else {
+ $selectedPages = '';
+ }
+
+ // this looks like it gets the property but it eats all the
links.
+ $input = $parser->recursiveTagParse( $input, $frame );
+ $input = self::anchorLinkHolders( $input );
+ $input = $parser->replaceLinkHoldersText( $input );
+ $input = $parser->parse( $input,
+ $parser->getTitle(),
+ $parser->Options(),
+ true,
+ false )->getText();
+
+ $hierarchy = HierarchyBuilder::parseHierarchy( $input,
+ $displayNameProperty, $dummy,
+ function ( $pageName, $displayNameProperty, $data ) {
+ $pageLinkArray = array();
+ $title = Title::newFromText( $pageName );
+ if ( $title ) {
+ $pageLinkArray['href'] =
$title->getLinkURL();
+ }
+ if ( strlen( $displayNameProperty ) > 0 ) {
+ $pageName =
HierarchyBuilder::getPageDisplayName( $pageName,
+ $displayNameProperty );
+ }
+ return Html::element( 'a', $pageLinkArray,
$pageName );
+ } );
+
+ $parser->getOutput()->addModules(
'ext.HierarchyBuilder.renderSelected' );
+
+ $hierarchy = strtr( $hierarchy, array( '"' => "'" ) );
+
+ $script = <<<END
+mw.loader.using(['ext.HierarchyBuilder.renderSelected'], function () {
+ renderHierarchySelected("$hierarchyName", "$hierarchy", $collapsed,
$numbered, $selectedPages);
});
END;
@@ -771,7 +853,7 @@
* @return array: An array with the root as the first element and each
* child subhierarchy as a subsequent element.
*/
- private static function splitHierarchy( $wikiTextHierarchy, $depth ) {
+ public static function splitHierarchy( $wikiTextHierarchy, $depth ) {
$nextDepth = "\n" . $depth . "*";
$r1 = "/\*/"; // this guy finds * characters
// this is building the regex that will be used later
@@ -846,7 +928,11 @@
$subHierarchyRows[0] = str_repeat( '*', strlen(
$depth ) + 1) . $subHierarchyRows[0]; // put the stars on the root row to start
$result = array_reduce( $subHierarchyRows,
function( $carry, $item ) use ( $depth
) {
- $carry .= "\n" . substr( $item,
strlen($depth));
+ if ( $carry != '' ){
+ $carry .= "\n" .
substr( $item, strlen($depth));
+ } else {
+ $carry = substr( $item,
strlen($depth));
+ }
return $carry;
}
);
@@ -861,4 +947,11 @@
return '';
}
+
+ public static function parseHierarchyToTree( $hierarchyPageName,
$hierarchyPropertyName ) {
+ $hierarchy = HierarchyBuilder::getPropertyFromPage(
$hierarchyPageName, $hierarchyPropertyName );
+ $hierarchy = "[[Hierarchy_Root]]\n" . $hierarchy;
+
+ HierarchyTree::parseHierarchyToTree( $hierarchy );
+ }
}
diff --git a/HierarchyBuilder.i18n.magic.php b/HierarchyBuilder.i18n.magic.php
index bb927b7..fb95d37 100644
--- a/HierarchyBuilder.i18n.magic.php
+++ b/HierarchyBuilder.i18n.magic.php
@@ -29,5 +29,6 @@
'hierarchySectionNumber' => array(0, 'hierarchySectionNumber'),
'hierarchyParent' => array(0, 'hierarchyParent'),
'hierarchyChildren' => array(0, 'hierarchyChildren'),
- 'hierarchySubtree' => array(0, 'hierarchySubtree')
+ 'hierarchySubtree' => array(0, 'hierarchySubtree'),
+ 'hierarchySelected' => array(0, 'hierarchySelected')
);
diff --git a/HierarchyBuilder.php b/HierarchyBuilder.php
index c81d0fd..60a6a70 100644
--- a/HierarchyBuilder.php
+++ b/HierarchyBuilder.php
@@ -61,6 +61,8 @@
$wgAutoloadClasses['HierarchyBuilder'] = __DIR__ .
'/HierarchyBuilder.class.php';
$wgAutoloadClasses['HierarchyFormInput'] = __DIR__ . '/HierarchyFormInput.php';
$wgAutoloadClasses['HierarchySelectFormInput'] = __DIR__ .
'/HierarchySelectFormInput.php';
+$wgAutoloadClasses['HierarchyTree'] = __DIR__ . '/HierarchyTree.php';
+$wgAutoloadClasses['TreeNode'] = __DIR__ . '/TreeNode.php';
$wgMessagesDirs['HierarchyBuilder'] = __DIR__ . "/i18n";
@@ -78,6 +80,15 @@
'localBasePath' => __DIR__,
'remoteExtPath' => 'HierarchyBuilder',
'scripts' => 'renderHierarchy.js',
+ 'dependencies' => array(
+ 'ext.HierarchyBuilder.jstree'
+ )
+);
+
+$wgResourceModules['ext.HierarchyBuilder.renderSelected'] = array(
+ 'localBasePath' => __DIR__,
+ 'remoteExtPath' => 'HierarchyBuilder',
+ 'scripts' => 'renderHierarchySelected.js',
'dependencies' => array(
'ext.HierarchyBuilder.jstree'
)
@@ -115,12 +126,101 @@
$parser->setFunctionHook( 'hierarchyParent', 'parent' );
$parser->setFunctionHook( 'hierarchyChildren', 'children' );
$parser->setFunctionHook( 'hierarchySubtree', 'subhierarchy' );
+ $parser->setFunctionHook( 'hierarchySelected', 'hierarchySelected' );
$parser->setHook( 'hierarchy', 'renderHierarchy' );
+ $parser->setHook( 'hierarchySelected', 'renderHierarchySelected');
global $sfgFormPrinter;
$sfgFormPrinter->registerInputType( 'HierarchyFormInput' );
$sfgFormPrinter->registerInputType( 'HierarchySelectFormInput' );
return true;
+}
+
+/**
+ * This parser function will return only specific selected rows of a hierarchy
+ * in addition to any necessary contextual rows.
+ *
+ * The returned hierarchy is displayd similarly to the
HierarchySelectFormInput,
+ * with each row preceeded by a checkbox. However, the checkboxes will be
inactive.
+ *
+ * For a given set of selected rows, only those rows will be provided from the
+ * hierarchy in addition to the minimal necessary contextual rows needed to
display
+ * the hierarchical relationships. For example, if a single selected row is
given,
+ * but that row is a leaf node which is 5 levels deep within the hierarchy,
then
+ * that row will be given along with each of its ancestors. This is conidered
the
+ * "pruned" behavior.
+ *
+ * The "collapsed" behavior will not remove any branches of the hierarchy, even
+ * when those branches do not contain any of the specified selected rows.
Instead,
+ * these unnecessary branches will be collapsed initially, allowing only the
+ * selected rows and their siblings to be shown.
+ *
+ * @param $parser: Parser
+ * @return I don't know yet.
+ *
+ * Example invokation:
+ * @code
+ * {{#hierarchySelected:<list of page names>|<hierarchy page name>|<hierarchy
property>}}
+ * {{#hierarchySelected:<list of page names>|<hierarchy page name>|<hierarchy
property>|pruned}}
+ * {{#hierarchySelected:<list of page names>|<hierarchy page name>|<hierarchy
property>|collapsed}}
+ * @endcode
+ */
+function hierarchySelected( $parser ) {
+ $params = func_get_args();
+ if ( count( $params ) < 4 || count( $params ) > 5) {
+ $output = '';
+ } else {
+ $selectedPages = $params[1];
+ $hierarchyPageName = $params[2];
+ $hierarchyPropertyName = $params[3];
+ // if "pruned" is given, then set the displaymode to pruned.
otherwise, "collapsed"
+ if ( isset( $params[4] ) && $params[4] == 'collapsed') {
+ $displayMode = 'collapsed';
+ } else {
+ $displayMode = 'pruned';
+ }
+
+ $wikitextHierarchy = HierarchyBuilder::getPropertyFromPage(
$hierarchyPageName, $hierarchyPropertyName );
+ // this is where we ask HierarchyBuilder class to actually do
the work for us.
+ $hierarchyTree = HierarchyTree::fromWikitext(
$wikitextHierarchy );
+
+ $normalizedSelectedPages =
+ array_map(
+ function( $page ) {
+ $pagename =
HierarchyBuilder::getPageNameFromHierarchyRow( $page );
+ if ( $pagename == '' ) {
+ $pagename = $page;
+ }
+ return $pagename;
+ },
+ explode( ',', $selectedPages )
+ );
+ $mst = $hierarchyTree->getMST( $normalizedSelectedPages );
+
+ // output formatting
+ $flatNormalizedSelectedPages =
+ array_reduce( $normalizedSelectedPages,
+ function( $carry, $item ) {
+ if ( $carry == '') {
+ $carry = $item;
+ } else {
+ $carry .= ',' . $item;
+ }
+ return $carry;
+ }
+ );
+ $selected = htmlspecialchars( str_replace( " ", "%20",
$flatNormalizedSelectedPages ) );
+
+ $output = '';
+ if ( $displayMode == 'collapsed') {
+ $output = "<hierarchySelected collapsed
selected=$selected>" . (string)$mst . '</hierarchySelected>';
+ } else {
+ $output = "<hierarchySelected selected=$selected>" .
(string)$mst . '</hierarchySelected>';
+ }
+ $output = $parser->recursiveTagParse( $output );
+
+ }
+ return $parser->insertStripItem( $output, $parser->mStripState );
}
/**
@@ -171,6 +271,18 @@
if ( isset(
$optionalParams[HierarchyBuilder::DISPLAYNAMEPROPERTY] ) ) {
$displaynameproperty =
$optionalParams[HierarchyBuilder::DISPLAYNAMEPROPERTY];
}
+ /*$displaymode = '';
+ if ( isset( $optionalParams[HierarchyBuilder::DISPLAYMODE] ) ) {
+ $displaymode =
$optionalParams[HierarchyBuilder::DISPLAYMODE];
+ }*/
+ $showroot = '';
+ if ( isset( $optionalParams[HierarchyBuilder::SHOWROOT] ) ) {
+ $showroot = $optionalParams[HierarchyBuilder::SHOWROOT];
+ }
+ $collapsed = '';
+ if ( isset( $optionalParams[HierarchyBuilder::COLLAPSED] ) ) {
+ $collapsed =
$optionalParams[HierarchyBuilder::COLLAPSED];
+ }
$output = HierarchyBuilder::getSubhierarchy(
$rootNode,
@@ -178,12 +290,32 @@
$hierarchyPropertyName
);
- // this is the default output display mode
+ // this is where we have to handle the default mode which is
not showroot and not collapsed
+ if ( $showroot == '' ) {
+ // fix $output so only the children are given
+ $hierarchyrows = preg_split( '/\n/', $output );
+ $root = $hierarchyrows[0];
+ $children = array_slice( $hierarchyrows, 1 );
+
+ $depth = HierarchyBuilder::getDepthOfHierarchyRow(
$root );
+ $output = array_reduce( $children,
+ function( $carry, $item ) use ( $depth
) {
+ if ($carry != '') {
+ $carry .= "\n" .
substr( $item, strlen($depth));
+ } else {
+ $carry = substr( $item,
strlen($depth));
+ }
+ return $carry;
+ }
+ );
+ }
+
+ // this is the default output display format
if ($format != 'ul') {
if ( $displaynameproperty == '' ) {
- $output = "<hierarchy>$output</hierarchy>";
+ $output = "<hierarchy
$collapsed>$output</hierarchy>";
} else {
- $output = "<hierarchy
displaynameproperty=$displaynameproperty>$output</hierarchy>";
+ $output = "<hierarchy $collapsed
displaynameproperty=$displaynameproperty>$output</hierarchy>";
}
}
// otherwise it's the bulleted format and we don't modify
output.
@@ -527,6 +659,15 @@
'noparse' => false );
}
+function renderHierarchySelected( $input, $attributes, $parser, $frame ) {
+ $hierarchyBuilder = new HierarchyBuilder;
+ $output = $hierarchyBuilder->renderHierarchySelected( $input,
$attributes, $parser,
+ $frame );
+ $parser->disableCache();
+ return array( $parser->insertStripItem( $output, $parser->mStripState ),
+ 'noparse' => false );
+}
+
/**
* Helper function for parsing a list of named parser function parameters.
*
@@ -554,6 +695,8 @@
$ret = preg_split( '/=/', $param, 2 );
if ( count( $ret ) > 1 ) {
$paramArray[$ret[0]] = $ret[1];
+ } else {
+ $paramArray[$ret[0]] = $ret[0];
}
return $paramArray;
-}
+}
\ No newline at end of file
diff --git a/HierarchyTree.php b/HierarchyTree.php
new file mode 100644
index 0000000..4cbaddc
--- /dev/null
+++ b/HierarchyTree.php
@@ -0,0 +1,149 @@
+<?php
+
+/*
+ * Copyright (c) 2014 The MITRE Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+class HierarchyTree {
+ protected $root = null;
+
+ function __construct() {
+ //$root = $node;
+ }
+
+ public static function fromNode( $node ) {
+ $instance = new self();
+ $instance->loadRootNode( $node );
+ return $instance;
+ }
+
+ public static function fromWikitext( $wikitextHierarchy ) {
+ $hierarchy = "[[Hierarchy_Root]]\n" . $wikitextHierarchy;
+ $rootNode = HierarchyTree::parseHierarchyToTreeHelper(
$hierarchy, '' );
+
+ $instance = new self();
+ $instance->loadRootNode( $rootNode );
+ return $instance;
+ }
+
+ private function loadRootNode( $node ) {
+ $this->root = $node;
+ }
+
+ private function parseHierarchyToTreeHelper( $wikiTextHierarchy, $depth
) {
+ $curRootAndChildren =
HierarchyBuilder::splitHierarchy($wikiTextHierarchy, $depth);
+ $curRootText = $curRootAndChildren[0];
+ $curChildrenText = array_slice($curRootAndChildren, 1);
+
+ $curRootNode = new TreeNode( $depth . $curRootText );
+
+ foreach ( $curChildrenText as $childText ) {
+ $childNode = self::parseHierarchyToTreeHelper(
$childText, $depth . '*' );
+ $curRootNode->getValue();
+ $curRootNode->addChild( $childNode );
+ }
+
+ return $curRootNode;
+ }
+
+ public function __toString() {
+ return $this->serialize( $this->root );
+ }
+
+ private function serialize( $node ) {
+ $returnValue = '';
+ if ( $node != null) {
+ if ( $node != $this->root) {
+ $returnValue .= $node->getValue() . "\n";
+ }
+ if ( count( $node->getChildren() ) ) {
+ foreach ( $node->getChildren() as $child ) {
+ $returnValue .=
$this->serialize($child);
+ }
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Returns the MST that is a subtree of this hierarchy containing the
+ * specified rows and the hierarchy root.
+ *
+ * The returned MST will always contain this hierarchy's root node,
each of
+ * the specified target rows, and any intermediate nodes from the
hierarchy
+ * which are necessary to form a connected graph.
+ *
+ * @param array $rows: List of targeted rows within the hierarchy that
must
+ * included in the MST.
+ *
+ * @return HierarchyTree: A new HierarchyTree object representing the
MST
+ * that holds all the targeted rows in $rows
+ */
+ public function getMST( array $rows ) {
+ $mstRoot = $this->getMSTHelper( $this->root, $rows );
+ $mst = self::fromNode( $mstRoot );
+ return $mst;
+ }
+
+ /**
+ * Helper function to return a branch or subbranch of the final MST.
+ *
+ * Logically this function proceeds as follows. If the current node
being
+ * examined is one of the targeted rows, then a copy of this node will
be
+ * returned (children may or may not be included). If any descendants of
+ * the current node are targeted rows, then the immediate child of the
+ * current node leading to that targeted descendant is included as a
child
+ * of the returned node. This proceeds recursively until the entire tree
+ * has been explored.
+ *
+ * @param TreeNode $node: The current node being examined within this
hierarchy.
+ * @param array $rows: The list of target rows to be included in the
MST.
+ *
+ * @return TreeNode: A treenode that forms the root of a branch of the
final
+ * MST. If the current node being examined in the hierarchy is one of
the
+ * targeted rows or any of it's children is a targeted row, then a
TreeNode
+ * object will be returned here. Otherwise, the entire subtree of this
+ * hierarchy that is rooted at the current node is not a part of the
final
+ * MST and null is returned instead.
+ */
+ private function getMSTHelper( TreeNode $node, array $rows ) {
+ // make a copy of this node
+ $clone = new TreeNode( $node->getValue() );
+
+ // if any of the children are found in $rows then add them to
the copy
+ if ( count( $node->getChildren()) > 0 ) {
+ foreach ( $node->getChildren() as $child ) {
+ $subtree = $this->getMSTHelper( $child, $rows );
+ if ( $subtree != null ) {
+ $clone->addChild( $subtree );
+ }
+ }
+ }
+
+ // if children are in $rows or self is in rows then return the
copy
+ if ( $clone->getChildren() != null || in_array(
HierarchyBuilder::getPageNameFromHierarchyRow( $node->getValue() ), $rows ) ) {
+ return $clone;
+ } else { // otherwise this whole branch gets cut
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TreeNode.php b/TreeNode.php
new file mode 100644
index 0000000..35976f7
--- /dev/null
+++ b/TreeNode.php
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * Copyright (c) 2014 The MITRE Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+class TreeNode {
+ protected $value = null; // this is the actuall row value like
"***[[Hierarchy Builder]]"
+ protected $parent = null; // this is a pointer to the parent node
within the tree
+ protected $children = null; // an array of pointers to children
+
+ function __construct( $text ) {
+ $this->value = $text;
+ }
+
+ public function getValue() {
+ return $this->value;
+ }
+
+ public function getParent() {
+ return $this->parent;
+ }
+
+ public function getChildren() {
+ return $this->children;
+ }
+
+ public function setValue( $text ) {
+ $this->value = $text;
+ }
+
+ public function setParent( TreeNode $node ) {
+ $this->parent = $node;
+ }
+
+ public function addChild( TreeNode $node ) {
+ if ( $this->children == null ) {
+ $this->children = array( $node );
+ } else {
+ array_push( $this->children, $node );
+ }
+ }
+}
\ No newline at end of file
diff --git a/renderHierarchySelected.js b/renderHierarchySelected.js
new file mode 100644
index 0000000..f8915f7
--- /dev/null
+++ b/renderHierarchySelected.js
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2014 The MITRE Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+
+( function( $ ) {
+ /**
+ * Gobal function to display a hierarchy.
+ *
+ * @param {string} divId Name of the div to render the hierarchy into
+ * @param {string} hierarchy A 2 dimensional array of the data
+ * - for each row in hierarchy, the first column is the level of
indentation,
+ * the second column is the name of the page, and the third
column is
+ * the URL of the page
+ * @param {string} collapsed A Boolean that indicates if the tree should
+ * start collapsed
+ * @param {string} numbered A boolean that indicates if the hierarchy
+ * should be auto-numbered
+ */
+ window.renderHierarchySelected = function( divId, hierarchy, collapsed,
numbered, selectedComponents ) {
+ /**
+ * @class RenderHierarchy
+ *
+ * Object literal implementing hierarchy rendering.
+ *
+ * This is actually an object literal which is defined and used
by the
+ * global renderHierarchy function which contains all of the
necessary
+ * functions for rendering a hierarchy.
+ */
+ ( {
+ /**
+ * Render a hierarchy on a page.
+ *
+ * @param {string} divId Name of the div to render the
hierarchy into
+ * @param {string} hierarchy A 2 dimensional array of
the data
+ * - for each row in hierarchy, the first column is the
level of
+ * indentation, the second column is the name of the
page, and the
+ * third column is the URL of the page
+ * @param {string} collapsed A Boolean that indicates
if the tree
+ * should start collapsed
+ * @param {string} numbered A boolean that indicates if
the hierarchy
+ * should be auto-numbered
+ */
+ render: function( divId, hierarchy, collapsed,
numbered, selectedComponents ) {
+ if ( hierarchy.length < 1 ) {
+ return;
+ }
+
+ if ( numbered ) {
+ var $hierarchy = $( hierarchy );
+ $hierarchy = this.numberHtml(
$hierarchy );
+ hierarchy = $hierarchy[ 0 ].outerHTML;
+ }
+
+ if ( selectedComponents &&
selectedComponents.length > 0) {
+ for (var i = 0; i <
selectedComponents.length; i++) {
+ selectedComponents[i] =
selectedComponents[i].replace("%20", " ");
+ }
+ }
+
+
+ var obj = this;
+
+ var jqDivId = "#" + divId;
+ $( jqDivId )
+ .html( hierarchy )
+ .attr('dir', 'ltr');
+ $( jqDivId + " * li" )
+ .css( "list-style-image", "none" );
+ $( jqDivId )
+ .bind( "loaded.jstree", function(
event, data ) {
+ obj.initializeTree( jqDivId,
selectedComponents, true, collapsed );
+ } );
+ $( jqDivId )
+ .bind( "refresh.jstree", function(
event, data ) {
+ obj.initializeTree( jqDivId,
selectedComponents, true, collapsed );
+ } );
+
+ $( jqDivId )
+ .jstree( {
+ "themes": {
+ "theme": "apple",
+ "dots": true,
+ "icons": false
+ },
+ "checkbox": {
+ "two_state": true
+ },
+ "types": {
+ "types": {
+ "disabled": {
+
"check_node": false,
+
"uncheck_node": false
+ }
+ }
+ },
+ "plugins": [ "themes",
"html_data", "checkbox", "types" ]
+ } );
+ },
+
+ /**
+ * Initializes the displayed hierarchy to display the
properly and
+ * have the correct behaviors.
+ *
+ * @param {string} jqDivId The id of the div containing
the hierarchy
+ * to be initialized.
+ * @param {Array} selectedComponents A list of the rows
which should
+ * be displayed as selected to begin with.
+ * @param {boolean} init A boolean to indicate whether
the hierarchy
+ * is being initialized or refreshed.
+ * @param {boolean} collapsed A boolean to indicate
whether the
+ * hierarchy should start out in collapsed or expanded
form.
+ */
+ initializeTree: function( jqDivId, selectedComponents,
init, collapsed ) {
+ var obj = this;
+
+ if ( collapsed ) {
+ $( jqDivId )
+ .jstree( "close_all" );
+ } else {
+ $( jqDivId )
+ .jstree( "open_all" );
+ }
+
+ $( jqDivId + "* li" )
+ .each( function() {
+ var parent = $( this );
+ $( this )
+ .children( "a" )
+ .each( function() {
+ var $element =
$( this );
+ var elementName
= $( this )
+
//.children( "span:first" )
+ .text()
+ .trim();
+ if (
obj.isSelectedHierarchyComponent( elementName,
+
selectedComponents ) ) {
+ $(
jqDivId )
+
.jstree( "check_node", parent );
+ $( this
).attr("class", "selectedHierarchyRow");
+ }
+ } );
+ } );
+
+ $( jqDivId + "* li" )
+ .each( function() {
+ $.jstree._reference( jqDivId )
+ .set_type( "disabled",
+ $( this ) );
+ } );
+ },
+
+ /**
+ * Determine if a row has been selected or not.
+ *
+ * @param {string} elementName The row who's selected
status is being
+ * determined.
+ * @param {Array} selectedComponents The list of
currently seleccted
+ * hierarchy rows.
+ *
+ * @return {boolean} True if elementName is included in
the array
+ * selectedComponents. False otherwise.
+ */
+ isSelectedHierarchyComponent: function( elementName,
+ selectedComponents ) {
+ if ( selectedComponents &&
selectedComponents.length > 0 ) {
+ var pageName = elementName;
+ var index = $.inArray( pageName,
selectedComponents );
+ if ( index !== -1 ) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /**
+ * Updates a hierarchy to include section numbers.
+ *
+ * This function takes an HTML hierarchy and applies
section numbers
+ * to each row of the hierarchy. The resulting HTML
hierarchy with
+ * section numbers applied is then returned.
+ *
+ * @param {Object} uListRoot A jquery object
representing the top
+ * level ul element of the entire HTML hierarchy.
+ */
+ numberHtml: function( uListRoot ) {
+ var list = uListRoot.clone();
+ return this.numberHtmlHelper( list, "" );
+ },
+
+ /**
+ * Helper function for applying section numbers to a
hierarchy.
+ *
+ * This is a helper function which recursively
traverses a hierarchy
+ * and computes and applies section numbers to the
beginning of each
+ * row in that hierarchy.
+ *
+ * @param {Object} uListRoot A jquery object
representing the top
+ * level ul element of the entire HTML hierarchy.
+ * @param {string} numberPrefix A string containing the
immediate
+ * parent's complete section number. (ex: 1.1) This is
used to
+ * construct the child's complete section number by
using the
+ * numberPrefix as a suffix. (ex: 1.1 is used to
create 1.1.1)
+ *
+ * @return {Object} A jquery object representing the
top level ul
+ * element of the HTML hierarchy with section numbers
applied.
+ */
+ numberHtmlHelper: function( uListRoot, numberPrefix ) {
+ var that = this;
+
+ var $numberSuffix = 1; // this is the
subsection number for a particular child. It starts at one because the first
child is numbered 1.
+
+ var cur = uListRoot[ 0 ];
+ $( cur )
+ .children( $( "li" ) )
+ .each( function() {
+ var $children = $( this )
+ .contents();
+
+ var childNumber = numberPrefix
=== "" ? $numberSuffix++ : numberPrefix + "." + $numberSuffix++;
+ var numberedChild = childNumber
+ " " + $children.first()
+ .text();
+ $children.first()
+ .text( numberedChild );
+
+ var $sublist =
$children.filter( "ul" );
+ if ( $sublist.size() > 0 ) {
+ that.numberHtmlHelper(
$sublist, childNumber ); // recurse on the sublist
+ }
+ } );
+ return uListRoot;
+ }
+ } )
+ .render( divId, hierarchy, collapsed, numbered,
selectedComponents );
+ };
+}( jQuery ) );
--
To view, visit https://gerrit.wikimedia.org/r/189494
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I47cbee05fc6eaac42285531475baad87dd892e41
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/HierarchyBuilder
Gerrit-Branch: master
Gerrit-Owner: Kji <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits