PHP Classes

File: sqlite-snippets-admin.php

Recommend this page to a friend!
  Classes of JImmy Bo   PHP SQLite Code Vault   sqlite-snippets-admin.php   Download  
File: sqlite-snippets-admin.php
Role: Example script
Content type: text/plain
Description: An example admin page for SQLiteCodeVault class to manage code snippets in a SQLite database.
Class: PHP SQLite Code Vault
Manage sets of code snippets stored in SQLite
Author: By
Last change: created a cookie management javascript class for the ace.js code editor
created a jquery plugin to:
- handle the cookie management for combo boxes for ace code editor section
- attach the ace.js code editor to the code textarea
- this was not fun and all sorts of gotchas :( 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: ace editor 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.
Date: 9 months ago
Size: 54,522 bytes
 

Contents

Class file image Download
<?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, '&lt;').replace(/>/g, '&gt;'); 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>