insights4you-jekyll-theme 0.2.0 → 0.2.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.
data/assets/js/theme.js CHANGED
@@ -1,31 +1,194 @@
1
- /**
2
- * demo-theme is specifically loaded right after the body and not deferred
3
- * to ensure we switch to the chosen dark/light theme as fast as possible.
4
- * This will prevent any flashes of the light theme (default) before switching.
5
- */
1
+ // Ensure jsyaml is available
2
+ /* eslint-disable no-undef */ // Disable undefined variable check for 'jsyaml'
3
+ if (typeof jsyaml === "undefined") {
4
+ throw new Error("js-yaml library is not loaded. Please include it in your project.");
5
+ }
6
+ /* eslint-enable no-undef */
6
7
 
7
8
  const THEME_STORAGE_KEY = "tablerTheme";
8
- const defaultTheme = "light";
9
- let selectedTheme;
9
+ const DEFAULT_THEME = "light";
10
10
 
11
- // https://stackoverflow.com/a/901144
12
- // Create a Proxy for URLSearchParams to simplify access to query parameters.
13
- // This allows us to use `params.key` instead of `params.get('key')`.
11
+ // Proxy to access URL parameters securely
14
12
  const params = new Proxy(new URLSearchParams(window.location.search), {
15
- get: (searchParams, prop) => searchParams.get(prop)
13
+ get: (searchParams, prop) => searchParams.get(prop),
16
14
  });
17
15
 
18
- if (params.theme) {
19
- localStorage.setItem(THEME_STORAGE_KEY, params.theme);
20
- selectedTheme = params.theme;
21
- } else {
22
- const storedTheme = localStorage.getItem(THEME_STORAGE_KEY);
23
- selectedTheme = storedTheme || defaultTheme;
16
+ // Validate theme values
17
+ function isValidTheme(theme) {
18
+ return ["light", "dark"].includes(theme);
19
+ }
20
+
21
+ // Apply the selected theme to the document
22
+ function applyTheme(theme) {
23
+ document.body.setAttribute("data-bs-theme", theme);
24
+
25
+ // Accessibility: Notify screen readers of theme change
26
+ const themeAnnouncement = document.getElementById("theme-announcement");
27
+ if (themeAnnouncement) {
28
+ themeAnnouncement.textContent = `Theme changed to ${theme}`;
29
+ }
30
+ }
31
+
32
+ // Save the selected theme to localStorage
33
+ function saveTheme(theme) {
34
+ localStorage.setItem(THEME_STORAGE_KEY, theme);
35
+ }
36
+
37
+ // Load the current theme based on priority: URL > localStorage > system preference > default
38
+ function loadTheme() {
39
+ let selectedTheme;
40
+
41
+ // Prioritize theme from URL (sanitize input to prevent XSS)
42
+ if (params.theme && isValidTheme(params.theme)) {
43
+ selectedTheme = params.theme;
44
+ saveTheme(selectedTheme);
45
+ } else {
46
+ // Use localStorage or system preference
47
+ const storedTheme = localStorage.getItem(THEME_STORAGE_KEY);
48
+ selectedTheme = isValidTheme(storedTheme)
49
+ ? storedTheme
50
+ : window.matchMedia("(prefers-color-scheme: dark)").matches
51
+ ? "dark"
52
+ : DEFAULT_THEME;
53
+ }
54
+
55
+ // Apply the selected theme
56
+ applyTheme(selectedTheme);
57
+ }
58
+
59
+ // Toggle between light and dark themes
60
+ /* eslint-disable no-unused-vars */ // Disable unused function check for 'toggleTheme'
61
+ function toggleTheme(theme) {
62
+ saveTheme(theme);
63
+ applyTheme(theme);
64
+ }
65
+ /* eslint-enable no-unused-vars */
66
+
67
+ // Load dynamic notifications
68
+ async function loadNotifications() {
69
+ const notificationsList = document.getElementById("notifications-list");
70
+ const badge = document.getElementById("notification-badge");
71
+
72
+ if (!notificationsList || !badge) return;
73
+
74
+ try {
75
+ // Fetch local and remote notifications
76
+ const [localNotifications, remoteNotifications] = await Promise.all([
77
+ getLocalNotifications(),
78
+ fetchRemoteNotifications(),
79
+ ]);
80
+
81
+ // Combine, sort, and render notifications
82
+ const allNotifications = combineAndSortNotifications(localNotifications, remoteNotifications);
83
+ renderNotifications(allNotifications, notificationsList, badge);
84
+ } catch (error) {
85
+ console.error("Error loading notifications:", error);
86
+ }
87
+ }
88
+
89
+ // Fetch local notifications from site data
90
+ function getLocalNotifications() {
91
+ try {
92
+ return Array.isArray(window.siteData?.notifications) ? window.siteData.notifications : [];
93
+ } catch (error) {
94
+ console.error("Error loading local notifications:", error);
95
+ return [];
96
+ }
97
+ }
98
+
99
+ // Fetch remote notifications from GitHub repository
100
+ async function fetchRemoteNotifications() {
101
+ try {
102
+ const response = await fetch(
103
+ "https://raw.githubusercontent.com/marciopaiva/insights4you-jekyll-theme/main/_data/notifications.yml"
104
+ );
105
+ if (!response.ok) throw new Error("Failed to fetch remote notifications");
106
+ const yamlText = await response.text();
107
+ /* eslint-disable no-undef */ // Disable undefined variable check for 'jsyaml'
108
+ const parsedData = jsyaml.load(yamlText); // Parse YAML data
109
+ /* eslint-enable no-undef */
110
+ return Array.isArray(parsedData) ? parsedData : [];
111
+ } catch (error) {
112
+ console.error("Error fetching remote notifications:", error);
113
+ return [];
114
+ }
115
+ }
116
+
117
+ // Combine and sort notifications by date
118
+ function combineAndSortNotifications(local, remote) {
119
+ return [...remote, ...local]
120
+ .filter((notification) => notification.date) // Ensure each notification has a valid date
121
+ .sort((a, b) => new Date(b.date) - new Date(a.date))
122
+ .slice(0, 5); // Display only the latest 5 notifications
123
+ }
124
+
125
+ // Render notifications in the DOM securely
126
+ function renderNotifications(notifications, listElement, badgeElement) {
127
+ badgeElement.textContent = notifications.length;
128
+
129
+ // Clear existing notifications
130
+ listElement.innerHTML = "";
131
+
132
+ // Append notifications using DOM manipulation for security
133
+ notifications.forEach((notification) => {
134
+ const item = createNotificationItem(notification);
135
+ listElement.appendChild(item);
136
+ });
137
+ }
138
+
139
+ // Create a single notification item securely
140
+ function createNotificationItem(notification) {
141
+ const container = document.createElement("div");
142
+ container.className = "list-group-item";
143
+
144
+ const row = document.createElement("div");
145
+ row.className = "row align-items-center";
146
+
147
+ const colAuto = document.createElement("div");
148
+ colAuto.className = "col-auto";
149
+ const statusDot = document.createElement("span");
150
+ statusDot.className = `status-dot bg-${notification.color || "blue"} d-block`;
151
+ colAuto.appendChild(statusDot);
152
+
153
+ const colText = document.createElement("div");
154
+ colText.className = "col text-truncate";
155
+ const title = document.createElement("a");
156
+ title.href = notification.link;
157
+ title.className = "text-body d-block";
158
+ title.textContent = notification.title || "Untitled Notification";
159
+ const description = document.createElement("div");
160
+ description.className = "text-secondary";
161
+ description.textContent = notification.description || "No description available";
162
+ const date = document.createElement("small");
163
+ date.className = "text-muted";
164
+ date.textContent = new Date(notification.date).toLocaleDateString();
165
+ colText.append(title, description, date);
166
+
167
+ const colActions = document.createElement("div");
168
+ colActions.className = "col-auto";
169
+ const actionLink = document.createElement("a");
170
+ actionLink.href = notification.link;
171
+ actionLink.className = "list-group-item-actions";
172
+ actionLink.target = "_blank";
173
+ const svgIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
174
+ svgIcon.setAttribute("class", "icon text-muted icon-2");
175
+ svgIcon.setAttribute("viewBox", "0 0 24 24");
176
+ svgIcon.setAttribute("stroke", "currentColor");
177
+ svgIcon.setAttribute("fill", "none");
178
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
179
+ path.setAttribute("d", "M12 17.75l-6.172 3.245l1.179 -6.873l-5 -4.867l6.9 -1l3.086 -6.253l3.086 6.253l6.9 1l-5 4.867l1.179 6.873z");
180
+ svgIcon.appendChild(path);
181
+ actionLink.appendChild(svgIcon);
182
+ colActions.appendChild(actionLink);
183
+
184
+ row.append(colAuto, colText, colActions);
185
+ container.appendChild(row);
186
+
187
+ return container;
24
188
  }
25
189
 
26
- // Apply the selected theme to the document body
27
- if (selectedTheme === "dark") {
28
- document.body.setAttribute("data-bs-theme", "dark");
29
- } else if (selectedTheme === "light") {
30
- document.body.setAttribute("data-bs-theme", "light");
31
- }
190
+ // Initialize theme and notifications when the page loads
191
+ document.addEventListener("DOMContentLoaded", () => {
192
+ loadTheme();
193
+ loadNotifications();
194
+ });
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: insights4you-jekyll-theme
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcio Paiva Barbosa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-23 00:00:00.000000000 Z
11
+ date: 2025-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -108,7 +108,7 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '1.50'
111
- description: "A sleek and modern Jekyll theme inspired by the [Tabler Admin Dashboard](https://github.com/tabler).
111
+ description: "A sleek and modern Jekyll theme inspired by the Tabler Admin Dashboard.
112
112
  \nThis theme offers a clean, professional, and responsive interface, making it ideal
113
113
  for developers, \ncontent creators, and businesses. Whether you're building documentation
114
114
  sites, admin panels, or \nproject showcases, this theme provides a minimal-effort
@@ -121,6 +121,12 @@ extra_rdoc_files: []
121
121
  files:
122
122
  - LICENSE
123
123
  - README.md
124
+ - _data/i4y-errors.json
125
+ - _data/i4y-icons.json
126
+ - _data/i4y-illustrations.json
127
+ - _data/i4y-social-media.yml
128
+ - _data/notifications.yml
129
+ - _includes/card/profile.html
124
130
  - _includes/footer.html
125
131
  - _includes/head.html
126
132
  - _includes/header-logo.html
@@ -133,6 +139,7 @@ files:
133
139
  - _layouts/default.html
134
140
  - _layouts/error.html
135
141
  - _layouts/home.html
142
+ - _layouts/post.html
136
143
  - assets/css/theme.min.css
137
144
  - assets/images/android-chrome-192x192.png
138
145
  - assets/images/android-chrome-512x512.png
@@ -140,6 +147,8 @@ files:
140
147
  - assets/images/favicon-16x16.png
141
148
  - assets/images/favicon-32x32.png
142
149
  - assets/images/favicon.ico
150
+ - assets/images/i4y-logo.jpg
151
+ - assets/images/preview-dark.png
143
152
  - assets/images/site.webmanifest
144
153
  - assets/js/theme.js
145
154
  homepage: https://github.com/marciopaiva/insights4you-jekyll-theme
@@ -170,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
179
  - !ruby/object:Gem::Version
171
180
  version: '0'
172
181
  requirements: []
173
- rubygems_version: 3.3.26
182
+ rubygems_version: 3.3.7
174
183
  signing_key:
175
184
  specification_version: 4
176
185
  summary: A sleek and modern Jekyll theme inspired by the Tabler Admin Dashboard.