Clean up codebase: remove unused demos and test files
- Remove dan-eden-portfolio and devigo-web demo sites - Clean up demo testing infrastructure and scripts - Remove frontend test files (html-preservation, style-detection tests) - Update configuration and auth improvements - Simplify demo structure to focus on core functionality This cleanup reduces repository size and focuses on essential demos.
@@ -1,140 +0,0 @@
|
||||
# Insertr Testing Infrastructure Report
|
||||
|
||||
## Overview
|
||||
|
||||
Successfully established a comprehensive testing infrastructure for insertr CMS across real-world websites, moving beyond the single demo site to demonstrate insertr's versatility across different site types and frameworks.
|
||||
|
||||
## Infrastructure Components
|
||||
|
||||
### ✅ Directory Structure
|
||||
```
|
||||
demos/
|
||||
├── simple/ # Simple vanilla CSS sites
|
||||
│ └── dan-eden-portfolio/ # ✅ COMPLETE
|
||||
├── framework-based/ # CSS framework sites
|
||||
├── complex/ # Complex layouts
|
||||
├── templates/ # Template files
|
||||
├── scripts/ # Automation utilities
|
||||
└── results/ # Testing documentation
|
||||
```
|
||||
|
||||
### ✅ Automation Scripts
|
||||
- **`download-site.js`** - wget-based site downloader with assets
|
||||
- **`enhance-dan-eden.py`** - Site-specific insertr class injection
|
||||
- **Server Integration** - Sites registered in insertr.yaml
|
||||
|
||||
## Test Site: Dan Eden Portfolio
|
||||
|
||||
### Site Characteristics
|
||||
- **URL**: https://daneden.me
|
||||
- **Framework**: Next.js with CSS Modules
|
||||
- **Complexity**: Simple - ideal for baseline testing
|
||||
- **Content**: Personal portfolio, project descriptions, bio
|
||||
|
||||
### Enhancement Results
|
||||
✅ **7 elements** successfully enhanced with insertr classes:
|
||||
1. App descriptions (Ora, Solstice)
|
||||
2. Action buttons ("Learn more →", "Read the post →")
|
||||
3. Talk title ("Where We Can Go")
|
||||
4. Content spans with auto-generated IDs
|
||||
|
||||
### Technical Validation
|
||||
- ✅ **Content ID Generation**: `index-span-4ba35c`, `index-span-7-3dcb19`
|
||||
- ✅ **Content Type Detection**: All elements correctly identified as "markdown"
|
||||
- ✅ **Asset Preservation**: Next.js bundles, CSS, images intact
|
||||
- ✅ **Server Registration**: Site registered as "dan-eden-test"
|
||||
- ✅ **Enhancement Pipeline**: `./insertr enhance` worked seamlessly
|
||||
|
||||
## Key Findings
|
||||
|
||||
### ✅ Zero Configuration Success
|
||||
- No configuration files needed - just `class="insertr"`
|
||||
- Insertr automatically detected content types and generated IDs
|
||||
- Works seamlessly with CSS Modules and Next.js
|
||||
|
||||
### ✅ Framework Compatibility
|
||||
- CSS Modules don't interfere with insertr classes
|
||||
- Complex asset paths preserved correctly
|
||||
- Next.js client-side hydration unaffected
|
||||
|
||||
### ✅ Developer Experience
|
||||
- Simple enhancement workflow: download → add classes → enhance → serve
|
||||
|
||||
- Clear feedback on enhancement results
|
||||
|
||||
## Comparison with Demo Site
|
||||
|
||||
| Feature | Demo Site | Dan Eden Portfolio |
|
||||
|---------|-----------|-------------------|
|
||||
| Framework | Vanilla HTML/CSS | Next.js + CSS Modules |
|
||||
| Complexity | Designed for insertr | Real-world site |
|
||||
| Content Types | All types tested | Primarily text/markdown |
|
||||
| Asset Handling | Simple | Complex (fonts, images, JS bundles) |
|
||||
| Enhancement | Pre-configured | Added insertr classes manually |
|
||||
|
||||
## Next Steps for Expansion
|
||||
|
||||
### Immediate (Simple Sites)
|
||||
- [ ] Download GitHub Pages portfolio sites
|
||||
- [ ] Test Bootstrap documentation pages
|
||||
- [ ] Test Jekyll blog sites
|
||||
|
||||
### Framework-Based Sites
|
||||
- [ ] Tailwind CSS marketing pages
|
||||
- [ ] Vue.js documentation
|
||||
- [ ] React component library sites
|
||||
|
||||
### Complex Sites
|
||||
- [ ] Stripe product pages (advanced layouts)
|
||||
- [ ] Corporate sites with multiple sections
|
||||
- [ ] E-commerce product pages
|
||||
|
||||
## Technical Insights
|
||||
|
||||
### What Works Well
|
||||
1. **CSS Framework Agnostic** - Insertr classes don't conflict with existing CSS
|
||||
2. **Asset Preservation** - Complex build assets maintained perfectly
|
||||
3. **Content Type Detection** - Smart defaults for different HTML elements
|
||||
4. **ID Generation** - Deterministic, content-based IDs
|
||||
|
||||
### Areas for Future Testing
|
||||
1. **JavaScript Interactions** - Test sites with heavy client-side JS
|
||||
2. **Dynamic Content** - Sites with client-side routing
|
||||
3. **Complex Forms** - Contact forms, search interfaces
|
||||
4. **Media Rich Content** - Image galleries, video embeds
|
||||
|
||||
## Success Metrics
|
||||
|
||||
- ✅ **Infrastructure**: Complete test site collection structure
|
||||
- ✅ **Automation**: Working download and enhancement scripts
|
||||
- ✅ **Real-world validation**: Successfully enhanced professional portfolio
|
||||
- ✅ **Framework compatibility**: Next.js + CSS Modules working
|
||||
- ✅ **Zero-config philosophy**: No configuration files needed
|
||||
- ✅ **Demo system**: Easy-to-use demo commands for testing
|
||||
|
||||
## Demo Commands
|
||||
|
||||
### **Quick Demo Access**
|
||||
```bash
|
||||
# Start default insertr demo
|
||||
just demo
|
||||
|
||||
# Start Dan Eden portfolio demo
|
||||
just demo dan-eden
|
||||
|
||||
# List all available demos
|
||||
just list-demos
|
||||
|
||||
# Test demo infrastructure
|
||||
node demos/scripts/test-demo.js
|
||||
```
|
||||
|
||||
### **Demo Sites Available**
|
||||
1. **Default Demo** (`just demo`) - Built-in insertr showcase
|
||||
2. **Dan Eden Portfolio** (`just demo dan-eden`) - Real-world Next.js site
|
||||
|
||||
## Conclusion
|
||||
|
||||
The testing infrastructure is successfully established and validated. Dan Eden's portfolio demonstrates that insertr works seamlessly with real-world sites using modern frameworks. The zero-configuration approach proves effective - developers only need to add `class="insertr"` to make content editable.
|
||||
|
||||
Ready to expand testing to additional site types and complexity levels.
|
||||
@@ -1,46 +0,0 @@
|
||||
# Dan Eden Portfolio
|
||||
|
||||
## Original URL
|
||||
https://daneden.me
|
||||
|
||||
## Downloaded
|
||||
2025-09-11T15:48:33.014Z
|
||||
|
||||
## Site Characteristics
|
||||
- **Framework**: Next.js with CSS Modules
|
||||
- **Styling**: Clean, minimal design with CSS-in-JS
|
||||
- **Content**: Personal portfolio with bio, projects, and talks
|
||||
- **Complexity**: Simple - good for basic insertr testing
|
||||
|
||||
## Insertr Enhancement Status
|
||||
- [x] Content sections identified
|
||||
- [x] Insertr classes added to key elements
|
||||
- [x] Enhanced version created
|
||||
- [x] Insertr functionality tested
|
||||
- [x] Results documented
|
||||
|
||||
## Test Results
|
||||
✅ **Enhancement Success**: 7 elements successfully enhanced with insertr
|
||||
✅ **Server Integration**: Site registered as "dan-eden-test" in insertr.yaml
|
||||
✅ **Content ID Generation**: Auto-generated IDs like "index-span-4ba35c"
|
||||
✅ **Content Type Detection**: All elements correctly identified as "markdown" type
|
||||
✅ **Asset Preservation**: All original Next.js assets and styling preserved
|
||||
|
||||
## Enhanced Elements
|
||||
1. **Main bio paragraph** (`<p class="home_xxl__iX0Z1 insertr">`) - Product designer introduction
|
||||
2. **Company name** (`<span class="insertr">Meta Reality Labs</span>`) - Current employer
|
||||
3. **App descriptions** - Ora and Solstice project descriptions
|
||||
4. **Talk content** - "Where We Can Go" title and description
|
||||
5. **Action buttons** - "Learn more" and "Read the post" links
|
||||
|
||||
## Testing Notes
|
||||
- Clean HTML structure ideal for insertr compatibility
|
||||
- CSS Modules shouldn't interfere with insertr classes
|
||||
- Good test case for semantic content editing
|
||||
- Minimal JavaScript complexity
|
||||
|
||||
## Files
|
||||
- `index.html.original` - Original downloaded version
|
||||
- `index.html` - Enhanced version with insertr classes
|
||||
- `insertr-config.json` - Configuration for testing
|
||||
- `_next/` - Next.js assets and styles
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,185 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="stylesheet" href="_next/static/css/53b634381c30ef2d.css" />
|
||||
<link rel="stylesheet" href="_next/static/css/e0f28746eee6ced7.css" />
|
||||
<title>Daniel Eden, Designer</title>
|
||||
<meta name="description" content="The personal site, blog, and portfolio of Daniel Eden, a designer writing and thinking about design systems."/>
|
||||
<link rel="icon" href="icon.jpeg" type="image/jpeg" sizes="512x512"/>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="styles_skipLink__2dFX9">
|
||||
<a href="index.html#content">Skip to content</a>
|
||||
</nav>
|
||||
<div class="home_root__o7QEV">
|
||||
<div class="home_intro__8dWW4 undefined">
|
||||
<p class="home_xxl__iX0Z1">Daniel Eden is a Product Designer at<!-- --> <a href="#meta">Meta Reality Labs</a>
|
||||
, working on Avatars & Identity, helping people express their full selves in the Metaverse. He spends his time<!-- --> <a href="#">writing</a>
|
||||
, thinking,<!-- --> <a rel="me" href="#social">posting</a>
|
||||
, and talking about Design Systems: how they scale, how they break, and the people that maintain them.</p>
|
||||
</div>
|
||||
<section class="styles_root__ezqfE">
|
||||
<div class="styles_card__Zgiwg styles_highlight__PDTTu">
|
||||
<h2>Ora</h2>
|
||||
<p>An app about time for iPhone, iPad, and Apple Watch.</p>
|
||||
<div>
|
||||
<div class="styles_root__rUjFN" style="aspect-ratio:0.49300441826215025">
|
||||
<div style="width:1339px;height:2716px;background:#f8f9fa;border:8px solid #333;border-radius:60px;position:relative;margin:0 auto;">
|
||||
<div style="position:absolute;top:100px;left:100px;right:100px;bottom:100px;background:#000;border-radius:40px;overflow:hidden;"></div>
|
||||
</div>
|
||||
<div class="styles_children__D9Nsi" style="padding:5.925%">
|
||||
<div style="width:589px;height:1278px;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);border-radius:20px;display:flex;align-items:center;justify-content:center;color:white;font-size:16px;font-weight:500;text-align:center;box-shadow:0 10px 30px rgba(0,0,0,0.3);">
|
||||
<div>
|
||||
<div style="font-size:24px;margin-bottom:10px;">📱</div>
|
||||
<div>App Preview</div>
|
||||
<div style="font-size:12px;opacity:0.8;margin-top:5px;">iOS App Demo</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="styles_button__OAX5k" href="#">Learn more →</a>
|
||||
</div>
|
||||
<div class="styles_card__Zgiwg styles_highlight__PDTTu">
|
||||
<h2>Solstice</h2>
|
||||
<p>An app about daylight for iPhone, iPad, Mac, Apple Watch, and Apple Vision Pro.</p>
|
||||
<div>
|
||||
<div class="styles_root__rUjFN" style="aspect-ratio:0.49300441826215025">
|
||||
<div style="width:1339px;height:2716px;background:#f8f9fa;border:8px solid #333;border-radius:60px;position:relative;margin:0 auto;">
|
||||
<div style="position:absolute;top:100px;left:100px;right:100px;bottom:100px;background:#000;border-radius:40px;overflow:hidden;"></div>
|
||||
</div>
|
||||
<div class="styles_children__D9Nsi" style="padding:5.925%">
|
||||
<div style="width:589px;height:1278px;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);border-radius:20px;display:flex;align-items:center;justify-content:center;color:white;font-size:16px;font-weight:500;text-align:center;box-shadow:0 10px 30px rgba(0,0,0,0.3);">
|
||||
<div>
|
||||
<div style="font-size:24px;margin-bottom:10px;">📱</div>
|
||||
<div>App Preview</div>
|
||||
<div style="font-size:12px;opacity:0.8;margin-top:5px;">iOS App Demo</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="styles_button__OAX5k" href="#">Learn more →</a>
|
||||
</div>
|
||||
<div class="styles_card__Zgiwg styles_highlight__PDTTu">
|
||||
<h2>Where We Can Go</h2>
|
||||
<p>A<!-- --> <a href="https://www.clarityconf.com/session/where-we-can-go">conference talk</a>
|
||||
<!-- -->and <a href="#">essay</a>
|
||||
about design systems and design tools.</p>
|
||||
<div class="styles_stretcher__vQB9_">
|
||||
<div class="styles_wwcgImage__6T0vh" style="width:698px;height:707px;background:linear-gradient(45deg, #4f46e5 0%, #06b6d4 100%);border-radius:12px;display:flex;align-items:center;justify-content:center;color:white;font-size:18px;font-weight:600;text-align:center;box-shadow:0 8px 25px rgba(79, 70, 229, 0.3);">
|
||||
<div><div style="font-size:48px;margin-bottom:20px;">🎤</div><div>"Where We Can Go"</div><div style="font-size:14px;opacity:0.9;margin-top:8px;">Conference Presentation</div></div></div>
|
||||
<a class="styles_button__OAX5k" href="#">Read the post →</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="styles_card__Zgiwg styles_highlight__PDTTu">
|
||||
<h2>Zeitgeist</h2>
|
||||
<p>An app for <a href="https://vercel.com">Vercel</a>
|
||||
developers for iPhone, iPad, and Mac.</p>
|
||||
<div>
|
||||
<div class="styles_root__rUjFN" style="aspect-ratio:0.49300441826215025">
|
||||
<div style="width:1339px;height:2716px;background:#f8f9fa;border:8px solid #333;border-radius:60px;position:relative;margin:0 auto;">
|
||||
<div style="position:absolute;top:100px;left:100px;right:100px;bottom:100px;background:#000;border-radius:40px;overflow:hidden;"></div>
|
||||
</div>
|
||||
<div class="styles_children__D9Nsi" style="padding:5.925%">
|
||||
<div style="width:589px;height:1278px;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);border-radius:20px;display:flex;align-items:center;justify-content:center;color:white;font-size:16px;font-weight:500;text-align:center;box-shadow:0 10px 30px rgba(0,0,0,0.3);">
|
||||
<div>
|
||||
<div style="font-size:24px;margin-bottom:10px;">📱</div>
|
||||
<div>App Preview</div>
|
||||
<div style="font-size:12px;opacity:0.8;margin-top:5px;">iOS App Demo</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="styles_button__OAX5k" href="#">Learn more →</a>
|
||||
</div>
|
||||
</section>
|
||||
<div>
|
||||
<table class="styles_root__loSke">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>2025</th>
|
||||
<td>
|
||||
<a href="https://ora.daneden.me">Ora</a>
|
||||
<p class="small sans meta">iOS App</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>2021</th>
|
||||
<td>
|
||||
<a href="https://apps.apple.com/app/solstice/id1547580907">Solstice</a>
|
||||
<p class="small sans meta">iOS App</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://apps.apple.com/gb/app/broadcast-just-tweet/id1574084018">Broadcast</a>
|
||||
<p class="small sans meta">iOS App</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>2020</th>
|
||||
<td>
|
||||
<a href="https://apps.apple.com/app/eventually/id1532457758">Eventually</a>
|
||||
<p class="small sans meta">iOS App</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://apps.apple.com/app/zeitgeist/id1526052028">Zeitgeist</a>
|
||||
<p class="small sans meta">iOS App</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>2019</th>
|
||||
<td>
|
||||
<a href="https://www.clarityconf.com/session/where-we-can-go">Clarity 2019</a>
|
||||
<p class="small sans meta">“Where We Can Go”</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>2018</th>
|
||||
<td>
|
||||
<a href="https://www.loversmagazine.com/interviews/daniel-eden">Lovers Magazine</a>
|
||||
<p class="small sans meta">Interview</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>2016</th>
|
||||
<td>
|
||||
<a href="https://designdetails.fm/episodes/4f977647">Design Details</a>
|
||||
<p class="small sans meta">Podcast</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.youtube.com/watch?v=G_58lgdPdw8">One Day Out 2016</a>
|
||||
<p class="small sans meta">“System Failure”</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>2015</th>
|
||||
<td>
|
||||
<a href="https://www.youtube.com/watch?v=zmjfh099zYg">dotCSS 2015</a>
|
||||
<p class="small sans meta">“Move Slow and Fix Things”</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!--/$-->
|
||||
<!--/$-->
|
||||
<!--/$-->
|
||||
<!--/$-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"site_name": "Dan Eden Portfolio",
|
||||
"description": "Personal portfolio site with clean design and minimal styling",
|
||||
"base_url": "https://daneden.me",
|
||||
"content_sections": [
|
||||
{
|
||||
"selector": ".home_xxl__iX0Z1",
|
||||
"type": "markdown",
|
||||
"editable": true,
|
||||
"description": "Main bio paragraph - Product Designer intro"
|
||||
},
|
||||
{
|
||||
"selector": "span.insertr",
|
||||
"type": "text",
|
||||
"editable": true,
|
||||
"description": "Various text content elements (company, descriptions, titles)"
|
||||
}
|
||||
],
|
||||
"css_frameworks": ["Next.js CSS Modules"],
|
||||
"complexity": "simple",
|
||||
"testing_notes": "Clean Next.js site with CSS modules. Good test for CSS-in-JS compatibility and semantic HTML structure."
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Insertr Configuration for Dan Eden Portfolio Demo Site
|
||||
# Specific configuration for the Dan Eden portfolio demo
|
||||
|
||||
# Global settings
|
||||
dev_mode: true # Development mode for demos
|
||||
|
||||
# Database configuration
|
||||
database:
|
||||
path: "./insertr.db" # Shared database with main config
|
||||
|
||||
# Demo-specific configuration
|
||||
demo:
|
||||
site_id: "dan-eden-portfolio" # Unique site ID for Dan Eden demo
|
||||
inject_demo_gate: true # Auto-inject demo gate if no gates exist
|
||||
mock_auth: true # Use mock authentication for demos
|
||||
api_endpoint: "http://localhost:8080/api/content"
|
||||
demo_port: 3000 # Port for live-server
|
||||
|
||||
# CLI enhancement configuration
|
||||
cli:
|
||||
site_id: "dan-eden-portfolio" # Site ID for this demo
|
||||
output: "./demos/dan-eden-portfolio_enhanced" # Output directory for enhanced files
|
||||
inject_demo_gate: true # Inject demo gate in development mode
|
||||
|
||||
# Authentication configuration (for demo)
|
||||
auth:
|
||||
provider: "mock" # Mock auth for demos
|
||||
@@ -32,12 +32,12 @@
|
||||
<section class="services">
|
||||
<div class="container">
|
||||
<h2 class="insertr">Our Story</h2>
|
||||
<div class="insertr-group">
|
||||
<p class="insertr">Founded in 2020, Acme Consulting emerged from a simple observation: small businesses needed access to the same high-quality strategic advice that large corporations receive, but in a format that was accessible, affordable, and actionable.</p>
|
||||
<div class="insertr">
|
||||
<p class="">Founded in 2020, Acme Consulting emerged from a simple observation: small businesses needed access to the same high-quality strategic advice that large corporations receive, but in a format that was accessible, affordable, and actionable.</p>
|
||||
|
||||
<p class="insertr">Our founders, with combined experience of over 30 years in business strategy, operations, and technology, recognized that the traditional consulting model wasn't serving the needs of growing businesses. We set out to change that.</p>
|
||||
<p class="">Our founders, with combined experience of over 30 years in business strategy, operations, and technology, recognized that the traditional consulting model wasn't serving the needs of growing businesses. We set out to change that.</p>
|
||||
|
||||
<p class="insertr">Today, we've helped over **200 businesses** streamline their operations, clarify their strategy, and achieve sustainable growth. Our approach combines proven methodologies with a deep understanding of the unique challenges facing small to medium-sized businesses.</p>
|
||||
<p class="">Today, we've helped over **200 businesses** streamline their operations, clarify their strategy, and achieve sustainable growth. Our approach combines proven methodologies with a deep understanding of the unique challenges facing small to medium-sized businesses.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -110,16 +110,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Test 2: .insertr-group collective editing (should edit all together) -->
|
||||
<div>
|
||||
<h3 class="insertr">Test 2: Group Editing (.insertr-group)</h3>
|
||||
<div class="insertr-group" style="border: 2px solid #007cba; padding: 1rem;">
|
||||
<p class="insertr">This paragraph is part of a <strong>group</strong>.</p>
|
||||
<p class="insertr">Clicking anywhere should open one markdown editor with <em>rich formatting</em>.</p>
|
||||
<p class="insertr">All content should be <strong>editable together</strong> as markdown with proper <em>HTML conversion</em>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Test 3: Link formatting options -->
|
||||
<div style="margin-top: 2rem;">
|
||||
<h3 class="insertr">Test 3: Link Formatting Styles</h3>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<!doctype html><html itemscope class="snap dark" lang=nb-no itemtype=http://schema.org/WebPage><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=5"><meta name=theme-name content="hugoplate"><link rel="shortcut icon" href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon type=image/png sizes=48x48 href=/images/favicon_hu_e204b38d3bc06b2.png><link rel=icon type=image/png sizes=96x96 href=/images/favicon_hu_fc3e2a6bee6bf4a7.png><link rel=apple-touch-icon sizes=144x144 href=/images/favicon_hu_e81cb40e0831378f.png><link rel=manifest href=/manifest.webmanifest><meta name=msapplication-TileColor content="#ddd"><meta name=theme-color content="#ffffff"><base href=https://devigo.no/404.html><title>DEVIGO</title><meta name=keywords content="Salgstrening,Salgskurs,Kundeservice,Closing,Salgsutvikling"><meta name=description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=author content="jnss.me"><meta property="og:image:type" content="image/svg+xml"><meta property="og:title" content="DEVIGO"><meta property="og:description" content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta property="og:type" content="website"><meta property="og:url" content="https://devigo.no/404.html"><meta name=twitter:title content="DEVIGO"><meta name=twitter:description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=twitter:creator content="@jnss.me"><script>let indexURL="https://devigo.no/searchindex.json",includeSectionsInSearch=["blog"],search_no_results="0 results for",search_initial_message=""</script><meta http-equiv=x-dns-prefetch-control content="on"><link rel=preconnect href=https://use.fontawesome.com crossorigin><link rel=preconnect href=//cdnjs.cloudflare.com><link rel=preconnect href=//www.googletagmanager.com><link rel=preconnect href=//www.google-analytics.com><link rel=dns-prefetch href=https://use.fontawesome.com><link rel=dns-prefetch href=//ajax.googleapis.com><link rel=dns-prefetch href=//cdnjs.cloudflare.com><link rel=dns-prefetch href=//www.googletagmanager.com><link rel=dns-prefetch href=//www.google-analytics.com><link rel=dns-prefetch href=//fonts.googleapis.com><link rel=dns-prefetch href=//connect.facebook.net><link rel=dns-prefetch href=//platform.linkedin.com><link rel=dns-prefetch href=//platform.twitter.com><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;600&family=Signika:wght@500;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script><link href=/css/style.min.7689eba5aa202a985c862a65b270b80d749060ba27871723c7580d379f55eee9.css integrity="sha256-donrpaogKphchiplsnC4DXSQYLonhxcjx1gNN59V7uk=" rel=stylesheet><link defer async rel=stylesheet href=/css/style-lazy.min.85575460e013a8f1e96ccd0560c44120e7e785297933c62e65534d5652eebe78.css integrity="sha256-hVdUYOATqPHpbM0FYMRBIOfnhSl5M8YuZVNNVlLuvng=" media=print onload='this.media="all",this.onload=null'></head><body><header class="header sticky top-0 z-30"><nav class="navbar container"><div class=order-0><a class="navbar-brand block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><input id=nav-toggle type=checkbox class=hidden>
|
||||
<label for=nav-toggle class="order-3 cursor-pointer flex items-center lg:hidden text-text-dark dark:text-white lg:order-1"><svg id="show-button" class="h-6 fill-current block" viewBox="0 0 20 20"><title>Menu Open</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0V0z"/></svg>
|
||||
<svg id="hide-button" class="h-6 fill-current hidden" viewBox="0 0 20 20"><title>Menu Close</title><polygon points="11 9 22 9 22 11 11 11 11 22 9 22 9 11 -2 11 -2 9 9 9 9 -2 11 -2" transform="rotate(45 10 10)"/></svg></label><ul id=nav-menu class="navbar-nav order-3 hidden lg:flex w-full pb-6 lg:order-1 lg:w-auto lg:space-x-2 lg:pb-0 xl:space-x-8"><li class=nav-item><a class=nav-link href=/#feat>Kurs og Utvikling</a></li><li class=nav-item><a class=nav-link href=/#about>Om oss</a></li><li class=nav-item><a class=nav-link href=/#testimonials>Referanser</a></li><li class=nav-item><a class=nav-link href=/#contact>Kontakt</a></li><li class="mt-4 inline-block lg:hidden"><a class="btn btn-outline-primary btn-sm" href=/#contact>selg mer!</a></li></ul><div class="order-1 ml-auto flex items-center md:order-2 lg:ml-0"><a href=/#contact class="btn btn-outline-primary btn-sm hidden lg:inline-block">selg mer!</a></div></nav></header><main><section class="section-sm text-center"><div class=container><div class="row justify-center"><div class="sm:col-10 md:col-8 lg:col-6"><span class="text-[8rem] block font-bold text-text-dark dark:text-darkmode-text-dark">404</span><h1 class="h2 mb-4">Oi. Vi fant ikke denne siden</h1><div class=content><p>Siden du leter etter eksisterer ikke.</p></div><a href=/ class="btn btn-primary mt-8">Tilbake til hjem</a></div></div></div></section></main><footer class="bg-light dark:bg-darkmode-light relative z-10"><div class=container><div class="row items-center py-10"><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:text-left"><a class="navbar-brand inline-block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><div class="lg:col-6 mb-8 text-center lg:mb-0"><ul></ul></div><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:mt-0 lg:text-right"><ul class=social-icons><li><a target=_blank aria-label=facebook rel="nofollow noopener" href=https://www.facebook.com/><i class="fab fa-facebook"></i></a></li><li><a target=_blank aria-label=linkedin rel="nofollow noopener" href=https://www.linkedin.com/><i class="fab fa-linkedin"></i></a></li></ul></div></div></div><div class="border-border dark:border-darkmode-border border-t py-7"><div class="text-text-light dark:text-darkmode-text-light container text-center"><p>Utviklet og levert av Joakim Schäffer</p></div></div></footer><script crossorigin=anonymous integrity="sha256-NoqgJlugD+mB4Hf8AueiiPBQSb7ASin+oZS0fYEdtiA=" src=/js/script.min.368aa0265ba00fe981e077fc02e7a288f05049bec04a29fea194b47d811db620.js></script><script defer async crossorigin=anonymous integrity="sha256-GrJYek7lbMIWuRNCD5ImM6PNBJ72jzIDo/urKVLfPDk=" src=/js/script-lazy.min.1ab2587a4ee56cc216b913420f922633a3cd049ef68f3203a3fbab2952df3c39.js></script><script>"serviceWorker"in navigator&&navigator.serviceWorker.register("/service-worker.js")</script></body></html>
|
||||
@@ -1,174 +0,0 @@
|
||||
# Devigo - Real Norwegian Sales Training Website Demo
|
||||
|
||||
## Overview
|
||||
The actual Devigo website showcasing Norwegian sales training services, now enhanced with Insertr for content management. This is a real production website built with Hugo and styled with TailwindCSS.
|
||||
|
||||
## Demo URL
|
||||
Production site: https://devigo.no/
|
||||
Source: Hugo-generated static site from `/home/fitz/devigo-web`
|
||||
|
||||
## Site Characteristics
|
||||
- **Language**: Norwegian (Norsk)
|
||||
- **Industry**: Sales training and development
|
||||
- **Framework**: Hugo static site generator with TailwindCSS
|
||||
- **Content Type**: Sales courses, training programs, testimonials
|
||||
- **Complexity**: Medium - professional sales training business site
|
||||
- **Original Build**: Hugoplate theme with custom Norwegian content
|
||||
- **Demo Adaptations**: Removed production base href and hardcoded URLs for localhost compatibility
|
||||
|
||||
## Insertr Enhancement Status
|
||||
- [x] Content sections identified
|
||||
- [x] Insertr classes added to key elements
|
||||
- [x] Norwegian language content
|
||||
- [x] Business-focused content types
|
||||
- [x] Production-ready structure
|
||||
- [x] Responsive design maintained
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
### 1. **Norwegian Language Content**
|
||||
- Native Norwegian text throughout
|
||||
- Professional business terminology
|
||||
- Cultural context and local market focus
|
||||
|
||||
### 2. **Business Content Types**
|
||||
- **Service descriptions** - Editable service offerings
|
||||
- **Company information** - About section with stats
|
||||
- **Contact details** - Phone, email, address
|
||||
- **Testimonials** - Client feedback and quotes
|
||||
- **Call-to-action buttons** - Lead generation elements
|
||||
|
||||
### 3. **Professional Layout**
|
||||
- **Hero section** with value proposition
|
||||
- **Services grid** with icon-based cards
|
||||
- **About section** with company stats
|
||||
- **Testimonials** with client quotes
|
||||
- **Contact section** with multiple contact methods
|
||||
- **Footer** with organized links
|
||||
|
||||
## Enhanced Elements (39 insertr classes → 51 enhanced items)
|
||||
|
||||
### Navigation & Branding
|
||||
- Navigation links: "Kurs og Utvikling", "Om oss", "Referanser", "Kontakt"
|
||||
- CTA buttons: "selg mer!" (call-to-action)
|
||||
|
||||
### Hero Section
|
||||
- Main headline: "Closing skjer før start"
|
||||
- Value proposition: "Vår tjeneste er å sørge for at dine selgere yter sitt beste. Så selger du mer!"
|
||||
- Action buttons: "Kontakt oss", "Les mer"
|
||||
|
||||
### Training Services
|
||||
- **Kurs for nye selgere** - New sales training program
|
||||
- **Selgerutvikling** - Sales development services
|
||||
- **Emnekurs** - Subject-specific courses
|
||||
- Feature lists and benefit descriptions
|
||||
|
||||
### About Section
|
||||
- Company background and history
|
||||
- **Patric Rustan** - Founder profile and bio
|
||||
- Company values and approach
|
||||
|
||||
### Testimonials
|
||||
- Client testimonials section
|
||||
- Customer quotes and attributions
|
||||
- Social proof elements
|
||||
|
||||
### Contact Section
|
||||
- Contact form with Norwegian labels
|
||||
- Form fields: "Navn", "Epost", "Noe mer?"
|
||||
- Submit button: "Send"
|
||||
|
||||
### Footer
|
||||
- Social media links (Facebook, LinkedIn)
|
||||
- Developer credit: "Utviklet og levert av Joakim Schäffer"
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
### Content Type Distribution
|
||||
- **Text elements**: 25+ (headings, labels, contact info)
|
||||
- **Markdown elements**: 15+ (descriptions, testimonials, paragraphs)
|
||||
- **Link elements**: 8+ (navigation, CTAs, footer links)
|
||||
|
||||
### CSS Framework
|
||||
- Modern CSS Grid and Flexbox
|
||||
- CSS Custom Properties (variables)
|
||||
- Responsive design with mobile-first approach
|
||||
- Professional color scheme with Norwegian design sensibilities
|
||||
|
||||
### Asset Structure
|
||||
```
|
||||
assets/
|
||||
├── style.css # Main stylesheet
|
||||
└── images/ # Placeholder images
|
||||
├── devigo-logo.svg
|
||||
├── hero-illustration.svg
|
||||
├── strategy-icon.svg
|
||||
├── tech-icon.svg
|
||||
├── org-icon.svg
|
||||
└── team-photo.jpg
|
||||
```
|
||||
|
||||
## Demo Commands
|
||||
|
||||
```bash
|
||||
# Start unified insertr server (serves all demos)
|
||||
just dev
|
||||
|
||||
# Or enhance devigo demo specifically
|
||||
just enhance-devigo
|
||||
|
||||
# Visit demo at:
|
||||
# http://localhost:8080/sites/devigo-web/
|
||||
```
|
||||
|
||||
## Testing Scenarios
|
||||
|
||||
### 1. **Content Editing**
|
||||
- Edit service descriptions for different market positioning
|
||||
- Update contact information for new office locations
|
||||
- Modify testimonials and client names
|
||||
- Change company statistics and achievements
|
||||
|
||||
### 2. **Marketing Updates**
|
||||
- Update value propositions in hero section
|
||||
- Modify service offerings and descriptions
|
||||
- Change call-to-action button text
|
||||
- Update company messaging
|
||||
|
||||
### 3. **Localization Testing**
|
||||
- Norwegian content editing
|
||||
- Business terminology updates
|
||||
- Cultural context modifications
|
||||
|
||||
## Production Use Case
|
||||
This demo represents a real client website structure, demonstrating how Insertr can be used in production environments for:
|
||||
|
||||
- **Client content management** - Allow clients to update their own content
|
||||
- **Marketing campaigns** - Quick updates to messaging and CTAs
|
||||
- **Service evolution** - Easy updates as services evolve
|
||||
- **Contact information** - Address, phone, email updates
|
||||
- **Team changes** - Staff updates and bio modifications
|
||||
|
||||
## Key Insights
|
||||
|
||||
### What Works Well
|
||||
1. **Professional content** - Real business language and context
|
||||
2. **Norwegian language** - Demonstrates international/multilingual support
|
||||
3. **Service-focused content** - B2B consulting industry specifics
|
||||
4. **Production structure** - Realistic website architecture
|
||||
5. **Client workflow** - Typical client content management needs
|
||||
|
||||
### Business Value
|
||||
- **Reduced client dependency** - Clients can update content independently
|
||||
- **Faster iteration** - No developer needed for content changes
|
||||
- **Better client satisfaction** - Immediate content control
|
||||
- **Maintenance efficiency** - Reduced support tickets for content updates
|
||||
|
||||
## Files
|
||||
- `index.html` - Main website with insertr classes
|
||||
- `assets/style.css` - Professional Norwegian website styles
|
||||
- `assets/images/` - Placeholder images for logos and illustrations
|
||||
- `insertr.yaml` - Configuration for Norwegian consulting demo
|
||||
- `README.md` - This documentation
|
||||
|
||||
This demo showcases Insertr's production readiness with real-world business content in Norwegian, demonstrating international support and professional service industry applications.
|
||||
@@ -1,5 +0,0 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="40" height="40" rx="8" fill="#2563eb"/>
|
||||
<path d="M12 12h6c4.4 0 8 3.6 8 8s-3.6 8-8 8h-6V12z" fill="white"/>
|
||||
<circle cx="15" cy="20" r="2" fill="#2563eb"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 276 B |
@@ -1,10 +0,0 @@
|
||||
<svg width="400" height="300" viewBox="0 0 400 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="400" height="300" fill="#f8fafc"/>
|
||||
<rect x="50" y="50" width="300" height="200" rx="8" fill="white" stroke="#e5e7eb"/>
|
||||
<rect x="80" y="80" width="240" height="20" rx="4" fill="#2563eb"/>
|
||||
<rect x="80" y="120" width="180" height="12" rx="2" fill="#6b7280"/>
|
||||
<rect x="80" y="140" width="200" height="12" rx="2" fill="#6b7280"/>
|
||||
<rect x="80" y="160" width="160" height="12" rx="2" fill="#6b7280"/>
|
||||
<circle cx="300" cy="180" r="40" fill="#3b82f6" opacity="0.2"/>
|
||||
<circle cx="300" cy="180" r="20" fill="#2563eb"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 643 B |
@@ -1,8 +0,0 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="15" cy="8" r="3" fill="white"/>
|
||||
<circle cx="8" cy="20" r="2.5" fill="white"/>
|
||||
<circle cx="22" cy="20" r="2.5" fill="white"/>
|
||||
<path d="M15 12C12.2 12 10 14.2 10 17V18H20V17C20 14.2 17.8 12 15 12Z" fill="white"/>
|
||||
<path d="M8 24C6.3 24 5 22.7 5 21V20.5C5 19.1 6.1 18 7.5 18H8.5C9.9 18 11 19.1 11 20.5V21C11 22.7 9.7 24 8 24Z" fill="white"/>
|
||||
<path d="M22 24C20.3 24 19 22.7 19 21V20.5C19 19.1 20.1 18 21.5 18H22.5C23.9 18 25 19.1 25 20.5V21C25 22.7 23.7 24 22 24Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 602 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 5L25 12L15 19L5 12L15 5Z" fill="white"/>
|
||||
<path d="M15 25L25 18V12L15 19V25Z" fill="white" opacity="0.7"/>
|
||||
<path d="M5 18L15 25V19L5 12V18Z" fill="white" opacity="0.7"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 290 B |
@@ -1,7 +0,0 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="3" y="8" width="24" height="14" rx="2" fill="white"/>
|
||||
<rect x="5" y="10" width="20" height="10" rx="1" fill="white" stroke="rgba(255,255,255,0.3)"/>
|
||||
<circle cx="24" cy="15" r="1" fill="white"/>
|
||||
<rect x="12" y="23" width="6" height="2" fill="white"/>
|
||||
<rect x="8" y="25" width="14" height="1" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 427 B |
@@ -1,484 +0,0 @@
|
||||
/* Devigo - Norwegian Consulting Website Styles */
|
||||
:root {
|
||||
--primary-color: #2563eb;
|
||||
--secondary-color: #1e40af;
|
||||
--accent-color: #3b82f6;
|
||||
--text-primary: #1f2937;
|
||||
--text-secondary: #6b7280;
|
||||
--bg-light: #f8fafc;
|
||||
--bg-white: #ffffff;
|
||||
--border-color: #e5e7eb;
|
||||
--success-color: #10b981;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.navbar {
|
||||
background: var(--bg-white);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.navbar .container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
padding: 5rem 0;
|
||||
background: linear-gradient(135deg, var(--bg-light) 0%, var(--bg-white) 100%);
|
||||
}
|
||||
|
||||
.hero .container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hero-content h1 {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 2rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hero-image img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 0.875rem 2rem;
|
||||
border-radius: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--secondary-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Sections */
|
||||
section {
|
||||
padding: 5rem 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-secondary);
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
max-width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* Services */
|
||||
.services {
|
||||
background: var(--bg-light);
|
||||
}
|
||||
|
||||
.services-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
background: var(--bg-white);
|
||||
padding: 2.5rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.service-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 10px 25px -3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin-bottom: 1.5rem;
|
||||
background: var(--primary-color);
|
||||
border-radius: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.service-icon img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
.service-card h3 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.service-card p {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* About */
|
||||
.about .container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.about h2 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.about p {
|
||||
font-size: 1.125rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.stat {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
display: block;
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.about-image img {
|
||||
width: 100%;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
/* Testimonials */
|
||||
.testimonials {
|
||||
background: var(--bg-light);
|
||||
}
|
||||
|
||||
.testimonial-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.testimonial {
|
||||
background: var(--bg-white);
|
||||
padding: 2.5rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.testimonial blockquote {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.7;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1.5rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.testimonial cite {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Contact */
|
||||
.contact {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.contact h2 {
|
||||
color: white;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.contact p {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 3rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.contact-item h4 {
|
||||
font-size: 1.125rem;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.contact-item p {
|
||||
opacity: 0.8;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.contact .btn-primary {
|
||||
background: white;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.contact .btn-primary:hover {
|
||||
background: var(--bg-light);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
background: var(--text-primary);
|
||||
color: white;
|
||||
padding: 3rem 0 1rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr;
|
||||
gap: 3rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.footer-brand .brand-name {
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.footer-brand p {
|
||||
opacity: 0.7;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.footer-links h4,
|
||||
.footer-contact h4 {
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer-links ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.footer-contact p {
|
||||
opacity: 0.7;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding-top: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-bottom p {
|
||||
opacity: 0.7;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.hero .container,
|
||||
.about .container {
|
||||
grid-template-columns: 1fr;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-content h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.stats {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.testimonial-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.services-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insertr Demo Gate Styles */
|
||||
.insertr-gate {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
margin-left: 0.5rem;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.insertr-gate:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<!doctype html><html itemscope class="snap dark" lang=nb-no itemtype=http://schema.org/WebPage><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=5"><meta name=theme-name content="hugoplate"><link rel="shortcut icon" href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon type=image/png sizes=48x48 href=/images/favicon_hu_e204b38d3bc06b2.png><link rel=icon type=image/png sizes=96x96 href=/images/favicon_hu_fc3e2a6bee6bf4a7.png><link rel=apple-touch-icon sizes=144x144 href=/images/favicon_hu_e81cb40e0831378f.png><link rel=manifest href=/manifest.webmanifest><meta name=msapplication-TileColor content="#ddd"><meta name=theme-color content="#ffffff"><base href=https://devigo.no/authors/><title>Hvem er vi</title><meta name=keywords content="Salgstrening,Salgskurs,Kundeservice,Closing,Salgsutvikling"><meta name=description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=author content="jnss.me"><meta property="og:image:type" content="image/svg+xml"><meta property="og:title" content="Hvem er vi"><meta property="og:description" content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta property="og:type" content="website"><meta property="og:url" content="https://devigo.no/authors/"><meta name=twitter:title content="Hvem er vi"><meta name=twitter:description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=twitter:creator content="@jnss.me"><script>let indexURL="https://devigo.no/searchindex.json",includeSectionsInSearch=["blog"],search_no_results="0 results for",search_initial_message=""</script><meta http-equiv=x-dns-prefetch-control content="on"><link rel=preconnect href=https://use.fontawesome.com crossorigin><link rel=preconnect href=//cdnjs.cloudflare.com><link rel=preconnect href=//www.googletagmanager.com><link rel=preconnect href=//www.google-analytics.com><link rel=dns-prefetch href=https://use.fontawesome.com><link rel=dns-prefetch href=//ajax.googleapis.com><link rel=dns-prefetch href=//cdnjs.cloudflare.com><link rel=dns-prefetch href=//www.googletagmanager.com><link rel=dns-prefetch href=//www.google-analytics.com><link rel=dns-prefetch href=//fonts.googleapis.com><link rel=dns-prefetch href=//connect.facebook.net><link rel=dns-prefetch href=//platform.linkedin.com><link rel=dns-prefetch href=//platform.twitter.com><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;600&family=Signika:wght@500;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script><link href=/css/style.min.7689eba5aa202a985c862a65b270b80d749060ba27871723c7580d379f55eee9.css integrity="sha256-donrpaogKphchiplsnC4DXSQYLonhxcjx1gNN59V7uk=" rel=stylesheet><link defer async rel=stylesheet href=/css/style-lazy.min.85575460e013a8f1e96ccd0560c44120e7e785297933c62e65534d5652eebe78.css integrity="sha256-hVdUYOATqPHpbM0FYMRBIOfnhSl5M8YuZVNNVlLuvng=" media=print onload='this.media="all",this.onload=null'></head><body><header class="header sticky top-0 z-30"><nav class="navbar container"><div class=order-0><a class="navbar-brand block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><input id=nav-toggle type=checkbox class=hidden>
|
||||
<label for=nav-toggle class="order-3 cursor-pointer flex items-center lg:hidden text-text-dark dark:text-white lg:order-1"><svg id="show-button" class="h-6 fill-current block" viewBox="0 0 20 20"><title>Menu Open</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0V0z"/></svg>
|
||||
<svg id="hide-button" class="h-6 fill-current hidden" viewBox="0 0 20 20"><title>Menu Close</title><polygon points="11 9 22 9 22 11 11 11 11 22 9 22 9 11 -2 11 -2 9 9 9 9 -2 11 -2" transform="rotate(45 10 10)"/></svg></label><ul id=nav-menu class="navbar-nav order-3 hidden lg:flex w-full pb-6 lg:order-1 lg:w-auto lg:space-x-2 lg:pb-0 xl:space-x-8"><li class=nav-item><a class=nav-link href=/#feat>Kurs og Utvikling</a></li><li class=nav-item><a class=nav-link href=/#about>Om oss</a></li><li class=nav-item><a class=nav-link href=/#testimonials>Referanser</a></li><li class=nav-item><a class=nav-link href=/#contact>Kontakt</a></li><li class="mt-4 inline-block lg:hidden"><a class="btn btn-outline-primary btn-sm" href=/#contact>selg mer!</a></li></ul><div class="order-1 ml-auto flex items-center md:order-2 lg:ml-0"><a href=/#contact class="btn btn-outline-primary btn-sm hidden lg:inline-block">selg mer!</a></div></nav></header><main><section><div class="container text-center"><div class="from-body to-light dark:from-darkmode-body dark:to-darkmode-light rounded-2xl bg-gradient-to-b px-8 py-14"><h1>Hvem Er Vi</h1><ul class="mt-6 inline-flex space-x-1 capitalize"><li><a class="text-primary dark:text-darkmode-primary" href=https://devigo.no/>Hjem
|
||||
</a><span class="inlin-block mr-1">/</span></li><li><span class="text-primary dark:text-darkmode-primary">Hvem er vi</span></li></ul></div></div></section><section class=section><div class=container><ul class=text-center><li class=m-3><a href=https://devigo.no/authors/patrick-rustan/ class="text-text-dark dark:text-darkmode-text-dark block text-xl">Patric Rustan</a></li></ul></div></section></main><footer class="bg-light dark:bg-darkmode-light relative z-10"><div class=container><div class="row items-center py-10"><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:text-left"><a class="navbar-brand inline-block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><div class="lg:col-6 mb-8 text-center lg:mb-0"><ul></ul></div><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:mt-0 lg:text-right"><ul class=social-icons><li><a target=_blank aria-label=facebook rel="nofollow noopener" href=https://www.facebook.com/><i class="fab fa-facebook"></i></a></li><li><a target=_blank aria-label=linkedin rel="nofollow noopener" href=https://www.linkedin.com/><i class="fab fa-linkedin"></i></a></li></ul></div></div></div><div class="border-border dark:border-darkmode-border border-t py-7"><div class="text-text-light dark:text-darkmode-text-light container text-center"><p>Utviklet og levert av Joakim Schäffer</p></div></div></footer><script crossorigin=anonymous integrity="sha256-NoqgJlugD+mB4Hf8AueiiPBQSb7ASin+oZS0fYEdtiA=" src=/js/script.min.368aa0265ba00fe981e077fc02e7a288f05049bec04a29fea194b47d811db620.js></script><script defer async crossorigin=anonymous integrity="sha256-GrJYek7lbMIWuRNCD5ImM6PNBJ72jzIDo/urKVLfPDk=" src=/js/script-lazy.min.1ab2587a4ee56cc216b913420f922633a3cd049ef68f3203a3fbab2952df3c39.js></script><script>"serviceWorker"in navigator&&navigator.serviceWorker.register("/service-worker.js")</script></body></html>
|
||||
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hvem er vi on DEVIGO</title><link>https://devigo.no/authors/</link><description>Recent content in Hvem er vi on DEVIGO</description><generator>Hugo</generator><language>nb-no</language><atom:link href="https://devigo.no/authors/index.xml" rel="self" type="application/rss+xml"/><item><title>Patric Rustan</title><link>https://devigo.no/authors/patrick-rustan/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://devigo.no/authors/patrick-rustan/</guid><description><p>Jeg har sju år på folkeskolen, tre år på gymnas, men etter to netter på en husmorsskole gikk utdannelsen i knas!</p></description></item></channel></rss>
|
||||
@@ -1,7 +0,0 @@
|
||||
<!doctype html><html itemscope class="snap dark" lang=nb-no itemtype=http://schema.org/WebPage><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=5"><meta name=theme-name content="hugoplate"><link rel="shortcut icon" href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon type=image/png sizes=48x48 href=/images/favicon_hu_e204b38d3bc06b2.png><link rel=icon type=image/png sizes=96x96 href=/images/favicon_hu_fc3e2a6bee6bf4a7.png><link rel=apple-touch-icon sizes=144x144 href=/images/favicon_hu_e81cb40e0831378f.png><link rel=manifest href=/manifest.webmanifest><meta name=msapplication-TileColor content="#ddd"><meta name=theme-color content="#ffffff"><base href=https://devigo.no/authors/patrick-rustan/><title>Patric Rustan</title><meta name=keywords content="Salgstrening,Salgskurs,Kundeservice,Closing,Salgsutvikling"><meta name=description content="Grunnlegger"><meta name=author content="jnss.me"><meta property="og:image" content="https://devigo.no/images/avatar.png"><meta name=twitter:image content="https://devigo.no/images/avatar.png"><meta name=twitter:card content="summary"><meta property="og:image:width" content="3024"><meta property="og:image:height" content="3024"><meta property="og:image:type" content="image/.png"><meta property="og:title" content="Patric Rustan"><meta property="og:description" content="Grunnlegger"><meta property="og:type" content="website"><meta property="og:url" content="https://devigo.no/authors/patrick-rustan/"><meta name=twitter:title content="Patric Rustan"><meta name=twitter:description content="Grunnlegger"><meta name=twitter:creator content="@jnss.me"><script>let indexURL="https://devigo.no/searchindex.json",includeSectionsInSearch=["blog"],search_no_results="0 results for",search_initial_message=""</script><meta http-equiv=x-dns-prefetch-control content="on"><link rel=preconnect href=https://use.fontawesome.com crossorigin><link rel=preconnect href=//cdnjs.cloudflare.com><link rel=preconnect href=//www.googletagmanager.com><link rel=preconnect href=//www.google-analytics.com><link rel=dns-prefetch href=https://use.fontawesome.com><link rel=dns-prefetch href=//ajax.googleapis.com><link rel=dns-prefetch href=//cdnjs.cloudflare.com><link rel=dns-prefetch href=//www.googletagmanager.com><link rel=dns-prefetch href=//www.google-analytics.com><link rel=dns-prefetch href=//fonts.googleapis.com><link rel=dns-prefetch href=//connect.facebook.net><link rel=dns-prefetch href=//platform.linkedin.com><link rel=dns-prefetch href=//platform.twitter.com><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;600&family=Signika:wght@500;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script><link href=/css/style.min.7689eba5aa202a985c862a65b270b80d749060ba27871723c7580d379f55eee9.css integrity="sha256-donrpaogKphchiplsnC4DXSQYLonhxcjx1gNN59V7uk=" rel=stylesheet><link defer async rel=stylesheet href=/css/style-lazy.min.85575460e013a8f1e96ccd0560c44120e7e785297933c62e65534d5652eebe78.css integrity="sha256-hVdUYOATqPHpbM0FYMRBIOfnhSl5M8YuZVNNVlLuvng=" media=print onload='this.media="all",this.onload=null'></head><body><header class="header sticky top-0 z-30"><nav class="navbar container"><div class=order-0><a class="navbar-brand block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><input id=nav-toggle type=checkbox class=hidden>
|
||||
<label for=nav-toggle class="order-3 cursor-pointer flex items-center lg:hidden text-text-dark dark:text-white lg:order-1"><svg id="show-button" class="h-6 fill-current block" viewBox="0 0 20 20"><title>Menu Open</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0V0z"/></svg>
|
||||
<svg id="hide-button" class="h-6 fill-current hidden" viewBox="0 0 20 20"><title>Menu Close</title><polygon points="11 9 22 9 22 11 11 11 11 22 9 22 9 11 -2 11 -2 9 9 9 9 -2 11 -2" transform="rotate(45 10 10)"/></svg></label><ul id=nav-menu class="navbar-nav order-3 hidden lg:flex w-full pb-6 lg:order-1 lg:w-auto lg:space-x-2 lg:pb-0 xl:space-x-8"><li class=nav-item><a class=nav-link href=/#feat>Kurs og Utvikling</a></li><li class=nav-item><a class=nav-link href=/#about>Om oss</a></li><li class=nav-item><a class=nav-link href=/#testimonials>Referanser</a></li><li class=nav-item><a class=nav-link href=/#contact>Kontakt</a></li><li class="mt-4 inline-block lg:hidden"><a class="btn btn-outline-primary btn-sm" href=/#contact>selg mer!</a></li></ul><div class="order-1 ml-auto flex items-center md:order-2 lg:ml-0"><a href=/#contact class="btn btn-outline-primary btn-sm hidden lg:inline-block">selg mer!</a></div></nav></header><main><section><div class="container text-center"><div class="from-body to-light dark:from-darkmode-body dark:to-darkmode-light rounded-2xl bg-gradient-to-b px-8 py-14"><h1>Patric Rustan</h1><ul class="mt-6 inline-flex space-x-1 capitalize"><li><a class="text-primary dark:text-darkmode-primary" href=https://devigo.no/>Hjem
|
||||
</a><span class="inlin-block mr-1">/</span></li><li><a class="text-primary dark:text-darkmode-primary" href=/authors/>Hvem er vi
|
||||
</a><span class="inlin-block mr-1">/</span></li><li><span class="text-primary dark:text-darkmode-primary">Patric Rustan</span></li></ul></div></div></section><section class=section-sm><div class=container><div class="row justify-center"><div class=lg:col-10><div class=content><p>Jeg har sju år på folkeskolen, tre år på gymnas, men etter to netter på en husmorsskole gikk utdannelsen i knas!</p></div></div></div></div></section></main><footer class="bg-light dark:bg-darkmode-light relative z-10"><div class=container><div class="row items-center py-10"><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:text-left"><a class="navbar-brand inline-block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><div class="lg:col-6 mb-8 text-center lg:mb-0"><ul></ul></div><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:mt-0 lg:text-right"><ul class=social-icons><li><a target=_blank aria-label=facebook rel="nofollow noopener" href=https://www.facebook.com/><i class="fab fa-facebook"></i></a></li><li><a target=_blank aria-label=linkedin rel="nofollow noopener" href=https://www.linkedin.com/><i class="fab fa-linkedin"></i></a></li></ul></div></div></div><div class="border-border dark:border-darkmode-border border-t py-7"><div class="text-text-light dark:text-darkmode-text-light container text-center"><p>Utviklet og levert av Joakim Schäffer</p></div></div></footer><script crossorigin=anonymous integrity="sha256-NoqgJlugD+mB4Hf8AueiiPBQSb7ASin+oZS0fYEdtiA=" src=/js/script.min.368aa0265ba00fe981e077fc02e7a288f05049bec04a29fea194b47d811db620.js></script><script defer async crossorigin=anonymous integrity="sha256-GrJYek7lbMIWuRNCD5ImM6PNBJ72jzIDo/urKVLfPDk=" src=/js/script-lazy.min.1ab2587a4ee56cc216b913420f922633a3cd049ef68f3203a3fbab2952df3c39.js></script><script>"serviceWorker"in navigator&&navigator.serviceWorker.register("/service-worker.js")</script></body></html>
|
||||
@@ -1,6 +0,0 @@
|
||||
<!doctype html><html itemscope class="snap dark" lang=nb-no itemtype=http://schema.org/WebPage><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=5"><meta name=theme-name content="hugoplate"><link rel="shortcut icon" href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon type=image/png sizes=48x48 href=/images/favicon_hu_e204b38d3bc06b2.png><link rel=icon type=image/png sizes=96x96 href=/images/favicon_hu_fc3e2a6bee6bf4a7.png><link rel=apple-touch-icon sizes=144x144 href=/images/favicon_hu_e81cb40e0831378f.png><link rel=manifest href=/manifest.webmanifest><meta name=msapplication-TileColor content="#ddd"><meta name=theme-color content="#ffffff"><base href=https://devigo.no/categories/><title>DEVIGO</title><meta name=keywords content="Salgstrening,Salgskurs,Kundeservice,Closing,Salgsutvikling"><meta name=description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=author content="jnss.me"><meta property="og:image:type" content="image/svg+xml"><meta property="og:title" content="DEVIGO"><meta property="og:description" content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta property="og:type" content="website"><meta property="og:url" content="https://devigo.no/categories/"><meta name=twitter:title content="DEVIGO"><meta name=twitter:description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=twitter:creator content="@jnss.me"><script>let indexURL="https://devigo.no/searchindex.json",includeSectionsInSearch=["blog"],search_no_results="0 results for",search_initial_message=""</script><meta http-equiv=x-dns-prefetch-control content="on"><link rel=preconnect href=https://use.fontawesome.com crossorigin><link rel=preconnect href=//cdnjs.cloudflare.com><link rel=preconnect href=//www.googletagmanager.com><link rel=preconnect href=//www.google-analytics.com><link rel=dns-prefetch href=https://use.fontawesome.com><link rel=dns-prefetch href=//ajax.googleapis.com><link rel=dns-prefetch href=//cdnjs.cloudflare.com><link rel=dns-prefetch href=//www.googletagmanager.com><link rel=dns-prefetch href=//www.google-analytics.com><link rel=dns-prefetch href=//fonts.googleapis.com><link rel=dns-prefetch href=//connect.facebook.net><link rel=dns-prefetch href=//platform.linkedin.com><link rel=dns-prefetch href=//platform.twitter.com><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;600&family=Signika:wght@500;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script><link href=/css/style.min.7689eba5aa202a985c862a65b270b80d749060ba27871723c7580d379f55eee9.css integrity="sha256-donrpaogKphchiplsnC4DXSQYLonhxcjx1gNN59V7uk=" rel=stylesheet><link defer async rel=stylesheet href=/css/style-lazy.min.85575460e013a8f1e96ccd0560c44120e7e785297933c62e65534d5652eebe78.css integrity="sha256-hVdUYOATqPHpbM0FYMRBIOfnhSl5M8YuZVNNVlLuvng=" media=print onload='this.media="all",this.onload=null'></head><body><header class="header sticky top-0 z-30"><nav class="navbar container"><div class=order-0><a class="navbar-brand block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><input id=nav-toggle type=checkbox class=hidden>
|
||||
<label for=nav-toggle class="order-3 cursor-pointer flex items-center lg:hidden text-text-dark dark:text-white lg:order-1"><svg id="show-button" class="h-6 fill-current block" viewBox="0 0 20 20"><title>Menu Open</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0V0z"/></svg>
|
||||
<svg id="hide-button" class="h-6 fill-current hidden" viewBox="0 0 20 20"><title>Menu Close</title><polygon points="11 9 22 9 22 11 11 11 11 22 9 22 9 11 -2 11 -2 9 9 9 9 -2 11 -2" transform="rotate(45 10 10)"/></svg></label><ul id=nav-menu class="navbar-nav order-3 hidden lg:flex w-full pb-6 lg:order-1 lg:w-auto lg:space-x-2 lg:pb-0 xl:space-x-8"><li class=nav-item><a class=nav-link href=/#feat>Kurs og Utvikling</a></li><li class=nav-item><a class=nav-link href=/#about>Om oss</a></li><li class=nav-item><a class=nav-link href=/#testimonials>Referanser</a></li><li class=nav-item><a class=nav-link href=/#contact>Kontakt</a></li><li class="mt-4 inline-block lg:hidden"><a class="btn btn-outline-primary btn-sm" href=/#contact>selg mer!</a></li></ul><div class="order-1 ml-auto flex items-center md:order-2 lg:ml-0"><a href=/#contact class="btn btn-outline-primary btn-sm hidden lg:inline-block">selg mer!</a></div></nav></header><main><section><div class="container text-center"><div class="from-body to-light dark:from-darkmode-body dark:to-darkmode-light rounded-2xl bg-gradient-to-b px-8 py-14"><h1>Categories</h1><ul class="mt-6 inline-flex space-x-1 capitalize"><li><a class="text-primary dark:text-darkmode-primary" href=https://devigo.no/>Hjem
|
||||
</a><span class="inlin-block mr-1">/</span></li><li><span class="text-primary dark:text-darkmode-primary">Categories</span></li></ul></div></div></section><section class=section><div class=container><ul class=text-center></ul></div></section></main><footer class="bg-light dark:bg-darkmode-light relative z-10"><div class=container><div class="row items-center py-10"><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:text-left"><a class="navbar-brand inline-block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><div class="lg:col-6 mb-8 text-center lg:mb-0"><ul></ul></div><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:mt-0 lg:text-right"><ul class=social-icons><li><a target=_blank aria-label=facebook rel="nofollow noopener" href=https://www.facebook.com/><i class="fab fa-facebook"></i></a></li><li><a target=_blank aria-label=linkedin rel="nofollow noopener" href=https://www.linkedin.com/><i class="fab fa-linkedin"></i></a></li></ul></div></div></div><div class="border-border dark:border-darkmode-border border-t py-7"><div class="text-text-light dark:text-darkmode-text-light container text-center"><p>Utviklet og levert av Joakim Schäffer</p></div></div></footer><script crossorigin=anonymous integrity="sha256-NoqgJlugD+mB4Hf8AueiiPBQSb7ASin+oZS0fYEdtiA=" src=/js/script.min.368aa0265ba00fe981e077fc02e7a288f05049bec04a29fea194b47d811db620.js></script><script defer async crossorigin=anonymous integrity="sha256-GrJYek7lbMIWuRNCD5ImM6PNBJ72jzIDo/urKVLfPDk=" src=/js/script-lazy.min.1ab2587a4ee56cc216b913420f922633a3cd049ef68f3203a3fbab2952df3c39.js></script><script>"serviceWorker"in navigator&&navigator.serviceWorker.register("/service-worker.js")</script></body></html>
|
||||
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Categories on DEVIGO</title><link>https://devigo.no/categories/</link><description>Recent content in Categories on DEVIGO</description><generator>Hugo</generator><language>nb-no</language><atom:link href="https://devigo.no/categories/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
||||
@@ -1 +0,0 @@
|
||||
/* Placeholder CSS file for Hugo import - demo only */
|
||||
@@ -1,5 +0,0 @@
|
||||
@import "tailwindcss";@plugin "@tailwindcss/forms";@plugin "@tailwindcss/typography";@plugin "./tailwind-plugin/tw-theme";@plugin "./tailwind-plugin/tw-bs-grid";@source "hugo_stats.json";@custom-variant dark (&:where(.dark,.dark *));@import "./safe.css";@import "./utilities.css";@layer base{@import "./base.css";
|
||||
}@layer components{@import "./components.css";
|
||||
@import "./navigation.css";
|
||||
@import "./buttons.css";
|
||||
}@import "search.css";@import "social-share.css";@import "gallery-slider.css";@import "images.css";@import "toc.css";@import "tab.css";@import "accordion.css";@import "modal.css";@import "notice.css";@import "module-overrides.css";@import "custom.css"
|
||||
@@ -1 +0,0 @@
|
||||
/* Placeholder CSS file for Hugo import - demo only */
|
||||
@@ -1 +0,0 @@
|
||||
/* Placeholder CSS file for Hugo import - demo only */
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 9.5 MiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="292.56464"
|
||||
height="227.457"
|
||||
viewBox="0 0 292.56463 227.457"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="rustan_logo.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="crown_logo.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="px"
|
||||
inkscape:export-bgcolor="#ffffffff"
|
||||
inkscape:zoom="0.70710678"
|
||||
inkscape:cx="-62.225397"
|
||||
inkscape:cy="4.2426407"
|
||||
inkscape:window-width="2048"
|
||||
inkscape:window-height="1084"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
showborder="false" /><defs
|
||||
id="defs1"><linearGradient
|
||||
id="linearGradient27"
|
||||
inkscape:collect="always"
|
||||
inkscape:label="rustan"><stop
|
||||
style="stop-color:#fdbe00;stop-opacity:0.60326397;"
|
||||
offset="0"
|
||||
id="stop24" /><stop
|
||||
style="stop-color:#ffbf00;stop-opacity:1;"
|
||||
offset="0.24895304"
|
||||
id="stop25" /><stop
|
||||
style="stop-color:#ffbf00;stop-opacity:1;"
|
||||
offset="0.74559295"
|
||||
id="stop26" /><stop
|
||||
style="stop-color:#ffbf00;stop-opacity:0.5;"
|
||||
offset="1"
|
||||
id="stop27" /></linearGradient><linearGradient
|
||||
id="linearGradient14"
|
||||
inkscape:swatch="gradient"><stop
|
||||
style="stop-color:#99a024;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15" /><stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0.59368014"
|
||||
id="stop14" /></linearGradient><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath5"><rect
|
||||
style="fill:none;stroke-width:27.5398;stroke-dasharray:none"
|
||||
id="rect6"
|
||||
width="90.332893"
|
||||
height="74.599762"
|
||||
x="361.33157"
|
||||
y="112.42998"
|
||||
ry="0" /></clipPath><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath12"><rect
|
||||
style="opacity:0.73;fill:#808080;stroke:none;stroke-width:25.8093;stroke-dasharray:none"
|
||||
id="rect13"
|
||||
width="110.44633"
|
||||
height="80.25853"
|
||||
x="-312.28189"
|
||||
y="182.2175"
|
||||
ry="8.3687115" /></clipPath><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient27"
|
||||
id="linearGradient24"
|
||||
x1="-296.4393"
|
||||
y1="221.15575"
|
||||
x2="-212.80244"
|
||||
y2="221.15575"
|
||||
gradientUnits="userSpaceOnUse" /></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-233.902,-270.86589)"><path
|
||||
style="fill:url(#linearGradient24)"
|
||||
d="m -285.14661,253.06175 c 0,-3.27597 -3.01679,-8.21941 -4.76202,-12.59971 -2.09864,-4.37784 -3.70203,-9.01442 -4.72242,-13.81898 -0.60695,-3.61222 -4.04453,-10.73727 0.53578,-11.80999 4.39319,1.1031 7.94714,4.26038 12.04436,6.12662 3.23761,2.23586 7.28439,5.57271 9.58711,7.10235 l 2.19312,1.22999 c 0.94505,-3.41336 1.56239,-5.61217 2.03457,-7.61507 0.46237,-1.91352 2.18619,-8.26566 2.60743,-9.89224 0.48344,-2.43766 1.55082,-6.8614 1.9827,-9.00938 0.45042,-1.60014 0.84935,-3.58747 1.36445,-5.88457 0.5294,-1.53773 1.45063,-5.29604 2.72265,-6.4671 2.3941,-5.13058 5.24471,2.37821 5.65585,3.20575 0.49247,1.10188 1.0787,2.30588 1.55864,3.2392 0.129,0.18389 1.52579,3.57929 2.06874,4.65835 1.80839,4.10212 2.34489,5.50629 3.80329,9.92734 1.75167,2.66332 2.72647,8.53905 6.02661,6.00401 2.60713,-3.31457 4.9425,-5.93137 6.91298,-9.09058 2.81888,-3.23294 5.13654,-6.87355 7.95219,-10.07102 4.62049,-3.19468 5.16556,3.82876 6.17581,7.08622 0.99604,4.2703 1.10667,8.76081 1.92175,13.23669 0.53491,5.0654 1.75127,10.02016 3.11885,14.78118 1.129,3.773 2.20003,10.6979 1.10215,12.93656 -0.36642,0.87934 -2.73644,0.90542 -4.09891,1.05545 -4.33279,0.37052 -8.15208,0.94811 -13.01617,1.42118 -4.40499,0.42921 -5.9455,0.65948 -10.4286,1.19674 -11.72768,1.32284 -23.15617,2.1081 -34.74205,3.01013 -2.51895,0.1617 -7.62257,1.34582 -9.59886,0.0409 z m 5.03152,-3.26226 c 11.08849,-1.70872 22.18972,-3.41198 33.33236,-4.74198 7.62422,-0.74035 18.25589,-2.01424 22.85825,-2.13216 3.9747,0.16043 7.19121,-1.50003 4.97687,-5.98385 -2.19519,-6.87738 -2.7663,-14.12638 -4.58023,-21.06207 -0.50507,-2.70443 -0.49206,-10.5095 -4.14677,-5.58974 -2.45504,3.80109 -3.0458,4.44603 -6.09738,7.88954 -1.74227,2.11665 -5.82616,6.41192 -7.33745,7.76015 -1.37734,0.16306 -1.92548,0.0561 -2.8222,-0.55657 -3.9649,-2.27742 -5.93118,-7.65699 -7.46841,-11.23906 -2.17324,-4.06097 -2.53871,-8.43502 -5.31935,-10.86376 -2.77523,3.30344 -2.66424,9.07644 -4.15346,13.39454 -0.83944,4.24038 -2.84075,14.21058 -4.4011,18.24591 -1.58738,2.43263 -3.45634,1.62575 -6.23643,-0.0256 -0.99945,-0.68547 -7.72789,-5.49139 -11.97481,-8.01831 -7.6852,-5.3216 -6.10656,-3.74361 -5.65029,-0.88398 0.10492,0.81241 0.44397,3.1353 1.54425,5.46866 0.32584,1.86517 2.59789,8.26471 3.31218,9.6252 0.83241,2.28532 2.21293,6.80295 4.16397,8.71311 z"
|
||||
id="path7"
|
||||
clip-path="url(#clipPath12)"
|
||||
sodipodi:nodetypes="sccccccccccccccccccccccccccsscccccccccccccccccccc"
|
||||
transform="matrix(3.4980348,0,0,3.4980348,1270.857,-389.01634)" /></g></svg>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
@@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="600"
|
||||
height="150"
|
||||
viewBox="0 0 639.71932 159.92983"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1"><linearGradient
|
||||
id="linearGradient27"><stop
|
||||
style="stop-color:#fdbe00;stop-opacity:0.60326397;"
|
||||
offset="0"
|
||||
id="stop24" /><stop
|
||||
style="stop-color:#ffbf00;stop-opacity:1;"
|
||||
offset="0.24895304"
|
||||
id="stop25" /><stop
|
||||
style="stop-color:#ffbf00;stop-opacity:1;"
|
||||
offset="0.74559295"
|
||||
id="stop26" /><stop
|
||||
style="stop-color:#ffbf00;stop-opacity:0.5;"
|
||||
offset="1"
|
||||
id="stop27" /></linearGradient><linearGradient
|
||||
id="linearGradient14"><stop
|
||||
style="stop-color:#99a024;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15" /><stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0.59368014"
|
||||
id="stop14" /></linearGradient><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath12"><rect
|
||||
style="opacity:0.73;fill:#808080;stroke:none;stroke-width:25.8093;stroke-dasharray:none"
|
||||
id="rect13"
|
||||
width="110.44633"
|
||||
height="80.25853"
|
||||
x="-312.28189"
|
||||
y="182.2175"
|
||||
ry="8.3687115" /></clipPath><linearGradient
|
||||
xlink:href="#linearGradient27"
|
||||
id="linearGradient24"
|
||||
x1="-296.4393"
|
||||
y1="221.15575"
|
||||
x2="-212.80244"
|
||||
y2="221.15575"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
xlink:href="#linearGradient27"
|
||||
id="linearGradient1003"
|
||||
x1="297.85858"
|
||||
y1="188.08138"
|
||||
x2="683.32751"
|
||||
y2="188.08138"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0879091,0,0,1.0879091,-42.031151,-22.33766)" /></defs><g
|
||||
id="layer1"
|
||||
transform="translate(-70.752116,-102.47985)"><path
|
||||
style="fill:url(#linearGradient24)"
|
||||
d="m -285.14661,253.06175 c 0,-3.27597 -3.01679,-8.21941 -4.76202,-12.59971 -2.09864,-4.37784 -3.70203,-9.01442 -4.72242,-13.81898 -0.60695,-3.61222 -4.04453,-10.73727 0.53578,-11.80999 4.39319,1.1031 7.94714,4.26038 12.04436,6.12662 3.23761,2.23586 7.28439,5.57271 9.58711,7.10235 l 2.19312,1.22999 c 0.94505,-3.41336 1.56239,-5.61217 2.03457,-7.61507 0.46237,-1.91352 2.18619,-8.26566 2.60743,-9.89224 0.48344,-2.43766 1.55082,-6.8614 1.9827,-9.00938 0.45042,-1.60014 0.84935,-3.58747 1.36445,-5.88457 0.5294,-1.53773 1.45063,-5.29604 2.72265,-6.4671 2.3941,-5.13058 5.24471,2.37821 5.65585,3.20575 0.49247,1.10188 1.0787,2.30588 1.55864,3.2392 0.129,0.18389 1.52579,3.57929 2.06874,4.65835 1.80839,4.10212 2.34489,5.50629 3.80329,9.92734 1.75167,2.66332 2.72647,8.53905 6.02661,6.00401 2.60713,-3.31457 4.9425,-5.93137 6.91298,-9.09058 2.81888,-3.23294 5.13654,-6.87355 7.95219,-10.07102 4.62049,-3.19468 5.16556,3.82876 6.17581,7.08622 0.99604,4.2703 1.10667,8.76081 1.92175,13.23669 0.53491,5.0654 1.75127,10.02016 3.11885,14.78118 1.129,3.773 2.20003,10.6979 1.10215,12.93656 -0.36642,0.87934 -2.73644,0.90542 -4.09891,1.05545 -4.33279,0.37052 -8.15208,0.94811 -13.01617,1.42118 -4.40499,0.42921 -5.9455,0.65948 -10.4286,1.19674 -11.72768,1.32284 -23.15617,2.1081 -34.74205,3.01013 -2.51895,0.1617 -7.62257,1.34582 -9.59886,0.0409 z m 5.03152,-3.26226 c 11.08849,-1.70872 22.18972,-3.41198 33.33236,-4.74198 7.62422,-0.74035 18.25589,-2.01424 22.85825,-2.13216 3.9747,0.16043 7.19121,-1.50003 4.97687,-5.98385 -2.19519,-6.87738 -2.7663,-14.12638 -4.58023,-21.06207 -0.50507,-2.70443 -0.49206,-10.5095 -4.14677,-5.58974 -2.45504,3.80109 -3.0458,4.44603 -6.09738,7.88954 -1.74227,2.11665 -5.82616,6.41192 -7.33745,7.76015 -1.37734,0.16306 -1.92548,0.0561 -2.8222,-0.55657 -3.9649,-2.27742 -5.93118,-7.65699 -7.46841,-11.23906 -2.17324,-4.06097 -2.53871,-8.43502 -5.31935,-10.86376 -2.77523,3.30344 -2.66424,9.07644 -4.15346,13.39454 -0.83944,4.24038 -2.84075,14.21058 -4.4011,18.24591 -1.58738,2.43263 -3.45634,1.62575 -6.23643,-0.0256 -0.99945,-0.68547 -7.72789,-5.49139 -11.97481,-8.01831 -7.6852,-5.3216 -6.10656,-3.74361 -5.65029,-0.88398 0.10492,0.81241 0.44397,3.1353 1.54425,5.46866 0.32584,1.86517 2.59789,8.26471 3.31218,9.6252 0.83241,2.28532 2.21293,6.80295 4.16397,8.71311 z"
|
||||
id="path7"
|
||||
clip-path="url(#clipPath12)"
|
||||
transform="matrix(2.3242299,0,0,2.3242299,759.74521,-331.57204)" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:111.353px;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:url(#linearGradient1003);fill-opacity:1;stroke-width:103.887"
|
||||
x="273.43774"
|
||||
y="222.25346"
|
||||
id="text391"><tspan
|
||||
id="tspan389"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:111.353px;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:url(#linearGradient1003);fill-opacity:1;stroke-width:103.887"
|
||||
x="273.43774"
|
||||
y="222.25346">DEVIGO</tspan></text></g></svg>
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 972 KiB |
@@ -1,346 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html itemscope class="snap dark" lang="nb-no" itemtype="http://schema.org/WebPage">
|
||||
<head>
|
||||
<meta name="generator" content="Hugo 0.148.2">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=5">
|
||||
<meta name="theme-name" content="hugoplate">
|
||||
<link rel="shortcut icon" href="images/favicon_hu_fc3e2a6bee6bf4a7.png" type="image/x-icon">
|
||||
<link rel="icon" href="images/favicon_hu_fc3e2a6bee6bf4a7.png" type="image/x-icon">
|
||||
<link rel="icon" type="image/png" sizes="48x48" href="images/favicon_hu_e204b38d3bc06b2.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="images/favicon_hu_fc3e2a6bee6bf4a7.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="images/favicon_hu_e81cb40e0831378f.png">
|
||||
<link rel="manifest" href="manifest.webmanifest">
|
||||
<meta name="msapplication-TileColor" content="#ddd">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<!-- Base href removed for localhost demo -->
|
||||
<title class="insertr" data-content-type="text">DEVIGO</title>
|
||||
<meta name="keywords" content="Salgstrening,Salgskurs,Kundeservice,Closing,Salgsutvikling">
|
||||
<meta name="description" content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, ">
|
||||
<meta name="author" content="jnss.me">
|
||||
<meta property="og:image:type" content="image/svg+xml">
|
||||
<meta property="og:title" content="DEVIGO">
|
||||
<meta property="og:description" content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, ">
|
||||
<meta property="og:type" content="website">
|
||||
<!-- og:url removed for localhost demo -->
|
||||
<meta name="twitter:title" content="DEVIGO">
|
||||
<meta name="twitter:description" content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, ">
|
||||
<meta name="twitter:creator" content="@jnss.me">
|
||||
<script>
|
||||
let indexURL = "searchindex.json",
|
||||
includeSectionsInSearch = ["blog"],
|
||||
search_no_results = "0 results for",
|
||||
search_initial_message = ""
|
||||
</script>
|
||||
<meta http-equiv="x-dns-prefetch-control" content="on">
|
||||
<link rel="preconnect" href="https://use.fontawesome.com" crossorigin>
|
||||
<link rel="preconnect" href="//cdnjs.cloudflare.com">
|
||||
<link rel="preconnect" href="//www.googletagmanager.com">
|
||||
<link rel="stylesheet" href="css/style.min.7689eba5aa202a985c862a65b270b80d749060ba27871723c7580d379f55eee9.css" media="screen">
|
||||
</head>
|
||||
|
||||
<body class="border-color-light-1 dark:border-color-dark-1 min-h-screen">
|
||||
<header class="header z-30 bg-white dark:bg-darkmode-color-dark">
|
||||
<nav class="navbar container">
|
||||
<div class="order-0">
|
||||
<a class="navbar-brand inline-block" href="#">
|
||||
<img fetchpriority="high" decoding="async" class="img img-light" width="180" height="" src="images/rustan_logo.svg" alt="Devigo" onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority="high" decoding="async" class="img img-dark" width="180" height="" src="images/rustan_logo.svg" alt="Devigo" onerror='this.onerror=null,this.src=""'>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<input id="nav-toggle" type="checkbox" class="hidden">
|
||||
<label for="nav-toggle" class="order-3 cursor-pointer flex items-center lg:hidden text-text-dark dark:text-white lg:order-1">
|
||||
<svg id="show-button" class="h-6 fill-current block" viewBox="0 0 20 20">
|
||||
<title>Menu Open</title>
|
||||
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0V0z"/>
|
||||
</svg>
|
||||
<svg id="hide-button" class="h-6 fill-current hidden" viewBox="0 0 20 20">
|
||||
<title>Menu Close</title>
|
||||
<polygon points="11 9 22 9 22 11 11 11 11 22 9 22 9 11 -2 11 -2 9 9 9 9 -2 11 -2" transform="rotate(45 10 10)"/>
|
||||
</svg>
|
||||
</label>
|
||||
|
||||
<ul id="nav-menu" class="navbar-nav order-3 hidden lg:flex w-full pb-6 lg:order-1 lg:w-auto lg:space-x-2 lg:pb-0 xl:space-x-8">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link insertr" href="#feat" data-content-type="text">Kurs og Utvikling</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link insertr" href="#about" data-content-type="text">Om oss</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link insertr" href="#testimonials" data-content-type="text">Referanser</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link insertr" href="#contact" data-content-type="text">Kontakt</a>
|
||||
</li>
|
||||
<li class="mt-4 inline-block lg:hidden">
|
||||
<a class="btn btn-outline-primary btn-sm insertr" href="#contact" data-content-type="link">selg mer!</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="order-1 ml-auto flex items-center md:order-2 lg:ml-0">
|
||||
<a href="#contact" class="btn btn-outline-primary btn-sm hidden lg:inline-block insertr" data-content-type="link">selg mer!</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- Background Word Cloud -->
|
||||
<div class="fixed inset-0 z-0 overflow-hidden pointer-events-none">
|
||||
<img src="images/wordcloud.svg" loading="eager" decoding="async" alt="Banner image" class="!min-w-[500px] lg:!max-w-[1200px] -rotate-35 img" width="" height="">
|
||||
</div>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="section relative z-10">
|
||||
<div class="container pt-8 bg-(--section-color)">
|
||||
<div class="row items-center">
|
||||
<div class="lg:col-6 md:col-6 mb-8 md:mb-0 md:order-1">
|
||||
<img src="images/banner-img_hu_7d8ce6cf91237d02.webp" loading="eager" decoding="async" alt="Banner image" class="mx-auto lg:!max-w-[600px] img" width="368" height="541" onerror='this.onerror="null",this.src="images/banner-img.png"'>
|
||||
</div>
|
||||
<div class="lg:col-6 md:col-6 md:order-2">
|
||||
<h1 class="mb-4 text-h3 lg:text-h1 insertr" data-content-type="text">Closing skjer før start</h1>
|
||||
<p class="mb-8 insertr" data-content-type="markdown">Vår tjeneste er å sørge for at dine selgere yter sitt beste. Så selger du mer!</p>
|
||||
<a class="btn btn-primary insertr" href="#contact" data-content-type="link">Kontakt oss
|
||||
<i class="fa fa-arrow-right pl-2"></i>
|
||||
</a>
|
||||
<a class="btn btn-outline-primary mt-6 ml-4 insertr" href="#feat" data-content-type="link">Les mer</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<span id="feat"></span>
|
||||
|
||||
<!-- Course for New Sellers -->
|
||||
<section class="section-sm relative z-10">
|
||||
<div class="container">
|
||||
<div class="row items-center justify-between">
|
||||
<div class="mb:md-0 md:col-5 mb-6"></div>
|
||||
<div class="bg-light dark:bg-darkmode-light rounded-lg px-7 py-10 md:col-7 lg:col-6">
|
||||
<h3 class="mb-4 insertr" data-content-type="text">Kurs for nye selgere</h3>
|
||||
<p class="mb-8 text-lg insertr" data-content-type="markdown">Salg er ikke bare et yrke, men en kultur. Vi kick-starter dine nye ansattes selvtillit</p>
|
||||
<ul>
|
||||
<li class="relative mb-4 pl-6">
|
||||
<i class="fa fa-check absolute left-0 top-1.5"></i>
|
||||
<span class="insertr" data-content-type="text">12 ukers intensivkurs</span>
|
||||
</li>
|
||||
<li class="relative mb-4 pl-6">
|
||||
<i class="fa fa-check absolute left-0 top-1.5"></i>
|
||||
<span class="insertr" data-content-type="text">Bli en del av et voksende nettverk for støtte og oppfølging</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Sales Development -->
|
||||
<section class="section-sm relative z-10">
|
||||
<div class="container">
|
||||
<div class="row items-center justify-between">
|
||||
<div class="mb:md-0 md:order-2 md:col-5 mb-6"></div>
|
||||
<div class="md:order-1 bg-light dark:bg-darkmode-light rounded-lg px-7 py-10 md:col-7 lg:col-6">
|
||||
<h3 class="mb-4 insertr" data-content-type="text">Selgerutvikling</h3>
|
||||
<p class="mb-8 text-lg insertr" data-content-type="markdown">Ved å investere i selgeren…</p>
|
||||
<ul>
|
||||
<li class="relative mb-4 pl-6">
|
||||
<i class="fa fa-check absolute left-0 top-1.5"></i>
|
||||
<span class="insertr" data-content-type="text">Bli en del av et voksende nettverk for støtte og oppfølging</span>
|
||||
</li>
|
||||
<li class="relative mb-4 pl-6">
|
||||
<i class="fa fa-check absolute left-0 top-1.5"></i>
|
||||
<span class="insertr" data-content-type="text">Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Subject Courses -->
|
||||
<section class="section-sm relative z-10">
|
||||
<div class="container">
|
||||
<div class="row items-center justify-between">
|
||||
<div class="mb:md-0 md:col-5 mb-6"></div>
|
||||
<div class="bg-light dark:bg-darkmode-light rounded-lg px-7 py-10 md:col-7 lg:col-6">
|
||||
<h3 class="mb-4 insertr" data-content-type="text">Emnekurs</h3>
|
||||
<p class="mb-8 text-lg insertr" data-content-type="markdown">Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate. Culpa proident adipisicing id nulla nisi laboris ex</p>
|
||||
<ul>
|
||||
<li class="relative mb-4 pl-6">
|
||||
<i class="fa fa-check absolute left-0 top-1.5"></i>
|
||||
<span class="insertr" data-content-type="text">Salgsinnledning</span>
|
||||
</li>
|
||||
<li class="relative mb-4 pl-6">
|
||||
<i class="fa fa-check absolute left-0 top-1.5"></i>
|
||||
<span class="insertr" data-content-type="text">Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- About Section -->
|
||||
<section id="about" class="section relative z-10">
|
||||
<div class="container bg-(--section-color)">
|
||||
<div class="row justify-center">
|
||||
<div class="md:col-10 lg:col-7">
|
||||
<img src="images/crown_logo.svg" loading="lazy" decoding="async" alt="Om oss" class="mx-auto my-8 img" width="200" height="">
|
||||
<h2 class="mb-6 text-center insertr" data-content-type="text">Om oss</h2>
|
||||
<div class="content">
|
||||
<div class="insertr" data-content-type="markdown">
|
||||
<p>Ble stiftet i Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley</p>
|
||||
<ul>
|
||||
<li>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been th</li>
|
||||
<li>Le industry's standard dummy text ever since the 1500s, when an unknown printer took a galley</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="mb-8">
|
||||
<div class="bg-light dark:bg-darkmode-light rounded p-8 text-center">
|
||||
<picture>
|
||||
<source srcset="images/avatar_hu_5a1b8585ebace5d4.webp" media="(max-width: 575px)">
|
||||
<source srcset="images/avatar_hu_89c3e1abf2e492be.webp" media="(max-width: 767px)">
|
||||
<source srcset="images/avatar_hu_2afcab3aadbfeab2.webp" media="(max-width: 991px)">
|
||||
<source srcset="images/avatar_hu_434a732a647167ac.webp">
|
||||
<img loading="lazy" decoding="async" src="images/avatar_hu_7661af6aa1e392e9.png" class="mx-auto mb-6 rounded img" alt="Patric Rustan" width="3024" height="3024">
|
||||
</picture>
|
||||
<h4 class="mb-3">
|
||||
<a href="#" class="insertr" data-content-type="text">Patric Rustan</a>
|
||||
</h4>
|
||||
<p class="mb-4 insertr" data-content-type="markdown">
|
||||
Jeg har sju år på folkeskolen, tre år på gymnas, men etter to netter på en husmorsskole gikk utdannelsen i knas!
|
||||
</p>
|
||||
<ul class="social-icons my-3">
|
||||
<li>
|
||||
<a href="https://linkedin.com/in/knutrustan" target="_blank" rel="noopener nofollow">
|
||||
<span class="sr-only"></span>
|
||||
<i class="fa-brands fa-linkedin"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Testimonials -->
|
||||
<section id="testimonials" class="relative z-10 section">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="md:col-10 lg:col-8 xl:col-6 mx-auto text-center mb-8 bg-light dark:bg-darkmode-light rounded-lg px-7 py-10">
|
||||
<h2 class="mb-4 insertr" data-content-type="text">Hva andre sier om oss</h2>
|
||||
<p class="insertr" data-content-type="markdown">Ikke tro på oss, tro på våre fantastiske kunder</p>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="swiper testimonial-slider">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide">
|
||||
<div class="bg-light dark:bg-darkmode-light rounded-lg px-7 py-10">
|
||||
<div class="text-text-dark dark:text-white">
|
||||
<svg width="33" height="20" viewBox="0 0 33 20" fill="none">
|
||||
<path d="M1.28375 19.41l-.49-.77c.42-1.6333.95667-3.57 1.61-5.81.65333-2.2867 1.35333-4.55 2.1-6.79.79333-2.28667 1.56333-4.27 2.31-5.9500041H15.3538C14.9338 2.09666 14.4904 4.26667 14.0238 6.6 13.5571 8.88666 13.1371 11.15 12.7638 13.39 12.4371 15.5833 12.1571 17.59 11.9238 19.41H1.28375zM31.69.0899959 32.18.859998C31.76 2.54 31.2233 4.5 30.57 6.74 29.9167 8.98 29.2167 11.2433 28.47 13.53 27.7233 15.77 26.9533 17.73 26.16 19.41H17.69C18.0167 17.9167 18.3433 16.33 18.67 14.65 18.9967 12.9233 19.3 11.22 19.58 9.54 19.9067 7.81333 20.1867 6.15667 20.42 4.57 20.7 2.93666 20.91 1.44333 21.05.0899959H31.69z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<blockquote class="mt-8 insertr" data-content-type="markdown">Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui iusto illo molestias, assumenda expedita commodi inventore non itaque molestiae voluptatum dolore, facilis sapiente, repellat veniam.</blockquote>
|
||||
<div class="mt-11 flex items-center">
|
||||
<div class="text-text-dark dark:text-white">
|
||||
<img src="images/avatar-sm_hu_cf2347ff1d8fd524.webp" loading="lazy" decoding="async" alt="Marvin McKinney" class="rounded-full img" width="50" height="50" onerror='this.onerror="null",this.src="images/avatar-sm_hu_d38f234cd389381f.png"'>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h3 class="h5 font-primary font-semibold insertr" data-content-type="text">Marvin McKinney</h3>
|
||||
<p class="text-text-dark dark:text-white insertr" data-content-type="text">Web Designer</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Additional testimonial slides can be added here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Contact Section -->
|
||||
<section id="contact" class="section relative z-10">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="md:col-10 lg:col-8 xl:col-6 mx-auto text-center mb-8 bg-light dark:bg-darkmode-light rounded-lg px-7 py-10">
|
||||
<h2 class="mb-4 insertr" data-content-type="text">Kontakt oss</h2>
|
||||
<p class="insertr" data-content-type="markdown">Vi svarer så fort vi kan, vanligvis samme dag.</p>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="contact-form">
|
||||
<form method="POST" action="#" class="row">
|
||||
<div class="mb-6 col-md-6">
|
||||
<label for="name" class="form-label insertr" data-content-type="text">Navn <span class="text-red-500">*</span></label>
|
||||
<input id="name" name="name" class="form-input" placeholder="Kari Nordman" type="text">
|
||||
</div>
|
||||
<div class="mb-6 col-md-6">
|
||||
<label for="email" class="form-label insertr" data-content-type="text">Epost <span class="text-red-500">*</span></label>
|
||||
<input id="email" name="email" class="form-input" placeholder="kari@nordman.no" type="email">
|
||||
</div>
|
||||
<div class="mb-6 col-12">
|
||||
<label for="message" class="form-label insertr" data-content-type="text">Noe mer? <span class="text-red-500">*</span></label>
|
||||
<textarea id="message" name="message" class="form-input" placeholder="Hva lurer du på?" rows="6"></textarea>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-primary insertr" data-content-type="text">Send</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-light dark:bg-darkmode-light relative z-10">
|
||||
<div class="container">
|
||||
<div class="row items-center py-10">
|
||||
<div class="lg:col-3 mb-8 text-center lg:mb-0 lg:text-left">
|
||||
<a class="navbar-brand inline-block" href="#">
|
||||
<img fetchpriority="high" decoding="async" class="img img-light" width="180" height="" src="images/rustan_logo.svg" alt="Devigo" onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority="high" decoding="async" class="img img-dark" width="180" height="" src="images/rustan_logo.svg" alt="Devigo" onerror='this.onerror=null,this.src=""'>
|
||||
</a>
|
||||
</div>
|
||||
<div class="lg:col-6 mb-8 text-center lg:mb-0">
|
||||
<ul></ul>
|
||||
</div>
|
||||
<div class="lg:col-3 mb-8 text-center lg:mb-0 lg:mt-0 lg:text-right">
|
||||
<ul class="social-icons">
|
||||
<li>
|
||||
<a target="_blank" aria-label="facebook" rel="nofollow noopener" href="https://www.facebook.com/">
|
||||
<i class="fab fa-facebook"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" aria-label="linkedin" rel="nofollow noopener" href="https://www.linkedin.com/">
|
||||
<i class="fab fa-linkedin"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-border dark:border-darkmode-border border-t py-7">
|
||||
<div class="text-text-light dark:text-darkmode-text-light container text-center">
|
||||
<p class="insertr" data-content-type="markdown">Utviklet og levert av Joakim Schäffer</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script crossorigin="anonymous" integrity="sha256-NoqgJlugD+mB4Hf8AueiiPBQSb7ASin+oZS0fYEdtiA=" src="js/script.min.368aa0265ba00fe981e077fc02e7a288f05049bec04a29fea194b47d811db620.js"></script>
|
||||
<script defer async crossorigin="anonymous" integrity="sha256-GrJYek7lbMIWuRNCD5ImM6PNBJ72jzIDo/urKVLfPDk=" src="js/script-lazy.min.1ab2587a4ee56cc216b913420f922633a3cd049ef68f3203a3fbab2952df3c39.js"></script>
|
||||
<!-- Service worker disabled for demo -->
|
||||
<!-- <script>"serviceWorker" in navigator && navigator.serviceWorker.register("/service-worker.js")</script> -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,58 +0,0 @@
|
||||
# Insertr Configuration for Devigo Demo Site
|
||||
# Norwegian consulting website demonstrating production use case
|
||||
|
||||
# Global settings
|
||||
dev_mode: true # Development mode for demos
|
||||
|
||||
# Database configuration
|
||||
database:
|
||||
path: "./insertr.db" # Shared database with main config
|
||||
|
||||
# Demo-specific configuration
|
||||
demo:
|
||||
site_id: "devigo-web" # Unique site ID for Devigo demo (matches server config)
|
||||
inject_demo_gate: true # Auto-inject demo gate if no gates exist
|
||||
mock_auth: true # Use mock authentication for demos
|
||||
api_endpoint: "http://localhost:8080/api/content"
|
||||
# All sites served through unified server at localhost:8080/sites/devigo-web/
|
||||
|
||||
# CLI enhancement configuration
|
||||
cli:
|
||||
site_id: "devigo-web" # Site ID for this demo (matches server config)
|
||||
output: "./demos/devigo-web_enhanced" # Output directory for enhanced files
|
||||
inject_demo_gate: true # Inject demo gate in development mode
|
||||
|
||||
# Authentication configuration (for demo)
|
||||
auth:
|
||||
provider: "mock" # Mock auth for demos
|
||||
|
||||
# Site-specific metadata
|
||||
site:
|
||||
name: "Devigo - Digital Transformasjon"
|
||||
description: "Norwegian consulting website showcasing digital transformation services"
|
||||
language: "no" # Norwegian language
|
||||
url: "https://devigo.no"
|
||||
type: "consulting" # Site category
|
||||
|
||||
# Content types and examples for this demo
|
||||
content_examples:
|
||||
- type: "text"
|
||||
description: "Company name, service titles, contact information"
|
||||
elements: ["h1", "h2", "h3", "span.brand-name"]
|
||||
|
||||
- type: "markdown"
|
||||
description: "Service descriptions, testimonials, company info"
|
||||
elements: ["p", "blockquote", ".hero-subtitle"]
|
||||
|
||||
- type: "link"
|
||||
description: "Navigation links, call-to-action buttons"
|
||||
elements: ["a.btn", "nav a"]
|
||||
|
||||
# Features demonstrated in this demo
|
||||
features:
|
||||
- norwegian_content: true
|
||||
- consulting_services: true
|
||||
- testimonials: true
|
||||
- contact_forms: true
|
||||
- responsive_design: true
|
||||
- production_ready: true
|
||||
@@ -1 +0,0 @@
|
||||
[]
|
||||
@@ -1,7 +0,0 @@
|
||||
<!doctype html><html itemscope class="snap dark" lang=nb-no itemtype=http://schema.org/WebPage><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=5"><meta name=theme-name content="hugoplate"><link rel="shortcut icon" href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon type=image/png sizes=48x48 href=/images/favicon_hu_e204b38d3bc06b2.png><link rel=icon type=image/png sizes=96x96 href=/images/favicon_hu_fc3e2a6bee6bf4a7.png><link rel=apple-touch-icon sizes=144x144 href=/images/favicon_hu_e81cb40e0831378f.png><link rel=manifest href=/manifest.webmanifest><meta name=msapplication-TileColor content="#ddd"><meta name=theme-color content="#ffffff"><base href=https://devigo.no/sections/contact/><title>Kontakt oss</title><meta name=keywords content="Salgstrening,Salgskurs,Kundeservice,Closing,Salgsutvikling"><meta name=description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=author content="jnss.me"><meta property="og:image:type" content="image/svg+xml"><meta property="og:title" content="Kontakt oss"><meta property="og:description" content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta property="og:type" content="website"><meta property="og:url" content="https://devigo.no/sections/contact/"><meta name=twitter:title content="Kontakt oss"><meta name=twitter:description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=twitter:creator content="@jnss.me"><script>let indexURL="https://devigo.no/searchindex.json",includeSectionsInSearch=["blog"],search_no_results="0 results for",search_initial_message=""</script><meta http-equiv=x-dns-prefetch-control content="on"><link rel=preconnect href=https://use.fontawesome.com crossorigin><link rel=preconnect href=//cdnjs.cloudflare.com><link rel=preconnect href=//www.googletagmanager.com><link rel=preconnect href=//www.google-analytics.com><link rel=dns-prefetch href=https://use.fontawesome.com><link rel=dns-prefetch href=//ajax.googleapis.com><link rel=dns-prefetch href=//cdnjs.cloudflare.com><link rel=dns-prefetch href=//www.googletagmanager.com><link rel=dns-prefetch href=//www.google-analytics.com><link rel=dns-prefetch href=//fonts.googleapis.com><link rel=dns-prefetch href=//connect.facebook.net><link rel=dns-prefetch href=//platform.linkedin.com><link rel=dns-prefetch href=//platform.twitter.com><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;600&family=Signika:wght@500;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script><link href=/css/style.min.7689eba5aa202a985c862a65b270b80d749060ba27871723c7580d379f55eee9.css integrity="sha256-donrpaogKphchiplsnC4DXSQYLonhxcjx1gNN59V7uk=" rel=stylesheet><link defer async rel=stylesheet href=/css/style-lazy.min.85575460e013a8f1e96ccd0560c44120e7e785297933c62e65534d5652eebe78.css integrity="sha256-hVdUYOATqPHpbM0FYMRBIOfnhSl5M8YuZVNNVlLuvng=" media=print onload='this.media="all",this.onload=null'></head><body><header class="header sticky top-0 z-30"><nav class="navbar container"><div class=order-0><a class="navbar-brand block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><input id=nav-toggle type=checkbox class=hidden>
|
||||
<label for=nav-toggle class="order-3 cursor-pointer flex items-center lg:hidden text-text-dark dark:text-white lg:order-1"><svg id="show-button" class="h-6 fill-current block" viewBox="0 0 20 20"><title>Menu Open</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0V0z"/></svg>
|
||||
<svg id="hide-button" class="h-6 fill-current hidden" viewBox="0 0 20 20"><title>Menu Close</title><polygon points="11 9 22 9 22 11 11 11 11 22 9 22 9 11 -2 11 -2 9 9 9 9 -2 11 -2" transform="rotate(45 10 10)"/></svg></label><ul id=nav-menu class="navbar-nav order-3 hidden lg:flex w-full pb-6 lg:order-1 lg:w-auto lg:space-x-2 lg:pb-0 xl:space-x-8"><li class=nav-item><a class=nav-link href=/#feat>Kurs og Utvikling</a></li><li class=nav-item><a class=nav-link href=/#about>Om oss</a></li><li class=nav-item><a class=nav-link href=/#testimonials>Referanser</a></li><li class=nav-item><a class=nav-link href=/#contact>Kontakt</a></li><li class="mt-4 inline-block lg:hidden"><a class="btn btn-outline-primary btn-sm" href=/#contact>selg mer!</a></li></ul><div class="order-1 ml-auto flex items-center md:order-2 lg:ml-0"><a href=/#contact class="btn btn-outline-primary btn-sm hidden lg:inline-block">selg mer!</a></div></nav></header><main><section><div class="container text-center"><div class="from-body to-light dark:from-darkmode-body dark:to-darkmode-light rounded-2xl bg-gradient-to-b px-8 py-14"><h1>Kontakt Oss</h1><ul class="mt-6 inline-flex space-x-1 capitalize"><li><a class="text-primary dark:text-darkmode-primary" href=https://devigo.no/>Hjem
|
||||
</a><span class="inlin-block mr-1">/</span></li><li><a class="text-primary dark:text-darkmode-primary" href=/sections/>Sections
|
||||
</a><span class="inlin-block mr-1">/</span></li><li><span class="text-primary dark:text-darkmode-primary">Kontakt oss</span></li></ul></div></div></section><section class=section-sm><div class=container><div class="row justify-center"><div class=lg:col-10><div class=content><p>Du kan bruke skjema nedenfor til å sende oss en henvendelse, eller ta kontakt på telefon.</p></div></div></div></div></section></main><footer class="bg-light dark:bg-darkmode-light relative z-10"><div class=container><div class="row items-center py-10"><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:text-left"><a class="navbar-brand inline-block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><div class="lg:col-6 mb-8 text-center lg:mb-0"><ul></ul></div><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:mt-0 lg:text-right"><ul class=social-icons><li><a target=_blank aria-label=facebook rel="nofollow noopener" href=https://www.facebook.com/><i class="fab fa-facebook"></i></a></li><li><a target=_blank aria-label=linkedin rel="nofollow noopener" href=https://www.linkedin.com/><i class="fab fa-linkedin"></i></a></li></ul></div></div></div><div class="border-border dark:border-darkmode-border border-t py-7"><div class="text-text-light dark:text-darkmode-text-light container text-center"><p>Utviklet og levert av Joakim Schäffer</p></div></div></footer><script crossorigin=anonymous integrity="sha256-NoqgJlugD+mB4Hf8AueiiPBQSb7ASin+oZS0fYEdtiA=" src=/js/script.min.368aa0265ba00fe981e077fc02e7a288f05049bec04a29fea194b47d811db620.js></script><script defer async crossorigin=anonymous integrity="sha256-GrJYek7lbMIWuRNCD5ImM6PNBJ72jzIDo/urKVLfPDk=" src=/js/script-lazy.min.1ab2587a4ee56cc216b913420f922633a3cd049ef68f3203a3fbab2952df3c39.js></script><script>"serviceWorker"in navigator&&navigator.serviceWorker.register("/service-worker.js")</script></body></html>
|
||||
@@ -1,6 +0,0 @@
|
||||
<!doctype html><html itemscope class="snap dark" lang=nb-no itemtype=http://schema.org/WebPage><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=5"><meta name=theme-name content="hugoplate"><link rel="shortcut icon" href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon type=image/png sizes=48x48 href=/images/favicon_hu_e204b38d3bc06b2.png><link rel=icon type=image/png sizes=96x96 href=/images/favicon_hu_fc3e2a6bee6bf4a7.png><link rel=apple-touch-icon sizes=144x144 href=/images/favicon_hu_e81cb40e0831378f.png><link rel=manifest href=/manifest.webmanifest><meta name=msapplication-TileColor content="#ddd"><meta name=theme-color content="#ffffff"><base href=https://devigo.no/sections/><title>DEVIGO</title><meta name=keywords content="Salgstrening,Salgskurs,Kundeservice,Closing,Salgsutvikling"><meta name=description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=author content="jnss.me"><meta property="og:image:type" content="image/svg+xml"><meta property="og:title" content="DEVIGO"><meta property="og:description" content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta property="og:type" content="website"><meta property="og:url" content="https://devigo.no/sections/"><meta name=twitter:title content="DEVIGO"><meta name=twitter:description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=twitter:creator content="@jnss.me"><script>let indexURL="https://devigo.no/searchindex.json",includeSectionsInSearch=["blog"],search_no_results="0 results for",search_initial_message=""</script><meta http-equiv=x-dns-prefetch-control content="on"><link rel=preconnect href=https://use.fontawesome.com crossorigin><link rel=preconnect href=//cdnjs.cloudflare.com><link rel=preconnect href=//www.googletagmanager.com><link rel=preconnect href=//www.google-analytics.com><link rel=dns-prefetch href=https://use.fontawesome.com><link rel=dns-prefetch href=//ajax.googleapis.com><link rel=dns-prefetch href=//cdnjs.cloudflare.com><link rel=dns-prefetch href=//www.googletagmanager.com><link rel=dns-prefetch href=//www.google-analytics.com><link rel=dns-prefetch href=//fonts.googleapis.com><link rel=dns-prefetch href=//connect.facebook.net><link rel=dns-prefetch href=//platform.linkedin.com><link rel=dns-prefetch href=//platform.twitter.com><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;600&family=Signika:wght@500;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script><link href=/css/style.min.7689eba5aa202a985c862a65b270b80d749060ba27871723c7580d379f55eee9.css integrity="sha256-donrpaogKphchiplsnC4DXSQYLonhxcjx1gNN59V7uk=" rel=stylesheet><link defer async rel=stylesheet href=/css/style-lazy.min.85575460e013a8f1e96ccd0560c44120e7e785297933c62e65534d5652eebe78.css integrity="sha256-hVdUYOATqPHpbM0FYMRBIOfnhSl5M8YuZVNNVlLuvng=" media=print onload='this.media="all",this.onload=null'></head><body><header class="header sticky top-0 z-30"><nav class="navbar container"><div class=order-0><a class="navbar-brand block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><input id=nav-toggle type=checkbox class=hidden>
|
||||
<label for=nav-toggle class="order-3 cursor-pointer flex items-center lg:hidden text-text-dark dark:text-white lg:order-1"><svg id="show-button" class="h-6 fill-current block" viewBox="0 0 20 20"><title>Menu Open</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0V0z"/></svg>
|
||||
<svg id="hide-button" class="h-6 fill-current hidden" viewBox="0 0 20 20"><title>Menu Close</title><polygon points="11 9 22 9 22 11 11 11 11 22 9 22 9 11 -2 11 -2 9 9 9 9 -2 11 -2" transform="rotate(45 10 10)"/></svg></label><ul id=nav-menu class="navbar-nav order-3 hidden lg:flex w-full pb-6 lg:order-1 lg:w-auto lg:space-x-2 lg:pb-0 xl:space-x-8"><li class=nav-item><a class=nav-link href=/#feat>Kurs og Utvikling</a></li><li class=nav-item><a class=nav-link href=/#about>Om oss</a></li><li class=nav-item><a class=nav-link href=/#testimonials>Referanser</a></li><li class=nav-item><a class=nav-link href=/#contact>Kontakt</a></li><li class="mt-4 inline-block lg:hidden"><a class="btn btn-outline-primary btn-sm" href=/#contact>selg mer!</a></li></ul><div class="order-1 ml-auto flex items-center md:order-2 lg:ml-0"><a href=/#contact class="btn btn-outline-primary btn-sm hidden lg:inline-block">selg mer!</a></div></nav></header><main><section><div class="container text-center"><div class="from-body to-light dark:from-darkmode-body dark:to-darkmode-light rounded-2xl bg-gradient-to-b px-8 py-14"><h1>Sections</h1><ul class="mt-6 inline-flex space-x-1 capitalize"><li><a class="text-primary dark:text-darkmode-primary" href=https://devigo.no/>Hjem
|
||||
</a><span class="inlin-block mr-1">/</span></li><li><span class="text-primary dark:text-darkmode-primary">Sections</span></li></ul></div></div></section><section class=section><div class=container><ul class=text-center><li class=m-3><a href class="text-text-dark dark:text-darkmode-text-dark block text-xl">Hva andre sier om oss</a></li><li class=m-3><a href class="text-text-dark dark:text-darkmode-text-dark block text-xl">Hvor havner dette da tro?</a></li><li class=m-3><a href=https://devigo.no/sections/contact/ class="text-text-dark dark:text-darkmode-text-dark block text-xl">Kontakt oss</a></li><li class=m-3><a href class="text-text-dark dark:text-darkmode-text-dark block text-xl">Om oss</a></li></ul></div></section></main><footer class="bg-light dark:bg-darkmode-light relative z-10"><div class=container><div class="row items-center py-10"><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:text-left"><a class="navbar-brand inline-block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><div class="lg:col-6 mb-8 text-center lg:mb-0"><ul></ul></div><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:mt-0 lg:text-right"><ul class=social-icons><li><a target=_blank aria-label=facebook rel="nofollow noopener" href=https://www.facebook.com/><i class="fab fa-facebook"></i></a></li><li><a target=_blank aria-label=linkedin rel="nofollow noopener" href=https://www.linkedin.com/><i class="fab fa-linkedin"></i></a></li></ul></div></div></div><div class="border-border dark:border-darkmode-border border-t py-7"><div class="text-text-light dark:text-darkmode-text-light container text-center"><p>Utviklet og levert av Joakim Schäffer</p></div></div></footer><script crossorigin=anonymous integrity="sha256-NoqgJlugD+mB4Hf8AueiiPBQSb7ASin+oZS0fYEdtiA=" src=/js/script.min.368aa0265ba00fe981e077fc02e7a288f05049bec04a29fea194b47d811db620.js></script><script defer async crossorigin=anonymous integrity="sha256-GrJYek7lbMIWuRNCD5ImM6PNBJ72jzIDo/urKVLfPDk=" src=/js/script-lazy.min.1ab2587a4ee56cc216b913420f922633a3cd049ef68f3203a3fbab2952df3c39.js></script><script>"serviceWorker"in navigator&&navigator.serviceWorker.register("/service-worker.js")</script></body></html>
|
||||
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Sections on DEVIGO</title><link>https://devigo.no/sections/</link><description>Recent content in Sections on DEVIGO</description><generator>Hugo</generator><language>nb-no</language><atom:link href="https://devigo.no/sections/index.xml" rel="self" type="application/rss+xml"/><item><title>Hva andre sier om oss</title><link/><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid/><description/></item><item><title>Hvor havner dette da tro?</title><link/><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid/><description/></item><item><title>Kontakt oss</title><link>https://devigo.no/sections/contact/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://devigo.no/sections/contact/</guid><description><p>Du kan bruke skjema nedenfor til å sende oss en henvendelse, eller ta kontakt på telefon.</p></description></item><item><title>Om oss</title><link/><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid/><description><p>Ble stiftet i Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry&rsquo;s standard dummy text ever since the 1500s, when an unknown printer took a galley</p></description></item></channel></rss>
|
||||
@@ -1,10 +0,0 @@
|
||||
importScripts(
|
||||
"https://storage.googleapis.com/workbox-cdn/releases/6.0.2/workbox-sw.js"
|
||||
);
|
||||
|
||||
workbox.setConfig({ debug: false });
|
||||
|
||||
workbox.routing.registerRoute(
|
||||
({ request }) => request.destination === "image",
|
||||
new workbox.strategies.NetworkFirst()
|
||||
);
|
||||
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"><url><loc>https://devigo.no/</loc></url><url><loc>https://devigo.no/categories/</loc></url><url><loc>https://devigo.no/authors/</loc></url><url><loc>https://devigo.no/sections/contact/</loc></url><url><loc>https://devigo.no/authors/patrick-rustan/</loc></url><url><loc>https://devigo.no/sections/</loc></url><url><loc>https://devigo.no/tags/</loc></url></urlset>
|
||||
@@ -1,6 +0,0 @@
|
||||
<!doctype html><html itemscope class="snap dark" lang=nb-no itemtype=http://schema.org/WebPage><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=5"><meta name=theme-name content="hugoplate"><link rel="shortcut icon" href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon href=/images/favicon_hu_fc3e2a6bee6bf4a7.png type=image/x-icon><link rel=icon type=image/png sizes=48x48 href=/images/favicon_hu_e204b38d3bc06b2.png><link rel=icon type=image/png sizes=96x96 href=/images/favicon_hu_fc3e2a6bee6bf4a7.png><link rel=apple-touch-icon sizes=144x144 href=/images/favicon_hu_e81cb40e0831378f.png><link rel=manifest href=/manifest.webmanifest><meta name=msapplication-TileColor content="#ddd"><meta name=theme-color content="#ffffff"><base href=https://devigo.no/tags/><title>DEVIGO</title><meta name=keywords content="Salgstrening,Salgskurs,Kundeservice,Closing,Salgsutvikling"><meta name=description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=author content="jnss.me"><meta property="og:image:type" content="image/svg+xml"><meta property="og:title" content="DEVIGO"><meta property="og:description" content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta property="og:type" content="website"><meta property="og:url" content="https://devigo.no/tags/"><meta name=twitter:title content="DEVIGO"><meta name=twitter:description content="Et selskap dedikert til å utvikle og fremme god salgsteknikk, "><meta name=twitter:creator content="@jnss.me"><script>let indexURL="https://devigo.no/searchindex.json",includeSectionsInSearch=["blog"],search_no_results="0 results for",search_initial_message=""</script><meta http-equiv=x-dns-prefetch-control content="on"><link rel=preconnect href=https://use.fontawesome.com crossorigin><link rel=preconnect href=//cdnjs.cloudflare.com><link rel=preconnect href=//www.googletagmanager.com><link rel=preconnect href=//www.google-analytics.com><link rel=dns-prefetch href=https://use.fontawesome.com><link rel=dns-prefetch href=//ajax.googleapis.com><link rel=dns-prefetch href=//cdnjs.cloudflare.com><link rel=dns-prefetch href=//www.googletagmanager.com><link rel=dns-prefetch href=//www.google-analytics.com><link rel=dns-prefetch href=//fonts.googleapis.com><link rel=dns-prefetch href=//connect.facebook.net><link rel=dns-prefetch href=//platform.linkedin.com><link rel=dns-prefetch href=//platform.twitter.com><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><script>(function(){const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;600&family=Signika:wght@500;700&display=swap",e.type="text/css",e.rel="stylesheet",document.head.appendChild(e)})()</script><link href=/css/style.min.7689eba5aa202a985c862a65b270b80d749060ba27871723c7580d379f55eee9.css integrity="sha256-donrpaogKphchiplsnC4DXSQYLonhxcjx1gNN59V7uk=" rel=stylesheet><link defer async rel=stylesheet href=/css/style-lazy.min.85575460e013a8f1e96ccd0560c44120e7e785297933c62e65534d5652eebe78.css integrity="sha256-hVdUYOATqPHpbM0FYMRBIOfnhSl5M8YuZVNNVlLuvng=" media=print onload='this.media="all",this.onload=null'></head><body><header class="header sticky top-0 z-30"><nav class="navbar container"><div class=order-0><a class="navbar-brand block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><input id=nav-toggle type=checkbox class=hidden>
|
||||
<label for=nav-toggle class="order-3 cursor-pointer flex items-center lg:hidden text-text-dark dark:text-white lg:order-1"><svg id="show-button" class="h-6 fill-current block" viewBox="0 0 20 20"><title>Menu Open</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0V0z"/></svg>
|
||||
<svg id="hide-button" class="h-6 fill-current hidden" viewBox="0 0 20 20"><title>Menu Close</title><polygon points="11 9 22 9 22 11 11 11 11 22 9 22 9 11 -2 11 -2 9 9 9 9 -2 11 -2" transform="rotate(45 10 10)"/></svg></label><ul id=nav-menu class="navbar-nav order-3 hidden lg:flex w-full pb-6 lg:order-1 lg:w-auto lg:space-x-2 lg:pb-0 xl:space-x-8"><li class=nav-item><a class=nav-link href=/#feat>Kurs og Utvikling</a></li><li class=nav-item><a class=nav-link href=/#about>Om oss</a></li><li class=nav-item><a class=nav-link href=/#testimonials>Referanser</a></li><li class=nav-item><a class=nav-link href=/#contact>Kontakt</a></li><li class="mt-4 inline-block lg:hidden"><a class="btn btn-outline-primary btn-sm" href=/#contact>selg mer!</a></li></ul><div class="order-1 ml-auto flex items-center md:order-2 lg:ml-0"><a href=/#contact class="btn btn-outline-primary btn-sm hidden lg:inline-block">selg mer!</a></div></nav></header><main><section><div class="container text-center"><div class="from-body to-light dark:from-darkmode-body dark:to-darkmode-light rounded-2xl bg-gradient-to-b px-8 py-14"><h1>Tags</h1><ul class="mt-6 inline-flex space-x-1 capitalize"><li><a class="text-primary dark:text-darkmode-primary" href=https://devigo.no/>Hjem
|
||||
</a><span class="inlin-block mr-1">/</span></li><li><span class="text-primary dark:text-darkmode-primary">Tags</span></li></ul></div></div></section><section class=section><div class=container><ul class=text-center></ul></div></section></main><footer class="bg-light dark:bg-darkmode-light relative z-10"><div class=container><div class="row items-center py-10"><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:text-left"><a class="navbar-brand inline-block" href=/><img fetchpriority=high decoding=async class="img img-light" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'>
|
||||
<img fetchpriority=high decoding=async class="img img-dark" width=180 height src=/images/rustan_logo.svg alt=Devigo onerror='this.onerror=null,this.src=""'></a></div><div class="lg:col-6 mb-8 text-center lg:mb-0"><ul></ul></div><div class="lg:col-3 mb-8 text-center lg:mb-0 lg:mt-0 lg:text-right"><ul class=social-icons><li><a target=_blank aria-label=facebook rel="nofollow noopener" href=https://www.facebook.com/><i class="fab fa-facebook"></i></a></li><li><a target=_blank aria-label=linkedin rel="nofollow noopener" href=https://www.linkedin.com/><i class="fab fa-linkedin"></i></a></li></ul></div></div></div><div class="border-border dark:border-darkmode-border border-t py-7"><div class="text-text-light dark:text-darkmode-text-light container text-center"><p>Utviklet og levert av Joakim Schäffer</p></div></div></footer><script crossorigin=anonymous integrity="sha256-NoqgJlugD+mB4Hf8AueiiPBQSb7ASin+oZS0fYEdtiA=" src=/js/script.min.368aa0265ba00fe981e077fc02e7a288f05049bec04a29fea194b47d811db620.js></script><script defer async crossorigin=anonymous integrity="sha256-GrJYek7lbMIWuRNCD5ImM6PNBJ72jzIDo/urKVLfPDk=" src=/js/script-lazy.min.1ab2587a4ee56cc216b913420f922633a3cd049ef68f3203a3fbab2952df3c39.js></script><script>"serviceWorker"in navigator&&navigator.serviceWorker.register("/service-worker.js")</script></body></html>
|
||||
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tags on DEVIGO</title><link>https://devigo.no/tags/</link><description>Recent content in Tags on DEVIGO</description><generator>Hugo</generator><language>nb-no</language><atom:link href="https://devigo.no/tags/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
||||
@@ -1,71 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Script to download a website with its assets for insertr testing
|
||||
* Usage: node download-site.js <url> <output-directory>
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
async function downloadSite(url, outputDir) {
|
||||
console.log(`Downloading ${url} to ${outputDir}`);
|
||||
|
||||
// Create output directory
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
// Use wget to download the site with assets
|
||||
// --page-requisites: download all files needed to display page
|
||||
// --convert-links: convert links to work locally
|
||||
// --adjust-extension: add proper extensions
|
||||
// --no-parent: don't ascend to parent directory
|
||||
// --no-host-directories: don't create host directories
|
||||
// --cut-dirs=1: cut directory levels
|
||||
const wgetCmd = `wget --page-requisites --convert-links --adjust-extension --no-parent --no-host-directories --directory-prefix="${outputDir}" --user-agent="Mozilla/5.0 (compatible; insertr-test-bot)" "${url}"`;
|
||||
|
||||
execSync(wgetCmd, { stdio: 'inherit' });
|
||||
|
||||
console.log('✅ Download completed successfully');
|
||||
|
||||
// Create README for the site
|
||||
const readmeContent = `# ${path.basename(outputDir)}
|
||||
|
||||
## Original URL
|
||||
${url}
|
||||
|
||||
## Downloaded
|
||||
${new Date().toISOString()}
|
||||
|
||||
## Testing Notes
|
||||
- Site downloaded with assets for insertr testing
|
||||
- Check HTML structure for suitable content sections
|
||||
- Add insertr classes to editable sections
|
||||
|
||||
## Insertr Enhancement Status
|
||||
- [ ] Content sections identified
|
||||
- [ ] Insertr classes added
|
||||
- [ ] Enhanced version tested
|
||||
- [ ] Results documented
|
||||
`;
|
||||
|
||||
fs.writeFileSync(path.join(outputDir, 'README.md'), readmeContent);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Download failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
console.log('Usage: node download-site.js <url> <output-directory>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [url, outputDir] = args;
|
||||
downloadSite(url, outputDir);
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Test script to verify demo sites are working correctly
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
console.log('🧪 Testing Insertr Demo Infrastructure');
|
||||
console.log('=====================================\n');
|
||||
|
||||
// Test 1: Check if enhanced sites exist
|
||||
console.log('📁 Checking enhanced test sites...');
|
||||
|
||||
const danEdenPath = './demos/simple/dan-eden-portfolio-enhanced';
|
||||
if (fs.existsSync(danEdenPath)) {
|
||||
console.log('✅ Dan Eden enhanced site exists');
|
||||
|
||||
// Check if it has insertr elements
|
||||
const indexPath = path.join(danEdenPath, 'index.html');
|
||||
if (fs.existsSync(indexPath)) {
|
||||
const content = fs.readFileSync(indexPath, 'utf8');
|
||||
const insertrElements = content.match(/data-content-id="[^"]+"/g);
|
||||
if (insertrElements && insertrElements.length > 0) {
|
||||
console.log(`✅ Found ${insertrElements.length} insertr-enhanced elements`);
|
||||
} else {
|
||||
console.log('❌ No insertr elements found in enhanced site');
|
||||
}
|
||||
} else {
|
||||
console.log('❌ index.html not found in enhanced site');
|
||||
}
|
||||
} else {
|
||||
console.log('❌ Dan Eden enhanced site not found');
|
||||
console.log(' Run: just enhance-demos');
|
||||
}
|
||||
|
||||
console.log('\n🎯 Demo Commands Available:');
|
||||
console.log(' just demo - Default demo');
|
||||
console.log(' just demo dan-eden - Dan Eden portfolio demo');
|
||||
console.log(' just list-demos - List all available demos');
|
||||
|
||||
console.log('\n🚀 Testing complete!');
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"site_name": "{{SITE_NAME}}",
|
||||
"description": "{{SITE_DESCRIPTION}}",
|
||||
"base_url": "{{BASE_URL}}",
|
||||
"content_sections": [
|
||||
{
|
||||
"selector": ".hero-content",
|
||||
"type": "markdown",
|
||||
"editable": true,
|
||||
"description": "Hero section content"
|
||||
},
|
||||
{
|
||||
"selector": ".feature-block",
|
||||
"type": "markdown",
|
||||
"editable": true,
|
||||
"description": "Feature description blocks"
|
||||
},
|
||||
{
|
||||
"selector": ".about-content",
|
||||
"type": "markdown",
|
||||
"editable": true,
|
||||
"description": "About section content"
|
||||
}
|
||||
],
|
||||
"css_frameworks": ["{{CSS_FRAMEWORK}}"],
|
||||
"complexity": "{{COMPLEXITY}}",
|
||||
"testing_notes": "{{TESTING_NOTES}}"
|
||||
}
|
||||
14
insertr.yaml
@@ -26,20 +26,6 @@ server:
|
||||
discovery:
|
||||
enabled: false # Uses explicit class="insertr" markings
|
||||
aggressive: false
|
||||
- site_id: "dan-eden-portfolio"
|
||||
path: "./demos/dan-eden-portfolio_enhanced"
|
||||
source_path: "./demos/dan-eden-portfolio"
|
||||
auto_enhance: true
|
||||
discovery:
|
||||
enabled: true # Auto-discover elements (no explicit markings)
|
||||
aggressive: false
|
||||
- site_id: "devigo-web"
|
||||
path: "./demos/devigo-web_enhanced"
|
||||
source_path: "./demos/devigo-web"
|
||||
auto_enhance: true
|
||||
discovery:
|
||||
enabled: false # Uses explicit class="insertr" markings
|
||||
aggressive: false
|
||||
# Example additional site configuration:
|
||||
# - site_id: "mysite"
|
||||
# path: "/var/www/mysite"
|
||||
|
||||
@@ -25,20 +25,15 @@ type UserInfo struct {
|
||||
Provider string `json:"iss,omitempty"`
|
||||
}
|
||||
|
||||
// Type aliases for backward compatibility
|
||||
type AuthConfig = config.AuthConfig
|
||||
type OAuthConfig = config.OAuthConfig
|
||||
type OIDCConfig = config.OIDCConfig
|
||||
|
||||
// AuthService handles authentication operations
|
||||
type AuthService struct {
|
||||
config *AuthConfig
|
||||
config *config.AuthConfig
|
||||
provider *oidc.Provider
|
||||
oauth2 *oauth2.Config
|
||||
}
|
||||
|
||||
// NewAuthService creates a new authentication service
|
||||
func NewAuthService(config *AuthConfig) (*AuthService, error) {
|
||||
func NewAuthService(config *config.AuthConfig) (*AuthService, error) {
|
||||
service := &AuthService{config: config}
|
||||
|
||||
// Initialize OIDC provider if configured
|
||||
@@ -176,7 +171,7 @@ func (a *AuthService) parseOIDCToken(tokenString string) (*UserInfo, error) {
|
||||
// parseHMACToken parses and validates a JWT token using HMAC signing
|
||||
func (a *AuthService) parseHMACToken(tokenString string) (*UserInfo, error) {
|
||||
// Parse the token
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
|
||||
// Validate signing method
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
@@ -336,7 +331,7 @@ func (a *AuthService) RequireAuth(next http.Handler) http.Handler {
|
||||
func (a *AuthService) HandleOAuthLogin(w http.ResponseWriter, r *http.Request) {
|
||||
// Handle mock authentication in dev mode
|
||||
if a.config.DevMode && a.config.Provider == "mock" {
|
||||
response := map[string]interface{}{
|
||||
response := map[string]any{
|
||||
"message": "Mock OAuth login",
|
||||
"redirect_url": "/auth/callback?code=mock_code&state=mock_state",
|
||||
"dev_mode": true,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// GetClasses extracts CSS classes from an HTML node
|
||||
@@ -19,12 +20,7 @@ func GetClasses(node *html.Node) []string {
|
||||
|
||||
// ContainsClass checks if a class list contains a specific class
|
||||
func ContainsClass(classes []string, target string) bool {
|
||||
for _, class := range classes {
|
||||
if class == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(classes, target)
|
||||
}
|
||||
|
||||
// getAttribute gets an attribute value from an HTML node
|
||||
@@ -37,29 +33,6 @@ func getAttribute(node *html.Node, key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// hasOnlyTextContent checks if a node contains only text content (no nested HTML elements)
|
||||
// DEPRECATED: Use hasEditableContent for more sophisticated detection
|
||||
func hasOnlyTextContent(node *html.Node) bool {
|
||||
if node.Type != html.ElementNode {
|
||||
return false
|
||||
}
|
||||
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
switch child.Type {
|
||||
case html.ElementNode:
|
||||
// Found a nested HTML element - not text-only
|
||||
return false
|
||||
case html.TextNode:
|
||||
// Text nodes are fine, continue checking
|
||||
continue
|
||||
default:
|
||||
// Comments, etc. - continue checking
|
||||
continue
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Inline formatting elements that are safe for editing
|
||||
var inlineFormattingTags = map[string]bool{
|
||||
"strong": true,
|
||||
|
||||
@@ -192,7 +192,7 @@ export class InsertrAuth {
|
||||
console.log('🔐 Mock OAuth: Simulating authentication...');
|
||||
|
||||
// Simulate network delay
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
// Set authenticated state
|
||||
this.state.isAuthenticated = true;
|
||||
|
||||
@@ -20,13 +20,6 @@ export class InsertrFormRenderer {
|
||||
showEditForm(meta, currentContent, onSave, onCancel) {
|
||||
const { element } = meta;
|
||||
|
||||
// Handle insertr-group elements by getting their viable children
|
||||
if (element.classList.contains('insertr-group')) {
|
||||
const children = this.getGroupChildren(element);
|
||||
const groupMeta = { ...meta, element: children };
|
||||
return this.editor.edit(groupMeta, currentContent, onSave, onCancel);
|
||||
}
|
||||
|
||||
// All other elements use the editor directly
|
||||
return this.editor.edit(meta, currentContent, onSave, onCancel);
|
||||
}
|
||||
|
||||
@@ -1,348 +0,0 @@
|
||||
/**
|
||||
* Unit tests for HTMLPreservationEngine
|
||||
* Tests HTML preservation, sanitization, and attribute maintenance
|
||||
*/
|
||||
import { HTMLPreservationEngine } from './html-preservation.js';
|
||||
|
||||
// Mock DOM environment for testing
|
||||
const mockDocument = {
|
||||
createElement: (tagName) => ({
|
||||
tagName: tagName.toUpperCase(),
|
||||
innerHTML: '',
|
||||
textContent: '',
|
||||
children: [],
|
||||
childNodes: [],
|
||||
attributes: [],
|
||||
classList: new Set(),
|
||||
|
||||
// Mock methods
|
||||
appendChild: function(child) { this.children.push(child); },
|
||||
removeChild: function(child) {
|
||||
const index = this.children.indexOf(child);
|
||||
if (index > -1) this.children.splice(index, 1);
|
||||
},
|
||||
replaceChild: function(newChild, oldChild) {
|
||||
const index = this.children.indexOf(oldChild);
|
||||
if (index > -1) this.children[index] = newChild;
|
||||
},
|
||||
querySelector: function(selector) { return null; },
|
||||
querySelectorAll: function(selector) { return []; },
|
||||
cloneNode: function(deep) { return mockDocument.createElement(tagName); },
|
||||
setAttribute: function(name, value) { this.attributes[name] = value; },
|
||||
getAttribute: function(name) { return this.attributes[name]; },
|
||||
removeAttribute: function(name) { delete this.attributes[name]; }
|
||||
}),
|
||||
createTextNode: (text) => ({
|
||||
nodeType: 3, // TEXT_NODE
|
||||
textContent: text
|
||||
})
|
||||
};
|
||||
|
||||
global.document = mockDocument;
|
||||
global.Node = {
|
||||
TEXT_NODE: 3,
|
||||
ELEMENT_NODE: 1
|
||||
};
|
||||
|
||||
describe('HTMLPreservationEngine', () => {
|
||||
let engine;
|
||||
|
||||
beforeEach(() => {
|
||||
engine = new HTMLPreservationEngine();
|
||||
});
|
||||
|
||||
describe('Content Extraction', () => {
|
||||
test('should extract content with preservation metadata', () => {
|
||||
const mockElement = createMockElement('p', {
|
||||
innerHTML: 'Hello <strong class="emph">world</strong>!',
|
||||
classes: ['insertr'],
|
||||
attributes: { id: 'test-element' }
|
||||
});
|
||||
|
||||
const extracted = engine.extractForEditing(mockElement);
|
||||
|
||||
expect(extracted.html).toBe('Hello <strong class="emph">world</strong>!');
|
||||
expect(extracted.text).toBe('Hello world!');
|
||||
expect(extracted.containerAttributes.class).toBe('insertr');
|
||||
expect(extracted.containerAttributes.id).toBe('test-element');
|
||||
expect(extracted.elementTag).toBe('p');
|
||||
expect(extracted.hasNestedElements).toBe(true);
|
||||
});
|
||||
|
||||
test('should handle simple text content', () => {
|
||||
const mockElement = createMockElement('p', {
|
||||
innerHTML: 'Simple text content',
|
||||
textContent: 'Simple text content'
|
||||
});
|
||||
|
||||
const extracted = engine.extractForEditing(mockElement);
|
||||
|
||||
expect(extracted.html).toBe('Simple text content');
|
||||
expect(extracted.text).toBe('Simple text content');
|
||||
expect(extracted.hasNestedElements).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTML Validation and Sanitization', () => {
|
||||
test('should validate safe HTML', () => {
|
||||
const safeHTML = 'Hello <strong class="emph">world</strong>!';
|
||||
expect(engine.isValidHTML(safeHTML)).toBe(true);
|
||||
});
|
||||
|
||||
test('should reject dangerous HTML', () => {
|
||||
const dangerousHTML = 'Hello <script>alert("xss")</script> world!';
|
||||
expect(engine.isValidHTML(dangerousHTML)).toBe(false);
|
||||
});
|
||||
|
||||
test('should sanitize HTML by removing dangerous elements', () => {
|
||||
const unsafeHTML = 'Hello <script>alert("xss")</script><strong>safe</strong>';
|
||||
const sanitized = engine.validateAndSanitizeHTML(unsafeHTML);
|
||||
|
||||
expect(sanitized).not.toContain('<script>');
|
||||
expect(sanitized).toContain('<strong>safe</strong>');
|
||||
});
|
||||
|
||||
test('should preserve allowed attributes', () => {
|
||||
const htmlWithAttrs = '<a href="#test" class="fancy" data-track="click">Link</a>';
|
||||
const sanitized = engine.validateAndSanitizeHTML(htmlWithAttrs);
|
||||
|
||||
expect(sanitized).toContain('href="#test"');
|
||||
expect(sanitized).toContain('class="fancy"');
|
||||
expect(sanitized).toContain('data-track="click"');
|
||||
});
|
||||
|
||||
test('should remove dangerous href attributes', () => {
|
||||
const htmlWithDangerousHref = '<a href="javascript:alert(1)">Bad Link</a>';
|
||||
const sanitized = engine.validateAndSanitizeHTML(htmlWithDangerousHref);
|
||||
|
||||
expect(sanitized).not.toContain('javascript:');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Attribute Preservation', () => {
|
||||
test('should extract all element attributes', () => {
|
||||
const mockElement = createMockElement('div', {
|
||||
attributes: {
|
||||
class: 'insertr test',
|
||||
id: 'unique-id',
|
||||
'data-value': '123',
|
||||
'aria-label': 'Test element'
|
||||
}
|
||||
});
|
||||
|
||||
const attributes = engine.extractElementAttributes(mockElement);
|
||||
|
||||
expect(attributes.class).toBe('insertr test');
|
||||
expect(attributes.id).toBe('unique-id');
|
||||
expect(attributes['data-value']).toBe('123');
|
||||
expect(attributes['aria-label']).toBe('Test element');
|
||||
});
|
||||
|
||||
test('should restore element attributes correctly', () => {
|
||||
const mockElement = createMockElement('div');
|
||||
const attributes = {
|
||||
class: 'restored',
|
||||
id: 'restored-id',
|
||||
'data-test': 'value'
|
||||
};
|
||||
|
||||
engine.restoreElementAttributes(mockElement, attributes);
|
||||
|
||||
expect(mockElement.attributes.class).toBe('restored');
|
||||
expect(mockElement.attributes.id).toBe('restored-id');
|
||||
expect(mockElement.attributes['data-test']).toBe('value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Content Application', () => {
|
||||
test('should apply HTML content while preserving container attributes', () => {
|
||||
const mockElement = createMockElement('p', {
|
||||
classes: ['insertr', 'special'],
|
||||
attributes: { id: 'preserved-id' }
|
||||
});
|
||||
|
||||
const newHTML = 'Updated <strong class="emph">content</strong>!';
|
||||
const success = engine.applyFromEditing(mockElement, newHTML);
|
||||
|
||||
expect(success).toBe(true);
|
||||
expect(mockElement.innerHTML).toBe('Updated <strong class="emph">content</strong>!');
|
||||
// Container attributes should remain unchanged
|
||||
expect(mockElement.classList.has('insertr')).toBe(true);
|
||||
expect(mockElement.classList.has('special')).toBe(true);
|
||||
});
|
||||
|
||||
test('should handle invalid HTML gracefully', () => {
|
||||
const mockElement = createMockElement('p');
|
||||
const invalidHTML = '<script>malicious()</script>';
|
||||
|
||||
const success = engine.applyFromEditing(mockElement, invalidHTML);
|
||||
|
||||
// Should handle gracefully (either sanitize or reject)
|
||||
expect(typeof success).toBe('boolean');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Plain Text Extraction', () => {
|
||||
test('should extract plain text preserving structure', () => {
|
||||
const mockElement = createMockElement('p', {
|
||||
textContent: 'Hello world and welcome!'
|
||||
});
|
||||
|
||||
const plainText = engine.extractPlainTextWithStructure(mockElement);
|
||||
expect(plainText).toBe('Hello world and welcome!');
|
||||
});
|
||||
|
||||
test('should handle complex nested content', () => {
|
||||
const mockElement = createMockElement('p', {
|
||||
children: [
|
||||
{ nodeType: 3, textContent: 'Hello ' },
|
||||
{ nodeType: 1, textContent: 'world' },
|
||||
{ nodeType: 3, textContent: ' and welcome!' }
|
||||
],
|
||||
childNodes: [
|
||||
{ nodeType: 3, textContent: 'Hello ' },
|
||||
{ nodeType: 1, textContent: 'world' },
|
||||
{ nodeType: 3, textContent: ' and welcome!' }
|
||||
]
|
||||
});
|
||||
|
||||
const plainText = engine.extractPlainTextWithStructure(mockElement);
|
||||
expect(plainText).toBe('Hello world and welcome!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Editable Content Preparation', () => {
|
||||
test('should prepare content for safe editing', () => {
|
||||
const mockElement = createMockElement('p', {
|
||||
innerHTML: 'Hello <strong class="emph">world</strong>!',
|
||||
children: [{ tagName: 'STRONG' }]
|
||||
});
|
||||
|
||||
const prepared = engine.prepareForEditing(mockElement);
|
||||
|
||||
expect(prepared.html).toBe('Hello <strong class="emph">world</strong>!');
|
||||
expect(prepared.editableHTML).toBeDefined();
|
||||
expect(prepared.isComplex).toBe(true);
|
||||
expect(prepared.originalHTML).toBe('Hello <strong class="emph">world</strong>!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Content Finalization', () => {
|
||||
test('should finalize string content', () => {
|
||||
const mockElement = createMockElement('p');
|
||||
const editedContent = 'New <strong>content</strong>';
|
||||
|
||||
const success = engine.finalizeEditing(mockElement, editedContent);
|
||||
expect(success).toBe(true);
|
||||
});
|
||||
|
||||
test('should finalize object content', () => {
|
||||
const mockElement = createMockElement('p');
|
||||
const editedContent = {
|
||||
html: 'New <strong>content</strong>',
|
||||
text: 'New content'
|
||||
};
|
||||
|
||||
const success = engine.finalizeEditing(mockElement, editedContent);
|
||||
expect(success).toBe(true);
|
||||
});
|
||||
|
||||
test('should handle invalid content gracefully', () => {
|
||||
const mockElement = createMockElement('p');
|
||||
const invalidContent = null;
|
||||
|
||||
const success = engine.finalizeEditing(mockElement, invalidContent);
|
||||
expect(success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Security and Safety', () => {
|
||||
test('should allow safe tags', () => {
|
||||
const safeTags = ['strong', 'em', 'a', 'span', 'p', 'div', 'h1', 'h2', 'h3'];
|
||||
safeTags.forEach(tag => {
|
||||
expect(engine.allowedTags.has(tag)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('should allow safe attributes', () => {
|
||||
const safeAttrs = ['class', 'id', 'href', 'title', 'data-test', 'aria-label'];
|
||||
safeAttrs.forEach(attr => {
|
||||
expect(
|
||||
engine.allowedAttributes.has(attr) ||
|
||||
attr.startsWith('data-') ||
|
||||
attr.startsWith('aria-')
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('should create safe editable copy', () => {
|
||||
const unsafeHTML = '<p>Safe content</p><script>alert("unsafe")</script>';
|
||||
const safeCopy = engine.createEditableCopy(unsafeHTML);
|
||||
|
||||
expect(safeCopy).toContain('<p>Safe content</p>');
|
||||
expect(safeCopy).not.toContain('<script>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Merge with Preservation', () => {
|
||||
test('should merge content while preserving specific elements', () => {
|
||||
const originalHTML = '<p>Hello <span class="preserve">world</span>!</p>';
|
||||
const editedHTML = '<p>Hi <span class="preserve">universe</span>!</p>';
|
||||
const preserveSelectors = ['.preserve'];
|
||||
|
||||
const merged = engine.mergeWithPreservation(originalHTML, editedHTML, preserveSelectors);
|
||||
|
||||
// Should preserve the original .preserve element
|
||||
expect(merged).toContain('world'); // Original preserved content
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Helper function to create mock DOM elements for testing
|
||||
function createMockElement(tagName, options = {}) {
|
||||
const element = {
|
||||
tagName: tagName.toUpperCase(),
|
||||
innerHTML: options.innerHTML || '',
|
||||
textContent: options.textContent || options.innerHTML?.replace(/<[^>]*>/g, '') || '',
|
||||
children: options.children || [],
|
||||
childNodes: options.childNodes || options.children || [],
|
||||
classList: new Set(options.classes || []),
|
||||
attributes: { ...options.attributes } || {},
|
||||
|
||||
// Mock methods
|
||||
querySelector: () => null,
|
||||
querySelectorAll: () => [],
|
||||
appendChild: function(child) { this.children.push(child); },
|
||||
removeChild: function(child) {
|
||||
const index = this.children.indexOf(child);
|
||||
if (index > -1) this.children.splice(index, 1);
|
||||
},
|
||||
replaceWith: function(newElement) {
|
||||
// Mock implementation
|
||||
},
|
||||
cloneNode: function(deep) {
|
||||
return createMockElement(tagName, options);
|
||||
},
|
||||
setAttribute: function(name, value) {
|
||||
this.attributes[name] = value;
|
||||
},
|
||||
getAttribute: function(name) {
|
||||
return this.attributes[name];
|
||||
},
|
||||
removeAttribute: function(name) {
|
||||
delete this.attributes[name];
|
||||
}
|
||||
};
|
||||
|
||||
// Add length property to children for proper iteration
|
||||
Object.defineProperty(element.children, 'length', {
|
||||
get: function() { return this.filter(Boolean).length; }
|
||||
});
|
||||
|
||||
// Add classList methods
|
||||
element.classList.has = (className) => element.classList.has(className);
|
||||
element.classList.contains = (className) => element.classList.has(className);
|
||||
element.classList.add = (className) => element.classList.add(className);
|
||||
|
||||
return element;
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
/**
|
||||
* Unit tests for StyleDetectionEngine
|
||||
* Tests based on actual demo examples from simple/index.html
|
||||
*/
|
||||
import { StyleDetectionEngine } from './style-detection.js';
|
||||
|
||||
// Mock DOM environment for testing
|
||||
const mockDocument = {
|
||||
createElement: (tagName) => ({
|
||||
tagName: tagName.toUpperCase(),
|
||||
classList: new Set(),
|
||||
attributes: [],
|
||||
textContent: '',
|
||||
outerHTML: `<${tagName}></${tagName}>`,
|
||||
cloneNode: () => mockDocument.createElement(tagName)
|
||||
})
|
||||
};
|
||||
|
||||
global.document = mockDocument;
|
||||
|
||||
describe('StyleDetectionEngine', () => {
|
||||
let engine;
|
||||
|
||||
beforeEach(() => {
|
||||
engine = new StyleDetectionEngine();
|
||||
});
|
||||
|
||||
describe('Demo Example 1: Styled Strong Element', () => {
|
||||
test('should detect <strong class="emph"> as Emphasis style', () => {
|
||||
// Simulate: <p class="insertr">Hello <strong class="emph">world</strong> and welcome!</p>
|
||||
const mockElement = createMockElement('p', {
|
||||
children: [
|
||||
createMockElement('strong', {
|
||||
classes: ['emph'],
|
||||
textContent: 'world'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
const detectedStyles = engine.detectStyles(mockElement);
|
||||
|
||||
expect(detectedStyles.size).toBe(1);
|
||||
expect(detectedStyles.has('strong_emph')).toBe(true);
|
||||
|
||||
const emphStyle = detectedStyles.get('strong_emph');
|
||||
expect(emphStyle.name).toBe('Emphasis');
|
||||
expect(emphStyle.tagName).toBe('strong');
|
||||
expect(emphStyle.classes).toEqual(['emph']);
|
||||
expect(emphStyle.textContent).toBe('world');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Demo Example 2: Styled Link Element', () => {
|
||||
test('should detect <a class="fancy"> as Fancy Link style', () => {
|
||||
// Simulate: <p class="insertr">Visit our <a class="fancy" href="#about">about page</a> for more info.</p>
|
||||
const mockElement = createMockElement('p', {
|
||||
children: [
|
||||
createMockElement('a', {
|
||||
classes: ['fancy'],
|
||||
attributes: { href: '#about' },
|
||||
textContent: 'about page'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
const detectedStyles = engine.detectStyles(mockElement);
|
||||
|
||||
expect(detectedStyles.size).toBe(1);
|
||||
const fancyLinkStyle = detectedStyles.get('a_fancy');
|
||||
expect(fancyLinkStyle.name).toBe('Fancy Link');
|
||||
expect(fancyLinkStyle.tagName).toBe('a');
|
||||
expect(fancyLinkStyle.attributes.href).toBe('#about');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Demo Example 3: Mixed Content with Complex Attributes', () => {
|
||||
test('should detect link with multiple attributes', () => {
|
||||
// Simulate: <a href="https://example.com" rel="noopener" target="_blank" class="fancy">our site</a>
|
||||
const mockElement = createMockElement('p', {
|
||||
children: [
|
||||
createMockElement('a', {
|
||||
classes: ['fancy'],
|
||||
attributes: {
|
||||
href: 'https://example.com',
|
||||
rel: 'noopener',
|
||||
target: '_blank'
|
||||
},
|
||||
textContent: 'our site'
|
||||
}),
|
||||
createMockElement('span', {
|
||||
classes: ['highlight'],
|
||||
textContent: 'amazing'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
const detectedStyles = engine.detectStyles(mockElement);
|
||||
|
||||
expect(detectedStyles.size).toBe(2);
|
||||
|
||||
const linkStyle = detectedStyles.get('a_fancy');
|
||||
expect(linkStyle.attributes.rel).toBe('noopener');
|
||||
expect(linkStyle.attributes.target).toBe('_blank');
|
||||
|
||||
const highlightStyle = detectedStyles.get('span_highlight');
|
||||
expect(highlightStyle.name).toBe('Highlight');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Demo Example 4: Multiple Styled Elements', () => {
|
||||
test('should detect multiple different styled elements', () => {
|
||||
// Simulate: <strong class="brand">Acme Corp</strong> <span class="highlight">innovative</span> <em class="emph">modern</em>
|
||||
const mockElement = createMockElement('p', {
|
||||
children: [
|
||||
createMockElement('strong', {
|
||||
classes: ['brand'],
|
||||
textContent: 'Acme Corp'
|
||||
}),
|
||||
createMockElement('span', {
|
||||
classes: ['highlight'],
|
||||
textContent: 'innovative'
|
||||
}),
|
||||
createMockElement('em', {
|
||||
classes: ['emph'],
|
||||
textContent: 'modern'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
const detectedStyles = engine.detectStyles(mockElement);
|
||||
|
||||
expect(detectedStyles.size).toBe(3);
|
||||
expect(detectedStyles.has('strong_brand')).toBe(true);
|
||||
expect(detectedStyles.has('span_highlight')).toBe(true);
|
||||
expect(detectedStyles.has('em_emph')).toBe(true);
|
||||
|
||||
expect(detectedStyles.get('strong_brand').name).toBe('Brand');
|
||||
expect(detectedStyles.get('span_highlight').name).toBe('Highlight');
|
||||
expect(detectedStyles.get('em_emph').name).toBe('Emphasis Italic');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Demo Example 5: Button with Data Attributes', () => {
|
||||
test('should detect button with data attributes', () => {
|
||||
// Simulate: <button class="btn" data-action="signup" data-analytics="cta-main">Sign Up Now</button>
|
||||
const mockElement = createMockElement('p', {
|
||||
children: [
|
||||
createMockElement('button', {
|
||||
classes: ['btn'],
|
||||
attributes: {
|
||||
'data-action': 'signup',
|
||||
'data-analytics': 'cta-main'
|
||||
},
|
||||
textContent: 'Sign Up Now'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
const detectedStyles = engine.detectStyles(mockElement);
|
||||
|
||||
expect(detectedStyles.size).toBe(1);
|
||||
const buttonStyle = detectedStyles.get('button_btn');
|
||||
expect(buttonStyle.name).toBe('Button Style');
|
||||
expect(buttonStyle.attributes['data-action']).toBe('signup');
|
||||
expect(buttonStyle.attributes['data-analytics']).toBe('cta-main');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Style Name Generation', () => {
|
||||
test('should generate correct names for common patterns', () => {
|
||||
expect(engine.generateStyleName('strong', ['highlight-price'], {})).toBe('Highlight Price');
|
||||
expect(engine.generateStyleName('span', ['brand-color'], {})).toBe('Brand Color');
|
||||
expect(engine.generateStyleName('a', ['fancy'], {})).toBe('Fancy Link');
|
||||
expect(engine.generateStyleName('button', ['btn'], {})).toBe('Button Style');
|
||||
});
|
||||
|
||||
test('should handle elements with IDs', () => {
|
||||
const name = engine.generateStyleName('span', [], { id: 'unique-element' });
|
||||
expect(name).toBe('Style (unique-element)');
|
||||
});
|
||||
|
||||
test('should fallback to tag names for unstyled elements', () => {
|
||||
expect(engine.generateStyleName('blockquote', [], {})).toBe('Quote');
|
||||
expect(engine.generateStyleName('code', [], {})).toBe('Code');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Template Extraction', () => {
|
||||
test('should create proper templates with placeholders', () => {
|
||||
const mockElement = createMockElement('strong', {
|
||||
classes: ['emph'],
|
||||
attributes: { 'data-test': 'value' }
|
||||
});
|
||||
|
||||
const template = engine.extractTemplate(mockElement);
|
||||
expect(template).toContain('{{TEXT}}');
|
||||
expect(template).toContain('class="emph"');
|
||||
expect(template).toContain('data-test="value"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Element Creation from Templates', () => {
|
||||
test('should create elements with correct styling', () => {
|
||||
const styleInfo = {
|
||||
tagName: 'strong',
|
||||
classes: ['emph'],
|
||||
attributes: { 'data-test': 'value' }
|
||||
};
|
||||
|
||||
const element = engine.createElementFromTemplate(styleInfo, 'test content');
|
||||
|
||||
expect(element.tagName.toLowerCase()).toBe('strong');
|
||||
expect(element.textContent).toBe('test content');
|
||||
expect(element.classList.contains('emph')).toBe(true);
|
||||
expect(element.getAttribute('data-test')).toBe('value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Complex Nesting Detection', () => {
|
||||
test('should detect when elements have nested styled children', () => {
|
||||
const mockElement = createMockElement('p', {
|
||||
children: [
|
||||
createMockElement('strong', { classes: ['emph'] })
|
||||
]
|
||||
});
|
||||
|
||||
expect(engine.hasNestedStyledElements(mockElement)).toBe(true);
|
||||
|
||||
const simpleElement = createMockElement('p', { children: [] });
|
||||
expect(engine.hasNestedStyledElements(simpleElement)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Attribute Significance Detection', () => {
|
||||
test('should identify significant attributes', () => {
|
||||
expect(engine.hasSignificantAttributes({ 'data-action': 'test' })).toBe(true);
|
||||
expect(engine.hasSignificantAttributes({ 'aria-label': 'test' })).toBe(true);
|
||||
expect(engine.hasSignificantAttributes({ href: '#link' })).toBe(true);
|
||||
expect(engine.hasSignificantAttributes({ id: 'unique' })).toBe(true);
|
||||
expect(engine.hasSignificantAttributes({})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
test('should skip elements without styling', () => {
|
||||
const mockElement = createMockElement('p', {
|
||||
children: [
|
||||
createMockElement('span', { classes: [], attributes: {} }),
|
||||
createMockElement('strong', { classes: ['emph'] })
|
||||
]
|
||||
});
|
||||
|
||||
const detectedStyles = engine.detectStyles(mockElement);
|
||||
expect(detectedStyles.size).toBe(1);
|
||||
expect(detectedStyles.has('strong_emph')).toBe(true);
|
||||
});
|
||||
|
||||
test('should handle empty elements', () => {
|
||||
const mockElement = createMockElement('p', { children: [] });
|
||||
const detectedStyles = engine.detectStyles(mockElement);
|
||||
expect(detectedStyles.size).toBe(0);
|
||||
});
|
||||
|
||||
test('should handle elements with only whitespace', () => {
|
||||
const mockElement = createMockElement('p', {
|
||||
children: [
|
||||
createMockElement('span', {
|
||||
classes: ['test'],
|
||||
textContent: ' '
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
const detectedStyles = engine.detectStyles(mockElement);
|
||||
expect(detectedStyles.size).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Helper function to create mock DOM elements for testing
|
||||
function createMockElement(tagName, options = {}) {
|
||||
const element = {
|
||||
tagName: tagName.toUpperCase(),
|
||||
classList: new Set(options.classes || []),
|
||||
children: options.children || [],
|
||||
textContent: options.textContent || '',
|
||||
attributes: [],
|
||||
id: options.attributes?.id || '',
|
||||
outerHTML: `<${tagName}${formatAttributes(options)}></${tagName}>`,
|
||||
|
||||
// Mock methods
|
||||
cloneNode: (deep) => createMockElement(tagName, options),
|
||||
setAttribute: (name, value) => {
|
||||
element.attributes[name] = value;
|
||||
},
|
||||
getAttribute: (name) => element.attributes[name],
|
||||
removeAttribute: (name) => {
|
||||
delete element.attributes[name];
|
||||
}
|
||||
};
|
||||
|
||||
// Add attributes
|
||||
if (options.attributes) {
|
||||
Object.entries(options.attributes).forEach(([key, value]) => {
|
||||
element.attributes.push({ name: key, value: value });
|
||||
});
|
||||
}
|
||||
|
||||
// Add classList methods
|
||||
element.classList.contains = (className) => element.classList.has(className);
|
||||
element.classList.add = (className) => element.classList.add(className);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
function formatAttributes(options) {
|
||||
let attrs = '';
|
||||
if (options.classes && options.classes.length > 0) {
|
||||
attrs += ` class="${options.classes.join(' ')}"`;
|
||||
}
|
||||
if (options.attributes) {
|
||||
Object.entries(options.attributes).forEach(([key, value]) => {
|
||||
attrs += ` ${key}="${value}"`;
|
||||
});
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
/**
|
||||
* Simple test runner for style preservation system
|
||||
* Tests our implementation with actual DOM elements
|
||||
*/
|
||||
|
||||
import { styleDetectionEngine } from './style-detection.js';
|
||||
import { htmlPreservationEngine } from './html-preservation.js';
|
||||
|
||||
/**
|
||||
* Run all style detection tests with real DOM elements
|
||||
*/
|
||||
export function runStyleDetectionTests() {
|
||||
console.log('🧪 Running Style Detection Tests');
|
||||
console.log('================================');
|
||||
|
||||
const results = [];
|
||||
|
||||
// Test 1: Demo Example 1 - Styled Strong Element
|
||||
results.push(testExample1());
|
||||
|
||||
// Test 2: Demo Example 2 - Styled Link Element
|
||||
results.push(testExample2());
|
||||
|
||||
// Test 3: Demo Example 4 - Multiple Styled Elements
|
||||
results.push(testExample4());
|
||||
|
||||
// Test 4: Demo Example 5 - Button with Data Attributes
|
||||
results.push(testExample5());
|
||||
|
||||
// Summary
|
||||
const passed = results.filter(r => r.passed).length;
|
||||
const total = results.length;
|
||||
|
||||
console.log(`\n📊 Test Results: ${passed}/${total} passed`);
|
||||
|
||||
if (passed === total) {
|
||||
console.log('✅ All style detection tests passed!');
|
||||
} else {
|
||||
console.log('❌ Some tests failed - see details above');
|
||||
}
|
||||
|
||||
return { passed, total, results };
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Example 1: <p>Hello <strong class="emph">world</strong> and welcome!</p>
|
||||
*/
|
||||
function testExample1() {
|
||||
console.log('\n🔍 Test 1: Styled Strong Element');
|
||||
|
||||
try {
|
||||
// Create test element
|
||||
const container = document.createElement('p');
|
||||
container.className = 'insertr';
|
||||
container.innerHTML = 'Hello <strong class="emph">world</strong> and welcome!';
|
||||
|
||||
// Detect styles
|
||||
const detectedStyles = styleDetectionEngine.detectStyles(container);
|
||||
|
||||
// Validate results
|
||||
const hasEmphStyle = detectedStyles.has('strong_emph');
|
||||
const emphStyle = detectedStyles.get('strong_emph');
|
||||
const correctName = emphStyle?.name === 'Emphasis';
|
||||
const correctTag = emphStyle?.tagName === 'strong';
|
||||
const correctClasses = emphStyle?.classes?.includes('emph');
|
||||
|
||||
const passed = hasEmphStyle && correctName && correctTag && correctClasses;
|
||||
|
||||
console.log(` Detected ${detectedStyles.size} style(s)`);
|
||||
console.log(` Found "Emphasis" style: ${hasEmphStyle ? '✅' : '❌'}`);
|
||||
console.log(` Correct name: ${correctName ? '✅' : '❌'}`);
|
||||
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
||||
|
||||
return { test: 'Example 1', passed, details: { hasEmphStyle, correctName, correctTag, correctClasses } };
|
||||
|
||||
} catch (error) {
|
||||
console.log(` Error: ${error.message}`);
|
||||
console.log(` Result: ❌ FAILED`);
|
||||
return { test: 'Example 1', passed: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Example 2: <p>Visit our <a class="fancy" href="#about">about page</a> for more info.</p>
|
||||
*/
|
||||
function testExample2() {
|
||||
console.log('\n🔍 Test 2: Styled Link Element');
|
||||
|
||||
try {
|
||||
const container = document.createElement('p');
|
||||
container.className = 'insertr';
|
||||
container.innerHTML = 'Visit our <a class="fancy" href="#about">about page</a> for more info.';
|
||||
|
||||
const detectedStyles = styleDetectionEngine.detectStyles(container);
|
||||
|
||||
const hasFancyStyle = detectedStyles.has('a_fancy');
|
||||
const fancyStyle = detectedStyles.get('a_fancy');
|
||||
const correctName = fancyStyle?.name === 'Fancy Link';
|
||||
const hasHref = fancyStyle?.attributes?.href === '#about';
|
||||
|
||||
const passed = hasFancyStyle && correctName && hasHref;
|
||||
|
||||
console.log(` Detected ${detectedStyles.size} style(s)`);
|
||||
console.log(` Found "Fancy Link" style: ${hasFancyStyle ? '✅' : '❌'}`);
|
||||
console.log(` Preserved href attribute: ${hasHref ? '✅' : '❌'}`);
|
||||
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
||||
|
||||
return { test: 'Example 2', passed, details: { hasFancyStyle, correctName, hasHref } };
|
||||
|
||||
} catch (error) {
|
||||
console.log(` Error: ${error.message}`);
|
||||
console.log(` Result: ❌ FAILED`);
|
||||
return { test: 'Example 2', passed: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Example 4: Multiple styled elements
|
||||
*/
|
||||
function testExample4() {
|
||||
console.log('\n🔍 Test 4: Multiple Styled Elements');
|
||||
|
||||
try {
|
||||
const container = document.createElement('p');
|
||||
container.className = 'insertr';
|
||||
container.innerHTML = 'Welcome to <strong class="brand">Acme Corp</strong> where we create <span class="highlight">innovative</span> solutions for <em class="emph">modern</em> businesses.';
|
||||
|
||||
const detectedStyles = styleDetectionEngine.detectStyles(container);
|
||||
|
||||
const hasBrand = detectedStyles.has('strong_brand');
|
||||
const hasHighlight = detectedStyles.has('span_highlight');
|
||||
const hasEmphItalic = detectedStyles.has('em_emph');
|
||||
const correctCount = detectedStyles.size === 3;
|
||||
|
||||
const passed = hasBrand && hasHighlight && hasEmphItalic && correctCount;
|
||||
|
||||
console.log(` Detected ${detectedStyles.size} style(s) (expected 3)`);
|
||||
console.log(` Found "Brand" style: ${hasBrand ? '✅' : '❌'}`);
|
||||
console.log(` Found "Highlight" style: ${hasHighlight ? '✅' : '❌'}`);
|
||||
console.log(` Found "Emphasis Italic" style: ${hasEmphItalic ? '✅' : '❌'}`);
|
||||
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
||||
|
||||
return { test: 'Example 4', passed, details: { hasBrand, hasHighlight, hasEmphItalic, correctCount } };
|
||||
|
||||
} catch (error) {
|
||||
console.log(` Error: ${error.message}`);
|
||||
console.log(` Result: ❌ FAILED`);
|
||||
return { test: 'Example 4', passed: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Example 5: Button with data attributes
|
||||
*/
|
||||
function testExample5() {
|
||||
console.log('\n🔍 Test 5: Button with Data Attributes');
|
||||
|
||||
try {
|
||||
const container = document.createElement('p');
|
||||
container.className = 'insertr';
|
||||
container.innerHTML = 'Ready to start? <button class="btn" data-action="signup" data-analytics="cta-main">Sign Up Now</button> and begin your journey!';
|
||||
|
||||
const detectedStyles = styleDetectionEngine.detectStyles(container);
|
||||
|
||||
const hasButtonStyle = detectedStyles.has('button_btn');
|
||||
const buttonStyle = detectedStyles.get('button_btn');
|
||||
const correctName = buttonStyle?.name === 'Button Style';
|
||||
const hasDataAction = buttonStyle?.attributes?.['data-action'] === 'signup';
|
||||
const hasDataAnalytics = buttonStyle?.attributes?.['data-analytics'] === 'cta-main';
|
||||
|
||||
const passed = hasButtonStyle && correctName && hasDataAction && hasDataAnalytics;
|
||||
|
||||
console.log(` Detected ${detectedStyles.size} style(s)`);
|
||||
console.log(` Found "Button Style": ${hasButtonStyle ? '✅' : '❌'}`);
|
||||
console.log(` Preserved data-action: ${hasDataAction ? '✅' : '❌'}`);
|
||||
console.log(` Preserved data-analytics: ${hasDataAnalytics ? '✅' : '❌'}`);
|
||||
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
||||
|
||||
return { test: 'Example 5', passed, details: { hasButtonStyle, correctName, hasDataAction, hasDataAnalytics } };
|
||||
|
||||
} catch (error) {
|
||||
console.log(` Error: ${error.message}`);
|
||||
console.log(` Result: ❌ FAILED`);
|
||||
return { test: 'Example 5', passed: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run HTML preservation tests
|
||||
*/
|
||||
export function runHTMLPreservationTests() {
|
||||
console.log('\n🧪 Running HTML Preservation Tests');
|
||||
console.log('==================================');
|
||||
|
||||
const results = [];
|
||||
|
||||
results.push(testHTMLExtraction());
|
||||
results.push(testHTMLApplication());
|
||||
results.push(testAttributePreservation());
|
||||
|
||||
const passed = results.filter(r => r.passed).length;
|
||||
const total = results.length;
|
||||
|
||||
console.log(`\n📊 HTML Preservation Results: ${passed}/${total} passed`);
|
||||
|
||||
return { passed, total, results };
|
||||
}
|
||||
|
||||
function testHTMLExtraction() {
|
||||
console.log('\n🔍 HTML Extraction Test');
|
||||
|
||||
try {
|
||||
const element = document.createElement('p');
|
||||
element.className = 'insertr test';
|
||||
element.id = 'test-element';
|
||||
element.innerHTML = 'Hello <strong class="emph">world</strong>!';
|
||||
|
||||
const extracted = htmlPreservationEngine.extractForEditing(element);
|
||||
|
||||
const hasHTML = extracted.html === 'Hello <strong class="emph">world</strong>!';
|
||||
const hasText = extracted.text === 'Hello world!';
|
||||
const hasAttributes = extracted.containerAttributes.class === 'insertr test';
|
||||
const detectsNesting = extracted.hasNestedElements === true;
|
||||
|
||||
const passed = hasHTML && hasText && hasAttributes && detectsNesting;
|
||||
|
||||
console.log(` HTML extraction: ${hasHTML ? '✅' : '❌'}`);
|
||||
console.log(` Text extraction: ${hasText ? '✅' : '❌'}`);
|
||||
console.log(` Attribute preservation: ${hasAttributes ? '✅' : '❌'}`);
|
||||
console.log(` Nesting detection: ${detectsNesting ? '✅' : '❌'}`);
|
||||
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
||||
|
||||
return { test: 'HTML Extraction', passed };
|
||||
|
||||
} catch (error) {
|
||||
console.log(` Error: ${error.message}`);
|
||||
console.log(` Result: ❌ FAILED`);
|
||||
return { test: 'HTML Extraction', passed: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
function testHTMLApplication() {
|
||||
console.log('\n🔍 HTML Application Test');
|
||||
|
||||
try {
|
||||
const element = document.createElement('p');
|
||||
element.className = 'insertr original';
|
||||
|
||||
const newHTML = 'Updated <strong class="emph">content</strong>!';
|
||||
const success = htmlPreservationEngine.applyFromEditing(element, newHTML);
|
||||
|
||||
const appliedCorrectly = element.innerHTML === newHTML;
|
||||
const preservedClass = element.className === 'insertr original';
|
||||
|
||||
const passed = success && appliedCorrectly && preservedClass;
|
||||
|
||||
console.log(` Application success: ${success ? '✅' : '❌'}`);
|
||||
console.log(` Content applied: ${appliedCorrectly ? '✅' : '❌'}`);
|
||||
console.log(` Container class preserved: ${preservedClass ? '✅' : '❌'}`);
|
||||
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
||||
|
||||
return { test: 'HTML Application', passed };
|
||||
|
||||
} catch (error) {
|
||||
console.log(` Error: ${error.message}`);
|
||||
console.log(` Result: ❌ FAILED`);
|
||||
return { test: 'HTML Application', passed: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
function testAttributePreservation() {
|
||||
console.log('\n🔍 Attribute Preservation Test');
|
||||
|
||||
try {
|
||||
const element = document.createElement('div');
|
||||
const originalAttrs = {
|
||||
class: 'insertr test',
|
||||
id: 'unique-id',
|
||||
'data-value': '123'
|
||||
};
|
||||
|
||||
// Apply original attributes
|
||||
Object.entries(originalAttrs).forEach(([key, value]) => {
|
||||
element.setAttribute(key, value);
|
||||
});
|
||||
|
||||
// Extract and restore
|
||||
const extracted = htmlPreservationEngine.extractElementAttributes(element);
|
||||
const newElement = document.createElement('div');
|
||||
htmlPreservationEngine.restoreElementAttributes(newElement, extracted);
|
||||
|
||||
const classRestored = newElement.getAttribute('class') === 'insertr test';
|
||||
const idRestored = newElement.getAttribute('id') === 'unique-id';
|
||||
const dataRestored = newElement.getAttribute('data-value') === '123';
|
||||
|
||||
const passed = classRestored && idRestored && dataRestored;
|
||||
|
||||
console.log(` Class attribute: ${classRestored ? '✅' : '❌'}`);
|
||||
console.log(` ID attribute: ${idRestored ? '✅' : '❌'}`);
|
||||
console.log(` Data attribute: ${dataRestored ? '✅' : '❌'}`);
|
||||
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
||||
|
||||
return { test: 'Attribute Preservation', passed };
|
||||
|
||||
} catch (error) {
|
||||
console.log(` Error: ${error.message}`);
|
||||
console.log(` Result: ❌ FAILED`);
|
||||
return { test: 'Attribute Preservation', passed: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all tests
|
||||
*/
|
||||
export function runAllTests() {
|
||||
console.log('🚀 Running All Style Preservation System Tests');
|
||||
console.log('==============================================');
|
||||
|
||||
const styleResults = runStyleDetectionTests();
|
||||
const htmlResults = runHTMLPreservationTests();
|
||||
|
||||
const totalPassed = styleResults.passed + htmlResults.passed;
|
||||
const totalTests = styleResults.total + htmlResults.total;
|
||||
|
||||
console.log(`\n🎯 Overall Results: ${totalPassed}/${totalTests} tests passed`);
|
||||
|
||||
if (totalPassed === totalTests) {
|
||||
console.log('🎉 All tests passed! Style preservation system is working correctly.');
|
||||
} else {
|
||||
console.log('⚠️ Some tests failed. Review implementation before proceeding.');
|
||||
}
|
||||
|
||||
return {
|
||||
passed: totalPassed === totalTests,
|
||||
details: { styleResults, htmlResults }
|
||||
};
|
||||
}
|
||||