Move some stuff around.
|
@ -1,6 +1,10 @@
|
||||||
punishedponepaste
|
punishedponepaste
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
# Requirements
|
||||||
|
* An HTTP server that can perform URL rewriting and execute PHP 8 code (eg: nginx with php8-fpm,) and the following PHP extensions: pdo, openssl, gd, mbstring, redis.
|
||||||
|
* A MySQL-compatible server (eg: MariaDB 10.)
|
||||||
|
* A Redis server.
|
||||||
|
|
||||||
# Building the JS
|
# Building the JS
|
||||||
When you change the JS, you need to rebuild it. `assets/bundle.js` is used in dev, `assets/bundle.min.js` is used in production.
|
When you change the JS, you need to rebuild it. `assets/bundle.js` is used in dev, `assets/bundle.min.js` is used in production.
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once('includes/common.php');
|
|
||||||
require_once('includes/functions.php');
|
|
||||||
|
|
||||||
|
|
||||||
// Theme
|
|
||||||
$page_template = 'event';
|
|
||||||
$page_title = 'Event';
|
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
|
|
@ -4,9 +4,8 @@ use JetBrains\PhpStorm\ArrayShape;
|
||||||
|
|
||||||
#[ArrayShape(['code' => "mixed|string", 'image_src' => "string"])]
|
#[ArrayShape(['code' => "mixed|string", 'image_src' => "string"])]
|
||||||
function captcha($color, $mode, $mul, $allowed) : array {
|
function captcha($color, $mode, $mul, $allowed) : array {
|
||||||
|
$bg_path = __DIR__ . '/../public/assets/img/captcha/';
|
||||||
$bg_path = __DIR__ . '/../assets/img/captcha/';
|
$font_path = __DIR__ . '/../public/assets/fonts/';
|
||||||
$font_path = __DIR__ . '/../assets/fonts/';
|
|
||||||
|
|
||||||
if ($mul == "on") {
|
if ($mul == "on") {
|
||||||
$captcha_config = array(
|
$captcha_config = array(
|
||||||
|
@ -89,8 +88,7 @@ function captcha($color, $mode, $mul, $allowed) : array {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate HTML for image src
|
// Generate HTML for image src
|
||||||
$image_src = substr(__FILE__, strlen(realpath($_SERVER['DOCUMENT_ROOT']))) . '?_CAPTCHA&_R=' . urlencode(rand());
|
$image_src = '/captcha?_CAPTCHA&_R=' . urlencode(rand());
|
||||||
$image_src = '/' . ltrim(preg_replace('/\\\\/', '/', $image_src), '/');
|
|
||||||
|
|
||||||
$_SESSION['_CAPTCHA']['config'] = serialize($captcha_config);
|
$_SESSION['_CAPTCHA']['config'] = serialize($captcha_config);
|
||||||
|
|
||||||
|
@ -122,60 +120,3 @@ if (!function_exists('hex2rgb')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the image
|
|
||||||
if (isset($_GET['_CAPTCHA'])) {
|
|
||||||
session_start();
|
|
||||||
|
|
||||||
$captcha_config = unserialize(@$_SESSION['_CAPTCHA']['config']);
|
|
||||||
if (!$captcha_config)
|
|
||||||
exit();
|
|
||||||
|
|
||||||
// Pick random background, get info, and start captcha
|
|
||||||
$background = $captcha_config['backgrounds'][rand(0, count($captcha_config['backgrounds']) - 1)];
|
|
||||||
list($bg_width, $bg_height, $bg_type, $bg_attr) = getimagesize($background);
|
|
||||||
|
|
||||||
$captcha = imagecreatefrompng($background);
|
|
||||||
|
|
||||||
$color = hex2rgb($captcha_config['color']);
|
|
||||||
$color = imagecolorallocate($captcha, $color['r'], $color['g'], $color['b']);
|
|
||||||
|
|
||||||
// Determine text angle
|
|
||||||
$angle = rand($captcha_config['angle_min'], $captcha_config['angle_max']) * (rand(0, 1) == 1 ? -1 : 1);
|
|
||||||
|
|
||||||
// Select font randomly
|
|
||||||
$font = $captcha_config['fonts'][rand(0, count($captcha_config['fonts']) - 1)];
|
|
||||||
|
|
||||||
// Verify font file exists
|
|
||||||
if (!file_exists($font)) {
|
|
||||||
die('Font file not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the font size
|
|
||||||
$font_size = rand($captcha_config['min_font_size'], $captcha_config['max_font_size']);
|
|
||||||
$text_box_size = imagettfbbox($font_size, $angle, $font, $captcha_config['code']);
|
|
||||||
|
|
||||||
// Determine text position
|
|
||||||
$box_width = abs($text_box_size[6] - $text_box_size[2]);
|
|
||||||
$box_height = abs($text_box_size[5] - $text_box_size[1]);
|
|
||||||
$text_pos_x_min = 0;
|
|
||||||
$text_pos_x_max = ($bg_width) - ($box_width);
|
|
||||||
$text_pos_x = rand($text_pos_x_min, $text_pos_x_max);
|
|
||||||
$text_pos_y_min = $box_height;
|
|
||||||
$text_pos_y_max = ($bg_height) - ($box_height / 2);
|
|
||||||
$text_pos_y = rand($text_pos_y_min, $text_pos_y_max);
|
|
||||||
|
|
||||||
// Draw shadow
|
|
||||||
if ($captcha_config['shadow']) {
|
|
||||||
$shadow_color = hex2rgb($captcha_config['shadow_color']);
|
|
||||||
$shadow_color = imagecolorallocate($captcha, $shadow_color['r'], $shadow_color['g'], $shadow_color['b']);
|
|
||||||
imagettftext($captcha, $font_size, $angle, $text_pos_x + $captcha_config['shadow_offset_x'], $text_pos_y + $captcha_config['shadow_offset_y'], $shadow_color, $font, $captcha_config['code']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw text
|
|
||||||
imagettftext($captcha, $font_size, $angle, $text_pos_x, $text_pos_y, $color, $font, $captcha_config['code']);
|
|
||||||
|
|
||||||
// Output image
|
|
||||||
header("Content-type: image/png");
|
|
||||||
imagepng($captcha);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('includes/common.php');
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
use PonePaste\Models\Paste;
|
use PonePaste\Models\Paste;
|
||||||
|
|
||||||
|
|
||||||
$date = date('jS F Y');
|
|
||||||
|
|
||||||
// Temp count for untagged pastes
|
// Temp count for untagged pastes
|
||||||
$total_untagged = Paste::doesntHave('tags')->count();
|
$total_untagged = Paste::doesntHave('tags')->count();
|
||||||
|
|
||||||
updatePageViews();
|
updatePageViews();
|
||||||
|
|
||||||
// Theme
|
|
||||||
$page_template = 'archive';
|
$page_template = 'archive';
|
||||||
$page_title = 'Pastes Archive';
|
$page_title = 'Pastes Archive';
|
||||||
$script_bundles[] = 'archive';
|
$script_bundles[] = 'archive';
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
|
||||||
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
514
public/assets/bundle/archive.js
Normal file
|
@ -0,0 +1,514 @@
|
||||||
|
const $ = function(selector) {
|
||||||
|
return document.querySelector(selector);
|
||||||
|
};
|
||||||
|
|
||||||
|
const $$ = function(selector) {
|
||||||
|
return document.querySelectorAll(selector) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeEl = function(html) {
|
||||||
|
const template = document.createElement('template');
|
||||||
|
|
||||||
|
template.innerHTML = html.trim();
|
||||||
|
|
||||||
|
return template.content.firstChild;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearEl = function(el) {
|
||||||
|
while (el.firstChild) {
|
||||||
|
el.removeChild(el.firstChild);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleEl = function(el) {
|
||||||
|
if (el.classList.contains('is-hidden')) {
|
||||||
|
el.classList.remove('is-hidden');
|
||||||
|
} else {
|
||||||
|
el.classList.add('is-hidden');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const escape = function(unsafe) {
|
||||||
|
return unsafe
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
};
|
||||||
|
|
||||||
|
const whenReady = function(funcp) {
|
||||||
|
if (document.readyState !== 'loading') {
|
||||||
|
funcp();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', funcp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SimplePaginator {
|
||||||
|
constructor(element) {
|
||||||
|
this.element = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach(pageCallback) {
|
||||||
|
this.element.addEventListener('click', evt => {
|
||||||
|
if (evt.target && evt.target.classList.contains('paginator__button')) {
|
||||||
|
pageCallback(+evt.target.dataset.page);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update(totalRecords, perPage, currentPage) {
|
||||||
|
clearEl(this.element);
|
||||||
|
|
||||||
|
/* First and last page in existence */
|
||||||
|
const firstPage = 0;
|
||||||
|
const lastPage = Math.floor(totalRecords / perPage); // ish?
|
||||||
|
const numPagesToShow = 2;
|
||||||
|
|
||||||
|
if (lastPage === firstPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First and last page the main paginator will show */
|
||||||
|
const firstPageShow = (currentPage - numPagesToShow) < firstPage ? firstPage : (currentPage - numPagesToShow);
|
||||||
|
const lastPageShow = (currentPage + numPagesToShow) > lastPage ? lastPage : (currentPage + numPagesToShow);
|
||||||
|
|
||||||
|
/* Whether to show the first and last pages in existence at the ends of the paginator */
|
||||||
|
const showFirstPage = (Math.abs(firstPage - currentPage)) > (numPagesToShow);
|
||||||
|
const showLastPage = (Math.abs(lastPage - currentPage)) > (numPagesToShow);
|
||||||
|
|
||||||
|
|
||||||
|
const prevButtonDisabled = currentPage === firstPage ? 'disabled' : '';
|
||||||
|
|
||||||
|
/* Previous button */
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button previous" ${prevButtonDisabled} data-page="${currentPage - 1}">Previous</button>`
|
||||||
|
));
|
||||||
|
|
||||||
|
/* First page button */
|
||||||
|
if (showFirstPage) {
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button" data-page="${firstPage}">${firstPage}</button>`
|
||||||
|
));
|
||||||
|
this.element.appendChild(makeEl(`<span class="ellipsis">…</span>`));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "window" buttons */
|
||||||
|
for (let i = firstPageShow; i <= lastPageShow; i++) {
|
||||||
|
const selected = (i === currentPage ? 'paginator__button--selected' : '');
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button ${selected}" data-page="${i}">${i}</button>`
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Last page button */
|
||||||
|
if (showLastPage) {
|
||||||
|
this.element.appendChild(makeEl(`<span class="ellipsis">…</span>`));
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button" data-page="${lastPage}">${lastPage}</button>`
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextButtonDisabled = currentPage === lastPage ? 'disabled' : '';
|
||||||
|
/* Next button */
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button next" ${nextButtonDisabled} data-page="${currentPage + 1}">Next</button>`
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataTable {
|
||||||
|
constructor(element, options) {
|
||||||
|
this.element = element;
|
||||||
|
this.container = element.parentElement;
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
this.ajaxCallback = options.ajaxCallback;
|
||||||
|
this.data = [];
|
||||||
|
this.unfilteredData = [];
|
||||||
|
|
||||||
|
this.totalRecords = -1;
|
||||||
|
this.perPage = 20;
|
||||||
|
this.currentPage = 0;
|
||||||
|
|
||||||
|
this.paginator = new SimplePaginator(this.container.querySelector('.paginator'));
|
||||||
|
|
||||||
|
this.filterCallback = options.filterCallback;
|
||||||
|
this.sortField = null;
|
||||||
|
this.sortDir = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach() {
|
||||||
|
this.filterField = this.container.querySelector('input.search');
|
||||||
|
if (this.filterField && this.filterCallback) {
|
||||||
|
this.filterField.addEventListener('keyup', evt => {
|
||||||
|
if (evt.target) {
|
||||||
|
this._updateFilter(evt.target.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.options.preFilter) {
|
||||||
|
this.filterField.value = this.options.preFilter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.perPageField = this.container.querySelector('select[name=per_page]');
|
||||||
|
|
||||||
|
if (this.perPageField) {
|
||||||
|
this.perPageField.addEventListener('change', evt => {
|
||||||
|
this.perPage = Number(evt.target.value);
|
||||||
|
this._updatePage(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = this.element.querySelector('tr.paginator__sort');
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
header.addEventListener('click', evt => {
|
||||||
|
const target = evt.target;
|
||||||
|
|
||||||
|
if (!target.dataset.sortField) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sortField) {
|
||||||
|
const elem = this.element.querySelector(`th[data-sort-field=${this.sortField}]`);
|
||||||
|
elem.classList.remove('paginator__sort--down');
|
||||||
|
elem.classList.remove('paginator__sort--up');
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateSort(target.dataset.sortField, !this.sortDir);
|
||||||
|
|
||||||
|
target.classList.add(this.sortDir ? 'paginator__sort--up' : 'paginator__sort--down');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.paginator.attach(this._updatePage.bind(this));
|
||||||
|
this._loadEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load the requested data from the server, and when done, update the DOM. */
|
||||||
|
_loadEntries() {
|
||||||
|
new Promise(this.ajaxCallback)
|
||||||
|
.then(data => {
|
||||||
|
this.element.classList.remove('hidden');
|
||||||
|
this.unfilteredData = data.data;
|
||||||
|
this._updateFilter(this.options.preFilter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the DOM to reflect the current state of the data we have loaded */
|
||||||
|
_updateEntries(data) {
|
||||||
|
this.data = data;
|
||||||
|
this.totalRecords = this.data.length;
|
||||||
|
|
||||||
|
const bodyElement = this.element.querySelector('tbody');
|
||||||
|
clearEl(bodyElement);
|
||||||
|
|
||||||
|
const firstIndex = (this.perPage * this.currentPage);
|
||||||
|
const lastIndex = (firstIndex + this.perPage) > this.totalRecords ? this.totalRecords : (firstIndex + this.perPage);
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = firstIndex; i < lastIndex; i++) {
|
||||||
|
const rowElem = makeEl(this.options.rowCallback(this.data[i]));
|
||||||
|
rowElem.classList.add(i % 2 === 0 ? 'odd' : 'even');
|
||||||
|
|
||||||
|
bodyElement.appendChild(rowElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.paginator.update(this.totalRecords, this.perPage, this.currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updatePage(n) {
|
||||||
|
this.currentPage = n;
|
||||||
|
this.paginator.update(this.totalRecords, this.perPage, this.currentPage);
|
||||||
|
this._updateEntries(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateFilter(query) {
|
||||||
|
/* clearing the query */
|
||||||
|
if (query === null || query === '') {
|
||||||
|
this._updateEntries(this.unfilteredData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = [];
|
||||||
|
for (const datum of this.unfilteredData) {
|
||||||
|
if (this.filterCallback(datum, query)) {
|
||||||
|
data.push(datum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updatePage(0);
|
||||||
|
this._updateEntries(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateSort(field, direction) {
|
||||||
|
this.sortField = field;
|
||||||
|
this.sortDir = direction;
|
||||||
|
|
||||||
|
let newEntries = [...this.data].sort((a, b) => {
|
||||||
|
let sorter = 0;
|
||||||
|
|
||||||
|
if (a[field] > b[field]) {
|
||||||
|
sorter = 1;
|
||||||
|
} else if (a[field] < b[field]) {
|
||||||
|
sorter = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!direction) {
|
||||||
|
sorter = -sorter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorter;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._updatePage(0);
|
||||||
|
this._updateEntries(newEntries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dumbFilterCallback = (datum, query) => {
|
||||||
|
if (!query) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (datum.title.indexOf(query) !== -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is inefficient */
|
||||||
|
for (const tag of datum.tags) {
|
||||||
|
if (tag.name.toLowerCase() === query.toLowerCase()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagsToHtml = (tags) => {
|
||||||
|
|
||||||
|
return tags.map(tagData => {
|
||||||
|
let tagColorClass;
|
||||||
|
if (tagData.name.indexOf('nsfw') !== -1) {
|
||||||
|
tagColorClass = 'is-danger';
|
||||||
|
} else if (tagData.name.indexOf('safe') !== -1) {
|
||||||
|
tagColorClass = 'is-success';
|
||||||
|
} else if (tagData.name.indexOf('/') !== -1) {
|
||||||
|
tagColorClass = 'is-primary';
|
||||||
|
} else {
|
||||||
|
tagColorClass = 'is-info';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<a href="/archive?q=${tagData.slug}">
|
||||||
|
<span class="tag ${tagColorClass}">${escape(tagData.name)}</span>
|
||||||
|
</a>`;
|
||||||
|
}).join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
class TagsInput {
|
||||||
|
constructor(element, options = {}) {
|
||||||
|
this.element = element;
|
||||||
|
this.tags = [];
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
this.maxTags = options.maxTags || 10;
|
||||||
|
this.inputNode = null;
|
||||||
|
this.containerNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach() {
|
||||||
|
this.element.style.display = 'none';
|
||||||
|
|
||||||
|
this.containerNode = makeEl('<div class="tags-input"></div>');
|
||||||
|
this.inputNode = makeEl('<input class="input" type="text" placeholder="10 tags maximum" value="" />');
|
||||||
|
this.containerNode.appendChild(this.inputNode);
|
||||||
|
|
||||||
|
this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);
|
||||||
|
|
||||||
|
/* Load existing tags from input */
|
||||||
|
if (this.element.value) {
|
||||||
|
for (const tag of this.element.value.split(',')) {
|
||||||
|
this.addTag(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle addition and removal of tags via key-presses */
|
||||||
|
this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));
|
||||||
|
|
||||||
|
/* Handle deletions by clicking the delete button */
|
||||||
|
this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
detach() {
|
||||||
|
this.tags.clear();
|
||||||
|
this.containerNode.remove();
|
||||||
|
this.element.style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHiddenInputValue() {
|
||||||
|
this.element.value = this.tags.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTagNode(node) {
|
||||||
|
this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);
|
||||||
|
node.remove();
|
||||||
|
|
||||||
|
/* Below the limit? Make sure the input is enabled. */
|
||||||
|
if (this.tags.length < this.maxTags) {
|
||||||
|
this.inputNode.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addTag(tagValue) {
|
||||||
|
tagValue = tagValue.trim();
|
||||||
|
|
||||||
|
/* Tag value is probably not empty and we don't already have the same tag. */
|
||||||
|
if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {
|
||||||
|
this.tags.push(tagValue.toLowerCase());
|
||||||
|
|
||||||
|
this.inputNode.parentNode.insertBefore(
|
||||||
|
makeEl('<span class="tag is-info" data-value="' + escape(tagValue) + '">' + escape(tagValue) + '<span class="delete is-small" /></span>'),
|
||||||
|
this.inputNode
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Too many tags, disable the input for now. */
|
||||||
|
if (this.tags.length >= this.maxTags) {
|
||||||
|
this.inputNode.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleInputKeyUp(evt) {
|
||||||
|
let tagValue = this.inputNode.value;
|
||||||
|
|
||||||
|
if (evt.key === 'Backspace' && tagValue === '') {
|
||||||
|
// Remove the child
|
||||||
|
if (this.inputNode.previousSibling) {
|
||||||
|
this.deleteTagNode(this.inputNode.previousSibling);
|
||||||
|
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
}
|
||||||
|
} else if (evt.key === ',') {
|
||||||
|
this.addTag(tagValue);
|
||||||
|
|
||||||
|
this.inputNode.value = '';
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleContainerClick(evt) {
|
||||||
|
if (evt.target && evt.target.classList.contains('delete')) {
|
||||||
|
this.deleteTagNode(evt.target.closest('.tag'));
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupSignupModal = () => {
|
||||||
|
const signupButton = $('[data-target~="#signin"],[data-target~="#signup"]');
|
||||||
|
|
||||||
|
if (signupButton) {
|
||||||
|
signupButton.addEventListener('click', () => {
|
||||||
|
$('.modal').classList.add('is-active');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.modal-button-close').addEventListener('click', () => {
|
||||||
|
$('.modal').classList.remove('is-active');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const globalSetup = () => {
|
||||||
|
Array.prototype.forEach.call($$('.js-tag-input'), (el) => {
|
||||||
|
new TagsInput(el).attach();
|
||||||
|
});
|
||||||
|
|
||||||
|
setupSignupModal();
|
||||||
|
|
||||||
|
const embedButton = $('.panel-tools .embed-tool');
|
||||||
|
|
||||||
|
if (embedButton){
|
||||||
|
embedButton.addEventListener('click', (evt) => {
|
||||||
|
if (evt.target && evt.target.closest('.panel-tools')) {
|
||||||
|
toggleEl(evt.target.closest('.panel-tools').querySelector('.panel-embed'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandButton = $('.expand-tool');
|
||||||
|
|
||||||
|
if (expandButton) {
|
||||||
|
expandButton.addEventListener('click', (evt) => {
|
||||||
|
if (evt.target && evt.target.closest('.panel')) {
|
||||||
|
const panel = evt.target.closest('.panel');
|
||||||
|
|
||||||
|
if (panel.classList.contains('panel-fullsize')) {
|
||||||
|
panel.classList.remove('panel-fullsize');
|
||||||
|
} else {
|
||||||
|
panel.classList.add('panel-fullsize');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
|
||||||
|
const $notification = $delete.parentNode;
|
||||||
|
|
||||||
|
$delete.addEventListener('click', () => {
|
||||||
|
$notification.parentNode.removeChild($notification);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hamburger menu
|
||||||
|
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||||
|
if ($navbarBurgers.length > 0) {
|
||||||
|
$navbarBurgers.forEach(el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const target = el.dataset.target;
|
||||||
|
const $target = document.getElementById(target);
|
||||||
|
el.classList.toggle('is-active');
|
||||||
|
$target.classList.toggle('is-active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const preloader = $('.preloader');
|
||||||
|
const main = $('main');
|
||||||
|
|
||||||
|
if (preloader && main) {
|
||||||
|
preloader.remove();
|
||||||
|
main.id = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
whenReady(() => {
|
||||||
|
globalSetup();
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const myParam = urlParams.get('q');
|
||||||
|
const apiUrl = /* myParam !== null ? '/api/ajax_pastes.php?q=' + myParam : */ '/api/ajax_pastes.php';
|
||||||
|
|
||||||
|
const table = new DataTable(document.getElementById('archive'), {
|
||||||
|
ajaxCallback: (resolve) => {
|
||||||
|
fetch(apiUrl)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(resolve);
|
||||||
|
},
|
||||||
|
rowCallback: (rowData) => {
|
||||||
|
return `<tr>
|
||||||
|
<td><a href="/${rowData.id}">${escape(rowData.title)}</a></td>
|
||||||
|
<td><a href="/user/${escape(rowData.author)}">${escape(rowData.author)}</a></td>
|
||||||
|
<td>${tagsToHtml(rowData.tags)}</td>
|
||||||
|
</tr>`;
|
||||||
|
},
|
||||||
|
filterCallback: dumbFilterCallback,
|
||||||
|
preFilter: myParam
|
||||||
|
});
|
||||||
|
table.attach();
|
||||||
|
});
|
2
public/assets/bundle/archive.min.js
vendored
Normal file
1
public/assets/bundle/archive.min.js.map
Normal file
221
public/assets/bundle/generic.js
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
const $ = function(selector) {
|
||||||
|
return document.querySelector(selector);
|
||||||
|
};
|
||||||
|
|
||||||
|
const $$ = function(selector) {
|
||||||
|
return document.querySelectorAll(selector) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeEl = function(html) {
|
||||||
|
const template = document.createElement('template');
|
||||||
|
|
||||||
|
template.innerHTML = html.trim();
|
||||||
|
|
||||||
|
return template.content.firstChild;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleEl = function(el) {
|
||||||
|
if (el.classList.contains('is-hidden')) {
|
||||||
|
el.classList.remove('is-hidden');
|
||||||
|
} else {
|
||||||
|
el.classList.add('is-hidden');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const escape = function(unsafe) {
|
||||||
|
return unsafe
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
};
|
||||||
|
|
||||||
|
const whenReady = function(funcp) {
|
||||||
|
if (document.readyState !== 'loading') {
|
||||||
|
funcp();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', funcp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TagsInput {
|
||||||
|
constructor(element, options = {}) {
|
||||||
|
this.element = element;
|
||||||
|
this.tags = [];
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
this.maxTags = options.maxTags || 10;
|
||||||
|
this.inputNode = null;
|
||||||
|
this.containerNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach() {
|
||||||
|
this.element.style.display = 'none';
|
||||||
|
|
||||||
|
this.containerNode = makeEl('<div class="tags-input"></div>');
|
||||||
|
this.inputNode = makeEl('<input class="input" type="text" placeholder="10 tags maximum" value="" />');
|
||||||
|
this.containerNode.appendChild(this.inputNode);
|
||||||
|
|
||||||
|
this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);
|
||||||
|
|
||||||
|
/* Load existing tags from input */
|
||||||
|
if (this.element.value) {
|
||||||
|
for (const tag of this.element.value.split(',')) {
|
||||||
|
this.addTag(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle addition and removal of tags via key-presses */
|
||||||
|
this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));
|
||||||
|
|
||||||
|
/* Handle deletions by clicking the delete button */
|
||||||
|
this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
detach() {
|
||||||
|
this.tags.clear();
|
||||||
|
this.containerNode.remove();
|
||||||
|
this.element.style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHiddenInputValue() {
|
||||||
|
this.element.value = this.tags.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTagNode(node) {
|
||||||
|
this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);
|
||||||
|
node.remove();
|
||||||
|
|
||||||
|
/* Below the limit? Make sure the input is enabled. */
|
||||||
|
if (this.tags.length < this.maxTags) {
|
||||||
|
this.inputNode.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addTag(tagValue) {
|
||||||
|
tagValue = tagValue.trim();
|
||||||
|
|
||||||
|
/* Tag value is probably not empty and we don't already have the same tag. */
|
||||||
|
if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {
|
||||||
|
this.tags.push(tagValue.toLowerCase());
|
||||||
|
|
||||||
|
this.inputNode.parentNode.insertBefore(
|
||||||
|
makeEl('<span class="tag is-info" data-value="' + escape(tagValue) + '">' + escape(tagValue) + '<span class="delete is-small" /></span>'),
|
||||||
|
this.inputNode
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Too many tags, disable the input for now. */
|
||||||
|
if (this.tags.length >= this.maxTags) {
|
||||||
|
this.inputNode.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleInputKeyUp(evt) {
|
||||||
|
let tagValue = this.inputNode.value;
|
||||||
|
|
||||||
|
if (evt.key === 'Backspace' && tagValue === '') {
|
||||||
|
// Remove the child
|
||||||
|
if (this.inputNode.previousSibling) {
|
||||||
|
this.deleteTagNode(this.inputNode.previousSibling);
|
||||||
|
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
}
|
||||||
|
} else if (evt.key === ',') {
|
||||||
|
this.addTag(tagValue);
|
||||||
|
|
||||||
|
this.inputNode.value = '';
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleContainerClick(evt) {
|
||||||
|
if (evt.target && evt.target.classList.contains('delete')) {
|
||||||
|
this.deleteTagNode(evt.target.closest('.tag'));
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupSignupModal = () => {
|
||||||
|
const signupButton = $('[data-target~="#signin"],[data-target~="#signup"]');
|
||||||
|
|
||||||
|
if (signupButton) {
|
||||||
|
signupButton.addEventListener('click', () => {
|
||||||
|
$('.modal').classList.add('is-active');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.modal-button-close').addEventListener('click', () => {
|
||||||
|
$('.modal').classList.remove('is-active');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const globalSetup = () => {
|
||||||
|
Array.prototype.forEach.call($$('.js-tag-input'), (el) => {
|
||||||
|
new TagsInput(el).attach();
|
||||||
|
});
|
||||||
|
|
||||||
|
setupSignupModal();
|
||||||
|
|
||||||
|
const embedButton = $('.panel-tools .embed-tool');
|
||||||
|
|
||||||
|
if (embedButton){
|
||||||
|
embedButton.addEventListener('click', (evt) => {
|
||||||
|
if (evt.target && evt.target.closest('.panel-tools')) {
|
||||||
|
toggleEl(evt.target.closest('.panel-tools').querySelector('.panel-embed'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandButton = $('.expand-tool');
|
||||||
|
|
||||||
|
if (expandButton) {
|
||||||
|
expandButton.addEventListener('click', (evt) => {
|
||||||
|
if (evt.target && evt.target.closest('.panel')) {
|
||||||
|
const panel = evt.target.closest('.panel');
|
||||||
|
|
||||||
|
if (panel.classList.contains('panel-fullsize')) {
|
||||||
|
panel.classList.remove('panel-fullsize');
|
||||||
|
} else {
|
||||||
|
panel.classList.add('panel-fullsize');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
|
||||||
|
const $notification = $delete.parentNode;
|
||||||
|
|
||||||
|
$delete.addEventListener('click', () => {
|
||||||
|
$notification.parentNode.removeChild($notification);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hamburger menu
|
||||||
|
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||||
|
if ($navbarBurgers.length > 0) {
|
||||||
|
$navbarBurgers.forEach(el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const target = el.dataset.target;
|
||||||
|
const $target = document.getElementById(target);
|
||||||
|
el.classList.toggle('is-active');
|
||||||
|
$target.classList.toggle('is-active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const preloader = $('.preloader');
|
||||||
|
const main = $('main');
|
||||||
|
|
||||||
|
if (preloader && main) {
|
||||||
|
preloader.remove();
|
||||||
|
main.id = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
whenReady(globalSetup);
|
2
public/assets/bundle/generic.min.js
vendored
Normal file
1
public/assets/bundle/generic.min.js.map
Normal file
189
public/assets/bundle/main.js
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
const $ = function(selector) {
|
||||||
|
return document.querySelector(selector);
|
||||||
|
};
|
||||||
|
|
||||||
|
const $$ = function(selector) {
|
||||||
|
return document.querySelectorAll(selector) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeEl = function(html) {
|
||||||
|
const template = document.createElement('template');
|
||||||
|
|
||||||
|
template.innerHTML = html.trim();
|
||||||
|
|
||||||
|
return template.content.firstChild;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleEl = function(el) {
|
||||||
|
if (el.classList.contains('is-hidden')) {
|
||||||
|
el.classList.remove('is-hidden');
|
||||||
|
} else {
|
||||||
|
el.classList.add('is-hidden');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const escape = function(unsafe) {
|
||||||
|
return unsafe
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
};
|
||||||
|
|
||||||
|
const whenReady = function(funcp) {
|
||||||
|
if (document.readyState !== 'loading') {
|
||||||
|
funcp();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', funcp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TagsInput {
|
||||||
|
constructor(element, options = {}) {
|
||||||
|
this.element = element;
|
||||||
|
this.tags = [];
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
this.maxTags = options.maxTags || 10;
|
||||||
|
this.inputNode = null;
|
||||||
|
this.containerNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach() {
|
||||||
|
this.element.style.display = 'none';
|
||||||
|
|
||||||
|
this.containerNode = makeEl('<div class="tags-input"></div>');
|
||||||
|
this.inputNode = makeEl('<input class="input" type="text" placeholder="10 tags maximum" value="" />');
|
||||||
|
this.containerNode.appendChild(this.inputNode);
|
||||||
|
|
||||||
|
this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);
|
||||||
|
|
||||||
|
/* Load existing tags from input */
|
||||||
|
if (this.element.value) {
|
||||||
|
for (const tag of this.element.value.split(',')) {
|
||||||
|
this.addTag(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle addition and removal of tags via key-presses */
|
||||||
|
this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));
|
||||||
|
|
||||||
|
/* Handle deletions by clicking the delete button */
|
||||||
|
this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
detach() {
|
||||||
|
this.tags.clear();
|
||||||
|
this.containerNode.remove();
|
||||||
|
this.element.style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHiddenInputValue() {
|
||||||
|
this.element.value = this.tags.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTagNode(node) {
|
||||||
|
this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);
|
||||||
|
node.remove();
|
||||||
|
|
||||||
|
/* Below the limit? Make sure the input is enabled. */
|
||||||
|
if (this.tags.length < this.maxTags) {
|
||||||
|
this.inputNode.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addTag(tagValue) {
|
||||||
|
tagValue = tagValue.trim();
|
||||||
|
|
||||||
|
/* Tag value is probably not empty and we don't already have the same tag. */
|
||||||
|
if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {
|
||||||
|
this.tags.push(tagValue.toLowerCase());
|
||||||
|
|
||||||
|
this.inputNode.parentNode.insertBefore(
|
||||||
|
makeEl('<span class="tag is-info" data-value="' + escape(tagValue) + '">' + escape(tagValue) + '<span class="delete is-small" /></span>'),
|
||||||
|
this.inputNode
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Too many tags, disable the input for now. */
|
||||||
|
if (this.tags.length >= this.maxTags) {
|
||||||
|
this.inputNode.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleInputKeyUp(evt) {
|
||||||
|
let tagValue = this.inputNode.value;
|
||||||
|
|
||||||
|
if (evt.key === 'Backspace' && tagValue === '') {
|
||||||
|
// Remove the child
|
||||||
|
if (this.inputNode.previousSibling) {
|
||||||
|
this.deleteTagNode(this.inputNode.previousSibling);
|
||||||
|
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
}
|
||||||
|
} else if (evt.key === ',') {
|
||||||
|
this.addTag(tagValue);
|
||||||
|
|
||||||
|
this.inputNode.value = '';
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleContainerClick(evt) {
|
||||||
|
if (evt.target && evt.target.classList.contains('delete')) {
|
||||||
|
this.deleteTagNode(evt.target.closest('.tag'));
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupSignupModal = () => {
|
||||||
|
const signupButton = $('[data-target~="#signin"],[data-target~="#signup"]');
|
||||||
|
|
||||||
|
if (signupButton) {
|
||||||
|
signupButton.addEventListener('click', () => {
|
||||||
|
$('.modal').classList.add('is-active');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.modal-button-close').addEventListener('click', () => {
|
||||||
|
$('.modal').classList.remove('is-active');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
whenReady(() => {
|
||||||
|
Array.prototype.forEach.call($$('.js-tag-input'), (el) => {
|
||||||
|
new TagsInput(el).attach();
|
||||||
|
});
|
||||||
|
|
||||||
|
setupSignupModal();
|
||||||
|
|
||||||
|
const embedButton = $('.panel-tools .embed-tool');
|
||||||
|
|
||||||
|
if (embedButton){
|
||||||
|
embedButton.addEventListener('click', (evt) => {
|
||||||
|
if (evt.target && evt.target.closest('.panel-tools')) {
|
||||||
|
toggleEl(evt.target.closest('.panel-tools').querySelector('.panel-embed'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandButton = $('.expand-tool');
|
||||||
|
|
||||||
|
if (expandButton) {
|
||||||
|
expandButton.addEventListener('click', (evt) => {
|
||||||
|
if (evt.target && evt.target.closest('.panel')) {
|
||||||
|
const panel = evt.target.closest('.panel');
|
||||||
|
|
||||||
|
if (panel.classList.contains('panel-fullsize')) {
|
||||||
|
panel.classList.remove('panel-fullsize');
|
||||||
|
} else {
|
||||||
|
panel.classList.add('panel-fullsize');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
2
public/assets/bundle/main.min.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
function e(e,n){var a="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!a){if(Array.isArray(e)||(a=function(e,n){if(!e)return;if("string"==typeof e)return t(e,n);var a=Object.prototype.toString.call(e).slice(8,-1);"Object"===a&&e.constructor&&(a=e.constructor.name);if("Map"===a||"Set"===a)return Array.from(e);if("Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a))return t(e,n)}(e))||n&&e&&"number"==typeof e.length){a&&(e=a);var i=0,s=function(){};return{s:s,n:function(){return i>=e.length?{done:!0}:{done:!1,value:e[i++]}},e:function(e){throw e},f:s}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,o=!0,l=!1;return{s:function(){a=a.call(e)},n:function(){var e=a.next();return o=e.done,e},e:function(e){l=!0,r=e},f:function(){try{o||null==a.return||a.return()}finally{if(l)throw r}}}}function t(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,a=new Array(t);n<t;n++)a[n]=e[n];return a}function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}var i,s=function(e){return document.querySelector(e)},r=function(e){var t=document.createElement("template");return t.innerHTML=e.trim(),t.content.firstChild},o=function(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")},l=function(){function t(e){var a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};n(this,t),this.element=e,this.tags=[],this.options=a,this.maxTags=a.maxTags||10,this.inputNode=null,this.containerNode=null}var i,s,l;return i=t,(s=[{key:"attach",value:function(){if(this.element.style.display="none",this.containerNode=r('<div class="tags-input"></div>'),this.inputNode=r('<input class="input" type="text" placeholder="10 tags maximum" value="" />'),this.containerNode.appendChild(this.inputNode),this.element.parentNode.insertBefore(this.containerNode,this.element.nextSibling),this.element.value){var t,n=e(this.element.value.split(","));try{for(n.s();!(t=n.n()).done;){var a=t.value;this.addTag(a)}}catch(e){n.e(e)}finally{n.f()}}this.containerNode.addEventListener("keydown",this._handleInputKeyUp.bind(this)),this.containerNode.addEventListener("click",this._handleContainerClick.bind(this))}},{key:"detach",value:function(){this.tags.clear(),this.containerNode.remove(),this.element.style.display="inline-block"}},{key:"updateHiddenInputValue",value:function(){this.element.value=this.tags.join(",")}},{key:"deleteTagNode",value:function(e){this.tags.splice(this.tags.indexOf(e.dataset.value.toLowerCase()),1),e.remove(),this.tags.length<this.maxTags&&(this.inputNode.disabled=!1)}},{key:"addTag",value:function(e){""!==(e=e.trim())&&-1===this.tags.indexOf(e.toLowerCase())&&(this.tags.push(e.toLowerCase()),this.inputNode.parentNode.insertBefore(r('<span class="tag is-info" data-value="'+o(e)+'">'+o(e)+'<span class="delete is-small" /></span>'),this.inputNode),this.tags.length>=this.maxTags&&(this.inputNode.disabled=!0))}},{key:"_handleInputKeyUp",value:function(e){var t=this.inputNode.value;"Backspace"===e.key&&""===t?this.inputNode.previousSibling&&(this.deleteTagNode(this.inputNode.previousSibling),this.updateHiddenInputValue()):","===e.key&&(this.addTag(t),this.inputNode.value="",this.updateHiddenInputValue(),e.preventDefault())}},{key:"_handleContainerClick",value:function(e){e.target&&e.target.classList.contains("delete")&&(this.deleteTagNode(e.target.closest(".tag")),this.updateHiddenInputValue())}}])&&a(i.prototype,s),l&&a(i,l),t}();i=function(){var e,t;Array.prototype.forEach.call((e=".js-tag-input",document.querySelectorAll(e)||[]),(function(e){new l(e).attach()})),(t=s('[data-target~="#signin"],[data-target~="#signup"]'))&&(t.addEventListener("click",(function(){s(".modal").classList.add("is-active")})),s(".modal-button-close").addEventListener("click",(function(){s(".modal").classList.remove("is-active")})));var n=s(".panel-tools .embed-tool");n&&n.addEventListener("click",(function(e){var t;e.target&&e.target.closest(".panel-tools")&&((t=e.target.closest(".panel-tools").querySelector(".panel-embed")).classList.contains("is-hidden")?t.classList.remove("is-hidden"):t.classList.add("is-hidden"))}));var a=s(".expand-tool");a&&a.addEventListener("click",(function(e){if(e.target&&e.target.closest(".panel")){var t=e.target.closest(".panel");t.classList.contains("panel-fullsize")?t.classList.remove("panel-fullsize"):t.classList.add("panel-fullsize")}}))},"loading"!==document.readyState?i():document.addEventListener("DOMContentLoaded",i);
|
||||||
|
//# sourceMappingURL=main.min.js.map
|
1
public/assets/bundle/main.min.js.map
Normal file
577
public/assets/bundle/user_profile.js
Normal file
|
@ -0,0 +1,577 @@
|
||||||
|
const $ = function(selector) {
|
||||||
|
return document.querySelector(selector);
|
||||||
|
};
|
||||||
|
|
||||||
|
const $$ = function(selector) {
|
||||||
|
return document.querySelectorAll(selector) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeEl = function(html) {
|
||||||
|
const template = document.createElement('template');
|
||||||
|
|
||||||
|
template.innerHTML = html.trim();
|
||||||
|
|
||||||
|
return template.content.firstChild;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearEl = function(el) {
|
||||||
|
while (el.firstChild) {
|
||||||
|
el.removeChild(el.firstChild);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleEl = function(el) {
|
||||||
|
if (el.classList.contains('is-hidden')) {
|
||||||
|
el.classList.remove('is-hidden');
|
||||||
|
} else {
|
||||||
|
el.classList.add('is-hidden');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const escape = function(unsafe) {
|
||||||
|
return unsafe
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
};
|
||||||
|
|
||||||
|
const whenReady = function(funcp) {
|
||||||
|
if (document.readyState !== 'loading') {
|
||||||
|
funcp();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', funcp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SimplePaginator {
|
||||||
|
constructor(element) {
|
||||||
|
this.element = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach(pageCallback) {
|
||||||
|
this.element.addEventListener('click', evt => {
|
||||||
|
if (evt.target && evt.target.classList.contains('paginator__button')) {
|
||||||
|
pageCallback(+evt.target.dataset.page);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update(totalRecords, perPage, currentPage) {
|
||||||
|
clearEl(this.element);
|
||||||
|
|
||||||
|
/* First and last page in existence */
|
||||||
|
const firstPage = 0;
|
||||||
|
const lastPage = Math.floor(totalRecords / perPage); // ish?
|
||||||
|
const numPagesToShow = 2;
|
||||||
|
|
||||||
|
if (lastPage === firstPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First and last page the main paginator will show */
|
||||||
|
const firstPageShow = (currentPage - numPagesToShow) < firstPage ? firstPage : (currentPage - numPagesToShow);
|
||||||
|
const lastPageShow = (currentPage + numPagesToShow) > lastPage ? lastPage : (currentPage + numPagesToShow);
|
||||||
|
|
||||||
|
/* Whether to show the first and last pages in existence at the ends of the paginator */
|
||||||
|
const showFirstPage = (Math.abs(firstPage - currentPage)) > (numPagesToShow);
|
||||||
|
const showLastPage = (Math.abs(lastPage - currentPage)) > (numPagesToShow);
|
||||||
|
|
||||||
|
|
||||||
|
const prevButtonDisabled = currentPage === firstPage ? 'disabled' : '';
|
||||||
|
|
||||||
|
/* Previous button */
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button previous" ${prevButtonDisabled} data-page="${currentPage - 1}">Previous</button>`
|
||||||
|
));
|
||||||
|
|
||||||
|
/* First page button */
|
||||||
|
if (showFirstPage) {
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button" data-page="${firstPage}">${firstPage}</button>`
|
||||||
|
));
|
||||||
|
this.element.appendChild(makeEl(`<span class="ellipsis">…</span>`));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "window" buttons */
|
||||||
|
for (let i = firstPageShow; i <= lastPageShow; i++) {
|
||||||
|
const selected = (i === currentPage ? 'paginator__button--selected' : '');
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button ${selected}" data-page="${i}">${i}</button>`
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Last page button */
|
||||||
|
if (showLastPage) {
|
||||||
|
this.element.appendChild(makeEl(`<span class="ellipsis">…</span>`));
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button" data-page="${lastPage}">${lastPage}</button>`
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextButtonDisabled = currentPage === lastPage ? 'disabled' : '';
|
||||||
|
/* Next button */
|
||||||
|
this.element.appendChild(makeEl(
|
||||||
|
`<button class="paginator__button next" ${nextButtonDisabled} data-page="${currentPage + 1}">Next</button>`
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataTable {
|
||||||
|
constructor(element, options) {
|
||||||
|
this.element = element;
|
||||||
|
this.container = element.parentElement;
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
this.ajaxCallback = options.ajaxCallback;
|
||||||
|
this.data = [];
|
||||||
|
this.unfilteredData = [];
|
||||||
|
|
||||||
|
this.totalRecords = -1;
|
||||||
|
this.perPage = 20;
|
||||||
|
this.currentPage = 0;
|
||||||
|
|
||||||
|
this.paginator = new SimplePaginator(this.container.querySelector('.paginator'));
|
||||||
|
|
||||||
|
this.filterCallback = options.filterCallback;
|
||||||
|
this.sortField = null;
|
||||||
|
this.sortDir = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach() {
|
||||||
|
this.filterField = this.container.querySelector('input.search');
|
||||||
|
if (this.filterField && this.filterCallback) {
|
||||||
|
this.filterField.addEventListener('keyup', evt => {
|
||||||
|
if (evt.target) {
|
||||||
|
this._updateFilter(evt.target.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.options.preFilter) {
|
||||||
|
this.filterField.value = this.options.preFilter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.perPageField = this.container.querySelector('select[name=per_page]');
|
||||||
|
|
||||||
|
if (this.perPageField) {
|
||||||
|
this.perPageField.addEventListener('change', evt => {
|
||||||
|
this.perPage = Number(evt.target.value);
|
||||||
|
this._updatePage(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = this.element.querySelector('tr.paginator__sort');
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
header.addEventListener('click', evt => {
|
||||||
|
const target = evt.target;
|
||||||
|
|
||||||
|
if (!target.dataset.sortField) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sortField) {
|
||||||
|
const elem = this.element.querySelector(`th[data-sort-field=${this.sortField}]`);
|
||||||
|
elem.classList.remove('paginator__sort--down');
|
||||||
|
elem.classList.remove('paginator__sort--up');
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateSort(target.dataset.sortField, !this.sortDir);
|
||||||
|
|
||||||
|
target.classList.add(this.sortDir ? 'paginator__sort--up' : 'paginator__sort--down');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.paginator.attach(this._updatePage.bind(this));
|
||||||
|
this._loadEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load the requested data from the server, and when done, update the DOM. */
|
||||||
|
_loadEntries() {
|
||||||
|
new Promise(this.ajaxCallback)
|
||||||
|
.then(data => {
|
||||||
|
this.element.classList.remove('hidden');
|
||||||
|
this.unfilteredData = data.data;
|
||||||
|
this._updateFilter(this.options.preFilter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the DOM to reflect the current state of the data we have loaded */
|
||||||
|
_updateEntries(data) {
|
||||||
|
this.data = data;
|
||||||
|
this.totalRecords = this.data.length;
|
||||||
|
|
||||||
|
const bodyElement = this.element.querySelector('tbody');
|
||||||
|
clearEl(bodyElement);
|
||||||
|
|
||||||
|
const firstIndex = (this.perPage * this.currentPage);
|
||||||
|
const lastIndex = (firstIndex + this.perPage) > this.totalRecords ? this.totalRecords : (firstIndex + this.perPage);
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = firstIndex; i < lastIndex; i++) {
|
||||||
|
const rowElem = makeEl(this.options.rowCallback(this.data[i]));
|
||||||
|
rowElem.classList.add(i % 2 === 0 ? 'odd' : 'even');
|
||||||
|
|
||||||
|
bodyElement.appendChild(rowElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.paginator.update(this.totalRecords, this.perPage, this.currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updatePage(n) {
|
||||||
|
this.currentPage = n;
|
||||||
|
this.paginator.update(this.totalRecords, this.perPage, this.currentPage);
|
||||||
|
this._updateEntries(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateFilter(query) {
|
||||||
|
/* clearing the query */
|
||||||
|
if (query === null || query === '') {
|
||||||
|
this._updateEntries(this.unfilteredData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = [];
|
||||||
|
for (const datum of this.unfilteredData) {
|
||||||
|
if (this.filterCallback(datum, query)) {
|
||||||
|
data.push(datum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updatePage(0);
|
||||||
|
this._updateEntries(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateSort(field, direction) {
|
||||||
|
this.sortField = field;
|
||||||
|
this.sortDir = direction;
|
||||||
|
|
||||||
|
let newEntries = [...this.data].sort((a, b) => {
|
||||||
|
let sorter = 0;
|
||||||
|
|
||||||
|
if (a[field] > b[field]) {
|
||||||
|
sorter = 1;
|
||||||
|
} else if (a[field] < b[field]) {
|
||||||
|
sorter = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!direction) {
|
||||||
|
sorter = -sorter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorter;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._updatePage(0);
|
||||||
|
this._updateEntries(newEntries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dumbFilterCallback = (datum, query) => {
|
||||||
|
if (!query) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (datum.title.indexOf(query) !== -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is inefficient */
|
||||||
|
for (const tag of datum.tags) {
|
||||||
|
if (tag.name.toLowerCase() === query.toLowerCase()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagsToHtml = (tags) => {
|
||||||
|
|
||||||
|
return tags.map(tagData => {
|
||||||
|
let tagColorClass;
|
||||||
|
if (tagData.name.indexOf('nsfw') !== -1) {
|
||||||
|
tagColorClass = 'is-danger';
|
||||||
|
} else if (tagData.name.indexOf('safe') !== -1) {
|
||||||
|
tagColorClass = 'is-success';
|
||||||
|
} else if (tagData.name.indexOf('/') !== -1) {
|
||||||
|
tagColorClass = 'is-primary';
|
||||||
|
} else {
|
||||||
|
tagColorClass = 'is-info';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<a href="/archive?q=${tagData.slug}">
|
||||||
|
<span class="tag ${tagColorClass}">${escape(tagData.name)}</span>
|
||||||
|
</a>`;
|
||||||
|
}).join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
class TagsInput {
|
||||||
|
constructor(element, options = {}) {
|
||||||
|
this.element = element;
|
||||||
|
this.tags = [];
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
this.maxTags = options.maxTags || 10;
|
||||||
|
this.inputNode = null;
|
||||||
|
this.containerNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach() {
|
||||||
|
this.element.style.display = 'none';
|
||||||
|
|
||||||
|
this.containerNode = makeEl('<div class="tags-input"></div>');
|
||||||
|
this.inputNode = makeEl('<input class="input" type="text" placeholder="10 tags maximum" value="" />');
|
||||||
|
this.containerNode.appendChild(this.inputNode);
|
||||||
|
|
||||||
|
this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);
|
||||||
|
|
||||||
|
/* Load existing tags from input */
|
||||||
|
if (this.element.value) {
|
||||||
|
for (const tag of this.element.value.split(',')) {
|
||||||
|
this.addTag(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle addition and removal of tags via key-presses */
|
||||||
|
this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));
|
||||||
|
|
||||||
|
/* Handle deletions by clicking the delete button */
|
||||||
|
this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
detach() {
|
||||||
|
this.tags.clear();
|
||||||
|
this.containerNode.remove();
|
||||||
|
this.element.style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHiddenInputValue() {
|
||||||
|
this.element.value = this.tags.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTagNode(node) {
|
||||||
|
this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);
|
||||||
|
node.remove();
|
||||||
|
|
||||||
|
/* Below the limit? Make sure the input is enabled. */
|
||||||
|
if (this.tags.length < this.maxTags) {
|
||||||
|
this.inputNode.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addTag(tagValue) {
|
||||||
|
tagValue = tagValue.trim();
|
||||||
|
|
||||||
|
/* Tag value is probably not empty and we don't already have the same tag. */
|
||||||
|
if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {
|
||||||
|
this.tags.push(tagValue.toLowerCase());
|
||||||
|
|
||||||
|
this.inputNode.parentNode.insertBefore(
|
||||||
|
makeEl('<span class="tag is-info" data-value="' + escape(tagValue) + '">' + escape(tagValue) + '<span class="delete is-small" /></span>'),
|
||||||
|
this.inputNode
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Too many tags, disable the input for now. */
|
||||||
|
if (this.tags.length >= this.maxTags) {
|
||||||
|
this.inputNode.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleInputKeyUp(evt) {
|
||||||
|
let tagValue = this.inputNode.value;
|
||||||
|
|
||||||
|
if (evt.key === 'Backspace' && tagValue === '') {
|
||||||
|
// Remove the child
|
||||||
|
if (this.inputNode.previousSibling) {
|
||||||
|
this.deleteTagNode(this.inputNode.previousSibling);
|
||||||
|
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
}
|
||||||
|
} else if (evt.key === ',') {
|
||||||
|
this.addTag(tagValue);
|
||||||
|
|
||||||
|
this.inputNode.value = '';
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleContainerClick(evt) {
|
||||||
|
if (evt.target && evt.target.classList.contains('delete')) {
|
||||||
|
this.deleteTagNode(evt.target.closest('.tag'));
|
||||||
|
this.updateHiddenInputValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupSignupModal = () => {
|
||||||
|
const signupButton = $('[data-target~="#signin"],[data-target~="#signup"]');
|
||||||
|
|
||||||
|
if (signupButton) {
|
||||||
|
signupButton.addEventListener('click', () => {
|
||||||
|
$('.modal').classList.add('is-active');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.modal-button-close').addEventListener('click', () => {
|
||||||
|
$('.modal').classList.remove('is-active');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const globalSetup = () => {
|
||||||
|
Array.prototype.forEach.call($$('.js-tag-input'), (el) => {
|
||||||
|
new TagsInput(el).attach();
|
||||||
|
});
|
||||||
|
|
||||||
|
setupSignupModal();
|
||||||
|
|
||||||
|
const embedButton = $('.panel-tools .embed-tool');
|
||||||
|
|
||||||
|
if (embedButton){
|
||||||
|
embedButton.addEventListener('click', (evt) => {
|
||||||
|
if (evt.target && evt.target.closest('.panel-tools')) {
|
||||||
|
toggleEl(evt.target.closest('.panel-tools').querySelector('.panel-embed'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandButton = $('.expand-tool');
|
||||||
|
|
||||||
|
if (expandButton) {
|
||||||
|
expandButton.addEventListener('click', (evt) => {
|
||||||
|
if (evt.target && evt.target.closest('.panel')) {
|
||||||
|
const panel = evt.target.closest('.panel');
|
||||||
|
|
||||||
|
if (panel.classList.contains('panel-fullsize')) {
|
||||||
|
panel.classList.remove('panel-fullsize');
|
||||||
|
} else {
|
||||||
|
panel.classList.add('panel-fullsize');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
|
||||||
|
const $notification = $delete.parentNode;
|
||||||
|
|
||||||
|
$delete.addEventListener('click', () => {
|
||||||
|
$notification.parentNode.removeChild($notification);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hamburger menu
|
||||||
|
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||||
|
if ($navbarBurgers.length > 0) {
|
||||||
|
$navbarBurgers.forEach(el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const target = el.dataset.target;
|
||||||
|
const $target = document.getElementById(target);
|
||||||
|
el.classList.toggle('is-active');
|
||||||
|
$target.classList.toggle('is-active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const preloader = $('.preloader');
|
||||||
|
const main = $('main');
|
||||||
|
|
||||||
|
if (preloader && main) {
|
||||||
|
preloader.remove();
|
||||||
|
main.id = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUserInfo = () => {
|
||||||
|
const elem = document.getElementById('js-data-holder');
|
||||||
|
|
||||||
|
if (!elem) {
|
||||||
|
return { userId: null, csrfToken: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { userId: elem.dataset.userId, csrfToken: elem.dataset.csrfToken };
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsePasteInfo = (elem) => {
|
||||||
|
if (!elem.dataset.pasteInfo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(elem.dataset.pasteInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
whenReady(() => {
|
||||||
|
globalSetup();
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const myParam = urlParams.get('q');
|
||||||
|
const myPastesElem = document.getElementById('archive');
|
||||||
|
const table = new DataTable(myPastesElem, {
|
||||||
|
ajaxCallback: (resolve) => {
|
||||||
|
resolve({
|
||||||
|
data: Array.prototype.map.call(myPastesElem.querySelectorAll('tbody > tr'), parsePasteInfo)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
rowCallback: (rowData) => {
|
||||||
|
const userData = getUserInfo();
|
||||||
|
const ownedByUser = (parseInt(rowData.user_id) === parseInt(userData.userId));
|
||||||
|
|
||||||
|
const deleteElem = ownedByUser ? `<td class="td-center">
|
||||||
|
<form action="/${rowData.id}" method="POST">
|
||||||
|
<input type="hidden" name="delete" value="delete" />
|
||||||
|
<input type="hidden" name="csrf_token" value="${userData.csrfToken}" />
|
||||||
|
<input type="submit" value="Delete" />
|
||||||
|
</form>
|
||||||
|
</td>` : '';
|
||||||
|
const pasteCreatedAt = new Date(rowData.created_at).toLocaleString();
|
||||||
|
const pasteVisibility = ownedByUser ? `<td class="td-center">${rowData.visibility}</td>` : '';
|
||||||
|
|
||||||
|
return `<tr>
|
||||||
|
<td><a href="/${rowData.id}">${escape(rowData.title)}</a></td>
|
||||||
|
<td class="td-center">${pasteCreatedAt}</td>
|
||||||
|
${pasteVisibility}
|
||||||
|
<td class="td-center">${rowData.views || 0}</td>
|
||||||
|
<td>${tagsToHtml(rowData.tags)}</td>
|
||||||
|
${deleteElem}
|
||||||
|
</tr>`;
|
||||||
|
},
|
||||||
|
filterCallback: dumbFilterCallback,
|
||||||
|
preFilter: myParam
|
||||||
|
});
|
||||||
|
table.attach();
|
||||||
|
|
||||||
|
const myFavesElem = document.getElementById('favs');
|
||||||
|
|
||||||
|
if (!myFavesElem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const faveTable = new DataTable(myFavesElem, {
|
||||||
|
ajaxCallback: (resolve) => {
|
||||||
|
resolve({
|
||||||
|
data: Array.prototype.map.call(myFavesElem.querySelectorAll('tbody > tr'), parsePasteInfo)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
rowCallback: (rowData) => {
|
||||||
|
const recentUpdate = rowData.recently_updated ?
|
||||||
|
`<i class='far fa-check-square fa-lg' aria-hidden='true'></i>` :
|
||||||
|
`<i class='far fa-minus-square fa-lg' aria-hidden='true'></i>`;
|
||||||
|
const pasteFavedAt = new Date(rowData.favourited_at).toLocaleString();
|
||||||
|
|
||||||
|
// <td><a href="/user/${escape(rowData.author)}">${escape(rowData.author)}</a></td>
|
||||||
|
return `<tr>
|
||||||
|
<td><a href="/${rowData.id}">${escape(rowData.title)}</a></td>
|
||||||
|
<td class="td-center">${pasteFavedAt}</td>
|
||||||
|
<td class="td-center">${recentUpdate}</td>
|
||||||
|
<td>${tagsToHtml(rowData.tags)}</td>
|
||||||
|
</tr>`;
|
||||||
|
},
|
||||||
|
filterCallback: dumbFilterCallback
|
||||||
|
});
|
||||||
|
faveTable.attach();
|
||||||
|
});
|
2
public/assets/bundle/user_profile.min.js
vendored
Normal file
1
public/assets/bundle/user_profile.min.js.map
Normal file
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
58
public/captcha.php
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
require_once(__DIR__ . '/../includes/captcha.php');
|
||||||
|
|
||||||
|
$captcha_config = unserialize(@$_SESSION['_CAPTCHA']['config']);
|
||||||
|
if (!$captcha_config) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick random background, get info, and start captcha
|
||||||
|
$background = $captcha_config['backgrounds'][rand(0, count($captcha_config['backgrounds']) - 1)];
|
||||||
|
list($bg_width, $bg_height, $bg_type, $bg_attr) = getimagesize($background);
|
||||||
|
|
||||||
|
$captcha = imagecreatefrompng($background);
|
||||||
|
|
||||||
|
$color = hex2rgb($captcha_config['color']);
|
||||||
|
$color = imagecolorallocate($captcha, $color['r'], $color['g'], $color['b']);
|
||||||
|
|
||||||
|
// Determine text angle
|
||||||
|
$angle = rand($captcha_config['angle_min'], $captcha_config['angle_max']) * (rand(0, 1) == 1 ? -1 : 1);
|
||||||
|
|
||||||
|
// Select font randomly
|
||||||
|
$font = $captcha_config['fonts'][rand(0, count($captcha_config['fonts']) - 1)];
|
||||||
|
|
||||||
|
// Verify font file exists
|
||||||
|
if (!file_exists($font)) {
|
||||||
|
die('Font file not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the font size
|
||||||
|
$font_size = rand($captcha_config['min_font_size'], $captcha_config['max_font_size']);
|
||||||
|
$text_box_size = imagettfbbox($font_size, $angle, $font, $captcha_config['code']);
|
||||||
|
|
||||||
|
// Determine text position
|
||||||
|
$box_width = abs($text_box_size[6] - $text_box_size[2]);
|
||||||
|
$box_height = abs($text_box_size[5] - $text_box_size[1]);
|
||||||
|
$text_pos_x_min = 0;
|
||||||
|
$text_pos_x_max = ($bg_width) - ($box_width);
|
||||||
|
$text_pos_x = rand($text_pos_x_min, $text_pos_x_max);
|
||||||
|
$text_pos_y_min = $box_height;
|
||||||
|
$text_pos_y_max = ($bg_height) - ($box_height / 2);
|
||||||
|
$text_pos_y = rand($text_pos_y_min, $text_pos_y_max);
|
||||||
|
|
||||||
|
// Draw shadow
|
||||||
|
if ($captcha_config['shadow']) {
|
||||||
|
$shadow_color = hex2rgb($captcha_config['shadow_color']);
|
||||||
|
$shadow_color = imagecolorallocate($captcha, $shadow_color['r'], $shadow_color['g'], $shadow_color['b']);
|
||||||
|
imagettftext($captcha, $font_size, $angle, $text_pos_x + $captcha_config['shadow_offset_x'], $text_pos_y + $captcha_config['shadow_offset_y'], $shadow_color, $font, $captcha_config['code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw text
|
||||||
|
imagettftext($captcha, $font_size, $angle, $text_pos_x, $text_pos_y, $color, $font, $captcha_config['code']);
|
||||||
|
|
||||||
|
// Output image
|
||||||
|
header("Content-type: image/png");
|
||||||
|
imagepng($captcha);
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('includes/common.php');
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
require_once('includes/functions.php');
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
use PonePaste\Models\Paste;
|
use PonePaste\Models\Paste;
|
||||||
|
|
||||||
|
@ -13,5 +14,5 @@ $random_pastes = Paste::getRandom();//->map('transformPasteRow');
|
||||||
// Theme
|
// Theme
|
||||||
$page_template = 'discover';
|
$page_template = 'discover';
|
||||||
$page_title = 'Discover';
|
$page_title = 'Discover';
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
9
public/event.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
|
$page_template = 'event';
|
||||||
|
$page_title = 'Event';
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
|
@ -1,12 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
require_once(__DIR__ . '/../includes/captcha.php');
|
||||||
|
|
||||||
use PonePaste\Models\Paste;
|
use PonePaste\Models\Paste;
|
||||||
use PonePaste\Models\Tag;
|
use PonePaste\Models\Tag;
|
||||||
use PonePaste\Models\User;
|
use PonePaste\Models\User;
|
||||||
|
|
||||||
require_once('includes/common.php');
|
|
||||||
require_once('includes/captcha.php');
|
|
||||||
require_once('includes/functions.php');
|
|
||||||
|
|
||||||
function verifyCaptcha() : string|bool {
|
function verifyCaptcha() : string|bool {
|
||||||
global $captcha_config;
|
global $captcha_config;
|
||||||
|
@ -190,4 +191,5 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
OutPut:
|
OutPut:
|
||||||
$csrf_token = setupCsrfToken();
|
$csrf_token = setupCsrfToken();
|
||||||
$page_template = 'main';
|
$page_template = 'main';
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
|
||||||
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('includes/common.php');
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
require_once('includes/functions.php');
|
define('IN_PONEPASTE', 1);
|
||||||
require_once('includes/passwords.php');
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
use PonePaste\Helpers\SessionHelper;
|
use PonePaste\Helpers\SessionHelper;
|
||||||
use PonePaste\Models\User;
|
use PonePaste\Models\User;
|
||||||
|
@ -130,6 +130,5 @@ if (isset($_POST['forgot'])) {
|
||||||
// Theme
|
// Theme
|
||||||
$page_template = 'login';
|
$page_template = 'login';
|
||||||
$page_title = 'Login / Register';
|
$page_title = 'Login / Register';
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('includes/common.php');
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
require_once('includes/functions.php');
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
use PonePaste\Helpers\SessionHelper;
|
use PonePaste\Helpers\SessionHelper;
|
||||||
|
|
||||||
|
@ -15,5 +16,6 @@ SessionHelper::destroySession();
|
||||||
/* Destroy PHP session */
|
/* Destroy PHP session */
|
||||||
unset($_SESSION['user_id']);
|
unset($_SESSION['user_id']);
|
||||||
session_destroy();
|
session_destroy();
|
||||||
|
flashSuccess('You have been logged out.');
|
||||||
|
|
||||||
header('Location: ' . $_SERVER['HTTP_REFERER']);
|
header('Location: ' . $_SERVER['HTTP_REFERER']);
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('includes/common.php');
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
require_once('includes/functions.php');
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
use PonePaste\Models\Page;
|
use PonePaste\Models\Page;
|
||||||
|
|
||||||
|
@ -19,5 +20,5 @@ if (isset($_GET['page'])) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$page_template = 'pages';
|
$page_template = 'pages';
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('includes/common.php');
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
require_once('includes/functions.php');
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
use Highlight\Highlighter;
|
use Highlight\Highlighter;
|
||||||
use PonePaste\Models\Paste;
|
use PonePaste\Models\Paste;
|
||||||
|
@ -222,4 +223,4 @@ if ($error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$csrf_token = setupCsrfToken();
|
$csrf_token = setupCsrfToken();
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('includes/common.php');
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
require_once('includes/functions.php');
|
define('IN_PONEPASTE', 1);
|
||||||
require_once('includes/passwords.php');
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
use PonePaste\Models\Paste;
|
use PonePaste\Models\Paste;
|
||||||
|
|
||||||
|
@ -42,5 +42,5 @@ $csrf_token = setupCsrfToken();
|
||||||
|
|
||||||
$page_template = 'profile';
|
$page_template = 'profile';
|
||||||
$page_title = 'My Profile';
|
$page_title = 'My Profile';
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
||||||
|
|
8
public/rules.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
|
$page_template = 'rules';
|
||||||
|
$page_title = 'Rules';
|
||||||
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once('includes/common.php');
|
/** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
||||||
require_once('includes/functions.php');
|
define('IN_PONEPASTE', 1);
|
||||||
|
require_once(__DIR__ . '/../includes/common.php');
|
||||||
|
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use PonePaste\Models\User;
|
use PonePaste\Models\User;
|
||||||
|
@ -78,4 +79,4 @@ if (isset($profile_info)) {
|
||||||
$page_template = 'errors';
|
$page_template = 'errors';
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
require_once(__DIR__ . '/../theme/' . $default_theme . '/common.php');
|
|
@ -8,11 +8,11 @@ const output = (name) => {
|
||||||
input: `js/${name}.js`,
|
input: `js/${name}.js`,
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
file: `assets/bundle/${name}.js`,
|
file: `public/assets/bundle/${name}.js`,
|
||||||
format: 'esm'
|
format: 'esm'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
file: `assets/bundle/${name}.min.js`,
|
file: `public/assets/bundle/${name}.min.js`,
|
||||||
format: 'esm',
|
format: 'esm',
|
||||||
plugins: [getBabelOutputPlugin({ presets: ['@babel/preset-env'] }), terser()],
|
plugins: [getBabelOutputPlugin({ presets: ['@babel/preset-env'] }), terser()],
|
||||||
sourcemap: true
|
sourcemap: true
|
||||||
|
|
18
router.php
|
@ -1,18 +0,0 @@
|
||||||
<?php /** @noinspection PhpDefineCanBeReplacedWithConstInspection */
|
|
||||||
define('IN_PONEPASTE', 1);
|
|
||||||
|
|
||||||
const ROUTER_VALID_PAGES = [
|
|
||||||
'archive', 'discover', 'event', 'index', 'login',
|
|
||||||
'logout', 'pages', 'paste', 'profile', 'user'
|
|
||||||
];
|
|
||||||
|
|
||||||
if (empty($_GET['route'])) {
|
|
||||||
die('No route specified.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!in_array($_GET['route'], ROUTER_VALID_PAGES)) {
|
|
||||||
die('Invalid route specified.');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is safe, because this is whitelisted to the options above. */
|
|
||||||
require_once($_GET['route'] . '.php');
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once('includes/common.php');
|
|
||||||
require_once('includes/functions.php');
|
|
||||||
|
|
||||||
// Theme
|
|
||||||
$page_template = 'rules';
|
|
||||||
$page_title = 'Rules';
|
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
|