I've been working with a great deal of DekiScript recently and I haven't really had much time to "show off" any of my developments. Well, just this week I completed a new DekiScript dropdown menu and it's quite nice so I figured I'd share.
The dropdown menu was developed using a combination of DekiScript and JQuery. It is very easy to implement and doesn't require using any extensions. Simply just create a new template and paste in the provided code. The template then can by included on desired pages to provide an additional layer of navigation to your community.
The menu that is generated from the template displays two tiers of navigation and originates from the MindTouch root by default. Alternatively you can specify a path either statically or dynamically using DekiScript to identify where the navigation should originate from.
The code can be cleanly separated into two portions, DekiScript which handles the markup and Jquery which takes care of the CSS. Lets first take a look at the DekiScript as a whole and then I'll step through individual portions of the DekiScript code.
{{
var langpath ="/";
if ($path) {
let langpath=$path;
}
var langdir = wiki.getpage(langpath);
var topnav = langdir.subpages;
var subnav;
var navhtml;
var tophtml;
var subhtml;
var dropicon;
var topselect;
foreach(var top in topnav) {
let subhtml='';
let subnav = top.subpages;
let dropicon='';
let topselect='';
foreach(var sub in subnav) {
let subhtml..=('<li class="'..sub.name..'">'..web.link(sub.uri,sub.title)..'</li>')
}
if (#subnav > 0) {
let dropicon='<span class="dropicon">v</span>';
}
if (string.contains(page.uri,top.uri)) {
let topselect=' selected ';
}
let navhtml..=('<li class="'..top.name..topselect..'"><span>'..web.link(top.uri,top.title)..dropicon..'</span><ul>'..subhtml..'</ul></li>');
}
web.html('<ul id="DWdynnav">'..navhtml..'</ul>');
}}
Now lets break it down into smaller DekiScript chunks to take a look at what each piece is doing. The first part of the DekiScript is responsible for defining the path that the navigation should originate from. I used var langpath ="/"; to set the path default to your wiki homepage. $path is a template variable and is optional. If you are going to provide a path (discussed at the bottom of this blog post) you will do so when you call the template from the page that will display the menu. Lastly I need to work with the page object so I can access the properties and subpages to map out my menu. This is done by using var langdir = wiki.getpage(langpath);. Wiki.getpage returns a page object so if I later needed to I could access properties of the navigations origin page using something such as langdir.title or langdir.author.
var langpath ="/";
if ($path) {
let langpath=$path;
}
var langdir = wiki.getpage(langpath);
The next section is fairly simple. First I am finding out what pages are going to make up my top nav or the 1st tier of the dropdown menu. This is done by accessing the properties of langdir as mentioned in the previous section. You can see that I use var topnav = langdir.subpages; which will set topnav to a map of page objects which I will loop through soon to build my structure. After that I have to define a series of variables that are used to generate the navigations, HTML, CSS and the dropdown icon.
var topnav = langdir.subpages;
var subnav;
var navhtml;
var tophtml;
var subhtml;
var dropicon;
var topselect;
So here's where it gets a little trickier. To start off I need to loop through each item from the topnav so I setup my foreach loop using foreach(var top in topnav). As I start looping there are some variables that need to be set, they may have different outputs based on the dropdown content.
The first and most important variable I have to set is the dropdown pages. I do this by using let subnav = top.subpages; which will again return a map of page objects which I can later loop through to output the subpages. The other variable are just being reset.
Now that I know the dropdown pages (subnav) I can begin to generate the HTML that will be used to construct the navigation. As I loop through the subnav pages using foreach(var sub in subnav) I am appending new HTML to the subhtml variable which looks like this let subhtml..=('<li class="'..sub.name..'">'..web.link(sub.uri,sub.title)..'</li>')
After I have generated my subnav I need to differentiate my topnav menu items with a subnav from the topnav menu items with no subnav. Basically, if a topnav menu item does have subpage I want to display a little down arrow to let users know. This is done by using if (#subnav > 0). The # will return a count of the objects in the subnav variable, if any. If the topnav does have a submenu I add in a down arrow using let dropicon='<span class="dropicon">v</span>';.
The next part is not really necessary but I added it in regardless. Using if (string.contains(page.uri,top.uri)) I am comparing the topnav page URI's to the location of the page the the user is on. If their is a match I output let topselect=' selected '; which is later used in the topnav class. This allows users to know where they are relative to all of the pages in the dropdown menu.
The last part simply pulls all of the previous pieces together to construct the final HTML that will be used for the menu. You can see that using some DekiScript page properties such as top.name and top.title and I'm also using some DekiScript functions such as web.link. Lastly I'm placing my variables as need throughout the HTML: topselect, dropicon, subhtml. let navhtml..=('<li class="'..top.name..topselect..'"><span>'..web.link(top.uri,top.title)..dropicon..'</span><ul>'..subhtml..'</ul></li>');
foreach(var top in topnav) {
let subnav = top.subpages;
let subhtml='';
let dropicon='';
let topselect='';
foreach(var sub in subnav) {
let subhtml..=('<li class="'..sub.name..'">'..web.link(sub.uri,sub.title)..'</li>')
}
if (#subnav > 0) {
let dropicon='<span class="dropicon">v</span>';
}
if (string.contains(page.uri,top.uri)) {
let topselect=' selected ';
}
let navhtml..=('<li class="'..top.name..topselect..'"><span>'..web.link(top.uri,top.title)..dropicon..'</span><ul>'..subhtml..'</ul></li>');
}
If you were to leave this output without the additional Jquery code you would have a bulleted list of a page and it's subpages. Now for the sanity of the readers I'm not going to step through Jquery. All you should know is that it is completely customizable and I created color and style variables at the top to easily modify the look and feel of the menu. You can view a full output of the DekiScript SOURCE with the Jquery at http://developer.mindtouch.com/User:Howleyda/DekiScript_dropdown_menu/Source. To utilize this code simply copy and page the content into a new template on your MindTouch site.
The very last part which is most important is including the template in a wiki page. If you want to show the root menu use:
This looks like an incredibly useful script! I am running Deki Wiki 8.05.1 and followed these instructions over and over and after save of the Template I get an error as follows:
line 7, column 11: "=" expected
this prevents me from using the drop-downs at all. :(
Any ideas as to what this could be? The only difference I can see in your code vs mine... is after it's pasted in you get some HTML encoding of quotes, and ">" symbols.
I would really love to use this very helpful script.
Here are the first 15 lines of the code pasted into the HTML source of my template file:
<h1>Template:DropDown</h1>
<p>{{<br />
var langpath ="/";<br />
if ($path) {<br />
let langpath=$path;<br />
} <br />
var langdir = wiki.getpage(langpath);<br /> <br />
var topnav = langdir.subpages;<br />
var subnav;<br />
var navhtml;<br />
var tophtml;<br />
var subhtml;<br />
var dropicon;<br />
var topselect;<br /> <br />
foreach(var top in topnav) {<br /> edited 08:45, 28 Dec 2008
The problem is probably that the "var" lines without an "=" sign. Up until 8.08.1, the notation had to be "var x = null;" instead of "var x;". Try adding "= null" to each variable declaration.
You can also exclude pages from showing up in the menu. This example line would be added directly after declaring the var "topnav" to exclude a page named "TEST":
let topnav = list.select(topnav, " $.title!='TEST' ");
This was posted in the forums by user "lktest" andthe URLof the forum post which answered this question is:
http://forums.developer.mindtouch.com/showthread.php?t=4914 edited 10:58, 10 Feb 2009
For sorting, if you want to order pages via this method here (http://wiki.developer.mindtouch.com/index.php?title=MindTouch_Deki/FAQ/Page_Management/How_do_I...Control_page_order_in_the_navigation_panel%3F) then sorting by title won't work. If you change the 'title' to 'name' in this line:
This is very cool! However, it has a security problem in that it can expose the names of top-level directories and their children that the user does not have access to. In keeping with the security model which hides directories in the left-hand navbar if the user is restricted, how would you modify this script to exclude privileged directories?
- Peter
line 7, column 11: "=" expected
this prevents me from using the drop-downs at all. :(
Any ideas as to what this could be? The only difference I can see in your code vs mine... is after it's pasted in you get some HTML encoding of quotes, and ">" symbols.
I would really love to use this very helpful script.
Here are the first 15 lines of the code pasted into the HTML source of my template file:
<h1>Template:DropDown</h1>
<p>{{<br />
var langpath ="/";<br />
if ($path) {<br />
let langpath=$path;<br />
} <br />
var langdir = wiki.getpage(langpath);<br /> <br />
var topnav = langdir.subpages;<br />
var subnav;<br />
var navhtml;<br />
var tophtml;<br />
var subhtml;<br />
var dropicon;<br />
var topselect;<br /> <br />
foreach(var top in topnav) {<br /> edited 08:45, 28 Dec 2008
Change "var topnav = langdir.subpages;" to "var topnav = list.sort(map.values(langdir.subpages), 'title', false);"
And then also this line:
"let subnav = top.subpages;" to "let subnav = list.sort(map.values(top.subpages), 'title', false);"
You can also exclude pages from showing up in the menu. This example line would be added directly after declaring the var "topnav" to exclude a page named "TEST":
let topnav = list.select(topnav, " $.title!='TEST' ");
This was posted in the forums by user "lktest" andthe URLof the forum post which answered this question is:
http://forums.developer.mindtouch.com/showthread.php?t=4914 edited 10:58, 10 Feb 2009
"let subnav = top.subpages;" to "let subnav = list.sort(map.values(top.subpages), 'title', false);"
then it'll sort them the way you want it.
I also wanted to comment and say that this is a visually fantastic bit of coding, Damien. Good stuff.
- Peter
Would It work for showing the table of contents (toc) instead of the wikitree?