Skip to content

Commit

Permalink
Merge pull request #2 from geezee/plugins
Browse files Browse the repository at this point in the history
Basic plugin management tool
  • Loading branch information
geezee authored Mar 17, 2018
2 parents da50866 + c7623e2 commit 46dbe48
Show file tree
Hide file tree
Showing 18 changed files with 440 additions and 49 deletions.
112 changes: 112 additions & 0 deletions app/Console/Commands/NoteFormat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;

class NoteFormatter
{
private $state;
private $repo;

public function __construct() {
$this->state = $this->getState();
$this->repo = $this->getRepositoryMap();
}

const DIR = "./resources/note-format/";
const STATE = "./resources/note-format/state.json";
const REPOSITORY = "./resources/note-format/repository.json";
const OUT = "./resources/note-format/note-format.out.js";
const COMMON_JS = "./resources/note-format/src/common.js";


public function getRepository() {
return json_decode(File::get(NoteFormatter::REPOSITORY));
}


public function getRepositoryMap() {
return collect(NoteFormatter::getRepository())->reduce(function($map, $plugin) {
$map[$plugin->name] = $plugin;
return $map;
});
}

public function getState() {
return json_decode(File::get(NoteFormatter::STATE));
}

public function writeToOutput() {
$out = File::get(NoteFormatter::COMMON_JS)."\nconst formatters=[";
foreach ($this->state->installed as $installed) {
$out .= sprintf("%s,", File::get($this::DIR.$this->repo[$installed]->src));
}
$out .= "]";

return File::put($this::OUT, $out);
}


public function list($all) {
if ($all) {
printf("%d available formatters\n\n", count(array_keys($this->repo)));
foreach ($this->repo as $plugin) {
printf("%s%-20s - %s\n",
in_array($plugin->name, $this->state->installed) ? "* " : " ",
$plugin->name, $plugin->description);
}
} else {
$index = 1;
foreach ($this->state->installed as $name) {
printf("%3d. %-20s - %s\n", $index, $name, $this->repo[$name]->description);
$index++;
}
}
}


public function install($after, $requested) {
if (strlen($after) == 0) {
$index = 0;
} else {
$index = intval($after);
if ($index === false || $index > count($this->state->installed)) {
printf("[ERROR] Index $after does not exist\n");
$this->list(false);
return -1;
}
}

foreach ($requested as $plugin) {
if (!isset($this->repo[$plugin])) {
printf("[ERROR] Formatter $plugin does not exist\n");
return -1;
}
}

array_splice($this->state->installed, $index, 0, $requested);

$this->writeToOutput();

File::put($this::STATE, json_encode($this->state));
}


public function remove($index) {
$index = intval($index);
if ($index === false || $index <= 0 || $index > count($this->state->installed)) {
printf("[ERROR] Index does not exist\n");
return;
}

$this->state->installed = array_values(collect($this->state->installed)->forget($index-1)->toArray());

$this->writeToOutput();

File::put($this::STATE, json_encode($this->state));
}


}
9 changes: 7 additions & 2 deletions public/css/style.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 79 additions & 13 deletions public/js/app.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,70 @@
function getConfig(key, def) {
if (typeof window.noteFormatConfig === 'undefined') {
window.noteFormatConfig = {};
return def;
}
const val = window.noteFormatConfig[key];
return val === undefined ? def : val;
}

function setConfig(key, val) {
if (typeof window.noteFormatConfig === 'undefined') {
window.noteFormatConfig = {};
}
window.noteFormatConfig[key] = val;
}

function loadScript(name, src) {
console.log('NoteFormat requesting', name, src);

const isLoaded = getConfig(name+'.loaded', false);

return new Promise((resolve, reject) => {
if (isLoaded) {
console.log('NoteFormat script already loaded', name);
if (typeof resolve === 'function') {
resolve();
return;
}
}

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;

script.onload = _ => {
console.log('NoteFormat loaded', name);
window.SHOWDOWN_LOADED = true;
setConfig(name+'.loaded', true);
if (typeof resolve === 'function') {
resolve();
}
}

document.body.appendChild(script);
});
}



const formatters=[function (body) {
return loadScript('showdown', 'js/showdown.min.js')
.then(() => {
return new showdown.Converter().makeHtml(body);
});
}
,function (body) {
var sandbox = document.createElement('div');
sandbox.innerHTML = body;

Array.from(sandbox.querySelectorAll('iframe')).forEach(iframe => {
iframe.sandbox = 'allow-scripts allow-same-origin allow-forms';
});

return Promise.resolve(sandbox.innerHTML);
}
,]

function makeEmptyNote() {
return {
id: 0,
Expand Down Expand Up @@ -40,14 +107,16 @@ const SelectedNote = {
DESELECT_NOTE: state =>
state.selectedNote = makeEmptyNote(),

RENDER_SELECTED_NOTE: state =>
state.selectedNote.html = new showdown.Converter()
.makeHtml(state.selectedNote.body)
.replace(/\$asciinema\([^\)\(]+\)/g, match => {
var filename = match.substring(11).slice(0, -1);
var path = ['./attachments', store.getters.getSelection.id, filename].join('/');
return `<asciinema-player src="${path}"></asciinema-player>`;
}),
RENDER_SELECTED_NOTE: state => {
// chain all the formatters so one feeds its body to the other
formatters.reduce((promise, formatter) =>
promise.then(body => formatter(body, state.selectedNote)),
Promise.resolve(state.selectedNote.body))
// then set the html property
.then(html => {
Vue.set(state.selectedNote, 'html', html);
});
},

REMOVE_ATTACHMENT: (state, index) =>
Vue.delete(this.selectedNote.attachments, index),
Expand Down Expand Up @@ -131,7 +200,6 @@ const SelectedNote = {
state.editing = false;
state.versioning = false;
commit('RENDER_SELECTED_NOTE');
dispatch('RENDER_MATHJAX');
}
}

Expand Down Expand Up @@ -423,6 +491,8 @@ const Attachments = {



//




Expand Down Expand Up @@ -479,10 +549,6 @@ SELECT_FIRST_NOTE: ({ state, commit, dispatch }) =>
commit('DESELECT_NOTE'),


RENDER_MATHJAX: ctx =>
Vue.nextTick(_ => MathJax.Hub.Queue(["Typeset", MathJax.Hub])),


CREATE_NOTE: ({ state, commit, dispatch }) =>
new Promise((resolve, reject) => {
Vue.http.get(`./api/note/create`).then(request => {
Expand Down
7 changes: 6 additions & 1 deletion public/sass/style.sass
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ a
h1
width: 100%
margin-bottom: 30px
font-size: 1em
font-size: 2em
span
width: 70%
display: block
Expand All @@ -281,6 +281,11 @@ a
color: #333333
.preview-area
padding: 10px 40px
width: 100%
pre
word-wrap: break-word
white-space: pre-wrap
iframe.javascript
width: 100%
border: 1px solid #DDD
resize: vertical
66 changes: 66 additions & 0 deletions resources/note-format/note-format.out.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
function getConfig(key, def) {
if (typeof window.noteFormatConfig === 'undefined') {
window.noteFormatConfig = {};
return def;
}
const val = window.noteFormatConfig[key];
return val === undefined ? def : val;
}

function setConfig(key, val) {
if (typeof window.noteFormatConfig === 'undefined') {
window.noteFormatConfig = {};
}
window.noteFormatConfig[key] = val;
}

function loadScript(name, src) {
console.log('NoteFormat requesting', name, src);

const isLoaded = getConfig(name+'.loaded', false);

return new Promise((resolve, reject) => {
if (isLoaded) {
console.log('NoteFormat script already loaded', name);
if (typeof resolve === 'function') {
resolve();
return;
}
}

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;

script.onload = _ => {
console.log('NoteFormat loaded', name);
window.SHOWDOWN_LOADED = true;
setConfig(name+'.loaded', true);
if (typeof resolve === 'function') {
resolve();
}
}

document.body.appendChild(script);
});
}



const formatters=[function (body) {
return loadScript('showdown', 'js/showdown.min.js')
.then(() => {
return new showdown.Converter().makeHtml(body);
});
}
,function (body) {
var sandbox = document.createElement('div');
sandbox.innerHTML = body;

Array.from(sandbox.querySelectorAll('iframe')).forEach(iframe => {
iframe.sandbox = 'allow-scripts allow-same-origin allow-forms';
});

return Promise.resolve(sandbox.innerHTML);
}
,]
27 changes: 27 additions & 0 deletions resources/note-format/repository.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"markdown": {
"name": "markdown",
"description": "Renders the note in markdown",
"src": "src\/markdown.js"
},
"iframe": {
"name": "iframe",
"description": "Play around with iframe attributes or block them",
"src": "src\/iframe.js"
},
"mathjax": {
"name": "mathjax",
"description": "Render LaTeX equations inside the note",
"src": "src\/mathjax.js"
},
"asciinema": {
"name": "asciinema",
"description": "Use $asciinema(attachment_url) to include an asciinema cast",
"src": "src\/asciinema.js"
},
"js-eval": {
"name": "js-eval",
"description": "Evaluate javascript code and display output in an <iframe>",
"src": "src\/js-eval.js"
}
}
10 changes: 10 additions & 0 deletions resources/note-format/src/asciinema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function (body, note) {
return loadScript('asciinema', 'js/asciinema-player.js')
.then(() => {
return body.replace(/\$asciinema\([^\)\(]+\)/g, match => {
var filename = match.substring(11).slice(0, -1);
var path = ['./attachments', note.id, filename].join('/');
return `<asciinema-player src="${path}"></asciinema-player>`;
})
});
}
Loading

0 comments on commit 46dbe48

Please sign in to comment.