{"_id":"@asciisd/vue-progressive-iframe","_rev":"2-3e1da831c57580913df51baba90c5a4e","name":"@asciisd/vue-progressive-iframe","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@asciisd/vue-progressive-iframe","version":"1.0.0","keywords":["vue","vue3","iframe","progressive-loading","content-detection","typescript","composition-api"],"author":{"name":"Caveo Development Team"},"license":"MIT","_id":"@asciisd/vue-progressive-iframe@1.0.0","maintainers":[{"name":"aemaddin","email":"aemaddin@gmail.com"}],"homepage":"https://github.com/aemaddin/vue-progressive-iframe#readme","bugs":{"url":"https://github.com/aemaddin/vue-progressive-iframe/issues"},"dist":{"shasum":"7564d0a856f14c5536e7002b2e261e2e89013a6b","tarball":"https://registry.npmjs.org/@asciisd/vue-progressive-iframe/-/vue-progressive-iframe-1.0.0.tgz","fileCount":28,"integrity":"sha512-2ij4BPLBFyrdlSfUUGJhO0LEuQBpf6oVVhfNrKTennZTIKW8o7SCytbHhcJPrtp3wcWlcBSKyfkWiDLdytfraw==","signatures":[{"sig":"MEQCIArUZNf/9xYs5JcBoIRK7k4emMwwKPLwnmEZj1X+qASYAiBK2FnnbWWIhUOItKx+wIeRoCsduV21lzzfXJPC0VmUKA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":138918},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.esm.js","engines":{"node":">=16.0.0"},"gitHead":"e66fcc23c3f76e8406bff7d9f63b97707b307961","scripts":{"dev":"vite build --watch","lint":"eslint src --ext .ts,.vue","test":"vitest","build":"vite build","typecheck":"vue-tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"aemaddin","email":"aemaddin@gmail.com"},"repository":{"url":"git+https://github.com/aemaddin/vue-progressive-iframe.git","type":"git"},"_npmVersion":"10.9.3","description":"A Vue 3 component for progressive iframe loading with advanced content detection","directories":{},"_nodeVersion":"22.18.0","dependencies":{"@vueuse/core":"^10.7.0"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.3.13","vite":"^5.0.8","eslint":"^8.55.0","rollup":"^4.6.1","vitest":"^1.0.4","vue-tsc":"^1.8.25","typescript":"^5.3.3","@types/node":"^20.10.0","vite-plugin-dts":"^4.5.4","eslint-plugin-vue":"^9.19.2","rollup-plugin-vue":"^6.0.0","@vitejs/plugin-vue":"^4.5.2","@rollup/plugin-typescript":"^11.1.6","@typescript-eslint/parser":"^6.14.0","@rollup/plugin-node-resolve":"^15.2.3","@vue/eslint-config-typescript":"^12.0.0","@typescript-eslint/eslint-plugin":"^6.14.0"},"peerDependencies":{"vue":"^3.3.0"},"_npmOperationalInternal":{"tmp":"tmp/vue-progressive-iframe_1.0.0_1754572110971_0.18939727420423602","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@asciisd/vue-progressive-iframe","version":"1.1.0","description":"A Vue 3 component for progressive iframe loading with advanced content detection","main":"dist/index.js","module":"dist/index.esm.js","types":"dist/index.d.ts","scripts":{"build":"vite build","dev":"vite build --watch","typecheck":"vue-tsc --noEmit","lint":"eslint src --ext .ts,.vue","test":"vitest","prepublishOnly":"npm run build"},"keywords":["vue","vue3","iframe","progressive-loading","content-detection","typescript","composition-api"],"author":{"name":"Caveo Development Team"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/aemaddin/vue-progressive-iframe.git"},"bugs":{"url":"https://github.com/aemaddin/vue-progressive-iframe/issues"},"homepage":"https://github.com/aemaddin/vue-progressive-iframe#readme","peerDependencies":{"vue":"^3.5.0"},"devDependencies":{"@eslint/js":"^9.32.0","@rollup/plugin-node-resolve":"^16.0.1","@rollup/plugin-typescript":"^12.1.4","@types/node":"^22.10.0","@typescript-eslint/eslint-plugin":"^8.39.0","@typescript-eslint/parser":"^8.39.0","@vitejs/plugin-vue":"^6.0.1","@vue/eslint-config-typescript":"^14.6.0","eslint":"^9.32.0","eslint-plugin-vue":"^10.4.0","rollup":"^4.6.1","rollup-plugin-vue":"^6.0.0","typescript":"^5.7.2","vite":"^6.0.1","vite-plugin-dts":"^4.5.4","vitest":"^3.2.4","vue":"^3.5.13","vue-tsc":"^2.1.10"},"dependencies":{"@vueuse/core":"^13.6.0"},"engines":{"node":">=16.0.0"},"type":"module","_id":"@asciisd/vue-progressive-iframe@1.1.0","gitHead":"be52379823078545352fc415e05934554a74508e","_nodeVersion":"22.18.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-mGKWDZWQ+Upta/lqt5sh+F4Vtf0h5YQT0o3pLHn67UU3cdps2Ef9Yt7jc0/MbdlcSB0OuR+sfq3hmZ6Uv2WCgg==","shasum":"0f2340640e78f26fa5c916e9649806d49a71141c","tarball":"https://registry.npmjs.org/@asciisd/vue-progressive-iframe/-/vue-progressive-iframe-1.1.0.tgz","fileCount":28,"unpackedSize":142610,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDuBtOwIYWuleLAYgUxmkmTTNXDZbMLvwndag8W8Fr5vAIhALifWoGuAWW/j7wb+s2uyR3WENE8qjS3u1h3JFOIn+fk"}]},"_npmUser":{"name":"aemaddin","email":"aemaddin@gmail.com"},"directories":{},"maintainers":[{"name":"aemaddin","email":"aemaddin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/vue-progressive-iframe_1.1.0_1754573789537_0.6664660078894695"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-07T13:08:30.887Z","modified":"2025-08-07T13:36:29.951Z","1.0.0":"2025-08-07T13:08:31.135Z","1.1.0":"2025-08-07T13:36:29.729Z"},"bugs":{"url":"https://github.com/aemaddin/vue-progressive-iframe/issues"},"author":{"name":"Caveo Development Team"},"license":"MIT","homepage":"https://github.com/aemaddin/vue-progressive-iframe#readme","keywords":["vue","vue3","iframe","progressive-loading","content-detection","typescript","composition-api"],"repository":{"type":"git","url":"git+https://github.com/aemaddin/vue-progressive-iframe.git"},"description":"A Vue 3 component for progressive iframe loading with advanced content detection","maintainers":[{"name":"aemaddin","email":"aemaddin@gmail.com"}],"readme":"# Vue Progressive Iframe\n\nA Vue 3 component library for progressive iframe loading with advanced content detection. Perfect for embedding third-party content like analytics dashboards, trading platforms, or any iframe-based applications.\n\n## ✨ Features\n\n- **Multiple Loading Strategies**: Choose from 5 optimized loading strategies based on [Aaron Peters' research](https://www.aaronpeters.nl/blog/iframe-loading-techniques-and-performance/)\n- **Progressive Loading**: Show iframe immediately with loading overlay (default)\n- **Dynamic Async**: Meebo's ultra-awesome technique - no onload blocking, no busy indicators\n- **Smart Content Detection**: Automatically detect when iframe content is actually ready\n- **Performance Optimized**: Prevent main page onload blocking and minimize browser busy indicators\n- **Cross-Origin Support**: Works with cross-origin iframes using intelligent heuristics\n- **TypeScript Support**: Full TypeScript support with proper type definitions\n- **Customizable UI**: Flexible loading states and error handling\n- **Vue 3 Composition API**: Built with modern Vue 3 patterns\n- **Lightweight**: Minimal dependencies, optimized bundle size\n\n## 🚀 Installation\n\n[![npm version](https://badge.fury.io/js/@asciisd%2Fvue-progressive-iframe.svg)](https://www.npmjs.com/package/@asciisd/vue-progressive-iframe)\n[![npm downloads](https://img.shields.io/npm/dm/@asciisd/vue-progressive-iframe.svg)](https://www.npmjs.com/package/@asciisd/vue-progressive-iframe)\n[![license](https://img.shields.io/npm/l/@asciisd/vue-progressive-iframe.svg)](https://github.com/aemaddin/vue-progressive-iframe/blob/main/LICENSE)\n\n```bash\nnpm install @asciisd/vue-progressive-iframe\n```\n\n**Requirements:**\n\n- Vue 3.5+\n- Node.js 16+\n- TypeScript 5+ (if using TypeScript)\n\n### Plugin Installation (Optional)\n\nIf you want to register the component globally:\n\n```typescript\nimport { createApp } from \"vue\";\nimport { VueProgressiveIframe } from \"@asciisd/vue-progressive-iframe\";\n\nconst app = createApp({});\napp.use(VueProgressiveIframe);\n```\n\n## 📖 Usage\n\n### Basic Usage\n\n```vue\n<template>\n  <ProgressiveIframe\n    :src=\"iframeUrl\"\n    :height=\"600\"\n    @content-loaded=\"onContentLoaded\"\n    @load-error=\"onLoadError\"\n  />\n</template>\n\n<script setup>\nimport { ProgressiveIframe } from \"@asciisd/vue-progressive-iframe\";\n\nconst iframeUrl = \"https://example.com/dashboard\";\n\nconst onContentLoaded = (loadTime) => {\n  console.log(`Content loaded in ${loadTime}ms`);\n};\n\nconst onLoadError = (error) => {\n  console.error(\"Failed to load iframe content:\", error);\n};\n</script>\n```\n\n### Performance-Optimized Usage\n\n```vue\n<template>\n  <ProgressiveIframe\n    :src=\"iframeUrl\"\n    :height=\"800\"\n    loading-strategy=\"dynamic-async\"\n    :prevent-onload-blocking=\"true\"\n    :minimize-busy-indicators=\"true\"\n    :content-detection-timeout=\"30000\"\n    :show-debug-info=\"true\"\n    @content-loaded=\"onContentLoaded\"\n    @detection-progress=\"onProgress\"\n  >\n    <template #loading>\n      <div class=\"custom-loading\">\n        <h3>Loading Dashboard...</h3>\n        <progress :value=\"progress\" max=\"100\"></progress>\n      </div>\n    </template>\n\n    <template #error=\"{ error, retry }\">\n      <div class=\"custom-error\">\n        <h3>Failed to Load</h3>\n        <p>{{ error.message }}</p>\n        <button @click=\"retry\">Try Again</button>\n      </div>\n    </template>\n  </ProgressiveIframe>\n</template>\n```\n\n### Using the Composable\n\n```vue\n<script setup>\nimport { useProgressiveIframe } from \"@asciisd/vue-progressive-iframe\";\n\nconst {\n  iframeRef,\n  isContentLoading,\n  hasError,\n  errorMessage,\n  contentCheckCount,\n  forceLoad,\n  refresh,\n} = useProgressiveIframe({\n  src: \"https://example.com/dashboard\",\n  contentDetectionTimeout: 30000,\n  maxContentChecks: 30,\n  crossOriginWaitTime: 10000,\n});\n</script>\n\n<template>\n  <div class=\"relative\">\n    <iframe\n      ref=\"iframeRef\"\n      :src=\"src\"\n      class=\"w-full h-96\"\n      :class=\"{ 'opacity-30': isContentLoading }\"\n    />\n\n    <div\n      v-if=\"isContentLoading\"\n      class=\"absolute inset-0 flex items-center justify-center\"\n    >\n      <div>Loading... ({{ contentCheckCount }}/30)</div>\n    </div>\n\n    <div\n      v-if=\"hasError\"\n      class=\"absolute inset-0 flex items-center justify-center bg-red-50\"\n    >\n      <div>\n        <p>{{ errorMessage }}</p>\n        <button @click=\"forceLoad\">Force Load</button>\n        <button @click=\"refresh\">Retry</button>\n      </div>\n    </div>\n  </div>\n</template>\n```\n\n## 🚀 Loading Strategies\n\nChoose the optimal loading strategy based on your use case:\n\n| Strategy        | Blocks Onload | Busy Indicator | Best For                             | Performance |\n| --------------- | ------------- | -------------- | ------------------------------------ | ----------- |\n| `progressive`   | ❌ No         | ❌ No          | Default choice, immediate visibility | ⭐⭐⭐⭐⭐  |\n| `dynamic-async` | ❌ No         | ❌ No          | Third-party widgets, ads             | ⭐⭐⭐⭐⭐  |\n| `friendly`      | ❌ No         | ❌ No          | Same-domain content, widgets         | ⭐⭐⭐⭐⭐  |\n| `after-onload`  | ❌ No         | ✅ Yes         | Below-fold content                   | ⭐⭐⭐⭐    |\n| `settimeout`    | ❌ No\\*       | ✅ Yes         | Legacy compatibility                 | ⭐⭐⭐      |\n| `traditional`   | ✅ Yes        | ✅ Yes         | Critical content only                | ⭐⭐        |\n\n\\*Note: setTimeout strategy may block onload in IE8\n\n### Strategy Recommendations\n\n```typescript\nimport { getRecommendedStrategy } from \"@asciisd/vue-progressive-iframe\";\n\n// For third-party analytics dashboard\nconst strategy = getRecommendedStrategy({\n  isThirdParty: true,\n  isAboveFold: true,\n  isCritical: false,\n  blockingTolerance: \"none\",\n}); // Returns: 'dynamic-async'\n\n// For critical above-the-fold content\nconst strategy = getRecommendedStrategy({\n  isThirdParty: false,\n  isAboveFold: true,\n  isCritical: true,\n  blockingTolerance: \"medium\",\n}); // Returns: 'progressive'\n```\n\n### Friendly Iframe for Same-Domain Content\n\nThe **Friendly Iframe** technique is perfect for same-domain content, ads, and widgets. Inspired by [vue-friendly-iframe](https://github.com/officert/vue-friendly-iframe) and IAB recommendations:\n\n```vue\n<template>\n  <!-- Embed HTML content directly -->\n  <ProgressiveIframe\n    loading-strategy=\"friendly\"\n    :html-content=\"widgetHtml\"\n    :styles=\"['body { margin: 0; padding: 20px; }']\"\n    :auto-resize=\"true\"\n    :enable-bridge=\"true\"\n    @bridge-message=\"handleMessage\"\n  />\n</template>\n\n<script setup>\nconst widgetHtml = `\n  <div>\n    <h3>Analytics Widget</h3>\n    <div id=\"stats\">Loading...</div>\n    <button onclick=\"updateStats()\">Refresh</button>\n    <script>\n      // Set flag for friendly iframe\n      window.inDapIF = true;\n\n      function updateStats() {\n        // Send message to parent\n        parent.postMessage({\n          type: 'iframe_bridge_stats_updated',\n          payload: { views: 1234, users: 567 }\n        }, '*');\n      }\n    </script>\n  </div>\n`\n\nconst handleMessage = (data) => {\n  if (data.stats_updated) {\n    console.log('Stats updated:', data.payload)\n  }\n}\n</script>\n```\n\n#### Friendly Iframe Benefits\n\n- ✅ **Same-domain access** - Full control over iframe content\n- ✅ **Auto-resize** - Automatically adjusts to content height\n- ✅ **PostMessage bridge** - Seamless parent-iframe communication\n- ✅ **No onload blocking** - Doesn't interfere with main page loading\n- ✅ **Keep-alive support** - Preserve state across route changes\n\n## 📦 Available Exports\n\n```typescript\nimport {\n  // Main component\n  ProgressiveIframe,\n\n  // Composable\n  useProgressiveIframe,\n\n  // Plugin for global registration\n  VueProgressiveIframe,\n\n  // Utilities\n  createFriendlyIframe,\n  generateSandboxAttributes,\n  detectContentReady,\n\n  // Types\n  type IframeLoadingStrategy,\n  type ProgressiveIframeOptions,\n  type ProgressiveIframeEvents,\n} from \"@asciisd/vue-progressive-iframe\";\n```\n\n## 🔧 API Reference\n\n### ProgressiveIframe Component\n\n#### Props\n\n| Prop                      | Type                    | Default                                                      | Description                               |\n| ------------------------- | ----------------------- | ------------------------------------------------------------ | ----------------------------------------- |\n| `src`                     | `string`                | **required**                                                 | The iframe source URL                     |\n| `height`                  | `number \\| string`      | `600`                                                        | Iframe height in pixels or CSS value      |\n| `width`                   | `number \\| string`      | `'100%'`                                                     | Iframe width in pixels or CSS value       |\n| `contentDetectionTimeout` | `number`                | `30000`                                                      | Timeout for content detection (ms)        |\n| `maxContentChecks`        | `number`                | `30`                                                         | Maximum number of content checks          |\n| `crossOriginWaitTime`     | `number`                | `10000`                                                      | Wait time for cross-origin detection (ms) |\n| `showDebugInfo`           | `boolean`               | `false`                                                      | Show debug information                    |\n| `allowedOrigins`          | `string[]`              | `[]`                                                         | Allowed origins for postMessage           |\n| `sandbox`                 | `string`                | `'allow-scripts allow-same-origin allow-forms allow-popups'` | Iframe sandbox attributes                 |\n| `loadingStrategy`         | `IframeLoadingStrategy` | `'progressive'`                                              | Loading strategy to use                   |\n| `preventOnloadBlocking`   | `boolean`               | `true`                                                       | Prevent blocking main page onload         |\n| `minimizeBusyIndicators`  | `boolean`               | `true`                                                       | Minimize browser busy indicators          |\n| `htmlContent`             | `string`                | `undefined`                                                  | HTML content for friendly iframes         |\n| `styles`                  | `string[]`              | `[]`                                                         | CSS styles for friendly iframes           |\n| `autoResize`              | `boolean`               | `false`                                                      | Auto-resize iframe to content height      |\n| `enableKeepAlive`         | `boolean`               | `false`                                                      | Enable iframe state preservation          |\n| `enableBridge`            | `boolean`               | `false`                                                      | Enable PostMessage communication bridge   |\n\n#### Events\n\n| Event                | Payload                                     | Description                              |\n| -------------------- | ------------------------------------------- | ---------------------------------------- |\n| `content-loaded`     | `{ loadTime: number, checkCount: number }`  | Fired when content is detected as loaded |\n| `load-error`         | `{ error: Error, errorCount: number }`      | Fired when loading fails                 |\n| `detection-progress` | `{ checkCount: number, maxChecks: number }` | Fired during content detection           |\n| `force-loaded`       | `{ loadTime: number }`                      | Fired when force load is used            |\n\n#### Slots\n\n| Slot      | Props                                                         | Description       |\n| --------- | ------------------------------------------------------------- | ----------------- |\n| `loading` | `{ checkCount: number, maxChecks: number, loadTime: number }` | Custom loading UI |\n| `error`   | `{ error: Error, retry: Function, forceLoad: Function }`      | Custom error UI   |\n\n### useProgressiveIframe Composable\n\n```typescript\nfunction useProgressiveIframe(options: {\n  src: string;\n  contentDetectionTimeout?: number;\n  maxContentChecks?: number;\n  crossOriginWaitTime?: number;\n  allowedOrigins?: string[];\n}): {\n  // Refs\n  iframeRef: Ref<HTMLIFrameElement | undefined>;\n  isContentLoading: Ref<boolean>;\n  hasError: Ref<boolean>;\n  errorMessage: Ref<string>;\n  contentCheckCount: Ref<number>;\n  loadStartTime: Ref<number>;\n\n  // Methods\n  forceLoad: () => void;\n  refresh: () => void;\n  startContentDetection: () => void;\n\n  // Computed\n  loadTime: ComputedRef<number>;\n  isTimeout: ComputedRef<boolean>;\n};\n```\n\n## 🎯 How It Works\n\n1. **Immediate Visibility**: The iframe is rendered and visible immediately\n2. **Progressive Detection**: Content detection starts after iframe initialization\n3. **Smart Heuristics**: Uses different detection methods for same-origin vs cross-origin\n4. **Fallback Layers**: Multiple fallback mechanisms ensure content eventually loads\n5. **User Control**: Force load option available if automatic detection fails\n\n### Content Detection Methods\n\n- **Same-Origin**: Direct DOM inspection of iframe content\n- **Cross-Origin**: Property-based detection with timing heuristics\n- **PostMessage**: Listens for messages from iframe content\n- **Timeout Fallback**: Graceful degradation after timeout\n\n## 🔧 Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Start development server\nnpm run dev\n\n# Build package\nnpm run build\n\n# Run tests\nnpm run test\n\n# Type checking\nnpm run typecheck\n\n# Lint code\nnpm run lint\n```\n\n## 📄 License\n\nMIT License - see LICENSE file for details.\n\n## 🤝 Contributing\n\nContributions are welcome! Please read our contributing guidelines and submit pull requests.\n\n## 🐛 Issues\n\nIf you encounter any issues, please file them on our [GitHub Issues](https://github.com/aemaddin/vue-progressive-iframe/issues) page.\n","readmeFilename":"README.md"}