<?php
/*
* SQLite Code Vault :: SQLite Snippets Manager :: Example Admin Page
*
* @package SQLite Snippets Manager :: SQLiteCodeVault
* @version 1.1
* @author https://www.phpclasses.org/browse/author/144301.html
* @link https://www.phpclasses.org/package/12863-PHP-SQLite-Code-Vault-is-a-PHP-SQLite-snippets-manager.html
* @license BSD License
* @filename sqlite-snippets-admin.php
*
* please leave this comment block intact so others can find the original source.
*
* description: An example admin page for SQLiteCodeVault class to manage code snippets in a SQLite database.
// fixed up listing rows a bit for the add section update
// added some classes to db for json
// one day I will have to get in to clean this beast, but today isn't that day. Neither is tomorrow.
// v1.1 has a json export/import feature
// currently broken: using not in the search field (and/or work)
// added copy button to copy the code to the clipboard
// added syntax highlighting and theme dropdown for syntax highlighting
// added copy json button to copy the json to the clipboard
// added cookie storing ability to remember the last used theme
// created a cookie management javascript class for the ace.js code editor
// created a jquery plugin to:
// - handle the cookie management for combo boxes
// - attach the ace.js code editor to the code textarea
// - this was not fun :( but hey it works.
// -- adds all sorts of cool ways to edit your code with syntax highlighting, error checking, autoindent, etc.
// -- so using highlight.js for the listings and ace.js for the code editor. Note: only currently attached to update code section, but easy enough to add to add section which I will do if everything runs smooth in the update section, which I highly doubt, but hey, fingers crossed.
// fixed some width issues with the page.
*/
ob_start();
session_start();
// Include SQLiteCodeVault class
require_once 'sqlite-snippets-manager.php';
// Initialize the class
$snippets = new PHPSQLiteCodeVault();
function get_snippet_data_html($snippet_row)
{
global $snippets;
$html = "";
// create a page anchor
$html .= "<a name='" . $snippet_row['id'] . "'></a>";
$html .= "<div class='title'>" . $snippet_row['title'] . "</div>";
$html .= "<div class='subcontent' style='display:none'>";
$html .= "<div class='code-snippet' data-id='" . $snippet_row['id'] . "'>" . $snippet_row['code'] . "</div>";
$html .= "<div class='json-row'>";
$html .= "<div class=' json-title'>JSON Export:</div>";
$html .= "<div class=' json'><textarea class='json-textarea' data-id='" . $snippet_row['id'] . "'>" . $snippets->snippet_to_json_str($snippet_row) . "</textarea></div>";
$html .= "</div><!-- end json-row -->";
$html .= "<div class='subinfo'>";
$html .= "<div class='language'>language: " . $snippet_row['language'] . "</div>";
$html .= "<div class='tags'>tags: " . $snippet_row['tags'] . "</div>";
$html .= "<div class='author'>author: " . $snippet_row['author'] . "</div>";
$html .= "<div class='license'>license: " . $snippet_row['license'] . "</div>";
$html .= "<div class='timestamp'>" . date('Y-m-d H:i:s', $snippet_row['timestamp']) . "</div>";
$html .= "</div><!-- end subinfo -->";
$html .= "<div class='actions'>";
$html .= '<div><button class="copyBtn" data-id="' . $snippet_row['id'] . '">Copy</button></div>';
$html .= '<div><button class="copyJsonBtn" data-id="' . $snippet_row['id'] . '">Copy JSON</button></div>';
$html .= "<div><button class='editBtn' data-id='" . $snippet_row['id'] . "' data-title='" . $snippet_row['title'] . "' data-code='" . htmlentities($snippet_row['code']) . "' data-language='" . $snippet_row['language'] . "' data-tags='" . $snippet_row['tags'] . "' data-author='" . $snippet_row['author'] . "' data-license='" . $snippet_row['license'] . "'>Edit</button></div>";
$html .= '<div><button class="deleteBtn" data-id="' . $snippet_row['id'] . '">Delete</button></div>';
$html .= "</div>";
$html .= "</div><!-- end subcontent -->";
return $html;
}
function get_snippet_html($snippet_row, $evenodd="odd")
{
// make safe for html
$snippet_row['title'] = htmlspecialchars($snippet_row['title']);
$snippet_row['code'] = htmlspecialchars($snippet_row['code']);
$snippet_row['language'] = htmlspecialchars($snippet_row['language']);
$snippet_row['tags'] = htmlspecialchars($snippet_row['tags']);
$snippet_row['author'] = htmlspecialchars($snippet_row['author']);
$snippet_row['license'] = htmlspecialchars($snippet_row['license']);
// Return HTML for a snippet
$html = "";
$html .= "<div class='snippet $evenodd' data-id='" . $snippet_row['id'] . "'>";
$html .= "<div class='snippet-data' data-id='" . $snippet_row['id'] . "'>";
$html .= get_snippet_data_html($snippet_row);
$html .= "</div>";
$html .= "</div>";
return $html;
}
function get_snippets_html()
{
global $snippets;
// Return HTML for all snippets to go in .snippets div
$html = "";
/*
$evenodd = 'odd';
foreach ($snippetsList as $snippet) {
echo get_snippet_html($snippet, $evenodd);
if ($evenodd == 'odd')
$evenodd = 'even';
else
$evenodd = 'odd';
}
*/
// if $_SESSION['search'] is set, then search for snippets
if (isset($_SESSION['search']) && !empty(trim($_SESSION['search']))) {
$snippetsList = $snippets->fuzzySearch($_SESSION['search']);
} else {
// If no search is submitted, get all snippets from database
$snippetsList = $snippets->getAllSnippets();
}
$evenodd = "odd";
foreach ($snippetsList as $snippet) {
$html .= get_snippet_html($snippet, $evenodd);
if ($evenodd == "odd") {
$evenodd = "even";
} else {
$evenodd = "odd";
}
}
return $html;
}
// If the form is submitted to add a new snippet
if (isset($_POST['addSnippet'])) {
// Get form data and add snippet to database
$snippets->addSnippet($_POST['title'], $_POST['code'], $_POST['language'], $_POST['tags'], $_POST['author'], $_POST['license'], time());
}
// If the form is submitted to update a snippet
if (isset($_POST['updateSnippet'])) {
// Get form data and update snippet in database
$snippets->saveSnippet($_POST['id'], $_POST['title'], $_POST['code'], $_POST['language'], $_POST['tags'], $_POST['author'], $_POST['license'], time());
}
// If the form is submitted to search for snippets
if (isset($_POST['searchSnippet'])&& !empty(trim($_POST['search']))) {
// Get form data and search for snippets in database
# $snippetsList = $snippets->getSnippetsBySearch($_POST['search']);
// $snippetsList = $snippets->fuzzySearch($_POST['search']);
$_SESSION['search'] = $_POST['search'];
} else
if (isset($_POST['searchSnippet']) && empty(trim($_POST['search']))) {
$_SESSION['search'] = '%';
}
// else {
// // If no search is submitted, get all snippets from database
// // $snippetsList = $snippets->getAllSnippets();
// $_SESSION['search'] = '';
// }
if (isset($_GET['cmd']) && $_GET['cmd'] == 'ajax') {
// If the request is an AJAX request, return the snippets list and quit
# echo $snippetsList;
// $evenodd = "odd";
// foreach ($snippetsList as $snippet) {
// echo get_snippet_html($snippet, $evenodd);
// if ($evenodd == "odd") {
// $evenodd = "even";
// } else {
// $evenodd = "odd";
// }
// }
echo get_snippets_html();
exit;
} // end if ajax
?><?php
/*
// Cancel page reload on form submit for update snippet form
$('#updateSnippetForm form').submit(function(e) {
// Get form data
var id = $('#updateSnippetForm').find('input[name="id"]').val();
var title = $('#updateSnippetForm').find('input[name="title"]').val();
var code = $('#updateSnippetForm').find('textarea[name="code"]').val();
var language = $('#updateSnippetForm').find('input[name="language"]').val();
var tags = $('#updateSnippetForm').find('input[name="tags"]').val();
var author = $('#updateSnippetForm').find('input[name="author"]').val();
var license = $('#updateSnippetForm').find('input[name="license"]').val();
// Update snippet in database
$.ajax({
url: 'sqlite-snippets-admin.php?cmd=ajax',
type: 'post',
data: {
updateSnippet: true,
id: id,
title: title,
code: code,
language: language,
tags: tags,
author: author,
license: license
},
success: function(response) {
// If snippet is updated successfully, reload the snippet list
$('#snippetsList').html(response);
alert('Snippet updated successfully!');
}
});
e.preventDefault();
}); // end -- update snippet form submit
*/
// --- BEGIN --- PHP SIDE OF COMMAND PROCESSOR
if(!empty($_GET['ajax']))
{
switch($_GET['ajax'])
{
case 'add-from-json':
$json = $_POST['flds']['json'];
$snippets->snippet_from_json_str_to_db($json);
$return_arr[] = array(
"command" => 'alert',
"process" => "add-from-json",
"msg" => "added from json"
);
// update snippet list
$return_arr[] = array(
"command" => 'html',
"selector" => ".snippets",
"msg" => get_snippets_html()
);
$return_arr[] = array(
"command" => 'html',
"selector" => "#snippetCount",
"msg" => $snippets->getSnippetCount()." Snippets in DB"
);
break;
case 'update':
$id = $_POST['flds']['id'];
$title = $_POST['flds']['title'];
$code = $_POST['flds']['code'];
$language = $_POST['flds']['language'];
$tags = $_POST['flds']['tags'];
$author = $_POST['flds']['author'];
$license = $_POST['flds']['license'];
if(empty($id))
{
$return_arr[] = array(
"command" => 'alert',
"process" => "update",
"msg" => "id is empty for update"
);
}
else
{
// update ( saveSnippet($id, $title, $code, $language, $tags, $author, $license, $timestamp) )
$snippets->saveSnippet($id, $title, $code, $language, $tags, $author, $license, time());
$return_arr[] = array(
"command" => 'alert',
"process" => "update",
"msg" => "updated id: $id"
);
$snippet = $snippets->getSnippetById($id);
## print_r($snippet['code']);
// for sending back to a <pre></pre> fix the stripping of <html> tags
$snippet['code'] = htmlspecialchars($snippet['code']);
$return_arr[] = array(
"command" => 'html',
"selector" => ".snippet-data[data-id='$id']",
"msg" => get_snippet_data_html($snippet)
// "msg" => get_snippet_data_html($snippets->getSnippetById($id))
);
// $return_array[] = array(
// "command" => 'html',
// "selector" => '.snippet12',
// // "msg" => get_snippet_html($snippets->getSnippetById($id))
// "msg" => '....'
// );
// print_r($return_array);
}
break; // end -- update snippet
case 'delete':
$id = $_POST['flds']['id'];
if(empty($id))
{
$return_arr[] = array(
"command" => 'alert',
"process" => "delete",
"msg" => "id is empty for delete"
);
}
else
{
// delete ( deleteSnippet($id) )
$snippets->deleteSnippet($id);
$return_arr[] = array(
"command" => 'alert',
"process" => "delete",
"msg" => "deleted id: $id"
);
$return_arr[] = array(
"command" => 'html',
"selector" => "#snippetCount",
"msg" => $snippets->getSnippetCount()." Snippets in DB"
);
}
break; // end -- delete snippet
}
if(!empty($return_arr) && is_array($return_arr))
die(json_encode($return_arr));
die(); // ajax request, so just quit instead of showing a page
}
// --- END --- PHP SIDE OF COMMAND PROCESSOR
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snippet Manager</title>
<link rel="stylesheet" href="style.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
// Show add snippet form when add button is clicked
$('#addBtn').click(function() {
$('#addSnippetForm').toggle();
});
// Hide add snippet form when cancel button is clicked
$('#cancelBtn').click(function() {
$('#addSnippetForm').hide();
});
// Cancel page reload on form submit for add snippet form
$('#addSnippetForm form').submit(function(e) {
// Get form data
var title = $('#addSnippetForm').find('input[name="title"]').val();
var code = $('#addSnippetForm').find('textarea[name="code"]').val();
var language = $('#addSnippetForm').find('input[name="language"]').val();
var tags = $('#addSnippetForm').find('input[name="tags"]').val();
var author = $('#addSnippetForm').find('input[name="author"]').val();
var license = $('#addSnippetForm').find('input[name="license"]').val();
// Add snippet to database
$.ajax({
url: 'sqlite-snippets-admin.php?cmd=ajax',
type: 'post',
data: {
addSnippet: true,
title: title,
code: code,
language: language,
tags: tags,
author: author,
license: license
},
success: function(response) {
// If snippet is added successfully, reload the snippet list
$('#snippetsList').html(response);
alert('Snippet added successfully!');
}
});
e.preventDefault();
});
// Cancel page reload on form submit for update snippet form
// OBSOLETE.. Now using command processor
$('#updateSnippetForm form').submit(function(e) {
// Get form data
var id = $('#updateSnippetForm').find('input[name="id"]').val();
var title = $('#updateSnippetForm').find('input[name="title"]').val();
var code = $('#updateSnippetForm').find('textarea[name="code"]').val();
var language = $('#updateSnippetForm').find('input[name="language"]').val();
var tags = $('#updateSnippetForm').find('input[name="tags"]').val();
var author = $('#updateSnippetForm').find('input[name="author"]').val();
var license = $('#updateSnippetForm').find('input[name="license"]').val();
// Update snippet in database
$.ajax({
url: 'sqlite-snippets-admin.php?cmd=ajax',
type: 'post',
data: {
updateSnippet: true,
id: id,
title: title,
code: code,
language: language,
tags: tags,
author: author,
license: license
},
success: function(response) {
// If snippet is updated successfully, reload the snippet list
$('#snippetsList').html(response);
alert('Snippet updated successfully!');
}
});
e.preventDefault();
}); // end -- update snippet form submit
// Show edit snippet form when edit button is clicked (handle dynamically created edit buttons too)
// $('.editBtn').click(function() { // old way
$(document).on('click', '.editBtn', function() {
var id = $(this).data('id');
var title = $(this).data('title');
var code = $(this).data('code');
var language = $(this).data('language');
var tags = $(this).data('tags');
var author = $(this).data('author');
var license = $(this).data('license');
// code is escaped so unescape it
const parser = new DOMParser();
const doc = parser.parseFromString(code, 'text/html');
code = doc.documentElement.textContent;
$('#updateSnippetForm').find('input[name="id"]').val(id);
$('#updateSnippetForm').find('input[name="title"]').val(title);
$('#updateSnippetForm').find('textarea[name="code"]').val(code);
$('#updateSnippetForm').find('input[name="language"]').val(language);
$('#updateSnippetForm').find('input[name="tags"]').val(tags);
$('#updateSnippetForm').find('input[name="author"]').val(author);
$('#updateSnippetForm').find('input[name="license"]').val(license);
$('#updateSnippetForm').show();
// scroll to #updateSnippetForm
$('html, body').animate({
scrollTop: $("#updateSnippetForm").offset().top
}, 1000);
});
// Hide edit snippet form when cancel button is clicked
$('#cancelBtn').click(function() {
$('#updateSnippetForm').hide();
});
});
</script>
</head>
<body>
<div class='row'>
<div class='col'><h1 id='pageTtl'>Snippet Manager</h1></div>
<div class='col'><button id="addBtn">Show/Hide Add Snippet</button></div>
</div>
<style>
body {
font-family:arial;
}
.row {
display:flex;
flex-direction:row;
justify-content:space-between;
align-items:center;
margin:2vw;
}
.col {
flex:1;
}
#pageTtl {
margin-top:12px;
font-size:2min;
}
#addBtn {
float:right;
margin-top:0;
}
#snippetCount {
position:fixed;
/* top:0; */
bottom:0px;
/* right:10px; */
left:50%;
transform:translateX(-50%);
background-color: #fff;
padding: 10px 20px;
margin-top:0px;
z-index:9999;
border:1px solid #ccc;
cursor:pointer;
}
</style>
<div id="snippetCount"><?php echo $snippets->getSnippetCount(); ?> Snippets in DB</div>
<div id="addSnippetForm" style="display:none;">
<style>
#addSnippetFromJson {
margin: 20px 0;
}
#addSnippetFromJson .fld {
margin: 5px 0;
}
#addSnippetFromJson .fld input {
width: 100%;
padding-top:1em;
padding-bottom:1em;
}
</style>
<div id="addSnippetFromJson">
<div class='title'><h2>Add Snippet From JSON</h2></div>
<div class='fld'><input type="text" name="json" placeholder="JSON" required></div>
<div class='fld'><input type="button" class="addJsonBtn" name="addSnippetFromJson" value="Add Snippet From JSON"></div>
</div>
<h2>Add Snippet</h2>
<form>
<input type="text" name="title" placeholder="Title" required>
<textarea class='code-textarea' name="code" placeholder="Code" required></textarea>
<input type="text" name="language" placeholder="Language" required>
<input type="text" name="tags" placeholder="Tags" required>
<input type="text" name="author" placeholder="Author" required>
<input type="text" name="license" placeholder="License" required>
<input type="submit" name="addSnippet" value="Add Snippet">
</form>
</div>
<br />
<div id="updateSnippetForm" style="display:none;">
<h2>Edit Snippet</h2>
<!-- just have action cancel page reload -->
<form action="return false;">
<input type="hidden" name="id">
<input type="text" name="title" placeholder="Title" required>
<textarea class='code-textarea use_ace_edit' name="code" placeholder="Code" required></textarea>
<input type="text" name="language" placeholder="Language" required>
<input type="text" name="tags" placeholder="Tags" required>
<input type="text" name="author" placeholder="Author" required>
<input type="text" name="license" placeholder="License" required>
<!-- <input type="submit" name="updateSnippet" value="Update Snippet"> -->
<input type="button" id="updateBtn" value="Update">
<input type="button" id="cancelBtn" value="Cancel">
</form>
</div>
<form method="post">
<input type="text" name="search" placeholder="Search Snippets">
<input type="submit" name="searchSnippet" value="Search">
</form>
<h2>Snippets List:<?php if(!empty($_SESSION['search'])) echo ' (Search: ' . $_SESSION['search']. ')'; ?></h2>
<div id="snippetsList">
<div class='snippets'>
<?php
echo get_snippets_html();
// $evenodd = 'odd';
// foreach ($snippetsList as $snippet) {
// echo get_snippet_html($snippet, $evenodd);
// if ($evenodd == 'odd')
// $evenodd = 'even';
// else
// $evenodd = 'odd';
// }
?>
</div>
</div>
<script>
// BEGIN --> JAVASCRIPT COMMAND PROCESSOR //
function do_cmd_post(url, send_data)
{
$.post( url, {
flds: send_data /* in php will appear as $_POST['flds'] */ },
function( return_data ) {
do_cmd_process(return_data);
}, "json" ); // punt any returned data to processor
}
// ---
function do_cmd_process(data) // handle data coming back from ajax
{
if (data instanceof Array) {
data.forEach(function(entry) {
console.log(entry.command);
//console.log(entry.message);
// handle returned commands //
switch(entry.command)
{
// generic commands //
case 'alert':
alert(entry.msg);
break;
case 'log':
console.log(entry.msg);
break;
case 'append':
$(entry.selector).append(entry.msg);
break;
case 'prepend':
$(entry.selector).prepend(entry.msg);
break;
case 'html':
$(entry.selector).html(entry.msg);
break;
case 'val':
$(entry.selector).val(entry.msg);
break;
case 'focus':
$(entry.selector).focus();
break;
case 'blur':
$(entry.selector).blur();
break;
case 'clear':
$(entry.selector).val('');
break;
case 'js':
eval(entry.msg);
break;
case 'resize_textarea_to_fit_contents':
$(entry.selector).height(0);
$(entry.selector).height($(entry.selector)[0].scrollHeight);
break;
case 'disable_input':
$(entry.selector).prop('disabled', true);
break;
case 'enable_input':
$(entry.selector).prop('disabled', false);
break;
case 'resize_textareas':
$(".message_content textarea").each(function(){
$(this).css("height", ($(this).prop("scrollHeight")) + "px");
});
break;
} // end switch
});
}
}
// END --> JAVASCRIPT COMMAND PROCESSOR //
$(document).ready(function() {
// handle copy json button
$(document).on('click', '.copyJsonBtn', function() {
// get id from attr on button called data-id
var id = $(this).attr('data-id');
// get text from a dynamic element .code-snippet with a data-id of id
var text_fld = $('.json-textarea[data-id="' + id + '"]').val();
// now copy text_fld to clipboard
// create a temporary input element
var $temp = $("<input>");
// add it to the document
$("body").append($temp);
// set the value of the input to the text_fld
$temp.val(text_fld).select();
// copy the text to the clipboard
document.execCommand("copy");
// remove the temporary input
$temp.remove();
alert('copied json to clipboard');
}); // end -- handle copy json button
// handle copy button
$(document).on('click', '.copyBtn', function() {
// get id from attr on button called data-id
var id = $(this).attr('data-id');
// get text from a dynamic element .code-snippet with a data-id of id
var text_fld = $('.code-snippet[data-id="' + id + '"]').text();
// now copy text_fld to clipboard
// create a temporary input element
var $temp = $("<input>");
// add it to the document
$("body").append($temp);
// set the value of the input to the text_fld
$temp.val(text_fld).select();
// copy the text to the clipboard
document.execCommand("copy");
// remove the temporary input
$temp.remove();
alert('copied source code to clipboard');
}); // end -- handle copy button
// on click a dynamically created .addJsonBtn
// add snippet from json
$(document).on('click', '.addJsonBtn', function() {
// get json from input
var json = $('input[name="json"]').val();
var send_data = {
//"file": $('select[name="conversation_combobox"]').val()
"json": json
};
do_cmd_post('sqlite-snippets-admin.php?ajax=add-from-json', send_data);
}); // end -- add snippet from json
// on click a dynamically created .deleteBtn
// delete button will alert ('hi')
$(document).on('click', '.deleteBtn', function() {
// confirm delete
if(!confirm('Are you sure you want to delete this snippet?'))
return;
// get id from attr on button called data-id
var id = $(this).attr('data-id');
var send_data = {
//"file": $('select[name="conversation_combobox"]').val()
"id": id
};
// hide .snippet with data-id = id
$('.snippet[data-id="' + id + '"]').hide();
do_cmd_post('sqlite-snippets-admin.php?ajax=delete', send_data);
}); // end -- delete snippet
// updateBtn
$(document).on('click', '#updateBtn', function() {
// get id from attr on button called data-id
var id = $('#updateSnippetForm').find('input[name="id"]').val();
var send_data = {
//"file": $('select[name="conversation_combobox"]').val()
"id": id,
"title": $('#updateSnippetForm').find('input[name="title"]').val(),
"code": $('#updateSnippetForm').find('textarea[name="code"]').val(),
"language": $('#updateSnippetForm').find('input[name="language"]').val(),
"tags": $('#updateSnippetForm').find('input[name="tags"]').val(),
"author": $('#updateSnippetForm').find('input[name="author"]').val(),
"license": $('#updateSnippetForm').find('input[name="license"]').val()
};
do_cmd_post('sqlite-snippets-admin.php?ajax=update', send_data);
}); // end -- update snippet
});
</script>
<style>
.snippet {
position:relative;
display:block;
border: 1px solid #ccc;
}
.snippet-data {
position:relative;
display:block;
}
.snippet .actions {
position:relative;
display:block;
text-align:right;
}
.snippet .actions div,
.snippet-data div {
display:inline-block;
padding:1vw;
}
.snippet .actions div {
cursor:pointer;
}
.snippet.even {
background-color:#bbb;
}
.snippet.odd {
background-color:#ddd;
}
/* ****************************************** */
/* Container */
#addSnippetForm, #updateSnippetForm {
background-color: #f2f2f2;
border-radius: 5px;
padding: 20px;
width: 80%;
margin: 0 auto;
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
}
/* Form elements */
input[type=text],
input[type=submit],
textarea {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
resize: vertical;
font-size: 16px;
font-family: "Arial", sans-serif;
margin-bottom: 12px;
}
textarea {
min-height: 100px;
}
/* Placeholder styling */
input[type=text]::placeholder,
textarea::placeholder {
color: #999;
font-style: italic;
}
/* Submit button */
input[type=submit] {
background-color: #04AA6D;
color: white;
cursor: pointer;
transition: background-color 0.2s;
}
input[type=submit]:hover {
background-color: #048458;
}
/* button */
input[type=button] {
background-color: #007bff;
border: none;
border-radius: 4px;
color: white;
cursor: pointer;
font-family: 'Roboto', sans-serif;
font-size: 0.9rem;
padding: 0.5rem 1rem;
text-transform: uppercase;
}
input[type=button]:hover {
background-color: #0056b3;
}
/* Form headings */
h2 {
font-size: 24px;
font-weight: bold;
margin-bottom: 20px;
}
/* --- */
.snippets {
display: grid;
/* grid-template-columns: repeat(auto-fill, minmax(1400px, 1fr)); */
grid-gap: 1rem;
margin: 1rem;
}
.snippet {
background-color: #f9f9f9;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 1rem;
font-family: 'Roboto', sans-serif;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.snippet-data {
display: flex;
flex-direction: column;
}
.title,
.language,
.tags,
.author,
.license,
.timestamp {
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.title {
font-weight: bold;
font-size: 1.1rem;
margin-bottom: 0.8rem;
/* border-bottom: 1px solid #e0e0e0; */
/* make nicer */
padding-bottom: 0.5rem;
cursor:pointer;
}
.code-snippet {
background-color: #f0f0f0;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 0.5rem;
font-family: 'Courier', monospace;
white-space: pre-wrap;
overflow-x: auto;
}
.actions {
display: flex;
justify-content: space-between;
margin-top: 1rem;
}
button {
background-color: #007bff;
border: none;
border-radius: 4px;
color: white;
cursor: pointer;
font-family: 'Roboto', sans-serif;
font-size: 0.9rem;
padding: 0.5rem 1rem;
text-transform: uppercase;
}
button:hover {
background-color: #0056b3;
}
.code-snippet .subinfo {
display: flex;
justify-content: space-between;
margin-top: 1rem;
}
.code-snippet .subinfo .left {
display: flex;
flex-direction: column;
}
.code-snippet .subinfo .right {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.code-snippet .subinfo .left .language,
.code-snippet .subinfo .left .tags,
.code-snippet .subinfo .left .author,
.code-snippet .subinfo .left .license {
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.code-snippet .subinfo .right .timestamp {
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.json-row {
position:relative;
display: block;
justify-content: space-between;
margin-top: 1rem;
width:100%;
}
.json-row div
{
position:relative;
display:block;
width:97%;
margin:0;
padding:0;
}
.json-row textarea {
width:100%;
}
/* --- */
</style>
<script>
/* when snippet-data .title is clicked, toggle the code snippet (nearest .subcontent section) */
$(document).ready(function(){
// $(".snippet-data .title").click(function(){
// $(this).next(".subcontent").slideToggle("slow");
// });
/* handle dynamic */
$(document).on('click', '.snippet-data .title', function(){
$(this).next(".subcontent").slideToggle("fast");
});
});
</script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/default.min.css"> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/monokai.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github.min.css">
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/atom-one-dark.min.css"> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<style>
.highlight-theme {
position:fixed;
bottom:0px;
left:0px;
margin:2px;
}
.highlight-theme-label {
position:fixed;
bottom:22px;
left:0px;
margin:2px;
font-size:0.8rem;
font-weight:bold;
}
</style>
<!-- label for select -->
<label class='highlight-theme-label' for="highlight-theme">Highlight.js theme:</label>
<select name='highlight-theme' class='highlight-theme' onchange="switch_highlight_theme(this.value)">
<option value='github' SELECTED>github</option>
<!-- <option value='default'>default</option> -->
<!-- <option value='monokai'>monokai</option> -->
<!-- <option value='atom-one-dark'>atom-one-dark</option> -->
<!-- <option value='nord'>nord</option> -->
<!-- a11y-dark,a11y-light,agate,an-old-hope,androidstudio,arduino-light,arta,ascetic,atom-one-dark-reasonable,atom-one-light,brown-paper,dark,github-dark-dimmed,github-dark -->
<!-- <option value='github-dark'>github-dark</option> -->
<!-- <option value='a11y-dark'>a11y-dark</option> -->
<option value='a11y-light'>a11y-light</option>
<!-- <option value='agate'>agate</option> -->
<option value='arduino-light'>arduino-light</option>
<!-- <option value='arta'>arta</option> -->
<!-- <option value='atom-one-dark-reasonable'>atom-one-dark-reasonable</option> -->
<option value='googlecode'>googlecode</option>
<option value='intellij-light'>intellij-light</option>
<!-- <option value='kimbie-light'>kimbie-light</option> -->
</select>
<script>
function getCookie(cname)
{
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
// console.log(ca);
for(var i = 0; i <ca.length; i++)
{
var c = ca[i];
// console.log(c);
while (c.charAt(0) == ' ')
{
c = c.substring(1);
}
if (c.indexOf(name) == 0)
{
return c.substring(name.length, c.length);
}
}
return "";
}
// if there is a cookie set, switch to that theme
// on document ready
$(document).ready(function(){
var highlight_theme = getCookie('highlight-theme');
if (highlight_theme != "") {
switch_highlight_theme(highlight_theme);
}
});
function switch_highlight_theme(themename)
{
/* save a cookie with current highlight */
document.cookie = "highlight-theme="+themename+"; path=/; SameSite=None; Secure";
// remove all highlight.js stylesheets
$('link[href*="highlight.js"]').remove();
// add the new one
$('head').append('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/'+themename+'.min.css">');
// update the highlights
update_highlights();
}
</script>
<style>
/* for highlights */
.code-snippet {
background-color:#fff;
/* color:#fff; */
padding:1rem;
border:1px solid #333;
}
</style>
<script>
function update_highlights()
{
$(document).ready(function() {
// update_highlights();
document.querySelectorAll('#code-textarea,.code-snippet').forEach(el => {
// if we do not already have a highlit element then..
if (!el.classList.contains('hljs'))
{
// escape the text //
el.innerHTML = el.innerHTML.replace(/</g, '<').replace(/>/g, '>');
hljs.highlightElement(el);
}
});
});
};
update_highlights();
// when a .title is clicked, update_highlights
$(document).ready(function(){
$(document).on('click', '.snippet-data .title', function(){
update_highlights();
});
});
</script>
<!-- BEGIN BEGIN BEGIN BEGIN -- ace.js code editor (my js cookie class and jq plugin) -->
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ace.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ext-language_tools.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ext-beautify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ext-settings_menu.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ext-spellcheck.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ext-whitespace.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ext-split.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ext-searchbox.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ext-statusbar.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.19.0/ext-textarea.js"></script>
<script>
// simple cookie class in javascript
class Cookie {
constructor() {
this.cookie = document.cookie;
}
get(name) {
const value = this.cookie.match(`(^|;)\\s*${name}\\s*=\\s*([^;]+)`);
return value ? value.pop() : '';
}
set(name, value, days) {
let expires = '';
if (days) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = `; expires=${date.toUTCString()}`;
}
// set for SameSite=None;
document.cookie = `${name}=${value || ''}${expires}; path=/; SameSite=None; Secure`;
// document.cookie = `${name}=${value || ''}${expires}; path=/`;
}
delete(name) {
this.set(name, '', -1);
}
} // end my cookie class
// example usage:
// const cookie = new Cookie();
// cookie.set('name', 'value', 7);
// cookie.get('name');
// cookie.delete('name');
</script>
<!-- the plugin -->
<script>
(function($) {
$.fn.customAceEditor = function(removeyn='no', themeselect_id='#theme-selector', modeselect_id='#mode-selector') {
// removeyn = 'destroy' to remove the editor
// make an editors array
var editors = [];
if (removeyn == 'destroy') {
return this.each(function() {
const textarea = $(this);
const editorDiv = textarea.prev();
editorDiv.remove();
textarea.show();
textarea.removeClass('hascodearea');
});
}
if (this.hasClass('hascodearea')) {
this.customAceEditor('destroy');
}
this.each(function() {
const textarea = $(this);
const editorDiv = $('<div class="hascodearea">').insertBefore(textarea).width(textarea.width()).height(textarea.height());
const textarea_content = textarea.val();
textarea.addClass('hascodearea');
textarea.hide();
/* to autoresize the Ace editor first you need to set the height of the editor to auto and then call the resize() method on the editor instance. */
/* example calling the resize method on the editor instance:
var editor = ace.edit("editor");
editor.resize();
*/
const editor = ace.edit(editorDiv[0], {
autoScrollEditorIntoView: false,
width: '100%',
height: 'auto',
fontSize: '16px',
tabSize: 4,
useSoftTabs: true,
showPrintMargin: false,
showGutter: true,
highlightActiveLine: true,
wrap: true,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
maxLines: Infinity,
minLines: 5,
maxLines: 900000,
scrollPastEnd: 1, /* fixed line 1 disappearing */
});
// add to editors array
editors.push(editor);
/* when window resizes, resize this editor */
editor.setAutoScrollEditorIntoView(true);
editor.setValue(textarea_content, 1); // 1 = moves cursor to end
// localstorage
// use themeselect_id and modeselect_id to prefix the localstorage keys
// revamped to use cookies, however have some variables still sharing localstorage name
localstorage_prefix_theme = themeselect_id;
localstorage_prefix_mode = modeselect_id;
// clean the . and # out of the id's
localstorage_prefix_theme = localstorage_prefix_theme.replace(/\.|#/, '');
localstorage_prefix_mode = localstorage_prefix_mode.replace(/\.|#/, '');
cookie_name_theme = localstorage_prefix_theme+'_theme';
cookie_name_mode = localstorage_prefix_mode+'_mode';
const cookie = new Cookie();
console.log('cookie theme:',cookie_name_theme,"::",cookie.get(cookie_name_theme));
console.log('cookie mode:',cookie_name_mode,"::",cookie.get(cookie_name_mode));
const defaultTheme = cookie.get(cookie_name_theme) || 'monokai';
const defaultMode = cookie.get(cookie_name_mode) || 'text';
// ^^ localstorage
editor.setTheme(`ace/theme/${defaultTheme}`);
editor.session.setMode(`ace/mode/${defaultMode}`);
// editor.session.on('change input', function() {
// textarea.val(editor.getValue());
// });
const themes = [
"monokai",
"ambiance", "chaos", "chrome", "clouds", "clouds_midnight", "cobalt",
"crimson_editor", "dawn", "dracula", "dreamweaver", "eclipse", "github",
"gob", "gruvbox", "idle_fingers", "iplastic", "katzenmilch", "kr_theme",
"kuroir", "merbivore", "merbivore_soft", "mono_industrial", "monokai",
"pastel_on_dark", "solarized_dark", "solarized_light", "sqlserver",
"terminal", "textmate", "tomorrow", "tomorrow_night", "tomorrow_night_blue",
"tomorrow_night_bright", "tomorrow_night_eighties", "twilight", "vibrant_ink",
"xcode"
// ... (other themes)
];
const themeSelector = $(themeselect_id);
// add attr data-cookie=cookie_name_theme
themeSelector.attr('data-cookietheme', cookie_name_theme);
themeSelector.attr('test', 'testthemmmme');
themes.forEach(theme => {
themeSelector.append($('<option>').val(theme).text(theme));
});
themeSelector.val(defaultTheme);
themeSelector.on('change', function() {
editor.setTheme(`ace/theme/${this.value}`);
// localstorage
// localStorage.setItem(localstorage_prefix_theme+'defaultTheme', this.value);
// get cookie name from attr data-cookie
cookie_name = $(this).attr('data-cookietheme')
const cookie = new Cookie();
cookie.delete(cookie_name);
cookie.set(cookie_name, this.value, 7);
// alert(this.value);
});
const modes = [
"text",
"javascript", "php", "python", "ruby", "html", "css", "php", "java", "c_cpp",
"markdown", "json", "xml", "yaml", "typescript", "sql", "go", "lua",
"swift", "perl", "csharp", "rust", "r"
// ... (other modes)
];
const modeSelector = $(modeselect_id);
// add attr data-cookie=cookie_name_mode
modeSelector.attr('data-cookiemode', cookie_name_mode);
modes.forEach(mode => {
modeSelector.append($('<option>').val(mode).text(mode));
});
modeSelector.val(defaultMode);
modeSelector.on('change', function() {
editor.session.setMode(`ace/mode/${this.value}`);
// localstorage
// localStorage.setItem(localstorage_prefix_mode+'defaultMode', this.value);
// get cookie name from attr data-cookie
cookie_name = $(this).attr('data-cookiemode')
const cookie = new Cookie();
cookie.delete(cookie_name);
cookie.set(cookie_name, this.value, 7);
});
// editor.setValue(textarea.val(), 1);
// set editor value to textarea
}); // end each
return editors;
};
})(jQuery); // end jquery plugin
</script>
<script>
// use use use
// after the .code-textarea, append a row containing two blank select boxes with a class of .theme-selector and .mode-selector
// the code for the jquery append
$('.use_ace_edit').after('<div class="row"><div class="col-sm-6"><select class="theme-selector"></select></div><div class="col-sm-6"><select class="mode-selector"></select></div></div>');
// $('.code-editor-1').customAceEditor('no', '.theme-selector-1', '.mode-selector-1');
// document ready
// $('.code-textarea').customAceEditor('no', '.theme-selector', '.mode-selector');
// document ready first
$(document).ready(function() {
var the_editors = [];
$(document).on('click', '.editBtn', function() {
// when form done showing execute this
// show and when complete showing do $('#updateSnippetForm')
// after showing #updateSnippetForm, execute this
var code = $(this).data('code');
console.log(code);
// code is escaped so unescape it
const parser = new DOMParser();
const doc = parser.parseFromString(code, 'text/html');
code = doc.documentElement.textContent;
console.log(code);
the_editors = $('.use_ace_edit').customAceEditor('no', '.theme-selector', '.mode-selector');
// find ace editor kind of like
// editor = document.querySelector('.ace_editor')
// the_editors should contain an array of ace editors
// loop through an set their value to code
the_editors.forEach(editor => {
editor.setValue(code, 1);
});
console.log(the_editors);
});
// when a key down is clicked while an ace code editor is focused
// set the textarea value to the ace editor value
$(document).on('keydown', function() {
the_editors.forEach(editor => {
$('.use_ace_edit').val(editor.getValue());
console.log(editor.getValue());
});
});
});
</script>
<script>
// get all ace.js code editors on page, and set them to 100% width (fixed an autoresize issue)
const editors = document.querySelectorAll('.ace_editor');
editors.forEach(editor => {
editor.style.width = '100%';
});
</script>
<!-- END END END -- ace.js code editor -->
</body>
</html>
|