Skip to content

Commit e38bbdc

Browse files
Add notes extension
1 parent 6faaeb6 commit e38bbdc

File tree

6 files changed

+263
-0
lines changed

6 files changed

+263
-0
lines changed

Diff for: extensions/web-notes/manifest.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "Web Notes",
3+
"description": "Expose web notes add-on",
4+
"dependencies": ["web-base"],
5+
"version": "1.0",
6+
"script": "web-notes.lua"
7+
}

Diff for: extensions/web-notes/web-draw.xml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<app-page id="draw" title="Draw">
2+
<template slot="bar-right">
3+
<button v-on:click="toggleFullScreen()" title="Refresh"><i class="fas fa-expand-alt"></i></button>
4+
<button v-on:click="clear()" title="Delete"><i class="fa fa-trash"></i></button>
5+
<button v-on:click="app.back()" title="Close"><i class="fa fa-window-close"></i></button>
6+
</template>
7+
<article class="content">
8+
<canvas id="draw-canvas" style="display: block; border: none; margin: 0px; padding: 0px;"></canvas>
9+
</article>
10+
</app-page>

Diff for: extensions/web-notes/web-note.xml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<app-page id="note" title="Note">
2+
<template slot="bar-middle">
3+
<h1 v-if="newName === false" v-on:click="newName = extname(name, true)">{{ name }}</h1>
4+
<div v-else>
5+
<input v-model="newName" type="text" placeholder="Note name">
6+
<button v-on:click="onRename()" title="Apply"><i class="fas fa-check"></i></button>
7+
<button v-on:click="newName = false" title="Cancel"><i class="fa fa-window-close"></i></button>
8+
</div>
9+
</template>
10+
<template slot="bar-right">
11+
<button v-on:click="toggleFullScreen()" title="Refresh"><i class="fas fa-expand-alt"></i></button>
12+
<button v-on:click="onShow()" title="Refresh"><i class="fas fa-sync"></i></button>
13+
<button v-on:click="onDelete().then(function(){ app.back(); })" title="Delete"><i class="fa fa-trash"></i></button>
14+
<button v-on:click="onSave()" title="Save"><i class="far fa-save"></i></button>
15+
<button v-on:click="app.back()" title="Close"><i class="fa fa-window-close"></i></button>
16+
</template>
17+
<article class="content">
18+
<textarea v-model="text" spellcheck="false" wrap="off" style="height: 100%; width: 100%; border: none; margin: 0px; padding: 0px; resize: none; font-size: 2rem;" placeholder="Enter your note here"></textarea>
19+
</article>
20+
</app-page>

Diff for: extensions/web-notes/web-notes.js

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
define(['./web-notes.xml', './web-note.xml', './web-draw.xml'], function(notesTemplate, noteTemplate, drawTemplate) {
2+
3+
var notesVue = new Vue({
4+
template: notesTemplate,
5+
data: {
6+
notes: []
7+
},
8+
methods: {
9+
onShow: function() {
10+
var self = this;
11+
return fetch('/notes/', {
12+
headers: {
13+
"Accept": 'application/json'
14+
}
15+
}).then(rejectIfNotOk).then(rejectIfNotOk).then(function(response) {
16+
return response.json();
17+
}).then(function(response) {
18+
if (!isEmpty(response)) {
19+
self.notes = response.filter(function(note) {
20+
return endsWith(note.name, '.txt');
21+
});
22+
}
23+
});
24+
}
25+
}
26+
});
27+
28+
var noteVue = new Vue({
29+
template: noteTemplate,
30+
data: {
31+
name: '',
32+
newName: false,
33+
text: ''
34+
},
35+
methods: {
36+
onShow: function(name) {
37+
this.name = name;
38+
this.newName = false;
39+
this.text = '';
40+
var self = this;
41+
return fetch('/notes/' + this.name).then(rejectIfNotOk).then(function(response) {
42+
return response.text();
43+
}).then(function(text) {
44+
self.text = text;
45+
});
46+
},
47+
onRename: function () {
48+
var self = this;
49+
this.onDelete().then(function() {
50+
self.name = self.newName + '.txt';
51+
return self.onSave();
52+
}).then(function() {
53+
self.newName = false;
54+
});
55+
},
56+
onDelete: function () {
57+
return fetch('/notes/' + this.name, {
58+
method: 'DELETE'
59+
}).then(function() {
60+
toaster.toast('Note deleted');
61+
});
62+
},
63+
onSave: function () {
64+
return fetch('/notes/' + this.name, {
65+
method: 'PUT',
66+
body: this.text
67+
}).then(function() {
68+
toaster.toast('Note saved');
69+
});
70+
}
71+
}
72+
});
73+
74+
function drawDot(ctx, x, y, size) {
75+
ctx.fillStyle = "rgba(0,0,0,0.6)";
76+
ctx.beginPath();
77+
ctx.arc(x, y, size, 0, Math.PI*2, true);
78+
ctx.closePath();
79+
ctx.fill();
80+
}
81+
82+
var canvas, context, size = 6;
83+
var mouseX, mouseY, mouseDown = 0;
84+
var touchX, touchY;
85+
86+
function onMouseDown() {
87+
mouseDown = 1;
88+
drawDot(context , mouseX, mouseY, size);
89+
}
90+
function onMouseUp() {
91+
mouseDown = 0;
92+
}
93+
function onMouseMove(event) {
94+
getMousePos(event);
95+
if (mouseDown === 1) {
96+
drawDot(context, mouseX, mouseY, size);
97+
}
98+
}
99+
function getMousePos(event) {
100+
if (event.offsetX) {
101+
mouseX = event.offsetX;
102+
mouseY = event.offsetY;
103+
} else if (event.layerX) {
104+
mouseX = event.layerX;
105+
mouseY = event.layerY;
106+
}
107+
}
108+
function onTouchStart(event) {
109+
getTouchPos();
110+
drawDot(context, touchX, touchY, size);
111+
event.preventDefault();
112+
}
113+
function onTouchMove(event) {
114+
getTouchPos(event);
115+
drawDot(context, touchX, touchY, size);
116+
event.preventDefault();
117+
}
118+
function getTouchPos(event) {
119+
if(event.touches) {
120+
if (event.touches.length === 1) {
121+
var touch = event.touches[0];
122+
touchX = touch.pageX - touch.target.offsetLeft;
123+
touchY = touch.pageY - touch.target.offsetTop;
124+
}
125+
}
126+
}
127+
function resizeCanvas() {
128+
var draw = document.getElementById('draw');
129+
canvas.width = window.innerWidth;
130+
canvas.height = window.innerHeight - draw.children[0].offsetHeight;
131+
}
132+
133+
var drawVue = new Vue({
134+
template: drawTemplate,
135+
methods: {
136+
onShow: function() {
137+
canvas = document.getElementById('draw-canvas');
138+
if (!canvas) {
139+
return;
140+
}
141+
context = canvas.getContext && canvas.getContext('2d');
142+
if (context) {
143+
canvas.addEventListener('mousedown', onMouseDown, false);
144+
canvas.addEventListener('mousemove', onMouseMove, false);
145+
window.addEventListener('mouseup', onMouseUp, false);
146+
canvas.addEventListener('touchstart', onTouchStart, false);
147+
canvas.addEventListener('touchmove', onTouchMove, false);
148+
window.addEventListener('resize', resizeCanvas, false);
149+
resizeCanvas();
150+
}
151+
},
152+
onHide: function() {
153+
canvas.removeEventListener('mousedown', onMouseDown);
154+
canvas.removeEventListener('mousemove', onMouseMove);
155+
window.removeEventListener('mouseup', onMouseUp);
156+
canvas.removeEventListener('touchstart', onTouchStart);
157+
canvas.removeEventListener('touchmove', onTouchMove);
158+
window.removeEventListener('resize', resizeCanvas);
159+
},
160+
clear: function() {
161+
context.clearRect(0, 0, canvas.width, canvas.height);
162+
}
163+
}
164+
});
165+
166+
addPageComponent(notesVue, 'fa-sticky-note');
167+
addPageComponent(noteVue);
168+
addPageComponent(drawVue);
169+
170+
});

Diff for: extensions/web-notes/web-notes.lua

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
local extension = ...
2+
3+
local logger = require('jls.lang.logger')
4+
local File = require('jls.io.File')
5+
local FileHttpHandler = require('jls.net.http.handler.FileHttpHandler')
6+
7+
local contexts = {}
8+
9+
local function cleanup(server)
10+
for _, context in ipairs(contexts) do
11+
server:removeContext(context)
12+
end
13+
contexts = {}
14+
end
15+
16+
local function addContext(server, ...)
17+
local context = server:createContext(...)
18+
table.insert(contexts, context)
19+
end
20+
21+
extension:subscribeEvent('startup', function()
22+
local engine = extension:getEngine()
23+
local server = engine:getHTTPServer()
24+
cleanup(server)
25+
local notesDir = File:new(engine:getWorkDirectory(), 'notes')
26+
if not notesDir:isDirectory() then
27+
if not notesDir:mkdir() then
28+
logger:warn('Unable to create the directory "'..notesDir:getPath()..'"')
29+
end
30+
end
31+
addContext(server, '/notes/(.*)', FileHttpHandler:new(notesDir, 'rwl'))
32+
engine:onExtension('web-base', function(webBaseExtension)
33+
webBaseExtension:registerAddonExtension(extension, true)
34+
end)
35+
end)
36+
37+
extension:subscribeEvent('shutdown', function()
38+
local engine = extension:getEngine()
39+
local server = engine:getHTTPServer()
40+
engine:onExtension('web-base', function(webBaseExtension)
41+
webBaseExtension:unregisterAddonExtension(extension)
42+
end)
43+
cleanup(server)
44+
end)

Diff for: extensions/web-notes/web-notes.xml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<app-page id="notes" title="Notes">
2+
<template slot="bar-right">
3+
<button v-on:click="app.toPage('draw')" title="Open draw"><i class="fas fa-paint-brush"></i></button>
4+
<button v-on:click="app.toPage('note', 'New note.txt')" title="Create new note"><i class="fa fa-plus"></i></button>
5+
<button v-on:click="onShow()" title="Refresh"><i class="fas fa-sync"></i></button>
6+
</template>
7+
<article class="tiles">
8+
<div class="tile" v-on:click="app.toPage('note', note.name)" v-for="note in notes">
9+
<p>{{ note.name }}</p>
10+
</div>
11+
</article>
12+
</app-page>

0 commit comments

Comments
 (0)