Index: trunk/extensions/ApiExplorer/SpecialApiExplorer.php |
— | — | @@ -0,0 +1,109 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * A Special Page for interactively exploring the documentation of the specific |
| 5 | + * version of the MediaWiki API running on this MediaWiki installation. |
| 6 | + * |
| 7 | + * @author Sean Colombo <firstname>@<firstname><lastname>.com |
| 8 | + * @addtogroup Extensions |
| 9 | + * |
| 10 | + * TODO: LATER: Auto-create forms (like Flickr's API Explorer) so that |
| 11 | + * the user can do test-runs of all of the API calls. Should show the final URL |
| 12 | + * similar to Special:CategoryIntersection, but should be generic so that it can |
| 13 | + * handle all functions. |
| 14 | + */ |
| 15 | + |
| 16 | +if (!defined('MEDIAWIKI')) die(); |
| 17 | + |
| 18 | +$wgExtensionFunctions[] = 'wfSpecialApiExplorer'; |
| 19 | +$wgExtensionCredits['specialpage'][] = array( |
| 20 | + 'path' => __FILE__, |
| 21 | + 'name' => 'API Explorer', |
| 22 | + //'url' => 'http://www.mediawiki.org/wiki/Extension:ApiExplorer', // TODO: Put into MediaWiki Extensions repo |
| 23 | + 'description' => 'Extension for interactively viewing live API documentation', // TODO: Update this if we have forms for each function. |
| 24 | + 'descriptionmsg' => 'apiexplorer-desc', |
| 25 | + 'author' => '[http://seancolombo.com Sean Colombo]' |
| 26 | +); |
| 27 | + |
| 28 | +$dir = dirname(__FILE__) . '/'; |
| 29 | +$wgExtensionMessagesFiles['ApiExplorer'] = $dir . 'ApiExplorer.i18n.php'; |
| 30 | + |
| 31 | +function wfSpecialApiExplorer () { |
| 32 | + global $IP; |
| 33 | + require_once "$IP/includes/SpecialPage.php"; |
| 34 | + class SpecialApiExplorer extends SpecialPage { |
| 35 | + /** |
| 36 | + * Constructor |
| 37 | + */ |
| 38 | + function SpecialApiExplorer() { |
| 39 | + SpecialPage::SpecialPage( 'ApiExplorer' ); |
| 40 | + $this->includable( false ); |
| 41 | + } |
| 42 | + |
| 43 | + /** |
| 44 | + * Main function of the special page. Basically serves as a wrapper which loads |
| 45 | + * the main functionality (which is in Javascript). |
| 46 | + * |
| 47 | + * @param $par - parameters for SpecialPage. Ignored. |
| 48 | + */ |
| 49 | + function execute( $par = null ) { |
| 50 | + global $wgOut, $wgExtensionsPath, $wgCityId, $wgStyleVersion; |
| 51 | + wfProfileIn( __METHOD__ ); |
| 52 | + |
| 53 | + wfLoadExtensionMessages( "AutoCreateWiki" ); // TODO: This isn't needed anymore, even in Wikia code (which is 2 versions back at the moment), is it? |
| 54 | + |
| 55 | + // TODO: Make this work for ResourceLoader (Wikia isn't using RL yet at the time of this writing). |
| 56 | + // Wikia has the cachebuster in the wgExtensionPath (we rewrite that in varnish because many proxies won't cache things that have "?" in the URL), but other MediaWikis need the style-version in the querystring. |
| 57 | + $cbSuffix = ( isset($wgCityId) ? "?{$wgStyleVersion}" : "" ); |
| 58 | + $wgOut->addScript( "<script type=\"text/javascript\" src=\"{$wgExtensionsPath}/JavascriptAPI/Mediawiki.js{$cbSuffix}\"></script>" ); |
| 59 | + $wgOut->addScript( "<script type=\"text/javascript\" src=\"{$wgExtensionsPath}/ApiExplorer/apiExplorer.js{$cbSuffix}\"></script>" ); |
| 60 | + $wgOut->addScript( "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$wgExtensionsPath}/ApiExplorer/apiExplorer.css{$cbSuffix}\" />" ); |
| 61 | + |
| 62 | + ob_start(); |
| 63 | + $buttonHeight = 15; |
| 64 | + $collapseSrc = "$wgExtensionsPath/ApiExplorer/collapse.png$cbSuffix"; |
| 65 | + $expandSrc = "$wgExtensionsPath/ApiExplorer/collapse.png$cbSuffix"; |
| 66 | + ?><style> |
| 67 | + .collapsible h2 span, .collapsible h3 span{ |
| 68 | + width:<?= $buttonHeight ?>px; |
| 69 | + height:1em; |
| 70 | + float:right; |
| 71 | + display:inline-block; |
| 72 | + |
| 73 | + background-repeat:no-repeat; |
| 74 | + background-position:right center; |
| 75 | + background-image: url(<?= "$wgExtensionsPath/ApiExplorer/collapse.png$cbSuffix"; ?>); |
| 76 | + } |
| 77 | + .collapsed h2 span, .collapsed h3 span{ |
| 78 | + background-image: url(<?= "$wgExtensionsPath/ApiExplorer/expand.png$cbSuffix"; ?>); |
| 79 | + } |
| 80 | + </style> |
| 81 | + <div id='apEx_intro'> |
| 82 | + <?= wfMsg('apiexplorer-intro', "<a href='http://www.mediawiki.org/wiki/API:Main_page'>http://www.mediawiki.org/wiki/API:Main_page</a>") ?> |
| 83 | + </div> |
| 84 | + <div id='apEx_loading'><?= wfMsg('apiexplorer-loading') ?></div> |
| 85 | + <div id='apEx'> |
| 86 | + <?php |
| 87 | + $params = array("modules", "querymodules", "formatmodules"); |
| 88 | + foreach($params as $param){ |
| 89 | + ?><div class='<?= $param ?> collapsible collapsed paramName' data-param-name='<?= $param ?>'> |
| 90 | + <h2 class='name'><span class='toggleIcon'></span></h2> |
| 91 | + <div class='paramContent'> |
| 92 | + <div class='description'></div> |
| 93 | + <dl> |
| 94 | + <!-- Filled by a call to the API --> |
| 95 | + </dl> |
| 96 | + </div> |
| 97 | + </div><?php |
| 98 | + } ?> |
| 99 | + </div> |
| 100 | + <?php |
| 101 | + $outHtml = ob_get_clean(); |
| 102 | + |
| 103 | + $this->setHeaders(); |
| 104 | + $wgOut->addHTML( $outHtml ); |
| 105 | + wfProfileOut( __METHOD__ ); |
| 106 | + } // end execute() |
| 107 | + } |
| 108 | + |
| 109 | + SpecialPage::addPage( new SpecialApiExplorer ); |
| 110 | +} // end wfSpecialApiExplorer() |
Property changes on: trunk/extensions/ApiExplorer/SpecialApiExplorer.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 111 | + native |
Index: trunk/extensions/ApiExplorer/apiExplorer.css |
— | — | @@ -0,0 +1,105 @@ |
| 2 | + |
| 3 | +#apEx{ |
| 4 | + display:none; |
| 5 | +} |
| 6 | +#apEx_loading{ |
| 7 | + display:none; |
| 8 | +} |
| 9 | + |
| 10 | +#apEx div.paramName{ |
| 11 | + margin-bottom:10px; |
| 12 | +} |
| 13 | +dt.collapsible{ |
| 14 | + margin-bottom:15px; |
| 15 | +} |
| 16 | +dt.collapsed{ |
| 17 | + width:35%; |
| 18 | + min-width:300px; /* gives less "wall of green" effect when list is loaded & will still expand when the module is clicked on */ |
| 19 | +} |
| 20 | + |
| 21 | +#apEx div.paramName h2, .paramContent{ |
| 22 | + padding:10px; |
| 23 | + margin:0px; |
| 24 | +} |
| 25 | +dd.paramContent{ /* even with the rule above, this is still needed in order to have higher specificity than other statements in the skin */ |
| 26 | + margin:0px; |
| 27 | +} |
| 28 | + |
| 29 | +.paramContent{ |
| 30 | + border-left: 2px solid #008; |
| 31 | + border-right: 2px solid #008; |
| 32 | +} |
| 33 | +div.paramContent{ /* Top level*/ |
| 34 | + background-color: #bdf; |
| 35 | +} |
| 36 | +dd.paramContent{ /* per-module */ |
| 37 | + background-color:#bfd; |
| 38 | +} |
| 39 | + |
| 40 | +.collapsible h2, .collapsible h3{ |
| 41 | + /* Make it into a bar */ |
| 42 | + margin:0px; |
| 43 | + text-decoration:none; |
| 44 | + cursor:pointer; |
| 45 | + border:2px solid #008; |
| 46 | + -webkit-border-radius: 10px; |
| 47 | + -moz-border-radius: 10px; |
| 48 | + border-radius: 10px; |
| 49 | + |
| 50 | + /* Not collapsed... so hide the bottom border so it bleeds into the rest */ |
| 51 | + border-bottom:0px; |
| 52 | + border-bottom-left-radius:0px; |
| 53 | + border-bottom-right-radius:0px; |
| 54 | +} |
| 55 | +.collapsible.collapsed h2, .collapsible.collapsed h3, .paramContent{ |
| 56 | + /* Collapsed state for h2, h3 or expanded for content... show bottom border & rounded corners */ |
| 57 | + border-bottom:2px solid #008; |
| 58 | + border-bottom-left-radius:10px; |
| 59 | + border-bottom-right-radius:10px; |
| 60 | +} |
| 61 | + |
| 62 | +#apEx div.collapsible h2{ |
| 63 | + /* Pretty gradient background */ |
| 64 | + background-image: linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%); |
| 65 | + background-image: -o-linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%); |
| 66 | + background-image: -moz-linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%); |
| 67 | + background-image: -webkit-linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%); |
| 68 | + background-image: -ms-linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%); |
| 69 | + background-image: -webkit-gradient( |
| 70 | + linear, |
| 71 | + left bottom, |
| 72 | + left top, |
| 73 | + color-stop(0.43, rgb(10,74,143)), |
| 74 | + color-stop(0.72, rgb(52,112,217)) |
| 75 | + ); |
| 76 | + |
| 77 | + color:white; |
| 78 | +} |
| 79 | + |
| 80 | +.collapsed div, .collapsed dl, .collapsed dt, .collapsed dd{ |
| 81 | + display:none; |
| 82 | +} |
| 83 | + |
| 84 | + |
| 85 | +/** Second-level of style... the dts **/ |
| 86 | +#apEx dt h3, #apEx dt dd{ |
| 87 | + border-color: #080; |
| 88 | + box-shadow: 4px 4px 4px #888888; |
| 89 | +} |
| 90 | +#apEx dt h3{ |
| 91 | + padding:5px; |
| 92 | + |
| 93 | + background-image: linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%); |
| 94 | + background-image: -o-linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%); |
| 95 | + background-image: -moz-linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%); |
| 96 | + background-image: -webkit-linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%); |
| 97 | + background-image: -ms-linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%); |
| 98 | + |
| 99 | + background-image: -webkit-gradient( |
| 100 | + linear, |
| 101 | + left bottom, |
| 102 | + left top, |
| 103 | + color-stop(0.48, rgb(40,230,30)), |
| 104 | + color-stop(0.74, rgb(133,248,156)) |
| 105 | + ); |
| 106 | +} |
Property changes on: trunk/extensions/ApiExplorer/apiExplorer.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 107 | + native |
Index: trunk/extensions/ApiExplorer/expand.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Property changes on: trunk/extensions/ApiExplorer/expand.png |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 108 | + image/png |
Index: trunk/extensions/ApiExplorer/apiExplorer.js |
— | — | @@ -0,0 +1,138 @@ |
| 2 | +/** |
| 3 | + * @author Sean Colombo |
| 4 | + * @date 20110917 |
| 5 | + * |
| 6 | + * Javascript app that uses the API on the current wiki to dynamically generate browsable documentation |
| 7 | + * for the same API. |
| 8 | + */ |
| 9 | + |
| 10 | +$().log("== API Explorer =="); |
| 11 | + |
| 12 | +if(typeof ApiExplorer == "undefined"){ |
| 13 | + ApiExplorer = function(){ |
| 14 | + var self = this; |
| 15 | + |
| 16 | + /** |
| 17 | + * Called on document ready to make the first call to the API to build the list of available functions. |
| 18 | + */ |
| 19 | + this.run = function(){ |
| 20 | + $().log("Starting API Explorer..."); |
| 21 | + |
| 22 | + // Show the loading indicator until the API request has returned & the new page-content is set-up. |
| 23 | + $('#apEx_loading').show(); |
| 24 | + |
| 25 | + // Make a request to the API to get the list of available modules, querymodules, and formats. |
| 26 | + Mediawiki.paraminfo('modules', 'paraminfo', function(result){ |
| 27 | + var params = result.paraminfo.modules[0].parameters; |
| 28 | + for(var index in params){ |
| 29 | + var param = params[index]; |
| 30 | + if(param.type){ |
| 31 | + var allTypes = []; |
| 32 | + for(var typeIndex in param.type){ |
| 33 | + var t = param.type[typeIndex]; |
| 34 | + allTypes.push(t); |
| 35 | + } |
| 36 | + allTypes = allTypes.sort(); |
| 37 | + |
| 38 | + // There are three specific top-level lists we care about. |
| 39 | + var names = { |
| 40 | + 'modules': 'modules', |
| 41 | + 'querymodules': 'querymodules', |
| 42 | + 'formatmodules': 'formatmodules' |
| 43 | + }; |
| 44 | + if(typeof names[param.name] != 'undefined'){ |
| 45 | + var name = param.name; |
| 46 | + $('#apEx div.'+name+' h2.name').prepend( param.name ).data('module-name', param.name); |
| 47 | + $('#apEx div.'+name+' div.description').html( param.description ); |
| 48 | + for(var typeIndex in allTypes){ |
| 49 | + var t = allTypes[typeIndex]; |
| 50 | + $('#apEx div.'+name+' dl').append("<dt class='collapsible collapsed'><h3 data-param-name='"+t+"'>" + t + "<span class='toggleIcon'></span></h3></dt>"); |
| 51 | + } |
| 52 | + |
| 53 | + // Add click-handlers to each dt to get more info on that function. |
| 54 | + $('#apEx div.'+name+' dt.collapsible').click( function(e){ |
| 55 | + // If already expanded, just collapse. |
| 56 | + if(!$( e.currentTarget ).hasClass('collapsed')){ |
| 57 | + $( e.currentTarget ).addClass('collapsed'); |
| 58 | + } else { |
| 59 | + // Toggle dt (yes, T) state to be expanded |
| 60 | + $( e.currentTarget ).removeClass('collapsed'); |
| 61 | + |
| 62 | + var paramName = $(e.currentTarget).parents('div.paramName').data('param-name'); |
| 63 | + |
| 64 | + // Make call to API to get info for that dt... ONLY if the dd is still empty. |
| 65 | + var ddContent = $(e.currentTarget).find('dd').html(); |
| 66 | + if(ddContent == "" || ddContent == null){ |
| 67 | + // The dd (definition) for this dt (term) is not loaded yet. Use the API to load it. |
| 68 | + |
| 69 | + // TODO: Put a loading-indicator in the h3 and then remove when finished loading? |
| 70 | + // TODO: Put a loading-indicator in the h3 and then remove when finished loading? |
| 71 | + |
| 72 | + var dtName = $(e.currentTarget).find('h3').data('param-name'); |
| 73 | + $().log("Content is empty, loading info from API for " + paramName + '=' + dtName); |
| 74 | + |
| 75 | + Mediawiki.paraminfo(paramName, dtName, function(result){ |
| 76 | + $().log("Got info for "+ paramName + '=' + dtName); |
| 77 | + var modulesArray = result.paraminfo[paramName]; |
| 78 | + |
| 79 | + var module = modulesArray[0]; |
| 80 | + //var rawData = JSON.stringify( module ); |
| 81 | + var html = self.objToHtml( module ); |
| 82 | + $(e.currentTarget).append("<dd class='paramContent'>" + html + "</dd>"); |
| 83 | + }); |
| 84 | + } |
| 85 | + } |
| 86 | + }); |
| 87 | + } |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + // Make the top-level divs (modules, querymodules, formatmodules) collapsible. |
| 92 | + $('#apEx div.collapsible h2').click(function( e ){ |
| 93 | + $( e.currentTarget ).parent('div').toggleClass('collapsed'); |
| 94 | + }); |
| 95 | + |
| 96 | + $('#apEx_loading').hide(); |
| 97 | + $('#apEx').show(); |
| 98 | + }, function(err){ |
| 99 | + $().log("ERROR GETTING LIST OF MODULES FROM THE API.\nMake sure this wiki is a Wikia wiki or running v1.18+ of MediaWiki."); |
| 100 | + $().log(err); |
| 101 | + }); |
| 102 | + }; |
| 103 | + |
| 104 | + /** |
| 105 | + * Converts a JS object to HTML (mostly nested lists), but tweaked for the specific use-case of |
| 106 | + * the API Explorer's results from the API... so it will make the result more readable, but this function |
| 107 | + * isn't as simple/portable as it would be otherwise. |
| 108 | + */ |
| 109 | + this.objToHtml = function( obj ){ |
| 110 | + var html = ""; |
| 111 | + var html = "<ul>"; |
| 112 | + |
| 113 | + // TODO: Customize this for better display of parameters, errors, etc. (they're too nested right now). |
| 114 | + |
| 115 | + for(var key in obj){ |
| 116 | + var value = obj[key]; |
| 117 | + if(typeof value == 'object'){ |
| 118 | + value = self.objToHtml( value ); |
| 119 | + } |
| 120 | + html += "<li><strong>"+key+"</strong>: " + value + "</li>"; |
| 121 | + } |
| 122 | + html += "</ul>"; |
| 123 | + return html; |
| 124 | + }; |
| 125 | + |
| 126 | + this.clicked_modules = function(e){ return self.dtClicked( e, 'modules'); } |
| 127 | + this.clicked_querymodules = function(e){ return self.dtClicked( e, 'querymodules'); } |
| 128 | + this.clicked_formatmodules = function(e){ return self.dtClicked( e, 'formatmodules'); } |
| 129 | + this.dtClicked = function(e, paramName){ |
| 130 | + $().log("Got a click on " + paramName); |
| 131 | + $().log(e); |
| 132 | + }; |
| 133 | + }; |
| 134 | +} |
| 135 | + |
| 136 | +$(document).ready(function(){ |
| 137 | + var apEx = new ApiExplorer(); |
| 138 | + apEx.run(); |
| 139 | +}); |
Property changes on: trunk/extensions/ApiExplorer/apiExplorer.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 140 | + native |
Index: trunk/extensions/ApiExplorer/collapse.png |
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
Property changes on: trunk/extensions/ApiExplorer/collapse.png |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 141 | + image/png |
Index: trunk/extensions/ApiExplorer/ApiExplorer.i18n.php |
— | — | @@ -0,0 +1,26 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for extension ApiExplorer. |
| 5 | + * |
| 6 | + * @addtogroup Extensions |
| 7 | +*/ |
| 8 | + |
| 9 | +$messages = array(); |
| 10 | + |
| 11 | +/** English |
| 12 | + * @author Sean Colombo |
| 13 | + */ |
| 14 | +$messages['en'] = array( |
| 15 | + 'apiexplorer' => 'API Explorer', |
| 16 | + 'apiexplorer-desc' => 'Page for interactively exploring the documentation of the live version of the MediaWiki API running on this installation.', |
| 17 | + 'apiexplorer-intro' => 'This page shows documentation for the current wiki\'s API. Please click on a function below to see more info about what parameters can be used in each call. For the general MediaWiki API documentation, see $1', |
| 18 | + |
| 19 | + 'apiexplorer-loading' => 'Loading functions...', |
| 20 | +); |
| 21 | + |
| 22 | +/** Message documentation (Message documentation) |
| 23 | + */ |
| 24 | +$messages['qqq'] = array( |
| 25 | + 'apiexplorer-desc' => "Description of the extension (for Special:Version, etc.).", |
| 26 | + 'apiexplorer-intro' => "The intro at the top of the page to acquiant new users with what the page does." |
| 27 | +); |
Property changes on: trunk/extensions/ApiExplorer/ApiExplorer.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 28 | + native |