iobroker.staticsfilefolder 0.0.10 → 0.0.12

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.
package/io-package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "staticsfilefolder",
4
- "version": "0.0.10",
4
+ "version": "0.0.12",
5
5
  "news": {
6
+ "0.0.12": {
7
+ "en": "Automatically detect ioBroker system language. Added German and Russian translations."
8
+ },
9
+ "0.0.11": {
10
+ "en": "Added multi-language support (English and Azerbaijani) with English as the default."
11
+ },
6
12
  "0.0.10": {
7
13
  "en": "Updated README.md with detailed instructions for instance configuration settings."
8
14
  },
package/lib/web.js CHANGED
@@ -12,6 +12,7 @@ class ExtensionExample {
12
12
  */
13
13
  constructor(server, webSettings, adapter, instanceSettings, app) {
14
14
  this.app = app;
15
+ this.adapter = adapter;
15
16
  this.config = instanceSettings ? instanceSettings.native : {};
16
17
  this.config.route = this.config.route || "demo";
17
18
 
@@ -27,6 +28,17 @@ class ExtensionExample {
27
28
  this.app.use("/" + this.config.route + "/files", express.static(this.config.dirname));
28
29
  }
29
30
 
31
+ // API Endpoint for configuration and system language
32
+ this.app.get("/" + this.config.route + "/api/config", async (req, res) => {
33
+ try {
34
+ const systemConfig = await this.adapter.getForeignObjectAsync("system.config");
35
+ const systemLanguage = (systemConfig && systemConfig.common && systemConfig.common.language) || "en";
36
+ res.json({ success: true, language: systemLanguage });
37
+ } catch (e) {
38
+ res.json({ success: false, language: "en" });
39
+ }
40
+ });
41
+
30
42
  // API Endpoint for listing files
31
43
  this.app.get("/" + this.config.route + "/api/list", (req, res) => {
32
44
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.staticsfilefolder",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "Static Files Folder",
5
5
  "author": {
6
6
  "name": "gokturk413",
package/www/app.js CHANGED
@@ -16,6 +16,7 @@ const btnBack = document.getElementById('btn-back');
16
16
  const btnForward = document.getElementById('btn-forward');
17
17
  const btnHome = document.getElementById('btn-home');
18
18
  const btnTheme = document.getElementById('btn-theme');
19
+ const btnLang = document.getElementById('btn-lang');
19
20
  const searchBox = document.getElementById('search-box');
20
21
  const typeFilter = document.getElementById('type-filter');
21
22
  const sortSelect = document.getElementById('sort-select');
@@ -33,11 +34,176 @@ const modalBtnDownload = document.getElementById('modal-btn-download');
33
34
  let currentOpenItem = null;
34
35
  let currentFilesList = [];
35
36
 
37
+ // Localization Settings
38
+ const translations = {
39
+ en: {
40
+ back: "← Back",
41
+ forward: "Forward →",
42
+ home: "Home",
43
+ searchPlaceholder: "Search...",
44
+ allTypes: "All Types",
45
+ sortAz: "Sort A-Z",
46
+ sortZa: "Sort Z-A",
47
+ sortNewest: "Newest First",
48
+ sortOldest: "Oldest First",
49
+ title: "Omni Reports",
50
+ loadingPdf: "Loading PDF...",
51
+ loadingExcel: "Loading Excel...",
52
+ loadingWord: "Loading Word...",
53
+ emptyFolder: "This folder is empty.",
54
+ root: "Root",
55
+ themeTooltip: "Toggle Dark/Light Mode",
56
+ langTooltip: "Change Language / Dili Dəyiş",
57
+ modalTitle: "File Viewer",
58
+ modalBack: "← Back",
59
+ modalForward: "Next →",
60
+ modalPrint: "🖨️ Print / PDF",
61
+ modalDownload: "📥 Download",
62
+ todayBadge: "Today",
63
+ errorLoading: "An error occurred: "
64
+ },
65
+ az: {
66
+ back: "← Geri",
67
+ forward: "İrəli →",
68
+ home: "Ev (Home)",
69
+ searchPlaceholder: "Axtarış...",
70
+ allTypes: "Bütün Tiplər",
71
+ sortAz: "A-Z Sırala",
72
+ sortZa: "Z-A Sırala",
73
+ sortNewest: "Ən Yeni",
74
+ sortOldest: "Ən Köhnə",
75
+ title: "Omni Hesabatlar",
76
+ loadingPdf: "PDF Yüklənir...",
77
+ loadingExcel: "Excel Yüklənir...",
78
+ loadingWord: "Word Yüklənir...",
79
+ emptyFolder: "Bu qovluq boşdur.",
80
+ root: "Kök",
81
+ themeTooltip: "Gecə/Gündüz Rejimi",
82
+ langTooltip: "Change Language / Dili Dəyiş",
83
+ modalTitle: "Fayl Baxışı",
84
+ modalBack: "← Geri",
85
+ modalForward: "İrəli →",
86
+ modalPrint: "🖨️ Çap / PDF",
87
+ modalDownload: "📥 Yüklə",
88
+ todayBadge: "Bu gün",
89
+ errorLoading: "Xəta baş verdi: "
90
+ },
91
+ de: {
92
+ back: "← Zurück",
93
+ forward: "Weiter →",
94
+ home: "Startseite",
95
+ searchPlaceholder: "Suchen...",
96
+ allTypes: "Alle Typen",
97
+ sortAz: "A-Z sortieren",
98
+ sortZa: "Z-A sortieren",
99
+ sortNewest: "Neueste zuerst",
100
+ sortOldest: "Älteste zuerst",
101
+ title: "Omni Berichte",
102
+ loadingPdf: "PDF wird geladen...",
103
+ loadingExcel: "Excel wird geladen...",
104
+ loadingWord: "Word wird geladen...",
105
+ emptyFolder: "Dieser Ordner ist leer.",
106
+ root: "Hauptverzeichnis",
107
+ themeTooltip: "Dunkel/Hell-Modus umschalten",
108
+ langTooltip: "Sprache ändern",
109
+ modalTitle: "Dateibetrachter",
110
+ modalBack: "← Zurück",
111
+ modalForward: "Weiter →",
112
+ modalPrint: "🖨️ Drucken / PDF",
113
+ modalDownload: "📥 Herunterladen",
114
+ todayBadge: "Heute",
115
+ errorLoading: "Ein Fehler ist aufgetreten: "
116
+ },
117
+ ru: {
118
+ back: "← Назад",
119
+ forward: "Вперед →",
120
+ home: "Главная",
121
+ searchPlaceholder: "Поиск...",
122
+ allTypes: "Все типы",
123
+ sortAz: "Сортировка А-Я",
124
+ sortZa: "Сортировка Я-А",
125
+ sortNewest: "Сначала новые",
126
+ sortOldest: "Сначала старые",
127
+ title: "Omni Отчеты",
128
+ loadingPdf: "Загрузка PDF...",
129
+ loadingExcel: "Загрузка Excel...",
130
+ loadingWord: "Загрузка Word...",
131
+ emptyFolder: "Эта папка пуста.",
132
+ root: "Корень",
133
+ themeTooltip: "Переключить тему",
134
+ langTooltip: "Изменить язык",
135
+ modalTitle: "Просмотр файла",
136
+ modalBack: "← Назад",
137
+ modalForward: "Вперед →",
138
+ modalPrint: "🖨️ Печать / PDF",
139
+ modalDownload: "📥 Скачать",
140
+ todayBadge: "Сегодня",
141
+ errorLoading: "Произошла ошибка: "
142
+ }
143
+ };
144
+
145
+ const supportedLangs = ['en', 'az', 'de', 'ru'];
146
+ let currentLang = 'en'; // default English
147
+
36
148
  // Setup PDF.js worker
37
149
  if (window.pdfjsLib) {
38
150
  window.pdfjsLib.GlobalWorkerOptions.workerSrc = 'libs/pdfjs/pdf.worker.min.mjs';
39
151
  }
40
152
 
153
+ // Language Initialization
154
+ async function initLang() {
155
+ let savedLang = localStorage.getItem('staticsfilefolder_lang');
156
+ if (!savedLang) {
157
+ try {
158
+ const res = await fetch(`${BASE_ROUTE}/api/config`);
159
+ const data = await res.json();
160
+ if (data && data.language) {
161
+ savedLang = data.language;
162
+ }
163
+ } catch (e) {
164
+ // ignore
165
+ }
166
+ }
167
+
168
+ currentLang = savedLang || navigator.language.split('-')[0] || 'en';
169
+ if (!translations[currentLang]) {
170
+ currentLang = 'en';
171
+ }
172
+ applyTranslations();
173
+ }
174
+
175
+ function toggleLang() {
176
+ let index = supportedLangs.indexOf(currentLang);
177
+ index = (index + 1) % supportedLangs.length;
178
+ currentLang = supportedLangs[index];
179
+ localStorage.setItem('staticsfilefolder_lang', currentLang);
180
+ applyTranslations();
181
+ }
182
+
183
+ function applyTranslations() {
184
+ const t = translations[currentLang];
185
+
186
+ // Update elements with data-i18n attribute
187
+ document.querySelectorAll('[data-i18n]').forEach(el => {
188
+ const key = el.getAttribute('data-i18n');
189
+ if (t[key]) {
190
+ if (el.tagName === 'INPUT') {
191
+ el.placeholder = t[key];
192
+ } else {
193
+ el.textContent = t[key];
194
+ }
195
+ }
196
+ });
197
+
198
+ // Update search placeholder and other specific inputs
199
+ searchBox.placeholder = t.searchPlaceholder;
200
+ btnLang.textContent = `🌐 ${currentLang.toUpperCase()}`;
201
+
202
+ // Refresh UI to update dynamic content like dates/breadcrumbs
203
+ updateBreadcrumb();
204
+ renderItems();
205
+ }
206
+
41
207
  // Theme Initialization
42
208
  function initTheme() {
43
209
  const savedTheme = localStorage.getItem('staticsfilefolder_theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
@@ -56,6 +222,7 @@ function toggleTheme() {
56
222
  // Initialize
57
223
  document.addEventListener('DOMContentLoaded', () => {
58
224
  initTheme();
225
+ initLang();
59
226
  loadPath('');
60
227
 
61
228
  // Event Listeners
@@ -63,6 +230,7 @@ document.addEventListener('DOMContentLoaded', () => {
63
230
  btnForward.addEventListener('click', goForward);
64
231
  btnHome.addEventListener('click', () => navigateTo(''));
65
232
  btnTheme.addEventListener('click', toggleTheme);
233
+ btnLang.addEventListener('click', toggleLang);
66
234
 
67
235
  searchBox.addEventListener('input', renderItems);
68
236
  typeFilter.addEventListener('change', renderItems);
@@ -132,13 +300,14 @@ function updateNavButtons() {
132
300
  }
133
301
 
134
302
  function updateBreadcrumb() {
303
+ const rootName = translations[currentLang]?.root || 'Root';
135
304
  if (!currentPath) {
136
- breadcrumbEl.innerHTML = '<span><i class="icon">🏠</i> Kök Qovluq</span>';
305
+ breadcrumbEl.innerHTML = `<span><i class="icon">🏠</i> ${rootName}</span>`;
137
306
  return;
138
307
  }
139
308
 
140
309
  const parts = currentPath.split('/');
141
- let html = '<span data-path="">🏠 Kök</span>';
310
+ let html = `<span data-path="">🏠 ${rootName}</span>`;
142
311
  let buildPath = '';
143
312
 
144
313
  parts.forEach((part, index) => {
@@ -216,7 +385,8 @@ function renderItems() {
216
385
  const isToday = itemDate.setHours(0,0,0,0) === today;
217
386
  if (isToday) div.classList.add('is-today');
218
387
 
219
- const dateStr = new Date(item.mtime).toLocaleString('az-AZ');
388
+ const locale = currentLang === 'en' ? 'en-US' : 'az-AZ';
389
+ const dateStr = new Date(item.mtime).toLocaleString(locale);
220
390
 
221
391
  let sizeStr = '';
222
392
  if (!item.isDirectory) {
@@ -224,11 +394,13 @@ function renderItems() {
224
394
  else sizeStr = (item.size / 1024).toFixed(2) + ' KB';
225
395
  }
226
396
 
397
+ const todayText = translations[currentLang]?.todayBadge || 'Today';
398
+
227
399
  div.innerHTML = `
228
400
  <div class="file-icon">${icon}</div>
229
401
  <div class="file-name" title="${item.name}">${item.name}</div>
230
402
  <div class="file-meta">${dateStr}<br>${sizeStr}</div>
231
- ${isToday ? '<div class="today-badge">Bu gün</div>' : ''}
403
+ ${isToday ? `<div class="today-badge">${todayText}</div>` : ''}
232
404
  `;
233
405
 
234
406
  div.addEventListener('click', () => handleItemClick(item));
@@ -352,7 +524,7 @@ function downloadCurrentFile() {
352
524
 
353
525
  async function openPdf(url) {
354
526
  viewerModal.style.display = 'block';
355
- viewerBody.innerHTML = '<h2>PDF Yüklənir...</h2>';
527
+ viewerBody.innerHTML = `<h2>${translations[currentLang]?.loadingPdf || 'Loading PDF...'}</h2>`;
356
528
 
357
529
  try {
358
530
  const loadingTask = window.pdfjsLib.getDocument({ url: url });
@@ -379,13 +551,14 @@ async function openPdf(url) {
379
551
  await page.render(renderContext).promise;
380
552
  }
381
553
  } catch (e) {
382
- viewerBody.innerHTML = `<h2 style="color:red">Xəta baş verdi: ${e.message}</h2>`;
554
+ const errorText = translations[currentLang]?.errorLoading || 'An error occurred: ';
555
+ viewerBody.innerHTML = `<h2 style="color:red">${errorText}${e.message}</h2>`;
383
556
  }
384
557
  }
385
558
 
386
559
  async function openExcel(url) {
387
560
  viewerModal.style.display = 'block';
388
- viewerBody.innerHTML = '<h2>Excel Yüklənir...</h2>';
561
+ viewerBody.innerHTML = `<h2>${translations[currentLang]?.loadingExcel || 'Loading Excel...'}</h2>`;
389
562
 
390
563
  try {
391
564
  const res = await fetch(url);
@@ -407,13 +580,14 @@ async function openExcel(url) {
407
580
  if(table) table.className = 'sheetjs-table';
408
581
 
409
582
  } catch (e) {
410
- viewerBody.innerHTML = `<h2 style="color:red">Xəta baş verdi: ${e.message}</h2>`;
583
+ const errorText = translations[currentLang]?.errorLoading || 'An error occurred: ';
584
+ viewerBody.innerHTML = `<h2 style="color:red">${errorText}${e.message}</h2>`;
411
585
  }
412
586
  }
413
587
 
414
588
  async function openWord(url) {
415
589
  viewerModal.style.display = 'block';
416
- viewerBody.innerHTML = '<h2>Word Sənədi Yüklənir...</h2>';
590
+ viewerBody.innerHTML = `<h2>${translations[currentLang]?.loadingWord || 'Loading Word...'}</h2>`;
417
591
 
418
592
  try {
419
593
  const res = await fetch(url);
@@ -425,6 +599,7 @@ async function openWord(url) {
425
599
  ${result.value}
426
600
  </div>`;
427
601
  } catch (e) {
428
- viewerBody.innerHTML = `<h2 style="color:red">Xəta baş verdi: ${e.message}</h2>`;
602
+ const errorText = translations[currentLang]?.errorLoading || 'An error occurred: ';
603
+ viewerBody.innerHTML = `<h2 style="color:red">${errorText}${e.message}</h2>`;
429
604
  }
430
605
  }
package/www/index.html CHANGED
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html lang="az">
2
+ <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -16,31 +16,32 @@
16
16
  <div class="app-container">
17
17
  <!-- Header & Toolbar -->
18
18
  <header class="app-header">
19
- <div class="header-title">Omni Reports</div>
19
+ <div class="header-title" id="app-title" data-i18n="title">Omni Reports</div>
20
20
 
21
21
  <div class="toolbar">
22
- <button id="btn-back" class="nav-btn" disabled>&larr; Geri</button>
23
- <button id="btn-forward" class="nav-btn" disabled>İrəli &rarr;</button>
24
- <button id="btn-home" class="nav-btn">Evə (Home)</button>
22
+ <button id="btn-back" class="nav-btn" data-i18n="back" disabled>&larr; Back</button>
23
+ <button id="btn-forward" class="nav-btn" data-i18n="forward" disabled>Forward &rarr;</button>
24
+ <button id="btn-home" class="nav-btn" data-i18n="home">Home</button>
25
25
 
26
26
  <div class="breadcrumb" id="breadcrumb">
27
27
  <!-- Path will be injected here -->
28
28
  </div>
29
29
 
30
30
  <div class="filters">
31
- <button id="btn-theme" class="nav-btn" title="Gecə/Gündüz Rejimi">🌙</button>
32
- <input type="text" id="search-box" placeholder="Axtarış...">
31
+ <button id="btn-lang" class="nav-btn" title="Change Language / Dili Dəyiş">🌐 AZ</button>
32
+ <button id="btn-theme" class="nav-btn" title="Toggle Dark/Light Mode">🌙</button>
33
+ <input type="text" id="search-box" placeholder="Search...">
33
34
  <select id="type-filter">
34
- <option value="all">Bütün Tiplər</option>
35
+ <option value="all" data-i18n="allTypes">All Types</option>
35
36
  <option value=".pdf">PDF</option>
36
37
  <option value=".xlsx">Excel</option>
37
38
  <option value=".docx">Word</option>
38
39
  </select>
39
40
  <select id="sort-select">
40
- <option value="name-asc">A-Z Sırala</option>
41
- <option value="name-desc">Z-A Sırala</option>
42
- <option value="date-desc">Ən Yeni</option>
43
- <option value="date-asc">Ən Köhnə</option>
41
+ <option value="name-asc" data-i18n="sortAz">Sort A-Z</option>
42
+ <option value="name-desc" data-i18n="sortZa">Sort Z-A</option>
43
+ <option value="date-desc" data-i18n="sortNewest">Newest First</option>
44
+ <option value="date-asc" data-i18n="sortOldest">Oldest First</option>
44
45
  </select>
45
46
  </div>
46
47
  </div>
@@ -51,8 +52,8 @@
51
52
  <div id="file-list" class="file-list">
52
53
  <!-- File items will be injected here -->
53
54
  </div>
54
- <div id="empty-state" class="empty-state" style="display: none;">
55
- Fayl tapılmadı
55
+ <div id="empty-state" class="empty-state" data-i18n="emptyFolder" style="display: none;">
56
+ This folder is empty.
56
57
  </div>
57
58
  </main>
58
59
  </div>
@@ -61,12 +62,12 @@
61
62
  <div id="viewer-modal" class="modal">
62
63
  <div class="modal-content">
63
64
  <header class="modal-header">
64
- <div class="modal-title" id="modal-title">Fayl Baxışı</div>
65
+ <div class="modal-title" id="modal-title" data-i18n="modalTitle">File Viewer</div>
65
66
  <div class="modal-toolbar">
66
- <button id="modal-btn-prev" class="nav-btn" title="Əvvəlki">&larr; Geri</button>
67
- <button id="modal-btn-next" class="nav-btn" title="Növbəti">İrəli &rarr;</button>
68
- <button id="modal-btn-print" class="nav-btn" title="Çap et / PDF olaraq saxla">🖨️ Çap / PDF</button>
69
- <button id="modal-btn-download" class="nav-btn" title="Faylı Yüklə / Export">📥 Yüklə</button>
67
+ <button id="modal-btn-prev" class="nav-btn" data-i18n="modalBack" title="Previous">&larr; Back</button>
68
+ <button id="modal-btn-next" class="nav-btn" data-i18n="modalForward" title="Next">Next &rarr;</button>
69
+ <button id="modal-btn-print" class="nav-btn" data-i18n="modalPrint" title="Print / Save as PDF">🖨️ Print / PDF</button>
70
+ <button id="modal-btn-download" class="nav-btn" data-i18n="modalDownload" title="Download File">📥 Download</button>
70
71
  <span id="close-modal" class="close-button">&times;</span>
71
72
  </div>
72
73
  </header>