update tool matrix
This commit is contained in:
		
							parent
							
								
									0c00512c99
								
							
						
					
					
						commit
						25cdc64f99
					
				@ -1 +1 @@
 | 
			
		||||
[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.11.1","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false},\"legacy\":{\"collections\":false}}"]
 | 
			
		||||
[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.11.1","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"server\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":true,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\",\"entrypoint\":\"astro/assets/endpoint/node\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false},\"legacy\":{\"collections\":false},\"session\":{\"driver\":\"fs-lite\",\"options\":{\"base\":\"/var/home/user01/Projekte/cc24-hub/node_modules/.astro/sessions\"}}}"]
 | 
			
		||||
@ -108,9 +108,10 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
  </div>
 | 
			
		||||
  
 | 
			
		||||
  <!-- View Toggle -->
 | 
			
		||||
  <div style="display: flex; gap: 1rem; margin-bottom: 1.5rem; align-items: center; flex-wrap: wrap;">
 | 
			
		||||
    <button class="btn btn-secondary view-toggle active" data-view="grid">Kachelansicht</button>
 | 
			
		||||
    <button class="btn btn-secondary view-toggle" data-view="matrix">Matrix-Ansicht</button>
 | 
			
		||||
  <!--<div style="display: flex; gap: 1rem; margin-bottom: 1.5rem; align-items: center; flex-wrap: wrap;">-->
 | 
			
		||||
  <div style="display: flex; gap: 1rem; margin-bottom: 1.5rem; align-items: center;">
 | 
			
		||||
    <button class="btn btn-secondary view-toggle active" style="height:50px" data-view="grid">Kachelansicht</button>
 | 
			
		||||
    <button class="btn btn-secondary view-toggle" style="height:50px" data-view="matrix">Matrix-Ansicht</button>
 | 
			
		||||
    
 | 
			
		||||
    <!-- AI Recommendations Button (only visible when authenticated) -->
 | 
			
		||||
    <button 
 | 
			
		||||
@ -119,7 +120,7 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      data-view="ai"
 | 
			
		||||
      style="display: none; background-color: var(--color-accent); color: white; border-color: var(--color-accent);"
 | 
			
		||||
    >
 | 
			
		||||
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
 | 
			
		||||
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; height:25px">
 | 
			
		||||
        <path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
 | 
			
		||||
        <path d="M9 11V7a3 3 0 0 1 6 0v4"/>
 | 
			
		||||
      </svg>
 | 
			
		||||
 | 
			
		||||
@ -8,59 +8,82 @@ const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
 | 
			
		||||
const yamlContent = await fs.readFile(yamlPath, 'utf8');
 | 
			
		||||
const data = load(yamlContent) as any;
 | 
			
		||||
 | 
			
		||||
const domains = data.domains;
 | 
			
		||||
const phases = data.phases;
 | 
			
		||||
const tools = data.tools;
 | 
			
		||||
const domains = data.domains;
 | 
			
		||||
const phases = data.phases.filter((p: any) => p.id !== 'collaboration-general'); // Exclude from matrix
 | 
			
		||||
 | 
			
		||||
// Separate collaboration tools from domain-specific tools
 | 
			
		||||
// Filter collaboration tools (tools with "collaboration-general" phase)
 | 
			
		||||
const collaborationTools = tools.filter((tool: any) => 
 | 
			
		||||
  tool.phases && tool.phases.includes('collaboration-general')
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const domainTools = tools.filter((tool: any) => 
 | 
			
		||||
  !tool.phases || !tool.phases.includes('collaboration-general')
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// Create matrix structure for domain-specific tools only
 | 
			
		||||
const matrix: Record<string, Record<string, any[]>> = {};
 | 
			
		||||
domains.forEach((domain: any) => {
 | 
			
		||||
  matrix[domain.id] = {};
 | 
			
		||||
  phases.filter((phase: any) => phase.id !== 'collaboration-general').forEach((phase: any) => {
 | 
			
		||||
    matrix[domain.id][phase.id] = domainTools.filter((tool: any) => 
 | 
			
		||||
      tool.domains && tool.domains.includes(domain.id) && tool.phases && tool.phases.includes(phase.id)
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
// Filter regular tools for matrix (excluding collaboration-only tools from matrix display)
 | 
			
		||||
const matrixTools = tools.filter((tool: any) => {
 | 
			
		||||
  if (!tool.phases || tool.phases.length === 0) return false;
 | 
			
		||||
  // Include if has non-collaboration phases and non-empty domains
 | 
			
		||||
  const hasRegularPhases = tool.phases.some((phase: string) => phase !== 'collaboration-general');
 | 
			
		||||
  const hasDomains = tool.domains && tool.domains.length > 0;
 | 
			
		||||
  return hasRegularPhases && hasDomains;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Also include collaboration tools in matrix if they have domains and regular phases
 | 
			
		||||
const collaborationToolsInMatrix = collaborationTools.filter((tool: any) => {
 | 
			
		||||
  const hasRegularPhases = tool.phases && tool.phases.some((phase: string) => phase !== 'collaboration-general');
 | 
			
		||||
  const hasDomains = tool.domains && tool.domains.length > 0;
 | 
			
		||||
  return hasRegularPhases && hasDomains;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Combine for matrix display
 | 
			
		||||
const allMatrixTools = [...matrixTools, ...collaborationToolsInMatrix];
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<div id="matrix-container" class="matrix-wrapper" style="display: none;">
 | 
			
		||||
  <!-- Collaboration Tools Section (compact horizontal layout for matrix view) -->
 | 
			
		||||
  <div id="collaboration-tools-section" style="margin-bottom: 1.5rem;">
 | 
			
		||||
    <h3 style="margin-bottom: 0.75rem; color: var(--color-text); font-size: 1.125rem;">Übergreifend & Kollaboration</h3>
 | 
			
		||||
    <div class="collaboration-tools-compact" id="collaboration-tools-container">
 | 
			
		||||
<!-- Matrix View Container -->
 | 
			
		||||
<section id="matrix-container" style="display: none; padding: 2rem 0;">
 | 
			
		||||
  <div style="text-align: center; margin-bottom: 2rem;">
 | 
			
		||||
    <h2 style="margin-bottom: 1rem;">Tool-Matrix nach NIST-Framework</h2>
 | 
			
		||||
    <p class="text-muted" style="max-width: 600px; margin: 0 auto;">
 | 
			
		||||
      Übersicht der Tools nach forensischen Domänen und Untersuchungsphasen.
 | 
			
		||||
      Bewegen Sie die Maus über Zeilen und Spalten für bessere Orientierung.
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <!-- Collaboration Tools Section (above matrix) -->
 | 
			
		||||
  <div id="collaboration-section" style="margin-bottom: 2rem;">
 | 
			
		||||
    <h3 style="margin-bottom: 1rem; color: var(--color-accent); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
        <path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
 | 
			
		||||
        <circle cx="8.5" cy="7" r="4"/>
 | 
			
		||||
        <line x1="20" y1="8" x2="20" y2="14"/>
 | 
			
		||||
        <line x1="23" y1="11" x2="17" y2="11"/>
 | 
			
		||||
      </svg>
 | 
			
		||||
      Übergreifende Tools & Kollaboration
 | 
			
		||||
    </h3>
 | 
			
		||||
    <div class="collaboration-tools-compact">
 | 
			
		||||
      {collaborationTools.map((tool: any) => {
 | 
			
		||||
        const hasValidProjectUrl = tool.projectUrl !== undefined &&
 | 
			
		||||
                                  tool.projectUrl !== null &&
 | 
			
		||||
        const hasValidProjectUrl = tool.projectUrl !== undefined && 
 | 
			
		||||
                                  tool.projectUrl !== null && 
 | 
			
		||||
                                  tool.projectUrl !== "" && 
 | 
			
		||||
                                  tool.projectUrl.trim() !== "";
 | 
			
		||||
        return (
 | 
			
		||||
          <div class={`collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : tool.license !== 'Proprietary' ? 'oss' : ''}`}
 | 
			
		||||
          <div class={`collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : (tool.license !== 'Proprietary' ? 'oss' : '')}`}
 | 
			
		||||
               onclick={`window.showToolDetails('${tool.name}')`}>
 | 
			
		||||
            <div class="tool-compact-header">
 | 
			
		||||
              <h4 style="margin: 0; font-size: 0.875rem; font-weight: 600;">{tool.name}</h4>
 | 
			
		||||
              <h4 style="margin: 0; font-size: 1rem;">{tool.name}</h4>
 | 
			
		||||
              <div style="display: flex; gap: 0.25rem;">
 | 
			
		||||
                {hasValidProjectUrl && <span class="badge-mini badge-primary">Self-Hosted</span>}
 | 
			
		||||
                {tool.license !== 'Proprietary' && <span class="badge-mini badge-success">OSS</span>}
 | 
			
		||||
                {tool.knowledgebase === true && <span class="badge-mini badge-error">Infos 📖</span>}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p style="font-size: 0.75rem; color: var(--color-text-secondary); margin: 0.25rem 0; line-height: 1.3;">
 | 
			
		||||
              {tool.description}
 | 
			
		||||
            <p style="font-size: 0.8125rem; color: var(--color-text-secondary); margin-bottom: 0.75rem; line-height: 1.4;">
 | 
			
		||||
              {tool.description.length > 120 ? `${tool.description.substring(0, 120)}...` : tool.description}
 | 
			
		||||
            </p>
 | 
			
		||||
            <div style="display: flex; gap: 0.75rem; font-size: 0.6875rem; color: var(--color-text-secondary);">
 | 
			
		||||
              <span>{tool.platforms.join(', ')}</span>
 | 
			
		||||
              <span>•</span>
 | 
			
		||||
              <span>{tool.skillLevel}</span>
 | 
			
		||||
            <div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
 | 
			
		||||
              {tool.tags && tool.tags.slice(0, 3).map((tag: string) => (
 | 
			
		||||
                <span class="tag" style="font-size: 0.625rem;">{tag}</span>
 | 
			
		||||
              ))}
 | 
			
		||||
              {tool.tags && tool.tags.length > 3 && (
 | 
			
		||||
                <span class="tag" style="font-size: 0.625rem; font-style: italic;">+{tool.tags.length - 3}</span>
 | 
			
		||||
              )}
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        );
 | 
			
		||||
@ -68,372 +91,253 @@ domains.forEach((domain: any) => {
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <!-- DFIR Tools Matrix -->
 | 
			
		||||
  <div id="dfir-matrix-section">
 | 
			
		||||
    <h2 style="margin-bottom: 1rem; color: var(--color-text);">DFIR Tools Matrix</h2>
 | 
			
		||||
    <table class="matrix-table">
 | 
			
		||||
  <!-- Matrix Table -->
 | 
			
		||||
  <div class="matrix-wrapper">
 | 
			
		||||
    <table class="matrix-table" id="matrix-table">
 | 
			
		||||
      <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <th style="width: 200px;">Domain / Phase</th>
 | 
			
		||||
          {phases.filter((phase: any) => phase.id !== 'collaboration-general').map((phase: any) => (
 | 
			
		||||
            <th data-phase={phase.id}>{phase.name}</th>
 | 
			
		||||
          <th style="background-color: var(--color-bg-secondary);">Domäne / Phase</th>
 | 
			
		||||
          {phases.map((phase: any) => (
 | 
			
		||||
            <th data-phase={phase.id} style="text-align: center; min-width: 150px;">
 | 
			
		||||
              {phase.name}
 | 
			
		||||
            </th>
 | 
			
		||||
          ))}
 | 
			
		||||
        </tr>
 | 
			
		||||
      </thead>
 | 
			
		||||
      <tbody>
 | 
			
		||||
        {domains.map((domain: any) => (
 | 
			
		||||
          <tr data-domain={domain.id}>
 | 
			
		||||
            <th>{domain.name}</th>
 | 
			
		||||
            {phases.filter((phase: any) => phase.id !== 'collaboration-general').map((phase: any) => (
 | 
			
		||||
              <td class="matrix-cell" data-domain={domain.id} data-phase={phase.id}>
 | 
			
		||||
              {matrix[domain.id][phase.id].map((tool: any) => {
 | 
			
		||||
                const hasValidProjectUrl = tool.projectUrl !== undefined && 
 | 
			
		||||
                                          tool.projectUrl !== null && 
 | 
			
		||||
                                          tool.projectUrl !== "" && 
 | 
			
		||||
                                          tool.projectUrl.trim() !== "";
 | 
			
		||||
                return (
 | 
			
		||||
                  <span 
 | 
			
		||||
                    class={`tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`}
 | 
			
		||||
                    data-tool-name={tool.name}
 | 
			
		||||
                    onclick={`window.showToolDetails('${tool.name}')`}
 | 
			
		||||
                    title={`${tool.name}${tool.knowledgebase === true ? ' (KB verfügbar)' : ''}`}
 | 
			
		||||
                  >
 | 
			
		||||
                    {tool.name}
 | 
			
		||||
                    {tool.knowledgebase === true && <span style="margin-left: 0.25rem; font-size: 0.6875rem;">📖</span>}
 | 
			
		||||
                  </span>
 | 
			
		||||
                );
 | 
			
		||||
              })}
 | 
			
		||||
              </td>
 | 
			
		||||
            ))}
 | 
			
		||||
            <th data-domain={domain.id} style="background-color: var(--color-bg-secondary); font-weight: 600;">
 | 
			
		||||
              {domain.name}
 | 
			
		||||
            </th>
 | 
			
		||||
            {phases.map((phase: any) => {
 | 
			
		||||
              // Find tools for this domain-phase combination
 | 
			
		||||
              const cellTools = allMatrixTools.filter((tool: any) => {
 | 
			
		||||
                const hasPhase = tool.phases && tool.phases.includes(phase.id);
 | 
			
		||||
                const hasDomain = tool.domains && tool.domains.includes(domain.id);
 | 
			
		||||
                return hasPhase && hasDomain;
 | 
			
		||||
              });
 | 
			
		||||
 | 
			
		||||
              return (
 | 
			
		||||
                <td data-domain={domain.id} data-phase={phase.id} class="matrix-cell">
 | 
			
		||||
                  <div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
 | 
			
		||||
                    {cellTools.map((tool: any) => {
 | 
			
		||||
                      const hasValidProjectUrl = tool.projectUrl !== undefined && 
 | 
			
		||||
                                                tool.projectUrl !== null && 
 | 
			
		||||
                                                tool.projectUrl !== "" && 
 | 
			
		||||
                                                tool.projectUrl.trim() !== "";
 | 
			
		||||
                      return (
 | 
			
		||||
                        <span 
 | 
			
		||||
                          class={`tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : (tool.license !== 'Proprietary' ? 'tool-chip-oss' : '')}`}
 | 
			
		||||
                          onclick={`window.showToolDetails('${tool.name}')`}
 | 
			
		||||
                          title={tool.description}
 | 
			
		||||
                        >
 | 
			
		||||
                          {tool.name}
 | 
			
		||||
                        </span>
 | 
			
		||||
                      );
 | 
			
		||||
                    })}
 | 
			
		||||
                  </div>
 | 
			
		||||
                </td>
 | 
			
		||||
              );
 | 
			
		||||
            })}
 | 
			
		||||
          </tr>
 | 
			
		||||
        ))}
 | 
			
		||||
      </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- Tool Details Modal -->
 | 
			
		||||
<div class="modal-overlay" id="modal-overlay" onclick="window.hideToolDetails()"></div>
 | 
			
		||||
<div class="tool-details" id="tool-details">
 | 
			
		||||
  <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 1rem;">
 | 
			
		||||
    <h2 id="tool-name" style="margin: 0;">Tool Name</h2>
 | 
			
		||||
    <button class="btn-icon" onclick="window.hideToolDetails()">
 | 
			
		||||
      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
        <line x1="18" y1="6" x2="6" y2="18"></line>
 | 
			
		||||
        <line x1="6" y1="6" x2="18" y2="18"></line>
 | 
			
		||||
      </svg>
 | 
			
		||||
    </button>
 | 
			
		||||
  <!-- Matrix Statistics -->
 | 
			
		||||
  <div style="margin-top: 2rem; text-align: center;">
 | 
			
		||||
    <div style="display: inline-flex; gap: 2rem; background-color: var(--color-bg-secondary); padding: 1rem; border-radius: 0.5rem;">
 | 
			
		||||
      <div>
 | 
			
		||||
        <strong style="color: var(--color-primary);" id="matrix-total-tools">{allMatrixTools.length}</strong>
 | 
			
		||||
        <span class="text-muted" style="font-size: 0.875rem;">Tools in Matrix</span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div>
 | 
			
		||||
        <strong style="color: var(--color-accent);" id="collaboration-total-tools">{collaborationTools.length}</strong>
 | 
			
		||||
        <span class="text-muted" style="font-size: 0.875rem;">Collaboration Tools</span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div>
 | 
			
		||||
        <strong style="color: var(--color-text);" id="matrix-visible-tools">{allMatrixTools.length + collaborationTools.length}</strong>
 | 
			
		||||
        <span class="text-muted" style="font-size: 0.875rem;">Gesamt sichtbar</span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  
 | 
			
		||||
  <p id="tool-description" class="text-muted"></p>
 | 
			
		||||
  
 | 
			
		||||
  <div id="tool-badges" style="display: flex; gap: 0.5rem; margin-bottom: 1rem;"></div>
 | 
			
		||||
  
 | 
			
		||||
  <div id="tool-metadata" style="margin-bottom: 1rem;"></div>
 | 
			
		||||
  
 | 
			
		||||
  <div id="tool-tags" style="margin-bottom: 1rem;"></div>
 | 
			
		||||
  
 | 
			
		||||
  <!-- Updated button container for dual buttons -->
 | 
			
		||||
  <div id="tool-links" style="display: flex; gap: 0.5rem; flex-direction: column;"></div>
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
<script define:vars={{ toolsData: tools, collaborationTools, domainTools }}>
 | 
			
		||||
  // Helper function to get selected phase from active button
 | 
			
		||||
  function getSelectedPhase() {
 | 
			
		||||
    const activePhaseButton = document.querySelector('.phase-button.active');
 | 
			
		||||
    return activePhaseButton ? activePhaseButton.getAttribute('data-phase') : '';
 | 
			
		||||
<script define:vars={{ allMatrixTools, collaborationTools, domains, phases }}>
 | 
			
		||||
document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
  const matrixTable = document.getElementById('matrix-table');
 | 
			
		||||
  const collaborationSection = document.getElementById('collaboration-section');
 | 
			
		||||
  
 | 
			
		||||
  if (!matrixTable || !collaborationSection) return;
 | 
			
		||||
 | 
			
		||||
  // Store original tools data
 | 
			
		||||
  let currentMatrixTools = [...allMatrixTools];
 | 
			
		||||
  let currentCollaborationTools = [...collaborationTools];
 | 
			
		||||
 | 
			
		||||
  // Matrix hover effects
 | 
			
		||||
  let hoveredRow = null;
 | 
			
		||||
  let hoveredColumn = null;
 | 
			
		||||
 | 
			
		||||
  function updateMatrixHighlight() {
 | 
			
		||||
    // Clear all highlights
 | 
			
		||||
    matrixTable.querySelectorAll('tr').forEach(row => row.classList.remove('highlight-row'));
 | 
			
		||||
    matrixTable.querySelectorAll('th, td').forEach(cell => cell.classList.remove('highlight-column'));
 | 
			
		||||
 | 
			
		||||
    // Apply current highlights
 | 
			
		||||
    if (hoveredRow !== null) {
 | 
			
		||||
      const rows = matrixTable.querySelectorAll('tr');
 | 
			
		||||
      if (rows[hoveredRow]) {
 | 
			
		||||
        rows[hoveredRow].classList.add('highlight-row');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (hoveredColumn !== null) {
 | 
			
		||||
      const cells = matrixTable.querySelectorAll(`th:nth-child(${hoveredColumn + 1}), td:nth-child(${hoveredColumn + 1})`);
 | 
			
		||||
      cells.forEach(cell => cell.classList.add('highlight-column'));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Helper function to get selected domain from dropdown
 | 
			
		||||
  function getSelectedDomain() {
 | 
			
		||||
    const domainSelect = document.getElementById('domain-select');
 | 
			
		||||
    return domainSelect ? domainSelect.value : '';
 | 
			
		||||
  }
 | 
			
		||||
  // Add hover listeners to matrix table
 | 
			
		||||
  matrixTable.addEventListener('mouseover', (e) => {
 | 
			
		||||
    const target = e.target.closest('tr');
 | 
			
		||||
    if (target) {
 | 
			
		||||
      const rows = Array.from(matrixTable.querySelectorAll('tr'));
 | 
			
		||||
      hoveredRow = rows.indexOf(target);
 | 
			
		||||
      updateMatrixHighlight();
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Update matrix highlighting based on current filters
 | 
			
		||||
  function updateMatrixHighlighting() {
 | 
			
		||||
    const matrixTable = document.querySelector('.matrix-table');
 | 
			
		||||
    if (!matrixTable) return;
 | 
			
		||||
  matrixTable.addEventListener('mouseout', (e) => {
 | 
			
		||||
    if (!e.relatedTarget || !matrixTable.contains(e.relatedTarget)) {
 | 
			
		||||
      hoveredRow = null;
 | 
			
		||||
      updateMatrixHighlight();
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Add hover listeners to header cells
 | 
			
		||||
  matrixTable.addEventListener('mouseover', (e) => {
 | 
			
		||||
    if (e.target.matches('th[data-phase]')) {
 | 
			
		||||
      const headers = Array.from(matrixTable.querySelectorAll('thead th'));
 | 
			
		||||
      hoveredColumn = headers.indexOf(e.target);
 | 
			
		||||
      updateMatrixHighlight();
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  matrixTable.addEventListener('mouseout', (e) => {
 | 
			
		||||
    if (e.target.matches('th[data-phase]') && (!e.relatedTarget || !matrixTable.contains(e.relatedTarget))) {
 | 
			
		||||
      hoveredColumn = null;
 | 
			
		||||
      updateMatrixHighlight();
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Function to update matrix display based on filtered tools
 | 
			
		||||
  function updateMatrixDisplay(filteredTools) {
 | 
			
		||||
    // Separate collaboration tools from matrix tools
 | 
			
		||||
    const filteredCollaborationTools = filteredTools.filter(tool => 
 | 
			
		||||
      tool.phases && tool.phases.includes('collaboration-general')
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    // Clear existing highlights
 | 
			
		||||
    matrixTable.querySelectorAll('.highlight-row, .highlight-column').forEach(el => {
 | 
			
		||||
      el.classList.remove('highlight-row', 'highlight-column');
 | 
			
		||||
    const filteredMatrixTools = filteredTools.filter(tool => {
 | 
			
		||||
      if (!tool.phases || tool.phases.length === 0) return false;
 | 
			
		||||
      const hasRegularPhases = tool.phases.some(phase => phase !== 'collaboration-general');
 | 
			
		||||
      const hasDomains = tool.domains && tool.domains.length > 0;
 | 
			
		||||
      return hasRegularPhases && hasDomains;
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    const selectedDomain = getSelectedDomain();
 | 
			
		||||
    const selectedPhase = getSelectedPhase();
 | 
			
		||||
    
 | 
			
		||||
    // Highlight selected domain row
 | 
			
		||||
    if (selectedDomain) {
 | 
			
		||||
      const domainRow = matrixTable.querySelector(`tr[data-domain="${selectedDomain}"]`);
 | 
			
		||||
      if (domainRow) {
 | 
			
		||||
        domainRow.classList.add('highlight-row');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Highlight selected phase column
 | 
			
		||||
    if (selectedPhase) {
 | 
			
		||||
      const phaseHeaders = matrixTable.querySelectorAll('thead th[data-phase]');
 | 
			
		||||
      const phaseIndex = Array.from(phaseHeaders).findIndex(th => 
 | 
			
		||||
        th.getAttribute('data-phase') === selectedPhase
 | 
			
		||||
      );
 | 
			
		||||
      
 | 
			
		||||
      if (phaseIndex >= 0) {
 | 
			
		||||
        const columnIndex = phaseIndex + 1; // +1 because first column is domain names
 | 
			
		||||
        matrixTable.querySelectorAll(`tr`).forEach(row => {
 | 
			
		||||
          const cell = row.children[columnIndex];
 | 
			
		||||
          if (cell) {
 | 
			
		||||
            cell.classList.add('highlight-column');
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Helper function to create compact collaboration tool cards for matrix view
 | 
			
		||||
  function createCollaborationToolCardCompact(tool) {
 | 
			
		||||
    const hasValidProjectUrl = tool.projectUrl !== undefined && 
 | 
			
		||||
                              tool.projectUrl !== null && 
 | 
			
		||||
                              tool.projectUrl !== "" && 
 | 
			
		||||
                              tool.projectUrl.trim() !== "";
 | 
			
		||||
    
 | 
			
		||||
    const cardDiv = document.createElement('div');
 | 
			
		||||
    const cardClass = `collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : tool.license !== 'Proprietary' ? 'oss' : ''}`;
 | 
			
		||||
    cardDiv.className = cardClass;
 | 
			
		||||
    cardDiv.onclick = () => window.showToolDetails(tool.name);
 | 
			
		||||
    
 | 
			
		||||
    cardDiv.innerHTML = `
 | 
			
		||||
      <div class="tool-compact-header">
 | 
			
		||||
        <h4 style="margin: 0; font-size: 0.875rem; font-weight: 600;">${tool.name}</h4>
 | 
			
		||||
        <div style="display: flex; gap: 0.25rem;">
 | 
			
		||||
          ${hasValidProjectUrl ? '<span class="badge-mini badge-primary">Self-Hosted</span>' : ''}
 | 
			
		||||
          ${tool.license !== 'Proprietary' ? '<span class="badge-mini badge-success">OSS</span>' : ''}
 | 
			
		||||
          ${tool.knowledgebase === true ? '<span class="badge-mini badge-error">Infos 📖</span>' : ''}
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <p style="font-size: 0.75rem; color: var(--color-text-secondary); margin: 0.25rem 0; line-height: 1.3;">
 | 
			
		||||
        ${tool.description}
 | 
			
		||||
      </p>
 | 
			
		||||
      <div style="display: flex; gap: 0.75rem; font-size: 0.6875rem; color: var(--color-text-secondary);">
 | 
			
		||||
        <span>${(tool.platforms || []).join(', ')}</span>
 | 
			
		||||
        <span>•</span>
 | 
			
		||||
        <span>${tool.skillLevel}</span>
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
    
 | 
			
		||||
    return cardDiv;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Tool details functions
 | 
			
		||||
  window.showToolDetails = function(toolName) {
 | 
			
		||||
    const tool = toolsData.find(t => t.name === toolName);
 | 
			
		||||
    if (!tool) return;
 | 
			
		||||
    
 | 
			
		||||
    // Update modal content
 | 
			
		||||
    document.getElementById('tool-name').textContent = tool.name;
 | 
			
		||||
    document.getElementById('tool-description').textContent = tool.description;
 | 
			
		||||
    
 | 
			
		||||
    // Badges
 | 
			
		||||
    const badgesContainer = document.getElementById('tool-badges');
 | 
			
		||||
    const hasValidProjectUrl = tool.projectUrl !== undefined && 
 | 
			
		||||
                              tool.projectUrl !== null && 
 | 
			
		||||
                              tool.projectUrl !== "" && 
 | 
			
		||||
                              tool.projectUrl.trim() !== "";
 | 
			
		||||
    
 | 
			
		||||
    badgesContainer.innerHTML = '';
 | 
			
		||||
    if (hasValidProjectUrl) {
 | 
			
		||||
      badgesContainer.innerHTML += '<span class="badge badge-primary">Self-Hosted</span>';
 | 
			
		||||
    }
 | 
			
		||||
    if (tool.license !== 'Proprietary') {
 | 
			
		||||
      badgesContainer.innerHTML += '<span class="badge badge-success">Open Source</span>';
 | 
			
		||||
    }
 | 
			
		||||
    if (tool.knowledgebase === true) {
 | 
			
		||||
      badgesContainer.innerHTML += '<span class="badge badge-error">Infos 📖</span>';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Metadata - safe array handling
 | 
			
		||||
    const metadataContainer = document.getElementById('tool-metadata');
 | 
			
		||||
    const domains = tool.domains || [];
 | 
			
		||||
    const phases = tool.phases || [];
 | 
			
		||||
    const domainsText = domains.length > 0 ? domains.join(', ') : 'Domain-agnostic';
 | 
			
		||||
    const phasesText = phases.join(', ');
 | 
			
		||||
    metadataContainer.innerHTML = `
 | 
			
		||||
      <div style="display: grid; gap: 0.5rem;">
 | 
			
		||||
        <div><strong>Betriebssystem:</strong> ${(tool.platforms || []).join(', ')}</div>
 | 
			
		||||
        <div><strong>Skill Level:</strong> ${tool.skillLevel}</div>
 | 
			
		||||
        <div><strong>Lizenzmodell:</strong> ${tool.license}</div>
 | 
			
		||||
        <div><strong>Deployment:</strong> ${tool.accessType}</div>
 | 
			
		||||
        <div><strong>Einsatzgebiete:</strong> ${domainsText}</div>
 | 
			
		||||
        <div><strong>Ermittlungsphasen:</strong> ${phasesText}</div>
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
    
 | 
			
		||||
    // Tags - safe array handling
 | 
			
		||||
    const tagsContainer = document.getElementById('tool-tags');
 | 
			
		||||
    const tags = tool.tags || [];
 | 
			
		||||
    tagsContainer.innerHTML = `
 | 
			
		||||
      <div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
 | 
			
		||||
        ${tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
    
 | 
			
		||||
    // Links - Updated to handle dual buttons for hosted tools AND knowledgebase links
 | 
			
		||||
    const linksContainer = document.getElementById('tool-links');
 | 
			
		||||
    
 | 
			
		||||
    let linksHTML = '';
 | 
			
		||||
    
 | 
			
		||||
    // Main action buttons
 | 
			
		||||
    if (hasValidProjectUrl) {
 | 
			
		||||
      // Two buttons for tools we're hosting
 | 
			
		||||
      linksHTML += `
 | 
			
		||||
        <div style="display: flex; gap: 0.5rem;">
 | 
			
		||||
          <a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-secondary" style="flex: 1;">
 | 
			
		||||
            Software-Homepage
 | 
			
		||||
          </a>
 | 
			
		||||
          <a href="${tool.projectUrl}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="flex: 1;">
 | 
			
		||||
            Zugreifen
 | 
			
		||||
          </a>
 | 
			
		||||
        </div>
 | 
			
		||||
      `;
 | 
			
		||||
    // Update collaboration tools section visibility
 | 
			
		||||
    if (filteredCollaborationTools.length === 0) {
 | 
			
		||||
      collaborationSection.style.display = 'none';
 | 
			
		||||
    } else {
 | 
			
		||||
      // Single button for tools we're not hosting
 | 
			
		||||
      linksHTML += `
 | 
			
		||||
        <a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
 | 
			
		||||
          Software-Homepage
 | 
			
		||||
        </a>
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Add knowledgebase link if available
 | 
			
		||||
    if (tool.knowledgebase === true) {
 | 
			
		||||
      const kbId = tool.name.toLowerCase().replace(/\s+/g, '-');
 | 
			
		||||
      linksHTML += `
 | 
			
		||||
        <a href="/knowledgebase#kb-${kbId}" class="btn btn-secondary" style="width: 100%; margin-top: 0.5rem;">
 | 
			
		||||
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
 | 
			
		||||
            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
 | 
			
		||||
            <polyline points="14 2 14 8 20 8"/>
 | 
			
		||||
            <line x1="16" y1="13" x2="8" y2="13"/>
 | 
			
		||||
            <line x1="16" y1="17" x2="8" y2="17"/>
 | 
			
		||||
            <polyline points="10 9 9 9 8 9"/>
 | 
			
		||||
          </svg>
 | 
			
		||||
          Knowledgebase anzeigen
 | 
			
		||||
        </a>
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    linksContainer.innerHTML = linksHTML;
 | 
			
		||||
    
 | 
			
		||||
    // Show modal
 | 
			
		||||
    document.getElementById('modal-overlay').classList.add('active');
 | 
			
		||||
    document.getElementById('tool-details').classList.add('active');
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  window.hideToolDetails = function() {
 | 
			
		||||
    document.getElementById('modal-overlay').classList.remove('active');
 | 
			
		||||
    document.getElementById('tool-details').classList.remove('active');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Listen for view changes to trigger highlighting
 | 
			
		||||
  window.addEventListener('viewChanged', (event) => {
 | 
			
		||||
    const view = event.detail;
 | 
			
		||||
    if (view === 'matrix') {
 | 
			
		||||
      // Delay highlighting to ensure matrix is visible
 | 
			
		||||
      setTimeout(updateMatrixHighlighting, 100);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Listen for filter changes to update highlighting
 | 
			
		||||
  window.addEventListener('toolsFiltered', (event) => {
 | 
			
		||||
    const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
 | 
			
		||||
    if (currentView === 'matrix') {
 | 
			
		||||
      setTimeout(updateMatrixHighlighting, 50);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Update matrix on filter change
 | 
			
		||||
  window.addEventListener('toolsFiltered', (event) => {
 | 
			
		||||
    const filtered = event.detail;
 | 
			
		||||
    const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
 | 
			
		||||
    
 | 
			
		||||
    if (currentView === 'matrix') {
 | 
			
		||||
      // Get selected phase from active button instead of dropdown
 | 
			
		||||
      const selectedPhase = getSelectedPhase();
 | 
			
		||||
      collaborationSection.style.display = 'block';
 | 
			
		||||
      
 | 
			
		||||
      // Handle collaboration tools section
 | 
			
		||||
      const collaborationSection = document.getElementById('collaboration-tools-section');
 | 
			
		||||
      const dfirMatrixSection = document.getElementById('dfir-matrix-section');
 | 
			
		||||
      const collaborationContainer = document.getElementById('collaboration-tools-container');
 | 
			
		||||
      
 | 
			
		||||
      if (selectedPhase === 'collaboration-general') {
 | 
			
		||||
        // Show only collaboration tools, hide matrix
 | 
			
		||||
        collaborationSection.style.display = 'block';
 | 
			
		||||
        dfirMatrixSection.style.display = 'none';
 | 
			
		||||
        
 | 
			
		||||
        // Filter collaboration tools
 | 
			
		||||
        const filteredCollaboration = filtered.filter(tool => (tool.phases || []).includes('collaboration-general'));
 | 
			
		||||
      // Update collaboration tools display
 | 
			
		||||
      const collaborationContainer = collaborationSection.querySelector('.collaboration-tools-compact');
 | 
			
		||||
      if (collaborationContainer) {
 | 
			
		||||
        collaborationContainer.innerHTML = '';
 | 
			
		||||
        
 | 
			
		||||
        filteredCollaboration.forEach(tool => {
 | 
			
		||||
          const toolCard = createCollaborationToolCardCompact(tool);
 | 
			
		||||
          collaborationContainer.appendChild(toolCard);
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        // Show matrix, handle collaboration tools visibility
 | 
			
		||||
        dfirMatrixSection.style.display = 'block';
 | 
			
		||||
        
 | 
			
		||||
        if (selectedPhase === '' || selectedPhase === null) {
 | 
			
		||||
          // Show collaboration tools when no specific phase is selected
 | 
			
		||||
          collaborationSection.style.display = 'block';
 | 
			
		||||
          
 | 
			
		||||
          // Show all collaboration tools that pass general filters
 | 
			
		||||
          const filteredCollaboration = filtered.filter(tool => (tool.phases || []).includes('collaboration-general'));
 | 
			
		||||
          collaborationContainer.innerHTML = '';
 | 
			
		||||
          
 | 
			
		||||
          filteredCollaboration.forEach(tool => {
 | 
			
		||||
            const toolCard = createCollaborationToolCardCompact(tool);
 | 
			
		||||
            collaborationContainer.appendChild(toolCard);
 | 
			
		||||
          });
 | 
			
		||||
        } else {
 | 
			
		||||
          // Hide collaboration tools when specific DFIR phase is selected
 | 
			
		||||
          collaborationSection.style.display = 'none';
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Clear and update matrix cells with DFIR tools only
 | 
			
		||||
        document.querySelectorAll('.matrix-cell').forEach(cell => {
 | 
			
		||||
          cell.innerHTML = '';
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        // Re-populate with filtered DFIR tools - with safe array handling
 | 
			
		||||
        const filteredDfirTools = filtered.filter(tool => !(tool.phases || []).includes('collaboration-general'));
 | 
			
		||||
        filteredDfirTools.forEach(tool => {
 | 
			
		||||
        filteredCollaborationTools.forEach(tool => {
 | 
			
		||||
          const hasValidProjectUrl = tool.projectUrl !== undefined && 
 | 
			
		||||
                                    tool.projectUrl !== null && 
 | 
			
		||||
                                    tool.projectUrl !== "" && 
 | 
			
		||||
                                    tool.projectUrl.trim() !== "";
 | 
			
		||||
          
 | 
			
		||||
          const domains = tool.domains || [];
 | 
			
		||||
          const phases = tool.phases || [];
 | 
			
		||||
          const toolDiv = document.createElement('div');
 | 
			
		||||
          toolDiv.className = `collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : (tool.license !== 'Proprietary' ? 'oss' : '')}`;
 | 
			
		||||
          toolDiv.onclick = () => window.showToolDetails(tool.name);
 | 
			
		||||
          
 | 
			
		||||
          domains.forEach(domain => {
 | 
			
		||||
            phases.forEach(phase => {
 | 
			
		||||
              if (phase !== 'collaboration-general') {
 | 
			
		||||
                const cell = document.querySelector(`[data-domain="${domain}"][data-phase="${phase}"]`);
 | 
			
		||||
                if (cell) {
 | 
			
		||||
                  const chip = document.createElement('span');
 | 
			
		||||
                  chip.className = `tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`;
 | 
			
		||||
                  chip.setAttribute('title', `${tool.name}${tool.knowledgebase === true ? ' (KB verfügbar)' : ''}`);
 | 
			
		||||
                  chip.innerHTML = `${tool.name}${tool.knowledgebase === true ? '<span style="margin-left: 0.25rem; font-size: 0.6875rem;">📖</span>' : ''}`;
 | 
			
		||||
                  chip.onclick = () => window.showToolDetails(tool.name);
 | 
			
		||||
                  cell.appendChild(chip);
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
          });
 | 
			
		||||
          toolDiv.innerHTML = `
 | 
			
		||||
            <div class="tool-compact-header">
 | 
			
		||||
              <h4 style="margin: 0; font-size: 1rem;">${tool.name}</h4>
 | 
			
		||||
              <div style="display: flex; gap: 0.25rem;">
 | 
			
		||||
                ${hasValidProjectUrl ? '<span class="badge-mini badge-primary">Self-Hosted</span>' : ''}
 | 
			
		||||
                ${tool.license !== 'Proprietary' ? '<span class="badge-mini badge-success">OSS</span>' : ''}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p style="font-size: 0.8125rem; color: var(--color-text-secondary); margin-bottom: 0.75rem; line-height: 1.4;">
 | 
			
		||||
              ${tool.description.length > 120 ? `${tool.description.substring(0, 120)}...` : tool.description}
 | 
			
		||||
            </p>
 | 
			
		||||
            <div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
 | 
			
		||||
              ${tool.tags ? tool.tags.slice(0, 3).map(tag => `<span class="tag" style="font-size: 0.625rem;">${tag}</span>`).join('') : ''}
 | 
			
		||||
              ${tool.tags && tool.tags.length > 3 ? `<span class="tag" style="font-size: 0.625rem; font-style: italic;">+${tool.tags.length - 3}</span>` : ''}
 | 
			
		||||
            </div>
 | 
			
		||||
          `;
 | 
			
		||||
          
 | 
			
		||||
          collaborationContainer.appendChild(toolDiv);
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        // Update highlighting after matrix content is updated
 | 
			
		||||
        setTimeout(updateMatrixHighlighting, 50);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Update matrix cells
 | 
			
		||||
    domains.forEach(domain => {
 | 
			
		||||
      phases.forEach(phase => {
 | 
			
		||||
        const cell = matrixTable.querySelector(`td[data-domain="${domain.id}"][data-phase="${phase.id}"]`);
 | 
			
		||||
        if (!cell) return;
 | 
			
		||||
 | 
			
		||||
        const cellTools = filteredMatrixTools.filter(tool => {
 | 
			
		||||
          const hasPhase = tool.phases && tool.phases.includes(phase.id);
 | 
			
		||||
          const hasDomain = tool.domains && tool.domains.includes(domain.id);
 | 
			
		||||
          return hasPhase && hasDomain;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const cellContent = cell.querySelector('div') || cell;
 | 
			
		||||
        cellContent.innerHTML = '';
 | 
			
		||||
 | 
			
		||||
        cellTools.forEach(tool => {
 | 
			
		||||
          const hasValidProjectUrl = tool.projectUrl !== undefined && 
 | 
			
		||||
                                    tool.projectUrl !== null && 
 | 
			
		||||
                                    tool.projectUrl !== "" && 
 | 
			
		||||
                                    tool.projectUrl.trim() !== "";
 | 
			
		||||
          
 | 
			
		||||
          const chip = document.createElement('span');
 | 
			
		||||
          chip.className = `tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : (tool.license !== 'Proprietary' ? 'tool-chip-oss' : '')}`;
 | 
			
		||||
          chip.onclick = () => window.showToolDetails(tool.name);
 | 
			
		||||
          chip.title = tool.description;
 | 
			
		||||
          chip.textContent = tool.name;
 | 
			
		||||
          
 | 
			
		||||
          cellContent.appendChild(chip);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Update statistics
 | 
			
		||||
    const matrixTotalEl = document.getElementById('matrix-total-tools');
 | 
			
		||||
    const collaborationTotalEl = document.getElementById('collaboration-total-tools');
 | 
			
		||||
    const visibleTotalEl = document.getElementById('matrix-visible-tools');
 | 
			
		||||
    
 | 
			
		||||
    if (matrixTotalEl) matrixTotalEl.textContent = filteredMatrixTools.length;
 | 
			
		||||
    if (collaborationTotalEl) collaborationTotalEl.textContent = filteredCollaborationTools.length;
 | 
			
		||||
    if (visibleTotalEl) visibleTotalEl.textContent = (filteredMatrixTools.length + filteredCollaborationTools.length);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Listen for filter events
 | 
			
		||||
  window.addEventListener('toolsFiltered', (event) => {
 | 
			
		||||
    const filteredTools = event.detail;
 | 
			
		||||
    updateMatrixDisplay(filteredTools);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Initial display
 | 
			
		||||
  updateMatrixDisplay([...allMatrixTools, ...collaborationTools]);
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
@ -697,8 +697,17 @@ tools:
 | 
			
		||||
      kollaborative Phasen und sichere Speicherung von Beweismitteln
 | 
			
		||||
      mit Versionierung.
 | 
			
		||||
    domains:
 | 
			
		||||
      - incident-response
 | 
			
		||||
      - law-enforcement
 | 
			
		||||
      - malware-analysis
 | 
			
		||||
      - fraud-investigation
 | 
			
		||||
      - network-forensics
 | 
			
		||||
      - mobile-forensics
 | 
			
		||||
      - cloud-forensics
 | 
			
		||||
      - ics-forensics
 | 
			
		||||
    phases:
 | 
			
		||||
      - collaboration-general
 | 
			
		||||
      - reporting
 | 
			
		||||
    platforms:
 | 
			
		||||
      - Web
 | 
			
		||||
    skillLevel: novice
 | 
			
		||||
 | 
			
		||||
@ -46,14 +46,14 @@ const hostedServices = data.tools.filter((tool: any) => {
 | 
			
		||||
      ))}
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <!-- Uptime Kuma Embed -->
 | 
			
		||||
    <!-- Uptime Kuma Embed 
 | 
			
		||||
    <div class="card" style="padding: 0; overflow: hidden;">
 | 
			
		||||
      <iframe 
 | 
			
		||||
        src="https://status.mikoshi.de/status/cc24-hub?embed=true"
 | 
			
		||||
        style="width: 100%; height: 600px; border: none;"
 | 
			
		||||
        title="Uptime Kuma Status Page"
 | 
			
		||||
      ></iframe>
 | 
			
		||||
    </div>
 | 
			
		||||
    </div>-->
 | 
			
		||||
  </section>
 | 
			
		||||
</BaseLayout>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -333,6 +333,7 @@ input[type="checkbox"] {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  border-collapse: collapse;
 | 
			
		||||
  min-width: 800px;
 | 
			
		||||
  table-layout: fixed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.matrix-table th,
 | 
			
		||||
@ -341,6 +342,8 @@ input[type="checkbox"] {
 | 
			
		||||
  padding: 0.75rem;
 | 
			
		||||
  text-align: left;
 | 
			
		||||
  transition: background-color 0.2s ease, border-color 0.2s ease;
 | 
			
		||||
  overflow: hidden; /* Prevent content overflow */
 | 
			
		||||
  word-wrap: break-word;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.matrix-table th {
 | 
			
		||||
@ -348,18 +351,34 @@ input[type="checkbox"] {
 | 
			
		||||
  font-weight: 600;
 | 
			
		||||
  position: sticky;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.matrix-table th:first-child {
 | 
			
		||||
  position: sticky;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  z-index: 2;
 | 
			
		||||
  background-color: var(--color-bg-secondary);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Ensure uniform column widths */
 | 
			
		||||
.matrix-table th:first-child,
 | 
			
		||||
.matrix-table td:first-child {
 | 
			
		||||
  width: 200px;
 | 
			
		||||
  min-width: 200px;
 | 
			
		||||
  max-width: 200px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.matrix-table th:not(:first-child),
 | 
			
		||||
.matrix-table td:not(:first-child) {
 | 
			
		||||
  width: 150px;
 | 
			
		||||
  min-width: 150px;
 | 
			
		||||
  max-width: 150px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.matrix-cell {
 | 
			
		||||
  min-height: 60px;
 | 
			
		||||
  vertical-align: top;
 | 
			
		||||
  width: 150px;
 | 
			
		||||
  min-width: 150px;
 | 
			
		||||
  max-width: 150px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Matrix Highlighting */
 | 
			
		||||
@ -1097,6 +1116,31 @@ footer {
 | 
			
		||||
    min-height: 100px;
 | 
			
		||||
    font-size: 0.8125rem;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .matrix-table {
 | 
			
		||||
    min-width: 600px; /* Reduce minimum width for mobile */
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .matrix-table th:first-child,
 | 
			
		||||
  .matrix-table td:first-child {
 | 
			
		||||
    width: 140px;
 | 
			
		||||
    min-width: 140px;
 | 
			
		||||
    max-width: 140px;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .matrix-table th:not(:first-child),
 | 
			
		||||
  .matrix-table td:not(:first-child) {
 | 
			
		||||
    width: 120px;
 | 
			
		||||
    min-width: 120px;
 | 
			
		||||
    max-width: 120px;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .matrix-cell {
 | 
			
		||||
    width: 120px;
 | 
			
		||||
    min-width: 120px;
 | 
			
		||||
    max-width: 120px;
 | 
			
		||||
    min-height: 50px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (width: 600px) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user