├── docs ├── .nojekyll ├── img │ ├── embedo.png │ └── favicon.ico ├── js │ └── main.js ├── index.html ├── usage.html └── css │ └── style.css ├── .npmrc ├── .github ├── CODEOWNERS └── FUNDING.yml ├── plugins ├── reddit │ ├── index.js │ ├── reddit.embedo.min.js │ └── reddit.embedo.js ├── flickr │ ├── index.js │ ├── flickr.embedo.min.js │ └── flickr.embedo.js ├── gmaps │ ├── index.js │ ├── gmaps.embedo.min.js │ └── gmaps.embedo.js └── index.js ├── index.js ├── .travis.yml ├── .editorconfig ├── bower.json ├── .gitignore ├── LICENSE.md ├── .jshintrc ├── package.json ├── README.md ├── test ├── index.dom.html ├── index.html ├── embedo.spec.js └── script.js ├── embedo.min.js └── embedo.js /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @shobhitsharma -------------------------------------------------------------------------------- /plugins/reddit/index.js: -------------------------------------------------------------------------------- 1 | exports.default = require('./reddit.embedo'); 2 | -------------------------------------------------------------------------------- /plugins/flickr/index.js: -------------------------------------------------------------------------------- 1 | exports.default = require('./flickr.embedo.js'); 2 | -------------------------------------------------------------------------------- /plugins/gmaps/index.js: -------------------------------------------------------------------------------- 1 | exports.default = require('./gmaps.embedo.js'); 2 | -------------------------------------------------------------------------------- /docs/img/embedo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shobhitsharma/embedo/HEAD/docs/img/embedo.png -------------------------------------------------------------------------------- /docs/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shobhitsharma/embedo/HEAD/docs/img/favicon.ico -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var Embedo = require('./embedo'); 2 | var Plugins = require('./plugins'); 3 | 4 | module.exports = Plugins; 5 | export default Embedo; 6 | -------------------------------------------------------------------------------- /plugins/index.js: -------------------------------------------------------------------------------- 1 | module.exports.EmbedoGMaps = require('./gmaps'); 2 | module.exports.EmbedoReddit = require('./reddit'); 3 | module.exports.EmbedoFlickr = require('./flickr'); 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | notifications: 4 | email: false 5 | 6 | node_js: 7 | - v8 8 | - v7 9 | - v6 10 | 11 | branches: 12 | only: 13 | - master 14 | 15 | addons: 16 | firefox: latest 17 | apt: 18 | sources: 19 | - google-chrome 20 | packages: 21 | - google-chrome-stable 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the configurations used 2 | # in this file, please see the EditorConfig documentation: 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | indent_size = 2 10 | indent_style = space 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.scss] 15 | indent_size = 2 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | 20 | [{package.json}] 21 | indent_size = 2 22 | indent_style = space 23 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "embedo", 3 | "description": "A simple, fast, lightweight and standalone content embed plugin for web", 4 | "main": "embedo.js", 5 | "authors": [ 6 | "Shobhit Sharma " 7 | ], 8 | "license": "MIT", 9 | "keywords": [ 10 | "embedo", 11 | "embed", 12 | "oemebed", 13 | "plugin" 14 | ], 15 | "homepage": "https://shobhitsharma.github.io/embedo", 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "test", 21 | "tests" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | *.pem 10 | 11 | pids 12 | logs 13 | results 14 | 15 | npm-debug.log 16 | package-lock.json 17 | node_modules 18 | node_modules* 19 | config.js 20 | config-local.js 21 | config-dev.js 22 | config-live.js 23 | config-production.js 24 | config-server.js 25 | worker-config.js 26 | 27 | .idea 28 | .vscode 29 | .project 30 | .settings 31 | dist 32 | dist-temp 33 | public/js/vendor 34 | public/js/config.js 35 | 36 | .DS_Store 37 | erl_crash.dump 38 | lib/emails/test.js 39 | lib/emails/test.html 40 | lib/emails/testData.json 41 | public/js/newsletter 42 | public/js/usermanagement 43 | public/js/offline 44 | nbproject 45 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [shobhitsharma] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Shobhit Sharma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /plugins/reddit/reddit.embedo.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file reddit.embedo.js 3 | * 4 | * Embedo plugin to embed Reddit. 5 | * 6 | * @author Shobhit Sharma 7 | * @license MIT 8 | */ 9 | "use strict";!function(e,t){if("function"==typeof define&&define.amd)define(t);else if("object"==typeof module&&module.exports)module.exports=t();else if(e){if(!e.Embedo||!window.Embedo)throw Error("Embedo script is either not imported or not available.");e.Embedo.Reddit=window.Embedo.Reddit=t()(e.Embedo||window.Embedo)}}(this,function(){return function(n){if(!n)throw Error("Embedo instance as argument is missing.");n.defaults.SOURCES.reddit={GLOBAL:"reddit",SDK:"//embed.redditmedia.com/widgets/platform.js",oEmbed:null,REGEX:/(https?:\/\/(ww.)?)?reddit(\.[a-z]+).*/i,PARAMS:{}},Object.defineProperty(n.prototype,"reddit",{value:function(e,t,i,d,o){var r=n.utils.dimensions(t,d.width,d.height),d=n.utils.merge({embed:!0,context:1,depth:2,uuid:null,showedits:!0,showmore:!1,width:r.width,height:r.height},d,n.defaults.RESTRICTED),d=i+"?"+n.utils.querystring(d),d=n.utils.generateElement("div",{style:"width:"+r.width+"px;"},'
'),d=n.utils.generateEmbed(e,"reddit",d);t.appendChild(d),o(null,{id:e,el:t,width:r.width,height:r.height})},writable:!1,enumerable:!0,configurable:!0})}}); -------------------------------------------------------------------------------- /plugins/flickr/flickr.embedo.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file flickr.embedo.js 3 | * 4 | * Embedo plugin to embed flickr. 5 | * 6 | * @author Shobhit Sharma 7 | * @license MIT 8 | */ 9 | "use strict";!function(e,t){if("function"==typeof define&&define.amd)define(t);else if("object"==typeof module&&module.exports)module.exports=t();else if(e){if(!e.Embedo||!window.Embedo)throw Error("Embedo script is either not imported or not available.");e.Embedo.Flickr=window.Embedo.Flickr=t()(e.Embedo||window.Embedo)}}(this,function(){return function(n){if(!n)throw Error("Embedo instance as argument is missing.");n.defaults.SOURCES.flickr={GLOBAL:null,SDK:"https://embedr.flickr.com/assets/embedr-loader.js",oEmbed:"//www.flickr.com/services/oembed",REGEX:/(https?:\/\/(ww.)?)?flickr(\.[a-z]+).*/i,PARAMS:{}},Object.defineProperty(n.prototype,"flickr",{value:function(r,o,e,d,l){var t=n.defaults.SOURCES.flickr.oEmbed,e=n.utils.merge({url:encodeURI(e),format:"json"},d,n.defaults.RESTRICTED);("width"in d||"maxwidth"in d)&&(e.maxwidth=d.maxwidth||d.width),("height"in d||"maxheight"in d)&&(e.maxheight=d.maxheight||d.height),t+="?"+n.utils.querystring(e),n.utils.fetch(t,{callback:"jsoncallback"},function(e,t){if(e)return n.log("error","flickr",e),l(e);var i=n.utils.generateEmbed(r,"flickr",t.html),e=i.querySelector("[data-flickr-embed]");e&&(e.setAttribute("data-header",d.header||!0),e.setAttribute("data-footer",d.footer||!0),e.setAttribute("data-context",d.context||!0)),o.appendChild(i),l(null,n.utils.extend({},t,{id:r,el:o,width:d.width,height:d.height}))})},writable:!1,enumerable:!0,configurable:!0})}}); -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "maxerr": 50, 3 | "bitwise": false, 4 | "camelcase": false, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "forin": true, 8 | "freeze": true, 9 | "immed": false, 10 | "latedef": false, 11 | "newcap": false, 12 | "noarg": true, 13 | "noempty": true, 14 | "nonbsp": true, 15 | "nonew": false, 16 | "plusplus": false, 17 | "quotmark": false, 18 | "undef": true, 19 | "unused": true, 20 | "strict": true, 21 | "maxparams": false, 22 | "maxdepth": false, 23 | "maxstatements": false, 24 | "maxcomplexity": false, 25 | "maxlen": false, 26 | "varstmt": false, 27 | "asi": false, 28 | "boss": false, 29 | "debug": false, 30 | "eqnull": false, 31 | "esversion": 5, 32 | "moz": false, 33 | "evil": false, 34 | "expr": false, 35 | "funcscope": false, 36 | "globalstrict": false, 37 | "iterator": false, 38 | "lastsemic": false, 39 | "laxbreak": false, 40 | "laxcomma": false, 41 | "loopfunc": false, 42 | "multistr": false, 43 | "noyield": false, 44 | "notypeof": false, 45 | "proto": false, 46 | "scripturl": false, 47 | "shadow": false, 48 | "sub": false, 49 | "supernew": false, 50 | "validthis": true, 51 | "browser": true, 52 | "browserify": false, 53 | "couch": false, 54 | "devel": true, 55 | "dojo": false, 56 | "jasmine": false, 57 | "jquery": false, 58 | "mocha": true, 59 | "mootools": false, 60 | "node": true, 61 | "nonstandard": false, 62 | "phantom": true, 63 | "prototypejs": false, 64 | "qunit": false, 65 | "rhino": false, 66 | "shelljs": false, 67 | "typed": false, 68 | "worker": false, 69 | "wsh": false, 70 | "yui": false, 71 | "globals": { 72 | "define": false, 73 | "Embedo": false 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /docs/js/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (window.addEventListener) { 4 | window.addEventListener('load', render, false); 5 | } else if (window.attachEvent) { 6 | window.attachEvent('onload', render); 7 | } else { 8 | window.onload = render; 9 | } 10 | 11 | function render() { 12 | var embedo = new Embedo({ 13 | facebook: { 14 | version: 'v2.10', 15 | appId: '269918776508696', 16 | xfbml: true 17 | }, 18 | twitter: true, 19 | instagram: true, 20 | pinterest: true, 21 | googlemaps: { 22 | key: 'AIzaSyDDmeVWuW86QT0JPikPas0BeWxbpVBlFy8' 23 | } 24 | }); 25 | 26 | if (document.getElementById('test-case')) { 27 | document.getElementById('test-url').addEventListener('input', build); 28 | document.getElementById('test-width').addEventListener('input', build); 29 | document.getElementById('test-height').addEventListener('input', build); 30 | } 31 | 32 | function build() { 33 | Embedo.utils.watcher('demo', function () { 34 | var url = document.getElementById('test-url').value; 35 | var width = document.getElementById('test-width').value; 36 | var height = document.getElementById('test-height').value; 37 | var dimensions = 'w=' + width + '&h=' + height; 38 | 39 | document.getElementById('test-container').innerHTML = ''; 40 | embedo.load(document.getElementById('test-container'), url, { 41 | width: width, 42 | height: height 43 | }).done(function (data) { 44 | if (window.ga) { 45 | window.ga('send', 'event', '[done] ' + url, data.url, dimensions); 46 | } 47 | }).fail(function (err) { 48 | document.getElementById('test-container').innerHTML = '' + err.message + ''; 49 | if (window.ga) { 50 | window.ga('send', 'event', '[error] ' + url, err.message, dimensions); 51 | } 52 | }); 53 | }, 500); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /plugins/gmaps/gmaps.embedo.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gmaps.embedo.js 3 | * 4 | * Embedo plugin to embed Google Maps. 5 | * 6 | * @author Shobhit Sharma 7 | * @license MIT 8 | */ 9 | "use strict";!function(e,o){if("function"==typeof define&&define.amd)define(o);else if("object"==typeof module&&module.exports)module.exports=o();else if(e){if(!e.Embedo||!window.Embedo)throw Error("Embedo script is either not imported or not available.");e.Embedo.GMaps=window.Embedo.GMaps=o()(e.Embedo||window.Embedo)}}(this,function(){return function(m){if(!m)throw Error("Embedo instance as argument is missing.");m.defaults.SOURCES.googlemaps={GLOBAL:"google",SDK:"https://maps.googleapis.com/maps/api/js",oEmbed:null,REGEX:/(http|https)?:\/\/(www\.|maps\.)?google(\.[a-z]+){1,2}\/maps\/.*/i,PARAMS:{}},Object.defineProperty(m.prototype,"googlemaps",{value:function(t,i,e,n,a){var d=m.utils.dimensions(i,n.width,n.height),r=function(e){e=e.match(/@(-?\d+\.\d+),(-?\d+\.\d+),(\d+\.?\d?)+z/);return e&&e.length&&e[1]&&e[2]?{lat:parseFloat(e[1],0),lng:parseFloat(e[2],0)}:null}(e);if(!r)return a(new Error("unable_to_find_cordinates"));var o,l,s,p,g=m.utils.generateEmbed(t,"googlemaps");i.appendChild(g),o=i,l=g,s={url:e,width:d.width,height:d.height,centerize:n.centerize},p=function(e){if(e)return m.log("error","googlemaps",e),a(e);var o=new window.google.maps.LatLng(r.lat,r.lng),e=new window.google.maps.Map(g,{zoom:n.zoom||12,center:o,mapTypeId:n.MapTypeId||window.google.maps.MapTypeId.ROADMAP}),o=new window.google.maps.Marker({map:e,draggable:!0,animation:window.google.maps.Animation.DROP,position:o});a(null,{id:t,el:i,width:d.width,height:d.height,marker:o})},m.utils.sdkReady("googlemaps",function(e){return e?p(e):(!1!==s.centerize&&m.utils.centerize(o,l,s),l.style.width=s.width?s.width+"px":m.utils.compute(o,"width"),l.style.height=s.height?s.height+"px":m.utils.compute(o,"height"),void p(null,{}))})},writable:!1,enumerable:!0,configurable:!0})}}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "embedo", 3 | "version": "1.14.0", 4 | "description": "Embeds third party content to DOM with perks", 5 | "main": "embedo.js", 6 | "scripts": { 7 | "prestart": "npm install", 8 | "start": "http-server ./ -p 8081 -o", 9 | "lint": "eslint embedo.js plugins/**/**.embedo.js", 10 | "test": "mocha test/*.spec.js", 11 | "build:embedo": "uglifyjs embedo.js --compress --comments --mangle -o embedo.min.js", 12 | "build:embedo:gmaps": "uglifyjs plugins/gmaps/gmaps.embedo.js --compress --comments --mangle -o plugins/gmaps/gmaps.embedo.min.js", 13 | "build:embedo:reddit": "uglifyjs plugins/reddit/reddit.embedo.js --compress --comments --mangle -o plugins/reddit/reddit.embedo.min.js", 14 | "build:embedo:flickr": "uglifyjs plugins/flickr/flickr.embedo.js --compress --comments --mangle -o plugins/flickr/flickr.embedo.min.js", 15 | "build": "npm run build:embedo && npm run build:embedo:gmaps && npm run build:embedo:reddit && npm run build:embedo:flickr" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/shobhitsharma/embedo.git" 20 | }, 21 | "keywords": [ 22 | "embed", 23 | "embedo", 24 | "social-media-plugin", 25 | "content-embed", 26 | "iframe", 27 | "oembed" 28 | ], 29 | "author": "Shobhit Sharma ", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/shobhitsharma/embedo/issues" 33 | }, 34 | "homepage": "https://github.com/shobhitsharma/embedo", 35 | "devDependencies": { 36 | "chai": "^4.2.0", 37 | "eslint": "^7.12.1", 38 | "http-server": "^0.12.3", 39 | "jsdom": "^16.4.0", 40 | "mocha": "^8.2.0", 41 | "prettier": "^2.1.2", 42 | "uglify-js": "^3.11.4" 43 | }, 44 | "prettier": { 45 | "tabWidth": 2, 46 | "semi": true, 47 | "singleQuote": true, 48 | "trailingComma": "none", 49 | "printWidth": 110 50 | }, 51 | "eslintConfig": { 52 | "parserOptions": { 53 | "ecmaVersion": 5 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /plugins/reddit/reddit.embedo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file reddit.embedo.js 3 | * 4 | * Embedo plugin to embed Reddit. 5 | * 6 | * @author Shobhit Sharma 7 | * @license MIT 8 | */ 9 | 10 | 'use strict'; 11 | 12 | (function (root, factory) { 13 | if (typeof define === 'function' && define.amd) { 14 | define(factory); 15 | } else if (typeof module === 'object' && module.exports) { 16 | module.exports = factory(); 17 | } else if (root) { 18 | if (!root.Embedo || !window.Embedo) { 19 | throw Error('Embedo script is either not imported or not available.'); 20 | } 21 | root.Embedo.Reddit = window.Embedo.Reddit = factory()(root.Embedo || window.Embedo); 22 | } 23 | })(this, function () { 24 | /** 25 | * Embedo Reddit plugin 26 | * @class EmbedoReddit 27 | * 28 | * @param {object} Embedo Class Instance 29 | */ 30 | function EmbedoReddit(Embedo) { 31 | if (!Embedo) { 32 | throw Error('Embedo instance as argument is missing.'); 33 | } 34 | 35 | // Add `reddit` as source 36 | Embedo.defaults.SOURCES.reddit = { 37 | GLOBAL: 'reddit', 38 | SDK: '//embed.redditmedia.com/widgets/platform.js', 39 | oEmbed: null, 40 | REGEX: /(https?:\/\/(ww.)?)?reddit(\.[a-z]+).*/i, 41 | PARAMS: {} 42 | }; 43 | 44 | /** 45 | * @method reddit 46 | * Reddit Embed 47 | * 48 | * @param {number} id 49 | * @param {HTMLElement} element 50 | * @param {string} url 51 | * @param {object} options Optional parameters. 52 | * @return callback 53 | */ 54 | Object.defineProperty(Embedo.prototype, 'reddit', { 55 | value: function (id, element, url, options, callback) { 56 | var size = Embedo.utils.dimensions(element, options.width, options.height); 57 | var embed_options = Embedo.utils.merge({ 58 | embed: true, 59 | context: 1, 60 | depth: 2, 61 | uuid: null, 62 | showedits: true, 63 | showmore: false, 64 | width: size.width, 65 | height: size.height 66 | }, 67 | options, 68 | Embedo.defaults.RESTRICTED 69 | ); 70 | var embed_url = url + '?' + Embedo.utils.querystring(embed_options); 71 | var embed_el = Embedo.utils.generateElement('div', { 72 | style: 'width:' + size.width + 'px;' 73 | }, '
'); 74 | var container = Embedo.utils.generateEmbed(id, 'reddit', embed_el); 75 | 76 | element.appendChild(container); 77 | 78 | callback(null, { 79 | id: id, 80 | el: element, 81 | width: size.width, 82 | height: size.height 83 | }); 84 | }, 85 | writable: false, 86 | enumerable: true, 87 | configurable: true 88 | }); 89 | } 90 | 91 | return EmbedoReddit; 92 | }); 93 | -------------------------------------------------------------------------------- /plugins/flickr/flickr.embedo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file flickr.embedo.js 3 | * 4 | * Embedo plugin to embed flickr. 5 | * 6 | * @author Shobhit Sharma 7 | * @license MIT 8 | */ 9 | 10 | 'use strict'; 11 | 12 | (function (root, factory) { 13 | if (typeof define === 'function' && define.amd) { 14 | define(factory); 15 | } else if (typeof module === 'object' && module.exports) { 16 | module.exports = factory(); 17 | } else if (root) { 18 | if (!root.Embedo || !window.Embedo) { 19 | throw Error('Embedo script is either not imported or not available.'); 20 | } 21 | root.Embedo.Flickr = window.Embedo.Flickr = factory()(root.Embedo || window.Embedo); 22 | } 23 | })(this, function () { 24 | /** 25 | * Embedo Flickr plugin 26 | * @class EmbedoFlickr 27 | * 28 | * @param {object} Embedo Class Instance 29 | */ 30 | function EmbedoFlickr(Embedo) { 31 | if (!Embedo) { 32 | throw Error('Embedo instance as argument is missing.'); 33 | } 34 | 35 | // Add `flickr` as source 36 | Embedo.defaults.SOURCES.flickr = { 37 | GLOBAL: null, 38 | SDK: 'https://embedr.flickr.com/assets/embedr-loader.js', 39 | oEmbed: '//www.flickr.com/services/oembed', 40 | REGEX: /(https?:\/\/(ww.)?)?flickr(\.[a-z]+).*/i, 41 | PARAMS: {} 42 | }; 43 | 44 | /** 45 | * @method flickr 46 | * Flickr Embed 47 | * 48 | * @param {number} id 49 | * @param {HTMLElement} element 50 | * @param {string} url 51 | * @param {object} options Optional parameters. 52 | * @return callback 53 | */ 54 | Object.defineProperty(Embedo.prototype, 'flickr', { 55 | value: function (id, element, url, options, callback) { 56 | var embed_uri = Embedo.defaults.SOURCES.flickr.oEmbed; 57 | var query = Embedo.utils.merge({ 58 | url: encodeURI(url), 59 | format: 'json' 60 | }, 61 | options, 62 | Embedo.defaults.RESTRICTED 63 | ); 64 | 65 | if ('width' in options || 'maxwidth' in options) { 66 | query.maxwidth = options.maxwidth || options.width; 67 | } 68 | 69 | if ('height' in options || 'maxheight' in options) { 70 | query.maxheight = options.maxheight || options.height; 71 | } 72 | 73 | embed_uri += '?' + Embedo.utils.querystring(query); 74 | 75 | Embedo.utils.fetch(embed_uri, { 76 | callback: 'jsoncallback' 77 | }, function (error, data) { 78 | if (error) { 79 | Embedo.log('error', 'flickr', error); 80 | return callback(error); 81 | } 82 | var container = Embedo.utils.generateEmbed(id, 'flickr', data.html); 83 | var embed_el = container.querySelector('[data-flickr-embed]'); 84 | if (embed_el) { 85 | embed_el.setAttribute('data-header', options.header || true); 86 | embed_el.setAttribute('data-footer', options.footer || true); 87 | embed_el.setAttribute('data-context', options.context || true); 88 | } 89 | element.appendChild(container); 90 | 91 | callback(null, Embedo.utils.extend({}, data, { 92 | id: id, 93 | el: element, 94 | width: options.width, 95 | height: options.height 96 | })); 97 | }); 98 | }, 99 | writable: false, 100 | enumerable: true, 101 | configurable: true 102 | }); 103 | } 104 | 105 | return EmbedoFlickr; 106 | }); 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # embedo [![npm](https://img.shields.io/npm/v/embedo.svg)](https://npmjs.org/package/embedo) 2 | 3 | 4 | 5 | > **Embedo** adds a layer on top of third party embed APIs while ensuring best practices and native guidelines for each component. It takes cares of resizing the container, emitting necessary events and with support for native and external options to be pass along. 6 | 7 | ### Docs: [Options](https://github.com/shobhitsharma/embedo/wiki/Options) / [API](https://github.com/shobhitsharma/embedo/wiki/API) / [Events](https://github.com/shobhitsharma/embedo/wiki/Events) / [Usage](https://github.com/shobhitsharma/embedo/wiki/Usage) / [Changelog](https://github.com/shobhitsharma/embedo/releases) 8 | 9 | ### What's currently supported? 10 | 11 | - Facebook URLs containing page, post, photos, videos or comments 12 | - Twitter URLs containing user timeline and tweets 13 | - YouTube and Vimeo videos URLs 14 | - Instagram URLs containing posts and videos 15 | - Pinterest URLs containing board, profile and pins 16 | - Google Maps URLs containing cordinates to a location 17 | - Embeds other urls like Github Gists, Soundcloud, Spotify or PDF, MP4, Webm, ... as `iframe` 18 | - Embeds any URL that fulfils HTTP access control (CORS) policy 19 | - Extended plugin support for additonal oembed services 20 | - Supports IE10+ and all modern browsers 21 | 22 | --- 23 | 24 | ## Installation 25 | 26 | #### NPM / Yarn 27 | 28 | ```sh 29 | $ npm install embedo --save 30 | $ yarn add embedo 31 | ``` 32 | 33 | #### Bower 34 | 35 | ```sh 36 | $ bower install embedo 37 | ``` 38 | 39 | #### CDN 40 | 41 | Alternatively, import using CDN while updating `version` as per requirements from any script below: 42 | 43 | ```html 44 | 45 | 46 | 47 | ``` 48 | 49 | ## Setup 50 | 51 | #### CommonJS / AMD 52 | 53 | ```js 54 | import Embedo from '/path/to/vendor'; 55 | 56 | // Initialize Embedo class object 57 | const embedo = new Embedo({ 58 | facebook: { 59 | appId: 'my_app_id', // Enable facebook SDK 60 | version: 'v8.0', 61 | access_token: 'app_id_with_client_token_here' 62 | }, 63 | twitter: true, // Enable twitter SDK 64 | instagram: { access_token: 'app_id_with_client_token_here' } // Enable instagram SDK 65 | pinterest: true // Enable pinterest SDK, 66 | googlemaps: { 67 | key: 'my_api_key' // Enables google maps API 68 | } 69 | }); 70 | 71 | // Then call .load() method from anywhere 72 | embedo.load(, , ); 73 | 74 | // OR Chaining methods and callback 75 | embedo 76 | .load(HTMLElement, URL, options) 77 | .done(Function) 78 | .fail(Function) 79 | 80 | // OR storing in a variable 81 | let my_embedo = embedo.load(HTMLElement, URL) 82 | my_embedo.done(Function); 83 | my_embedo.fail(Function); 84 | ``` 85 | 86 | Also, an example can be [found here](https://codepen.io/shobhitsharma/pen/yojJZp). 87 | 88 | #### HTML 89 | 90 | ```html 91 | ... 92 | 93 | 94 |
95 |
96 | 97 | 98 | ... 99 | 100 | 117 | ``` 118 | 119 | Also, an example can be [found here](https://github.com/shobhitsharma/embedo/blob/master/test/index.dom.html). 120 | -------------------------------------------------------------------------------- /test/index.dom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Embedo - DOM Suite 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | CommonJS + AMD 14 |
15 |
16 |
17 |

Embedo DOM Demo

18 |
19 |
20 |
21 |

Flickr Embed

22 |
23 |
24 |
25 |
26 |

Reddit Embed

27 |
29 |
30 |
31 |
32 |
33 |

Twitter Embed

34 |
35 |
36 |
37 |
38 |
39 |

YouTube Embed

40 |
41 |
42 |
43 |

Vimeo Embed

44 |
45 |
46 |
47 |

Facebook Embed

48 |
49 |
50 |
51 |
52 |
53 |

Pinterest Embed

54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 | 69 | 70 | 71 | 72 | 73 | 74 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Embedo - Test Suite 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 | DOMify 15 |
16 |
17 |

Embedo Test Demo

18 |
19 |
20 | 21 | 22 | 23 |
24 |
25 |
26 |
27 |
28 |

Inatagram Embed

29 |
30 |
31 |
32 |

Twitter Embed

33 |
34 |
35 |
36 |
37 |
38 |

YouTube Embed

39 |
40 |
41 |
42 |

Vimeo Embed

43 |
44 |
45 |
46 |

Facebook Embed

47 |
48 |
49 |
50 |
51 |
52 |

Facebook Page Embed

53 |
54 |
55 |
56 |

Facebook Comment(s)

57 |
58 |
59 |
60 |
61 |

Pinterest Embed

62 |
63 |
64 |
65 |
66 |
67 |

Github Gist Embed

68 |
69 |
70 |
71 |

Codepen Embed

72 |
73 |
74 |
75 |

File URL Embed

76 |
77 |
78 |
79 |

Multiple URLs Embed

80 |
81 |
82 |
83 |

Multiple Mixed URLs Embed

84 |
85 |
86 |
87 |

External Web URL Embed

88 |
89 |
90 |
91 |

Video Embed

92 |
93 |
94 |
95 |

Google Maps Embed

96 |
97 |
98 |
99 |

JSFiddle Embed

100 |
101 |
102 |
103 |

SoundCloud Embed

104 |
105 |
106 |
107 |
108 |
109 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /plugins/gmaps/gmaps.embedo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gmaps.embedo.js 3 | * 4 | * Embedo plugin to embed Google Maps. 5 | * 6 | * @author Shobhit Sharma 7 | * @license MIT 8 | */ 9 | 10 | 'use strict'; 11 | 12 | (function (root, factory) { 13 | if (typeof define === 'function' && define.amd) { 14 | define(factory); 15 | } else if (typeof module === 'object' && module.exports) { 16 | module.exports = factory(); 17 | } else if (root) { 18 | if (!root.Embedo || !window.Embedo) { 19 | throw Error('Embedo script is either not imported or not available.'); 20 | } 21 | root.Embedo.GMaps = window.Embedo.GMaps = factory()(root.Embedo || window.Embedo); 22 | } 23 | })(this, function () { 24 | /** 25 | * Embedo Google Maps plugin 26 | * @class EmbedoGMaps 27 | * 28 | * @param {object} Embedo Class Instance 29 | */ 30 | function EmbedoGMaps(Embedo) { 31 | if (!Embedo) { 32 | throw Error('Embedo instance as argument is missing.'); 33 | } 34 | 35 | // Add `googlemaps` as source 36 | Embedo.defaults.SOURCES.googlemaps = { 37 | GLOBAL: 'google', 38 | SDK: 'https://maps.googleapis.com/maps/api/js', 39 | oEmbed: null, 40 | REGEX: /(http|https)?:\/\/(www\.|maps\.)?google(\.[a-z]+){1,2}\/maps\/.*/i, 41 | PARAMS: {} 42 | }; 43 | 44 | /** 45 | * @method googlemaps 46 | * Google Maps Embed 47 | * 48 | * @param {number} id 49 | * @param {HTMLElement} element 50 | * @param {string} url 51 | * @param {object} options Optional parameters. 52 | * @return callback 53 | */ 54 | Object.defineProperty(Embedo.prototype, 'googlemaps', { 55 | value: function (id, element, url, options, callback) { 56 | var size = Embedo.utils.dimensions(element, options.width, options.height); 57 | var cordinates = getCordinates(url); 58 | if (!cordinates) { 59 | return callback(new Error('unable_to_find_cordinates')); 60 | } 61 | 62 | var container = Embedo.utils.generateEmbed(id, 'googlemaps'); 63 | element.appendChild(container); 64 | 65 | gmapsify( 66 | element, 67 | container, { 68 | url: url, 69 | width: size.width, 70 | height: size.height, 71 | centerize: options.centerize 72 | }, 73 | function (err) { 74 | if (err) { 75 | Embedo.log('error', 'googlemaps', err); 76 | return callback(err); 77 | } 78 | 79 | var location = new window.google.maps.LatLng(cordinates.lat, cordinates.lng); 80 | var map = new window.google.maps.Map(container, { 81 | zoom: options.zoom || 12, 82 | center: location, 83 | mapTypeId: options.MapTypeId || window.google.maps.MapTypeId.ROADMAP 84 | }); 85 | var marker = new window.google.maps.Marker({ 86 | map: map, 87 | draggable: true, 88 | animation: window.google.maps.Animation.DROP, 89 | position: location 90 | }); 91 | 92 | callback(null, { 93 | id: id, 94 | el: element, 95 | width: size.width, 96 | height: size.height, 97 | marker: marker 98 | }); 99 | } 100 | ); 101 | }, 102 | writable: false, 103 | enumerable: true, 104 | configurable: true 105 | }); 106 | 107 | /** 108 | * @func getCordinates 109 | * Get Cordinates from URL 110 | * 111 | * @param {string} url 112 | */ 113 | function getCordinates(url) { 114 | var regex = /@(-?\d+\.\d+),(-?\d+\.\d+),(\d+\.?\d?)+z/; 115 | var match = url.match(regex); 116 | return match && match.length && match[1] && match[2] ? { 117 | lat: parseFloat(match[1], 0), 118 | lng: parseFloat(match[2], 0) 119 | } : 120 | null; 121 | } 122 | 123 | /** 124 | * @function gmapsify 125 | * Parses Google Maps SDK 126 | * 127 | * @param {HTMLElement} parentNode 128 | * @param {HTMLElement} childNode 129 | * @param {object} options 130 | */ 131 | function gmapsify(parentNode, childNode, options, callback) { 132 | Embedo.utils.sdkReady('googlemaps', function (err) { 133 | if (err) { 134 | return callback(err); 135 | } 136 | if (options.centerize !== false) { 137 | Embedo.utils.centerize(parentNode, childNode, options); 138 | } 139 | childNode.style.width = options.width ? 140 | options.width + 'px' : 141 | Embedo.utils.compute(parentNode, 'width'); 142 | childNode.style.height = options.height ? 143 | options.height + 'px' : 144 | Embedo.utils.compute(parentNode, 'height'); 145 | callback(null, {}); 146 | }); 147 | } 148 | } 149 | 150 | return EmbedoGMaps; 151 | }); 152 | -------------------------------------------------------------------------------- /test/embedo.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var fs = require("fs"); 4 | var jsdom = require("jsdom"); 5 | var expect = require("chai").expect; 6 | var Embedo = require("../embedo"); 7 | var EmbedoGMaps = require("../plugins/gmaps"); 8 | var EmbedoReddit = require("../plugins/reddit"); 9 | 10 | // Cache FileBuffer 11 | var html = fs.readFileSync(require.resolve("./index.html"), "utf8"); 12 | var embedoScript = fs.readFileSync(require.resolve("../embedo.js"), "utf8"); 13 | var embedo = null; 14 | 15 | // Build virtual DOM Tree 16 | var dom = new jsdom.JSDOM(html, { 17 | runScripts: "dangerously", 18 | strictSSL: false, 19 | userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36" 20 | }); 21 | 22 | global.window = dom.window; 23 | global.document = dom.window.document; 24 | global.navigator = dom.window.navigator; 25 | 26 | describe("Embedo", function () { 27 | this.timeout(5000); 28 | 29 | before(function (done) { 30 | // Append library to DOM 31 | var script = document.createElement("script"); 32 | script.type = "text/javascript"; 33 | script.innerHTML = embedoScript; 34 | var head = document.getElementsByTagName("head")[0]; 35 | head.appendChild(script); 36 | 37 | // Initialize constructor 38 | embedo = new Embedo({ 39 | facebook: { 40 | version: "v3.1", 41 | appId: "269918776508696", 42 | xfbml: true 43 | }, 44 | twitter: true, 45 | instagram: true, 46 | pinterest: true, 47 | googlemaps: { 48 | key: "AIzaSyDDmeVWuW86QT0JPikPas0BeWxbpVBlFy8" 49 | } 50 | }); 51 | 52 | done(); 53 | }); 54 | 55 | after(function () { 56 | delete global.window; 57 | delete global.document; 58 | }); 59 | 60 | it("constructor", function (done) { 61 | expect(Embedo).to.be.a("function"); 62 | expect(EmbedoGMaps).to.be.a("object"); 63 | expect(EmbedoReddit).to.be.a("object"); 64 | expect(embedo).to.be.a("object"); 65 | done(); 66 | }); 67 | 68 | it("should expose dispatchers", function (done) { 69 | expect(embedo.on).to.be.a("function"); 70 | expect(embedo.off).to.be.a("function"); 71 | expect(embedo.emit).to.be.a("function"); 72 | expect(embedo.once).to.be.a("function"); 73 | done(); 74 | }); 75 | 76 | it("should have .load() method", function (done) { 77 | expect(embedo.load).to.be.a("function"); 78 | done(); 79 | }); 80 | 81 | it("should have .render() method", function (done) { 82 | expect(embedo.render).to.be.a("function"); 83 | done(); 84 | }); 85 | 86 | it("should have .refresh() method", function (done) { 87 | expect(embedo.refresh).to.be.a("function"); 88 | done(); 89 | }); 90 | 91 | it("should have .destroy() method", function (done) { 92 | expect(embedo.destroy).to.be.a("function"); 93 | done(); 94 | }); 95 | 96 | it("should have .domify() method", function (done) { 97 | expect(embedo.domify).to.be.a("function"); 98 | done(); 99 | }); 100 | 101 | it("should embed a PDF file", function (done) { 102 | embedo 103 | .load( 104 | document.getElementById("embedo-file"), 105 | "http://www.a3ts.org/wp-content/uploads/2014/07/PREVAC.pdf" 106 | ) 107 | .done(function (data) { 108 | expect(data).to.be.a("object"); 109 | done(); 110 | }) 111 | .fail(function (err) { 112 | done(err); 113 | }); 114 | }); 115 | 116 | it("should embed a HTML Website", function (done) { 117 | embedo 118 | .load( 119 | document.getElementById("embedo-website"), 120 | "https://static01.nyt.com/video/players/offsite/index.html?videoId=100000005363413" 121 | ) 122 | .done(function (data) { 123 | console.log(">>HTML>", data); 124 | expect(data).to.be.a("object"); 125 | done(); 126 | }) 127 | .fail(function (err) { 128 | done(err); 129 | }); 130 | }); 131 | 132 | it("should embed a webm video", function (done) { 133 | embedo 134 | .load( 135 | document.getElementById("embedo-video"), 136 | "https://archive.org/download/WebmVp8Vorbis/webmvp8.webm", { 137 | controls: "contorls" 138 | } 139 | ) 140 | .done(function (data) { 141 | expect(data).to.be.a("object"); 142 | done(); 143 | }) 144 | .fail(function (err) { 145 | done(err); 146 | }); 147 | }); 148 | 149 | it("should embed a facebook post", function (done) { 150 | embedo 151 | .load( 152 | document.getElementById("embedo-facebook"), 153 | "https://www.facebook.com/LaLiga/posts/1131604813542743", { 154 | width: 600, 155 | height: 400 156 | } 157 | ) 158 | .done(function (data) { 159 | expect(data).to.be.a("object"); 160 | done(); 161 | }) 162 | .fail(function (err) { 163 | done(err); 164 | }); 165 | }); 166 | 167 | it("should embed a pinterest pin", function (done) { 168 | embedo 169 | .load( 170 | document.getElementById("embedo-pinterest"), 171 | "https://www.pinterest.com/pin/99360735500167749" 172 | ) 173 | .done(function (data) { 174 | expect(data).to.be.a("object"); 175 | done(); 176 | }) 177 | .fail(function (err) { 178 | done(err); 179 | }); 180 | }); 181 | 182 | it("should embed google maps URL", function (done) { 183 | embedo 184 | .load( 185 | document.getElementById("embedo-googlemaps"), 186 | "https://www.google.de/maps/place/Berlin/@52.5076682,13.286064,11z/data=!3m1!4b1!4m5!3m4!1s0x47a84e373f035901:0x42120465b5e3b70!8m2!3d52.5200066!4d13.404954", { 187 | width: 640, 188 | height: 480, 189 | zoom: 10 190 | } 191 | ) 192 | .done(function (data) { 193 | expect(data).to.be.a("object"); 194 | done(); 195 | }) 196 | .fail(function (err) { 197 | done(err); 198 | }); 199 | }); 200 | 201 | it("should embed a instagram post", function (done) { 202 | embedo 203 | .load( 204 | document.getElementById("embedo-instagram"), 205 | "https://www.instagram.com/p/BJA9BB-B46A", { 206 | hidecaption: false 207 | } 208 | ) 209 | .done(function (data) { 210 | expect(data).to.be.a("object"); 211 | done(); 212 | }) 213 | .fail(function (err) { 214 | done(err); 215 | }); 216 | }); 217 | 218 | it("should embed multiple instagram posts", function (done) { 219 | embedo 220 | .load( 221 | document.getElementById("embedo-multiple"), 222 | [ 223 | "https://www.instagram.com/p/BX3fMnRjHpZ", 224 | "https://www.instagram.com/p/BX3ejdJHmkD", 225 | "https://www.instagram.com/p/BX3VEDqFvmg" 226 | ], { 227 | hidecaption: false 228 | } 229 | ) 230 | .done(function (data) { 231 | expect(data).to.be.a("object"); 232 | done(); 233 | }) 234 | .fail(function (err) { 235 | done(err); 236 | }); 237 | }); 238 | 239 | it("should embed a tweet", function (done) { 240 | embedo 241 | .load( 242 | document.getElementById("embedo-twitter"), 243 | "https://twitter.com/Sh0bhit/status/797584590214926340" 244 | ) 245 | .done(function (data) { 246 | expect(data).to.be.a("object"); 247 | done(); 248 | }) 249 | .fail(function (err) { 250 | done(err); 251 | }); 252 | }); 253 | 254 | it("should embed a youtube video", function (done) { 255 | embedo 256 | .load( 257 | document.getElementById("embedo-youtube"), 258 | "https://www.youtube.com/watch?v=JGwWNGJdvx8", { 259 | width: 640, 260 | height: 480 261 | } 262 | ) 263 | .done(function (data) { 264 | expect(data).to.be.a("object"); 265 | done(); 266 | }) 267 | .fail(function (err) { 268 | done(err); 269 | }); 270 | }); 271 | 272 | it("should embed a youtube video via embed-url", function (done) { 273 | embedo 274 | .load( 275 | document.getElementById("embedo-youtube-embed"), 276 | "https://www.youtube.com/embed/vn2qXpcon-s", { 277 | width: 640, 278 | height: 480 279 | } 280 | ) 281 | .done(function (data) { 282 | expect(data).to.be.a("object"); 283 | done(); 284 | }) 285 | .fail(function (err) { 286 | done(err); 287 | }); 288 | }); 289 | 290 | it("should embed a vimeo video", function (done) { 291 | embedo 292 | .load( 293 | document.getElementById("embedo-vimeo"), 294 | "https://vimeo.com/212603149" 295 | ) 296 | .done(function (data) { 297 | expect(data).to.be.a("object"); 298 | done(); 299 | }) 300 | .fail(function (err) { 301 | done(err); 302 | }); 303 | }); 304 | 305 | it("should embed a github gist", function (done) { 306 | embedo 307 | .load( 308 | document.getElementById("embedo-gist"), 309 | "https://gist.github.com/brandonb927/4149074.js" 310 | ) 311 | .done(function (data) { 312 | expect(data).to.be.a("object"); 313 | done(); 314 | }) 315 | .fail(function (err) { 316 | done(err); 317 | }); 318 | }); 319 | 320 | it("should embed a soundcloud track", function (done) { 321 | embedo 322 | .load( 323 | document.getElementById("embedo-soundcloud"), 324 | "https://soundcloud.com/uiceheidd/lucid-dreams-forget-me" 325 | ) 326 | .done(function (data) { 327 | expect(data).to.be.a("object"); 328 | done(); 329 | }) 330 | .fail(function (err) { 331 | done(err); 332 | }); 333 | }); 334 | 335 | it("should have requests stored after calling .load()", function (done) { 336 | expect(embedo.requests).to.be.an("array"); 337 | done(); 338 | }); 339 | }); 340 | -------------------------------------------------------------------------------- /test/script.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (window.addEventListener) { 4 | window.addEventListener('load', run, false); 5 | } else if (window.attachEvent) { 6 | window.attachEvent('onload', run); 7 | } else { 8 | window.onload = run; 9 | } 10 | 11 | function run() { 12 | if (!window.Embedo) { 13 | throw new Error('Embedo is unavailable.'); 14 | } 15 | 16 | Embedo.debug = true; // Enable debug mode for logs 17 | 18 | var embedo = new Embedo({ 19 | facebook: { 20 | version: 'v8.0', 21 | appId: '269918776508696', 22 | access_token: '{app-id}|{client-token}' 23 | }, 24 | twitter: true, 25 | instagram: { 26 | access_token: '{app-id}|{client-token}' 27 | }, 28 | pinterest: true, 29 | reddit: true, 30 | flickr: true, 31 | googlemaps: { 32 | key: 'ENTER_TEST_KEY_HERE' 33 | } 34 | }); 35 | 36 | /** 37 | * URL Form Tests 38 | */ 39 | document.getElementById('test-url').addEventListener('input', build); 40 | document.getElementById('test-width').addEventListener('input', build); 41 | document.getElementById('test-height').addEventListener('input', build); 42 | 43 | function build() { 44 | document.getElementById('test-container').innerHTML = ''; 45 | 46 | Embedo.utils.watcher( 47 | 'demo', 48 | function () { 49 | embedo 50 | .load(document.getElementById('test-container'), document.getElementById('test-url').value, { 51 | width: document.getElementById('test-width').value || null, 52 | height: document.getElementById('test-height').value || null 53 | }) 54 | .done(function (data) { 55 | Embedo.log('info', 'build', 'onDoneEvent', data); 56 | }) 57 | .fail(function (err) { 58 | Embedo.log('error', 'build', 'onErrorEvent', err); 59 | }); 60 | }, 61 | 250 62 | ); 63 | } 64 | 65 | // Loads instagram photo 66 | embedo.load(document.getElementById('embedo-instagram'), 'https://www.instagram.com/p/BsUZJNjl9Is/', { 67 | height: 800, 68 | hidecaption: false 69 | }); 70 | 71 | // Loads facebook post 72 | embedo 73 | .load( 74 | document.getElementById('embedo-facebook-post'), 75 | 'https://www.facebook.com/9gag/posts/10156278718151840' 76 | ) 77 | .fail(function (err) { 78 | Embedo.log('error', 'TESTING', 'Embedo instance error', err); 79 | }) 80 | .done(function (data) { 81 | Embedo.log('info', 'TESTING', 'Embedo instance watch', data); 82 | }); 83 | 84 | embedo.load( 85 | document.getElementById('embedo-facebook-photo'), 86 | 'https://www.facebook.com/9gag/photos/a.109041001839.105995.21785951839/10156236707186840' 87 | ); 88 | 89 | embedo.load( 90 | document.getElementById('embedo-facebook-video'), 91 | 'https://www.facebook.com/9gag/videos/10156133478761840' 92 | ); 93 | 94 | embedo.load(document.getElementById('embedo-facebook-page'), 'https://www.facebook.com/facebook'); 95 | 96 | embedo.load( 97 | document.getElementById('embedo-facebook-comment'), 98 | 'https://www.facebook.com/zuck/posts/10102577175875681?comment_id=1193531464007751&reply_comment_id=654912701278942' 99 | ); 100 | 101 | embedo.load( 102 | document.getElementById('embedo-facebook-comments'), 103 | 'https://developers.facebook.com/docs/plugins/comments#configurator' 104 | ); 105 | 106 | // Loads tweet 107 | embedo.load( 108 | document.getElementById('embedo-twitter'), 109 | 'https://twitter.com/Sh0bhit/status/797584590214926340' 110 | ); 111 | 112 | // Loads twitter timeline grid 113 | embedo.load( 114 | document.getElementById('embedo-twitter-grid'), 115 | 'https://twitter.com/TwitterDev/timelines/539487832448843776', 116 | { 117 | widget_type: 'grid' 118 | } 119 | ); 120 | 121 | // Loads twitter timeline 122 | embedo.load( 123 | document.getElementById('embedo-twitter-timeline'), 124 | 'https://twitter.com/TwitterDev/timelines/539487832448843776', 125 | { 126 | height: 500 127 | } 128 | ); 129 | 130 | // Multiple URLs 131 | embedo 132 | .load( 133 | document.getElementById('embedo-multiple'), 134 | [ 135 | 'https://www.instagram.com/p/BX3fMnRjHpZ', 136 | 'https://www.instagram.com/p/BX3ejdJHmkD', 137 | 'https://www.instagram.com/p/BX3VEDqFvmg' 138 | ], 139 | { 140 | hidecaption: false 141 | } 142 | ) 143 | .fail(function (err) { 144 | Embedo.log('error', 'TESTING', 'Embedo instance error', err); 145 | }) 146 | .done(function (data) { 147 | Embedo.log('info', 'TESTING', 'Embedo instance watch', data); 148 | }); 149 | 150 | embedo 151 | .load( 152 | document.getElementById('embedo-multiple-mixed'), 153 | [ 154 | 'https://twitter.com/samccone/status/918142052927291392', 155 | 'https://www.instagram.com/p/BX3ejdJHmkD', 156 | 'https://www.youtube.com/watch?v=iKzRIweSBLA' 157 | ], 158 | { 159 | width: 640 160 | } 161 | ) 162 | .fail(function (err) { 163 | Embedo.log('error', 'TESTING', 'Embedo instance error', err); 164 | }) 165 | .done(function (data) { 166 | Embedo.log('info', 'TESTING', 'Embedo instance watch', data); 167 | }); 168 | 169 | embedo.load(document.getElementById('embedo-gist'), 'https://gist.github.com/brandonb927/4149074.js'); 170 | 171 | embedo.load( 172 | document.getElementById('embedo-codepen'), 173 | 'https://codepen.io/PavelDoGreat/embed/zdWzEL/?height=265&theme-id=0&default-tab=js,result&embed-version=2' 174 | ); 175 | 176 | // Embed file test 177 | embedo.load( 178 | document.getElementById('embedo-file'), 179 | 'http://www.a3ts.org/wp-content/uploads/2014/07/PREVAC.pdf' 180 | ); 181 | 182 | // Embed external URL test 183 | embedo.load( 184 | document.getElementById('embedo-website'), 185 | 'https://static01.nyt.com/video/players/offsite/index.html?videoId=100000005363413' 186 | ); 187 | 188 | // Embed video URL 189 | embedo.load( 190 | document.getElementById('embedo-video'), 191 | 'https://archive.org/download/WebmVp8Vorbis/webmvp8.webm', 192 | { 193 | controls: 'contorls' 194 | } 195 | ); 196 | 197 | // Loads pinterest post 198 | embedo.load( 199 | document.getElementById('embedo-pinterest-pin'), 200 | 'https://www.pinterest.com/pin/99360735500167749', 201 | { 202 | strict: true 203 | } 204 | ); 205 | 206 | embedo.load( 207 | document.getElementById('embedo-pinterest-board'), 208 | 'https://www.pinterest.com/pinterest/official-news/', 209 | { 210 | 'data-pin-do': 'embedBoard', 211 | 'data-pin-board-width': 750, 212 | 'data-pin-scale-height': 600, 213 | 'data-pin-scale-width': 80, 214 | strict: true 215 | } 216 | ); 217 | 218 | embedo.load(document.getElementById('embedo-pinterest-profile'), 'https://www.pinterest.com/pinterest/', { 219 | 'data-pin-do': 'embedUser', 220 | 'data-pin-board-width': 750, 221 | 'data-pin-scale-height': 500, 222 | 'data-pin-scale-width': 80, 223 | strict: true 224 | }); 225 | 226 | // Loads youtube video 227 | embedo.load( 228 | document.getElementById('embedo-youtube'), 229 | ['https://www.youtube.com/watch?v=JGwWNGJdvx8', 'https://www.youtube.com/embed/vn2qXpcon-s'], 230 | { 231 | width: 640, 232 | height: 480 233 | } 234 | ); 235 | 236 | // Loads vimeo video 237 | embedo.load(document.getElementById('embedo-vimeo'), 'https://vimeo.com/212603149', { 238 | height: 500 239 | }); 240 | 241 | embedo.load(document.getElementById('embedo-jsfiddle'), 'http://jsfiddle.net/skelly/FX44w/embedded/'); 242 | 243 | embedo.load( 244 | document.getElementById('embedo-soundcloud'), 245 | 'https://soundcloud.com/uiceheidd/lucid-dreams-forget-me' 246 | ); 247 | 248 | embedo.load( 249 | document.getElementById('embedo-googlemaps'), 250 | 'https://www.google.de/maps/place/Berlin/@52.5076682,13.286064,11z/data=!3m1!4b1!4m5!3m4!1s0x47a84e373f035901:0x42120465b5e3b70!8m2!3d52.5200066!4d13.404954', 251 | { 252 | width: 640, 253 | height: 480, 254 | zoom: 10 255 | } 256 | ); 257 | 258 | // // Refresh All Embedo instances 259 | // setTimeout(function () { 260 | // embedo.refresh(); 261 | // }, 5000); 262 | 263 | // // Refresh Single Embedo instance 264 | // setTimeout(function () { 265 | // embedo.refresh(document.getElementById('embedo-facebook')); 266 | // }, 5000); 267 | 268 | // // Destroy All Embdos Test 269 | // setTimeout(function () { 270 | // embedo.destroy(document.getElementById('embedo-multiple')); 271 | // }, 5000); 272 | 273 | // // Destroy Single Embdos Test 274 | // setTimeout(function () { 275 | // embedo.destroy(document.getElementById('embedo-instagram')); 276 | // }, 20000); 277 | 278 | // Test Element Watch Events 279 | embedo.on('watch', function (result) { 280 | Embedo.log('info', 'TESTING', 'Embedo watch', result); 281 | }); 282 | 283 | embedo.on('refresh', function (request, data) { 284 | Embedo.log('info', 'TESTING', 'Embedo refresh', request, data); 285 | }); 286 | 287 | embedo.on('destroy', function () { 288 | Embedo.log('info', 'TESTING', 'Embedo destroy'); 289 | }); 290 | 291 | embedo.on('error', function (error) { 292 | Embedo.log('error', 'TESTING', 'Embedo error', error); 293 | }); 294 | 295 | /** 296 | * postMessage Event Tracking 297 | */ 298 | // Create IE + others compatible event handler 299 | var eventMethod = window.addEventListener ? 'addEventListener' : 'attachEvent'; 300 | var eventer = window[eventMethod]; 301 | var messageEvent = eventMethod === 'attachEvent' ? 'onmessage' : 'message'; 302 | 303 | // Decoding mesage posted on each successful render 304 | eventer( 305 | messageEvent, 306 | function (event) { 307 | if (event.data && Array.isArray(event.data) && event.data.indexOf('embedo') !== -1) { 308 | var decoded_message = JSON.parse(event.data[2]); 309 | Embedo.log('warn', event, decoded_message); 310 | } 311 | }, 312 | false 313 | ); 314 | } 315 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | embedo - Social Media Embed Plugin 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 |
34 |
35 |
36 | Setup 37 | Usage 38 | Docs 39 | 40 |
41 |
42 |

embedo

43 |

A simple, lightweight and standalone content embed plugin for web

44 |
45 | Star 46 | 47 | Documentation 48 |
49 |
50 |
51 |
52 | 53 | 54 |
55 |
56 |
57 |
58 |
59 |
60 |

embedo.js

61 |
62 | Bower version 63 | npm version 64 |
65 |
66 |
67 |
 68 |         
 69 |  /**
 70 |   * @implements Embedo setup
 71 |   *
 72 |   * @package {npm} npm install embedo --save
 73 |   * @package {bower} bower install embedo
 74 |   *
 75 |   * @import {jsdelivr} https://cdn.jsdelivr.net/npm/embedo
 76 |   * @import {cdnjs} https://cdnjs.com/libraries/embedo
 77 |   * @import {unpkg} https://unpkg.com/embedo
 78 |   */
 79 | 
 80 |   import Embedo from './my/vendor/folder';
 81 | 
 82 |   const embedo = new Embedo({
 83 |     facebook: {
 84 |       appId: 'my_app_id', // Enable facebook SDK
 85 |       version: 'v2.10'
 86 |     },
 87 |     twitter: true,  // Enable twitter SDK
 88 |     instagram: true,  // Enable instagram SDK
 89 |     pinterest: true  // Enable pinterest SDK,
 90 |     googlemaps: {
 91 |       key: 'my_api_key' // Enables google maps API
 92 |     }
 93 |   });
 94 | 
 95 |   // Calling .load() event (HTMLElement, url[String|Array], options[Object])
 96 |   embedo.load(
 97 |     document.getElementById('my-awesome-container'),
 98 |     'https://twitter.com/Twitter/status/849866660882206721'
 99 |   )
100 |   .done((data) => {})
101 |   .fail((err) => {})
102 | 
103 |   // OR calling .load() event (HTMLElement, url[String|Array], options[Object])
104 |   embedo.load(
105 |     document.getElementById('my-awesome-container'), [
106 |     'https://www.instagram.com/p/BX3fMnRjHpZ',
107 |     'https://www.instagram.com/p/BX3ejdJHmkD',
108 |     'https://www.instagram.com/p/BX3VEDqFvmg'
109 |   ], {
110 |     hidecaption: false
111 |   });
112 | 
113 |   // Load with native and external options
114 |   embedo.load(
115 |     document.getElementById('my-awesome-container'),
116 |     'https://www.instagram.com/p/BSoJJjQA3Td/',
117 |     {
118 |       width: 640,
119 |       height: 480,
120 |       hidecaption: true
121 |     }
122 |   );
123 | 
124 |   // Also, the jQuery way
125 |   embedo.load($('my-awesome-container').get(0), 'my://url');
126 | 
127 |   // Refresh event (if no element passed, refreshes all embedo instances)
128 |   embedo.refresh(document.getElementById('my-awesome-container'));
129 | 
130 |   // Destroy event (if no element passed, destroys all embedo instances)
131 |   embedo.destroy(document.getElementById('my-awesome-container'));
132 | 
133 |   // Single instance event handler
134 |   embedo.load(el, url, options).done((data) => {}).fail((err) => {})
135 | 
136 |   // Global instances event handlers
137 |   embedo.on('watch', (event, result) => {});
138 |   embedo.on('refresh', (result, data) => {});
139 |   embedo.on('destroy', () => {});
140 |         
141 |       
142 |
143 |
144 |
145 |
146 |

What's currently supported?

147 |
148 | 149 | Facebook URLs containing page, post, photos, videos or comments 150 |
151 |
152 | 153 | Twitter URLs containing user timeline and tweets 154 |
155 |
156 | 157 | YouTube videos URLs, playlists will play in loop 158 |
159 |
160 | 161 | Instagram URLs containing posts and videos 162 |
163 |
164 | 165 | Pinterest URLs containing boards, profile and pins 166 |
167 |
168 | 169 | Vimeo URLs containing videos 170 |
171 |
172 | 173 | SoundCloud URLs containing videos 174 |
175 |
176 | 177 | Reddit posts or comments as plugin 178 |
179 |
180 | 181 | Google Maps URLs containing cordinates to a location 182 |
183 |
184 | 185 | Fallback to iframe for allowed CORS urls or formats like .pdf, .webm, etc. 186 |
187 |
188 | 193 | 198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /docs/usage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | embedo - Social Media Embed Plugin - Setup 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 |
34 |
35 |
36 | Setup 37 | Usage 38 | Docs 39 | 40 |
41 |
42 |

embedo

43 |

A simple, lightweight and standalone content embed plugin for web

44 |
45 | Star 46 | 47 | Documentation 48 |
49 |
50 |
51 |
52 |

usage.md

53 |
54 |
55 |

embedo / Embed Guidelines

56 | 57 |

Embedo only works for public URI having no authentication required or promising cross-domain policies of the 58 | host. It uses oEmbed API endpoints for facebook, twitter, instagram and vimeo acting more as a wrapper around 59 | respective API which allows using existing field-value set as explained further in documentation.

60 | 61 | 62 | 63 | 64 | 74 | 75 | 76 |

Facebook Page, Posts and Comments

77 | 78 |

This supports only public entities as page, post, videos, photos or comment(s).

79 | 80 |

The URL must contain the pageId or pageUsername and URL for photos, posts or 81 | videos should look or closer which support this expression.

82 | 83 | 84 |
https://www.facebook.com/[FACEBOOK_HANDLE]
 85 | https://www.facebook.com/[FACEBOOK_HANDLE]/photos/[SESSION_OR_USER_SCOPE_ID]/[POST_ID_HERE]
 86 | https://www.facebook.com/[FACEBOOK_HANDLE]/posts/[POST_ID_HERE]
 87 | https://www.facebook.com/[FACEBOOK_HANDLE]/videos/[POST_ID_HERE]/
 88 | https://www.facebook.com/pages/[FACEBOOK_HANDLE]/posts/[POST_ID_HERE]
 89 | https://www.facebook.com/pages/[FACEBOOK_HANDLE]/videos/[POST_ID_HERE]
 90 | https://www.facebook.com/[FACEBOOK_HANDLE]/posts/[POST_ID_HERE]?comment_id=[COMMENT_ID]
 91 | https://www.facebook.com/[FACEBOOK_HANDLE]/posts/[POST_ID_HERE]?reply_comment_id=[REPLY_COMMENT_ID]
92 | 93 |

PS: If you’re running this in headless browsers (eg. phantom) or demos on jsfiddle/codepen/etc, facebook SDK 94 | will throw security exception due to recent changes in their server. But it will work normally when script is 95 | imported.

96 |

Resources

97 | 100 | 101 | 102 |

Twitter - Tweets, Timelines and Grid

103 | 104 |

This is straightforward, the exact twitter URL can be embedded while passing options from oembed API.

105 | 106 | 107 |
https://twitter.com/[TWITTER_HANDLE]
108 | https://twitter.com/[TWITTER_HANDLE]/status/[TWEET_ID]
109 | https://twitter.com/[TWITTER_HANDLE]/timelines/[TIMELINE_ID]
110 | 111 |

For Timeline embed: 112 |

113 | 114 | 115 |
embedo.load(HTMLElement, 'https://twitter.com/TwitterDev/timelines/539487832448843776')
116 | 117 |

For Timeline Grid embed: 118 |

119 | 120 | 121 |
embedo.load(HTMLElement, 'https://twitter.com/TwitterDev/timelines/539487832448843776', {
122 |   widget_type: 'grid'
123 | })
124 |

Resources

125 | 129 | 130 | 131 |

Instagram Posts

132 | 133 |

Only posts can be embedded, no timeline. So your URL should be closer to this pattern:

134 | 135 | 136 |
https://www.instagram.com/p/[POST_ID_HERE]
137 |

Resources

138 | 141 | 142 | 143 |

YouTube Videos

144 | 145 |

Supports URLs matching /watch?v= or /embed in URL should work. Avoid playlist(s) 146 | URL.

147 | 148 | 149 |
https://www.youtube.com/watch?v=[VIDEO_ID]
150 | https://www.youtube.com/embed/[VIDEO_ID]
151 | https://youtu.be/[VIDEO_ID]
152 | 153 | 154 |

Vimeo

155 | 156 |

Supports URLs matching video or channel links.

157 | 158 | 159 |
https://vimeo.com/[VIDEO_ID]
160 | https://vimeo.com/channels/[CHANNEL_HANDLE]/[VIDEO_ID]
161 | 162 | 163 |

Pinterest

164 | 165 |

By default, supports Pins embed.

166 | 167 | 168 |
https://www.pinterest.com/pin/[PIN_ID]
169 | https://www.pinterest(.co.uk|.de|ANY)/pin/[PIN_ID]/
170 | 171 |

For Board embed: 172 |

173 | 174 | 175 |
embedo.load(HTMLElement, 'https://www.pinterest.com/pinterest/official-news/', {
176 |   'data-pin-do': 'embedBoard',
177 |   'data-pin-board-width': 400,
178 |   'data-pin-scale-height': 240,
179 |   'data-pin-scale-width': 80,
180 |   'strict': true // To allow above size dimensions to be used
181 | })
182 | 183 |

For Profile embed: 184 |

185 | 186 | 187 |
embedo.load(HTMLElement, 'https://www.pinterest.com/pinterest/', {
188 |   'data-pin-do': 'embedUser'
189 | })
190 |

Resources

191 | 194 | 195 | 196 |

Others

197 | 198 |

The external links, based upon detected mime gets embed either in <iframe> or <video> 199 | elements. These could be any URL possible which has public access or cross-domain disabled.

200 | 201 |

Its also possible to override the HTML Tag using:

202 | 203 |
embedo.load(HTMLElement, 'https://my.awesome/url/here', { tagName: 'embed' })
204 |
205 | 206 |
207 |
208 |

What's currently supported?

209 |
210 | 211 | Facebook URLs containing page, post, photos, videos or comments 212 |
213 |
214 | 215 | Twitter URLs containing user timeline and tweets 216 |
217 |
218 | 219 | YouTube videos URLs, playlists will play in loop 220 |
221 |
222 | 223 | Instagram URLs containing posts and videos 224 |
225 |
226 | 227 | Pinterest URLs containing boards, profile and pins 228 |
229 |
230 | 231 | Vimeo URLs containing videos 232 |
233 |
234 | 235 | SoundCloud URLs containing videos 236 |
237 |
238 | 239 | Reddit posts or comments as plugin 240 |
241 |
242 | 243 | Google Maps URLs containing cordinates to a location 244 |
245 |
246 | 247 | Fallback to iframe for allowed CORS urls or formats like .pdf, .webm, etc. 248 |
249 |
250 | 255 | 260 |
261 |
262 |
263 | 279 | 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /embedo.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file embedo.js 3 | * 4 | * Embedo is third party content embed plugin with features having events and resizing. 5 | * It provides a layer above popular social media sites native embed snippets 6 | * making it easier to hook content without modifying much code. 7 | * 8 | * @author Shobhit Sharma 9 | * @license MIT 10 | */ 11 | "use strict";!function(t,e){"function"==typeof define&&define.amd?define(e):"object"==typeof module&&module.exports?module.exports=e():t&&(t.Embedo=window.Embedo=e())}(this,function(){function h(t){return this.options=t||h.defaults.OPTIONS,this.requests=[],this.events=[],this.init(this.options),this}function t(){this.resolved=[],this.rejected=[]}function c(r,o,s,u){if(h.log("info","automagic",r,o,s),s=s||{},u=u||function(){},!h.utils.validateElement(r)||!h.utils.validateElement(o))return u(new Error("HTMLElement does not exist in DOM."));h.utils.watcher(s.id||h.utils.uuid(),function(){var t,e={width:s.width||h.utils.compute(r,"width"),height:s.height||h.utils.compute(r,"height")},i=h.utils.compute(o,"width"),n=h.utils.compute(o,"height");if(s.strict)return u(null,{width:e.width,height:e.height});s.width&&s.height&&(t=e.width' + 1291 | '' 1292 | ); 1293 | iframe.contentWindow.document.close(); 1294 | iframe.onerror = function (err) { 1295 | callback(err); 1296 | }; 1297 | iframe.addEventListener('load', function (event) { 1298 | callback(null, { 1299 | id: id, 1300 | el: element, 1301 | event: event, 1302 | width: Embedo.utils.compute(container, 'width'), 1303 | height: Embedo.utils.compute(container, 'height') 1304 | }); 1305 | }); 1306 | }; 1307 | 1308 | /** 1309 | * @method soundcloud 1310 | * SoundCloud Embed Player (api-web) prototype 1311 | * 1312 | * @see https://developers.soundcloud.com/docs/oembed 1313 | * @param {number} id 1314 | * @param {HTMLElement} element 1315 | * @param {string} url 1316 | * @param {object} options Optional parameters. 1317 | * @return callback 1318 | */ 1319 | Embedo.prototype.soundcloud = function (id, element, url, options, callback) { 1320 | if (options.hasOwnProperty('width') && options.width) { 1321 | options.maxwidth = options.maxwidth || options.width || '100%'; 1322 | } 1323 | if (options.hasOwnProperty('height') && options.height) { 1324 | options.maxheight = options.maxheight || options.height; 1325 | } 1326 | var size = Embedo.utils.dimensions(element, options.maxwidth, options.maxheight); 1327 | var embed_options = Embedo.utils.merge( 1328 | { 1329 | url: encodeURI(url), 1330 | format: 'js' // Defaults JSONP 1331 | }, 1332 | options, 1333 | Embedo.defaults.RESTRICTED 1334 | ); 1335 | var embed_uri = Embedo.defaults.SOURCES.soundcloud.oEmbed + '?' + Embedo.utils.querystring(embed_options); 1336 | 1337 | Embedo.utils.fetch(embed_uri, function (error, content) { 1338 | if (error) { 1339 | Embedo.log('error', 'soundcloud', error); 1340 | return callback(error); 1341 | } 1342 | var container = Embedo.utils.generateEmbed(id, 'soundcloud', content.html); 1343 | element.appendChild(container); 1344 | 1345 | callback(null, { 1346 | id: id, 1347 | el: element, 1348 | width: size.width, 1349 | height: size.height 1350 | }); 1351 | }); 1352 | }; 1353 | 1354 | /** 1355 | * @method iframe 1356 | * Embed URLs to HTML5 frame prototype 1357 | * 1358 | * @param {number} id 1359 | * @param {HTMLElement} element 1360 | * @param {string} url 1361 | * @param {object} options Optional parameters. 1362 | * @return callback 1363 | */ 1364 | Embedo.prototype.iframe = function (id, element, url, options, callback) { 1365 | var fragment = document.createDocumentFragment(); 1366 | var size = Embedo.utils.dimensions(element, options.width, options.height); 1367 | var extension = (url.substr(url.lastIndexOf('.')) || '').replace('.', '').toLowerCase(); 1368 | var mimes = { 1369 | csv: 'text/csv', 1370 | pdf: 'application/pdf', 1371 | gif: 'image/gif', 1372 | js: 'application/javascript', 1373 | json: 'application/json', 1374 | xhtml: 'application/xhtml+xml', 1375 | pps: 'application/vnd.ms-powerpoint', 1376 | ppsx: 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 1377 | xml: 'application/xml', 1378 | ogg: 'video/ogg', 1379 | mp4: 'video/mp4', 1380 | webm: 'video/webm', 1381 | html: 'text/html' 1382 | }; 1383 | var mimetype = mimes[extension] || mimes.html; 1384 | var has_video = extension.match(/(mp4|ogg|webm|ogv|ogm)/); 1385 | var el_type = has_video ? 'video' : options.tagName || 'embed'; 1386 | var override = Embedo.utils.merge({}, options, Embedo.defaults.RESTRICTED); 1387 | var embed_el = Embedo.utils.generateElement( 1388 | el_type, 1389 | Embedo.utils.merge( 1390 | { 1391 | type: mimetype, 1392 | src: url, 1393 | width: size.width, 1394 | height: size.height 1395 | }, 1396 | override 1397 | ) 1398 | ); 1399 | 1400 | fragment.appendChild(Embedo.utils.generateEmbed(id, 'iframe', embed_el)); 1401 | element.appendChild(fragment); 1402 | 1403 | if (el_type === 'video') { 1404 | setTimeout(function () { 1405 | callback(null, { 1406 | id: id, 1407 | el: element, 1408 | width: Embedo.utils.compute(embed_el, 'width'), 1409 | height: Embedo.utils.compute(embed_el, 'height') 1410 | }); 1411 | }, 250); 1412 | } else { 1413 | embed_el.onerror = function (err) { 1414 | callback(err); 1415 | }; 1416 | embed_el.addEventListener('load', function (event) { 1417 | callback(null, { 1418 | id: id, 1419 | el: element, 1420 | event: event, 1421 | width: Embedo.utils.compute(embed_el, 'width'), 1422 | height: Embedo.utils.compute(embed_el, 'height') 1423 | }); 1424 | }); 1425 | } 1426 | }; 1427 | 1428 | /** 1429 | * @method render 1430 | * Renders an embedo instance 1431 | * 1432 | * @name load 1433 | * @param {HTMLElement} element 1434 | * @param {string} url 1435 | * @param {object} options Optional parameters. 1436 | * @return callback 1437 | */ 1438 | Embedo.prototype.render = function (element, url, options, callback) { 1439 | Embedo.log('info', 'render', element, url, options); 1440 | options = options || {}; 1441 | callback = callback || function () {}; 1442 | 1443 | if (!element || !Embedo.utils.validateElement(element)) { 1444 | Embedo.log('info', 'render', '`element` is either missing or invalid'); 1445 | return this.emit('error', new Error('element_is_missing')); 1446 | } 1447 | 1448 | if (typeof url !== 'string') { 1449 | return this.emit('error', new Error('invalid_url_string')); 1450 | } 1451 | 1452 | if (!url || !Embedo.utils.validateURL(url)) { 1453 | Embedo.log('info', 'render', '`url` is either missing or invalid'); 1454 | return this.emit('error', new Error('invalid_or_missing_url')); 1455 | } 1456 | 1457 | var source = getURLSource(url); 1458 | 1459 | if (!source) { 1460 | Embedo.log('info', 'render', new Error('Invalid or Unsupported URL')); 1461 | return this.emit('error', new Error('url_not_supported')); 1462 | } 1463 | 1464 | if (!this[source]) { 1465 | Embedo.log('info', 'render', new Error('Requested source is not implemented or missing.')); 1466 | return this.emit('error', new Error('unrecognised_url')); 1467 | } 1468 | 1469 | if ('width' in options && options.width) { 1470 | options.width = Embedo.utils.convertToPx(element, 'width', options.width); 1471 | } 1472 | 1473 | if ('height' in options && options.height) { 1474 | options.height = Embedo.utils.convertToPx(element, 'height', options.height); 1475 | } 1476 | 1477 | var id = Embedo.utils.uuid(); 1478 | var request = { 1479 | id: id, 1480 | el: element, 1481 | source: source, 1482 | url: url, 1483 | attributes: options 1484 | }; 1485 | 1486 | this.requests.push(request); 1487 | 1488 | this.emit('watch', 'load', request); 1489 | 1490 | this[source]( 1491 | id, 1492 | element, 1493 | url, 1494 | options, 1495 | function (err, data) { 1496 | if (err) { 1497 | this.emit('error', err); 1498 | return callback(err); 1499 | } 1500 | data.url = request.url; 1501 | data.source = request.source; 1502 | data.options = request.attributes; 1503 | 1504 | this.emit('watch', 'loaded', data); 1505 | callback(null, data); 1506 | }.bind(this) 1507 | ); 1508 | 1509 | /** 1510 | * @function getURLSource 1511 | * Checks Source from URI 1512 | * 1513 | * @param {string} url 1514 | * @returns {string} 1515 | */ 1516 | function getURLSource(url) { 1517 | var urlRegExp = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/; 1518 | var sources = Object.keys(Embedo.defaults.SOURCES) || []; 1519 | 1520 | if (!urlRegExp.test(url)) { 1521 | return null; 1522 | } 1523 | 1524 | var matched_source = sources 1525 | .filter(function (source) { 1526 | if (Embedo.defaults.SOURCES[source] && url.match(Embedo.defaults.SOURCES[source].REGEX)) { 1527 | return source; 1528 | } 1529 | }) 1530 | .filter(Boolean); 1531 | 1532 | return matched_source && matched_source.length ? matched_source[0] : 'iframe'; 1533 | } 1534 | }; 1535 | 1536 | /** 1537 | * @method load 1538 | * Loads single or multiple embedo instances 1539 | * 1540 | * @name load 1541 | * @param {HTMLElement} element 1542 | * @param {String|Array} urls 1543 | * @param {object} options Optional parameters. 1544 | * @return callback 1545 | */ 1546 | Embedo.prototype.load = function (element, urls, options) { 1547 | Embedo.log('info', 'load', element, urls, options); 1548 | options = options || {}; 1549 | var observer = new Embedo.utils.observer(); 1550 | 1551 | if (!element || !Embedo.utils.validateElement(element)) { 1552 | Embedo.log('info', 'load', '`element` is either missing or invalid'); 1553 | this.emit('error', new Error('element_is_missing')); 1554 | } else { 1555 | if (urls instanceof Array) { 1556 | var reqs = { 1557 | failed: [], 1558 | finished: [] 1559 | }; 1560 | var jobs = urls.map( 1561 | function (url) { 1562 | return function (done) { 1563 | this.render(element, url, options, function (err, data) { 1564 | if (err) { 1565 | reqs.failed.push(err); 1566 | return done(err); 1567 | } 1568 | reqs.finished.push(data); 1569 | done(null, data); 1570 | }); 1571 | }.bind(this); 1572 | }.bind(this) 1573 | ); 1574 | 1575 | Embedo.utils.sequencer.apply(this, jobs).then(function () { 1576 | if (reqs.failed.length > 0) { 1577 | return observer.reject(reqs.failed); 1578 | } 1579 | observer.resolve(reqs.finished); 1580 | }); 1581 | } else if (typeof urls === 'string') { 1582 | this.render(element, urls, options, function (err, data) { 1583 | if (err) { 1584 | return observer.reject(err); 1585 | } 1586 | observer.resolve(data); 1587 | }); 1588 | } else { 1589 | this.emit('error', new Error('invalid_url_string')); 1590 | } 1591 | } 1592 | 1593 | return observer; 1594 | }; 1595 | 1596 | /** 1597 | * @method refresh 1598 | * Refresh single or all embedo instances 1599 | * 1600 | * @param {object} element 1601 | */ 1602 | Embedo.prototype.refresh = function (element) { 1603 | Embedo.log('info', 'refresh', this.requests, element); 1604 | if (this.requests.length === 0) { 1605 | return; 1606 | } 1607 | this.requests.forEach( 1608 | function (request) { 1609 | if (!request.el) { 1610 | return; 1611 | } 1612 | 1613 | if (request.source === 'iframe') { 1614 | return this.emit('refresh', request, { 1615 | width: Embedo.utils.compute(request.el, 'width'), 1616 | height: Embedo.utils.compute(request.el, 'height') 1617 | }); 1618 | } 1619 | 1620 | if (element) { 1621 | if (!Embedo.utils.validateElement(element)) { 1622 | return; 1623 | } 1624 | if (element === request.el) { 1625 | automagic( 1626 | request.el, 1627 | document.getElementById(request.id), 1628 | request.attributes, 1629 | function (err, data) { 1630 | if (data) { 1631 | this.emit('refresh', request, data); 1632 | } 1633 | }.bind(this) 1634 | ); 1635 | } 1636 | } else { 1637 | automagic( 1638 | request.el, 1639 | document.getElementById(request.id), 1640 | request.attributes, 1641 | function (err, data) { 1642 | if (data) { 1643 | this.emit('refresh', request, data); 1644 | } 1645 | }.bind(this) 1646 | ); 1647 | } 1648 | }.bind(this) 1649 | ); 1650 | 1651 | return this; 1652 | }; 1653 | 1654 | /** 1655 | * @method destroy 1656 | * Destroy an/all instance(s) of embedo 1657 | * 1658 | * @param {object} element 1659 | */ 1660 | Embedo.prototype.destroy = function (element) { 1661 | Embedo.log('warn', 'destroy', this.requests, element); 1662 | if (this.requests.length === 0) { 1663 | return; 1664 | } 1665 | var removed = []; 1666 | 1667 | this.requests.forEach( 1668 | function (request) { 1669 | if (!request.el || !Embedo.utils.validateElement(request.el)) { 1670 | return; 1671 | } 1672 | if (element) { 1673 | if (!Embedo.utils.validateElement(element)) { 1674 | return; 1675 | } 1676 | if (element === request.el) { 1677 | if (document.getElementById(request.id)) { 1678 | document.getElementById(request.id).remove(); 1679 | } 1680 | removed.push(request.id); 1681 | this.emit('destroy', request); 1682 | } 1683 | } else { 1684 | if (document.getElementById(request.id)) { 1685 | document.getElementById(request.id).remove(); 1686 | } 1687 | removed.push(request.id); 1688 | this.emit('destroy', request); 1689 | } 1690 | }.bind(this) 1691 | ); 1692 | 1693 | this.requests = this.requests.filter(function (request) { 1694 | return removed.indexOf(request.id) < 0; 1695 | }); 1696 | 1697 | return this; 1698 | }; 1699 | 1700 | /** 1701 | * @function facebookify 1702 | * Parses Facebook SDK 1703 | * 1704 | * @param {HTMLElement} parentNode 1705 | * @param {HTMLElement} childNode 1706 | * @param {object} options 1707 | */ 1708 | function facebookify(parentNode, childNode, options, callback) { 1709 | Embedo.utils.sdkReady('facebook', function (err) { 1710 | if (err) { 1711 | return callback(err); 1712 | } 1713 | window.FB.XFBML.parse(parentNode); 1714 | window.FB.Event.subscribe('xfbml.render', function () { 1715 | // First state will be `parsed` and then `rendered` to acknowledge embed. 1716 | if (childNode.firstChild) { 1717 | if (options.centerize !== false) { 1718 | Embedo.utils.centerize(parentNode, childNode, options); 1719 | } 1720 | if (childNode.firstChild.getAttribute('fb-xfbml-state') === 'rendered') { 1721 | automagic(parentNode, childNode, options, callback); 1722 | } 1723 | } 1724 | }); 1725 | }); 1726 | } 1727 | 1728 | /** 1729 | * @function twitterify 1730 | * Parses Twitter SDK 1731 | * 1732 | * @param {HTMLElement} parentNode 1733 | * @param {HTMLElement} childNode 1734 | * @param {object} options 1735 | */ 1736 | function twitterify(parentNode, childNode, options, callback) { 1737 | Embedo.utils.sdkReady('twitter', function (err) { 1738 | if (err) { 1739 | return callback(err); 1740 | } 1741 | window.twttr.widgets.load(childNode); 1742 | window.twttr.events.bind('rendered', function (event) { 1743 | if ( 1744 | childNode.firstChild && 1745 | childNode.firstChild.getAttribute('id') === event.target.getAttribute('id') 1746 | ) { 1747 | if (options.centerize !== false) { 1748 | Embedo.utils.centerize(parentNode, childNode, options); 1749 | } 1750 | automagic(parentNode, childNode, options, callback); 1751 | } 1752 | }); 1753 | }); 1754 | } 1755 | 1756 | /** 1757 | * @function instagramify 1758 | * Parses Instagram SDK 1759 | * 1760 | * @param {HTMLElement} parentNode 1761 | * @param {HTMLElement} childNode 1762 | * @param {object} options 1763 | */ 1764 | function instagramify(parentNode, childNode, options, callback) { 1765 | Embedo.utils.sdkReady('instagram', function (err) { 1766 | if (err) { 1767 | return callback(err); 1768 | } 1769 | if (!window.instgrm.Embeds || !window.instgrm.Embeds) { 1770 | return callback(new Error('instagram_sdk_missing')); 1771 | } 1772 | 1773 | window.instgrm.Embeds.process(childNode); 1774 | var instagram_embed_timer = setInterval(handleInstagramRendered, 250); 1775 | 1776 | function handleInstagramRendered() { 1777 | if (childNode.firstChild && childNode.firstChild.className.match(/instagram-media-rendered/)) { 1778 | clearInterval(instagram_embed_timer); 1779 | if (options.centerize !== false) { 1780 | Embedo.utils.centerize(parentNode, childNode, options); 1781 | } 1782 | return automagic(parentNode, childNode, options, callback); 1783 | } 1784 | } 1785 | }); 1786 | } 1787 | 1788 | /** 1789 | * @function pinterestify 1790 | * Parses Pinterest SDK 1791 | * 1792 | * @param {HTMLElement} parentNode 1793 | * @param {HTMLElement} childNode 1794 | * @param {object} options 1795 | */ 1796 | function pinterestify(parentNode, childNode, options, callback) { 1797 | Embedo.utils.sdkReady('pinterest', function (err) { 1798 | if (err) { 1799 | return callback(err); 1800 | } 1801 | if (!window.PinUtils || !window.PinUtils || !childNode || !childNode.firstChild) { 1802 | return callback(new Error('pinterest_sdk_missing')); 1803 | } 1804 | 1805 | setTimeout(function () { 1806 | if (!childNode.querySelector('[data-pin-href]')) { 1807 | window.PinUtils.build(childNode); 1808 | } 1809 | 1810 | var pinterest_embed_timer_count = 0; 1811 | var pinterest_embed_timer = setInterval(function () { 1812 | pinterest_embed_timer_count += 1; 1813 | if (childNode.querySelector('[data-pin-href]')) { 1814 | clearInterval(pinterest_embed_timer); 1815 | if (options.centerize !== false) { 1816 | Embedo.utils.centerize(parentNode, childNode, options); 1817 | } 1818 | return automagic(parentNode, childNode, options, callback); 1819 | } else if (pinterest_embed_timer_count >= 20) { 1820 | clearInterval(pinterest_embed_timer); 1821 | return callback(new Error('pinterest_embed_failed')); 1822 | } 1823 | }, 250); 1824 | }, 750); 1825 | }); 1826 | } 1827 | 1828 | /** 1829 | * @function automagic 1830 | * Automagic - Scales and resizes embed container 1831 | * 1832 | * @param {HTMLElement} parentNode 1833 | * @param {HTMLElement} childNode 1834 | * @param {object} options 1835 | */ 1836 | function automagic(parentNode, childNode, options, callback) { 1837 | Embedo.log('info', 'automagic', parentNode, childNode, options); 1838 | options = options || {}; 1839 | callback = callback || function () {}; 1840 | 1841 | if (!Embedo.utils.validateElement(parentNode) || !Embedo.utils.validateElement(childNode)) { 1842 | return callback(new Error('HTMLElement does not exist in DOM.')); 1843 | } 1844 | 1845 | Embedo.utils.watcher( 1846 | options.id || Embedo.utils.uuid(), 1847 | function () { 1848 | var parent = { 1849 | width: options.width || Embedo.utils.compute(parentNode, 'width'), 1850 | height: options.height || Embedo.utils.compute(parentNode, 'height') 1851 | }; 1852 | var child = { 1853 | width: Embedo.utils.compute(childNode, 'width'), 1854 | height: Embedo.utils.compute(childNode, 'height') 1855 | }; 1856 | 1857 | if (options.strict) { 1858 | return callback(null, { 1859 | width: parent.width, 1860 | height: parent.height 1861 | }); 1862 | } 1863 | 1864 | // Odd case when requested height is beyond limit of third party 1865 | // Only apply when fixed width and heights are provided 1866 | if (options.width && options.height) { 1867 | var isOverflowing = child.width > parent.width || child.height > parent.height; 1868 | 1869 | if (options.width) { 1870 | childNode.style.width = options.width + 'px'; 1871 | } 1872 | 1873 | if (options.height) { 1874 | childNode.style.height = options.height + 'px'; 1875 | } 1876 | 1877 | if (isOverflowing) { 1878 | var scale = Math.min(parent.width / child.width, parent.height / child.height); 1879 | Embedo.utils.transform(childNode, 'scale(' + scale + ')'); 1880 | } 1881 | } 1882 | 1883 | callback(null, { 1884 | width: parent.width, 1885 | height: parent.height 1886 | }); 1887 | }, 1888 | 500 1889 | ); 1890 | } 1891 | 1892 | return Embedo; 1893 | }); 1894 | --------------------------------------------------------------------------------