{"id":13515228,"url":"https://github.com/orestbida/iframemanager","last_synced_at":"2025-10-26T18:08:22.890Z","repository":{"id":37785419,"uuid":"376595040","full_name":"orestbida/iframemanager","owner":"orestbida","description":"🍪 GDPR friendly iframe manager written in vanilla js","archived":false,"fork":false,"pushed_at":"2024-09-08T18:56:42.000Z","size":441,"stargazers_count":272,"open_issues_count":8,"forks_count":33,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-03-31T17:15:38.330Z","etag":null,"topics":["gdpr","gdpr-consent","iframe","iframe-manager","video","vimeo","youtube"],"latest_commit_sha":null,"homepage":"https://orestbida.com/demo-projects/iframemanager/demo1/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/orestbida.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"ko_fi":"orestbida"}},"created_at":"2021-06-13T16:52:56.000Z","updated_at":"2025-03-28T00:51:39.000Z","dependencies_parsed_at":"2024-06-19T05:16:15.149Z","dependency_job_id":"3b208cf8-5325-4410-866e-e56aef31c053","html_url":"https://github.com/orestbida/iframemanager","commit_stats":{"total_commits":125,"total_committers":4,"mean_commits":31.25,"dds":0.03200000000000003,"last_synced_commit":"ceecb1d0be478445216d8403c01431840319f112"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orestbida%2Fiframemanager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orestbida%2Fiframemanager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orestbida%2Fiframemanager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orestbida%2Fiframemanager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/orestbida","download_url":"https://codeload.github.com/orestbida/iframemanager/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247704569,"owners_count":20982298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gdpr","gdpr-consent","iframe","iframe-manager","video","vimeo","youtube"],"created_at":"2024-08-01T05:01:08.035Z","updated_at":"2025-10-26T18:08:22.797Z","avatar_url":"https://github.com/orestbida.png","language":"JavaScript","funding_links":["https://ko-fi.com/orestbida"],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cimg src=\"./demo/assets/iframemanager_logo.svg\" height=\"100px\" alt=\"IframeManager Logo\" /\u003e\n\u003c/h1\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[Demo](https://orestbida.com/demo-projects/iframemanager/demo1/)\u0026nbsp;\u0026nbsp;\u0026nbsp;|\u0026nbsp;\u0026nbsp;\u0026nbsp;[Features](#features)\u0026nbsp;\u0026nbsp;\u0026nbsp;|\u0026nbsp;\u0026nbsp;\u0026nbsp;[Installation](#installation)\u0026nbsp;\u0026nbsp;\u0026nbsp;\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)\n![Size](https://img.shields.io/github/size/orestbida/iframemanager/dist/iframemanager.js)\n[![Stable version](https://img.shields.io/github/v/release/orestbida/iframemanager)](https://github.com/orestbida/iframemanager/releases)\n\u003c/div\u003e\n\u003cdiv align=\"center\"\u003e\n\n**IframeMananger** is a lightweight javascript plugin which helps you **comply with `GDPR`** by completely removing iframes initially and setting a notice relative to that service. **Iframes are loaded only after consent**.\n\n\u003csub\u003e\n\u003ci\u003e\n\nThe plugin was mainly developed to aid [**CookieConsent**](https://github.com/orestbida/cookieconsent) with iframe management.\n\u003c/i\u003e\n\u003c/sub\u003e\n\n\u003c/div\u003e\n\n\u003cbr\u003e\n\n## Table of Contents\n\n- [**Key features**](#features)\n- [**Installation**](#installation)\n- [**Configuration options \u0026 API**](#configuration-options)\n- [**Configuration examples**](#configuration-examples)\n    - [youtube](#configuration-examples)\n    - [dailymotion](#configuration-examples)\n    - [vimeo](#configuration-examples)\n    - [twitch](#configuration-examples)\n    - [google maps](#configuration-examples)\n- [**Usage with CookieConsent**](#usage-with-cookieconsent-v120)\n- [**License**](#license)\n\n## Features\n- Lightweight\n- Complies with **GDPR**\n- **Multilanguage** support\n- Automatic/custom thumbnail [support *](#note)\n- Allows to integrate any service which uses iframes\n- Improves website **performance**:\n  - lazy-load thumbnails\n  - lazy-load iframes\n- Can be integrated with any consent solution\n\n## Installation\n1. #### Download the [latest release](https://github.com/orestbida/iframemanager/releases/latest) or use via CDN/NPM:\n\n    ```bash\n    https://cdn.jsdelivr.net/gh/orestbida/iframemanager@1.3.0/dist/iframemanager.js\n    https://cdn.jsdelivr.net/gh/orestbida/iframemanager@1.3.0/dist/iframemanager.css\n    ```\n    using [`npm`](https://www.npmjs.com/package/@orestbida/iframemanager):\n\n    ```bash\n    npm i @orestbida/iframemanager\n    ```\n\n2. #### Import script + stylesheet:\n\n    ```html\n    \u003chtml\u003e\n      \u003chead\u003e\n        ...\n        \u003clink rel=\"stylesheet\" href=\"iframemanager.css\"\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n        ...\n        \u003cscript defer src=\"iframemanager.js\"\u003e\u003c/script\u003e\n      \u003cbody\u003e\n    \u003c/html\u003e\n    ```\n\n3. #### Configure and run:\n    -   \u003cdetails\u003e\u003csummary\u003eAs external script\u003c/summary\u003e\n        \u003cp\u003e\n\n        - Create a .js file (e.g. `app.js`) and import it in your html markup:\n\n            ```html\n            \u003cbody\u003e\n                ...\n                \u003cscript defer src=\"iframemanager.js\"\u003e\u003c/script\u003e\n                \u003cscript defer src=\"app.js\"\u003e\u003c/script\u003e\n            \u003cbody\u003e\n            ```\n\n        - Configure iframemanager inside `app.js`:\n\n            ```javascript\n            (function(){\n\n                const im = iframemanager();\n\n                // Example with youtube embed\n                im.run({\n                    currLang: 'en',\n                    services : {\n                        youtube : {\n                            embedUrl: 'https://www.youtube-nocookie.com/embed/{data-id}',\n                            thumbnailUrl: 'https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg',\n                            iframe : {\n                                allow : 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;'\n                            },\n                            languages : {\n                                en : {\n                                    notice: 'This content is hosted by a third party. By showing the external content you accept the \u003ca rel=\"noreferrer noopener\" href=\"https://www.youtube.com/t/terms\" target=\"_blank\"\u003eterms and conditions\u003c/a\u003e of youtube.com.',\n                                    loadBtn: 'Load video',\n                                    loadAllBtn: \"Don't ask again\"\n                                }\n                            }\n                        }\n                    }\n                });\n            })();\n            ```\n        \u003c/p\u003e\n        \u003c/details\u003e\n    -   \u003cdetails\u003e\u003csummary\u003eAs inline script\u003c/summary\u003e\n        \u003cp\u003e\n\n        ```html\n        \u003cbody\u003e\n          ...\n          \u003cscript defer src=\"iframemanager.js\"\u003e\u003c/script\u003e\n\n          \u003c!-- Inline script --\u003e\n          \u003cscript\u003e\n            window.addEventListener('load', function(){\n\n                const im = iframemanager();\n\n                // Example with youtube embed\n                im.run({\n                    currLang: 'en',\n                    services : {\n                        youtube : {\n                            embedUrl: 'https://www.youtube-nocookie.com/embed/{data-id}',\n                            thumbnailUrl: 'https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg',\n                            iframe : {\n                                allow : 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;'\n                            },\n                            languages : {\n                                en : {\n                                    notice: 'This content is hosted by a third party. By showing the external content you accept the \u003ca rel=\"noreferrer noopener\" href=\"https://www.youtube.com/t/terms\" target=\"_blank\"\u003eterms and conditions\u003c/a\u003e of youtube.com.',\n                                    loadBtn: 'Load video',\n                                    loadAllBtn: \"Don't ask again\"\n                                }\n                            }\n                        }\n                    }\n                });\n            });\n          \u003c/script\u003e\n        \u003cbody\u003e\n        ```\n      \u003c/p\u003e\n    \u003c/details\u003e\n\n4. #### Create a div with `data-service` and `data-id` attributes:\n\n    ```html\n    \u003cdiv data-service=\"youtube\" data-id=\"\u003cvideo-id\u003e\"\u003e\u003c/div\u003e\n    ```\n\n## Configuration options\nAll available options for  the `\u003cdiv\u003e` element:\n```html\n\u003cdiv\n    data-service=\"\u003cservice-name\u003e\"\n    data-id=\"\u003cresource-id\u003e\"\n    data-params=\"\u003ciframe-query-parameters\u003e\"\n    data-thumbnail=\"\u003cpath-to-image\u003e\"\n    data-autoscale\n    data-ratio=\"\u003cx:y\u003e\"\u003e\n\u003c/div\u003e\n```\n\n- `data-service` :      [String, Required] name of the service (must also be defined in the config. object)\n- `data-id` :           [String, Required] unique id of the resource (example: video id)\n- `data-title` :        [String] notice title\n- `data-params` :       [String] iframe query parameters\n- `data-thumbnail` :    [String] path to custom thumbnail\n- `data-ratio` :        [String] custom aspect ratio ([Available values.](#available-data-ratio))[v1.1.0]\n- `data-autoscale` :    specify for **responsive iframe** (fill parent width + scale proportionally)\n- `data-widget` :       ignore the default aspect ratio; specify when implementing a custom widget with explicit width and height (twitter, facebook, instagram ...)[v1.2.0]\n\n### How to set attributes on the `iframe` element\nYou can set any attribute by using the following syntax:\n- `data-iframe-\u003cattribute\u003e`  [String] note: replace `\u003cattribute\u003e` with a valid attribute name. [v1.1.0]\n\nExample:\n```html\n\u003cdiv\n    data-service=\"youtube\"\n    data-id=\"5b35haQV7tU\"\n    data-autoscale\n    data-iframe-id=\"myYoutubeEmbed\"\n    data-iframe-loading=\"lazy\"\n    data-iframe-frameborder=\"0\"\u003e\n\u003c/div\u003e\n```\n\n\u003cbr\u003e\n\nAll available options for the config. object:\n```javascript\n{\n    currLang: 'en',     // current language of the notice (must also be defined in the \"languages\" object below)\n    autoLang: false,    // if enabled =\u003e use current client's browser language\n                        // instead of currLang [OPTIONAL]\n\n    // callback fired when state changes (a new service is accepted/rejected)\n    onChange: ({changedServices, eventSource}) =\u003e {\n        // changedServices: string[]\n        // eventSource.type: 'api' | 'click'\n        // eventSource.service: string\n        // eventSource.action: 'accept' | 'reject'\n    },\n\n    services : {\n        myservice : {\n\n            embedUrl: 'https://\u003cmyservice_embed_url\u003e',\n\n            // set valid url for automatic thumbnails   [OPTIONAL]\n            thumbnailUrl: 'https://\u003cmyservice_embed_thumbnail_url\u003e',\n\n            // global iframe settings (apply to all iframes relative to current service) [OPTIONAL]\n            iframe: {\n                allow: 'fullscreen',           // iframe's allow attribute\n                params: 'mute=1\u0026start=21',     // iframe's url query parameters\n\n                // function run for each iframe configured with current service\n                onload: (dataId, setThumbnail) =\u003e {\n                    console.log(`loaded iframe with data-id=${dataId}`);\n                }\n            },\n\n            // cookie is set if the current service is accepted\n            cookie: {\n                name: 'cc_youtube',            // cookie name\n                path: '/',                     // cookie path          [OPTIONAL]\n                samesite: 'lax',               // cookie samesite      [OPTIONAL]\n                domain: location.hostname      // cookie domain        [OPTIONAL]\n            },\n\n            languages: {\n                en: {\n                    notice: 'Html \u003cb\u003enotice\u003c/b\u003e message',\n                    loadBtn: 'Load video',          // Load only current iframe\n                    loadAllBtn: \"Don't ask again\"   // Load all iframes configured with this service + set cookie\n                }\n            }\n        },\n\n        anotherservice: {\n            // ...\n        }\n    }\n}\n```\n\nAny other property specified inside the `iframe` object, will be set directly to the `iframe` element as attribute.\n\nExample: add `frameborder` and `style` attributes:\n```javascript\n{\n    // ...\n\n    services: {\n        myservice: {\n            // ...\n\n            iframe: {\n                // ...\n\n                frameborder: '0',\n                style: 'border: 4px solid red;'\n            }\n        }\n    }\n}\n```\n\nNote: `thumbnailUrl` can be static string, dynamic string or a function:\n\n- `static string` : \"https://path_to_image/image.png\"\n- `dynamic string` : \"https://myservice_embed_url/{data-id}\"\n- `function` :\n    ```javascript\n    thumbnailUrl: (dataId, setThumbnail) =\u003e {\n        // fetch thumbnail url here based on dataId of the current element ...\n        let url = 'fetched_url';\n\n        // pass obtained url to the setThumbnail function\n        setThumbnail(url);\n    }\n    ```\n\n## Custom Widgets\nSome services (e.g. twitter) have their own markup and API to generate the iframe.\n\nNote: this is an example with twitter's widget. Each widget/service will have a slightly different implementation.\n\n1. Place the markup inside a special `data-placeholder` div. Remove any `script` tag that comes with the markup. Example:\n\n    ```html\n    \u003cdiv\n        data-service=\"twitter\"\n        data-widget\n        style=\"width: 300px; height: 501px\"\n    \u003e\n\n        \u003cdiv data-placeholder\u003e\n            \u003cblockquote class=\"twitter-tweet\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eSunsets don\u0026#39;t get much better than this one over \u003ca href=\"https://twitter.com/GrandTetonNPS?ref_src=twsrc%5Etfw\"\u003e@GrandTetonNPS\u003c/a\u003e. \u003ca href=\"https://twitter.com/hashtag/nature?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#nature\u003c/a\u003e \u003ca href=\"https://twitter.com/hashtag/sunset?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#sunset\u003c/a\u003e \u003ca href=\"http://t.co/YuKy2rcjyU\"\u003epic.twitter.com/YuKy2rcjyU\u003c/a\u003e\u003c/p\u003e\u0026mdash; US Department of the Interior (@Interior) \u003ca href=\"https://twitter.com/Interior/status/463440424141459456?ref_src=twsrc%5Etfw\"\u003eMay 5, 2014\u003c/a\u003e\u003c/blockquote\u003e\n        \u003c/div\u003e\n\n    \u003c/div\u003e\n    ```\n\n2. Create a new service and dynamically load and initialize the widget inside the `onAccept` callback:\n\n    ```javascript\n    im.run({\n        services: {\n            twitter: {\n                onAccept: async (div, setIframe) =\u003e {\n                    // Using cookieconsent v3\n                    await CookieConsent.loadScript('https://platform.twitter.com/widgets.js');\n\n                    // Make sure the \"window.twttr\" property exists\n                    await im.childExists({childProperty: 'twttr'}) \u0026\u0026 await twttr.widgets.load(div);\n\n                    // Make sure the \"iframe\" element exists\n                    await im.childExists({parent: div}) \u0026\u0026 setIframe(div.querySelector('iframe'));\n                },\n\n                onReject: (iframe) =\u003e {\n                    iframe \u0026\u0026 iframe.parentElement.remove();\n                }\n            }\n        }\n    })\n    ```\n\n\nIt is highly recommended to set a fixed `width` and `height` to the main `data-service` div, to avoid the (awful) content jump effect when the iframe is loaded.\n\n\n## Placeholder for non-js browsers\nYou can set a placeholder visible only if javascript is disabled via a special div:\n```html\n\u003cdiv data-placeholder data-visible\u003e\u003c/div\u003e\n```\n\nExample:\n```html\n\u003cdiv\n    data-service=\"youtube\"\n    data-id=\"5b35haQV7tU\"\n    data-autoscale\u003e\n\n    \u003cdiv data-placeholder data-visible\u003e\n        \u003cp\u003eI'm visible only if js is disabled\u003c/p\u003e\n    \u003c/div\u003e\n\n\u003c/div\u003e\n```\n\n## APIs\nThe plugin exposes the following methods:\n- `.run(\u003cconfig_object\u003e)`\n- `.acceptService(\u003cservice_name\u003e)`\n- `.rejectService(\u003cservice_name\u003e)`\n- `.getState()` [v1.2.0+]\n- `.getConfig()` [v1.2.0+]\n- `.reset(\u003chard_reset\u003e)` [v1.3.0+]\n\nExample usage:\n\n```javascript\n// accept specific service only\nim.acceptService('youtube');\n\n// accept all services (for example if user has given full consent to cookies)\nim.acceptService('all');\n\n// reject specific service\nim.rejectService('youtube');\n\n// reject all services (for example when user opts out of cookies)\nim.rejectService('all');\n\n// get entire config object\nconst config = im.getConfig();\n\n// get current state (enabled/disabled services)\nconst state = im.getState();\n\n// state.services: Map\u003cstring, boolean\u003e\n// state.acceptedServices: string[]\n\n// soft reset, removes internal event listeners\nim.reset();\n\n// hard reset, same as above, but also resets each div to its original state (for react frameworks)\nim.reset(true);\n```\n\nBoth `acceptService` and `rejectService` work the same way:\n1. set/erase cookie\n2. create/remove iframes\n\n## Configuration examples\n-   \u003cdetails\u003e\u003csummary\u003eYoutube\u003c/summary\u003e\n    \u003cp\u003e\n\n    ```javascript\n    im.run({\n        currLang: 'en',\n        services: {\n            youtube: {\n                embedUrl: 'https://www.youtube-nocookie.com/embed/{data-id}',\n\n                thumbnailUrl: 'https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg',\n\n                iframe: {\n                    allow: 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;',\n                },\n\n                languages: {\n                    en: {\n                        notice: 'This content is hosted by a third party. By showing the external content you accept the \u003ca rel=\"noreferrer noopener\" href=\"https://www.youtube.com/t/terms\" target=\"_blank\"\u003eterms and conditions\u003c/a\u003e of youtube.com.',\n                        loadBtn: 'Load video',\n                        loadAllBtn: \"Don't ask again\"\n                    }\n                }\n            }\n        }\n    });\n    ```\n    Example:\n    ```html\n    \u003c!-- https://www.youtube.com/watch?v=5b35haQV7tU --\u003e\n    \u003cdiv\n        data-service=\"youtube\"\n        data-id=\"5b35haQV7tU\"\n    \u003e\u003c/div\u003e\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n-   \u003cdetails\u003e\u003csummary\u003eDailymotion\u003c/summary\u003e\n    \u003cp\u003e\n\n    ```javascript\n    im.run({\n        currLang: 'en',\n        services: {\n            dailymotion: {\n                embedUrl: 'https://www.dailymotion.com/embed/video/{data-id}',\n\n                thumbnailUrl: async (dataId, setThumbnail) =\u003e {\n                    // Use dailymotion's API to fetch the thumbnail\n                    const url = `https://api.dailymotion.com/video/${dataId}?fields=thumbnail_large_url`;\n                    const response = await (await fetch(url)).json();\n                    const thumbnailUlr = response?.thumbnail_large_url;\n                    thumbnailUlr \u0026\u0026 setThumbnail(thumbnailUlr);\n                },\n\n                iframe: {\n                    allow: 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;',\n                },\n\n                languages: {\n                    en: {\n                        notice: 'This content is hosted by a third party. By showing the external content you accept the \u003ca rel=\"noreferrer noopener\" href=\"https://www.dailymotion.com/legal/privacy?localization=en\" target=\"_blank\"\u003eterms and conditions\u003c/a\u003e of dailymotion.com.',\n                        loadBtn: 'Load video',\n                        loadAllBtn: \"Don't ask again\"\n                    }\n                }\n            }\n        }\n    });\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n-   \u003cdetails\u003e\u003csummary\u003eVimeo\u003c/summary\u003e\n    \u003cp\u003e\n\n    ```javascript\n    im.run({\n        currLang: 'en',\n        services: {\n            vimeo: {\n                embedUrl: 'https://player.vimeo.com/video/{data-id}',\n\n                iframe: {\n                    allow : 'fullscreen; picture-in-picture, allowfullscreen;',\n                },\n\n                thumbnailUrl: async (dataId, setThumbnail) =\u003e {\n                    const url = `https://vimeo.com/api/v2/video/${dataId}.json`;\n                    const response = await (await fetch(url)).json();\n                    const thumbnailUrl = response[0]?.thumbnail_large;\n                    thumbnailUrl \u0026\u0026 setThumbnail(thumbnailUrl);\n                },\n\n                languages: {\n                    en: {\n                        notice: 'This content is hosted by a third party. By showing the external content you accept the \u003ca rel=\"noreferrer noopener\" href=\"https://vimeo.com/terms\" target=\"_blank\"\u003eterms and conditions\u003c/a\u003e of vimeo.com.',\n                        loadBtn: 'Load video',\n                        loadAllBtn: \"Don't ask again\"\n                    }\n                }\n            }\n        }\n    });\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n-   \u003cdetails\u003e\u003csummary\u003eTwitch\u003c/summary\u003e\n    \u003cp\u003e\n\n    ```javascript\n    im.run({\n        currLang: 'en',\n        services: {\n            twitch: {\n                embedUrl: `https://player.twitch.tv/?{data-id}\u0026parent=${location.hostname}`,\n\n                iframe: {\n                    allow: 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;',\n                },\n\n                languages: {\n                    en: {\n                        notice: 'This content is hosted by a third party. By showing the external content you accept the \u003ca rel=\"noreferrer noopener\" href=\"https://www.twitch.tv/p/en/legal/terms-of-service/\" target=\"_blank\"\u003eterms and conditions\u003c/a\u003e of twitch.com.',\n                        loadBtn: 'Load stream',\n                        loadAllBtn: \"Don't ask again\"\n                    }\n                }\n            }\n        }\n    });\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n-   \u003cdetails\u003e\u003csummary\u003eGoogle Maps\u003c/summary\u003e\n    \u003cp\u003e\n\n    -   \u003cdetails\u003e\u003csummary\u003eWith API key\u003c/summary\u003e\n        \u003cp\u003e\n\n        ```javascript\n        im.run({\n            currLang: 'en',\n            services: {\n                googlemaps: {\n                    embedUrl: 'https://www.google.com/maps/embed/v1/place?key=API_KEY\u0026q={data-id}',\n\n                    iframe: {\n                        allow: 'picture-in-picture; fullscreen;'\n                    },\n\n                    languages: {\n                        en: {\n                            notice: 'This content is hosted by a third party. By showing the external content you accept the \u003ca rel=\"noreferrer noopener\" href=\"https://cloud.google.com/maps-platform/terms\" target=\"_blank\"\u003eterms and conditions\u003c/a\u003e of Google Maps.',\n                            loadBtn: 'Load map',\n                            loadAllBtn: \"Don't ask again\"\n                        }\n                    }\n                }\n            }\n        });\n        ```\n\n        Example:\n        ```html\n        \u003cdiv\n            data-service=\"GoogleMaps\"\n            data-id=\"Space+Needle,Seattle+WA\"\n            data-autoscale\n        \u003e\u003c/div\u003e\n        ```\n        \u003c/p\u003e\n        \u003c/details\u003e\n\n    -   \u003cdetails\u003e\u003csummary\u003eWithout API key\u003c/summary\u003e\n        \u003cp\u003e\n\n        ```javascript\n        im.run({\n            currLang: 'en',\n            services : {\n                googlemaps : {\n                    embedUrl: 'https://www.google.com/maps/embed?pb={data-id}',\n\n                    iframe: {\n                        allow : 'picture-in-picture; fullscreen;'\n                    },\n\n                    languages : {\n                        en : {\n                            notice: 'This content is hosted by a third party. By showing the external content you accept the \u003ca rel=\"noreferrer noopener\" href=\"https://cloud.google.com/maps-platform/terms\" target=\"_blank\"\u003eterms and conditions\u003c/a\u003e of Google Maps.',\n                            loadBtn: 'Load map',\n                            loadAllBtn: \"Don't ask again\"\n                        }\n                    }\n                }\n            }\n        });\n        ```\n\n        Example usage:\n        ```html\n        \u003cdiv\n            data-service=\"googlemaps\"\n            data-id=\"!1m18!1m12!1m3!1d2659.4482749804133!2d11.644969316034478!3d48.19798087922823!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x479e7499e2d4c67f%3A0x32f7f02c5e77043a!2sM%C3%BCnchner+Str.+123%2C+85774+Unterf%C3%B6hring%2C+Germany!5e0!3m2!1sen!2sin!4v1565347252768!5m2!1sen!2sin\"\n            data-autoscale\n        \u003e\u003c/div\u003e\n        ```\n        \u003c/p\u003e\n        \u003c/details\u003e\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n## Usage with CookieConsent [v1.2.0+]\nYou can use the `onChange` callback to detect when an iframe is loaded by the `loadAllBtn` button click event and notify CookieConsent to also update its state.\n\nExample:\n```javascript\nim.run({\n    currLang: 'en',\n\n    onChange: ({changedServices, eventSource}) =\u003e {\n\n        if(eventSource.type === 'click') {\n            // Retrieve all accepted services:\n            // const allAcceptedServices = im.getState().acceptedServices;\n\n            /**\n             * Retrieve array of already accepted services\n             * and add the new service\n             */\n            const servicesToAccept = [\n                ...CookieConsent.getUserPreferences().acceptedServices['analytics'], //cookieconsent v3\n                ...changedServices\n            ];\n\n            CookieConsent.acceptService(servicesToAccept, 'analytics');\n        }\n    },\n\n    services: {\n        // ...\n    }\n});\n```\n\nNote: the above example assumes that all services belong to the `analytics` category.\n\n### Available `data-ratio`\n\nHorizontal aspect ratio:\n\n* `1:1`, `2:1`, `3:2`, `5:2`, `4:3`, `16:9`, `16:10`, `20:9`, `21:9`\n\nVertical aspect ratio:\n* `9:16`, `9:20`\n\n## License\nDistributed under the MIT License. See [LICENSE](https://github.com/orestbida/iframemanager/blob/master/LICENSE) for more information.\n\n\u003cbr\u003e\n\n#### Note\n\u003ci\u003eNot all services (example: twitch) allow automatic/easy thumbnail fetch.\u003c/i\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forestbida%2Fiframemanager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Forestbida%2Fiframemanager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forestbida%2Fiframemanager/lists"}