Last active
February 21, 2016 10:21
-
-
Save aercolino/6d872e178a101ed2a878 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function IndentationToList(options) { | |
'use strict'; | |
return Main(options); | |
function Interpolate(template, variables) { | |
var names = Object.keys(variables); | |
var result = template; | |
for (var i = 0, iTop = names.length; i < iTop; i++) { | |
var name = names[i]; | |
var value = variables[name]; | |
var re = RegExp('\\b' + name + '\\b', 'g'); | |
result = result.replace(re, value); | |
} | |
return result; | |
} | |
function Parse(lineText, lineNumber, indent) { | |
var aux = lineText.split(indent); | |
var result = { | |
'level': aux.length - 1, | |
'label': aux.pop(), | |
'line': lineNumber | |
}; | |
return result; | |
} | |
function InitOptions(options) { | |
var defaultOptions = { | |
indent: ' ', | |
templates: { | |
folder: '<li><label class="toggle" for="ID">FOLDER</label> <input type="checkbox" id="ID" /><ol>CONTENT</ol></li>', | |
file: '<li class="file" href="">FILE</li>', | |
folderWithoutContent: '<li><label for="ID">FOLDER</label> <input type="checkbox" id="ID" /></li>', | |
contentWithoutFolder: '<ol>CONTENT</ol>' | |
}, | |
text: '' | |
}; | |
if (options.constructor.name !== 'Object') { | |
options = { | |
text: String(options) | |
}; | |
} | |
var result = { | |
indent: options.indent || defaultOptions.indent, | |
templates: options.templates || defaultOptions.templates, | |
text: options.text | |
}; | |
return result; | |
} | |
function InitLines(text) { | |
var result = text. | |
replace('\r\n', '\n'). // remove double line endings | |
replace('\r', '\n'). // remove odd line endings | |
split('\n'). // get lines | |
filter(function(value) { // remove empty lines | |
return value !== ''; | |
}); | |
result.push(''); // append an empty line | |
return result; | |
} | |
function InitStack() { | |
var result = { | |
data: [], | |
lastParent: null, | |
lastChild: null | |
}; | |
result.lastChild = result.data; | |
return result; | |
} | |
function AddChild(stack, info, asSibling) { | |
var last = asSibling ? stack.lastParent : stack.lastChild; | |
info.parent = last; | |
var node = [info]; | |
last.push(node); | |
stack.lastParent = info.parent; | |
stack.lastChild = node; | |
} | |
function ChildrenToHTML(stack, templates, contentWithoutFolder) { | |
var last = stack.lastParent; | |
var parent = last[0]; | |
var children = last.splice(1); // last.length === 1 | |
var items = []; | |
for (var i = 0, iTop = children.length; i < iTop; i++) { | |
var child = children[i]; | |
var isStringified = typeof child[0] === 'string'; | |
var isFolder = ! isStringified && child[0].label.search(/\/$/) >= 0; | |
var isFile = ! isStringified && ! isFolder; | |
var html; | |
switch (true) { | |
case isStringified: | |
html = child[0]; | |
break; | |
case isFolder: | |
html = Interpolate(templates.folderWithoutContent, {ID: 'folder_' + child[0].line, FOLDER: child[0].label}); | |
break; | |
case isFile: | |
html = Interpolate(templates.file, {FILE: child[0].label}); | |
break; | |
} | |
items.push(html); | |
} | |
var template = contentWithoutFolder ? templates.contentWithoutFolder : templates.folder; | |
last[0] = Interpolate(template, {ID: 'folder_' + parent.line, FOLDER: parent.label, CONTENT: items.join('')}); | |
stack.lastParent = parent.parent; | |
stack.lastChild = last; | |
} | |
function ParentsToHTML(stack, templates) { | |
stack.data.pop(); // remove the last child (due to the last empty line) | |
stack.data.unshift({ // prepend a fictitious parent to easily wrap up all real parents | |
label: '', | |
level: -1, | |
line: '', | |
parent: null | |
}); | |
ChildrenToHTML(stack, templates, true); | |
var result = stack.lastChild[0]; | |
return result; | |
} | |
function Main(options) { | |
options = InitOptions(options); | |
if (! options.text) { | |
return ''; | |
} | |
var lines = InitLines(options.text); | |
var stack = InitStack(); | |
var level = -1; | |
for (var i = 0, iTop = lines.length; i < iTop; i++) { | |
var line = lines[i]; | |
var Parsed = Parse(line, i, options.indent); | |
switch (true) { | |
case Parsed.level > level: | |
AddChild(stack, Parsed); | |
level += 1; // go one level in | |
break; | |
case Parsed.level === level: | |
AddChild(stack, Parsed, true); | |
break; | |
case Parsed.level < level: | |
ChildrenToHTML(stack, options.templates); | |
level -= 1; // go only one level out, so that ChildrenToHTML can be called again (if any) | |
i -= 1; // still need to process the current line | |
break; | |
} | |
} | |
// thanks to the last empty line, all children have been stringified by now | |
var result = ParentsToHTML(stack, options.templates); | |
return result; | |
} | |
} | |
// SAMPLE TEXT | |
//--- | |
// components/ | |
// login/ | |
// login.html | |
// login.js | |
// register/ | |
// register.html | |
// register.js | |
//=== | |
// SAMPLE USAGE | |
//--- | |
// var text = document.getElementById('text').innerHTML; | |
// var converted = IndentationToList(text); | |
// document.getElementById('tree').innerHTML = converted; | |
//=== | |
// CSS | |
//--- | |
// See http://www.thecssninja.com/css/css-tree-menu for a suitable CSS | |
// Otherwise, try this, which I derived from that, by removing icons. | |
//--- | |
// ol.tree | |
// { | |
// padding: 0; | |
// width: 300px; | |
// } | |
// li | |
// { | |
// position: relative; | |
// margin-left: -15px; | |
// list-style: none; | |
// } | |
// li.file | |
// { | |
// } | |
// li.file a | |
// { | |
// color: #4C4C4C; | |
// padding-left: 21px; | |
// text-decoration: none; | |
// display: block; | |
// } | |
// li input | |
// { | |
// position: absolute; | |
// left: 0; | |
// margin-left: 0; | |
// opacity: 0; | |
// z-index: 2; | |
// cursor: pointer; | |
// height: 1em; | |
// width: 1em; | |
// top: 0; | |
// } | |
// li input + ol | |
// { | |
// margin: -0.938em 0 0 -44px; /* 15px */ | |
// height: 1em; | |
// } | |
// li input + ol > li { display: none; margin-left: -14px !important; padding-left: 1px; } | |
// li label | |
// { | |
// display: block; | |
// color:navy; | |
// } | |
// li label.toggle | |
// { | |
// cursor: pointer; | |
// font-weight: 600; | |
// } | |
// li input:checked + ol | |
// { | |
// margin: -1.25em 0 0 -44px; /* 20px */ | |
// padding: 1.563em 0 0 80px; | |
// height: auto; | |
// } | |
// li input:checked + ol > li { display: block; margin: 0 0 0.125em; /* 2px */} | |
// li input:checked + ol > li:last-child { margin: 0 0 0.063em; /* 1px */ } | |
//=== |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment