Skip to content

Instantly share code, notes, and snippets.

@kaleguy
Last active March 4, 2017 23:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kaleguy/41ea7024e22b45ff2f4160e5c2cc3682 to your computer and use it in GitHub Desktop.
Save kaleguy/41ea7024e22b45ff2f4160e5c2cc3682 to your computer and use it in GitHub Desktop.
Leo Tree Viewer with Vue
license: MIT
height: 480
border: yes

Leo is, among other things, an opensource outlining editor.

This code snippet shows how to display the tree portion of a simple Leo file.

View on bl.ocks.org

Leo files are XML, the snippet transforms the Leo XML into JSON via XSL and then uses Vue.js to create the tree.

<?xml version="1.0" encoding="utf-8"?>
<!-- Created by Leo: http://leoeditor.com/leo_toc.html -->
<leo_file xmlns:leo="http://leoeditor.com/namespaces/leo-python-editor/1.1" >
<leo_header file_format="2" tnodes="0" max_tnode_index="0" clone_windows="0"/>
<globals body_outline_ratio="0.5" body_secondary_ratio="0.5">
<global_window_position top="50" left="50" height="500" width="700"/>
<global_log_window_position top="0" left="0" height="0" width="0"/>
</globals>
<preferences/>
<find_panel_settings/>
<vnodes>
<v t="josephorr.20170228222411.2" a="E"><vh>Top</vh>
<v t="josephorr.20170228222452.1"><vh>Regions</vh>
<v t="josephorr.20170228222513.1"><vh>North America</vh>
<v t="josephorr.20170228225033.1"><vh>Canada</vh></v>
<v t="josephorr.20170228225040.1"><vh>USA</vh></v>
</v>
<v t="josephorr.20170228222521.1" a="E"><vh>South America</vh>
<v t="josephorr.20170228224939.1"><vh>Bolivia</vh></v>
<v t="josephorr.20170228224946.1"><vh>Brazil</vh></v>
</v>
<v t="josephorr.20170228222526.1" a="E"><vh>Europe</vh>
<v t="josephorr.20170228224925.1"><vh>France</vh></v>
<v t="josephorr.20170228224930.1"><vh>Italy</vh></v>
</v>
</v>
<v t="josephorr.20170228222534.1"><vh>Vegetables</vh>
<v t="josephorr.20170228222538.1"><vh>Broccoli</vh></v>
<v t="josephorr.20170228222548.1"><vh>Spinach</vh></v>
</v>
</v>
</vnodes>
<tnodes>
<t tx="josephorr.20170228222411.2"></t>
<t tx="josephorr.20170228222452.1"></t>
<t tx="josephorr.20170228222513.1"></t>
<t tx="josephorr.20170228222521.1"></t>
<t tx="josephorr.20170228222526.1"></t>
<t tx="josephorr.20170228222534.1"></t>
<t tx="josephorr.20170228222538.1"></t>
<t tx="josephorr.20170228222548.1"></t>
<t tx="josephorr.20170228224925.1"></t>
<t tx="josephorr.20170228224930.1"></t>
<t tx="josephorr.20170228224939.1"></t>
<t tx="josephorr.20170228224946.1"></t>
<t tx="josephorr.20170228225033.1"></t>
<t tx="josephorr.20170228225040.1"></t>
</tnodes>
</leo_file>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
<style type="text/css">
body {
font-family: Menlo, Consolas, monospace;
}
.item {
cursor: pointer;
}
.bold {
font-weight: bold;
}
ul {
padding-left: 1em;
line-height: 1.5em;
list-style-type: none;
}
</style>
</head>
<body>
<script type="text/x-template" id="item-template">
<li>
<div
:class="{bold: isFolder}"
@click="toggle">
<span v-if="isFolder">{{open ? '▼' : '▶'}}</span>
{{model.name}}
</div>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="model in model.children"
:model="model">
</item>
</ul>
</li>
</script>
<ul id="demo">
<item
class="item"
:model="treeData">
</item>
</ul>
</body>
<script src="tree.js"></script>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="v">
<xsl:variable name="t" select="@t"/>
{
"name":"<xsl:value-of select="vh"/>",
"text":"<xsl:value-of select="//t[@tx=$t]"/>",
"children":[<xsl:apply-templates select="v"/>]
}
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:template>
<xsl:template match="text()"><xsl:value-of select="."/></xsl:template>
</xsl:stylesheet>
Vue.component('item', {
template: '#item-template',
props: {
model: Object
},
data: function(){
return {
open: false
}
},
computed: {
isFolder: function() {
return this.model.children && this.model.children.length;
}
},
methods: {
toggle: function(){
if (this.isFolder){
this.open = ! this.open;
}
}
}
});
function loadXMLDoc(filename, type){
var xhttp = new XMLHttpRequest();
xhttp.open('GET', filename, false); // synchronous
xhttp.send('');
return xhttp['response' + type];
}
function displayResult(){
var xmlString = loadXMLDoc('example.leo', 'Text');
var oParser = new DOMParser();
var xml = oParser.parseFromString(xmlString,'text/xml');
var xsl = loadXMLDoc('leo.xsl', 'XML');
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsl);
var resultDocument = xsltProcessor.transformToFragment(xml, document);
var data = resultDocument.textContent;
data = data.replace(/,\s+$/,''); // kludge to get rid of trailing comma
data = JSON.parse(data);
var demo = new Vue({
el: '#demo',
data: {
treeData: data
}
})
}
displayResult();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment