PageSourceSearch

https://webnotepad.github.io/components/sidebar.js

js webnotepad.github.io collected 2026-10-03 10:09:50 UTC 17,773 bytes, 555 lines download raw bytes

1/**
2 * WebNotePad — sidebar.js
3 * Injects a fixed dynamic sidebar for the 15 productive tools
4 * Theme: Editorial / Ink-on-paper aesthetic
5 * Updated: Category-wise organization with attention-grabbing pulsing trigger
6 *          + 300x250 banner ad slot at the top of the sidebar
7 *          + 2-column grid layout for tools
8 */
9
10(function () {
11  // 1. Array list of tools organized by categories
12  const categories = [
13    {
14      name: "📝 Writing & Note-Taking",
15      tools: [
16        { name: "Notepad", icon: "📝", url: "/#notepad", desc: "Write, edit and auto-save notes instantly." },
17        { name: "Diary", icon: "📖", url: "/diary", desc: "Keep a private daily journal with dated entries." },
18        { name: "Focus Writer", icon: "🎯", url: "/focus-writer", desc: "Minimalist writing mode with a zen focus." },
19        { name: "Typing Test", icon: "⌨️", url: "/typing-test", desc: "Test your typing speed and accuracy in WPM." },
20        { name: "WordPad", icon: "📝", url: "/wordpad-online", desc: "Rich text editor with fonts, colors, and export." }
21      ]
22    },
23    {
24      name: "📊 Text Analysis & Manipulation",
25      tools: [
26        { name: "Case Converter", icon: "🔤", url: "/case-converter", desc: "Transform text to uppercase, lowercase, etc." },
27        { name: "Word Counter", icon: "📊", url: "/word-counter", desc: "Count words, characters, and sentences." },
28        { name: "Readability Analyzer", icon: "📊", url: "/readability", desc: "Check reading ease and complexity scores." },
29        { name: "Word Shuffler", icon: "🔀", url: "/word-shuffler", desc: "Randomize word order in any text." },
30        { name: "Special Character Converter", icon: "🔤", url: "/special-character-converter", desc: "Convert umlauts, diacritics, and special characters." }
31      ]
32    },
33    {
34      name: "🧠 Idea Organization & Visualization",
35      tools: [
36        { name: "MindMap", icon: "🧠", url: "/mindmap", desc: "Visualize ideas and brainstorm interactively." },
37        { name: "List Maker", icon: "✅", url: "/list-maker", desc: "Create checklists and to-dos with ease." }
38      ]
39    },
40    {
41      name: "🎲 Creativity & Randomization",
42      tools: [
43        
44        { name: "Random Text", icon: "🎲", url: "/random-text", desc: "Generate placeholder paragraphs or words." },
45        { name: "Word Cloud Generator", icon: "☁️", url: "/word-cloud", desc: "Turn text into a beautiful visual word cloud." },
46        { name: "Decision Maker", icon: "⚖️", url: "/choice-maker", desc: "Spin a wheel or flip a coin to decide." },
47        { name: "Random Name Generator", icon: "👤", url: "/random-name-generator", desc: "Generate random first, last, and full names." }
48      ]
49    },
50    {
51      name: "🔍 Word & Puzzle Helpers",
52      tools: [
53        { name: "Word Finder", icon: "🔍", url: "/word-finder", desc: "Find words, solve anagrams, and discover terms." },
54        { name: "Crossword Solver", icon: "🔠", url: "/crossword-solver", desc: "Clue helper, word finder, and puzzle help." },
55        { name: "Word Search Solver", icon: "🕵🏻", url: "/word-search-solver", desc: "Word search solver, and word puzzle solver." }
56      ]
57    },
58    {
59      name: "⏳ Productivity & Habit Management",
60      tools: [
61        { name: "Pomodoro Timer", icon: "🍅", url: "/pomodoro-timer", desc: "Stay focused with customizable intervals." },
62        { name: "Habit Tracker", icon: "📅", url: "/habit-tracker", desc: "Build streaks and track daily habits." }
63      ]
64    },
65    {
66      name: "😊 Fun & Utilities",
67      tools: [
68        { name: "Emoji Picker", icon: "🙂", url: "/emoji-picker", desc: "Pick emojis, and copy emojis." }
69      ]
70    }
71  ];
72
73  // Flatten tools for any potential use
74  const allTools = categories.flatMap(cat => cat.tools);
75
76  // 2. Inject CSS Styles with category enhancements and pulsing keyframes
77  const cssStyles = `
78    /* Floating Launch Trigger Button */
79    .tools-floating-trigger {
80      position: fixed;
81      bottom: 120px;
82      right: 24px;
83      z-index: 9999;
84      width: 52px;
85      height: 52px;
86      background: var(--ink);
87      color: var(--paper);
88      border-radius: 50%;
89      display: flex;
90      align-items: center;
91      justify-content: center;
92      font-size: 1.4rem;
93      box-shadow: var(--shadow);
94      cursor: pointer;
95      border: 1px solid var(--paper-edge);
96      transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), background 0.2s ease, color 0.2s ease, box-shadow 0.3s ease;
97      
98      /* Attention Grabber Pulse */
99      animation: attentionPulse 2.5s infinite cubic-bezier(0.4, 0, 0.2, 1);
100    }
101    body.dark .tools-floating-trigger {
102      background: var(--accent);
103      color: var(--white);
104    }
105    .tools-floating-trigger:hover {
106      transform: scale(1.08) rotate(15deg);
107      background: var(--accent);
108      color: var(--white);
109      animation-play-state: paused; /* Pause pulsing while user hovers */
110      box-shadow: 0 8px 24px rgba(196, 86, 42, 0.3);
111    }
112    .tools-floating-trigger.active {
113      transform: scale(0.9) rotate(-90deg);
114      background: var(--paper-warm);
115      color: var(--ink);
116      animation: none; /* Kill animation when the sidebar is open */
117      box-shadow: none;
118    }
119
120    /* Fixed Sidebar Layout Container */
121    .tools-fixed-sidebar {
122      position: fixed;
123      top: 0;
124      right: -340px;
125      width: 320px;
126      height: 100vh;
127      background: var(--paper);
128      border-left: 1px solid var(--paper-edge);
129      box-shadow: var(--shadow-lg);
130      z-index: 10000;
131      display: flex;
132      flex-direction: column;
133      transition: right 0.4s cubic-bezier(0.4, 0, 0.2, 1);
134    }
135    .tools-fixed-sidebar.open {
136      right: 0;
137    }
138
139    /* Dimmed Background Backdrop Overlay */
140    .tools-sidebar-overlay {
141      position: fixed;
142      top: 0;
143      left: 0;
144      width: 100vw;
145      height: 100vh;
146      background: rgba(26, 26, 46, 0.4);
147      backdrop-filter: blur(4px);
148      z-index: 9999;
149      opacity: 0;
150      pointer-events: none;
151      transition: opacity 0.3s ease;
152    }
153    body.dark .tools-sidebar-overlay {
154      background: rgba(0, 0, 0, 0.6);
155    }
156    .tools-sidebar-overlay.visible {
157      opacity: 1;
158      pointer-events: auto;
159    }
160
161    /* Sidebar Header Details */
162    .tools-sb-header {
163      padding: 20px 24px;
164      border-bottom: 1px solid var(--paper-edge);
165      display: flex;
166      align-items: center;
167      justify-content: space-between;
168      background: var(--paper-warm);
169      flex-shrink: 0;
170    }
171    .tools-sb-header h2 {
172      font-family: var(--font-display);
173      font-size: 1.25rem;
174      font-weight: 700;
175      color: var(--ink);
176    }
177    .tools-sb-header h2 em {
178      font-style: italic;
179      color: var(--accent);
180    }
181    .tools-sb-close {
182      width: 32px;
183      height: 32px;
184      font-size: 1rem;
185      color: var(--ink-muted);
186      border-radius: var(--radius);
187      display: flex;
188      align-items: center;
189      justify-content: center;
190      cursor: pointer;
191      transition: all var(--transition);
192      background: none;
193      border: none;
194    }
195    .tools-sb-close:hover {
196      color: var(--ink);
197      background: var(--paper-edge);
198    }
199
200    /* ---- Ad Slot (300x250) ---- */
201    .tools-sb-ad {
202      flex-shrink: 0;
203      padding: 14px 16px 10px;
204      border-bottom: 1px solid var(--paper-edge);
205      background: var(--paper);
206      display: flex;
207      flex-direction: column;
208      align-items: center;
209      gap: 6px;
210    }
211    .tools-sb-ad-label {
212      font-family: var(--font-display);
213      font-size: 0.6rem;
214      font-weight: 700;
215      letter-spacing: 0.12em;
216      text-transform: uppercase;
217      color: var(--ink-muted);
218      opacity: 0.55;
219      align-self: flex-start;
220    }
221    .tools-sb-ad-frame {
222      width: 300px;
223      max-width: 100%;
224      height: 250px;
225      display: flex;
226      align-items: center;
227      justify-content: center;
228      overflow: hidden;
229      border-radius: var(--radius);
230      background: var(--paper-warm);
231      border: 1px solid var(--paper-edge);
232    }
233    .tools-sb-ad-frame iframe,
234    .tools-sb-ad-frame img,
235    .tools-sb-ad-frame > div {
236      max-width: 100%;
237      border: 0;
238      display: block;
239    }
240
241    /* Scrollable items menu wrapper — 2 columns */
242    .tools-sb-body {
243      flex: 1;
244      overflow-y: auto;
245      padding: 12px 16px 24px;
246      display: grid;
247      grid-template-columns: 1fr 1fr;
248      gap: 8px;
249      align-content: start;
250    }
251
252    /* Category Section Headers — span both columns */
253    .tools-sb-category {
254      grid-column: 1 / -1;
255      font-family: var(--font-display);
256      font-size: 0.68rem;
257      font-weight: 700;
258      text-transform: uppercase;
259      letter-spacing: 0.06em;
260      color: var(--ink-muted);
261      padding: 14px 4px 6px 4px;
262      border-bottom: 1px solid var(--paper-edge);
263      margin-top: 4px;
264      opacity: 0.7;
265    }
266    .tools-sb-category:first-of-type {
267      padding-top: 6px;
268    }
269
270    /* Category item count badge */
271    .tools-sb-category-count {
272      font-size: 0.6rem;
273      font-weight: 400;
274      color: var(--ink-muted);
275      opacity: 0.5;
276      margin-left: 6px;
277    }
278
279    /* Single Tool Items Card — compact for 2-column grid */
280    .tools-sb-item {
281      display: flex;
282      flex-direction: column;
283      align-items: center;
284      text-align: center;
285      gap: 8px;
286      padding: 14px 8px 12px;
287      border-radius: var(--radius);
288      border: 1px solid var(--paper-edge);
289      background: var(--paper-warm);
290      transition: all var(--transition);
291      opacity: 0;
292      transform: translateY(10px);
293      text-decoration: none;
294      cursor: pointer;
295      min-width: 0;
296    }
297    .tools-fixed-sidebar.open .tools-sb-item {
298      animation: slideInItem 0.35s cubic-bezier(0.4, 0, 0.2, 1) forwards;
299    }
300    .tools-sb-item:hover {
301      background: var(--paper);
302      border-color: var(--accent);
303      transform: translateY(-2px);
304      box-shadow: var(--shadow-sm);
305    }
306    .tools-sb-item-icon {
307      font-size: 1.4rem;
308      width: 40px;
309      height: 40px;
310      background: var(--paper);
311      border-radius: var(--radius);
312      display: flex;
313      align-items: center;
314      justify-content: center;
315      border: 1px solid var(--paper-edge);
316      transition: background var(--transition), border-color var(--transition);
317      flex-shrink: 0;
318    }
319    .tools-sb-item:hover .tools-sb-item-icon {
320      background: var(--accent-pale);
321      border-color: var(--accent);
322    }
323    body.dark .tools-sb-item:hover .tools-sb-item-icon {
324      background: rgba(196,86,42,0.15);
325    }
326    .tools-sb-item-details {
327      flex: 1;
328      min-width: 0;
329      width: 100%;
330    }
331    .tools-sb-item-name {
332      font-size: 0.78rem;
333      font-weight: 600;
334      color: var(--ink);
335      margin-bottom: 2px;
336      line-height: 1.2;
337      word-break: break-word;
338    }
339    .tools-sb-item-desc {
340      font-size: 0.65rem;
341      color: var(--ink-muted);
342      line-height: 1.25;
343      display: -webkit-box;
344      -webkit-line-clamp: 2;
345      -webkit-box-orient: vertical;
346      overflow: hidden;
347    }
348
349    /* Scrollbar styling */
350    .tools-sb-body::-webkit-scrollbar {
351      width: 4px;
352    }
353    .tools-sb-body::-webkit-scrollbar-track {
354      background: transparent;
355    }
356    .tools-sb-body::-webkit-scrollbar-thumb {
357      background: var(--paper-edge);
358      border-radius: 4px;
359    }
360
361    /* Keyframe Animations */
362    @keyframes slideInItem {
363      to {
364        opacity: 1;
365        transform: translateY(0);
366      }
367    }
368
369    /* Elegant, Organic Attention Pulse Animation */
370    @keyframes attentionPulse {
371      0% {
372        transform: scale(1);
373        box-shadow: 0 0 0 0 rgba(196, 86, 42, 0.4), var(--shadow);
374      }
375      50% {
376        transform: scale(1.08);
377        box-shadow: 0 0 0 12px rgba(196, 86, 42, 0), var(--shadow);
378      }
379      100% {
380        transform: scale(1);
381        box-shadow: 0 0 0 0 rgba(196, 86, 42, 0), var(--shadow);
382      }
383    }
384
385    /* Responsive adjustments */
386    @media (max-width: 480px) {
387      .tools-fixed-sidebar {
388        width: 100%;
389        right: -100%;
390      }
391      .tools-fixed-sidebar.open {
392        right: 0;
393      }
394      .tools-sb-header h2 {
395        font-size: 1rem;
396      }
397      .tools-sb-body {
398        padding: 10px 12px 20px;
399        gap: 6px;
400      }
401      .tools-sb-item {
402        padding: 10px 6px 8px;
403        gap: 6px;
404      }
405      .tools-sb-item-icon {
406        width: 34px;
407        height: 34px;
408        font-size: 1.2rem;
409      }
410      .tools-sb-item-name {
411        font-size: 0.72rem;
412      }
413      .tools-sb-item-desc {
414        font-size: 0.6rem;
415      }
416      .tools-sb-ad {
417        padding: 12px 10px 8px;
418      }
419      .tools-sb-ad-frame {
420        height: auto;
421        min-height: 250px;
422      }
423    }
424  `;
425
426  // 3. Inject styles into document head
427  const styleEl = document.createElement("style");
428  styleEl.textContent = cssStyles;
429  document.head.appendChild(styleEl);
430
431  // 4. Generate the complete DOM structural markup dynamically
432  const rootContainer = document.getElementById("tools-sidebar-root");
433  if (!rootContainer) return;
434
435  // Render the floating toggle switch, backdrop container, sidebar dashboard, and ad slot
436  rootContainer.innerHTML = `
437    <div class="tools-sidebar-overlay" id="toolsSidebarOverlay"></div>
438    <div class="tools-floating-trigger" id="toolsSidebarTrigger" title="Explore Toolkit" aria-label="Toggle structural toolkit">🧰</div>
439    <aside class="tools-fixed-sidebar" id="toolsFixedSidebar" aria-label="WebNotepad Toolkit Sidebar">
440      <div class="tools-sb-header">
441        <h2>WebNotepad <em>Toolkit</em></h2>
442        <button class="tools-sb-close" id="toolsSidebarClose" aria-label="Close toolkit">✕</button>
443      </div>
444
445      <!-- 300x250 Banner Ad Slot -->
446      <div class="tools-sb-ad" id="toolsSidebarAd">
447        <span class="tools-sb-ad-label">Advertisement</span>
448        <div class="tools-sb-ad-frame" id="toolsSidebarAdFrame"></div>
449      </div>
450
451      <div class="tools-sb-body" id="toolsSidebarBody"></div>
452    </aside>
453  `;
454
455  const sidebarBody = document.getElementById("toolsSidebarBody");
456  const sidebar = document.getElementById("toolsFixedSidebar");
457  const trigger = document.getElementById("toolsSidebarTrigger");
458  const overlay = document.getElementById("toolsSidebarOverlay");
459  const closeBtn = document.getElementById("toolsSidebarClose");
460  const adFrame = document.getElementById("toolsSidebarAdFrame");
461
462  // 4a. Inject the 300x250 ad scripts into the ad frame.
463  //     We append the config + external invoke script into the ad frame container.
464  //     The external script writes its iframe to the element it is placed in,
465  //     so we place it directly inside the ad frame wrapper.
466  (function injectAd() {
467    if (!adFrame) return;
468
469    // Inline atOptions configuration (must run before the invoke script loads)
470    const configScript = document.createElement("script");
471    configScript.type = "text/javascript";
472    configScript.text = `
473      atOptions = {
474        'key' : 'f5214acd8479e07d7defe4626c574aa5',
475        'format' : 'iframe',
476        'height' : 250,
477        'width' : 300,
478        'params' : {}
479      };
480    `;
481    adFrame.appendChild(configScript);
482
483    // External invoke script
484    const invokeScript = document.createElement("script");
485    invokeScript.type = "text/javascript";
486    invokeScript.src = "https://www.highrevenueformat.com/f5214acd8479e07d7defe462
4866c574aa5/invoke.js";
487    invokeScript.async = true;
488    adFrame.appendChild(invokeScript);
489  })();
490
491  // 5. Populate list items with categories and staggered animations
492  let toolIndex = 0;
493
494  categories.forEach((category) => {
495    // Add category header
496    const catHeader = document.createElement("div");
497    catHeader.className = "tools-sb-category";
498    catHeader.textContent = category.name;
499    sidebarBody.appendChild(catHeader);
500
501    // Add tools under this category
502    category.tools.forEach((tool) => {
503      const item = document.createElement("a");
504      item.href = tool.url;
505      item.className = "tools-sb-item";
506      item.style.animationDelay = `${toolIndex * 0.025}s`;
507
508      item.innerHTML = `
509        <div class="tools-sb-item-icon">${tool.icon}</div>
510        <div class="tools-sb-item-details">
511          <div class="tools-sb-item-name">${tool.name}</div>
512          <div class="tools-sb-item-desc">${tool.desc}</div>
513        </div>
514      `;
515      sidebarBody.appendChild(item);
516      toolIndex++;
517    });
518  });
519
520  // 6. Active Structural Interface Controls and Handlers
521  function toggleSidebar() {
522    const isOpen = sidebar.classList.toggle("open");
523    trigger.classList.toggle("active", isOpen);
524    overlay.classList.toggle("visible", isOpen);
525    trigger.innerHTML = isOpen ? "✕" : "🧰";
526
527    // Re-trigger animations when opening
528    if (isOpen) {
529      const items = sidebarBody.querySelectorAll(".tools-sb-item");
530      items.forEach((item, idx) => {
531        item.style.animation = "none";
532        item.offsetHeight; // trigger reflow
533        item.style.animation = `slideInItem 0.35s cubic-bezier(0.4, 0, 0.2, 1) forwards`;
534        item.style.animationDelay = `${idx * 0.025}s`;
535      });
536    }
537  }
538
539  function closeSidebar() {
540    sidebar.classList.remove("open");
541    trigger.classList.remove("active");
542    overlay.classList.remove("visible");
543    trigger.innerHTML = "🧰";
544  }
545
546  // Bind Listeners
547  trigger.addEventListener("click", toggleSidebar);
548  overlay.addEventListener("click", closeSidebar);
549  closeBtn.addEventListener("click", closeSidebar);
550
551  // Close interface gracefully via the Escape key
552  window.addEventListener("keydown", (e) => {
553    if (e.key === "Escape") closeSidebar();
554  });
555})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.