jekyll-obsidian 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +49 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE +21 -0
- data/README.md +97 -0
- data/Rakefile +10 -0
- data/assets/css/obsidian.scss +103 -0
- data/assets/css/partials/_canvas.scss +62 -0
- data/assets/css/partials/_explorer.scss +128 -0
- data/assets/css/partials/_fileread.scss +111 -0
- data/assets/css/partials/_loading.scss +28 -0
- data/assets/css/partials/_modals.scss +206 -0
- data/assets/css/partials/_note.scss +113 -0
- data/assets/css/partials/_sidebar.scss +30 -0
- data/assets/includes/canvas.html +403 -0
- data/assets/includes/explorer.html +202 -0
- data/assets/includes/fileread.html +338 -0
- data/assets/includes/modals.html +277 -0
- data/assets/includes/note.html +175 -0
- data/assets/includes/sidebar.html +106 -0
- data/assets/layouts/obsidian.html +294 -0
- data/lib/jekyll/obsidian/version.rb +7 -0
- data/lib/jekyll/obsidian.rb +250 -0
- data/lib/jekyll/remote.rb +36 -0
- data/lib/jekyll-obsidian.rb +3 -0
- data/screenshots/jekyll-obsidian.png +0 -0
- data/sig/jekyll/obsidian.rbs +6 -0
- metadata +103 -0
@@ -0,0 +1,403 @@
|
|
1
|
+
<body>
|
2
|
+
<div id="canvas-content"></div>
|
3
|
+
</body>
|
4
|
+
|
5
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/two.js/0.8.14/two.min.js"></script>
|
6
|
+
|
7
|
+
<script>
|
8
|
+
let _scrollLeft = 0; let _scrollTop = 0;
|
9
|
+
var EDGES = null;
|
10
|
+
function loadCanvas(canvasPath) {
|
11
|
+
const fullFilePath = getFullFilePath(canvasPath);
|
12
|
+
generateBreadcrumbs(canvasPath);
|
13
|
+
console.log("fullFilePath Canvas: " + fullFilePath)
|
14
|
+
|
15
|
+
fetch(fullFilePath).then(response => {
|
16
|
+
if (!response.ok)
|
17
|
+
throw new Error('Network response: ' + response.statusText);
|
18
|
+
return response.text();
|
19
|
+
}).then(content => {
|
20
|
+
try {
|
21
|
+
FILEREAD.scrollTop = 0;
|
22
|
+
FILEREAD.scrollLeft = 0;
|
23
|
+
const canvasParsed = JSON.parse(content);
|
24
|
+
if (!canvasParsed.nodes || !Array.isArray(canvasParsed.nodes)) {
|
25
|
+
console.error('Invalid or missing nodes array');
|
26
|
+
return;
|
27
|
+
}
|
28
|
+
|
29
|
+
const canvasContent = CANVAS.querySelector('#canvas-content');
|
30
|
+
while (canvasContent.firstChild)
|
31
|
+
canvasContent.removeChild(canvasContent.firstChild);
|
32
|
+
canvasContent.style.transform = `scale(1)`;
|
33
|
+
|
34
|
+
const nodesKeysList = canvasParsed.nodes.map(node => Object.keys(node));
|
35
|
+
|
36
|
+
const canvasWidth = parseInt(window.getComputedStyle(CANVAS).width);
|
37
|
+
|
38
|
+
const minX = canvasParsed.nodes.reduce((min, node) => Math.min(min, node.x), Infinity);
|
39
|
+
const maxX = canvasParsed.nodes.reduce((max, node) => Math.max(max, node.x), -Infinity);
|
40
|
+
const minY = canvasParsed.nodes.reduce((min, node) => Math.min(min, node.y), Infinity);
|
41
|
+
const maxY = canvasParsed.nodes.reduce((max, node) => Math.max(max, node.y), -Infinity);
|
42
|
+
|
43
|
+
const centerX = (minX + maxX) / 2;
|
44
|
+
const centerY = (minY + maxY) / 2;
|
45
|
+
console.warn(`Center point: (${centerX}, ${centerY})`);
|
46
|
+
|
47
|
+
// <!-- ----------------------------- Draw Edges ------------------------------ -->
|
48
|
+
const svgElement = CANVAS.querySelector('svg');
|
49
|
+
if (svgElement) svgElement.remove();
|
50
|
+
const edgesKeysList = canvasParsed.edges.map(edge => Object.keys(edge))
|
51
|
+
const params = { width: canvasWidth, height: canvasWidth };
|
52
|
+
const two = new Two(params).appendTo(CANVAS);
|
53
|
+
canvasParsed.edges.forEach(edge => {
|
54
|
+
const fromNode = canvasParsed.nodes.find(node => node.id === edge.fromNode);
|
55
|
+
const toNode = canvasParsed.nodes.find(node => node.id === edge.toNode);
|
56
|
+
|
57
|
+
if (fromNode && toNode) {
|
58
|
+
function getSideOffset(node, offsetX, offsetY, side) {
|
59
|
+
let sideOffsetX = offsetX;
|
60
|
+
let sideOffsetY = offsetY;
|
61
|
+
switch (side) {
|
62
|
+
case "top":
|
63
|
+
sideOffsetY = (offsetY - node.height / 2) + 5; break;
|
64
|
+
case "bottom":
|
65
|
+
sideOffsetY = (offsetY + node.height / 2) - 5; break;
|
66
|
+
case "right":
|
67
|
+
sideOffsetX = (offsetX + node.width / 2) - 5; break;
|
68
|
+
case "left":
|
69
|
+
sideOffsetX = (offsetX - node.width / 2) + 5; break;
|
70
|
+
}
|
71
|
+
return { sideOffsetX, sideOffsetY };
|
72
|
+
}
|
73
|
+
|
74
|
+
// Calculate common offset values
|
75
|
+
const offsetXFrom = (-minX) + (canvasWidth / 2) + fromNode.width / 2;
|
76
|
+
const offsetYFrom = (-minY) + (canvasWidth / 2) + fromNode.height / 2;
|
77
|
+
|
78
|
+
const offsetXTo = (-minX) + (canvasWidth / 2) + toNode.width / 2;
|
79
|
+
const offsetYTo = (-minY) + (canvasWidth / 2) + toNode.height / 2;
|
80
|
+
|
81
|
+
const fromOffsets = getSideOffset(fromNode, offsetXFrom, offsetYFrom, edge.fromSide);
|
82
|
+
const from_SideOffsetX = fromOffsets.sideOffsetX;
|
83
|
+
const from_SideOffsetY = fromOffsets.sideOffsetY;
|
84
|
+
|
85
|
+
const toOffsets = getSideOffset(toNode, offsetXTo, offsetYTo, edge.toSide);
|
86
|
+
const to_SideOffsetX = toOffsets.sideOffsetX;
|
87
|
+
const to_SideOffsetY = toOffsets.sideOffsetY;
|
88
|
+
|
89
|
+
function getControlPointOffset(node, side) {
|
90
|
+
const controlOffset = 5;
|
91
|
+
let controlOffsetX = 0;
|
92
|
+
let controlOffsetY = 0;
|
93
|
+
|
94
|
+
switch (side) {
|
95
|
+
case "top":
|
96
|
+
controlOffsetY = -controlOffset; break;
|
97
|
+
case "bottom":
|
98
|
+
controlOffsetY = controlOffset; break;
|
99
|
+
case "right":
|
100
|
+
controlOffsetX = controlOffset;
|
101
|
+
controlOffsetY = 0; break;
|
102
|
+
case "left":
|
103
|
+
controlOffsetX = -controlOffset;
|
104
|
+
controlOffsetY = 0; break;
|
105
|
+
}
|
106
|
+
return { controlOffsetX, controlOffsetY };
|
107
|
+
}
|
108
|
+
|
109
|
+
// Calculate the start and end offsets based on node side
|
110
|
+
const fromCurveOffset = getControlPointOffset(fromNode, edge.fromSide);
|
111
|
+
const toCurveOffset = getControlPointOffset(toNode, edge.toSide);
|
112
|
+
|
113
|
+
const curve = two.makeCurve(
|
114
|
+
fromNode.x + from_SideOffsetX,
|
115
|
+
fromNode.y + from_SideOffsetY,
|
116
|
+
|
117
|
+
fromNode.x + from_SideOffsetX + fromCurveOffset.controlOffsetX,
|
118
|
+
fromNode.y + from_SideOffsetY + fromCurveOffset.controlOffsetY,
|
119
|
+
|
120
|
+
toNode.x + to_SideOffsetX + toCurveOffset.controlOffsetX,
|
121
|
+
toNode.y + to_SideOffsetY + toCurveOffset.controlOffsetY,
|
122
|
+
|
123
|
+
// End point at toNode
|
124
|
+
toNode.x + to_SideOffsetX,
|
125
|
+
toNode.y + to_SideOffsetY,
|
126
|
+
|
127
|
+
false // Not a closed path
|
128
|
+
);
|
129
|
+
|
130
|
+
if ('color' in edge) {
|
131
|
+
curve.stroke = getColorByIndex(edge.color);
|
132
|
+
} else curve.stroke = '#c0c0c0';
|
133
|
+
curve.linewidth = 4;
|
134
|
+
curve.fill = 'transparent';
|
135
|
+
curve.fillOpacity = 0;
|
136
|
+
curve.closed = false;
|
137
|
+
}
|
138
|
+
});
|
139
|
+
two.update()
|
140
|
+
EDGES = CANVAS.querySelector('svg');
|
141
|
+
// <!-- --------------------------- Draw Edges END ---------------------------- -->
|
142
|
+
|
143
|
+
canvasParsed.nodes.forEach(node => {
|
144
|
+
const card = document.createElement('div');
|
145
|
+
if ('color' in node) card.style.borderColor = getColorByIndex(node.color);
|
146
|
+
switch (node.type) {
|
147
|
+
case "text":
|
148
|
+
default:
|
149
|
+
card.classList.add('card', 'text');
|
150
|
+
if (node.text) {
|
151
|
+
const cardDiv = parseStringToMarkdown(node.text);
|
152
|
+
card.innerHTML = DOMPurify.sanitize(cardDiv.innerHTML);
|
153
|
+
cardDiv.remove();
|
154
|
+
if (BACKLINKS) mapBacklinksToJson(BACKLINKS);
|
155
|
+
}
|
156
|
+
break;
|
157
|
+
case "file":
|
158
|
+
card.classList.add('card', 'file');
|
159
|
+
card.textContent = node.file;
|
160
|
+
break;
|
161
|
+
case "link":
|
162
|
+
card.classList.add('card', 'link');
|
163
|
+
card.textContent = node.link;
|
164
|
+
break;
|
165
|
+
case "group":
|
166
|
+
card.classList.add('card', 'group');
|
167
|
+
if (node.label !== "") {
|
168
|
+
const groupLabel = document.createElement('p')
|
169
|
+
groupLabel.textContent = node.label;
|
170
|
+
card.appendChild(groupLabel);
|
171
|
+
}
|
172
|
+
break;
|
173
|
+
}
|
174
|
+
card.style.position = 'absolute';
|
175
|
+
card.style.top = `${node.y + (-minY) + (canvasWidth / 2)}px`;
|
176
|
+
card.style.left = `${node.x + (-minX) + (canvasWidth / 2)}px`;
|
177
|
+
|
178
|
+
card.style.width = `${node.width}px`;
|
179
|
+
card.style.height = `${node.height}px`;
|
180
|
+
|
181
|
+
canvasContent.appendChild(card);
|
182
|
+
});
|
183
|
+
|
184
|
+
const boundingBox = getBoundingBoxOfChildren(canvasContent);
|
185
|
+
if (boundingBox) {
|
186
|
+
const center = getCenterOfBoundingBox(boundingBox);
|
187
|
+
console.info(`Canvas center: x=${center.x}, y=${center.y}`);
|
188
|
+
const FILEREADWidth = FILEREAD.offsetWidth;
|
189
|
+
const FILEREADHeight = FILEREAD.offsetHeight;
|
190
|
+
|
191
|
+
const boundingBoxVolume = boundingBox.height * boundingBox.width;
|
192
|
+
const canvasVolume = canvasWidth ** 2;
|
193
|
+
|
194
|
+
const scale = (boundingBoxVolume / canvasVolume);
|
195
|
+
|
196
|
+
canvasContent.style.transform = `scale(${(1 - scale) / 2})`;
|
197
|
+
EDGES.style.transform = `scale(${(1 - scale) / 2})`;
|
198
|
+
|
199
|
+
_scrollLeft = center.x - boundingBox.width / 2;
|
200
|
+
_scrollTop = center.y - boundingBox.height / 2;
|
201
|
+
|
202
|
+
if (window.innerWidth < 481) {
|
203
|
+
_scrollLeft += window.innerWidth;
|
204
|
+
_scrollTop += window.innerHeight / 2;
|
205
|
+
}
|
206
|
+
FILEREAD.scrollLeft = _scrollLeft; FILEREAD.scrollTop = _scrollTop;
|
207
|
+
} else console.warn('The canvas has no children.');
|
208
|
+
|
209
|
+
FILEREAD.addEventListener('wheel', adjustCanvasScaleOnScroll);
|
210
|
+
} catch (e) {
|
211
|
+
console.error("Error parsing canvas:", e);
|
212
|
+
}
|
213
|
+
});
|
214
|
+
}
|
215
|
+
</script>
|
216
|
+
|
217
|
+
<!-- --------------------- Canvas movement and zooming --------------------- -->
|
218
|
+
<script>
|
219
|
+
function resetCanvasPosition() {
|
220
|
+
if (_scrollLeft) FILEREAD.scrollLeft = _scrollLeft;
|
221
|
+
if (_scrollTop) FILEREAD.scrollTop = _scrollTop;
|
222
|
+
}
|
223
|
+
|
224
|
+
function getBoundingBoxOfChildren(parentElement) {
|
225
|
+
const children = parentElement.children;
|
226
|
+
if (children.length === 0) {
|
227
|
+
return null;
|
228
|
+
}
|
229
|
+
// Initialize the bounding box with the first child's bounding rectangle
|
230
|
+
const firstChildRect = children[0].getBoundingClientRect();
|
231
|
+
let minX = firstChildRect.left;
|
232
|
+
let minY = firstChildRect.top;
|
233
|
+
let maxX = firstChildRect.right;
|
234
|
+
let maxY = firstChildRect.bottom;
|
235
|
+
|
236
|
+
// Iterate over all children to find the bounding box
|
237
|
+
for (let i = 1; i < children.length; i++) {
|
238
|
+
const rect = children[i].getBoundingClientRect();
|
239
|
+
if (rect.left < minX) minX = rect.left;
|
240
|
+
if (rect.top < minY) minY = rect.top;
|
241
|
+
if (rect.right > maxX) maxX = rect.right;
|
242
|
+
if (rect.bottom > maxY) maxY = rect.bottom;
|
243
|
+
}
|
244
|
+
|
245
|
+
const boundingBox = {
|
246
|
+
left: minX,
|
247
|
+
top: minY,
|
248
|
+
right: maxX,
|
249
|
+
bottom: maxY,
|
250
|
+
width: maxX - minX,
|
251
|
+
height: maxY - minY,
|
252
|
+
};
|
253
|
+
return boundingBox;
|
254
|
+
}
|
255
|
+
|
256
|
+
function getColorByIndex(colorInt) {
|
257
|
+
switch (parseInt(colorInt)) {
|
258
|
+
case 1: return '#ea3b50';
|
259
|
+
case 2: return '#ee7e0f';
|
260
|
+
case 3: return '#e0ac00';
|
261
|
+
case 4: return '#09b94f';
|
262
|
+
case 5: return '#18c5c2';
|
263
|
+
default: return '#c0c0c0';
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
// Function to calculate the center of the bounding box
|
268
|
+
function getCenterOfBoundingBox(boundingBox) {
|
269
|
+
const centerX = boundingBox.left + (boundingBox.width / 2);
|
270
|
+
const centerY = boundingBox.top + (boundingBox.height / 2);
|
271
|
+
return { x: centerX, y: centerY };
|
272
|
+
}
|
273
|
+
|
274
|
+
function adjustCanvasScaleOnScroll(e) {
|
275
|
+
if (e.ctrlKey) {
|
276
|
+
e.preventDefault();
|
277
|
+
|
278
|
+
const canvasContent = CANVAS.querySelector('#canvas-content');
|
279
|
+
let scale = parseFloat(canvasContent.style.transform.replace(/[^0-9.-]/g, ''));
|
280
|
+
if (isNaN(scale)) scale = 1; // Default scale
|
281
|
+
|
282
|
+
// Get cursor position relative to the canvas
|
283
|
+
const rect = CANVAS.getBoundingClientRect();
|
284
|
+
const x = e.clientX - rect.left;
|
285
|
+
const y = e.clientY - rect.top;
|
286
|
+
|
287
|
+
// Calculate the point on the canvas before scaling
|
288
|
+
const pointBeforeScale = {
|
289
|
+
x: (FILEREAD.scrollLeft + x) / scale,
|
290
|
+
y: (FILEREAD.scrollTop + y) / scale
|
291
|
+
};
|
292
|
+
|
293
|
+
console.info("Scroll direction: " + (e.deltaY > 0 ? "down" : "up"));
|
294
|
+
if (e.deltaY > 0) {
|
295
|
+
scale = Math.max(0.1, scale - 0.01);
|
296
|
+
} else {
|
297
|
+
scale = Math.min(1, scale + 0.01);
|
298
|
+
}
|
299
|
+
|
300
|
+
canvasContent.style.transform = `scale(${scale})`;
|
301
|
+
EDGES.style.transform = `scale(${scale})`;
|
302
|
+
EDGES.style.transformOrigin = 'center center';
|
303
|
+
|
304
|
+
const scrollMultiplier = 0.02;
|
305
|
+
const deltaX = (pointBeforeScale.x * scale - (FILEREAD.scrollLeft + x)) * scrollMultiplier;
|
306
|
+
const deltaY = (pointBeforeScale.y * scale - (FILEREAD.scrollTop + y)) * scrollMultiplier;
|
307
|
+
FILEREAD.scrollLeft += deltaX;
|
308
|
+
FILEREAD.scrollTop += deltaY;
|
309
|
+
}
|
310
|
+
}
|
311
|
+
</script>
|
312
|
+
|
313
|
+
<script>
|
314
|
+
document.addEventListener('DOMContentLoaded', () => {
|
315
|
+
const homepage = '{{ site.obsidian_homepage | escape }}';
|
316
|
+
if (homepage && homepage.endsWith('.canvas')) {
|
317
|
+
loadCanvas(homepage);
|
318
|
+
console.log("canvas read loaded: " + homepage);
|
319
|
+
}
|
320
|
+
|
321
|
+
CANVAS.addEventListener('mousemove', function (e) {
|
322
|
+
const rect = CANVAS.getBoundingClientRect();
|
323
|
+
const mouseX = e.clientX - rect.left;
|
324
|
+
const mouseY = e.clientY - rect.top;
|
325
|
+
// console.log(`Mouse X: ${mouseX}, Mouse Y: ${mouseY}`);
|
326
|
+
});
|
327
|
+
});
|
328
|
+
|
329
|
+
document.addEventListener('obsidian_canvasSelect', function (event) {
|
330
|
+
switchPage(event.detail.filePath);
|
331
|
+
loadCanvas(event.detail.filePath);
|
332
|
+
});
|
333
|
+
</script>
|
334
|
+
|
335
|
+
<!-- --------------------------- Drag Listeners ---------------------------- -->
|
336
|
+
<!-- <script>
|
337
|
+
const fileRead = document.querySelector('#fileread');
|
338
|
+
let isMouseDown = false;
|
339
|
+
let lastMouseX = 0;
|
340
|
+
let lastMouseY = 0;
|
341
|
+
let lastTime = 0;
|
342
|
+
|
343
|
+
fileRead.addEventListener('mousedown', (event) => {
|
344
|
+
isMouseDown = true;
|
345
|
+
const canvasContent = CANVAS.querySelector('#canvas-content');
|
346
|
+
canvasContent.style.pointerEvents = 'none';
|
347
|
+
canvasContent.style.userSelect = 'none';
|
348
|
+
|
349
|
+
lastMouseX = event.offsetX;
|
350
|
+
lastMouseY = event.offsetY;
|
351
|
+
lastTime = performance.now(); // Record the time when mouse is pressed
|
352
|
+
|
353
|
+
console.log(`Mouse pressed at: ${lastMouseX}, ${lastMouseY}`);
|
354
|
+
});
|
355
|
+
|
356
|
+
// Mouse move event to track the mouse movement
|
357
|
+
fileRead.addEventListener('mousemove', (event) => {
|
358
|
+
if (isMouseDown) {
|
359
|
+
const currentMouseX = event.offsetX;
|
360
|
+
const currentMouseY = event.offsetY;
|
361
|
+
const currentTime = performance.now();
|
362
|
+
|
363
|
+
let deltaX = currentMouseX - lastMouseX;
|
364
|
+
let deltaY = currentMouseY - lastMouseY;
|
365
|
+
const deltaTime = currentTime - lastTime;
|
366
|
+
|
367
|
+
let velocityX = deltaX / deltaTime;
|
368
|
+
velocityX = velocityX.toFixed(1)
|
369
|
+
let velocityY = deltaY / deltaTime;
|
370
|
+
velocityY = velocityY.toFixed(1)
|
371
|
+
|
372
|
+
// if (Math.abs(deltaX) > 1) deltaX = Math.sign(deltaX) * 1;
|
373
|
+
// if (Math.abs(deltaY) > 1) deltaY = Math.sign(deltaY) * 1;
|
374
|
+
|
375
|
+
// if (Math.abs(velocityX) > 1) velocityX = Math.sign(velocityX) * 1;
|
376
|
+
// if (Math.abs(velocityY) > 1) velocityY = Math.sign(velocityY) * 1;
|
377
|
+
|
378
|
+
console.log(`Velocity X: ${velocityX} px/ms, Velocity Y: ${velocityY} px/ms`);
|
379
|
+
FILEREAD.scrollLeft -= velocityX * 20; FILEREAD.scrollTop -= velocityY * 20;
|
380
|
+
|
381
|
+
lastMouseX = currentMouseX;
|
382
|
+
lastMouseY = currentMouseY;
|
383
|
+
lastTime = currentTime;
|
384
|
+
}
|
385
|
+
});
|
386
|
+
|
387
|
+
// Mouse up event to stop tracking
|
388
|
+
fileRead.addEventListener('mouseup', () => {
|
389
|
+
isMouseDown = false;
|
390
|
+
const canvasContent = CANVAS.querySelector('#canvas-content');
|
391
|
+
canvasContent.style.pointerEvents = 'auto';
|
392
|
+
canvasContent.style.userSelect = 'auto';
|
393
|
+
console.log('Mouse released');
|
394
|
+
});
|
395
|
+
|
396
|
+
fileRead.addEventListener('mouseleave', () => {
|
397
|
+
console.log('Mouse left the element!');
|
398
|
+
isMouseDown = false;
|
399
|
+
const canvasContent = CANVAS.querySelector('#canvas-content');
|
400
|
+
canvasContent.style.pointerEvents = 'auto';
|
401
|
+
canvasContent.style.userSelect = 'auto';
|
402
|
+
});
|
403
|
+
</script> -->
|
@@ -0,0 +1,202 @@
|
|
1
|
+
<body>
|
2
|
+
<div id="vault-name">
|
3
|
+
<div id="text">
|
4
|
+
{{ site.obsidian_vault | split: "/" | last }}</div>
|
5
|
+
<button id="collapseExpand"></button>
|
6
|
+
</div>
|
7
|
+
<div id="file-tree"></div>
|
8
|
+
</body>
|
9
|
+
|
10
|
+
<!-- ------------------------- Touch screen docker ------------------------- -->
|
11
|
+
<script>
|
12
|
+
|
13
|
+
let touchStartX = 0;
|
14
|
+
let touchCurrentX = 0;
|
15
|
+
let isDraggingTouch = false;
|
16
|
+
let explorerDisplayed = false;
|
17
|
+
let activationDistance = 0;
|
18
|
+
|
19
|
+
document.addEventListener('touchstart', handleTouchStart, false);
|
20
|
+
document.addEventListener('touchmove', handleTouchMove, false);
|
21
|
+
document.addEventListener('touchend', handleTouchEnd, false);
|
22
|
+
|
23
|
+
function handleTouchStart(evt) {
|
24
|
+
const firstTouch = evt.touches[0];
|
25
|
+
touchStartX = firstTouch.clientX;
|
26
|
+
let touchStartY = firstTouch.clientY;
|
27
|
+
|
28
|
+
explorerDisplayed = isDisplayNone(EXPLORER);
|
29
|
+
activationDistance = window.innerWidth - 120;
|
30
|
+
|
31
|
+
if (!explorerDisplayed) {
|
32
|
+
if (touchStartX < 40 && touchStartY > 150
|
33
|
+
&& touchStartY < window.innerHeight - 45) {
|
34
|
+
isDraggingTouch = true;
|
35
|
+
EXPLORER.style.display = '';
|
36
|
+
}
|
37
|
+
}
|
38
|
+
else {
|
39
|
+
if (touchStartX > window.innerWidth - 40
|
40
|
+
&& touchStartY > 100) {
|
41
|
+
isDraggingTouch = true;
|
42
|
+
FILEREAD.style.display = '';
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
}
|
47
|
+
|
48
|
+
function handleTouchMove(evt) {
|
49
|
+
if (!isDraggingTouch) return;
|
50
|
+
touchCurrentX = evt.touches[0].clientX;
|
51
|
+
|
52
|
+
if (!explorerDisplayed) {
|
53
|
+
// Dragging to adjust the explorer width from left to right
|
54
|
+
let diffX = touchCurrentX - touchStartX;
|
55
|
+
let padding = linearMapInRange(diffX, 0, activationDistance, 1, 100);
|
56
|
+
EXPLORER.style.width = `calc(${padding}% - 2rem)`;
|
57
|
+
} else {
|
58
|
+
// Dragging to reveal the explorer from right to left
|
59
|
+
let diffX = window.innerWidth - touchCurrentX;
|
60
|
+
let padding = linearMapInRange(diffX, 0, activationDistance, 100, 1);
|
61
|
+
EXPLORER.style.width = `calc(${padding}% - 2rem)`;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
function handleTouchEnd(evt) {
|
66
|
+
if (!isDraggingTouch) return;
|
67
|
+
|
68
|
+
if (!explorerDisplayed) {
|
69
|
+
let diffX = touchCurrentX - touchStartX;
|
70
|
+
if (diffX > activationDistance) {
|
71
|
+
EXPLORER.style.width = 'calc(100% - 2rem)';
|
72
|
+
FILEREAD.style.display = 'none';
|
73
|
+
} else {
|
74
|
+
EXPLORER.style.display = 'none';
|
75
|
+
EXPLORER.style.width = '1%';
|
76
|
+
}
|
77
|
+
} else {
|
78
|
+
let diffX = window.innerWidth - touchCurrentX;
|
79
|
+
if (diffX > activationDistance) {
|
80
|
+
EXPLORER.style.display = 'none';
|
81
|
+
EXPLORER.style.width = '1%';
|
82
|
+
} else {
|
83
|
+
FILEREAD.style.display = '';
|
84
|
+
EXPLORER.style.width = 'calc(100% - 2rem)';
|
85
|
+
}
|
86
|
+
}
|
87
|
+
isDraggingTouch = false;
|
88
|
+
touchStartX = 0; touchCurrentX = 0;
|
89
|
+
}
|
90
|
+
|
91
|
+
function isDisplayNone(element) {
|
92
|
+
const style = getComputedStyle(element);
|
93
|
+
return style.display !== 'none';
|
94
|
+
}
|
95
|
+
|
96
|
+
</script>
|
97
|
+
<!-- ------------------------- File tree generator ------------------------- -->
|
98
|
+
<script lang="text/javascript">
|
99
|
+
|
100
|
+
function buildFileTree(rootElement, tree, expandRoot = false) {
|
101
|
+
const EXPLORER = OBSIDIAN.querySelector('#explorer');
|
102
|
+
const FILE_TREE = EXPLORER.querySelector(' #file-tree');
|
103
|
+
const old_ul = rootElement.querySelector('ul');
|
104
|
+
if (old_ul) rootElement.removeChild(old_ul);
|
105
|
+
const ul = document.createElement('ul');
|
106
|
+
rootElement.appendChild(ul);
|
107
|
+
|
108
|
+
tree.forEach(child => {
|
109
|
+
const li = document.createElement('li');
|
110
|
+
ul.appendChild(li);
|
111
|
+
|
112
|
+
const button = document.createElement('button');
|
113
|
+
const decodedString = he.decode(child.name);
|
114
|
+
button.textContent = decodedString;
|
115
|
+
li.appendChild(button);
|
116
|
+
|
117
|
+
if (child.type === 'dir') {
|
118
|
+
li.classList.add('dir');
|
119
|
+
|
120
|
+
button.innerHTML =
|
121
|
+
DOMPurify.sanitize(getSVGIcon(Icon.RightArrow) + button.textContent);
|
122
|
+
var expanded = false;
|
123
|
+
button.addEventListener('click', () => {
|
124
|
+
expanded = !expanded;
|
125
|
+
button.innerHTML =
|
126
|
+
DOMPurify.sanitize((expanded ? getSVGIcon(Icon.DownArrow)
|
127
|
+
: getSVGIcon(Icon.RightArrow)) + button.textContent);
|
128
|
+
|
129
|
+
if (expanded) {
|
130
|
+
if (!li.querySelector('ul'))
|
131
|
+
buildFileTree(li, child.children);
|
132
|
+
} else {
|
133
|
+
const nestedUl = li.querySelector('ul');
|
134
|
+
if (nestedUl) li.removeChild(nestedUl);
|
135
|
+
}
|
136
|
+
});
|
137
|
+
if (expandRoot === true) {
|
138
|
+
expanded = true;
|
139
|
+
button.innerHTML =
|
140
|
+
DOMPurify.sanitize(getSVGIcon(Icon.DownArrow) + button.textContent);
|
141
|
+
if (!li.querySelector('ul'))
|
142
|
+
buildFileTree(li, child.children, true);
|
143
|
+
}
|
144
|
+
|
145
|
+
} else if (child.type === 'file') {
|
146
|
+
li.classList.add('file');
|
147
|
+
button.addEventListener('click', () => {
|
148
|
+
dispatchFileSelectEvt(he.decode(child.path));
|
149
|
+
});
|
150
|
+
const fileName = child.name.replace(/\.[^/.]+$/, "")
|
151
|
+
button.textContent = he.decode(fileName);
|
152
|
+
}
|
153
|
+
});
|
154
|
+
}
|
155
|
+
|
156
|
+
function sortObsidianVaultFiles(tree) {
|
157
|
+
const EXPLORER = OBSIDIAN.querySelector('#explorer');
|
158
|
+
const FILE_TREE = EXPLORER.querySelector(' #file-tree');
|
159
|
+
tree.sort((a, b) => {
|
160
|
+
if (a.type === 'dir' && b.type === 'file') {
|
161
|
+
return -1;
|
162
|
+
} else if (a.type === 'file' && b.type === 'dir') {
|
163
|
+
return 1;
|
164
|
+
} else return a.name.localeCompare(b.name);
|
165
|
+
});
|
166
|
+
tree.forEach(child => {
|
167
|
+
if (child.type === 'dir') {
|
168
|
+
sortObsidianVaultFiles(child.children);
|
169
|
+
}
|
170
|
+
});
|
171
|
+
return tree;
|
172
|
+
}
|
173
|
+
|
174
|
+
let expandOrCollapseFileTree = false;
|
175
|
+
document.addEventListener('DOMContentLoaded', () => {
|
176
|
+
const VAULT_FILES =
|
177
|
+
JSON.parse('{{ site.data.obsidian.vault_files | escape }}'.replace(/"/g, '"'));
|
178
|
+
const EXPLORER = OBSIDIAN.querySelector('#explorer');
|
179
|
+
const FILE_TREE = EXPLORER.querySelector(' #file-tree');
|
180
|
+
const sortedVaultFiles = sortObsidianVaultFiles(VAULT_FILES);
|
181
|
+
buildFileTree(FILE_TREE, sortedVaultFiles);
|
182
|
+
|
183
|
+
const collapseExpand = EXPLORER.querySelector('#vault-name #collapseExpand');
|
184
|
+
collapseExpand.innerHTML = DOMPurify.sanitize(getSVGIcon(Icon.ChevronExpand));
|
185
|
+
collapseExpand.addEventListener('click', () => {
|
186
|
+
expandOrCollapseFileTree = !expandOrCollapseFileTree;
|
187
|
+
if (expandOrCollapseFileTree)
|
188
|
+
EXPLORER.classList.add('hide-scrollbar');
|
189
|
+
buildFileTree(FILE_TREE, sortedVaultFiles, expandOrCollapseFileTree);
|
190
|
+
collapseExpand.innerHTML = DOMPurify.sanitize((expandOrCollapseFileTree ?
|
191
|
+
getSVGIcon(Icon.ChevronCollapse)
|
192
|
+
: getSVGIcon(Icon.ChevronExpand)));
|
193
|
+
});
|
194
|
+
|
195
|
+
EXPLORER.addEventListener('scroll', () => {
|
196
|
+
if (EXPLORER.scrollTop < 250) {
|
197
|
+
EXPLORER.classList.add('hide-scrollbar');
|
198
|
+
} else
|
199
|
+
EXPLORER.classList.remove('hide-scrollbar');
|
200
|
+
});
|
201
|
+
});
|
202
|
+
</script>
|