Compare commits

...

33 commits

Author SHA1 Message Date
ro
5653dcbd44
update version number, #3
it was right before. just having a brain fart moment...
2025-06-24 12:00:37 -06:00
ro
a0fd6242ba
updated version number
LOL, it would probably help if it was changed to the CORRECT version
number
2025-06-24 11:58:55 -06:00
ro
b513f1eace
updated version number 2025-06-24 11:57:24 -06:00
ro
9991c9647d
performance update, fix for bulky tags file
cleaned up the code to get a massive boost to publishing and page
rendering speeds

the issue was related to pages being collected and transformed into
objects the system could use multiple times, resulting in sluggish
rendering due to available memory getting drained to accomodate a long
script running more than it was needed. edited the rendering process so
the script is only called one and the data is passed to the respective
functions that require that data.

also plugged in some minor manual garbage collection that speed up the
process a bit more

there was also a bug with rendering the tags file that was resulting in
the `tags.json` file being unusually large. the issue was tags were
getting replicated in the process resulting in adding duplicates to the
tag file everytime it was run. go rid of the unnecessary function that
was calling the replicate tags array and now the tags file is it's
appropriate size

whew, this turned out to be a big one that wasn't meant to be, but the
peformance boost was worth it
2025-06-18 13:43:24 -06:00
ro
cec992ccd4
package updates, first pass at render performance
updated dependencies to the latest versions

also took a swing at rendering performance, which was pretty basic. it
was fine for sites with not that many pages, but it gets a bit stick
above 50

as of right now, performance has been improved slighthy by forcing some
garbage collection, rendering 65 pages in under 30 seconds with 256MB
available.

the issue seems to the speed of initial page collections from the
directories and then converting them to objects that the system can use.
going to explore some options to optimize that particular script

also removed page load from PageRepository instantion because when the
app loads, it was calling that class as part of the start of process and
loading pages everytime no matter what page was being looked at.
removing that made non page sections snappier
2025-06-17 18:24:53 -06:00
ro
9064d6008b
quick refactor note for page editor
the front js class for controlling the page editor is getting heavy, so
a quick note was added to refactor in the near future
2025-06-17 14:38:57 -06:00
ro
a0734724c6
refactor note for page editor js
page editor is getting a bit bulky so it needs some thought to slim it up
2025-05-27 19:10:54 -06:00
ro
82db115baf
patch for site initialization
still finding bugs related to the big routing rewrite, this one being an
error when starting a new set up.

fortunately, it was just a matter of getting the redirects pointing to
where they should, so it's a small fix
2025-05-27 12:58:04 -06:00
ro
ffe2ccdf5c
damn cdata tag again... 2025-05-20 18:29:50 -06:00
ro
7b94d1bff7
quick fix for malformed CDATA tag 2025-05-20 18:26:14 -06:00
ro
0213c7b180
added feature medio to rss
just thought it would be a nice touch to pull in the featured media into
the descption for some extra pretty in the feed
2025-05-20 18:21:16 -06:00
ro
a21cc74241
rss link fix
item links were pointing to the wrong thing. oops.
2025-05-20 17:28:43 -06:00
ro
4c9fbc3ded
Added RSS
plugged in a simple rss component so fipamo sites can be subscribed to
through feed reeders.

routing needed a bit of updating to make it work, so it's been edited to
accomodate any new url that deviate from created pages.

long live RSS
2025-05-20 17:10:57 -06:00
ro
c4d759c311
changed editor text from mono to san-serif
the editor looked stiff and techy with the monotype, so changed it to a
generic sans-serif just to make it friendler

ha, hopefully nothing breaks...
2025-05-18 13:02:57 -06:00
ro
981d182d1a
style, state and bounding tweaks for editor
new floating text formatter had some reveal state issues, some styling
wonkyness and didn't have any bounding rules, so all of that has been
addressed.

also removed css references to a component that has been removed.
2025-05-18 12:20:39 -06:00
ro
e1ef4ed1d2
removed deleted file reference
file callimg the editor script had a reference to a file that was
removed. fixed.
2025-05-18 07:59:43 -06:00
ro
28a2a2bc1e
text select patch
text select doesn't seem to work on iOS (because of course), so making a
quick adjustment and hopefully that solves the problem
2025-05-18 07:51:58 -06:00
ro
5300c91058
moved login/logout to dash; removed AuthController
Member authorization got moved to the member repo class a long time ago,
so AuthController was a leftover when that process was handled by
something else

moved dashboard login/logout to DashController and got rid of
AuthController as it's not necessary anymore
2025-05-17 08:03:46 -06:00
ro
59ab2e1536
url fix for page browser ui
the ui that is for browsing available pages was using the old url from
the previous routing set up and needed to be updated
2025-05-16 17:55:11 -06:00
ro
743d7c4d90
routing overhaul
previously all the of the page routing was handlede through controller
organized by CRUD methods. it worked, but organizing by CRUD and not
purpose resulted in more time spent looking for a specific task through
controllers, so it turned out not to an effecient way of organizing.

so routing has been completely reorganized into laravel's web routing
section and methods have been moved to their appropriate controller so
tasks are much easier to find

front facing page routing turned out to be a bit more tricky that
anticipated do the overap of routing paths, but it only affects dynamic
page rendering and there's a patch in place that handles that issue
until a better solution is found. Rendered HTML pages works fine.

whew!
2025-05-16 17:37:53 -06:00
ro
42dfdc947e
responsive pass for page edit ui
there were a couple of glaring issues with the responsiveness of the new
page ui layout, so some of the bigger ones have been addressed

there's was also a bug where the sub-menu options weren't getting picked
up when being assigned in the script. fixed
2025-05-14 15:01:53 -06:00
ro
543e615645
syntax highlighting
turned on some subtle syntax highlighting for markdown in the text
editor. colors still need to be tweaked, but so far so good
2025-05-13 19:55:13 -06:00
ro
5e3d2ea49f
improved file upload accessibility
the label for uploading files in the meta ui was not accessible by
keyboard, so that element was changed to a button that can be tabbed to
and clicked to open the file upload ui

there were also so minor css issues with the options toolbar, so those
have been tweaked
2025-05-13 12:56:55 -06:00
ro
6c44f58448
accessbility improvements for issue #109
the bold, italic and strikethrough options now function as toggles for
formatting options and removes them rather than just replacing text

the options buttons under the meta ui now communicate their status
through use of the 'aria pressed' attribute so they can be properly read
by assitive technologies like screen readers
2025-05-13 10:58:21 -06:00
ro
714485c6a6
cleaned up meta ui, made editor display contextual
there were some nagging space issues in the new meta ui layout, so those
were addressed. it'll will probably change a billion more times, but
it's starting from a solid place

the text editor ui now only display when text is selected and goes away
after an operation has been completed or when text is deselected, making
the overall page editing experience streamlined
2025-05-12 14:58:32 -06:00
ro
9c200ea78d
reorganized text editor
moved page meta data to it's own ui to clean up the page editing
experience as it was becoming a bit crowded.

also moved to saving and deleting buttons to title bar so they are
always available

added a note to web routes to reorganize them within the web routes file
to reduce the confusion present by having them in their own Controllers.
it's just an extra layer that's not needed
2025-05-12 13:12:58 -06:00
ro
16856f6c25
cleaned up page edit template, fix formatting
cleaned up the page edit template a bit to make it easier to convert to
the new editor UI.

php formatting also needed to be updated, so the php cs fixer file was
updated and added some setting to prettier to handle blade files
2025-05-08 18:25:57 -06:00
ro
47dfdbb7fc
upgraded laravel to the latest
Larave is at version 12.xx now, so that and its dependencies needed to
be upgraded
2025-05-01 13:57:48 -06:00
ro
3ae9dbea00
dynamic rendering patch
pages being rendered dynamically were not displaying properly due to
hitting a case that was not being captured. patched
2025-03-19 23:39:14 -06:00
ro
44b8e9b5ad
fix for incorrect page rendering
there was a generic place holder that defaulted to the default page
template to render pages. that's been fixed so it refers to the pages
layout to know what template to use
2025-02-04 15:58:50 -06:00
ro
5d74aafc9b
restored link to contribution guide
thought the contriubution guide was lost in the move because it wasn't
in the wiki, but it's a README file

take it easy, moving is hard lol
2025-01-23 19:46:48 -06:00
ro
5692a94ba5
quick key check 2025-01-23 19:39:30 -06:00
ro
1096971995
quick edit for new repo 2025-01-22 17:35:51 -06:00
70 changed files with 13776 additions and 2484 deletions

View file

@ -55,20 +55,15 @@ return $config
]
],
'no_multiline_whitespace_around_double_arrow' => true,
'no_spaces_around_offset' => true,
'no_unused_imports' => true,
'no_whitespace_before_comma_in_array' => true,
'no_whitespace_in_blank_line' => true,
'object_operator_without_whitespace' => true,
'single_blank_line_before_namespace' => true,
'ternary_operator_spaces' => true,
'trim_array_spaces' => true,
'unary_operator_spaces' => true,
'whitespace_after_comma_in_array' => true,
'single_line_after_imports' => true,
'ordered_imports' => [
'sort_algorithm' => 'none',
],
//'single_blank_line_before_namespace' => true, php fixer doesn't like this rule?
//Other rules here...
])
->setLineEnding("\n");

View file

@ -1,9 +1,6 @@
{
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
},
{ "files": ".prettierrc", "options": { "parser": "json" } },
{
"files": "*.css",
"options": {
@ -31,6 +28,10 @@
"tabWidth": 4,
"printWidth": 90
}
},
{
"files": ["*.blade.php"],
"options": { "parser": "blade", "tabWidth": 4 }
}
]
}

View file

@ -15,7 +15,7 @@ Fipamo will be a full-featured content management platform that makes it easy fo
- Plug-in system to extend functionality
- Easy data portability
# Contribute to Fipamo
# Contributing to Fipamo
Fipamo is a project that wants to foster a healthy community through the values of safety, ease of use, and transparency. The creation of a tool that promotes these attributes cannot be done by one person, so contributors from a variety of backgrounds are welcome to lend their expertise to accomplish this goal.
For details on how to lend a hand, check the [guide](https://koodu.h-i.works/projects/fipamo/src/branch/develop/CONTRIBUTING.md).

View file

@ -1,50 +0,0 @@
<?php
namespace App\Http\Controllers;
use Symfony\Component\HttpFoundation\Response;
use App\Interfaces\MemberRepositoryInterface;
use Illuminate\Http\Request;
class AuthController extends Controller
{
protected $member;
public function __construct(
MemberRepositoryInterface $memberRepository
) {
$this->member = $memberRepository;
}
public function enter(Request $request): Response
{
$token = csrf_token();
$credentials = $request->validate([
'handle' => ['required'],
'password' => ['required'],
]);
if ($credentials) {
$result = $this->member->auth($request);
if ($result['status']) {
//$request->session()->regenerate();
return redirect()->intended('dashboard/start');
} else {
return back()->withErrors([
'error' => $result['message'],
]);
}
} else {
return back()->withErrors([
'error' => 'Nope. Check your crendtials, champ',
]);
}
}
public function exit(Request $request): Response
{
session()->flush();
return redirect()->intended('dashboard');
}
}

View file

@ -2,131 +2,42 @@
namespace App\Http\Controllers;
use Symfony\Component\HttpFoundation\Response;
use App\Interfaces\PageRepositoryInterface;
use App\Interfaces\MemberRepositoryInterface;
use App\Services\Data\ThemeService;
use App\Services\Data\SortingService;
use App\Services\Assets\FileUploadService;
use Illuminate\Http\Request;
class DashController extends Controller
{
protected PageRepositoryInterface $pages;
protected MemberRepositoryInterface $member;
protected ThemeService $themes;
protected SortingService $sort;
protected FileUploadService $upload;
public function __construct(
PageRepositoryInterface $pageRepository,
MemberRepositoryInterface $memberRepo,
ThemeService $themeService,
SortingService $sortingService
FileUploadService $fileUploadService,
) {
$this->pages = $pageRepository;
$this->member = $memberRepo;
$this->themes = $themeService;
$this->sort = $sortingService;
$this->upload = $fileUploadService;
}
//---
// GET
//---
public function init($second, $third, $fourth)
{
switch ($second) {
case 'settings':
return $this->settings();
break;
case 'navigation':
return $this->navigation();
break;
case 'pages':
($third == null) ? $third = 'all' : $third = $third;
($fourth == null) ? $fourth = 1 : $fourth = $fourth;
return $this->book($third, $fourth);
break;
case 'page':
return $this->page($third, $fourth);
break;
case 'logout':
return $this->logout();
break;
default:
return $this->start();
break;
}
}
public function start()
{
$result = [];
if ($this->member::status()) {
$result = [];
$result = $this->pages->getGroup(1, 4);
}
return view('back.start', [
"status" => $this->member::status(),
"result" => $result,
"title" => "Start"
]);
}
public function book($pageFilter = 'all', $pageNum = 1)
{
$result = [];
if ($this->member::status()) {
$result = $this->pages->getGroup($pageNum, 4, $pageFilter);
}
return view('back.book', [
"status" => $this->member::status(),
"result" => $result,
"currentPage" => $pageNum,
"title" => "Pages"
]);
}
public function page($mode, $uuid)
{
$title;
$page = [];
$views = [];
$mode == 'edit' ? $page = $this->pages->getByUuid($uuid) : $page = [];
$mode == 'edit' ? $title = $page['title'] : $title = 'Add New';
$mode == 'edit' ? $views = $this->themes->getCustomViews($page['layout']) : $views[] = 'page';
return view('back.page', [
"status" => $this->member::status(),
"mode" => $mode,
"page" => $page,
"views" => $views,
"title" => $title,
]);
}
public function navigation()
{
return view('back.navigation', $this->sort->navigation());
}
public function settings()
{
return view('back.settings', $this->sort->settings());
}
//---
// POST
//---
//---
// PUT
//---
//---
// AUTH
//---
public function login()
{
if ($this->member::status()) {
return redirect('dashboard');
return view('back.start', [
"status" => $this->member::status(),
"result" => $result,
"title" => "Start"
]);
} else {
return view('back.login', [
"status" => $this->member::status(),
@ -135,9 +46,58 @@ class DashController extends Controller
}
}
public function logout()
public function exit()
{
session()->flush();
return redirect()->intended('dashboard');
}
//---
// POST
//---
public function enter(Request $request): Response
{
$token = csrf_token();
$credentials = $request->validate([
'handle' => ['required'],
'password' => ['required'],
]);
if ($credentials) {
$result = $this->member->auth($request);
if ($result['status']) {
//$request->session()->regenerate();
return redirect()->intended('dashboard');
//return $this->start();
} else {
return back()->withErrors([
'error' => $result['message'],
]);
}
} else {
return back()->withErrors([
'error' => 'Nope. Check your crendtials, champ',
]);
}
}
public function uploads(Request $request)
{
$result = $result = $this->upload->handleFile($request);
//update configs for specfic uploads
switch ($request['source']) {
case 'avatar-upload':
$member = [];
$member = session('member');
$member['avatar'] = $result['filePath'];
$member = (object) $member;
$this->member->update($member);
break;
case 'background-upload':
$this->settings->updateGlobalData('background', $result['filePath']);
break;
}
return $result;
}
}

View file

@ -6,6 +6,10 @@ use App\Interfaces\PageRepositoryInterface;
use App\Services\Assets\AssetService;
use App\Services\Data\SettingsService;
use App\Services\Data\SortingService;
use App\Services\Upkeep\InitService;
use App\Http\Controllers\DashController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
use function _\find;
@ -15,48 +19,39 @@ class FrontController extends Controller
protected PageRepositoryInterface $pages;
protected AssetService $assets;
protected SortingService $sort;
protected $init;
protected $dash;
protected $words;
public function __construct(
PageRepositoryInterface $pageRepository,
SettingsService $settingsService,
AssetService $assetService,
SortingService $sortService,
InitService $initService,
DashController $dashController,
) {
$this->pages = $pageRepository;
$this->settings = $settingsService;
$this->assets = $assetService;
$this->sort = $sortService;
$this->init = $initService;
$this->dash = $dashController;
$this->words = ['words'];
}
//REFACTOR: there is some method overlap between index and pages, so that needs to be addressed
public function index($first = 00, $second = 00, $third = 00)
public function start()
{
$global = $this->settings->getGlobal();
$currentTheme = $this->assets->getCurrentTheme();
$template;
$pageData = [];
$pages = $this->pages->getAll();
//check if configs are present
if (file_exists(env('FOLKS_PATH')) && file_exists(env('SETTINGS_PATH'))) {
if ($this->configCheck()) {
$pages = $this->pages->getAll();
$global = $this->settings->getGlobal();
$currentTheme = $this->assets->getCurrentTheme();
if ($global['dynamicRender'] == 'true') {
if (is_numeric($first)) {
if ($first == 00 || !isset($first)) {
$page = $pages->where('id', 1)->first();
$pageData = $this->sort->page($page);
$template = $currentTheme . '.index';
} else {
$page = $this->pages->getBySlug($third);
$pageData = $this->sort->page($page);
$template = $currentTheme . '.' . $page['layout'];
}
} else {
if ($first == null || $first == '') {
$page = $pages->where('id', 1)->first();
$pageData = $this->sort->page($page);
$template = $currentTheme . '.index';
} else {
}
}
$page = $pages->where('id', 0)->first();
$pageData = $this->sort->page($page, $pages, false);
$template = $currentTheme . '.index';
return view($template, $pageData);
} else {
if (is_file('../public/index.html')) {
@ -70,30 +65,105 @@ class FrontController extends Controller
}
}
public function page($first = 00, $second = 00, $third = 00)
public function page($year, $month, $slug)
{
$currentTheme = $this->assets->getCurrentTheme();
switch ($first) {
case 'archive':
case 'archives':
$template = $currentTheme . '.archive';
$pageData = $this->sort->archive();
$template;
$pageData = [];
if ($this->configCheck()) {
$pages = $this->pages->getAll();
$currentTheme = $this->assets->getCurrentTheme();
$page = $this->pages->getBySlug($slug);
$pageData = $this->sort->page($page, $pages, false);
$template = $currentTheme . '.' . $page['layout'];
return view($template, $pageData);
}
}
public function menu($slug, $option = null)
{
$template;
$pageData = [];
if ($this->configCheck()) {
$pages = $this->pages->getAll();
$currentTheme = $this->assets->getCurrentTheme();
switch ($slug) {
case 'archive':
case 'archives':
$template = $currentTheme . '.archive';
$pageData = $this->sort->archive($pages, false);
break;
case 'tags':
$template = $currentTheme . '.tags';
$tags = $this->sort->tags($pages, false);
$tagData = find($tags['tags'], ['tag_name' => $option]);
$pageData = [
'theme' => $currentTheme, // for theme kit
'layout' => $tags['layout'],
'title' => 'Pages Tagged as ' . $option,
'dynamicRender' => $tags['dynamicRender'],
'info' => $tags['info'],
'menu' => $tags['menu'],
'pages' => $tagData['pages'],
'media' => $tags['media'],
];
break;
default:
$pages = $this->pages->getAll();
$currentTheme = $this->assets->getCurrentTheme();
$page = $this->pages->getBySlug($slug);
$pageData = $this->sort->page($page, $pages, false);
$template = $currentTheme . '.' . $page['layout'];
break;
}
return view($template, $pageData);
}
}
private function configCheck()
{
if (file_exists(env('FOLKS_PATH')) && file_exists(env('SETTINGS_PATH'))) {
return true;
} else {
return view('back.init', ["status" => false, "title" => "Set Up"]);
}
}
public function things()
{
return $items = ['archive', 'archives', 'tags'];
}
public static function items()
{
if (file_exists(env('SETTINGS_PATH'))) {
//set permanent links
$items = ['archive', 'archives', 'tags'];
//grab menu items and set to array so router knows to look for them
$settings = json_decode(file_get_contents(env('SETTINGS_PATH')), true);
foreach ($settings['menu'] as $item) {
array_push($items, $item['slug']);
}
return $items;
} else {
//return view('back.init', ["status" => false, "title" => "Set Up"]);
// return redirect()->route('start');
return [];
}
}
//setup up a new site or restore from back up
public function init($task, Request $request)
{
$result = [];
switch ($task) {
case 'fresh':
$result = $this->init->fresh(json_decode($request->getContent()));
break;
case 'tags':
$template = $currentTheme . '.tags';
$tags = $this->sort->tags(false);
$tagData = find($tags['tags'], ['tag_name' => $second]);
$pageData = [
'theme' => $currentTheme, // for theme kit
'title' => 'Pages Tagged as ' . $second,
'dynamicRender' => $tags['dynamicRender'],
'info' => $tags['info'],
'menu' => $tags['menu'],
'pages' => $tagData['pages'],
'media' => $tags['media'],
];
case 'restore':
$result = $this->init->restore($request);
break;
}
return view($template, $pageData);
return response()->json($result)->header('Content-Type', 'application/json');
}
}

View file

@ -0,0 +1,147 @@
<?php
namespace App\Http\Controllers;
use App\Interfaces\PageRepositoryInterface;
use App\Interfaces\MemberRepositoryInterface;
use App\Services\Data\ThemeService;
use Illuminate\Http\Request;
class PageController extends Controller
{
protected PageRepositoryInterface $page;
protected MemberRepositoryInterface $member;
protected ThemeService $themes;
public function __construct(
PageRepositoryInterface $pageRepo,
MemberRepositoryInterface $memberRepo,
ThemeService $themeService,
) {
$this->page = $pageRepo;
$this->member = $memberRepo;
$this->themes = $themeService;
}
//---
// GET Actions
//---
public function start(string $one = 'all', string $two = '1')
{
//checks mode to see what needs to get loaded
switch ($one) {
case "edit":
case "add":
return $this->page($one, $two);
break;
default:
return $this->book($one, $two);
break;
}
}
private function page($mode, $uuid)
{
$title;
$page = [];
$views = [];
$mode == 'edit' ? $page = $this->page->getByUuid($uuid) : $page = [];
$mode == 'edit' ? $title = 'Edit Page' : $title = 'Add New';
$mode == 'edit' ? $views = $this->themes->getCustomViews($page['layout']) : $views[] = 'page';
//just a patch for now to get this out of the template
if ($mode == 'edit') {
$id = $page['id'];
$uuid = $page['uuid'];
$slug = $page['slug'];
$feature = $page['feature'];
$layout = $page['layout'];
$tags = $page['tags'];
$content = $page['content'];
$date = $page['created'];
$updated = $page['updated'];
$media = $page['media'];
$files = $page['docs'];
$editTitle = $page['title'];
} else {
$id = "";
$uuid = "";
$slug = "";
$feature = "";
$layout = "";
$tags = "";
$content = "";
$date = "";
$updated = "";
$media = "";
$files = "";
$editTitle = "";
};
return view('back.page', [
"status" => $this->member::status(),
"mode" => $mode,
"page" => $page,
"views" => $views,
"id" => $id,
"uuid" => $uuid,
"slug" => $slug,
"feature" => $feature,
"layout" => $layout,
"tags" => $tags,
"content" => $content,
"date" => $date,
"updated" => $updated,
"media" => $media,
"files" => $files,
"title" => urldecode($title),
"editTitle" => urldecode($editTitle),
]);
}
private function book($pageFilter, $pageNum)
{
$result = [];
if ($this->member::status()) {
$result = $this->page->getGroup($pageNum, 4, $pageFilter);
}
return view('back.book', [
"status" => $this->member::status(),
"result" => $result,
"currentPage" => $pageNum,
"title" => "Pages"
]);
}
//---
// POST Actions
//---
public function create(Request $request)
{
$body = json_decode($request->getContent());
$result = $this->page->create($body);
return response()->json($result)->header('Content-Type', 'application/json');
}
//---
// PUT Actions
//---
public function write(Request $request)
{
$body = json_decode($request->getContent());
$result = $this->page->update($body);
return response()->json($result)->header('Content-Type', 'application/json');
}
//---
// DELETE Actions
//---
public function delete(Request $request)
{
$body = json_decode($request->getContent());
$result = $this->page->delete($body);
return response()->json($result)->header('Content-Type', 'application/json');
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Interfaces\PageRepositoryInterface;
use App\Services\Data\SettingsService;
use Carbon\Carbon;
class RSSController extends Controller
{
protected PageRepositoryInterface $pages;
protected SettingsService $settings;
public function __construct(
PageRepositoryInterface $pageRepo,
SettingsService $settingsService,
) {
$this->settings = $settingsService;
$this->pages = $pageRepo;
}
public function getFeed(Request $request)
{
$pages = $this->pages->getAll();
$global = $this->settings->getGlobal();
$feedContent = "<?xml version='1.0' encoding='ISO-8859-1'?>
<rss version='2.0' xmlns:atom='http://www.w3.org/2005/Atom'>
<channel>
<title>{$global['title']}</title>
<atom:link href='{$global['base_url']}/rss/feed/' rel='self' type='application/rss+xml' />
<link>{$global['base_url']}</link>
<description>Feed @ {$global['title']}</description>
<language>en-us</language>";
// Loop over the content and add it to the feed
foreach ($pages as $item) {
$title = urldecode($item['title']);
$html = urldecode($item['html']);
$updatedAt = Carbon::parse($item['rawUpdated']);
$date = $updatedAt->format('D, d M Y H:i:s O');
$link = $global['base_url'] . "/" . $item['path'] . "/" . $item['slug'];
if ($global['dynamicRender'] != "true") {
$link = $link . ".html";
}
//grabs feature image or vid and tosses it in the description
$media = explode(",", $item['feature']);
$file = '';
if ($media[0] == null || $media[0] == '') {
$file = $global['background'];
} else {
$file = $media[0];
}
$ext = pathinfo($file, PATHINFO_EXTENSION);
$url = $global['base_url'] . $file;
if ($ext != 'mp4') {
$top = "<img src='{$url}'/>";
} else {
$top = " <video><source src='{$url}' /></video>";
}
$feedContent .= "
<item>
<title>{$title}</title>
<link>{$link}</link>
<guid>{$global['base_url']}/rss/feed/item/{$item['uuid']}</guid>
<pubDate>{$date}</pubDate>
<description><![CDATA[{$top}<br/>]]><![CDATA[{$html}]]></description>
</item>";
}
$feedContent .= "
</channel>
</rss>";
return response($feedContent, 200)->header('Content-Type', 'application/rss+xml');
/**
//set up feed info
header("Content-Type: text/xml");
echo("<?xml version='1.0' encoding='ISO-8859-1'?>");
echo("<rss version='2.0' xmlns:atom='http://www.w3.org/2005/Atom'>");
echo("<channel>");
echo("<title>" . $global['title'] . "</title>");
echo("<link>" . $global['base_url'] . "</link>");
echo("<description>The Feed for " . $global['title'] . "</description>");
//item loop start
foreach ($pages as $item) {
echo("<item id='" . $item['id'] . "'>");
echo("<title>" . urldecode($item['title']) . "</title>");
echo("<link>" . $global['base_url'] . "/rss/feed/item/" . $item['uuid'] . "</link>");
echo("<guid>" . $global['base_url'] . "/rss/feed/item/" . $item['uuid'] . "</guid>");
echo("<displayDate>" . $item['updated'] . "</displayDate>");
echo("<pubDate>" . $item['created'] . "</pubDate>");
echo("<description> <![CDATA[" . $item['html'] . "]]></description>");
echo("<author>" . $item['author'] . "</author>");
echo("</item>");
}
//item loop end
//close it up
echo("</channel>");
echo("</rss>");
**/
}
}

View file

@ -1,29 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Interfaces\PageRepositoryInterface;
class RouteDeleteController extends Controller
{
protected $page;
public function __construct(
PageRepositoryInterface $pageRepo
) {
$this->page = $pageRepo;
}
public function handleRequest(Request $request)
{
$path = explode('/', $request->path());
switch ($path[0]) {
case 'page':
$body = json_decode($request->getContent());
$result = $this->page->delete($body);
return response()->json($result)->header('Content-Type', 'application/json');
break;
}
}
}

View file

@ -1,88 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Interfaces\MemberRepositoryInterface;
use App\Services\Data\SettingsService;
class RouteGetController extends Controller
{
protected $dash;
protected $gate;
protected $theme;
protected $front;
protected $member;
protected $settings;
public function __construct(
DashController $dashController,
AuthController $authController,
ThemeController $themeController,
FrontController $frontController,
MemberRepositoryInterface $memberRepo,
SettingsService $settingsService,
) {
$this->dash = $dashController;
$this->gate = $authController;
$this->theme = $themeController;
$this->front = $frontController;
$this->member = $memberRepo;
$this->settings = $settingsService;
}
public function handleRequest($first = null, $second = null, $third = null, $fourth = null)
{
if (isset($first) && !is_numeric($first)) {
switch ($first) {
case 'dashboard':
if ($this->member::status()) {
return $this->dash->init($second, $third, $fourth);
} else {
return $this->dash->login();
}
break;
case 'theme':
if ($this->member::status()) {
if (isset($second)) {
return $this->theme->getView($third, $fourth);
} else {
return $this->theme->start();
}
} else {
return $this->dash->login();
}
break;
case 'tags':
case 'archives':
return $this->front->page($first, $second, $third);
break;
case 'backup':
return $this->downloadBackup($second);
break;
}
} else {
return $this->front->index($first, $second, $third);
}
}
private function downloadBackup($type)
{
if ($this->member::status()) {
$latest = '';
$file = '';
if ($type == 'content-download') {
$latest = $this->settings->getGlobal()['last_content_backup'];
$file = 'backup-content-' . $latest . '.zip';
} else {
$latest = $this->settings->getGlobal()['last_files_backup'];
$file = 'backup-files-' . $latest . '.zip';
}
return response()->download(
'../content/backups/' . $file,
$file,
['Content-Type: application/zip']
);
}
}
}

View file

@ -1,122 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Mail\SystemEmail;
use App\Interfaces\PageRepositoryInterface;
use App\Services\Upkeep\MaintenanceService;
use App\Services\Assets\FileUploadService;
use App\Interfaces\MemberRepositoryInterface;
use App\Services\Data\SettingsService;
use App\Services\Upkeep\InitService;
use App\Services\Upkeep\ResetService;
class RoutePostController extends Controller
{
protected $page;
protected $gate;
protected $maintenance;
protected $upload;
protected $settings;
protected $member;
protected $init;
protected $reset;
public function __construct(
PageRepositoryInterface $pageRepo,
AuthController $authController,
MaintenanceService $maintenanceService,
FileUploadService $fileUploadService,
SettingsService $settingsService,
MemberRepositoryInterface $memberRepo,
InitService $initService,
ResetService $resetService,
) {
$this->page = $pageRepo;
$this->gate = $authController;
$this->maintenance = $maintenanceService;
$this->upload = $fileUploadService;
$this->settings = $settingsService;
$this->member = $memberRepo;
$this->init = $initService;
$this->reset = $resetService;
}
public function handleRequest(Request $request)
{
$path = explode('/', $request->path());
switch ($path[0]) {
case 'init':
return $this->initTask($path[1], $request);
break;
case 'login':
return $this->gate->enter($request);
break;
case 'page':
$body = json_decode($request->getContent());
$result = $this->page->create($body);
return response()->json($result)->header('Content-Type', 'application/json');
break;
case 'settings':
if ($path[1] == 'mailer') {
return $this->sendNotify($request);
}
break;
case 'upload':
$result = $result = $this->upload->handleFile($request);
//update configs for specfic uploads
switch ($request['source']) {
case 'avatar-upload':
$member = [];
$member = session('member');
$member['avatar'] = $result['filePath'];
$member = (object) $member;
$this->member->update($member);
break;
case 'background-upload':
$this->settings->updateGlobalData('background', $result['filePath']);
break;
}
return $result;
break;
}
}
private function initTask($task, $request)
{
$result = [];
switch ($task) {
case 'fresh':
$result = $this->init->fresh(json_decode($request->getContent()));
break;
case 'restore':
$result = $this->init->restore($request);
break;
case 'reset':
$result = $this->reset->site($request);
break;
}
return response()->json($result)->header('Content-Type', 'application/json');
}
private function sendNotify($request)
{
$result = [];
try {
Mail::to(env('ADMIN_EMAIL'))->send(new SystemEmail($request->content));
$result = [
'type' => 'mail_good',
'message' => 'Mail Sent',
];
} catch (TransportException $e) {
$result = [
'type' => 'mail_not_good',
'message' => 'Mail Not Sent. It\'s cool. Just check mail settings in the .env',
];
}
return response()->json($result)->header('Content-Type', 'application/json');
}
}

View file

@ -2,75 +2,58 @@
namespace App\Http\Controllers;
use App\Interfaces\PageRepositoryInterface;
use App\Services\Assets\AssetService;
use App\Services\Assets\RenderService;
use App\Interfaces\MemberRepositoryInterface;
use App\Services\Data\SettingsService;
use App\Services\Data\SortingService;
use App\Services\Assets\AssetService;
use App\Services\Assets\RenderService;
use App\Services\Upkeep\MaintenanceService;
use App\Services\Upkeep\ResetService;
use Illuminate\Http\Request;
class RoutePutController extends Controller
class SettingsController extends Controller
{
protected $page;
protected $theme;
protected $member;
protected $settings;
protected $sort;
protected $assets;
protected $render;
protected $settings;
protected $member;
protected $maintenance;
protected $reset;
public function __construct(
PageRepositoryInterface $pageRepo,
AssetService $assetService,
RenderService $renderService,
SettingsService $settingsService,
ThemeController $themeController,
MemberRepositoryInterface $memberRepo,
SettingsService $settingsService,
SortingService $sortingService,
AssetService $assetsService,
RenderService $renderService,
MaintenanceService $maintenanceService,
ResetService $resetService,
) {
$this->page = $pageRepo;
$this->assets = $assetService;
$this->render = $renderService;
$this->settings = $settingsService;
$this->theme = $themeController;
$this->member = $memberRepo;
$this->settings = $settingsService;
$this->sort = $sortingService;
$this->assets = $assetsService;
$this->render = $renderService;
$this->maintenance = $maintenanceService;
$this->reset = $resetService;
}
public function handleRequest(Request $request)
public function start()
{
$path = explode('/', $request->path());
switch ($path[0]) {
case 'page':
$body = json_decode($request->getContent());
$result = $this->page->update($body);
return response()->json($result)->header('Content-Type', 'application/json');
break;
case 'settings':
return $this->settingsTasks($request, $path[1]);
break;
case 'backup':
return $this->createBackup($request);
break;
}
return view('back.settings', [
'settings' => $this->sort->settings(),
'nav' => $this->sort->navigation()
]);
}
private function createBackup($request)
{
$body = json_decode($request->getContent());
if ($body->task == 'content_backup') {
return response()->json(
$this->maintenance->createContentBackUp()
)->header('Content-Type', 'application/json');
} else {
return response()->json(
$this->maintenance->createFileBackUp()
)->header('Content-Type', 'application/json');
}
}
private function settingsTasks($request, $task)
public function tasks(Request $request)
{
$result = [];
switch ($task) {
switch (explode("/", $request->getURI())[5]) {
case 'publish':
$this->assets->moveToTheme(true);
$result = $this->render->publishAll();
@ -89,4 +72,45 @@ class RoutePutController extends Controller
}
return response()->json($result)->header('Content-Type', 'application/json');
}
public function createBackup(Request $request)
{
$body = json_decode($request->getContent());
if ($body->task == 'content_backup') {
return response()->json(
$this->maintenance->createContentBackUp()
)->header('Content-Type', 'application/json');
} else {
return response()->json(
$this->maintenance->createFileBackUp()
)->header('Content-Type', 'application/json');
}
}
public function downloadBackup($type)
{
if ($this->member::status()) {
$latest = '';
$file = '';
if ($type == 'content-download') {
$latest = $this->settings->getGlobal()['last_content_backup'];
$file = 'backup-content-' . $latest . '.zip';
} else {
$latest = $this->settings->getGlobal()['last_files_backup'];
$file = 'backup-files-' . $latest . '.zip';
}
return response()->download(
'../content/backups/' . $file,
$file,
['Content-Type: application/zip']
);
}
}
public function reset(Request $request)
{
$result = $this->reset->site($request);
return response()->json($result)->header('Content-Type', 'application/json');
}
}

View file

@ -3,6 +3,8 @@
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Mail;
use App\Mail\SystemEmail;
use Illuminate\Http\Request;
class SystemMailController extends Controller
{
@ -14,4 +16,23 @@ class SystemMailController extends Controller
$message = "This is something important. Probably";
Mail::to(env('ADMIN_EMAIL'))->send(new SystemEmail($message));
}
public function sendNotify(Request $request)
{
$result = [];
try {
Mail::to(env('ADMIN_EMAIL'))->send(new SystemEmail($request->content));
$result = [
'type' => 'mail_good',
'message' => 'Mail Sent',
];
} catch (TransportException $e) {
$result = [
'type' => 'mail_not_good',
'message' => 'Mail Not Sent. It\'s cool. Just check mail settings in the .env',
];
}
return response()->json($result)->header('Content-Type', 'application/json');
}
}

View file

@ -50,6 +50,7 @@ class ThemeController extends Controller
$currentTheme = $this->assets->getCurrentTheme();
$template;
$pageData = [];
$book = $this->pages->getAll();
switch ($view) {
case "index":
case "page":
@ -61,27 +62,27 @@ class ThemeController extends Controller
$template = $currentTheme . '.page';
//if coming from theme page, grabs id of latest page
if ($id == null) {
$uuid = $this->getPageUUID();
$page = $this->pages->getByUuid($this->getPageUUID());
} else {
//get page by uuid
$page = $this->pages->getByUuid($id);
}
}
$pageData = $this->sort->page($page);
$pageData = $this->sort->page($page, $book);
break;
case "tags":
$template = $currentTheme . '.tags';
$pageData = $this->sort->tags();
$pageData = $this->sort->tags($book);
break;
case "archives":
case "archive":
$template = $currentTheme . '.archive';
$pageData = $this->sort->archive();
$pageData = $this->sort->archive($book);
break;
default:
$template = $currentTheme . '.index';
$page = $this->pages->getBySlug('first');
$pageData = $this->sort->page($page);
$pageData = $this->sort->page($page, $book);
}
if ($this->member::status()) {
return view($template, $pageData);

View file

@ -4,16 +4,16 @@ namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use App\Services\AuthService;
use App\Interfaces\MemberRepositoryInterface;
class MemberCheck
{
protected $auth;
protected MemberRepositoryInterface $member;
public function __construct(
AuthService $authService,
MemberRepositoryInterface $memberRepo,
) {
$this->auth = $authService;
$this->member = $memberRepo;
}
/**
@ -23,7 +23,7 @@ class MemberCheck
*/
public function handle(Request $request, Closure $next)
{
if ($this->auth::status()) {
if ($this->member::status()) {
return $next($request);
} else {
return redirect('dashboard');

View file

@ -16,7 +16,7 @@ use Carbon\Carbon;
class PageRepository implements PageRepositoryInterface
{
protected $content;
protected $setttings;
protected $settings;
protected $paginate;
protected $pages;
protected $docs;
@ -37,17 +37,19 @@ class PageRepository implements PageRepositoryInterface
$this->docs = $docService;
$this->sort = $sortingService;
$this->render = $renderService;
$this->pages = $this->content->loadAllPages();
//change this because every time the class is created, it loads all pages, which kills
//performance site wide since it's being called by a service provider
//$this->pages = $this->content->loadAllPages();
}
public function getAll()
{
return $this->pages;
return $this->content->loadAllPages();
}
public function getById($id)
{
$page = $this->pages->where('id', $id)->first();
$page = $this->content->loadAllPages()->where('id', $id)->first();
//quick check to see if layout is set
$page['layout'] == '' ? $page['layout'] = 'page' : $page['layout'] = $page['layout'];
return $page;
@ -55,7 +57,7 @@ class PageRepository implements PageRepositoryInterface
public function getByUuid($uuid)
{
$page = $this->pages->where('uuid', $uuid)->first();
$page = $this->content->loadAllPages()->where('uuid', $uuid)->first();
//quick check to see if layout is set
$page['layout'] == '' ? $page['layout'] = 'page' : $page['layout'] = $page['layout'];
return $page;
@ -63,7 +65,7 @@ class PageRepository implements PageRepositoryInterface
public function getBySlug($slug)
{
$page = $this->pages->where('slug', $slug)->first();
$page = $this->content->loadAllPages()->where('slug', $slug)->first();
//quick check to see if layout is set
$page['layout'] == '' ? $page['layout'] = 'page' : $page['layout'] = $page['layout'];
return $page;
@ -71,7 +73,7 @@ class PageRepository implements PageRepositoryInterface
public function delete($page)
{
return $this->editPage($page, $this->pages->where('uuid', $page->uuid)->first(), 'delete');
return $this->editPage($page, $this->content->loadAllPages()->where('uuid', $page->uuid)->first(), 'delete');
}
public function create($page)
@ -81,7 +83,7 @@ class PageRepository implements PageRepositoryInterface
public function update($page)
{
return $this->editPage($page, $this->pages->where('uuid', $page->uuid)->first(), 'update');
return $this->editPage($page, $this->content->loadAllPages()->where('uuid', $page->uuid)->first(), 'update');
}
public function getGroup($num, $limit, $sort = "all")
@ -151,9 +153,8 @@ class PageRepository implements PageRepositoryInterface
$this->settings->getGlobal()['renderOnSave'] == 'true' &&
$this->settings->getGlobal()['dynamicRender'] == 'false'
) {
$this->render->tags();
$this->render->archive();
$this->render->pages();
//REFACTOR: Don't need to render entire site on a page edit, just render edited page
$this->render->publishAll();
$message = 'Filed edited and rendered. NOICE.';
} else {
$message = 'File edited. Nice work';
@ -162,7 +163,8 @@ class PageRepository implements PageRepositoryInterface
//upadte settings if needed
$body->path = $path;
$this->settings->updateMenu($body);
$this->settings->updateTags($this->sort->tags());
$tags = $this->sort->tags($this->content->loadAllPages());
$this->settings->updateTags($tags, false);
// if new page added, update current index in Settings file
if ($task == 'create') {
$this->settings->updatePageIndex();

View file

@ -9,6 +9,9 @@ class AssetService
protected $themeTestImagePath;
protected $themeTestCSSPath;
protected $themeTestScriptsPath;
protected $themeCSSPath;
protected $themeImagePath;
protected $themeScriptsPath;
protected $themes;
protected $currentTheme;
@ -34,8 +37,8 @@ class AssetService
$imagePath = '';
$cssPath = '';
$scriptPath = '';
($live) ? $imagePath = $this->themeImagePath : $imagePath = $this->themeTestImagePath;
($live) ? $cssPath = $this->themeCSSPath : $cssPath = $this->themeTestCSSPath;
($live) ? $imagePath = $this->themeImagePath : $imagePath = $this->themeTestImagePath;
($live) ? $cssPath = $this->themeCSSPath : $cssPath = $this->themeTestCSSPath;
($live) ? $scriptPath = $this->themeScriptsPath : $scriptPath = $this->themeTestScriptsPath;
//get current theme
foreach (

View file

@ -31,6 +31,7 @@ class RenderService
public function publishAll()
{
$message = [];
$result = [];
$dynamicRender = $this->settings->getGlobal()['dynamicRender'];
if (isset($dynamicRender) && $dynamicRender === 'true') {
$message = [
@ -38,10 +39,13 @@ class RenderService
'type' => 'RENDER_SUCCESS',
];
} else {
//grab all pages to pass to routines
$pages = $this->contents->loadAllPages();
try {
$this->archive();
$this->tags();
$this->pages();
$this->archive($pages);
$this->tags($pages);
$this->pages($pages);
$message = [
'message' => 'Site Rendered. GOOD EFFORT',
'type' => 'RENDER_SUCCESS',
@ -57,10 +61,10 @@ class RenderService
return $message;
}
public function archive()
public function archive($pages)
{
$template = $this->theme . '.archive';
$pageData = $this->sort->archive(false);
$pageData = $this->sort->archive($pages, false);
$location = '../public/archives.html';
File::put(
$location,
@ -68,11 +72,17 @@ class RenderService
->with($pageData)
->render()
);
$done = [
'status' => 'archive_complete',
];
return $done;
}
public function tags()
public function tags($pages)
{
$data = $this->sort->tags(false);
$data = $this->sort->tags($pages, false);
foreach ($data['tags'] as $item) {
//$template = 'tags.twig';
$template = $this->theme . '.tags';
@ -99,18 +109,25 @@ class RenderService
->render()
);
}
unset($data);
$done = [
'status' => 'tags_complete',
];
return $done;
}
public function pages()
public function pages($pages)
{
$pages = $this->contents->loadAllPages();
foreach ($pages as $page) {
$template;
$page['layout'] == 'index' ?
$template = $this->theme . '.index' :
$template = $this->theme . '.page';
$template = $this->theme . '.' . $page['layout'];
$pageData = $this->sort->page($page, false);
$pageData = $this->sort->page($page, $pages, false);
if (str_contains($page['layout'], 'index')) {
$location = '../public/index.html';
@ -127,6 +144,7 @@ class RenderService
}
}
File::put($location, view($template)->with($pageData)->render());
unset($pageData);
}
}
}

View file

@ -17,9 +17,30 @@ class ContentService
{
protected $files = [];
protected $config = [];
protected $environment;
protected $converter;
protected $pageCount = "";
public function __construct()
{
//set up conversion handler
$this->environment = new Environment($this->config);
$this->environment->addExtension(new CommonMarkCoreExtension());
// Add the extension
$this->environment->addExtension(new FrontMatterExtension());
//Add Strikethrough rendering
$this->environment->addExtension(new StrikethroughExtension());
//add attributes to elements in markdown
$this->environment->addExtension(new AttributesExtension());
//add table rendering
$this->environment->addExtension(new TableExtension());
// Instantiate the converter engine and start converting some Markdown!
$this->converter = new MarkdownConverter($this->environment);
$this->loadPages(env('PAGES_PATH'));
}
@ -34,32 +55,16 @@ class ContentService
foreach ($files as $file) {
$this->files[] = $file;
}
$this->pageCount = count($this->files);
}
public function loadAllPages()
{
$environment = new Environment($this->config);
$environment->addExtension(new CommonMarkCoreExtension());
// Add the extension
$environment->addExtension(new FrontMatterExtension());
//Add Strikethrough rendering
$environment->addExtension(new StrikethroughExtension());
//add attributes to elements in markdown
$environment->addExtension(new AttributesExtension());
//add table rendering
$environment->addExtension(new TableExtension());
// Instantiate the converter engine and start converting some Markdown!
$converter = new MarkdownConverter($environment);
$contents = [];
foreach ($this->files as $file) {
//get meta and html from file
$result = $converter->convertToHtml(file_get_contents($file));
$result = $this->converter->convert(file_get_contents($file));
$meta = [];
if ($result instanceof RenderedContentWithFrontMatter) {
$meta = $result->getFrontMatter();
@ -87,6 +92,7 @@ class ContentService
$laundry = new HtmlSanitizer($soap);
$scrubbed = $laundry->sanitize($result->getContent());
unset($result);
if (isset($meta['feature'])) {
$featureList = explode(',', $meta['feature']);
} else {
@ -123,7 +129,6 @@ class ContentService
}
}
}
//sort attributes into page object
$page = [
'id' => $meta['id'],
@ -164,6 +169,8 @@ class ContentService
if (!$found) {
array_push($contents, $page);
}
unset($file);
gc_collect_cycles();
}
$collection = collect($contents);
$sorted = $collection->sortBy([

View file

@ -29,8 +29,6 @@ class SortingService
'keywords' => isset($global['keywords'])
? $global['keywords']
: 'fipamo, blog, jamstack, php, markdown, js',
'menu' => $this->settings->getMenu(),
'tags' => $this->settings->getTags(),
'description' => $global['descriptions'],
'image' => $global['base_url'] . $global['background'],
'baseURL' => $global['base_url'],
@ -39,15 +37,14 @@ class SortingService
];
}
public function tags($debug = true)
public function tags($pages, $debug = true)
{
$pages = $this->contents->loadAllPages();
foreach ($pages as $page) {
$temp = [];
if (isset($page['tags'])) {
$temp = explode(',', $page['tags']);
foreach ($temp as $tag) {
$label = trim($tag);
$label = safeString(trim($tag));
if (!find($this->p_tags, ['tag_name' => $label])) {
array_push($this->p_tags, [
'tag_name' => $label,
@ -102,9 +99,8 @@ class SortingService
return $tagged;
}
public function archive($debug = true)
public function archive($pages, $debug = true)
{
$pages = $this->contents->loadAllPages();
$years = [];
$archive = [];
foreach ($pages as $page) {
@ -168,7 +164,7 @@ class SortingService
return $archiveData;
}
public function page($page, $debug = true)
public function page($page, $pages, $debug = true)
{
$pageOptions = [];
$tags = [];
@ -205,7 +201,6 @@ class SortingService
$recent = [];
$featured = [];
$limit = 4;
$pages = $this->contents->loadAllPages();
foreach ($pages as $item) {
if (
!$item['deleted'] &&

View file

@ -2,7 +2,7 @@
"name": "are0h/fipamo",
"type": "project",
"descriptions": "The most chill no database content framework ever.",
"version": "3.0.0-beta",
"version": "3.1.0-beta",
"keywords": ["fipamo","blog","activty-pub","laravel", "framework"],
"authors": [
{
@ -18,13 +18,13 @@
"require": {
"php": "^8.2",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^11.0",
"laravel/framework": "^12.0",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.8",
"league/commonmark": "^2.4",
"lodash-php/lodash-php": "^0.09.0",
"lodash-php/lodash-php": "^0.1.0",
"mnapoli/front-yaml": "^2.0",
"nesbot/carbon": "^2.72",
"nesbot/carbon": "^3.0",
"olegatro/html-sanitizer-relative": "^1.0",
"rbdwllr/reallysimplejwt": "^5.0",
"symfony/html-sanitizer": "^7.1",
@ -37,7 +37,7 @@
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^8.1",
"phpunit/phpunit": "^10.1",
"phpunit/phpunit": "^11.0",
"spatie/laravel-ignition": "^2.0"
},
"autoload": {

2524
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,69 +4,116 @@
* Small, reusable classes
*/
/*
* Padding
*/
.p1 { padding: 5px; }
.pt1 { padding-top: 5px; }
.pl1 { padding-left: 5px; }
.pr1 { padding-right: 5px; }
.pb1 { padding-bottom: 5px; }
.p2 { padding: 10px; }
.pt2 { padding-top: 10px; }
.pl2 { padding-left: 10px; }
.pr2 { padding-right: 10px; }
.pb2 { padding-bottom: 10px; }
.p1 {
padding: 5px;
}
.pt1 {
padding-top: 5px;
}
.pl1 {
padding-left: 5px;
}
.pr1 {
padding-right: 5px;
}
.pb1 {
padding-bottom: 5px;
}
.p2 {
padding: 10px;
}
.pt2 {
padding-top: 10px;
}
.pl2 {
padding-left: 10px;
}
.pr2 {
padding-right: 10px;
}
.pb2 {
padding-bottom: 10px;
}
/*
* Margin
*/
.mt1 { margin-top: 5px; }
.ml1 { margin-left: 5px; }
.mr1 { margin-right: 5px; }
.mb1 { margin-bottom: 5px; }
.mt2 { margin-top: 10px; }
.ml2 { margin-left: 10px; }
.mr2 { margin-right: 10px; }
.mb2 { margin-bottom: 10px; }
.mt1 {
margin-top: 5px;
}
.ml1 {
margin-left: 5px;
}
.mr1 {
margin-right: 5px;
}
.mb1 {
margin-bottom: 5px;
}
.mt2 {
margin-top: 10px;
}
.ml2 {
margin-left: 10px;
}
.mr2 {
margin-right: 10px;
}
.mb2 {
margin-bottom: 10px;
}
/*
* Layout
*/
.row {
display: flex;
display: flex;
}
.col {
display: flex;
flex-direction: column;
display: flex;
flex-direction: column;
}
.items-center {
display: flex;
align-items: center;
display: flex;
align-items: center;
}
.justify-center {
display: flex;
justify-content: flex;
display: flex;
justify-content: flex;
}
.grow {
flex-grow: 1;
flex-grow: 1;
}
/*
* Borders and rounding
*/
.rounded {
overflow: hidden;
border-radius: 5px;
overflow: hidden;
border-radius: 5px;
}
/*
* Show/Hide
*/
.hide-el {
display: none;
visibility: hidden;
opacity: 0;
}
.show-grid {
display: grid;
visibility: visible;
opacity: 1;
}

View file

@ -76,7 +76,7 @@ sup {
pre,
code {
background: var(--black);
background: var(--primary);
color: var(--secondary-highlight);
border-radius: 3px;
padding: 3px;

View file

@ -6,6 +6,14 @@ svg.icon {
color: var(--secondary);
}
svg.editor-icon {
transition: all 0.3s linear;
fill: var(--primary);
width: 25px;
height: 25px;
color: var(--primary);
}
svg#move-menu-item {
fill: var(--secondary-highlight);
top: 8px;

View file

@ -1,10 +1,8 @@
article.navigation {
width: 100%;
max-width: 900px;
margin: 100px auto;
main > article > section.settings-tabs > section#site-menu {
padding: 10px 0;
}
article.navigation > section > div.nav-item {
main > article > section.settings-tabs > section#site-menu > div.nav-item {
display: block;
width: 98%;
background: var(--secondary);
@ -15,7 +13,7 @@ article.navigation > section > div.nav-item {
cursor: move;
}
article.navigation > section > div.nav-item > label {
main > article > section.settings-tabs > section#site-menu > label {
display: inline-block;
padding: 5px;
margin: 12px 0 0 10px;
@ -23,24 +21,30 @@ article.navigation > section > div.nav-item > label {
cursor: move;
}
article.navigation > section > div.nav-item > div#nav-btns {
main > article > section.settings-tabs > section#site-menu > div.nav-item > div#nav-btns {
float: right;
padding: 5px;
position: relative;
}
article.navigation > section > div.nav-item > div#nav-btns button {
main
> article
> section.settings-tabs
> section#site-menu
> div.nav-item
> div#nav-btns
button {
margin-left: 5px;
}
@media only screen and (max-width: 500px) {
article.navigation > section > div.nav-item > label {
main > article > section.settings-tabs > section#site-menu > div.nav-item > label {
width: 55%;
margin: 0;
font-size: 0.8em;
}
article.navigation > section i {
main > article > section.settings-tabs > section#site-menu > section i {
font-size: 1.5em;
}
}

View file

@ -2,7 +2,7 @@ code[class*="language-"],
pre[class*="language-"] {
color: var(--editor-primary);
background: none;
font-family: var(--mono-type);
font-family: var(--base-type);
text-align: left;
white-space: pre;
word-spacing: normal;
@ -109,3 +109,75 @@ pre[class*="language-"] {
.token.bold {
font-weight: normal;
}
.hljs-comment {
color: #697070;
}
.hljs-tag,
.hljs-punctuation {
color: #444a;
}
.hljs-tag .hljs-name,
.hljs-tag .hljs-attr {
color: #444;
}
.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta .hljs-keyword,
.hljs-doctag,
.hljs-name {
font-weight: bold;
}
/* User color: hue: 0 */
.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
color: #d49e4f;
}
.hljs-title,
.hljs-section {
color: #dc37e0;
font-weight: bold;
}
.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-operator,
.hljs-selector-pseudo {
color: #ab5656;
}
/* Language color: hue: 90; */
.hljs-literal {
color: #695;
}
.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
color: #5bc1c5;
}
/* Meta color: hue: 200 */
.hljs-meta {
color: #1f7199;
}
.hljs-meta .hljs-string {
color: #38a;
}
/* Misc effects */
.hljs-emphasis {
font-style: italic;
color: #c3bc50;
}
.hljs-strong {
font-weight: bold;
color: #ecf;
}

View file

@ -1,12 +1,32 @@
/* FILE MANAGER */
main > section.file-manager {
width: 100%;
section.meta {
background: var(--primary-highlight);
padding: 20px 0;
margin-top: 75px;
display: grid;
grid-template-columns: 70% 1fr;
width: 90%;
max-width: 800px;
margin: 50px auto 0;
position: absolute;
top: 225px;
left: 50%;
transform: translate(-50%, -50%);
padding: 10px;
z-index: 500;
border-radius: 3px;
transition: all 0.2s linear;
font-size: 0.8em;
color: var(--primary);
}
main > section.file-manager label.list-title {
/* FILE MANAGER */
main > section > div.file-manager {
width: 100%;
}
button#files-upload {
background: var(--secondary);
}
main > section > div.file-manager label.list-title {
width: 100%;
max-width: 900px;
margin: 0 auto;
@ -14,22 +34,22 @@ main > section.file-manager label.list-title {
color: var(--primary);
}
main > section.file-manager > div.file-drop {
main > section > div.file-manager > div.file-drop {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 100px;
background: var(--white);
color: var(--primary);
min-height: 108px;
color: var(--white);
background: var(--primary);
vertical-align: middle;
border-radius: 5px;
border-radius: 3px;
max-width: 900px;
margin: 10px auto;
margin: 0 auto;
}
main > section.file-manager > div.page-images-list,
main > section.file-manager > div.page-files-list {
main > section > div.file-manager > div.page-images-list,
main > section > div.file-manager > div.page-files-list {
max-width: 900px;
width: 100%;
margin: 10px auto;
@ -38,8 +58,8 @@ main > section.file-manager > div.page-files-list {
gap: 10px;
}
main > section.file-manager > div.page-images-list > div,
main > section.file-manager > div.page-files-list > div {
main > section > div.file-manager > div.page-images-list > div,
main > section > div.file-manager > div.page-files-list > div {
width: 100%;
height: 150px;
border-radius: 3px;
@ -48,102 +68,90 @@ main > section.file-manager > div.page-files-list > div {
cursor: pointer;
}
main > section.file-manager > div.page-images-list > div > div.item-progress {
main > section > div.file-manager > div.page-images-list > div > div.item-progress {
width: 100%;
height: 100%;
background: var(--primary);
}
main > section.text-editor > div.text-editor-control button {
main > div.text-editor-control button {
border-radius: 0;
}
main > section.text-editor > div.text-editor-control button > i {
main > div.text-editor-control button > i {
font-size: 1.6em;
}
main > section.text-editor > div.text-editor-control > button:nth-child(1) {
main > div.text-editor-control > button:nth-child(1) {
border-radius: 3px 0 0 3px;
}
main > section.text-editor > div.text-editor-control > button:nth-child(10) {
main > div.text-editor-control > button:nth-child(10) {
border-radius: 0 3px 3px 0;
}
main > section.file-manager > div.page-images-list > div > button.media-remove,
main > section.file-manager > div.page-files-list > div > button.media-remove {
main > section > div.file-manager > div.page-images-list > div > button.media-remove,
main > section > div.file-manager > div.page-files-list > div > button.media-remove {
color: var(--white);
margin: 5px;
padding: 10px 15px 7px;
font-size: 1.5em;
}
main > section.file-manager > div.page-images-list > div.video-item > video {
main > section > div.file-manager > div.page-images-list > div.video-item > video {
object-fit: cover;
height: 100%;
width: 100%;
}
main > section.file-manager > div.page-images-list > div.video-item > button,
main > section.file-manager > div.page-files-list > div.audio-item > button,
main > section.file-manager > div.page-files-list > div.file-item > button {
main > section > div.file-manager > div.page-images-list > div.video-item > button,
main > section > div.file-manager > div.page-files-list > div.audio-item > button,
main > section > div.file-manager > div.page-files-list > div.file-item > button {
position: absolute;
top: 0;
left: 0;
}
main > section.file-manager > div.page-files-list > div.audio-item {
main > section > div.file-manager > div.page-files-list > div.audio-item {
background: url("/assets/images/global/upload-audio.png") no-repeat center center /
cover;
}
main > section.file-manager > div.page-files-list > div.file-item {
main > section > div.file-manager > div.page-files-list > div.file-item {
background: url("/assets/images/global/upload-doc.png") no-repeat center center / cover;
}
main > section.file-manager > div.page-files-list > div.file-item > a {
main > section > div.file-manager > div.page-files-list > div.file-item > a {
position: absolute;
bottom: 0;
background: var(--secondary);
padding: 2px;
}
main > section.file-manager > div.page-files-list > div.audio-item > audio {
main > section > div.file-manager > div.page-files-list > div.audio-item > audio {
height: 100%;
width: 100%;
}
/* PAGE META */
main > section.page-meta {
width: 100%;
background: var(--secondary-highlight);
main > section > div.page-meta {
padding: 0 10px;
}
main > section.page-meta > div.page-meta-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
width: 100%;
max-width: 900px;
margin: 0 auto;
color: var(--secondary);
}
main section.page-meta textarea#post-title-text {
main div.page-meta textarea#post-title-text {
font-family: var(--base-type);
width: 100%;
height: 80px;
font-size: 2em;
}
main section.page-meta textarea#post-tags {
main div.page-meta textarea#post-tags {
font-family: var(--base-type);
width: 100%;
height: 80px;
}
main section.page-meta select {
main div.page-meta select {
background: var(--primary);
color: var(--secondary);
border-radius: 3px;
@ -154,11 +162,11 @@ main section.page-meta select {
font-size: 1.5em;
}
main section.page-meta div.page-options {
main div.page-meta div.page-options {
width: 100%;
}
main section.page-meta div.page-meta-wrapper div.page-options button {
main div.page-meta div.page-options button {
width: 25%;
height: 45px;
transition: all 0.3s linear;
@ -170,18 +178,19 @@ main section.page-meta div.page-meta-wrapper div.page-options button {
}
main
> section.page-meta
> div.page-meta-wrapper
> section.meta
> div.page-meta
> div.page-options
> button.post-option-btn:nth-child(3) {
> div
> button.post-option-btn:nth-child(1) {
border-radius: 3px 0 0 3px;
}
main > section.page-meta > div.page-meta-wrapper > div.page-options > a > button {
main > section.meta > div.page-meta > div.page-options > div > a > button {
border-radius: 0 3px 3px 0;
}
main section.page-meta div.page-meta-wrapper div.page-created input {
main div.page-meta div.page-created input {
display: none;
visibility: hidden;
}
@ -191,7 +200,24 @@ main section.page-meta div.page-meta-wrapper div.page-created input {
main > section.text-editor {
width: 100%;
max-width: 900px;
margin: 10px auto;
margin: 90px auto;
}
main > section.text-editor button {
background: none;
width: 50px;
height: 50px;
}
main > section.text-editor .page-title #post-title-text {
font-size: 2em;
font-family: var(--base-type);
width: 99%;
padding: 5px;
font-weight: 550;
height: 48px;
border-bottom: 1px solid var(--primary-highlight);
border-radius: 0;
}
main section.text-editor .icon-hide {
@ -199,9 +225,22 @@ main section.text-editor .icon-hide {
visibility: hidden;
}
main > section.text-editor > div.text-editor-control {
main > div.text-editor-control {
display: grid;
grid-template-columns: repeat(10, 1fr);
grid-template-columns: repeat(8, 1fr);
position: absolute;
z-index: 400;
left: 10%;
top: 95px;
transition: all 10ms linear;
background: var(--secondary);
border-radius: 3px;
}
main > div.text-editor-control button {
background: none;
width: 45px;
height: 45px;
}
.control-freeze {
@ -226,7 +265,7 @@ main > section.text-editor > div.edit-post-wrapper textarea:focus {
main section.text-editor div.edit-post-wrapper #edit,
main section.text-editor div.edit-post-wrapper #highlight {
font-family: var(--mono-type);
font-family: var(--base-type);
border: 0;
width: 100%;
min-height: 300px;
@ -270,10 +309,10 @@ main section.text-editor div.edit-post-wrapper pre code {
/* RESPONSIVE */
@media only screen and (max-width: 900px) {
main > section.file-manager > div.file-drop,
main > section.file-manager > div.page-images-list,
main > section.file-manager > div.page-files-list,
main > section.page-meta > div.page-meta-wrapper,
main > div.file-manager > div.file-drop,
main > div.file-manager > div.page-images-list,
main > div.file-manager > div.page-files-list,
main > div.page-meta > div.page-meta-wrapper,
main > section.text-editor {
width: 97%;
}
@ -284,20 +323,35 @@ main section.text-editor div.edit-post-wrapper pre code {
}
@media only screen and (max-width: 480px) {
main > section.file-manager > div.page-images-list,
main > section.file-manager > div.page-files-list {
section.meta {
grid-template-columns: 100%;
top: 390px;
}
button#files-upload {
width: 85%;
font-size: 0.95em;
}
main > section > div.file-manager > div.page-images-list > div,
main > section > div.file-manager > div.page-files-list > div {
height: 110px;
}
main > div.file-manager > div.page-images-list,
main > div.file-manager > div.page-files-list {
grid-template-columns: 1fr 1fr 1fr;
}
main > section.file-manager > div.file-drop,
main > section.file-manager > div.page-images-list,
main > section.file-manager > div.page-files-list,
main > section.page-meta > div.page-meta-wrapper,
main > div.file-manager > div.file-drop,
main > div.file-manager > div.page-images-list,
main > div.file-manager > div.page-files-list,
main > div.page-meta > div.page-meta-wrapper,
main > section.text-editor {
width: 95%;
}
main > section.page-meta > div.page-meta-wrapper {
main > div.page-meta > div.page-meta-wrapper {
grid-template-columns: 1fr;
}
@ -327,6 +381,11 @@ main section.text-editor div.edit-post-wrapper pre code {
font-size: 0.8em;
}
main > section.text-editor .page-title #post-title-text {
font-size: 1.2em;
height: 40px;
}
.control-freeze {
width: 96%;
}

View file

@ -17,7 +17,7 @@ article[class="settings"] label {
}
article.settings div.tab-toolbar button {
width: 180px;
width: 120px;
height: 40px;
color: var(--secondary);
}

View file

@ -8,7 +8,7 @@ export default class NavActions {
//--------------------------
syncMenu() {
let navData = [];
let items = document.getElementById('nav-items').children;
let items = document.getElementById('site-menu').children;
for (let index = 0; index < items.length; index++) {
navData.push({
title: items[index].getElementsByTagName('label')[0].innerHTML,

View file

@ -20,7 +20,7 @@ export default class NavIndex {
start() {
//grabs elements and makes them sortables
let self = this;
Sortable.create(document.getElementById('nav-items'), {
Sortable.create(document.getElementById('site-menu'), {
onUpdate: () => {
new NavActions().syncMenu().then(data => {
notify.alert('Updating Menu', null);
@ -68,7 +68,7 @@ export default class NavIndex {
case 'edit-item':
self.processing = false;
window.location =
'/dashboard/page/edit/' + e.target.getAttribute('data-uuid');
'/dashboard/pages/edit/' + e.target.getAttribute('data-uuid');
break;
}
}

View file

@ -16,9 +16,12 @@ export default class PostEditor {
//--------------------------
// constructor
//--------------------------
//REFACTOR: This file is getting too big and needs to be rethought out
constructor() {
this.processing = false;
let self = 'this';
this.textSelected = false;
this.activeSelect = false;
let self = this;
this.cr = new ContentRequest(null, document.getElementById('notify-progress'));
this.mr = new Maintenance(null, null);
this.urlPieces = document.URL.split('/');
@ -38,6 +41,83 @@ export default class PostEditor {
.querySelector('.file-manager')
.getAttribute('data-layout');
}
let textEdit = document.getElementById('edit');
let control = document.querySelector('.text-editor-control');
textEdit.addEventListener('selectionchange', e => {
let start = e.target.selectionStart;
let end = e.target.selectionEnd;
if (start != end) {
this.activeSelect = true;
if (!self.textSelected) {
self.textSelected = true;
control.classList.remove('hide-el');
control.classList.add('show-grid');
}
}
});
textEdit.addEventListener('click', e => {
let start = e.target.selectionStart;
let end = e.target.selectionEnd;
if (start == end) {
if (self.textSelected) {
self.textSelected = false;
this.activeSelect = false;
control.classList.add('hide-el');
control.classList.remove('show-grid');
}
}
});
textEdit.addEventListener('blur', e => {
control.classList.add('hide-el');
control.classList.remove('show-grid');
});
textEdit.addEventListener('mouseup', e => {
if (window.getSelection().toString().length > 0) {
// Get selected text and encode it
const selection = encodeURIComponent(
window.getSelection().toString()
).replace(/[!'()*]/g, escape);
// Find out how much (if any) user has scrolled
var scrollTop =
window.pageYOffset !== undefined
? window.pageYOffset
: (
document.documentElement ||
document.body.parentNode ||
document.body
).scrollTop;
// Get cursor position
let posX = event.clientX - 110;
let posY = event.clientY + 20 + scrollTop;
let rect = document.querySelector('.text-editor').getBoundingClientRect();
let tangle = document
.querySelector('.text-editor-control')
.getBoundingClientRect();
//check for left side
if (posX < rect.left) {
posX = rect.left;
}
//check for right side
let editorWidth = rect.right - rect.left;
let controlWidth = tangle.right - tangle.left;
if (controlWidth + posX - rect.left > editorWidth) {
let adjust = controlWidth + posX - rect.left - editorWidth;
posX = posX - adjust;
}
document.querySelector('.text-editor-control').style.left = posX + 'px';
document.querySelector('.text-editor-control').style.top = posY + 'px';
}
});
if (document.getElementById('edit')) {
this.editor = new TextEditor(
document.getElementById('edit'),
@ -105,6 +185,26 @@ export default class PostEditor {
);
}
}
let metabtn = document.getElementById('');
let metas = document.querySelectorAll('#meta-info-toggle');
let meta = document.querySelector('.meta');
for (var i = 0, length = metas.length; i < length; i++) {
metas[i].addEventListener('click', e => {
let metaO = window.getComputedStyle(meta).getPropertyValue('opacity');
if (metaO == 0) {
meta.classList.remove('hide-el');
meta.classList.add('show-grid');
} else {
meta.classList.add('hide-el');
meta.classList.remove('show-grid');
}
});
}
document.getElementById('files-upload').addEventListener('click', e => {
document.getElementById('page-files-upload').click();
});
}
//--------------------------
// event handlers
@ -127,9 +227,13 @@ export default class PostEditor {
}
if (currentOption != null) {
let active = currentOption.getAttribute('data-active');
active == 'false'
? currentOption.setAttribute('data-active', 'true')
: currentOption.setAttribute('data-active', 'false');
if (active == 'false') {
currentOption.setAttribute('data-active', 'true');
currentOption.setAttribute('aria-pressed', 'true');
} else {
currentOption.setAttribute('data-active', 'false');
currentOption.setAttribute('aria-pressed', 'false');
}
}
}
handleEditorOptions(e) {
@ -159,7 +263,7 @@ export default class PostEditor {
notify.alert(r.message, true);
} else {
notify.alert(r.message, true);
window.location = '/dashboard/page/edit/' + r.id;
window.location = '/dashboard/pages/edit/' + r.id;
}
}
})

View file

@ -1,10 +1,11 @@
import * as DataEvent from '../events/DataEvent.js';
import EventEmitter from '../events/EventEmitter.js';
import * as EditorEvent from '../events/EditorEvent.js';
import Prism from '../vendor/prism.js';
//import MarkdownLang from '../vendor/lang/prism-markdown.js';
//Prism.language.md = '../vendor/lang/prism-markdown.js';
//Prism.languauges['markdown'];
import hljs from '../vendor/highlight/es/core.js';
import html from '../vendor/highlight/es/languages/xml.min.js';
import md from '../vendor/highlight/es/languages/markdown.min.js';
hljs.registerLanguage('html', html);
hljs.registerLanguage('markdown', md);
class TextEditor extends EventEmitter {
/**
* Text Editor UI Component
@ -25,6 +26,7 @@ class TextEditor extends EventEmitter {
document.body.addEventListener('scroll', e => {
var fixLimit = scrollLimit;
//console.log('POSITION', document.body.scrollTop + ' : ' + fixLimit);
/**
if (document.body.scrollTop + 5 >= fixLimit) {
document
.querySelector('.text-editor-control')
@ -34,6 +36,7 @@ class TextEditor extends EventEmitter {
.querySelector('.text-editor-control')
.classList.remove('control-freeze');
}
**/
});
document.getElementById('edit').addEventListener('input', e => {
let result_element = document.querySelector('#highlight-content');
@ -49,7 +52,12 @@ class TextEditor extends EventEmitter {
editorHeight + 'px';
e.target.style.height = editorHeight + 30 + 'px'; //TODO: yeah, it's ugly but it works for now, fix soon
// Syntax Highlight
Prism.highlightElement(result_element);
let shiny = hljs.highlight(this.textEditor.value, {
language: 'markdown',
ignoreIllegals: true
}).value;
result_element.innerHTML = shiny;
});
document.getElementById('edit').addEventListener('scroll', e => {
/* Scroll result to scroll coords of event - sync with textarea */
@ -122,30 +130,54 @@ class TextEditor extends EventEmitter {
let end = this.textEditor.selectionEnd;
let selectedText = this.textEditor.value.substring(start, end);
let formatText = this.textEditor.value.substring(start - 1, end + 1);
let insert = '';
switch (e.target.id) {
case 'edit-bold':
insert = '**' + selectedText + '**';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
if (formatText.includes('*')) {
insert = selectedText;
this.textEditor.value =
this.textEditor.value.substring(0, start - 2) +
insert +
this.textEditor.value.substring(end + 2, len);
} else {
insert = '**' + selectedText + '**';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
}
break;
case 'edit-italic':
insert = '*' + selectedText + '*';
//console.log(this.textEditor);
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
if (formatText.includes('*')) {
insert = selectedText;
this.textEditor.value =
this.textEditor.value.substring(0, start - 1) +
insert +
this.textEditor.value.substring(end + 1, len);
} else {
insert = '*' + selectedText + '*';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
}
break;
case 'edit-strikethrough':
insert = '~~' + selectedText + '~~';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
if (formatText.includes('~')) {
insert = selectedText;
this.textEditor.value =
this.textEditor.value.substring(0, start - 2) +
insert +
this.textEditor.value.substring(end + 2, len);
} else {
insert = '~~' + selectedText + '~~';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
}
break;
case 'edit-header1':
insert = '# ' + selectedText + '\n';

View file

@ -0,0 +1,33 @@
## Subresource Integrity
If you are loading Highlight.js via CDN you may wish to use [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) to guarantee that you are using a legimitate build of the library.
To do this you simply need to add the `integrity` attribute for each JavaScript file you download via CDN. These digests are used by the browser to confirm the files downloaded have not been modified.
```html
<script
src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"
integrity="sha384-5xdYoZ0Lt6Jw8GFfRP91J0jaOVUq7DGI1J5wIyNi0D+eHVdfUwHR4gW6kPsw489E"></script>
<!-- including any other grammars you might need to load -->
<script
src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/go.min.js"
integrity="sha384-HdearVH8cyfzwBIQOjL/6dSEmZxQ5rJRezN7spps8E7iu+R6utS8c2ab0AgBNFfH"></script>
```
The full list of digests for every file can be found below.
### Digests
```
sha384-+KkqXkoHKtuOmUzhZ0BjyV0qjljnS+z6i4fELMEg5brFPtmDIog4zZMhylaBTsVi /es/languages/markdown.js
sha384-E7UvgBH6skA1FIOcn3B2c68GtJzrmZlOOC5p/fsxwihTZG/bBedJZu5PC1+kGX7q /es/languages/markdown.min.js
sha384-9ECFzM+oWDye4s/MFx3QUXGo4mW43+SyLpWUDeQtWup6GZJ+KHFxVS89PmZt/fzl /es/languages/xml.js
sha384-PQrsaWeWrBiE1CFRw8K335CaJuQRTjDGm73vn8bXvlwaw6RyqWObdvMTBS8B75NN /es/languages/xml.min.js
sha384-Sk9XW/OOutdl6KS1M9Wson0imuqr0LkpoTRDHi5QFH4MWe0aViI5d86BOVkh8Ds0 /languages/markdown.js
sha384-Rv26WbhHH4MDPzeExq4ECmZUYF942tlfVhqA91Drw1P+Ey55KjihLF9RJENxjWr1 /languages/markdown.min.js
sha384-Pgzg6a405W6U1xFjjSs5i8d7V81Tmt/TYn8HFOa+u1psDc8cbs8nC7BuyNXbWWRK /languages/xml.js
sha384-FQjSArDMJE4WMAJGcCNAV+IXIOljcIxM3UFAD2vxjedWmBnnDaAyqRG7AQHf/uM/ /languages/xml.min.js
sha384-5dSxlNsT+SvIYU+LRNU9rE8E1caCx7VgFpS0sFxkmBDNOPSrDm+LnAfWU2KqdRxe /highlight.js
sha384-uqKMpsuSwEPTJn84gp+xJM5kh6K57NDyNULPf8l7XrWazf6gQDCI1tN3r85Jo16X /highlight.min.js
```

View file

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2006, Ivan Sagalaev.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,45 @@
# Highlight.js CDN Assets
[![install size](https://packagephobia.now.sh/badge?p=highlight.js)](https://packagephobia.now.sh/result?p=highlight.js)
**This package contains only the CDN build assets of highlight.js.**
This may be what you want if you'd like to install the pre-built distributable highlight.js client-side assets via NPM. If you're wanting to use highlight.js mainly on the server-side you likely want the [highlight.js][1] package instead.
To access these files via CDN:<br>
https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@latest/build/
**If you just want a single .js file with the common languages built-in:
<https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@latest/build/highlight.min.js>**
---
## Highlight.js
Highlight.js is a syntax highlighter written in JavaScript. It works in
the browser as well as on the server. It works with pretty much any
markup, doesnt depend on any framework, and has automatic language
detection.
If you'd like to read the full README:<br>
<https://github.com/highlightjs/highlight.js/blob/main/README.md>
## License
Highlight.js is released under the BSD License. See [LICENSE][7] file
for details.
## Links
The official site for the library is at <https://highlightjs.org/>.
The Github project may be found at: <https://github.com/highlightjs/highlight.js>
Further in-depth documentation for the API and other topics is at
<http://highlightjs.readthedocs.io/>.
A list of the Core Team and contributors can be found in the [CONTRIBUTORS.md][8] file.
[1]: https://www.npmjs.com/package/highlight.js
[7]: https://github.com/highlightjs/highlight.js/blob/main/LICENSE
[8]: https://github.com/highlightjs/highlight.js/blob/main/CONTRIBUTORS.md

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
/*!
Highlight.js v11.11.1 (git: 08cb242e7d)
(c) 2006-2025 Josh Goebel <hello@joshgoebel.com> and other contributors
License: BSD-3-Clause
*/
function e(t){return t instanceof Map?t.clear=t.delete=t.set=()=>{
throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
throw Error("set is read-only")
}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{
const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i)
})),t}class t{constructor(e){
void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}
ignoreMatch(){this.isMatchIgnored=!0}}function n(e){
return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;")
}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope
;class r{constructor(e,t){
this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{
if(e.startsWith("language:"))return e.replace("language:","language-")
;if(e.includes(".")){const n=e.split(".")
;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")
}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}
closeNode(e){s(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
this.buffer+=`<span class="${e}">`}}const o=(e={})=>{const t={children:[]}
;return Object.assign(t,e),t};class a{constructor(){
this.rootNode=o(),this.stack=[this.rootNode]}get top(){
return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
this.top.children.push(e)}openNode(e){const t=o({scope:e})
;this.add(t),this.stack.push(t)}closeNode(){
if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e}
addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){
this.closeNode()}__addSublanguage(e,t){const n=e.root
;t&&(n.scope="language:"+t),this.add(n)}toHTML(){
return new r(this,this.options).value()}finalize(){
return this.closeAllNodes(),!0}}function l(e){
return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")}
function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")}
function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{
const t=e[e.length-1]
;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}
})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"}
function p(e){return RegExp(e.toString()+"|").exec("").length-1}
const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./
;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n
;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break}
s+=i.substring(0,e.index),
i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],
"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)}
const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",_="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",O={
begin:"\\\\[\\s\\S]",relevance:0},k={scope:"string",begin:"'",end:"'",
illegal:"\\n",contains:[O]},v={scope:"string",begin:'"',end:'"',illegal:"\\n",
contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t,
contains:[]},n);s.contains.push({scope:"doctag",
begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",
end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0})
;const r=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/)
;return s.contains.push({begin:h(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s
},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var A=Object.freeze({
__proto__:null,APOS_STRING_MODE:k,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{
scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:N,
C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number",
begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{
"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E,
MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0},
NUMBER_MODE:{scope:"number",begin:_,relevance:0},NUMBER_RE:_,
PHRASAL_WORDS_MODE:{
begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
},QUOTE_STRING_MODE:v,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/,
end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]},
RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
SHEBANG:(e={})=>{const t=/^#![ ]*\//
;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t,
end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},
TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x,
UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function j(e,t){
"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){
void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){
t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
e.__beforeBegin=j,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
void 0===e.relevance&&(e.relevance=0))}function L(e,t){
Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){
if(e.match){
if(e.begin||e.end)throw Error("begin & end are not supported with match")
;e.begin=e.match,delete e.match}}function P(e,t){
void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return
;if(e.starts)throw Error("beforeMatch cannot be used with starts")
;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t]
})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={
relevance:0,contains:[Object.assign(n,{endsParent:!0})]
},e.relevance=0,delete n.beforeMatch
},H=["of","and","for","in","not","or","if","then","parent","list","value"]
;function C(e,t,n="keyword"){const i=Object.create(null)
;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{
Object.assign(i,C(e[n],t,n))})),i;function s(e,n){
t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
;i[n[0]]=[e,$(n[0],n[1])]}))}}function $(e,t){
return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const U={},z=e=>{
console.error(e)},W=(e,...t)=>{console.log("WARN: "+e,...t)},X=(e,t)=>{
U[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),U[`${e}/${t}`]=!0)
},G=Error();function K(e,t,{key:n}){let i=0;const s=e[n],r={},o={}
;for(let e=1;e<=t.length;e++)o[e+i]=s[e],r[e+i]=!0,i+=p(t[e-1])
;e[n]=o,e[n]._emit=r,e[n]._multi=!0}function F(e){(e=>{
e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,
delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={
_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope
}),(e=>{if(Array.isArray(e.begin)){
if(e.skip||e.excludeBegin||e.returnBegin)throw z("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),
G
;if("object"!=typeof e.beginScope||null===e.beginScope)throw z("beginScope must be object"),
G;K(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{
if(Array.isArray(e.end)){
if(e.skip||e.excludeEnd||e.returnEnd)throw z("skip, excludeEnd, returnEnd not compatible with endScope: {}"),
G
;if("object"!=typeof e.endScope||null===e.endScope)throw z("endScope must be object"),
G;K(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function Z(e){
function t(t,n){
return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":""))
}class n{constructor(){
this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
addRule(e,t){
t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null)
;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|"
}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex
;const t=this.matcherRe.exec(e);if(!t)return null
;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n]
;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){
this.rules=[],this.multiRegexes=[],
this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
;let n=t.exec(e)
;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
return n&&(this.regexIndex+=n.position+1,
this.regexIndex===this.count&&this.considerAll()),n}}
if(e.compilerExtensions||(e.compilerExtensions=[]),
e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
;return e.classNameAliases=i(e.classNameAliases||{}),function n(r,o){const a=r
;if(r.isCompiled)return a
;[I,B,F,D].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))),
r.__beforeBegin=null,[T,L,P].forEach((e=>e(r,o))),r.isCompiled=!0;let c=null
;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords),
c=r.keywords.$pattern,
delete r.keywords.$pattern),c=c||/\w+/,r.keywords&&(r.keywords=C(r.keywords,e.case_insensitive)),
a.keywordPatternRe=t(c,!0),
o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(a.begin),r.end||r.endsWithParent||(r.end=/\B|\b/),
r.end&&(a.endRe=t(a.end)),
a.terminatorEnd=l(a.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)),
r.illegal&&(a.illegalRe=t(r.illegal)),
r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{
variants:null},t)))),e.cachedVariants?e.cachedVariants:V(e)?i(e,{
starts:e.starts?i(e.starts):null
}):Object.isFrozen(e)?i(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a)
})),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new s
;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function V(e){
return!!e&&(e.endsWithParent||V(e.starts))}class q extends Error{
constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}}
const J=n,Y=i,Q=Symbol("nomatch"),ee=n=>{
const i=Object.create(null),s=Object.create(null),r=[];let o=!0
;const a="Could not find the language '{}', did you forget to load/include a language module?",l={
disableAutodetect:!0,name:"Plain text",contains:[]};let p={
ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,
languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
cssSelector:"pre code",languages:null,__emitter:c};function b(e){
return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s=""
;"object"==typeof t?(i=e,
n=t.ignoreIllegals,s=t.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."),
X("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),
s=e,i=t),void 0===n&&(n=!0);const r={code:i,language:s};N("before:highlight",r)
;const o=r.result?r.result:E(r.language,r.code,n)
;return o.code=r.code,N("after:highlight",o),o}function E(e,n,s,r){
const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R)
;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n=""
;for(;t;){n+=R.substring(e,t.index)
;const s=w.case_insensitive?t[0].toLowerCase():t[0],r=(i=s,N.keywords[i]);if(r){
const[e,i]=r
;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(A+=i),e.startsWith("_"))n+=t[0];else{
const n=w.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0]
;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i
;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{
if(""===R)return;let e=null;if("string"==typeof N.subLanguage){
if(!i[N.subLanguage])return void M.addText(R)
;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top
}else e=x(R,N.subLanguage.length?N.subLanguage:null)
;N.relevance>0&&(A+=e.relevance),M.__addSublanguage(e._emitter,e.language)
})():l(),R=""}function u(e,t){
""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1
;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue}
const i=w.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}}
function h(e,t){
return e.scope&&"string"==typeof e.scope&&M.openNode(w.classNameAliases[e.scope]||e.scope),
e.beginScope&&(e.beginScope._wrap?(u(R,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),
R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{
value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t)
;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e)
;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){
for(;e.endsParent&&e.parent;)e=e.parent;return e}}
if(e.endsWithParent)return f(e.parent,n,i)}function b(e){
return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){
const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return Q;const r=N
;N.endScope&&N.endScope._wrap?(g(),
u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(),
d(N.endScope,e)):r.skip?R+=t:(r.returnEnd||r.excludeEnd||(R+=t),
g(),r.excludeEnd&&(R=t));do{
N.scope&&M.closeNode(),N.skip||N.subLanguage||(A+=N.relevance),N=N.parent
}while(N!==s.parent);return s.starts&&h(s.starts,e),r.returnEnd?0:t.length}
let _={};function y(i,r){const a=r&&r[0];if(R+=i,null==a)return g(),0
;if("begin"===_.type&&"end"===r.type&&_.index===r.index&&""===a){
if(R+=n.slice(r.index,r.index+1),!o){const t=Error(`0 width match regex (${e})`)
;throw t.languageName=e,t.badRule=_.rule,t}return 1}
if(_=r,"begin"===r.type)return(e=>{
const n=e[0],i=e.rule,s=new t(i),r=[i.__beforeBegin,i["on:begin"]]
;for(const t of r)if(t&&(t(e,s),s.isMatchIgnored))return b(n)
;return i.skip?R+=n:(i.excludeBegin&&(R+=n),
g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(r)
;if("illegal"===r.type&&!s){
const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"<unnamed>")+'"')
;throw e.mode=N,e}if("end"===r.type){const e=m(r);if(e!==Q)return e}
if("illegal"===r.type&&""===a)return R+="\n",1
;if(I>1e5&&I>3*r.index)throw Error("potential infinite loop, way more iterations than matches")
;return R+=a,a.length}const w=O(e)
;if(!w)throw z(a.replace("{}",e)),Error('Unknown language: "'+e+'"')
;const k=Z(w);let v="",N=r||k;const S={},M=new p.__emitter(p);(()=>{const e=[]
;for(let t=N;t!==w;t=t.parent)t.scope&&e.unshift(t.scope)
;e.forEach((e=>M.openNode(e)))})();let R="",A=0,j=0,I=0,T=!1;try{
if(w.__emitTokens)w.__emitTokens(n,M);else{for(N.matcher.considerAll();;){
I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=j
;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(j,e.index),e)
;j=e.index+t}y(n.substring(j))}return M.finalize(),v=M.toHTML(),{language:e,
value:v,relevance:A,illegal:!1,_emitter:M,_top:N}}catch(t){
if(t.message&&t.message.includes("Illegal"))return{language:e,value:J(n),
illegal:!0,relevance:0,_illegalBy:{message:t.message,index:j,
context:n.slice(j-100,j+100),mode:t.mode,resultSoFar:v},_emitter:M};if(o)return{
language:e,value:J(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N}
;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{
const t={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)}
;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(v).map((t=>E(t,e,!1)))
;s.unshift(n);const r=s.sort(((e,t)=>{
if(e.relevance!==t.relevance)return t.relevance-e.relevance
;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1
;if(O(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=r,c=o
;return c.secondBest=a,c}function _(e){let t=null;const n=(e=>{
let t=e.className+" ";t+=e.parentNode?e.parentNode.className:""
;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1])
;return t||(W(a.replace("{}",n[1])),
W("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}
return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return
;if(N("before:highlightElement",{el:e,language:n
}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e)
;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),
console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),
console.warn("The element with unescaped HTML:"),
console.warn(e)),p.throwUnescapedHTML))throw new q("One of your code blocks includes unescaped HTML.",e.innerHTML)
;t=e;const i=t.textContent,r=n?m(i,{language:n,ignoreIllegals:!0}):x(i)
;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n
;e.classList.add("hljs"),e.classList.add("language-"+i)
})(e,n,r.language),e.result={language:r.language,re:r.relevance,
relevance:r.relevance},r.secondBest&&(e.secondBest={
language:r.secondBest.language,relevance:r.secondBest.relevance
}),N("after:highlightElement",{el:e,result:r,text:i})}let y=!1;function w(){
if("loading"===document.readyState)return y||window.addEventListener("DOMContentLoaded",(()=>{
w()}),!1),void(y=!0);document.querySelectorAll(p.cssSelector).forEach(_)}
function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}
function k(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
s[e.toLowerCase()]=t}))}function v(e){const t=O(e)
;return t&&!t.disableAutodetect}function N(e,t){const n=e;r.forEach((e=>{
e[n]&&e[n](t)}))}Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:w,
highlightElement:_,
highlightBlock:e=>(X("10.7.0","highlightBlock will be removed entirely in v12.0"),
X("10.7.0","Please use highlightElement now."),_(e)),configure:e=>{p=Y(p,e)},
initHighlighting:()=>{
w(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},
initHighlightingOnLoad:()=>{
w(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")
},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){
if(z("Language definition for '{}' could not be registered.".replace("{}",e)),
!o)throw t;z(t),s=l}
s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&k(s.aliases,{
languageName:e})},unregisterLanguage:e=>{delete i[e]
;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:k,
autoDetection:v,inherit:Y,addPlugin:e=>{(e=>{
e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
e["before:highlightBlock"](Object.assign({block:t.el},t))
}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),r.push(e)},
removePlugin:e=>{const t=r.indexOf(e);-1!==t&&r.splice(t,1)}}),n.debugMode=()=>{
o=!1},n.safeMode=()=>{o=!0},n.versionString="11.11.1",n.regex={concat:h,
lookahead:g,either:f,optional:d,anyNumberOfTimes:u}
;for(const t in A)"object"==typeof A[t]&&e(A[t]);return Object.assign(n,A),n
},te=ee({});te.newInstance=()=>ee({});export{te as default};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
/*!
Highlight.js v11.11.1 (git: 08cb242e7d)
(c) 2006-2025 Josh Goebel <hello@joshgoebel.com> and other contributors
License: BSD-3-Clause
*/
function e(t){return t instanceof Map?t.clear=t.delete=t.set=()=>{
throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
throw Error("set is read-only")
}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{
const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i)
})),t}class t{constructor(e){
void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}
ignoreMatch(){this.isMatchIgnored=!0}}function n(e){
return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;")
}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope
;class r{constructor(e,t){
this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{
if(e.startsWith("language:"))return e.replace("language:","language-")
;if(e.includes(".")){const n=e.split(".")
;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")
}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}
closeNode(e){s(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
this.buffer+=`<span class="${e}">`}}const o=(e={})=>{const t={children:[]}
;return Object.assign(t,e),t};class a{constructor(){
this.rootNode=o(),this.stack=[this.rootNode]}get top(){
return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
this.top.children.push(e)}openNode(e){const t=o({scope:e})
;this.add(t),this.stack.push(t)}closeNode(){
if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e}
addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){
this.closeNode()}__addSublanguage(e,t){const n=e.root
;t&&(n.scope="language:"+t),this.add(n)}toHTML(){
return new r(this,this.options).value()}finalize(){
return this.closeAllNodes(),!0}}function l(e){
return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")}
function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")}
function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{
const t=e[e.length-1]
;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}
})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"}
function p(e){return RegExp(e.toString()+"|").exec("").length-1}
const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./
;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n
;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break}
s+=i.substring(0,e.index),
i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],
"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)}
const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",_="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",O={
begin:"\\\\[\\s\\S]",relevance:0},k={scope:"string",begin:"'",end:"'",
illegal:"\\n",contains:[O]},v={scope:"string",begin:'"',end:'"',illegal:"\\n",
contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t,
contains:[]},n);s.contains.push({scope:"doctag",
begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",
end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0})
;const r=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/)
;return s.contains.push({begin:h(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s
},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var A=Object.freeze({
__proto__:null,APOS_STRING_MODE:k,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{
scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:N,
C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number",
begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{
"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E,
MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0},
NUMBER_MODE:{scope:"number",begin:_,relevance:0},NUMBER_RE:_,
PHRASAL_WORDS_MODE:{
begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
},QUOTE_STRING_MODE:v,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/,
end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]},
RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
SHEBANG:(e={})=>{const t=/^#![ ]*\//
;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t,
end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},
TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x,
UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function j(e,t){
"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){
void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){
t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
e.__beforeBegin=j,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
void 0===e.relevance&&(e.relevance=0))}function L(e,t){
Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){
if(e.match){
if(e.begin||e.end)throw Error("begin & end are not supported with match")
;e.begin=e.match,delete e.match}}function P(e,t){
void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return
;if(e.starts)throw Error("beforeMatch cannot be used with starts")
;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t]
})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={
relevance:0,contains:[Object.assign(n,{endsParent:!0})]
},e.relevance=0,delete n.beforeMatch
},H=["of","and","for","in","not","or","if","then","parent","list","value"]
;function C(e,t,n="keyword"){const i=Object.create(null)
;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{
Object.assign(i,C(e[n],t,n))})),i;function s(e,n){
t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
;i[n[0]]=[e,$(n[0],n[1])]}))}}function $(e,t){
return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const U={},z=e=>{
console.error(e)},W=(e,...t)=>{console.log("WARN: "+e,...t)},X=(e,t)=>{
U[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),U[`${e}/${t}`]=!0)
},G=Error();function K(e,t,{key:n}){let i=0;const s=e[n],r={},o={}
;for(let e=1;e<=t.length;e++)o[e+i]=s[e],r[e+i]=!0,i+=p(t[e-1])
;e[n]=o,e[n]._emit=r,e[n]._multi=!0}function F(e){(e=>{
e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,
delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={
_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope
}),(e=>{if(Array.isArray(e.begin)){
if(e.skip||e.excludeBegin||e.returnBegin)throw z("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),
G
;if("object"!=typeof e.beginScope||null===e.beginScope)throw z("beginScope must be object"),
G;K(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{
if(Array.isArray(e.end)){
if(e.skip||e.excludeEnd||e.returnEnd)throw z("skip, excludeEnd, returnEnd not compatible with endScope: {}"),
G
;if("object"!=typeof e.endScope||null===e.endScope)throw z("endScope must be object"),
G;K(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function Z(e){
function t(t,n){
return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":""))
}class n{constructor(){
this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
addRule(e,t){
t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null)
;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|"
}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex
;const t=this.matcherRe.exec(e);if(!t)return null
;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n]
;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){
this.rules=[],this.multiRegexes=[],
this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
;let n=t.exec(e)
;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
return n&&(this.regexIndex+=n.position+1,
this.regexIndex===this.count&&this.considerAll()),n}}
if(e.compilerExtensions||(e.compilerExtensions=[]),
e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
;return e.classNameAliases=i(e.classNameAliases||{}),function n(r,o){const a=r
;if(r.isCompiled)return a
;[I,B,F,D].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))),
r.__beforeBegin=null,[T,L,P].forEach((e=>e(r,o))),r.isCompiled=!0;let c=null
;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords),
c=r.keywords.$pattern,
delete r.keywords.$pattern),c=c||/\w+/,r.keywords&&(r.keywords=C(r.keywords,e.case_insensitive)),
a.keywordPatternRe=t(c,!0),
o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(a.begin),r.end||r.endsWithParent||(r.end=/\B|\b/),
r.end&&(a.endRe=t(a.end)),
a.terminatorEnd=l(a.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)),
r.illegal&&(a.illegalRe=t(r.illegal)),
r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{
variants:null},t)))),e.cachedVariants?e.cachedVariants:V(e)?i(e,{
starts:e.starts?i(e.starts):null
}):Object.isFrozen(e)?i(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a)
})),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new s
;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function V(e){
return!!e&&(e.endsWithParent||V(e.starts))}class q extends Error{
constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}}
const J=n,Y=i,Q=Symbol("nomatch"),ee=n=>{
const i=Object.create(null),s=Object.create(null),r=[];let o=!0
;const a="Could not find the language '{}', did you forget to load/include a language module?",l={
disableAutodetect:!0,name:"Plain text",contains:[]};let p={
ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,
languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
cssSelector:"pre code",languages:null,__emitter:c};function b(e){
return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s=""
;"object"==typeof t?(i=e,
n=t.ignoreIllegals,s=t.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."),
X("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),
s=e,i=t),void 0===n&&(n=!0);const r={code:i,language:s};N("before:highlight",r)
;const o=r.result?r.result:E(r.language,r.code,n)
;return o.code=r.code,N("after:highlight",o),o}function E(e,n,s,r){
const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R)
;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n=""
;for(;t;){n+=R.substring(e,t.index)
;const s=w.case_insensitive?t[0].toLowerCase():t[0],r=(i=s,N.keywords[i]);if(r){
const[e,i]=r
;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(A+=i),e.startsWith("_"))n+=t[0];else{
const n=w.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0]
;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i
;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{
if(""===R)return;let e=null;if("string"==typeof N.subLanguage){
if(!i[N.subLanguage])return void M.addText(R)
;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top
}else e=x(R,N.subLanguage.length?N.subLanguage:null)
;N.relevance>0&&(A+=e.relevance),M.__addSublanguage(e._emitter,e.language)
})():l(),R=""}function u(e,t){
""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1
;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue}
const i=w.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}}
function h(e,t){
return e.scope&&"string"==typeof e.scope&&M.openNode(w.classNameAliases[e.scope]||e.scope),
e.beginScope&&(e.beginScope._wrap?(u(R,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),
R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{
value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t)
;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e)
;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){
for(;e.endsParent&&e.parent;)e=e.parent;return e}}
if(e.endsWithParent)return f(e.parent,n,i)}function b(e){
return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){
const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return Q;const r=N
;N.endScope&&N.endScope._wrap?(g(),
u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(),
d(N.endScope,e)):r.skip?R+=t:(r.returnEnd||r.excludeEnd||(R+=t),
g(),r.excludeEnd&&(R=t));do{
N.scope&&M.closeNode(),N.skip||N.subLanguage||(A+=N.relevance),N=N.parent
}while(N!==s.parent);return s.starts&&h(s.starts,e),r.returnEnd?0:t.length}
let _={};function y(i,r){const a=r&&r[0];if(R+=i,null==a)return g(),0
;if("begin"===_.type&&"end"===r.type&&_.index===r.index&&""===a){
if(R+=n.slice(r.index,r.index+1),!o){const t=Error(`0 width match regex (${e})`)
;throw t.languageName=e,t.badRule=_.rule,t}return 1}
if(_=r,"begin"===r.type)return(e=>{
const n=e[0],i=e.rule,s=new t(i),r=[i.__beforeBegin,i["on:begin"]]
;for(const t of r)if(t&&(t(e,s),s.isMatchIgnored))return b(n)
;return i.skip?R+=n:(i.excludeBegin&&(R+=n),
g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(r)
;if("illegal"===r.type&&!s){
const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"<unnamed>")+'"')
;throw e.mode=N,e}if("end"===r.type){const e=m(r);if(e!==Q)return e}
if("illegal"===r.type&&""===a)return R+="\n",1
;if(I>1e5&&I>3*r.index)throw Error("potential infinite loop, way more iterations than matches")
;return R+=a,a.length}const w=O(e)
;if(!w)throw z(a.replace("{}",e)),Error('Unknown language: "'+e+'"')
;const k=Z(w);let v="",N=r||k;const S={},M=new p.__emitter(p);(()=>{const e=[]
;for(let t=N;t!==w;t=t.parent)t.scope&&e.unshift(t.scope)
;e.forEach((e=>M.openNode(e)))})();let R="",A=0,j=0,I=0,T=!1;try{
if(w.__emitTokens)w.__emitTokens(n,M);else{for(N.matcher.considerAll();;){
I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=j
;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(j,e.index),e)
;j=e.index+t}y(n.substring(j))}return M.finalize(),v=M.toHTML(),{language:e,
value:v,relevance:A,illegal:!1,_emitter:M,_top:N}}catch(t){
if(t.message&&t.message.includes("Illegal"))return{language:e,value:J(n),
illegal:!0,relevance:0,_illegalBy:{message:t.message,index:j,
context:n.slice(j-100,j+100),mode:t.mode,resultSoFar:v},_emitter:M};if(o)return{
language:e,value:J(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N}
;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{
const t={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)}
;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(v).map((t=>E(t,e,!1)))
;s.unshift(n);const r=s.sort(((e,t)=>{
if(e.relevance!==t.relevance)return t.relevance-e.relevance
;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1
;if(O(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=r,c=o
;return c.secondBest=a,c}function _(e){let t=null;const n=(e=>{
let t=e.className+" ";t+=e.parentNode?e.parentNode.className:""
;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1])
;return t||(W(a.replace("{}",n[1])),
W("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}
return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return
;if(N("before:highlightElement",{el:e,language:n
}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e)
;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),
console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),
console.warn("The element with unescaped HTML:"),
console.warn(e)),p.throwUnescapedHTML))throw new q("One of your code blocks includes unescaped HTML.",e.innerHTML)
;t=e;const i=t.textContent,r=n?m(i,{language:n,ignoreIllegals:!0}):x(i)
;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n
;e.classList.add("hljs"),e.classList.add("language-"+i)
})(e,n,r.language),e.result={language:r.language,re:r.relevance,
relevance:r.relevance},r.secondBest&&(e.secondBest={
language:r.secondBest.language,relevance:r.secondBest.relevance
}),N("after:highlightElement",{el:e,result:r,text:i})}let y=!1;function w(){
if("loading"===document.readyState)return y||window.addEventListener("DOMContentLoaded",(()=>{
w()}),!1),void(y=!0);document.querySelectorAll(p.cssSelector).forEach(_)}
function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}
function k(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
s[e.toLowerCase()]=t}))}function v(e){const t=O(e)
;return t&&!t.disableAutodetect}function N(e,t){const n=e;r.forEach((e=>{
e[n]&&e[n](t)}))}Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:w,
highlightElement:_,
highlightBlock:e=>(X("10.7.0","highlightBlock will be removed entirely in v12.0"),
X("10.7.0","Please use highlightElement now."),_(e)),configure:e=>{p=Y(p,e)},
initHighlighting:()=>{
w(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},
initHighlightingOnLoad:()=>{
w(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")
},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){
if(z("Language definition for '{}' could not be registered.".replace("{}",e)),
!o)throw t;z(t),s=l}
s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&k(s.aliases,{
languageName:e})},unregisterLanguage:e=>{delete i[e]
;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:k,
autoDetection:v,inherit:Y,addPlugin:e=>{(e=>{
e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
e["before:highlightBlock"](Object.assign({block:t.el},t))
}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),r.push(e)},
removePlugin:e=>{const t=r.indexOf(e);-1!==t&&r.splice(t,1)}}),n.debugMode=()=>{
o=!1},n.safeMode=()=>{o=!0},n.versionString="11.11.1",n.regex={concat:h,
lookahead:g,either:f,optional:d,anyNumberOfTimes:u}
;for(const t in A)"object"==typeof A[t]&&e(A[t]);return Object.assign(n,A),n
},te=ee({});te.newInstance=()=>ee({});export{te as default};

View file

@ -0,0 +1,256 @@
/*! `markdown` grammar compiled for Highlight.js 11.11.1 */
var hljsGrammar = (function () {
'use strict';
/*
Language: Markdown
Requires: xml.js
Author: John Crepezzi <john.crepezzi@gmail.com>
Website: https://daringfireball.net/projects/markdown/
Category: common, markup
*/
function markdown(hljs) {
const regex = hljs.regex;
const INLINE_HTML = {
begin: /<\/?[A-Za-z_]/,
end: '>',
subLanguage: 'xml',
relevance: 0
};
const HORIZONTAL_RULE = {
begin: '^[-\\*]{3,}',
end: '$'
};
const CODE = {
className: 'code',
variants: [
// TODO: fix to allow these to work with sublanguage also
{ begin: '(`{3,})[^`](.|\\n)*?\\1`*[ ]*' },
{ begin: '(~{3,})[^~](.|\\n)*?\\1~*[ ]*' },
// needed to allow markdown as a sublanguage to work
{
begin: '```',
end: '```+[ ]*$'
},
{
begin: '~~~',
end: '~~~+[ ]*$'
},
{ begin: '`.+?`' },
{
begin: '(?=^( {4}|\\t))',
// use contains to gobble up multiple lines to allow the block to be whatever size
// but only have a single open/close tag vs one per line
contains: [
{
begin: '^( {4}|\\t)',
end: '(\\n)$'
}
],
relevance: 0
}
]
};
const LIST = {
className: 'bullet',
begin: '^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)',
end: '\\s+',
excludeEnd: true
};
const LINK_REFERENCE = {
begin: /^\[[^\n]+\]:/,
returnBegin: true,
contains: [
{
className: 'symbol',
begin: /\[/,
end: /\]/,
excludeBegin: true,
excludeEnd: true
},
{
className: 'link',
begin: /:\s*/,
end: /$/,
excludeBegin: true
}
]
};
const URL_SCHEME = /[A-Za-z][A-Za-z0-9+.-]*/;
const LINK = {
variants: [
// too much like nested array access in so many languages
// to have any real relevance
{
begin: /\[.+?\]\[.*?\]/,
relevance: 0
},
// popular internet URLs
{
begin: /\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
relevance: 2
},
{
begin: regex.concat(/\[.+?\]\(/, URL_SCHEME, /:\/\/.*?\)/),
relevance: 2
},
// relative urls
{
begin: /\[.+?\]\([./?&#].*?\)/,
relevance: 1
},
// whatever else, lower relevance (might not be a link at all)
{
begin: /\[.*?\]\(.*?\)/,
relevance: 0
}
],
returnBegin: true,
contains: [
{
// empty strings for alt or link text
match: /\[(?=\])/ },
{
className: 'string',
relevance: 0,
begin: '\\[',
end: '\\]',
excludeBegin: true,
returnEnd: true
},
{
className: 'link',
relevance: 0,
begin: '\\]\\(',
end: '\\)',
excludeBegin: true,
excludeEnd: true
},
{
className: 'symbol',
relevance: 0,
begin: '\\]\\[',
end: '\\]',
excludeBegin: true,
excludeEnd: true
}
]
};
const BOLD = {
className: 'strong',
contains: [], // defined later
variants: [
{
begin: /_{2}(?!\s)/,
end: /_{2}/
},
{
begin: /\*{2}(?!\s)/,
end: /\*{2}/
}
]
};
const ITALIC = {
className: 'emphasis',
contains: [], // defined later
variants: [
{
begin: /\*(?![*\s])/,
end: /\*/
},
{
begin: /_(?![_\s])/,
end: /_/,
relevance: 0
}
]
};
// 3 level deep nesting is not allowed because it would create confusion
// in cases like `***testing***` because where we don't know if the last
// `***` is starting a new bold/italic or finishing the last one
const BOLD_WITHOUT_ITALIC = hljs.inherit(BOLD, { contains: [] });
const ITALIC_WITHOUT_BOLD = hljs.inherit(ITALIC, { contains: [] });
BOLD.contains.push(ITALIC_WITHOUT_BOLD);
ITALIC.contains.push(BOLD_WITHOUT_ITALIC);
let CONTAINABLE = [
INLINE_HTML,
LINK
];
[
BOLD,
ITALIC,
BOLD_WITHOUT_ITALIC,
ITALIC_WITHOUT_BOLD
].forEach(m => {
m.contains = m.contains.concat(CONTAINABLE);
});
CONTAINABLE = CONTAINABLE.concat(BOLD, ITALIC);
const HEADER = {
className: 'section',
variants: [
{
begin: '^#{1,6}',
end: '$',
contains: CONTAINABLE
},
{
begin: '(?=^.+?\\n[=-]{2,}$)',
contains: [
{ begin: '^[=-]*$' },
{
begin: '^',
end: "\\n",
contains: CONTAINABLE
}
]
}
]
};
const BLOCKQUOTE = {
className: 'quote',
begin: '^>\\s+',
contains: CONTAINABLE,
end: '$'
};
const ENTITY = {
//https://spec.commonmark.org/0.31.2/#entity-references
scope: 'literal',
match: /&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/
};
return {
name: 'Markdown',
aliases: [
'md',
'mkdown',
'mkd'
],
contains: [
HEADER,
INLINE_HTML,
LIST,
BOLD,
ITALIC,
BLOCKQUOTE,
CODE,
HORIZONTAL_RULE,
LINK,
LINK_REFERENCE,
ENTITY
]
};
}
return markdown;
})();
;
export default hljsGrammar;

View file

@ -0,0 +1,32 @@
/*! `markdown` grammar compiled for Highlight.js 11.11.1 */
var hljsGrammar=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/,
end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/,
relevance:0},{
begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
relevance:2},{
begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/),
relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{
begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/
},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,
returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",
excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",
end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[],
variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}]
},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{
begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[]
}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c)
;let l=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(l)
})),l=l.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{
className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:l},{
begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",
contains:l}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",
end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:l,
end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{
begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{
begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",
contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{
begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{
className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{
className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},{scope:"literal",
match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}})()
;export default hljsGrammar;

View file

@ -0,0 +1,249 @@
/*! `xml` grammar compiled for Highlight.js 11.11.1 */
var hljsGrammar = (function () {
'use strict';
/*
Language: HTML, XML
Website: https://www.w3.org/XML/
Category: common, web
Audit: 2020
*/
/** @type LanguageFn */
function xml(hljs) {
const regex = hljs.regex;
// XML names can have the following additional letters: https://www.w3.org/TR/xml/#NT-NameChar
// OTHER_NAME_CHARS = /[:\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]/;
// Element names start with NAME_START_CHAR followed by optional other Unicode letters, ASCII digits, hyphens, underscores, and periods
// const TAG_NAME_RE = regex.concat(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/, regex.optional(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*:/), /[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*/);;
// const XML_IDENT_RE = /[A-Z_a-z:\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]+/;
// const TAG_NAME_RE = regex.concat(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/, regex.optional(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*:/), /[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*/);
// however, to cater for performance and more Unicode support rely simply on the Unicode letter class
const TAG_NAME_RE = regex.concat(/[\p{L}_]/u, regex.optional(/[\p{L}0-9_.-]*:/u), /[\p{L}0-9_.-]*/u);
const XML_IDENT_RE = /[\p{L}0-9._:-]+/u;
const XML_ENTITIES = {
className: 'symbol',
begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/
};
const XML_META_KEYWORDS = {
begin: /\s/,
contains: [
{
className: 'keyword',
begin: /#?[a-z_][a-z1-9_-]+/,
illegal: /\n/
}
]
};
const XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, {
begin: /\(/,
end: /\)/
});
const APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, { className: 'string' });
const QUOTE_META_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, { className: 'string' });
const TAG_INTERNALS = {
endsWithParent: true,
illegal: /</,
relevance: 0,
contains: [
{
className: 'attr',
begin: XML_IDENT_RE,
relevance: 0
},
{
begin: /=\s*/,
relevance: 0,
contains: [
{
className: 'string',
endsParent: true,
variants: [
{
begin: /"/,
end: /"/,
contains: [ XML_ENTITIES ]
},
{
begin: /'/,
end: /'/,
contains: [ XML_ENTITIES ]
},
{ begin: /[^\s"'=<>`]+/ }
]
}
]
}
]
};
return {
name: 'HTML, XML',
aliases: [
'html',
'xhtml',
'rss',
'atom',
'xjb',
'xsd',
'xsl',
'plist',
'wsf',
'svg'
],
case_insensitive: true,
unicodeRegex: true,
contains: [
{
className: 'meta',
begin: /<![a-z]/,
end: />/,
relevance: 10,
contains: [
XML_META_KEYWORDS,
QUOTE_META_STRING_MODE,
APOS_META_STRING_MODE,
XML_META_PAR_KEYWORDS,
{
begin: /\[/,
end: /\]/,
contains: [
{
className: 'meta',
begin: /<![a-z]/,
end: />/,
contains: [
XML_META_KEYWORDS,
XML_META_PAR_KEYWORDS,
QUOTE_META_STRING_MODE,
APOS_META_STRING_MODE
]
}
]
}
]
},
hljs.COMMENT(
/<!--/,
/-->/,
{ relevance: 10 }
),
{
begin: /<!\[CDATA\[/,
end: /\]\]>/,
relevance: 10
},
XML_ENTITIES,
// xml processing instructions
{
className: 'meta',
end: /\?>/,
variants: [
{
begin: /<\?xml/,
relevance: 10,
contains: [
QUOTE_META_STRING_MODE
]
},
{
begin: /<\?[a-z][a-z0-9]+/,
}
]
},
{
className: 'tag',
/*
The lookahead pattern (?=...) ensures that 'begin' only matches
'<style' as a single word, followed by a whitespace or an
ending bracket.
*/
begin: /<style(?=\s|>)/,
end: />/,
keywords: { name: 'style' },
contains: [ TAG_INTERNALS ],
starts: {
end: /<\/style>/,
returnEnd: true,
subLanguage: [
'css',
'xml'
]
}
},
{
className: 'tag',
// See the comment in the <style tag about the lookahead pattern
begin: /<script(?=\s|>)/,
end: />/,
keywords: { name: 'script' },
contains: [ TAG_INTERNALS ],
starts: {
end: /<\/script>/,
returnEnd: true,
subLanguage: [
'javascript',
'handlebars',
'xml'
]
}
},
// we need this for now for jSX
{
className: 'tag',
begin: /<>|<\/>/
},
// open tag
{
className: 'tag',
begin: regex.concat(
/</,
regex.lookahead(regex.concat(
TAG_NAME_RE,
// <tag/>
// <tag>
// <tag ...
regex.either(/\/>/, />/, /\s/)
))
),
end: /\/?>/,
contains: [
{
className: 'name',
begin: TAG_NAME_RE,
relevance: 0,
starts: TAG_INTERNALS
}
]
},
// close tag
{
className: 'tag',
begin: regex.concat(
/<\//,
regex.lookahead(regex.concat(
TAG_NAME_RE, />/
))
),
contains: [
{
className: 'name',
begin: TAG_NAME_RE,
relevance: 0
},
{
begin: />/,
relevance: 0,
endsParent: true
}
]
}
]
};
}
return xml;
})();
;
export default hljsGrammar;

View file

@ -0,0 +1,29 @@
/*! `xml` grammar compiled for Highlight.js 11.11.1 */
var hljsGrammar=(()=>{"use strict";return e=>{
const a=e.regex,n=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),s={
className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},t={begin:/\s/,
contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]
},i=e.inherit(t,{begin:/\(/,end:/\)/}),c=e.inherit(e.APOS_STRING_MODE,{
className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),r={
endsWithParent:!0,illegal:/</,relevance:0,contains:[{className:"attr",
begin:/[\p{L}0-9._:-]+/u,relevance:0},{begin:/=\s*/,relevance:0,contains:[{
className:"string",endsParent:!0,variants:[{begin:/"/,end:/"/,contains:[s]},{
begin:/'/,end:/'/,contains:[s]},{begin:/[^\s"'=<>`]+/}]}]}]};return{
name:"HTML, XML",
aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],
case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin:/<![a-z]/,
end:/>/,relevance:10,contains:[t,l,c,i,{begin:/\[/,end:/\]/,contains:[{
className:"meta",begin:/<![a-z]/,end:/>/,contains:[t,i,l,c]}]}]
},e.COMMENT(/<!--/,/-->/,{relevance:10}),{begin:/<!\[CDATA\[/,end:/\]\]>/,
relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,
relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",
begin:/<style(?=\s|>)/,end:/>/,keywords:{name:"style"},contains:[r],starts:{
end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",
begin:/<script(?=\s|>)/,end:/>/,keywords:{name:"script"},contains:[r],starts:{
end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{
className:"tag",begin:/<>|<\/>/},{className:"tag",
begin:a.concat(/</,a.lookahead(a.concat(n,a.either(/\/>/,/>/,/\s/)))),
end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:r}]},{
className:"tag",begin:a.concat(/<\//,a.lookahead(a.concat(n,/>/))),contains:[{
className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}
})();export default hljsGrammar;

View file

@ -0,0 +1 @@
{ "type": "module" }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,367 @@
/*!
Highlight.js v11.11.1 (git: 08cb242e7d)
(c) 2006-2025 Josh Goebel <hello@joshgoebel.com> and other contributors
License: BSD-3-Clause
*/
var hljs=function(){"use strict";function e(t){
return t instanceof Map?t.clear=t.delete=t.set=()=>{
throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
throw Error("set is read-only")
}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{
const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i)
})),t}class t{constructor(e){
void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}
ignoreMatch(){this.isMatchIgnored=!0}}function n(e){
return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;")
}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope
;class r{constructor(e,t){
this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{
if(e.startsWith("language:"))return e.replace("language:","language-")
;if(e.includes(".")){const n=e.split(".")
;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")
}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}
closeNode(e){s(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
this.buffer+=`<span class="${e}">`}}const o=(e={})=>{const t={children:[]}
;return Object.assign(t,e),t};class a{constructor(){
this.rootNode=o(),this.stack=[this.rootNode]}get top(){
return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
this.top.children.push(e)}openNode(e){const t=o({scope:e})
;this.add(t),this.stack.push(t)}closeNode(){
if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e}
addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){
this.closeNode()}__addSublanguage(e,t){const n=e.root
;t&&(n.scope="language:"+t),this.add(n)}toHTML(){
return new r(this,this.options).value()}finalize(){
return this.closeAllNodes(),!0}}function l(e){
return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")}
function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")}
function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{
const t=e[e.length-1]
;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}
})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"}
function p(e){return RegExp(e.toString()+"|").exec("").length-1}
const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./
;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n
;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break}
s+=i.substring(0,e.index),
i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],
"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)}
const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",_="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",O={
begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'",
illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n",
contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t,
contains:[]},n);s.contains.push({scope:"doctag",
begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",
end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0})
;const r=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/)
;return s.contains.push({begin:h(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s
},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({
__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{
scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:N,
C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number",
begin:_,relevance:0},C_NUMBER_RE:_,END_SAME_AS_BEGIN:e=>Object.assign(e,{
"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E,
MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0},
NUMBER_MODE:{scope:"number",begin:y,relevance:0},NUMBER_RE:y,
PHRASAL_WORDS_MODE:{
begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/,
end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]},
RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
SHEBANG:(e={})=>{const t=/^#![ ]*\//
;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t,
end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},
TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x,
UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){
"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){
void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){
t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
void 0===e.relevance&&(e.relevance=0))}function L(e,t){
Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){
if(e.match){
if(e.begin||e.end)throw Error("begin & end are not supported with match")
;e.begin=e.match,delete e.match}}function P(e,t){
void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return
;if(e.starts)throw Error("beforeMatch cannot be used with starts")
;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t]
})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={
relevance:0,contains:[Object.assign(n,{endsParent:!0})]
},e.relevance=0,delete n.beforeMatch
},H=["of","and","for","in","not","or","if","then","parent","list","value"]
;function C(e,t,n="keyword"){const i=Object.create(null)
;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{
Object.assign(i,C(e[n],t,n))})),i;function s(e,n){
t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
;i[n[0]]=[e,$(n[0],n[1])]}))}}function $(e,t){
return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const U={},z=e=>{
console.error(e)},W=(e,...t)=>{console.log("WARN: "+e,...t)},X=(e,t)=>{
U[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),U[`${e}/${t}`]=!0)
},G=Error();function K(e,t,{key:n}){let i=0;const s=e[n],r={},o={}
;for(let e=1;e<=t.length;e++)o[e+i]=s[e],r[e+i]=!0,i+=p(t[e-1])
;e[n]=o,e[n]._emit=r,e[n]._multi=!0}function F(e){(e=>{
e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,
delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={
_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope
}),(e=>{if(Array.isArray(e.begin)){
if(e.skip||e.excludeBegin||e.returnBegin)throw z("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),
G
;if("object"!=typeof e.beginScope||null===e.beginScope)throw z("beginScope must be object"),
G;K(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{
if(Array.isArray(e.end)){
if(e.skip||e.excludeEnd||e.returnEnd)throw z("skip, excludeEnd, returnEnd not compatible with endScope: {}"),
G
;if("object"!=typeof e.endScope||null===e.endScope)throw z("endScope must be object"),
G;K(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function Z(e){
function t(t,n){
return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":""))
}class n{constructor(){
this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
addRule(e,t){
t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null)
;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|"
}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex
;const t=this.matcherRe.exec(e);if(!t)return null
;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n]
;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){
this.rules=[],this.multiRegexes=[],
this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
;let n=t.exec(e)
;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
return n&&(this.regexIndex+=n.position+1,
this.regexIndex===this.count&&this.considerAll()),n}}
if(e.compilerExtensions||(e.compilerExtensions=[]),
e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
;return e.classNameAliases=i(e.classNameAliases||{}),function n(r,o){const a=r
;if(r.isCompiled)return a
;[I,B,F,D].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))),
r.__beforeBegin=null,[T,L,P].forEach((e=>e(r,o))),r.isCompiled=!0;let c=null
;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords),
c=r.keywords.$pattern,
delete r.keywords.$pattern),c=c||/\w+/,r.keywords&&(r.keywords=C(r.keywords,e.case_insensitive)),
a.keywordPatternRe=t(c,!0),
o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(a.begin),r.end||r.endsWithParent||(r.end=/\B|\b/),
r.end&&(a.endRe=t(a.end)),
a.terminatorEnd=l(a.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)),
r.illegal&&(a.illegalRe=t(r.illegal)),
r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{
variants:null},t)))),e.cachedVariants?e.cachedVariants:V(e)?i(e,{
starts:e.starts?i(e.starts):null
}):Object.isFrozen(e)?i(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a)
})),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new s
;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function V(e){
return!!e&&(e.endsWithParent||V(e.starts))}class q extends Error{
constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}}
const J=n,Y=i,Q=Symbol("nomatch"),ee=n=>{
const i=Object.create(null),s=Object.create(null),r=[];let o=!0
;const a="Could not find the language '{}', did you forget to load/include a language module?",l={
disableAutodetect:!0,name:"Plain text",contains:[]};let p={
ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,
languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
cssSelector:"pre code",languages:null,__emitter:c};function b(e){
return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s=""
;"object"==typeof t?(i=e,
n=t.ignoreIllegals,s=t.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."),
X("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),
s=e,i=t),void 0===n&&(n=!0);const r={code:i,language:s};N("before:highlight",r)
;const o=r.result?r.result:E(r.language,r.code,n)
;return o.code=r.code,N("after:highlight",o),o}function E(e,n,s,r){
const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R)
;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n=""
;for(;t;){n+=R.substring(e,t.index)
;const s=w.case_insensitive?t[0].toLowerCase():t[0],r=(i=s,N.keywords[i]);if(r){
const[e,i]=r
;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{
const n=w.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0]
;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i
;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{
if(""===R)return;let e=null;if("string"==typeof N.subLanguage){
if(!i[N.subLanguage])return void M.addText(R)
;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top
}else e=x(R,N.subLanguage.length?N.subLanguage:null)
;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language)
})():l(),R=""}function u(e,t){
""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1
;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue}
const i=w.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}}
function h(e,t){
return e.scope&&"string"==typeof e.scope&&M.openNode(w.classNameAliases[e.scope]||e.scope),
e.beginScope&&(e.beginScope._wrap?(u(R,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),
R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{
value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t)
;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e)
;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){
for(;e.endsParent&&e.parent;)e=e.parent;return e}}
if(e.endsWithParent)return f(e.parent,n,i)}function b(e){
return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){
const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return Q;const r=N
;N.endScope&&N.endScope._wrap?(g(),
u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(),
d(N.endScope,e)):r.skip?R+=t:(r.returnEnd||r.excludeEnd||(R+=t),
g(),r.excludeEnd&&(R=t));do{
N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent
}while(N!==s.parent);return s.starts&&h(s.starts,e),r.returnEnd?0:t.length}
let y={};function _(i,r){const a=r&&r[0];if(R+=i,null==a)return g(),0
;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===a){
if(R+=n.slice(r.index,r.index+1),!o){const t=Error(`0 width match regex (${e})`)
;throw t.languageName=e,t.badRule=y.rule,t}return 1}
if(y=r,"begin"===r.type)return(e=>{
const n=e[0],i=e.rule,s=new t(i),r=[i.__beforeBegin,i["on:begin"]]
;for(const t of r)if(t&&(t(e,s),s.isMatchIgnored))return b(n)
;return i.skip?R+=n:(i.excludeBegin&&(R+=n),
g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(r)
;if("illegal"===r.type&&!s){
const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"<unnamed>")+'"')
;throw e.mode=N,e}if("end"===r.type){const e=m(r);if(e!==Q)return e}
if("illegal"===r.type&&""===a)return R+="\n",1
;if(I>1e5&&I>3*r.index)throw Error("potential infinite loop, way more iterations than matches")
;return R+=a,a.length}const w=O(e)
;if(!w)throw z(a.replace("{}",e)),Error('Unknown language: "'+e+'"')
;const v=Z(w);let k="",N=r||v;const S={},M=new p.__emitter(p);(()=>{const e=[]
;for(let t=N;t!==w;t=t.parent)t.scope&&e.unshift(t.scope)
;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{
if(w.__emitTokens)w.__emitTokens(n,M);else{for(N.matcher.considerAll();;){
I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A
;const e=N.matcher.exec(n);if(!e)break;const t=_(n.substring(A,e.index),e)
;A=e.index+t}_(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e,
value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){
if(t.message&&t.message.includes("Illegal"))return{language:e,value:J(n),
illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A,
context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(o)return{
language:e,value:J(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N}
;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{
const t={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)}
;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1)))
;s.unshift(n);const r=s.sort(((e,t)=>{
if(e.relevance!==t.relevance)return t.relevance-e.relevance
;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1
;if(O(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=r,c=o
;return c.secondBest=a,c}function y(e){let t=null;const n=(e=>{
let t=e.className+" ";t+=e.parentNode?e.parentNode.className:""
;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1])
;return t||(W(a.replace("{}",n[1])),
W("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}
return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return
;if(N("before:highlightElement",{el:e,language:n
}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e)
;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),
console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),
console.warn("The element with unescaped HTML:"),
console.warn(e)),p.throwUnescapedHTML))throw new q("One of your code blocks includes unescaped HTML.",e.innerHTML)
;t=e;const i=t.textContent,r=n?m(i,{language:n,ignoreIllegals:!0}):x(i)
;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n
;e.classList.add("hljs"),e.classList.add("language-"+i)
})(e,n,r.language),e.result={language:r.language,re:r.relevance,
relevance:r.relevance},r.secondBest&&(e.secondBest={
language:r.secondBest.language,relevance:r.secondBest.relevance
}),N("after:highlightElement",{el:e,result:r,text:i})}let _=!1;function w(){
if("loading"===document.readyState)return _||window.addEventListener("DOMContentLoaded",(()=>{
w()}),!1),void(_=!0);document.querySelectorAll(p.cssSelector).forEach(y)}
function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}
function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
s[e.toLowerCase()]=t}))}function k(e){const t=O(e)
;return t&&!t.disableAutodetect}function N(e,t){const n=e;r.forEach((e=>{
e[n]&&e[n](t)}))}Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:w,
highlightElement:y,
highlightBlock:e=>(X("10.7.0","highlightBlock will be removed entirely in v12.0"),
X("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{p=Y(p,e)},
initHighlighting:()=>{
w(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},
initHighlightingOnLoad:()=>{
w(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")
},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){
if(z("Language definition for '{}' could not be registered.".replace("{}",e)),
!o)throw t;z(t),s=l}
s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{
languageName:e})},unregisterLanguage:e=>{delete i[e]
;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v,
autoDetection:k,inherit:Y,addPlugin:e=>{(e=>{
e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
e["before:highlightBlock"](Object.assign({block:t.el},t))
}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),r.push(e)},
removePlugin:e=>{const t=r.indexOf(e);-1!==t&&r.splice(t,1)}}),n.debugMode=()=>{
o=!1},n.safeMode=()=>{o=!0},n.versionString="11.11.1",n.regex={concat:h,
lookahead:g,either:f,optional:d,anyNumberOfTimes:u}
;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n
},te=ee({});return te.newInstance=()=>ee({}),te}()
;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `markdown` grammar compiled for Highlight.js 11.11.1 */
(()=>{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/,
end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/,
relevance:0},{
begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
relevance:2},{
begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/),
relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{
begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/
},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,
returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",
excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",
end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[],
variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}]
},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{
begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[]
}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c)
;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g)
})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{
className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{
begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",
contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",
end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g,
end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{
begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{
begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",
contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{
begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{
className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{
className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},{scope:"literal",
match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}})()
;hljs.registerLanguage("markdown",e)})();/*! `xml` grammar compiled for Highlight.js 11.11.1 */
(()=>{var e=(()=>{"use strict";return e=>{
const a=e.regex,n=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),s={
className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},t={begin:/\s/,
contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]
},i=e.inherit(t,{begin:/\(/,end:/\)/}),c=e.inherit(e.APOS_STRING_MODE,{
className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),r={
endsWithParent:!0,illegal:/</,relevance:0,contains:[{className:"attr",
begin:/[\p{L}0-9._:-]+/u,relevance:0},{begin:/=\s*/,relevance:0,contains:[{
className:"string",endsParent:!0,variants:[{begin:/"/,end:/"/,contains:[s]},{
begin:/'/,end:/'/,contains:[s]},{begin:/[^\s"'=<>`]+/}]}]}]};return{
name:"HTML, XML",
aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],
case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin:/<![a-z]/,
end:/>/,relevance:10,contains:[t,l,c,i,{begin:/\[/,end:/\]/,contains:[{
className:"meta",begin:/<![a-z]/,end:/>/,contains:[t,i,l,c]}]}]
},e.COMMENT(/<!--/,/-->/,{relevance:10}),{begin:/<!\[CDATA\[/,end:/\]\]>/,
relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,
relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",
begin:/<style(?=\s|>)/,end:/>/,keywords:{name:"style"},contains:[r],starts:{
end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",
begin:/<script(?=\s|>)/,end:/>/,keywords:{name:"script"},contains:[r],starts:{
end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{
className:"tag",begin:/<>|<\/>/},{className:"tag",
begin:a.concat(/</,a.lookahead(a.concat(n,a.either(/\/>/,/>/,/\s/)))),
end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:r}]},{
className:"tag",begin:a.concat(/<\//,a.lookahead(a.concat(n,/>/))),contains:[{
className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}
})();hljs.registerLanguage("xml",e)})();

View file

@ -0,0 +1,258 @@
/*! `markdown` grammar compiled for Highlight.js 11.11.1 */
(function(){
var hljsGrammar = (function () {
'use strict';
/*
Language: Markdown
Requires: xml.js
Author: John Crepezzi <john.crepezzi@gmail.com>
Website: https://daringfireball.net/projects/markdown/
Category: common, markup
*/
function markdown(hljs) {
const regex = hljs.regex;
const INLINE_HTML = {
begin: /<\/?[A-Za-z_]/,
end: '>',
subLanguage: 'xml',
relevance: 0
};
const HORIZONTAL_RULE = {
begin: '^[-\\*]{3,}',
end: '$'
};
const CODE = {
className: 'code',
variants: [
// TODO: fix to allow these to work with sublanguage also
{ begin: '(`{3,})[^`](.|\\n)*?\\1`*[ ]*' },
{ begin: '(~{3,})[^~](.|\\n)*?\\1~*[ ]*' },
// needed to allow markdown as a sublanguage to work
{
begin: '```',
end: '```+[ ]*$'
},
{
begin: '~~~',
end: '~~~+[ ]*$'
},
{ begin: '`.+?`' },
{
begin: '(?=^( {4}|\\t))',
// use contains to gobble up multiple lines to allow the block to be whatever size
// but only have a single open/close tag vs one per line
contains: [
{
begin: '^( {4}|\\t)',
end: '(\\n)$'
}
],
relevance: 0
}
]
};
const LIST = {
className: 'bullet',
begin: '^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)',
end: '\\s+',
excludeEnd: true
};
const LINK_REFERENCE = {
begin: /^\[[^\n]+\]:/,
returnBegin: true,
contains: [
{
className: 'symbol',
begin: /\[/,
end: /\]/,
excludeBegin: true,
excludeEnd: true
},
{
className: 'link',
begin: /:\s*/,
end: /$/,
excludeBegin: true
}
]
};
const URL_SCHEME = /[A-Za-z][A-Za-z0-9+.-]*/;
const LINK = {
variants: [
// too much like nested array access in so many languages
// to have any real relevance
{
begin: /\[.+?\]\[.*?\]/,
relevance: 0
},
// popular internet URLs
{
begin: /\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
relevance: 2
},
{
begin: regex.concat(/\[.+?\]\(/, URL_SCHEME, /:\/\/.*?\)/),
relevance: 2
},
// relative urls
{
begin: /\[.+?\]\([./?&#].*?\)/,
relevance: 1
},
// whatever else, lower relevance (might not be a link at all)
{
begin: /\[.*?\]\(.*?\)/,
relevance: 0
}
],
returnBegin: true,
contains: [
{
// empty strings for alt or link text
match: /\[(?=\])/ },
{
className: 'string',
relevance: 0,
begin: '\\[',
end: '\\]',
excludeBegin: true,
returnEnd: true
},
{
className: 'link',
relevance: 0,
begin: '\\]\\(',
end: '\\)',
excludeBegin: true,
excludeEnd: true
},
{
className: 'symbol',
relevance: 0,
begin: '\\]\\[',
end: '\\]',
excludeBegin: true,
excludeEnd: true
}
]
};
const BOLD = {
className: 'strong',
contains: [], // defined later
variants: [
{
begin: /_{2}(?!\s)/,
end: /_{2}/
},
{
begin: /\*{2}(?!\s)/,
end: /\*{2}/
}
]
};
const ITALIC = {
className: 'emphasis',
contains: [], // defined later
variants: [
{
begin: /\*(?![*\s])/,
end: /\*/
},
{
begin: /_(?![_\s])/,
end: /_/,
relevance: 0
}
]
};
// 3 level deep nesting is not allowed because it would create confusion
// in cases like `***testing***` because where we don't know if the last
// `***` is starting a new bold/italic or finishing the last one
const BOLD_WITHOUT_ITALIC = hljs.inherit(BOLD, { contains: [] });
const ITALIC_WITHOUT_BOLD = hljs.inherit(ITALIC, { contains: [] });
BOLD.contains.push(ITALIC_WITHOUT_BOLD);
ITALIC.contains.push(BOLD_WITHOUT_ITALIC);
let CONTAINABLE = [
INLINE_HTML,
LINK
];
[
BOLD,
ITALIC,
BOLD_WITHOUT_ITALIC,
ITALIC_WITHOUT_BOLD
].forEach(m => {
m.contains = m.contains.concat(CONTAINABLE);
});
CONTAINABLE = CONTAINABLE.concat(BOLD, ITALIC);
const HEADER = {
className: 'section',
variants: [
{
begin: '^#{1,6}',
end: '$',
contains: CONTAINABLE
},
{
begin: '(?=^.+?\\n[=-]{2,}$)',
contains: [
{ begin: '^[=-]*$' },
{
begin: '^',
end: "\\n",
contains: CONTAINABLE
}
]
}
]
};
const BLOCKQUOTE = {
className: 'quote',
begin: '^>\\s+',
contains: CONTAINABLE,
end: '$'
};
const ENTITY = {
//https://spec.commonmark.org/0.31.2/#entity-references
scope: 'literal',
match: /&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/
};
return {
name: 'Markdown',
aliases: [
'md',
'mkdown',
'mkd'
],
contains: [
HEADER,
INLINE_HTML,
LIST,
BOLD,
ITALIC,
BLOCKQUOTE,
CODE,
HORIZONTAL_RULE,
LINK,
LINK_REFERENCE,
ENTITY
]
};
}
return markdown;
})();
hljs.registerLanguage('markdown', hljsGrammar);
})();

View file

@ -0,0 +1,32 @@
/*! `markdown` grammar compiled for Highlight.js 11.11.1 */
(()=>{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/,
end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/,
relevance:0},{
begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
relevance:2},{
begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/),
relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{
begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/
},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,
returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",
excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",
end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[],
variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}]
},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{
begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[]
}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c)
;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g)
})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{
className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{
begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",
contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",
end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g,
end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{
begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{
begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",
contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{
begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{
className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{
className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},{scope:"literal",
match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}})()
;hljs.registerLanguage("markdown",e)})();

View file

@ -0,0 +1,251 @@
/*! `xml` grammar compiled for Highlight.js 11.11.1 */
(function(){
var hljsGrammar = (function () {
'use strict';
/*
Language: HTML, XML
Website: https://www.w3.org/XML/
Category: common, web
Audit: 2020
*/
/** @type LanguageFn */
function xml(hljs) {
const regex = hljs.regex;
// XML names can have the following additional letters: https://www.w3.org/TR/xml/#NT-NameChar
// OTHER_NAME_CHARS = /[:\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]/;
// Element names start with NAME_START_CHAR followed by optional other Unicode letters, ASCII digits, hyphens, underscores, and periods
// const TAG_NAME_RE = regex.concat(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/, regex.optional(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*:/), /[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*/);;
// const XML_IDENT_RE = /[A-Z_a-z:\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]+/;
// const TAG_NAME_RE = regex.concat(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/, regex.optional(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*:/), /[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*/);
// however, to cater for performance and more Unicode support rely simply on the Unicode letter class
const TAG_NAME_RE = regex.concat(/[\p{L}_]/u, regex.optional(/[\p{L}0-9_.-]*:/u), /[\p{L}0-9_.-]*/u);
const XML_IDENT_RE = /[\p{L}0-9._:-]+/u;
const XML_ENTITIES = {
className: 'symbol',
begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/
};
const XML_META_KEYWORDS = {
begin: /\s/,
contains: [
{
className: 'keyword',
begin: /#?[a-z_][a-z1-9_-]+/,
illegal: /\n/
}
]
};
const XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, {
begin: /\(/,
end: /\)/
});
const APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, { className: 'string' });
const QUOTE_META_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, { className: 'string' });
const TAG_INTERNALS = {
endsWithParent: true,
illegal: /</,
relevance: 0,
contains: [
{
className: 'attr',
begin: XML_IDENT_RE,
relevance: 0
},
{
begin: /=\s*/,
relevance: 0,
contains: [
{
className: 'string',
endsParent: true,
variants: [
{
begin: /"/,
end: /"/,
contains: [ XML_ENTITIES ]
},
{
begin: /'/,
end: /'/,
contains: [ XML_ENTITIES ]
},
{ begin: /[^\s"'=<>`]+/ }
]
}
]
}
]
};
return {
name: 'HTML, XML',
aliases: [
'html',
'xhtml',
'rss',
'atom',
'xjb',
'xsd',
'xsl',
'plist',
'wsf',
'svg'
],
case_insensitive: true,
unicodeRegex: true,
contains: [
{
className: 'meta',
begin: /<![a-z]/,
end: />/,
relevance: 10,
contains: [
XML_META_KEYWORDS,
QUOTE_META_STRING_MODE,
APOS_META_STRING_MODE,
XML_META_PAR_KEYWORDS,
{
begin: /\[/,
end: /\]/,
contains: [
{
className: 'meta',
begin: /<![a-z]/,
end: />/,
contains: [
XML_META_KEYWORDS,
XML_META_PAR_KEYWORDS,
QUOTE_META_STRING_MODE,
APOS_META_STRING_MODE
]
}
]
}
]
},
hljs.COMMENT(
/<!--/,
/-->/,
{ relevance: 10 }
),
{
begin: /<!\[CDATA\[/,
end: /\]\]>/,
relevance: 10
},
XML_ENTITIES,
// xml processing instructions
{
className: 'meta',
end: /\?>/,
variants: [
{
begin: /<\?xml/,
relevance: 10,
contains: [
QUOTE_META_STRING_MODE
]
},
{
begin: /<\?[a-z][a-z0-9]+/,
}
]
},
{
className: 'tag',
/*
The lookahead pattern (?=...) ensures that 'begin' only matches
'<style' as a single word, followed by a whitespace or an
ending bracket.
*/
begin: /<style(?=\s|>)/,
end: />/,
keywords: { name: 'style' },
contains: [ TAG_INTERNALS ],
starts: {
end: /<\/style>/,
returnEnd: true,
subLanguage: [
'css',
'xml'
]
}
},
{
className: 'tag',
// See the comment in the <style tag about the lookahead pattern
begin: /<script(?=\s|>)/,
end: />/,
keywords: { name: 'script' },
contains: [ TAG_INTERNALS ],
starts: {
end: /<\/script>/,
returnEnd: true,
subLanguage: [
'javascript',
'handlebars',
'xml'
]
}
},
// we need this for now for jSX
{
className: 'tag',
begin: /<>|<\/>/
},
// open tag
{
className: 'tag',
begin: regex.concat(
/</,
regex.lookahead(regex.concat(
TAG_NAME_RE,
// <tag/>
// <tag>
// <tag ...
regex.either(/\/>/, />/, /\s/)
))
),
end: /\/?>/,
contains: [
{
className: 'name',
begin: TAG_NAME_RE,
relevance: 0,
starts: TAG_INTERNALS
}
]
},
// close tag
{
className: 'tag',
begin: regex.concat(
/<\//,
regex.lookahead(regex.concat(
TAG_NAME_RE, />/
))
),
contains: [
{
className: 'name',
begin: TAG_NAME_RE,
relevance: 0
},
{
begin: />/,
relevance: 0,
endsParent: true
}
]
}
]
};
}
return xml;
})();
hljs.registerLanguage('xml', hljsGrammar);
})();

View file

@ -0,0 +1,29 @@
/*! `xml` grammar compiled for Highlight.js 11.11.1 */
(()=>{var e=(()=>{"use strict";return e=>{
const a=e.regex,n=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),s={
className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},t={begin:/\s/,
contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]
},i=e.inherit(t,{begin:/\(/,end:/\)/}),c=e.inherit(e.APOS_STRING_MODE,{
className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),r={
endsWithParent:!0,illegal:/</,relevance:0,contains:[{className:"attr",
begin:/[\p{L}0-9._:-]+/u,relevance:0},{begin:/=\s*/,relevance:0,contains:[{
className:"string",endsParent:!0,variants:[{begin:/"/,end:/"/,contains:[s]},{
begin:/'/,end:/'/,contains:[s]},{begin:/[^\s"'=<>`]+/}]}]}]};return{
name:"HTML, XML",
aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],
case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin:/<![a-z]/,
end:/>/,relevance:10,contains:[t,l,c,i,{begin:/\[/,end:/\]/,contains:[{
className:"meta",begin:/<![a-z]/,end:/>/,contains:[t,i,l,c]}]}]
},e.COMMENT(/<!--/,/-->/,{relevance:10}),{begin:/<!\[CDATA\[/,end:/\]\]>/,
relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,
relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",
begin:/<style(?=\s|>)/,end:/>/,keywords:{name:"style"},contains:[r],starts:{
end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",
begin:/<script(?=\s|>)/,end:/>/,keywords:{name:"script"},contains:[r],starts:{
end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{
className:"tag",begin:/<>|<\/>/},{className:"tag",
begin:a.concat(/</,a.lookahead(a.concat(n,a.either(/\/>/,/>/,/\s/)))),
end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:r}]},{
className:"tag",begin:a.concat(/<\//,a.lookahead(a.concat(n,/>/))),contains:[{
className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}
})();hljs.registerLanguage("xml",e)})();

View file

@ -0,0 +1,93 @@
{
"name": "@highlightjs/cdn-assets",
"description": "Syntax highlighting with language autodetection. (pre-compiled CDN assets)",
"keywords": [
"highlight",
"syntax"
],
"homepage": "https://highlightjs.org/",
"version": "11.11.1",
"author": "Josh Goebel <hello@joshgoebel.com>",
"contributors": [
"Josh Goebel <hello@joshgoebel.com>",
"Egor Rogov <e.rogov@postgrespro.ru>",
"Vladimir Jimenez <me@allejo.io>",
"Ivan Sagalaev <maniac@softwaremaniacs.org>",
"Jeremy Hull <sourdrums@gmail.com>",
"Oleg Efimov <efimovov@gmail.com>",
"Gidi Meir Morris <gidi@gidi.io>",
"Jan T. Sott <git@idleberg.com>",
"Li Xuanji <xuanji@gmail.com>",
"Marcos Cáceres <marcos@marcosc.com>",
"Sang Dang <sang.dang@polku.io>"
],
"bugs": {
"url": "https://github.com/highlightjs/highlight.js/issues"
},
"license": "BSD-3-Clause",
"repository": {
"type": "git",
"url": "git://github.com/highlightjs/highlight.js.git"
},
"sideEffects": [
"./es/common.js",
"./lib/common.js",
"*.css",
"*.scss"
],
"scripts": {
"mocha": "mocha",
"lint": "eslint src/*.js src/lib/*.js demo/*.js tools/**/*.js --ignore-pattern vendor",
"lint-languages": "eslint --no-eslintrc -c .eslintrc.lang.js src/languages/**/*.js",
"build_and_test": "npm run build && npm run test",
"build_and_test_browser": "npm run build-browser && npm run test-browser",
"build": "node ./tools/build.js -t node",
"build-cdn": "node ./tools/build.js -t cdn",
"build-browser": "node ./tools/build.js -t browser :common",
"devtool": "npx http-server",
"test": "mocha test",
"test-markup": "mocha test/markup",
"test-detect": "mocha test/detect",
"test-browser": "mocha test/browser",
"test-parser": "mocha test/parser"
},
"engines": {
"node": ">=12.0.0"
},
"devDependencies": {
"@colors/colors": "^1.6.0",
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-json": "^6.0.1",
"@rollup/plugin-node-resolve": "^15.3.0",
"@types/mocha": "^10.0.2",
"@typescript-eslint/eslint-plugin": "^7.15.0",
"@typescript-eslint/parser": "^7.15.0",
"clean-css": "^5.3.2",
"cli-table": "^0.3.1",
"commander": "^12.1.0",
"css": "^3.0.0",
"css-color-names": "^1.0.1",
"deep-freeze-es6": "^3.0.2",
"del": "^8.0.0",
"dependency-resolver": "^2.0.1",
"eslint": "^8.57.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.1.1",
"glob": "^8.1.0",
"glob-promise": "^6.0.5",
"handlebars": "^4.7.8",
"http-server": "^14.1.1",
"jsdom": "^25.0.1",
"lodash": "^4.17.20",
"mocha": "^11.0.1",
"refa": "^0.4.1",
"rollup": "^4.0.2",
"should": "^13.2.3",
"terser": "^5.21.0",
"tiny-worker": "^2.3.0",
"typescript": "^5.2.2",
"wcag-contrast": "^3.0.0"
}
}

View file

@ -7,17 +7,17 @@ export const REQUEST_TYPE_DELETE = 'DELETE';
export const CONTENT_TYPE_JSON = 'json';
export const CONTENT_TYPE_FORM = 'x-www-form-urlencoded';
//** ACTIONS URLS **//
export const API_NEW_PAGE = '/page/create';
export const API_EDIT_PAGE = '/page/write';
export const API_DELETE_PAGE = '/page/delete';
export const API_GET_SETTINGS = '/settings/site';
export const API_SETTINGS_SYNC = '/settings/sync';
export const API_PUBLISH_PAGES = '/settings/publish';
export const API_NAV_SYNC = '/settings/nav-sync';
export const API_NEW_PAGE = '/dashboard/pages/create';
export const API_EDIT_PAGE = '/dashboard/pages/write';
export const API_DELETE_PAGE = '/dashboard/pages/delete';
export const API_GET_SETTINGS = '/dashboard/settings/site';
export const API_SETTINGS_SYNC = '/dashboard/settings/sync';
export const API_PUBLISH_PAGES = '/dashboard/settings/publish';
export const API_NAV_SYNC = '/dashboard/settings/nav-sync';
export const API_GET_MEMBER_INFO = '/settings/member';
export const API_REINDEX_PAGES = '/settings/reindex';
export const API_SEND_MAIL = '/settings/mailer';
export const API_SEND_MAIL = '/dashboard/mailer';
export const API_LOGIN = '/login';
//** ACTIONS TASKS **//

View file

@ -9,11 +9,11 @@ export const CONTENT_TYPE_FORM = 'x-www-form-urlencoded';
//** API URLS **//
export const API_INIT = '/init/fresh';
export const API_RESTORE = '/init/restore';
export const API_RESET = '/init/reset';
export const API_CREATE_BACKUP = '/backup/create';
export const API_DOWNLOAD_BACKUP = '/backup/download';
export const API_RESTORE_BACKUP = '/backup/restore';
export const API_FILES_UPLOAD = '/upload/files';
export const API_RESET = '/dashboard/settings/reset';
export const API_CREATE_BACKUP = '/dashboard/settings/backup/create';
export const API_DOWNLOAD_BACKUP = '/dashboard/settings/backup/download';
export const API_RESTORE_BACKUP = '/dashboard/settings/backup/restore';
export const API_FILES_UPLOAD = '/dashboard/uploads';
//export const API_RESET_PASS = '/api/v1/reset-password';
@ -23,7 +23,7 @@ export const TASK_BACKUP_RESTORE = 'restoreBackup';
export const TASK_BACKUP_CREATE = 'createBackup';
export const TASK_GET_SECRET = 'retrieveSecret';
export const TASK_RESET_PASS = 'resetPassword';
export const TASK_UPLOAD_FILES = 'uploadFiles';
export const TASK_UPLOAD_FILES = '/task/uploadFiles';
//** API STATUS **//
export const API_ACCESS_GOOD = 'apiUseAuthorized';
export const API_ACCESS_BAD = 'apiUseNotAuthorized';

View file

@ -1,88 +1,88 @@
@extends('frame')
@extends('frame')
@section('title', 'Pages')
@section('main-content')
<section class="book-index-header">
<div class="book-index-header-left">
{{ $result['paginate']['sort'] }}
Pages
</div>
<div class="book-index-header-right">
<a href="/dashboard/pages/all" title="view all pages">
<button>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-folder"/>
</svg>
{{ $result['stats']['all'] }}
</button>
</a>
<a href="/dashboard/pages/published" title="view publised pages">
<button>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-globe"/>
</svg>
{{ $result['stats']['published'] }}
</button>
</a>
<a href="/dashboard/pages/deleted" title="view deleted pages">
<button>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-trash"/>
</svg>
{{ $result['stats']['deleted'] }}
</button>
</a>
</section>
<section class="book-index-pages">
@foreach($result['pages'] as $page)
@php
@section('main-content')
<section class="book-index-header">
<div class="book-index-header-left">
{{$result['paginate']['sort']}}
Pages
</div>
<div class="book-index-header-right">
<a href="/dashboard/pages/all" title="view all pages">
<button>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-folder" />
</svg>
{{$result['stats']['all']}}
</button>
</a>
<a href="/dashboard/pages/published" title="view publised pages">
<button>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-globe" />
</svg>
{{$result['stats']['published']}}
</button>
</a>
<a href="/dashboard/pages/deleted" title="view deleted pages">
<button>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-trash" />
</svg>
{{$result['stats']['deleted']}}
</button>
</a>
</section>
<section class="book-index-pages">
@foreach($result['pages'] as $page)
@php
$type = '';
$file = '';
isset($page['media'][0]['type']) ? $type = $page['media'][0]['type'] : $type = '';
isset($page['media'][0]['file']) ? $file = $page['media'][0]['file'] : $file = '';
@endphp
@if($type =='mp4')
<a href="/dashboard/page/edit/{{ $page['uuid'] }}" id="{{ $page['uuid'] }}" class="page-link">
<div class="page-video">
<video class="post-video" loop muted autoplay>
<source src="{{ $file }}" type="video/mp4">
@endphp
@if($type =='mp4')
<a href="/dashboard/pages/edit/{{$page['uuid']}}" id="{{$page['uuid']}}" class="page-link">
<div class="page-video">
<video class="post-video" loop muted autoplay>
<source src="{{$file}}" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
<div id="meta">
@include('includes.recent-meta')
Sorry, your browser doesn't support embedded videos.
</video>
<div id="meta">
@include('includes.recent-meta')
</div>
</div>
</div>
</a>
@else
<a href="/dashboard/page/edit/{{ $page['uuid'] }}" id="{{ $page['uuid'] }}" class="page-link">
<div class="page-bg" style="background: url({{ $file }}) no-repeat center center / cover #cf436b">
<div id="meta">
@include('includes.recent-meta')
</a>
@else
<a href="/dashboard/pages/edit/{{$page['uuid']}}" id="{{$page['uuid']}}" class="page-link">
<div class="page-bg" style="background: url({{$file}}) no-repeat center center / cover #cf436b">
<div id="meta">
@include('includes.recent-meta')
</div>
</div>
</div>
</a>
</a>
@endif
@endforeach
@if($result['numOfPages'])
<div class="paginate">
<a class="page-btns" href="/dashboard/pages/{{ $result['paginate']['sort'] }}/{{ $result['paginate']['prevPage'] }}">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-arrow-with-circle-left"/>
@endforeach
@if($result['numOfPages'])
<div class="paginate">
<a class="page-btns" href="/dashboard/pages/{{$result['paginate']['sort']}}/{{$result['paginate']['prevPage']}}">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-arrow-with-circle-left" />
</svg>
</a>
<span class="count">
{{ $currentPage }}
of
{{ $result['numOfPages'] }}
</span>
<a class="page-btns" href="/dashboard/pages/{{ $result['paginate']['sort'] }}/{{ $result['paginate']['nextPage'] }}">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-arrow-with-circle-right"/>
</svg>
</a>
</div>
@endif
</section>
@endsection
</a>
<span class="count">
{{$currentPage}}
of
{{$result['numOfPages']}}
</span>
<a class="page-btns" href="/dashboard/pages/{{$result['paginate']['sort']}}/{{$result['paginate']['nextPage']}}">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-arrow-with-circle-right" />
</svg>
</a>
</div>
@endif
</section>
@endsection

View file

@ -1,33 +0,0 @@
@extends('frame')
@section('title', 'The Dash | Edit Navigation')
@section('main-content')
<article class="navigation">
<section id="nav-items">
@foreach($menu as $item)
<div id="{{ $item['id'] }}" class="nav-item" data-slug="{{ $item['slug'] }}" data-uuid="{{ $item['uuid'] }}" data-path="{{ $item['id'] }}">
<svg id="move-menu-item" class="icon">
<use id="move-menu-item" xlink:href="/assets/images/global/sprite.svg#entypo-select-arrows"/>
</svg>
<label>{{ $item['title'] }}</label>
<div id="nav-btns">
<button id="edit-item" class="nav-btn" data-uuid="{{ $item['uuid'] }}" data-id="{{ $item['id'] }}" title="edit page">
<svg id="edit-item" class="icon" data-uuid="{{ $item['uuid'] }}" data-id="{{ $item['id'] }}">
<use id="edit-item" data-uuid="{{ $item['uuid'] }}" data-id="{{ $item['id'] }}" xlink:href="/assets/images/global/sprite.svg#entypo-edit"/>
</svg>
</button>
<button id="remove-item" class="nav-btn" data-uuid="{{ $item['uuid'] }}" data-id="{{ $item['id'] }}" title="delete from menu">
<svg id="remove-item" class="icon" data-uuid="{{ $item['uuid'] }}" data-id="{{ $item['id'] }}">
<use id="remove-item" data-uuid="{{ $item['uuid'] }}" data-id="{{ $item['id'] }}" xlink:href="/assets/images/global/sprite.svg#entypo-cross"/>
</svg>
</button>
</div>
</div>
@endforeach
</section>
</article>
@endsection
@section('scripting')
<script type="module" src="/assets/scripts/dash/app/EditNav.js"></script>
@endsection

View file

@ -1,191 +1,82 @@
@extends('frame')
@extends('frame')
@section('title', 'The Dash | Editing '. $editTitle)
@php
$title = urldecode($title);
if($mode == 'edit')
{
$id = $page['id'];
$uuid = $page['uuid'];
$slug = $page['slug'];
$feature = $page['feature'];
$layout = $page['layout'];
$tags = $page['tags'];
$content = $page['content'];
$date = $page['created'];
$updated = $page['updated'];
$media = $page['media'];
$files = $page['docs'];
}else{
$id = "";
$uuid = "";
$slug = "";
$feature = "";
$layout = "";
$tags = "";
$content = "";
$date = "";
$updated = "";
$media = "";
$files = "";
}
@endphp
@section('title', 'The Dash | Editing '. $title)
@section('main-content')
<section data-index="{{ $id }}" data-uuid="{{ $uuid }}" data-slug="{{ $slug }}" data-layout="{{ $layout }}" class="file-manager">
@if($feature == '')
<div class="file-drop">
<label for="page-files-upload">DRAG AND DROP FILES OR CLICK TO SELECT</label>
</div>
<label class="list-title">IMAGES AND VIDEO</label>
<div id="page-images-list" class="page-images-list"></div>
<label class="list-title">FILES</label>
<div class="page-files-list"></div>
@else
<div class="file-drop">
<label for="page-files-upload">DRAG AND DROP FILES OR CLICK TO SELECT</label>
</div>
<label class="list-title">IMAGES AND VIDEO</label>
<div id="page-images-list" class="page-images-list">
@if(count($media)>1)
@foreach($media as $item)
@if($item['type'] == "mp4")
<div id="{{ $loop->index }}" class="video-item" data-id="{{ $item['file'] }}">
<video>
<source src="{{ $item['file'] }}"/>
</video>
<button id="{{ $loop->index }}" class="media-remove">
X
</button>
</div>
@else
<div id="{{ $loop->index }}" class="img-item" data-id="{{ $item['file'] }}" style="background: url({{ $item['file'] }}) no-repeat center center / cover">
<button id="{{ $loop->index }}" class="media-remove">
X
</button>
</div>
@endif
@endforeach
@section('main-content')
@include('includes.editor')
<section class="meta hide-el">
<div data-index="{{$id}}" data-uuid="{{$uuid}}" data-slug="{{$slug}}" data-layout="{{$layout}}" class="file-manager">
ADD IMAGES AND FILES
<div class="file-drop">
<button id="files-upload" title="upload files">DRAG AND DROP FILES OR CLICK TO SELECT</button>
</div>
<div class="page-files-list"></div>
@if($feature == '')
<label class="list-title">IMAGES AND VIDEO</label>
<div id="page-images-list" class="page-images-list"></div>
<label class="list-title">FILES</label>
<div class="page-files-list"></div>
@else
@if($media[0] != '')
@if($media[0]['type'] == "mp4")
<div id="0" class="video-item" data-id="{{ $media[0]['file'] }}">
<button id="0" class="media-remove">X</button>
</div>
@else
<div id="0" class="img-item" data-id="{{ $media[0]['file'] }}" style="background: url({{ $media[0]['file'] }}) no-repeat center center / cover">
<button id="0" class="media-remove">X</button>
</div>
@endif
@endif
<label class="list-title">IMAGES AND VIDEO</label>
<div id="page-images-list" class="page-images-list">
@include('includes.media-list')
</div>
<label class="list-title">FILES</label>
<div class="page-files-list">
@include('includes.files-list')
</div>
@endif
</div>
<label class="list-title">FILES</label>
<div class="page-files-list">
@if(count($files) > 1)
@foreach($files as $item)
@php
$fileName = explode("/", $item['file']);
@endphp
@if($item['type'] == "mp3")
<div id="{{ $loop->index }}" class="audio-item" data-id="{{ $item['file'] }}">
<audio controls>
<source src="{{ $item['file'] }}"/>
</audio>
<button id="{{ $loop->index }}" class="media-remove">
X
</button>
</div>
@else
<div id="{{ $loop->index }}" class="file-item" data-id="{{ $item['file'] }}">
<a href="{{ $item['file'] }}" target="_blank">{{ $fileName[6] }}"</a>
<button id="{{ $loop->index }}" class="media-remove">
X
</button>
</div>
@endif
@endforeach
@else
@if(isset($files[0]) && $files[0] != '')
@php
$fileName = explode("/", $item['file']);
@endphp
@if($files[0]['type'] == "mp3")
<div id="0" class="audio-item" data-id="{{ $files[0]['file'] }}">
<audio controls>
<source src="{{ $files[0]['file'] }}"/>
</audio>
<button id="{{ $loop->index }}" class="media-remove">
X
</button>
</div>
@else
<div id="0" class="file-item" data-id="{{ $files[0]['file'] }}">
<a href="{{ $item['file'] }}" target="_blank">{{ $fileName[6] }}"</a>
<button id="{{ $loop->index }}" class="media-remove">
X
</button>
</div>
@endif
@else
@endif
@endif
<div class="page-meta">
<div class="page-layouts">
LAYOUTS
<select id="page-templates">
@foreach($views as $view)
@if($view == $layout)
<option value= {{$view}} selected>{{$view}}</option>
@else
<option value= {{$view}}>{{$view}}</option>
@endif
@endforeach
</select>
</div>
<div class="page-tags">
TAGS
<textarea id="post-tags" class="input-dark" type="text" name="post-tags" class="form-control" placeholder="tags [comma seperated]">{{$tags}}</textarea>
</div>
<div class="page-options">
OPTIONS
@include('includes.options')
</div>
<div class="page-updated">
<strong>UPDATED</strong><br />
<span id="post-date" type="text">
{{$updated}}
</span>
</div>
<div class="page-created">
<strong>CREATED</strong><br />
<span id="post-date" type="text">
{{$date}}
</span>
<input id="page-files-upload" type="file" name="page-files-upload" multiple />
<input id="post-image-upload" type="file" name="post-image-upload" />
<input id="form_token" name="token" type="hidden" value="needtoaddtoken">
</div>
</div>
@endif
</section><section class="page-meta">
<div class="page-meta-wrapper">
</section>
<section class="text-editor">
<div class="page-title">
<strong>TITLE</strong>
<textarea id="post-title-text" class="input-dark" type="text" name="post-title-text" class="post-edit" placeholder="TITLE">{{ $title }}</textarea>
<textarea id="post-title-text" class="input-dark" type="text" name="post-title-text" class="post-edit" placeholder="TITLE">{{$editTitle}}</textarea>
</div>
<div class="page-tags">
<strong>TAGS</strong>
<textarea id="post-tags" class="input-dark" type="text" name="post-tags" class="form-control" placeholder="tags [comma seperated]">{{ $tags }}</textarea>
<div class="edit-post-wrapper">
<textarea id="edit" spellcheck="false" class="language-md">{{$content}}</textarea>
<pre id="highlight"><code id="highlight-content" class="language-md"></code></pre>
</div>
<div class="page-layouts">
<strong>LAYOUTS</strong>
<select id="page-templates">
@foreach($views as $view)
@if($view == $layout)
<option value={{ $view }} selected>{{ $view }}</option>
@else
<option value={{ $view }}>{{ $view }}</option>
@endif
@endforeach
</select>
</div>
<div class="page-options">
<strong>OPTIONS</strong>
@include('includes.options')
</div>
<div class="page-updated">
<strong>UPDATED</strong>
<span id="post-date" type="text">
{{ $updated }}
</span>
</div>
<div class="page-created">
<strong>CREATED</strong>
<span id="post-date" type="text">
{{ $date }}
</span>
<input id="page-files-upload" type="file" name="page-files-upload" multiple/>
<input id="post-image-upload" type="file" name="post-image-upload"/>
<input id="form_token" name="token" type="hidden" value="needtoaddtoken"></div>
</div>
</div>
</section>
<section class="text-editor">
@include('includes.editor')
<div class="edit-post-wrapper">
<textarea id="edit" spellcheck="false" class="language-md">{{ $content }}</textarea>
<pre id="highlight"><code id="highlight-content" class="language-md"></code></pre>
</div>
</section>
@endsection
@section('scripting')
<script type="module" src="/assets/scripts/dash/app/EditPage.js"></script>
@endsection
</section>
@endsection
@section('scripting')
<script type="module" src="/assets/scripts/dash/app/EditPage.js"></script>
@endsection

View file

@ -1,169 +1,198 @@
@extends('frame')
@extends('frame')
<?php
$status = $settings['status'];
$title = $settings['title'];
$renderOnSave = $settings['renderOnSave'];
?>
@section('title', 'The Dash | '. $title)
@section('title', 'The Dash | '. $settings['title'])
@section('main-content')
<article class="settings">
<div class="tab-toolbar" role="toolbar">
<button id="profile" class="tab-button">PROFILE</button>
<button id="features" class="tab-button">FEATURES</button>
<button id="themes" class="tab-button">THEMES</button>
</div>
<section class="settings-tabs">
<section id="site-profile" class="section-tab show">
<div class="member-avatar">
<div class="avatar" style="background: url({{ $member['avatar'] }} ) no-repeat center center / cover"></div>
<input id="avatar-upload" type="file" name="avatar-upload"/>
</div>
<div class="site-background">
<div class="background" style="background: url({{ $background }} ) no-repeat center center / cover"></div>
<input id="background-upload" type="file" name="backgrond-upload"/>
</div>
<div>
<input type='text' class="input-dark" name='handle' id='settings-handle' placeholder='handle' value="{{ $member['handle'] }}" autofocus/>
<input type='text' class="input-dark" name='email' id='settings-email' placeholder='email' value="{{ $member['email'] }}" autofocus/>
<input type='hidden' name='member-id' id='member-id' value="{{ $member['id'] }}"/>
</div>
<div>
<input type='text' class="input-dark" name='base-url' id='settings-url' placeholder='url' value="{{ $baseUrl }}" autofocus/>
<input type='text' class="input-dark" name='base-title' id='settings-title' placeholder='site title' value="{{ $siteTitle }}" autofocus/>
</div>
<div>
<textarea id="settings-desc" class="input-dark" type='text' name='settings_desc' class='settings-dec' placeholder='description stuff' , autofocus>{{ $desc }}</textarea>
</div>
<div>
<div>
<label>API KEY</label>
<span>{{ $member['key'] }}</span>
@section('main-content')
<article class="settings">
<div class="tab-toolbar" role="toolbar">
<button id="profile" class="tab-button">PROFILE</button>
<button id="features" class="tab-button">FEATURES</button>
<button id="themes" class="tab-button">THEMES</button>
<button id="menu" class="tab-button">MENU</button>
</div>
<section class="settings-tabs">
<section id="site-profile" class="section-tab show">
<div class="member-avatar">
<div class="avatar" style="background: url({{$settings['member']['avatar']}} ) no-repeat center center / cover"></div>
<input id="avatar-upload" type="file" name="avatar-upload" />
</div>
<div class="site-background">
<div class="background" style="background: url({{$settings['background']}} ) no-repeat center center / cover"></div>
<input id="background-upload" type="file" name="backgrond-upload" />
</div>
<div>
<label>FORM TOKEN</label><br/>
<span>{{ $ftoken }}</span>
<input type='text' class="input-dark" name='handle' id='settings-handle' placeholder='handle' value="{{$settings['member']['handle']}}" autofocus />
<input type='text' class="input-dark" name='email' id='settings-email' placeholder='email' value="{{$settings['member']['email']}}" autofocus />
<input type='hidden' name='member-id' id='member-id' value="{{$settings['member']['id']}}" />
</div>
</div>
</section>
<section id="site-features" class="section-tab hide">
<div class="features-mail">
<button id="send-test-mail">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-mail-with-circle"/>
</svg>
<span>TEST MAIL</span>
</button>
<div>
<label>SYSTEM EMAIL</label><br />
set email settings in .env file
<input type='text' class="input-dark" name='base-url' id='settings-url' placeholder='url' value="{{$settings['baseUrl']}}" autofocus />
<input type='text' class="input-dark" name='base-title' id='settings-title' placeholder='site title' value="{{$settings['siteTitle']}}" autofocus />
</div>
</div>
<div class="site-options">
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-landline"/>
</svg>
@if(isset($apiStatus) && $apiStatus == 'true')
<button id="api-access-toggle" title="allow external api" data-enabled="true">
<span id="api-status">API ACCESS ENABLED</span>
</button>
@else
<button id="api-access-toggle" title="allow external api" data-enabled="false">
<span id="api-status">API ACCESS NOT ENABLED</span>
</button>
@endif
<div>
<textarea id="settings-desc" class="input-dark" type='text' name='settings_desc' class='settings-dec' placeholder='description stuff' , autofocus>{{$settings['desc']}}</textarea>
</div>
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-cycle"/>
</svg>
@if(isset($dynamicRenderStatus) && $dynamicRenderStatus == 'true')
<button id="dynamic-render-toggle" title="allow external api" data-enabled="true">
<span id="dynamic-render-status">DYNAMIC PAGE RENDERING</span>
</button>
<div>
<div>
<label>API KEY</label>
<span>{{$settings['member']['key']}}</span>
</div>
<div>
<label>FORM TOKEN</label><br />
<span>{{$settings['ftoken']}}</span>
</div>
@else
<button id="dynamic-render-toggle" title="allow external api" data-enabled="false">
<span id="dynamic-render-status">STATIC PAGE RENDERING</span>
</button>
@endif
</div>
</div>
<div class="site-maintenance">
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-copy"/>
</svg>
<button id="create-content-backup">
<span>CONTENT BACKUP</span>
</section>
<section id="site-features" class="section-tab hide">
<div class="features-mail">
<button id="send-test-mail">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-mail-with-circle" />
</svg>
<span>TEST MAIL</span>
</button>
<span>
@if($lastContentBackup != '')
MOST RECENT:
<a href="/backup/content-download">{{ $lastContentBackup }}</a><br/>
<div>
<label>SYSTEM EMAIL</label><br />
set email settings in .env file
</div>
</div>
<div class="site-options">
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-landline" />
</svg>
@if(isset($settings['apiStatus']) && $settings['apiStatus'] == 'true')
<button id="api-access-toggle" title="allow external api" data-enabled="true">
<span id="api-status">API ACCESS ENABLED</span>
</button>
@else
<span>span No back ups. Frowny face.</span>
<button id="api-access-toggle" title="allow external api" data-enabled="false">
<span id="api-status">API ACCESS NOT ENABLED</span>
</button>
@endif
<span>
</div>
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-images"/>
</svg>
<button id="create-file-backup">
<span>FILE BACKUP</span>
</button>
<span>
@if($lastFilesBackup != '')
MOST RECENT:
<a href="/backup/files-download">{{ $lastFilesBackup }}</a><br/>
</div>
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-cycle" />
</svg>
@if(isset($settings['dynamicRenderStatus']) && $settings['dynamicRenderStatus'] == 'true')
<button id="dynamic-render-toggle" title="allow external api" data-enabled="true">
<span id="dynamic-render-status">DYNAMIC PAGE RENDERING</span>
</button>
@else
<span>span No back ups. Frowny face.</span>
<button id="dynamic-render-toggle" title="allow external api" data-enabled="false">
<span id="dynamic-render-status">STATIC PAGE RENDERING</span>
</button>
@endif
</span>
</div>
</div>
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-back-in-time"/>
</svg>
<button id="reset-to-default">
<span>RESET TO DEFAULT</span>
</button>
<span>Deletes all content and configs <strong>CANNOT UNDO</strong></span>
<div class="site-maintenance">
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-copy" />
</svg>
<button id="create-content-backup">
<span>CONTENT BACKUP</span>
</button>
<span>
@if($settings['lastContentBackup'] != '')
MOST RECENT:
<a href="/dashboard/settings/backup/get/content-download">{{$settings['lastContentBackup']}}</a><br />
@else
<span>span No back ups. Frowny face.</span>
@endif
<span>
</div>
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-images" />
</svg>
<button id="create-file-backup">
<span>FILE BACKUP</span>
</button>
<span>
@if($settings['lastFilesBackup'] != '')
MOST RECENT:
<a href="/dashboard/settings/backup/get/files-download">{{$settings['lastFilesBackup']}}</a><br />
@else
<span>span No back ups. Frowny face.</span>
@endif
</span>
</div>
<div class="option-container">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-back-in-time" />
</svg>
<button id="reset-to-default">
<span>RESET TO DEFAULT</span>
</button>
<span>Deletes all content and configs <strong>CANNOT UNDO</strong></span>
</div>
</div>
</div>
</section>
<section id="site-themes" class="section-tab hide">
@foreach($themes as $theme)
@if($theme['name'] == $currentTheme)
<!--
</section>
<section id="site-themes" class="section-tab hide">
@foreach($settings['themes'] as $theme)
@if($theme['name'] == $settings['currentTheme'])
<!--
<a target="_blank" href='/theme'>Edit</a>
-->
<button id="{{ $theme['name'] }}" class="theme-select" data-enabled="true">
<div for="{{ $theme['name'] }}">
<label id="label-{{ $theme['name'] }}">ACTIVE THEME</label>
</div>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-palette"/>
<button id="{{$theme['name']}}" class="theme-select" data-enabled="true">
<div for="{{$theme['name']}}">
<label id="label-{{$theme['name']}}">ACTIVE THEME</label>
</div>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-palette" />
</svg>
<span for="{{$theme['name']}}">{{$theme['display-name']}}</span>
</button>
@else
<button id="{{$theme['name']}}" class="theme-select" data-enabled="false">
<div for="{{$theme['name']}}">
<label id="label-{{$theme['name']}}">INACTIVE THEME</label>
</div>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-palette" />
</svg>
<span for="{{$theme['name']}}">{{$theme['display-name']}}</span>
</button>
@endif
@endforeach
</section>
<section id="site-menu" class="section-tab hide">
@foreach($nav['menu'] as $item)
<div id="{{$item['id']}}" class="nav-item" data-slug="{{$item['slug']}}" data-uuid="{{$item['uuid']}}" data-path="{{$item['id']}}">
<svg id="move-menu-item" class="icon">
<use id="move-menu-item" xlink:href="/assets/images/global/sprite.svg#entypo-select-arrows" />
</svg>
<span for="{{ $theme['name'] }}">{{ $theme['display-name'] }}</span>
</button>
@else
<button id="{{ $theme['name'] }}" class="theme-select" data-enabled="false">
<div for="{{ $theme['name'] }}">
<label id="label-{{ $theme['name'] }}">INACTIVE THEME</label>
<label>@php echo urldecode($item['title']) @endphp</label>
<div id="nav-btns">
<button id="edit-item" class="nav-btn" data-uuid="{{$item['uuid']}}" data-id="{{$item['id']}}" title="edit page">
<svg id="edit-item" class="icon" data-uuid="{{$item['uuid']}}" data-id="{{$item['id']}}">
<use id="edit-item" data-uuid="{{$item['uuid']}}" data-id="{{$item['id']}}" xlink:href="/assets/images/global/sprite.svg#entypo-edit" />
</svg>
</button>
<button id="remove-item" class="nav-btn" data-uuid="{{$item['uuid']}}" data-id="{{$item['id']}}" title="delete from menu">
<svg id="remove-item" class="icon" data-uuid="{{$item['uuid']}}" data-id="{{$item['id']}}">
<use id="remove-item" data-uuid="{{$item['uuid']}}" data-id="{{$item['id']}}" xlink:href="/assets/images/global/sprite.svg#entypo-cross" />
</svg>
</button>
</div>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-palette"/>
</svg>
<span for="{{ $theme['name'] }}">{{ $theme['display-name'] }}</span>
</button>
@endif
@endforeach
</div>
@endforeach
</section>
</section>
</section>
</article>
@endsection
@section('scripting')
<script type="module" src="/assets/scripts/dash/app/EditSettings.js"></script>
@endsection
</article>
@endsection
@section('scripting')
<script type="module" src="/assets/scripts/dash/app/EditSettings.js"></script>
<script type="module" src="/assets/scripts/dash/app/EditNav.js"></script>
@endsection

View file

@ -1,38 +1,38 @@
@extends('frame')
@extends('frame')
@section('title', 'The Dash | Start')
@section('main-content')
<section class="index-header">
<div class="index-header-left">
<h1>Recent</h1>
</div>
<div class="index-header-right"></div>
</section>
<section class="index-recent-pages">
@if($result['entryCount'] != 0)
@foreach($result['pages'] as $page)
@php
@section('main-content')
<section class="index-header">
<div class="index-header-left">
<h1>Recent</h1>
</div>
<div class="index-header-right"></div>
</section>
<section class="index-recent-pages">
@if($result['entryCount'] != 0)
@foreach($result['pages'] as $page)
@php
$type = '';
$file = '';
isset($page['media'][0]['type']) ? $type = $page['media'][0]['type'] : $type = '';
isset($page['media'][0]['file']) ? $file = $page['media'][0]['file'] : $file = '';
@endphp
@if($type =='mp4')
<a href="/dashboard/page/edit/{{ $page['uuid'] }}" id="{{ $page['uuid'] }}" class="post-video-link recent-link">
@include('includes.recent-meta')
<video class="post-video" loop muted autoplay>
<source src="{{ $file }}" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
</a>
@else
<a href="/dashboard/page/edit/{{ $page['uuid'] }}" id="{{ $page['uuid'] }}" class="post-link recent-link" style="background: url({{ $file }}) no-repeat center center / cover #cf436b">
@include('includes.recent-meta')
</a>
@endif
@endforeach
@endif
</section>
@endphp
@if($type =='mp4')
<a href="/dashboard/page/edit/{{$page['uuid']}}" id="{{$page['uuid']}}" class="post-video-link recent-link">
@include('includes.recent-meta')
<video class="post-video" loop muted autoplay>
<source src="{{$file}}" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
</a>
@else
<a href="/dashboard/pages/edit/{{$page['uuid']}}" id="{{$page['uuid']}}" class="post-link recent-link" style="background: url({{$file}}) no-repeat center center / cover #cf436b">
@include('includes.recent-meta')
</a>
@endif
@endforeach
@endif
</section>
@endsection
@endsection

View file

@ -1,10 +1,10 @@
<section class="login">
<div>
<img alt="fipamo logo" id="the-logo" class="logo-medium" src="/assets/images/global/fipamo-logo-secondary.svg"/>
<img alt="fipamo logo" id="the-logo" class="logo-medium" src="/assets/images/global/fipamo-logo-secondary.svg" />
</div>
<form action="/login" method="post" enctype="multipart/form-data">
<label class="inline">handle</label><input class="input-light" type="text" name="handle" class="inline" placeholder="Handle" required/>
<label class="inline">password</label><input class="input-light" type="password" name="password" class="inline" placeholder="Password" required/>
<form action="/dashboard/login" method="post" enctype="multipart/form-data">
<label class="inline">handle</label><input class="input-light" type="text" name="handle" class="inline" placeholder="Handle" required />
<label class="inline">password</label><input class="input-light" type="password" name="password" class="inline" placeholder="Password" required />
@if($errors->any())
<input type="submit" value="{{$errors->first()}}" name="submit_button">
@else
@ -12,4 +12,4 @@
@endif
@csrf
</form>
</section>
</section>

View file

@ -1,60 +1,42 @@
<div class="text-editor-control">
<div class="text-editor-control hide-el">
<button id="edit-bold" class="content-editor-btn-text editor-button" title="bold">
<svg id="edit-bold" class="icon">
<use id="edit-bold" xlink:href="/assets/images/global/sprite.svg#tabler-bold"/>
</svg>
<svg id="edit-bold" class="editor-icon">
<use id="edit-bold" xlink:href="/assets/images/global/sprite.svg#tabler-bold" />
</svg>
</button>
<button id="edit-italic" class="content-editor-btn-text editor-button" title="italic">
<svg id="edit-italic" class="icon">
<use id="edit-italic" xlink:href="/assets/images/global/sprite.svg#tabler-italic"/>
</svg>
<svg id="edit-italic" class="editor-icon">
<use id="edit-italic" xlink:href="/assets/images/global/sprite.svg#tabler-italic" />
</svg>
</button>
<button id="edit-strikethrough" class="content-editor-btn-text editor-button" title="strikethrough">
<svg id="edit-strikethrough" class="icon">
<use id="edit-strikethrough" xlink:href="/assets/images/global/sprite.svg#tabler-strikethrough"/>
</svg>
<svg id="edit-strikethrough" class="editor-icon">
<use id="edit-strikethrough" xlink:href="/assets/images/global/sprite.svg#tabler-strikethrough" />
</svg>
</button>
<button id="edit-link" class="content-editor-btn-icon editor-button" title="insert link">
<svg id="edit-link" class="icon">
<use id="edit-link" xlink:href="/assets/images/global/sprite.svg#entypo-link"/>
</svg>
<svg id="edit-link" class="editor-icon">
<use id="edit-link" xlink:href="/assets/images/global/sprite.svg#entypo-link" />
</svg>
</button>
<button id="edit-header1" class="content-editor-btn-text editor-button" title="header 1">
<svg id="edit-header1" class="icon">
<use id="edit-header1" xlink:href="/assets/images/global/sprite.svg#tabler-heading-1"/>
</svg>
<svg id="edit-header1" class="editor-icon">
<use id="edit-header1" xlink:href="/assets/images/global/sprite.svg#tabler-heading-1" />
</svg>
</button>
<button id="edit-header2" class="content-editor-btn-text editor-button" title="header 2">
<svg id="edit-header2" class="icon">
<use id="edit-header2" xlink:href="/assets/images/global/sprite.svg#tabler-heading-2"/>
</svg>
<svg id="edit-header2" class="editor-icon">
<use id="edit-header2" xlink:href="/assets/images/global/sprite.svg#tabler-heading-2" />
</svg>
</button>
<button id="edit-header3" class="content-editor-btn-text editor-button" title="header 3">
<svg id="edit-header3" class="icon">
<use id="edit-header3" xlink:href="/assets/images/global/sprite.svg#tabler-heading-3"/>
</svg>
<svg id="edit-header3" class="editor-icon">
<use id="edit-header3" xlink:href="/assets/images/global/sprite.svg#tabler-heading-3" />
</svg>
</button>
<button id="edit-image" class="content-editor-btn-icon editor-button" title="insert image">
<svg id="edit-image" class="icon">
<use id="edit-image" xlink:href="/assets/images/global/sprite.svg#entypo-image"/>
</svg>
<svg id="edit-image" class="editor-icon">
<use id="edit-image" xlink:href="/assets/images/global/sprite.svg#entypo-image" />
</svg>
</button>
@if($mode == "edit")
<button id="edit-update" class="post-sumbit-btn submit-start editor-button" data-action='blog-update' data-id="{{ $page['uuid'] }} type='submit' title=" bold">
<svg id="edit-update" class="icon">
<use id="edit-update" xlink:href="/assets/images/global/sprite.svg#entypo-save"/>
</svg>
</button>
<button id="edit-delete" class="content-editor-btn-icon editor-button submit-delete" for="post-delete" title='delete post'>
<svg id="edit-delete" class="icon">
<use id="edit-delete" xlink:href="/assets/images/global/sprite.svg#entypo-cross"/>
</svg>
</button>
@else
<button id="edit-save" class="post-sumbit-btn submit-start editor-button" data-action='blog-add' type='submit'>
<svg id="edit-save" class="icon">
<use id="edit-save" xlink:href="/assets/images/global/sprite.svg#entypo-plus"/>
</svg>
</button>
@endif
</div>
</div>

View file

@ -0,0 +1,50 @@
@if(count($files) > 1)
@foreach($files as $item)
@php
$fileName = explode("/", $item['file']);
@endphp
@if($item['type'] == "mp3")
<div id="{{$loop->index}}" class="audio-item" data-id="{{$item['file']}}">
<audio controls>
<source src="{{$item['file']}}" />
</audio>
<button id="{{$loop->index}}" class="media-remove">
X
</button>
</div>
@else
<div id="{{$loop->index}}" class="file-item" data-id="{{$item['file']}}">
<a href="{{$item['file']}}" target="_blank">{{$fileName[6]}}"</a>
<button id="{{$loop->index}}" class="media-remove">
X
</button>
</div>
@endif
@endforeach
@else
@if(isset($files[0]) && $files[0] != '')
@php
$fileName = explode("/", $item['file']);
@endphp
@if($files[0]['type'] == "mp3")
<div id="0" class="audio-item" data-id="{{$files[0]['file']}}">
<audio controls>
<source src="{{$files[0]['file']}}" />
</audio>
<button id="{{$loop->index}}" class="media-remove">
X
</button>
</div>
@else
<div id="0" class="file-item" data-id="{{$files[0]['file']}}">
<a href="{{$item['file']}}" target="_blank">{{$fileName[6]}}"</a>
<button id="{{$loop->index}}" class="media-remove">
X
</button>
</div>
@endif
@else
@endif
@endif

View file

@ -0,0 +1,32 @@
@if(count($media)>1)
@foreach($media as $item)
@if($item['type'] == "mp4")
<div id="{{$loop->index}}" class="video-item" data-id="{{$item['file']}}">
<video>
<source src="{{$item['file']}}" />
</video>
<button id="{{$loop->index}}" class="media-remove">
X
</button>
</div>
@else
<div id="{{$loop->index}}" class="img-item" data-id="{{$item['file']}}" style="background: url({{$item['file']}}) no-repeat center center / cover">
<button id="{{$loop->index}}" class="media-remove">
X
</button>
</div>
@endif
@endforeach
@else
@if($media[0] != '')
@if($media[0]['type'] == "mp4")
<div id="0" class="video-item" data-id="{{$media[0]['file']}}">
<button id="0" class="media-remove">X</button>
</div>
@else
<div id="0" class="img-item" data-id="{{$media[0]['file']}}" style="background: url({{$media[0]['file']}}) no-repeat center center / cover">
<button id="0" class="media-remove">X</button>
</div>
@endif
@endif
@endif

View file

@ -1,52 +1,59 @@
<div class="menu">
@if($title == "Settings" )
@include('includes.submenu-settings')
@elseif($title == "Start" )
@include('includes.submenu-start')
@endif
@switch($title)
@case("Settings")
@include('includes.submenu-settings')
@break
@case("Start")
@include('includes.submenu-start')
@break
@case("Add New")
@case("Edit Page")
@include('includes.submenu-edit-page')
@break
@endswitch
<a id="settings" class="main-nav-secondary-highlight" href="/dashboard/settings" title="settings">
<svg id="nav-settings-icon" class="icon">
<use id="nav-settings-icon" xlink:href="/assets/images/global/sprite.svg#entypo-cog"/>
</svg>
</a>
<a id="navigation" class="main-nav-secondary-highlight" href="/dashboard/navigation" title="edit navigation">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-link"/>
<use id="nav-settings-icon" xlink:href="/assets/images/global/sprite.svg#entypo-cog" />
</svg>
</a>
<a id="navigation" class="main-nav-secondary-highlight" href="/dashboard/logout" title="log out">
<svg id="nav-logout-icon" class="icon">
<use id="nav-logout-icon" xlink:href="/assets/images/global/sprite.svg#entypo-log-out"/>
<use id="nav-logout-icon" xlink:href="/assets/images/global/sprite.svg#entypo-log-out" />
</svg>
</a>
</div>
<label id='menu-toggle-label' for="mobile-toggle">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-menu"/>
</svg>
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-menu" />
</svg>
</label>
<input id="mobile-toggle" type="checkbox" />
<div class="mobile-menu">
@if($title == "Settings" )
@include('includes.submenu-settings')
@elseif($title == "Start" )
@include('includes.submenu-start')
@endif
@switch($title)
@case("Settings")
@include('includes.submenu-settings')
@break
@case("Start")
@include('includes.submenu-start')
@break
@case("Edit Page")
@include('includes.submenu-edit-page')
@break
@endswitch
<a id="settings" class="main-nav-secondary-highlight" href="/dashboard/settings" title="settings">
<svg id="nav-settings-icon" class="icon">
<use id="nav-settings-icon" xlink:href="/assets/images/global/sprite.svg#entypo-cog"/>
</svg>
</a>
<a id="navigation" class="main-nav-secondary-highlight" href="/dashboard/navigation" title="edit navigation">
<svg id="nav-menu-icon" class="icon">
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-link"/>
<use id="nav-settings-icon" xlink:href="/assets/images/global/sprite.svg#entypo-cog" />
</svg>
</a>
<a id="navigation" class="main-nav-secondary-highlight" href="/dashboard/logout" title="log out">
<svg id="nav-logout-icon" class="icon">
<use id="nav-logout-icon" xlink:href="/assets/images/global/sprite.svg#entypo-log-out"/>
<use id="nav-logout-icon" xlink:href="/assets/images/global/sprite.svg#entypo-log-out" />
</svg>
</a>
</div>
</div>

View file

@ -1,5 +1,4 @@
@php
<?php
if(isset($page['menu']) && $page['menu'])
{
@ -28,26 +27,25 @@ if(isset($page['uuid']))
}else{
$uuid = 1;
}
@endphp
?>
<div role="toolbar" aria-label="page options" aria-orientation="horizontal">
<button id="option-menu-pin" class="option-inactive post-option-btn" data-active="{{ $menu }}" title='Pin to Menu' aria-label="pin to menu">
<button id="option-menu-pin" class="option-inactive post-option-btn" aria-pressed="{{$menu}}" data-active="{{$menu}}" title='Pin to Menu' aria-label="pin to menu">
<svg id="option-menu-pin" class="icon">
<use id="option-menu-pin" xlink:href="/assets/images/global/sprite.svg#entypo-pin"/>
<use id="option-menu-pin" xlink:href="/assets/images/global/sprite.svg#entypo-pin" />
</svg>
</button><button id="option-feature" class="option-inactive post-option-btn" data-active="{{ $featured }}" title='Feature' aria-label="feature post toggle">
</button><button id="option-feature" class="option-inactive post-option-btn" aria-pressed="{{$featured}}" data-active="{{$featured}}" title='Feature' aria-label="feature post toggle">
<svg id="option-feature-icon" class="icon">
<use id="option-feature-icon" xlink:href="/assets/images/global/sprite.svg#entypo-star"/>
<use id="option-feature-icon" xlink:href="/assets/images/global/sprite.svg#entypo-star" />
</svg>
</button><button id="option-published" class="option-inactive post-option-btn" data-active="{{ $published }}" title='Published' aria-label="publish post toggle">
</button><button id="option-published" class="option-inactive post-option-btn" aria-pressed="{{$published}}" data-active="{{$published}}" title='Published' aria-label="publish post toggle">
<svg id="option-published-icon" class="icon">
<use id="option-published-icon" xlink:href="/assets/images/global/sprite.svg#entypo-globe"/>
<use id="option-published-icon" xlink:href="/assets/images/global/sprite.svg#entypo-globe" />
</svg>
</button><a href="/theme/view/page/{{ $uuid }}" target="_blank"><button id="option-preview" class="option-inactive post-option-btn" data-active="false" title='preview page' aria-label="preview post">
</button><a href="/dashboard/themekit/view/page/{{$uuid}}" target="_blank"><button id="option-preview" class="option-inactive post-option-btn" data-active="false" title='preview page' aria-label="preview post">
<svg id="option-preview-icon" class="icon">
<use id="option-preview-icon" xlink:href="/assets/images/global/sprite.svg#entypo-eye"/>
<use id="option-preview-icon" xlink:href="/assets/images/global/sprite.svg#entypo-eye" />
</svg>
</button>
</a>
</div>
</div>

View file

@ -1,4 +1,4 @@
@php
<?php
if($page['menu'] == 'true')
{
@ -21,35 +21,35 @@ if($page['featured'] == 'true')
$featured = 'false';
}
@endphp
?>
<aside>
<strong>
{{ $page['updated'] }}
{{$page['updated']}}
</strong>
<hr/>
<hr />
<strong>
@php
$title = urldecode($page['title']);
@endphp
{{ $title }}
{{$title}}
</strong>
<hr/>
<button data-active="{{ $menu }}">
<hr />
<button data-active="{{$menu}}">
<svg id="option-menu-pin" class="icon">
<use id="option-menu-pin" xlink:href="/assets/images/global/sprite.svg#entypo-pin"/>
<use id="option-menu-pin" xlink:href="/assets/images/global/sprite.svg#entypo-pin" />
</svg>
</button>
<button data-active="{{ $published }}">
<button data-active="{{$published}}">
<svg id="option-published-icon" class="icon">
<use id="option-published-icon" xlink:href="/assets/images/global/sprite.svg#entypo-globe"/>
<use id="option-published-icon" xlink:href="/assets/images/global/sprite.svg#entypo-globe" />
</svg>
</button>
<button data-active="{{ $featured }}">
<button data-active="{{$featured}}">
<svg id="option-feature-icon" class="icon">
<use id="option-feature-icon" xlink:href="/assets/images/global/sprite.svg#entypo-star"/>
<use id="option-feature-icon" xlink:href="/assets/images/global/sprite.svg#entypo-star" />
</svg>
</button>
</aside>
</aside>

View file

@ -0,0 +1,25 @@
<div class="submenu">
<button id="meta-info-toggle" title="page meta">
<svg id="meta-info-toggle" class="icon">
<use id="meta-info-toggle" xlink:href="/assets/images/global/sprite.svg#entypo-layers" />
</svg>
</button>
@if($mode == "edit")
<button id="edit-update" class="post-sumbit-btn submit-start editor-button" data-action='blog-update' data-id="{{$page['uuid']}}" title="update page">
<svg id="edit-update" class="icon">
<use id="edit-update" xlink:href="/assets/images/global/sprite.svg#entypo-save" />
</svg>
</button>
<button id="edit-delete" class="content-editor-btn-icon editor-button submit-delete" for="post-delete" title='delete page'>
<svg id="edit-delete" class="icon">
<use id="edit-delete" xlink:href="/assets/images/global/sprite.svg#entypo-cross" />
</svg>
</button>
@else
<button id="edit-save" class="post-sumbit-btn submit-start editor-button" data-action='blog-add' type='submit'>
<svg id="edit-save" class="icon">
<use id="edit-save" xlink:href="/assets/images/global/sprite.svg#entypo-plus" />
</svg>
</button>
@endif
</div>

View file

@ -1,12 +1,12 @@
<div class="submenu">
<a class="main-nav-primary" href='/dashboard/pages' title="view pages">
<svg id="option-menu-pin" class="icon">
<use id="option-menu-pin" xlink:href="/assets/images/global/sprite.svg#minicute-book"/>
<use id="option-menu-pin" xlink:href="/assets/images/global/sprite.svg#minicute-book" />
</svg>
</a>
<a class="main-nav-primary" href='/dashboard/page/add/new' title="add new page">
<a class="main-nav-primary" href='/dashboard/pages/add/new' title="add new page">
<svg id="option-menu-pin" class="icon">
<use id="option-menu-pin" xlink:href="/assets/images/global/sprite.svg#entypo-squared-plus"/>
<use id="option-menu-pin" xlink:href="/assets/images/global/sprite.svg#entypo-squared-plus" />
</svg>
</a>
</div>
</div>

View file

@ -1,23 +1,23 @@
@extends('frame')
@extends('frame')
@section('title', 'The Dash | Fipamo Theme Kit')
@section('main-content')
<section class="index-header">
<div class="index-header-left">
<h1>Templates</h1>
<h2>Base</h2>
<a class="secondary" href="/theme/view/index">Index</a><br />
<a class="secondary" href="/theme/view/page">Page</a><br />
<a class="secondary" href="/theme/view/tags">Tags</a><br />
<a class="secondary" href="/theme/view/archive">Archive</a><br />
<h2>Custom</h2>
@foreach($pages as $view)
@if($view != 'page')
<a href="/theme/view/{{ $view }}" class="secondary" href="">{{ $view }}</a><br />
@endif
@endforeach
</div>
<div class="index-header-right"></div>
</section>
@endsection
@section('main-content')
<section class="index-header">
<div class="index-header-left">
<h1>Templates</h1>
<h2>Base</h2>
<a class="secondary" href="/dashboard/themekit/view/index">Index</a><br />
<a class="secondary" href="/dashboard/themekit/view/page">Page</a><br />
<a class="secondary" href="/dashboard/themekit/view/tags">Tags</a><br />
<a class="secondary" href="/dashboard/themekit/view/archive">Archive</a><br />
<h2>Custom</h2>
@foreach($pages as $view)
@if($view != 'page')
<a href="/dashboard/themekit/view/{{$view}}" class="secondary" href="">{{$view}}</a><br />
@endif
@endforeach
</div>
<div class="index-header-right"></div>
</section>
@endsection

View file

@ -1,28 +1,83 @@
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\RouteGetController;
use App\Http\Controllers\RoutePostController;
use App\Http\Controllers\RoutePutController;
use App\Http\Controllers\RouteDeleteController;
use App\Http\Middleware\VerifyCsrfToken;
use Illuminate\Http\Request;
use App\Http\Controllers\FrontController;
use App\Http\Controllers\PageController;
use App\Http\Controllers\DashController;
use App\Http\Controllers\SettingsController;
use App\Http\Controllers\ThemeController;
use App\Http\Controllers\SystemMailController;
use App\Http\Controllers\RSSController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
//Frontend
Route::prefix('/')
->controller(FrontController::class)
->group(function () {
Route::get("/", 'start')->name('start');
Route::get("/{year}/{month}/{slug}", 'page')
->where(['year' => '[0-9]+', 'month' => '[0-9]+', 'slug' => '[A-Za-z0-9-]+']);
Route::get("/{slug}/{option?}", 'menu')
->whereIn('slug', FrontController::items());
Route::post("/init/{task}", 'init');
});
//routing needs a bit more nuance, so requests are sent to a controller to sort traffic
Route::get("/{first?}/{second?}/{third?}/{four?}", [RouteGetController::class, 'handleRequest']);
Route::post("/{first?}/{second?}/{third?}", [RoutePostController::class, 'handleRequest'])
->middleware(VerifyCsrfToken::class);
Route::put("/{first?}/{second?}/{third?}", [RoutePutController::class, 'handleRequest'])
->middleware(VerifyCsrfToken::class);
Route::delete("/{first?}/{second?}/{third?}", [RouteDeleteController::class, 'handleRequest'])
->middleware(VerifyCsrfToken::class);
//Dashboard
Route::prefix('dashboard')
->middleware('member.check')
->controller(DashController::class)
->group(function () {
Route::post("/login", 'enter')->withoutMiddleware('member.check');
Route::get("/", 'start')->withoutMiddleware('member.check');
Route::get("/logout", 'exit');
Route::post("/uploads", 'uploads');
})->name('dashboard');
//Pages
Route::prefix('dashboard/pages')
->middleware('member.check')
->controller(PageController::class)
->group(function () {
Route::get("/{pageFilter?}/{pageNum?}", 'start');
Route::get("/{mode?}/{uuid?}", 'start');
Route::put("/write", 'write');
Route::post("/create", 'create');
Route::delete("/delete", 'delete');
});
//Settings
Route::prefix('dashboard/settings')
->middleware('member.check')
->controller(SettingsController::class)
->group(function () {
Route::get("/", 'start');
Route::put("/{task}", 'tasks');
Route::put("/backup/create", 'createBackup');
Route::get("/backup/get/{type}", 'downloadBackup');
Route::post("/reset", 'reset');
});
//Themekit
Route::prefix('dashboard/themekit')
->middleware('member.check')
->controller(ThemeController::class)
->group(function () {
Route::get("/", 'start');
Route::get("/view/{view?}/{id?}", 'getView');
});
//mailer
Route::prefix('dashboard/mailer')
->middleware('member.check')
->controller(SystemMailController::class)
->group(function () {
Route::post("/", 'sendNotify');
});
//RSS
Route::prefix('rss/feed')
->controller(RSSController::class)
->group(function () {
Route::get("/", 'getFeed');
});