Compare commits
No commits in common. "2ddb1281c1c892ad30e15f8d16ddc46331917461" and "276a77b67e6b35caf9a19e92d70d5b609b2011a3" have entirely different histories.
2ddb1281c1
...
276a77b67e
|
@ -1,9 +1,9 @@
|
||||||
"""
|
"""
|
||||||
Logic for managing documents.
|
Logic for operations that depend on a whole collection of documents.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from redstring.parser import load, DocumentTag, DocumentTab, Document, TabOptions, TagOptions
|
from redstring.parser import load, TagOptions, DocumentTag, TabOptions, DocumentTab, Document
|
||||||
|
|
||||||
|
|
||||||
def generate_index_document(directory: str) -> Document:
|
def generate_index_document(directory: str) -> Document:
|
||||||
|
@ -17,18 +17,16 @@ def generate_index_document(directory: str) -> Document:
|
||||||
document: Document = load(f)
|
document: Document = load(f)
|
||||||
|
|
||||||
# Check if this document specifies a tab, and create it if necessary.
|
# Check if this document specifies a tab, and create it if necessary.
|
||||||
if category_tag := document.get_tag('category'):
|
category = document.get_tag('category')
|
||||||
category = category_tag.value
|
if not category:
|
||||||
else:
|
|
||||||
category = 'index'
|
category = 'index'
|
||||||
if category not in categories:
|
if category not in categories:
|
||||||
categories[category] = {}
|
categories[category] = {}
|
||||||
category_tab = categories[category]
|
category_tab = categories[category]
|
||||||
|
|
||||||
# Check if this document specifies a topic, and create it if necessary.
|
# Check if this document specifies a topic, and create it if necessary.
|
||||||
if topic_tag := document.get_tag('topic'):
|
topic = document.get_tag('topic')
|
||||||
topic = topic_tag.value
|
if not topic:
|
||||||
else:
|
|
||||||
topic = 'uncategorized'
|
topic = 'uncategorized'
|
||||||
if '.' in topic:
|
if '.' in topic:
|
||||||
topic, subtopic = topic.split('.', maxsplit=1)
|
topic, subtopic = topic.split('.', maxsplit=1)
|
||||||
|
@ -64,36 +62,8 @@ def generate_index_document(directory: str) -> Document:
|
||||||
docs = sorted(categories[category][topic], key=lambda x: x[0])
|
docs = sorted(categories[category][topic], key=lambda x: x[0])
|
||||||
doc_links = map(document_link, docs)
|
doc_links = map(document_link, docs)
|
||||||
value = '- ' + '<br>- '.join(doc_links)
|
value = '- ' + '<br>- '.join(doc_links)
|
||||||
built_tags.append(DocumentTag(topic, value))
|
built_tags.append(DocumentTag(topic, value, TagOptions(), []))
|
||||||
|
|
||||||
built_tabs.append(DocumentTab(category, built_tags))
|
built_tabs.append(DocumentTab(category, built_tags, TabOptions()))
|
||||||
|
|
||||||
return Document(built_tabs)
|
return Document(built_tabs)
|
||||||
|
|
||||||
|
|
||||||
def generate_default_document(doc_id) -> Document:
|
|
||||||
"""
|
|
||||||
Generate a blank document.
|
|
||||||
"""
|
|
||||||
return Document(tabs=[
|
|
||||||
DocumentTab(
|
|
||||||
name='tags',
|
|
||||||
tags=[
|
|
||||||
DocumentTag('id', doc_id),
|
|
||||||
DocumentTag('title', ''),
|
|
||||||
DocumentTag('author', ''),
|
|
||||||
DocumentTag('date', ''),
|
|
||||||
DocumentTag('source', ''),
|
|
||||||
DocumentTag('summary', ''),
|
|
||||||
DocumentTag('category', 'index', TagOptions(private=True)),
|
|
||||||
DocumentTag('topic', 'uncategorized', TagOptions(private=True)),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
DocumentTab(
|
|
||||||
name='notes',
|
|
||||||
tags=[
|
|
||||||
DocumentTag('notes', ''),
|
|
||||||
],
|
|
||||||
options=TabOptions(private=True, hide_names=True)
|
|
||||||
)
|
|
||||||
])
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ Logic for reading and writing documents from files.
|
||||||
import argparse
|
import argparse
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import json
|
import json
|
||||||
from typing import Any, List, IO, Optional
|
from typing import Any, List, IO
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -23,9 +23,6 @@ class TabOptions:
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
self.options: dict = OrderedDict(**kwargs)
|
self.options: dict = OrderedDict(**kwargs)
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
return self.options
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def priority(self) -> int:
|
def priority(self) -> int:
|
||||||
"""Priority determines tab order."""
|
"""Priority determines tab order."""
|
||||||
|
@ -63,10 +60,13 @@ class TagOptions:
|
||||||
_PRIVATE_KEY = 'private'
|
_PRIVATE_KEY = 'private'
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
self.options: dict = OrderedDict(**kwargs)
|
self.options = OrderedDict(**kwargs)
|
||||||
|
# Tag value is a hyperlink
|
||||||
def to_json(self):
|
self.hyperlink: bool = kwargs.get('hyperlink', False)
|
||||||
return self.options
|
# Tag value contains redstring interlinks
|
||||||
|
self.interlink: bool = kwargs.get('interlink', False)
|
||||||
|
# Hide the tag from unauthenticated viewers
|
||||||
|
self.private: bool = kwargs.get('private', False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hyperlink(self) -> bool:
|
def hyperlink(self) -> bool:
|
||||||
|
@ -98,22 +98,10 @@ class DocumentSubtag:
|
||||||
"""
|
"""
|
||||||
A keyvalue describing a document subject.
|
A keyvalue describing a document subject.
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(self, name: str, value: str, options: TagOptions) -> None:
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
value: str,
|
|
||||||
options: Optional[TagOptions] = None
|
|
||||||
) -> None:
|
|
||||||
self.name: str = name
|
self.name: str = name
|
||||||
self.value: str = value
|
self.value: str = value
|
||||||
self.options: TagOptions = options if options is not None else TagOptions()
|
self.options: TagOptions = options
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
return OrderedDict(
|
|
||||||
name=self.name,
|
|
||||||
value=self.value,
|
|
||||||
options=self.options.to_json(),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DocumentTag:
|
class DocumentTag:
|
||||||
|
@ -124,43 +112,23 @@ class DocumentTag:
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
value: str,
|
value: str,
|
||||||
options: Optional[TagOptions] = None,
|
options: TagOptions,
|
||||||
subtags: Optional[List[DocumentSubtag]] = None
|
subtags: List[DocumentSubtag]
|
||||||
) -> None:
|
) -> None:
|
||||||
self.name: str = name
|
self.name: str = name
|
||||||
self.value: str = value
|
self.value = value
|
||||||
self.options: TagOptions = options if options is not None else TagOptions()
|
self.options = options
|
||||||
self.subtags: list = subtags if subtags is not None else []
|
self.subtags = subtags
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
return OrderedDict(
|
|
||||||
name=self.name,
|
|
||||||
value=self.value,
|
|
||||||
options=self.options.to_json(),
|
|
||||||
subtags=[subtag.to_json() for subtag in self.subtags],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DocumentTab:
|
class DocumentTab:
|
||||||
"""
|
"""
|
||||||
A division of tags within a document.
|
A division of tags within a document.
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(self, name: str, tags: List[DocumentTag], options: TabOptions) -> None:
|
||||||
self,
|
self.name = name
|
||||||
name: str,
|
|
||||||
tags: List[DocumentTag],
|
|
||||||
options: Optional[TabOptions] = None
|
|
||||||
) -> None:
|
|
||||||
self.name: str = name
|
|
||||||
self.tags: List[DocumentTag] = tags
|
self.tags: List[DocumentTag] = tags
|
||||||
self.options: TabOptions = options if options is not None else TabOptions()
|
self.options: TabOptions = options
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
return OrderedDict(
|
|
||||||
name=self.name,
|
|
||||||
tags=[tag.to_json() for tag in self.tags],
|
|
||||||
options=self.options.to_json(),
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tag(self, name: str):
|
def get_tag(self, name: str):
|
||||||
for tag in self.tags:
|
for tag in self.tags:
|
||||||
|
@ -168,11 +136,6 @@ class DocumentTab:
|
||||||
return tag
|
return tag
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_tag_value(self, name: str, default: str):
|
|
||||||
if tag := self.get_tag(name):
|
|
||||||
return tag.value
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
class Document:
|
class Document:
|
||||||
"""
|
"""
|
||||||
|
@ -181,9 +144,6 @@ class Document:
|
||||||
def __init__(self, tabs: List[DocumentTab]) -> None:
|
def __init__(self, tabs: List[DocumentTab]) -> None:
|
||||||
self.tabs: List[DocumentTab] = tabs
|
self.tabs: List[DocumentTab] = tabs
|
||||||
|
|
||||||
def to_json(self):
|
|
||||||
return [tab.to_json() for tab in self.tabs]
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self.tabs.__iter__()
|
return self.tabs.__iter__()
|
||||||
|
|
||||||
|
@ -200,11 +160,6 @@ class Document:
|
||||||
return tag
|
return tag
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_tag_value(self, name: str, default: str):
|
|
||||||
if tag := self.get_tag(name):
|
|
||||||
return tag.value
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Parsing functions
|
# Parsing functions
|
||||||
|
@ -221,6 +176,16 @@ def load(fd: IO) -> Document:
|
||||||
return parse_document_from_json(parsed_json)
|
return parse_document_from_json(parsed_json)
|
||||||
|
|
||||||
|
|
||||||
|
def loads(string: str) -> Document:
|
||||||
|
"""
|
||||||
|
Load a document from a string.
|
||||||
|
"""
|
||||||
|
parsed_json = json.loads(string, object_pairs_hook=OrderedDict)
|
||||||
|
if not isinstance(parsed_json, list):
|
||||||
|
raise ValueError('Parsing as document, expected list')
|
||||||
|
return parse_document_from_json(parsed_json)
|
||||||
|
|
||||||
|
|
||||||
def parse_document_from_json(parsed_json: list) -> Document:
|
def parse_document_from_json(parsed_json: list) -> Document:
|
||||||
"""
|
"""
|
||||||
Parses JSON into a Document object.
|
Parses JSON into a Document object.
|
||||||
|
@ -317,14 +282,6 @@ def parse_subtag_from_json(subtag_json: dict) -> DocumentSubtag:
|
||||||
return DocumentSubtag(name, value, options)
|
return DocumentSubtag(name, value, options)
|
||||||
|
|
||||||
|
|
||||||
def dump(doc: Document, fd: IO):
|
|
||||||
"""
|
|
||||||
Write a document to a file descriptor.
|
|
||||||
"""
|
|
||||||
dumped_json = doc.to_json()
|
|
||||||
json.dump(dumped_json, fd, indent=2)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# CLI functions
|
# CLI functions
|
||||||
#
|
#
|
||||||
|
|
|
@ -10,13 +10,12 @@ from flask import (
|
||||||
Flask,
|
Flask,
|
||||||
redirect,
|
redirect,
|
||||||
render_template,
|
render_template,
|
||||||
request,
|
|
||||||
safe_join,
|
safe_join,
|
||||||
url_for,
|
url_for,
|
||||||
)
|
)
|
||||||
|
|
||||||
from redstring.library import generate_index_document, generate_default_document
|
from redstring.library import generate_index_document
|
||||||
from redstring.parser import load, dump, DocumentTab, DocumentTag, TagOptions, DocumentSubtag
|
from redstring.parser import load
|
||||||
|
|
||||||
|
|
||||||
CONFIG_ENVVAR = 'REDSTRING_CONFIG'
|
CONFIG_ENVVAR = 'REDSTRING_CONFIG'
|
||||||
|
@ -24,18 +23,18 @@ CONFIG_ENVVAR = 'REDSTRING_CONFIG'
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET'])
|
@app.route('/')
|
||||||
def root():
|
def root():
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/index/', methods=['GET'])
|
@app.route('/index/')
|
||||||
def index():
|
def index():
|
||||||
document = generate_index_document(current_app.config['root'])
|
document = generate_index_document(current_app.config['root'])
|
||||||
return render_template('doc.jinja', document=document, index=True)
|
return render_template('doc.jinja', document=document, index=True)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/doc/<document_id>', methods=['GET'])
|
@app.route('/doc/<document_id>')
|
||||||
def document(document_id):
|
def document(document_id):
|
||||||
doc_path = safe_join(current_app.config['root'], f'{document_id}.json')
|
doc_path = safe_join(current_app.config['root'], f'{document_id}.json')
|
||||||
if not os.path.isfile(doc_path):
|
if not os.path.isfile(doc_path):
|
||||||
|
@ -45,57 +44,6 @@ def document(document_id):
|
||||||
return render_template('doc.jinja', document=doc, index=False)
|
return render_template('doc.jinja', document=doc, index=False)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/new/', methods=['GET'])
|
|
||||||
def new():
|
|
||||||
document_id = 'new'
|
|
||||||
new_doc = generate_default_document(document_id)
|
|
||||||
doc_path = safe_join(current_app.config['root'], f'{document_id}.json')
|
|
||||||
with open(doc_path, 'w') as f:
|
|
||||||
dump(new_doc, f)
|
|
||||||
return redirect(url_for('document', document_id=document_id))
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/edit/<document_id>', methods=['GET', 'POST'])
|
|
||||||
def edit(document_id):
|
|
||||||
# Load the document to edit
|
|
||||||
doc_path = safe_join(current_app.config['root'], f'{document_id}.json')
|
|
||||||
if not os.path.isfile(doc_path):
|
|
||||||
return abort(404)
|
|
||||||
with open(doc_path) as f:
|
|
||||||
doc = load(f)
|
|
||||||
|
|
||||||
# Check for structural change requests
|
|
||||||
if add := request.args.get('add'):
|
|
||||||
if add == 'tab':
|
|
||||||
new_tab = DocumentTab('newtab', [])
|
|
||||||
doc.tabs.append(new_tab)
|
|
||||||
with open(doc_path, 'w') as f:
|
|
||||||
dump(doc, f)
|
|
||||||
return redirect(url_for('edit', document_id=document_id))
|
|
||||||
elif add == 'tag':
|
|
||||||
if tab_name := request.args.get('tab'):
|
|
||||||
tab = doc.get_tab(tab_name)
|
|
||||||
new_tag = DocumentTag('tag', '', TagOptions(private=True))
|
|
||||||
tab.tags.append(new_tag)
|
|
||||||
with open(doc_path, 'w') as f:
|
|
||||||
dump(doc, f)
|
|
||||||
return redirect(url_for('edit', document_id=document_id))
|
|
||||||
return abort(400)
|
|
||||||
elif add == 'subtag':
|
|
||||||
if tag_name := request.args.get('tag'):
|
|
||||||
tag = doc.get_tag(tag_name)
|
|
||||||
new_subtag = DocumentSubtag('subtag', '', TagOptions(private=True))
|
|
||||||
tag.subtags.append(new_subtag)
|
|
||||||
with open(doc_path, 'w') as f:
|
|
||||||
dump(doc, f)
|
|
||||||
return redirect(url_for('edit', document_id=document_id))
|
|
||||||
return abort(400)
|
|
||||||
|
|
||||||
# Otherwise, return the editor page
|
|
||||||
else:
|
|
||||||
return render_template('edit.jinja', document=doc, index=False)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="Run the redstring server.")
|
parser = argparse.ArgumentParser(description="Run the redstring server.")
|
||||||
parser.add_argument("--config", help="Config file path.")
|
parser.add_argument("--config", help="Config file path.")
|
||||||
|
|
|
@ -101,20 +101,12 @@
|
||||||
table.page-table td:nth-child(2) {
|
table.page-table td:nth-child(2) {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
table.page-table a {
|
table.page-table td:nth-child(2) a {
|
||||||
color: #8af;
|
color: #8af;
|
||||||
}
|
}
|
||||||
table.page-table a:visited {
|
table.page-table td:nth-child(2) a:visited {
|
||||||
color: #88f;
|
color: #88f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Edit page styling */
|
|
||||||
input.tag-name {
|
|
||||||
}
|
|
||||||
textarea.tag-value {
|
|
||||||
resize: none;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
{% block page_scripts %}{% endblock %}
|
{% block page_scripts %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends 'base.jinja' %}
|
{% extends 'base.jinja' %}
|
||||||
|
|
||||||
{% set page_title = document.get_tag_value('title', document.get_tag('id').value) -%}
|
{% set page_title = 'tmp' -%}
|
||||||
{% set page_summary = document.get_tag_value('summary', '') %}
|
{% set page_summary = 'tmpp' %}
|
||||||
|
|
||||||
{% block page_scripts %}
|
{% block page_scripts %}
|
||||||
<script>
|
<script>
|
||||||
|
@ -38,26 +38,22 @@ window.onload = function () {
|
||||||
<div id="{{ tab.name }}-page" class="tab-page{% if selected %} tab-page-selected{% endif %}">
|
<div id="{{ tab.name }}-page" class="tab-page{% if selected %} tab-page-selected{% endif %}">
|
||||||
<table id="{{ tab.name }}-page-table" class="page-table">
|
<table id="{{ tab.name }}-page-table" class="page-table">
|
||||||
{% for tag in tab.tags %}
|
{% for tag in tab.tags %}
|
||||||
{%- if not tag.options.private -%}
|
|
||||||
<tr {% if tab.options.hide_names %}class="hide-tag-name"{% endif %}>
|
<tr {% if tab.options.hide_names %}class="hide-tag-name"{% endif %}>
|
||||||
<td>{{ tag.name }}</td>
|
<td>{{ tag.name }}</td>
|
||||||
<td>{{ make_tag_value(tag) }}</td>
|
<td>{{ make_tag_value(tag) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{%- endif -%}
|
|
||||||
{% for subtag in tag.subtags %}
|
{% for subtag in tag.subtags %}
|
||||||
{%- if not tag.options.private and not subtag.options.private -%}
|
|
||||||
<tr {% if tab.options.hide_names %}class="hide-tag-name"{% endif %}>
|
<tr {% if tab.options.hide_names %}class="hide-tag-name"{% endif %}>
|
||||||
<td>{% if loop.last %}└{% else %}├{% endif %} {{ subtag.name }}</td>
|
<td>{% if loop.last %}└{% else %}├{% endif %} {{ subtag.name }}</td>
|
||||||
<td>{{ make_tag_value(subtag) }}</td>
|
<td>{{ make_tag_value(subtag) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{%- endif -%}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{# TODO: tag.interlink #}
|
{# TODO: tag.interlink and tag.private support #}
|
||||||
{% macro make_tag_value(tag) -%}
|
{% macro make_tag_value(tag) -%}
|
||||||
{%- if tag.options.hyperlink -%}
|
{%- if tag.options.hyperlink -%}
|
||||||
<a href="{{ tag.value }}">{{ tag.value }}</a>
|
<a href="{{ tag.value }}">{{ tag.value }}</a>
|
||||||
|
@ -66,19 +62,8 @@ window.onload = function () {
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{# TODO: tab.priority support #}
|
{# TODO: tab.priority and tab.private support #}
|
||||||
{% block page_content %}
|
{% block page_content %}
|
||||||
<div id="tabs">
|
<div id="tabs">{% for tab in document %}{{ make_content_tab(tab, loop.first) }}{% endfor %}{% if not index %}<div id="index" class="tab tab-right"><a href="/index/">index</a></div>{% endif %}</div>
|
||||||
{%- for tab in document -%}
|
{% for tab in document %}{{ make_tab_page(tab, loop.first) }}{% endfor %}
|
||||||
{%- if not tab.options.private -%}
|
|
||||||
{{ make_content_tab(tab, loop.first) }}
|
|
||||||
{%- endif -%}
|
|
||||||
{%- endfor -%}
|
|
||||||
{%- if not index -%}
|
|
||||||
<div id="index" class="tab tab-right"><a href="/index/">index</a></div>
|
|
||||||
{%- endif -%}
|
|
||||||
</div>
|
|
||||||
{% for tab in document -%}
|
|
||||||
{{ make_tab_page(tab, loop.first) }}
|
|
||||||
{%- endfor -%}
|
|
||||||
{% endblock page_content %}
|
{% endblock page_content %}
|
|
@ -1,135 +0,0 @@
|
||||||
{% extends 'base.jinja' %}
|
|
||||||
|
|
||||||
{% set page_title = document.get_tag_value('title', document.get_tag('id').value) -%}
|
|
||||||
{% set page_summary = document.get_tag_value('summary', '') %}
|
|
||||||
|
|
||||||
{% block page_scripts %}
|
|
||||||
<script>
|
|
||||||
var newTabCounter = 0;
|
|
||||||
|
|
||||||
function selectTab(name) {
|
|
||||||
|
|
||||||
let tab = document.getElementById("tab-" + name);
|
|
||||||
if (tab)
|
|
||||||
{
|
|
||||||
// Unselect all tabs and content
|
|
||||||
Array.from(document.getElementsByClassName("tab-content"))
|
|
||||||
.forEach(e => e.classList.remove("tab-down"));
|
|
||||||
Array.from(document.getElementsByClassName("tab-page"))
|
|
||||||
.forEach(e => e.classList.remove("tab-page-selected"));
|
|
||||||
// Select the new tab and content
|
|
||||||
tab.classList.add("tab-down");
|
|
||||||
let content = document.getElementById(name + "-page");
|
|
||||||
content.classList.add("tab-page-selected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTab() {
|
|
||||||
let newTabName = "newtab" + ++newTabCounter;
|
|
||||||
let tabsDiv = document.querySelector("#tabs");
|
|
||||||
let wrapper = document.querySelector("#wrapper");
|
|
||||||
let newTabTab = document.querySelector("#newtab");
|
|
||||||
|
|
||||||
// Add tab div
|
|
||||||
let newTab = document.createElement("div");
|
|
||||||
newTab.id = "tab-" + newTabName;
|
|
||||||
newTab.classList = "tab tab-content";
|
|
||||||
newTab.contenteditable = true;
|
|
||||||
newTab.innerText = newTabName;
|
|
||||||
newTab.onclick = function() { selectTab(newTabName); };
|
|
||||||
tabsDiv.insertBefore(newTab, newTabTab);
|
|
||||||
|
|
||||||
// Add page div
|
|
||||||
let newPage = document.createElement("div");
|
|
||||||
newPage.id = newTabName + "-page";
|
|
||||||
newPage.classList = "tag-page";
|
|
||||||
let newPageTable = document.createElement("table");
|
|
||||||
newPageTable.id = newTabName + "-page-table";
|
|
||||||
newPageTable.classList = "page-table";
|
|
||||||
wrapper.appendChild(newPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
// Respect fragment as a tab selector shortcut
|
|
||||||
if (window.location.hash) {
|
|
||||||
selectTab(window.location.hash.substring(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock page_scripts %}
|
|
||||||
|
|
||||||
{% macro make_content_tab(tab, selected) -%}
|
|
||||||
<div id="tab-{{ tab.name }}" class="tab tab-content{% if selected %} tab-down{% endif %}{% if index %} tab-right{% endif %}" onclick="javascript:selectTab('{{ tab.name }}')">{{ tab.name }}</div>
|
|
||||||
{%- endmacro %}
|
|
||||||
|
|
||||||
{% macro make_tab_page(tab, selected) %}
|
|
||||||
<div id="{{ tab.name }}-page" class="tab-page{% if selected %} tab-page-selected{% endif %}">
|
|
||||||
<table id="{{ tab.name }}-page-table" class="page-table">
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td><i>tab name</i></td>
|
|
||||||
<td><div contenteditable>{{ tab.name }}</div></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{% for tag in tab.tags %}
|
|
||||||
<tr>
|
|
||||||
{%- if tag.name == 'id' -%}
|
|
||||||
<td>{{ tag.name }}</td>
|
|
||||||
{%- else -%}
|
|
||||||
<td><div contenteditable>{{ tag.name }}</div></td>
|
|
||||||
{% endif %}
|
|
||||||
<td><div contenteditable>{{ tag.value }}</div></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{%- if tag.subtags -%}
|
|
||||||
│
|
|
||||||
{%- else -%}
|
|
||||||
<a href="/edit/{{ document.get_tag('id').value }}?add=subtag&tag={{ tag.name }}">└ +</a>
|
|
||||||
{%- endif -%}</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{% for subtag in tag.subtags %}
|
|
||||||
<tr>
|
|
||||||
<td><div contenteditable>{{ subtag.name }}</div></td>
|
|
||||||
<td><div contenteditable>{{ subtag.value }}</div></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{%- if not loop.last -%}
|
|
||||||
│
|
|
||||||
{%- else -%}
|
|
||||||
<a href="/edit/{{ document.get_tag('id').value }}?add=subtag&tag={{ tag.name }}">└ +</a>
|
|
||||||
{%- endif -%}
|
|
||||||
</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td><a href="/edit/{{ document.get_tag('id').value }}?add=tag&tab={{ tab.name }}">Add tag</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{# TODO: tab.priority support #}
|
|
||||||
{% block page_content %}
|
|
||||||
<div id="tabs">
|
|
||||||
{%- for tab in document -%}
|
|
||||||
{{ make_content_tab(tab, loop.first) }}
|
|
||||||
{%- endfor -%}
|
|
||||||
<div id="newtab" class="tab"><a href="/edit/{{ document.get_tag('id').value }}?add=tab">+</div>
|
|
||||||
<div id="index" class="tab tab-right"><a href="/index/">index</a></div>
|
|
||||||
</div>
|
|
||||||
{% for tab in document -%}
|
|
||||||
{{ make_tab_page(tab, loop.first) }}
|
|
||||||
{%- endfor -%}
|
|
||||||
{% endblock page_content %}
|
|
Loading…
Reference in New Issue