antville/code/Story/$Story.skin

300 lines
10 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<% #listItem %>
<tr>
<td class='uk-text-right'><% story.comments count %></td>
<td class='uk-width-1-2 av-overflow'>
<a href='<% story.href %>' title='<% ngettext '{0} request' '{0} requests' <% story.requests %> %>' data-uk-tooltip="{pos: 'top-left'}">
<% story.abstract %>
</a>
</td>
<td class='uk-text-truncate'><% story.creator %></td>
<td class='uk-text-truncate' title='<% story.modified short %>' data-uk-tooltip="{pos: 'top-left'}">
<% story.modified text %>
</td>
<td>
<% //if <% story.status %> is closed then "<i class='uk-text-muted uk-icon-lock'></i>" %>
<% //if <% story.status %> is public then "<i class='uk-text-muted uk-icon-unlock'></i>" %>
<% if <% story.status %> is shared then "<i class='uk-text-muted uk-icon-group'></i>" %>
<% if <% story.status %> is open then "<i class='uk-text-muted uk-icon-globe'></i>" %>
<% if <% story.tags count %> is 0 then '' else <% story.tags prefix="<i class='uk-text-muted uk-icon-tags' data-uk-tooltip title='" suffix="'></i>" %> %>
</td>
<td class='uk-text-nowrap'>
<% story.link status <% if <% story.status %> is closed then lock else unlock prefix="<i class='uk-icon-" suffix="'></i>" %> %>
<% story.link mode <% if <% story.mode %> is hidden then eye-slash else eye prefix="<i class='uk-icon-" suffix="'></i>" %> %>
</td>
<td class='uk-text-nowrap uk-text-right'>
<a href='javascript:' data-value="<% story.macro %>" data-text='<% gettext "Press CTRL & C to copy to clipboard." %>' class='av-clipboard-copy'><i class='uk-icon-clipboard'></i></a>
<% story.link delete "<i class='uk-icon-trash-o'></i>" %>
<% story.link edit "<i class='uk-icon-pencil'></i>" %>
</td>
</tr>
<% #links %>
<% if <% story.commentMode %> is open then
<% if <% site.commentMode %> is enabled then
<% story.skin Story#links %>
else
<% story.skin Story#permalink %>
%>
else
<% story.skin Story#permalink %>
%>
<% #tagged %>
<% story.skin Story#preview %>
<% #search %>
<dt><% this.abstract | this.link %></dt>
<dd>Posted <% this.created text %> by <% this.creator %>.</dd>
<% #update %>
<dt><% gettext '{0} {1} {2} {3} on {4} // e.g. “{3 days ago} {admin} {posted} {a story} on {Antville}”' <% story.site.modified text prefix=<strong> suffix=</strong> %> <% story.modifier %> <% if <% story.created %> is <% story.modified %> then <% gettext 'posted // has posted' %> else <% gettext 'updated // has updated' %> %><% if <% story.type %> is Story then <% gettext 'a story // accusative' %> else <% gettext 'a comment // accusative' %> %> <% story.site.title | story.site.link %> %></dt>
<dd><% story.abstract | story.link class='av-concealed-link' %></dd>
<% #embed %>
<div class='uk-margin-bottom'>
<div class='uk-panel uk-panel-box'>
<% story.title prefix="<h2 class='uk-panel-title'>" suffix=</h2> %>
<% story.text | story.format %>
</div>
<% story.source %>
</div>
<% #editor %>
<script>
// Load additonal scripts and styles for UIkits HTML editor plugin
document.addEventListener("alpine:init", () => {
const _form = document.querySelector('#av-story-form');
if (!_form) return;
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '<% root.static ../../styles/editor.min.css %>';
link.onload = loadScripts;
document.head.appendChild(link);
function loadScripts() {
const script = document.createElement('script');
script.src = '<% root.static ../../scripts/editor.min.js %>';
script.onload = setupEditor;
document.head.appendChild(script);
}
function setupEditor() {
UIkit.plugin('htmleditor', 'antville', {
init: function(editor) {
const addAction = function(name, replace, mode) {
editor.off('action.' + name);
editor.on('action.' + name, function() {
editor[mode === 'replaceLine' ? mode : 'replaceSelection'](replace);
});
};
editor.addButtons({
file: {
title: "<% gettext 'File' %>",
label: '<i class="uk-icon-file-o"></i>'
},
poll: {
title: "<% gettext 'Poll' %>",
label: '<i class="uk-icon-bar-chart"></i>'
}
});
addAction('file', "\<% file '$1' %\>");
addAction('image', "\<% image '$1' %\>");
addAction('link', "\<% link 'https://' '$1' %\>");
addAction('poll', "\<% poll '$1' %\>");
}
});
const _url = '<% site.href backup.js %>';
let _editor;
let _lastKeyPressed;
let _submitElement;
const enableOnBeforeUnload = function() {
window.onbeforeunload = function(event) {
return event.returnValue = "<% gettext 'You are going to discard unsaved content.' %>";
};
};
const disableOnBeforeUnload = function() {
window.onbeforeunload = null;
};
const update = function() {
if (!window.onbeforeunload) enableOnBeforeUnload();
_lastKeyPressed = new Date();
};
const scheduler = function() {
const now = new Date();
if (_lastKeyPressed && (now - _lastKeyPressed > 1000)) {
const data = {};
_form.querySelectorAll('input, select, textarea').forEach(function(element) {
if (element.id) data[element.id] = element.value.trim();
});
if (_editor) {
data.text = _editor.editor.getValue().trim();
}
Antville.http('post', _url, { data: data });
_lastKeyPressed = null;
}
setTimeout(scheduler, 100);
};
// TODO: This would probably be better done server-side
const parse = function(data) {
const html = document.createElement('div');
const assets = [];
html.innerHTML = data;
html.querySelectorAll('[src]').forEach(function(element) {
const url = element.src;
let re = new RegExp('\.(?:gif|jpg|jpeg|png)$', 'i');
if (url.match(re)) assets.push(url);
re = new RegExp('^.+://[^/]*(youtube)[^/]+/embed/([^/?]+)', 'i');
let match = url.match(re);
if (match) {
const input = document.createElement('input');
input.hidden = true;
input.name = 'og:video';
input.value = 'https://' + match[1] + '.com/v/' + match[2];
_form.appendChild(input);
assets.push('https://img.' + match[1] + '.com/vi/' + match[2] + '/0.jpg');
}
});
if (!assets.length) resubmit();
assets.forEach(function(url, index) {
const img = new Image;
img.src = url;
img.onload = function() {
const input = document.createElement('input');
input.hidden = true;
input.name = 'og:image';
input.value = url;
_form.appendChild(input);
assets.shift();
if (!assets.length) resubmit();
};
img.onerror = function() {
assets.shift();
if (!assets.length) resubmit();
};
});
};
const resubmit = function() {
// Turn the clicked (or default) submit button into a hidden value to be sure it is submitted again
const submit = _submitElement || _form.find(':submit:first');
const input = document.createElement('input');
_submitElement = null;
input.hidden = true;
input.name = submit.name;
input.value = submit.value;
_form.appendChild(input);
_form.submit();
};
// UIkits editor requires jQuery
_editor = UIkit.htmleditor(document.querySelector('#text'), {
mode: 'tab',
markdown: true,
lblPreview: '<% gettext Preview %>',
lblCodeview: '<% gettext HTML %>',
lblMarkedview: '<% gettext Markdown %>',
toolbar: ['bold', 'italic', 'strike', 'link', 'image', 'file', 'poll', 'blockquote', 'listUl', 'listOl']
});
_editor.editor.on('keyup', update);
_editor.htmleditor.on('click', '.uk-htmleditor-button-code, .uk-htmleditor-button-preview', function(event) {
if (event.currentTarget.classList.contains('uk-htmleditor-button-preview')) {
// TODO: Still a good idea to render the macros via AJAX call?
const content = _editor.editor.getValue();
if (!content) return;
Antville.http('post', '<% stories.href render.json %>', {
data: { content: content },
callback: function(data, error) {
if (error) return;
document.querySelector('.uk-htmleditor-preview div').innerHTML = data;
}
});
}
});
_form.addEventListener('keyup', update);
_form.querySelectorAll('.uk-button').forEach(function(element) {
element.addEventListener('click', disableOnBeforeUnload);
});
_form.addEventListener('submit', function(event) {
event.preventDefault();
const form = event.currentTarget;
const submit = form.querySelector('[type=submit]');
if (form.dataset.isProcessed) return;
form.dataset.isProcessed = true;
_submitElement = submit;
const elementList = form.querySelectorAll('button, input, select, textarea');
const buffer = Array.prototype.slice.call(elementList).map(function(element) {
return element.value;
});
const raw = encodeURIComponent(buffer.join(' '));
Antville.http('post', '<% stories.href render.json %>', {
data: { content: buffer.join(' ') },
callback: parse
});
});
// Restore backup text if available and if the editor refers to a new story
if (location.href.match('/create$') && '<% session.backup %>') {
Antville.http('GET', _url, {
callback: function(json, error) {
const data = JSON.parse(json);
Object.keys(data).forEach(function(key) {
document.querySelector('#' + key).value = data[key];
});
_editor && _editor.editor.setValue(data.text);
}
});
}
scheduler();
}
});
</script>