🐛 Koenig - Fix embedding of multiple FB Videos
refs https://github.com/TryGhost/Ghost/issues/9623 - wrap all embeds in an `<iframe>` so that their scripts are isolated (fixes FB Video) - add `MutationObserver` implementation to adjust iframe height as embed's content is loaded - add `noframe.js` to resize embedded iframes such as YouTube videos
This commit is contained in:
parent
f9b08d8d64
commit
57a66f7cdc
|
@ -138,6 +138,11 @@ module.exports = function (defaults) {
|
|||
app.import('node_modules/mobiledoc-kit/dist/amd/mobiledoc-kit.js');
|
||||
app.import('node_modules/mobiledoc-kit/dist/amd/mobiledoc-kit.map');
|
||||
app.import('node_modules/simplemde/debug/simplemde.js');
|
||||
app.import('node_modules/reframe.js/dist/noframe.es.js', {
|
||||
using: [
|
||||
{transformation: 'es6', as: 'noframe.js'}
|
||||
]
|
||||
});
|
||||
|
||||
// pull things we rely on via lazy-loading into the test-support.js file so
|
||||
// that tests don't break when running via http://localhost:4200/tests
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Component from '@ember/component';
|
||||
import layout from '../templates/components/koenig-card-embed';
|
||||
import noframe from 'noframe.js';
|
||||
import {NO_CURSOR_MOVEMENT} from './koenig-editor';
|
||||
import {isBlank} from '@ember/utils';
|
||||
import {run} from '@ember/runloop';
|
||||
|
@ -40,10 +41,22 @@ export default Component.extend({
|
|||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
this._loadPayloadScript();
|
||||
this._populateIframe();
|
||||
this._focusInput();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
run.cancel(this._resizeDebounce);
|
||||
|
||||
if (this._iframeMutationObserver) {
|
||||
this._iframeMutationObserver.disconnect();
|
||||
}
|
||||
|
||||
window.removeEventListener('resize', this._windowResizeHandler);
|
||||
},
|
||||
|
||||
actions: {
|
||||
onDeselect() {
|
||||
if (this.payload.url && !this.payload.html && !this.hasError) {
|
||||
|
@ -113,7 +126,7 @@ export default Component.extend({
|
|||
set(this.payload, 'type', response.type);
|
||||
this.saveCard(this.payload, false);
|
||||
|
||||
run.schedule('afterRender', this, this._loadPayloadScript);
|
||||
run.schedule('afterRender', this, this._populateIframe);
|
||||
} catch (err) {
|
||||
this.set('hasError', true);
|
||||
}
|
||||
|
@ -127,44 +140,118 @@ export default Component.extend({
|
|||
}
|
||||
},
|
||||
|
||||
// some oembeds will have a script tag but it won't automatically run
|
||||
// due to the way Ember renders the card components. Grab the script
|
||||
// element and push a new one to force the browser to download+run it
|
||||
_loadPayloadScript() {
|
||||
let oldScript = this.element.querySelector('script');
|
||||
if (oldScript) {
|
||||
let parent = oldScript.parentElement;
|
||||
let newScript = document.createElement('script');
|
||||
newScript.type = 'text/javascript';
|
||||
_populateIframe() {
|
||||
let iframe = this.element.querySelector('iframe');
|
||||
if (iframe) {
|
||||
iframe.contentWindow.document.open();
|
||||
iframe.contentWindow.document.write(this.payload.html);
|
||||
iframe.contentWindow.document.close();
|
||||
|
||||
if (oldScript.src) {
|
||||
// hide the original embed html to avoid ugly transitions as the
|
||||
// script runs (at least on reasonably good network and cpu)
|
||||
let embedElement = this.element.querySelector('[data-kg-embed]');
|
||||
embedElement.style.display = 'none';
|
||||
iframe.contentDocument.body.style.display = 'flex';
|
||||
iframe.contentDocument.body.style.margin = '0';
|
||||
iframe.contentDocument.body.style.justifyContent = 'center';
|
||||
|
||||
newScript.src = oldScript.src;
|
||||
|
||||
// once the script has loaded, wait a little while for it to do it's
|
||||
// thing before making everything visible again
|
||||
newScript.onload = run.bind(this, function () {
|
||||
run.later(this, function () {
|
||||
embedElement.style.display = null;
|
||||
}, 500);
|
||||
});
|
||||
|
||||
newScript.onerror = run.bind(this, function () {
|
||||
embedElement.style.display = null;
|
||||
});
|
||||
} else {
|
||||
newScript.innerHTML = oldScript.innerHTML;
|
||||
let nestedIframe = iframe.contentDocument.body.firstChild;
|
||||
if (nestedIframe.nodeName === 'IFRAME') {
|
||||
noframe(nestedIframe, '[data-kg-embed]');
|
||||
this._resizeIframe(iframe);
|
||||
}
|
||||
|
||||
oldScript.remove();
|
||||
parent.appendChild(newScript);
|
||||
this._iframeResizeHandler = run.bind(this, this._resizeIframe, iframe);
|
||||
this._iframeMutationObserver = this._createMutationObserver(
|
||||
iframe.contentWindow.document,
|
||||
this._iframeResizeHandler
|
||||
);
|
||||
|
||||
this._setupWindowResizeHandler(iframe);
|
||||
}
|
||||
},
|
||||
|
||||
_createMutationObserver(target, callback) {
|
||||
function addImageLoadListeners(mutation) {
|
||||
function addImageLoadListener(element) {
|
||||
if (element.complete === false) {
|
||||
element.addEventListener('load', imageEventTriggered, false);
|
||||
element.addEventListener('error', imageEventTriggered, false);
|
||||
imageElements.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
|
||||
addImageLoadListener(mutation.target);
|
||||
} else if (mutation.type === 'childList') {
|
||||
Array.prototype.forEach.call(
|
||||
mutation.target.querySelectorAll('img'),
|
||||
addImageLoadListener
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromElements(element) {
|
||||
imageElements.splice(imageElements.indexOf(element), 1);
|
||||
}
|
||||
|
||||
function removeImageLoadListener(element) {
|
||||
element.removeEventListener('load', imageEventTriggered, false);
|
||||
element.removeEventListener('error', imageEventTriggered, false);
|
||||
removeFromElements(element);
|
||||
}
|
||||
|
||||
function imageEventTriggered(event) {
|
||||
removeImageLoadListener(event.target);
|
||||
callback();
|
||||
}
|
||||
|
||||
function mutationObserved(mutations) {
|
||||
callback();
|
||||
|
||||
// deal with async image loads when tags are injected into the page
|
||||
mutations.forEach(addImageLoadListeners);
|
||||
}
|
||||
|
||||
function createMutationObserver(target) {
|
||||
let config = {
|
||||
attributes: true,
|
||||
attributeOldValue: false,
|
||||
characterData: true,
|
||||
characterDataOldValue: false,
|
||||
childList: true,
|
||||
subtree: true
|
||||
};
|
||||
|
||||
let observer = new MutationObserver(mutationObserved);
|
||||
observer.observe(target, config); // eslint-disable-line ghost/ember/no-observers
|
||||
return observer;
|
||||
}
|
||||
|
||||
let imageElements = [];
|
||||
let observer = createMutationObserver(target);
|
||||
|
||||
return {
|
||||
disconnect() {
|
||||
if ('disconnect' in observer) {
|
||||
observer.disconnect(); // eslint-disable-line ghost/ember/no-observers
|
||||
imageElements.forEach(removeImageLoadListener);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_resizeIframe(iframe) {
|
||||
this._resizeDebounce = run.debounce(this, this.__debouncedResizeIframe, iframe, 66);
|
||||
},
|
||||
|
||||
__debouncedResizeIframe(iframe) {
|
||||
iframe.style.height = null;
|
||||
let height = iframe.contentDocument.scrollingElement.scrollHeight;
|
||||
iframe.style.height = `${height}px`;
|
||||
},
|
||||
|
||||
_setupWindowResizeHandler(iframe) {
|
||||
this._windowResizeHandler = run.bind(this, this._resizeIframe, iframe);
|
||||
window.addEventListener('resize', this._windowResizeHandler, {passive: true});
|
||||
},
|
||||
|
||||
_deleteIfEmpty() {
|
||||
if (isBlank(this.payload.html) && !this.convertUrl.isRunning && !this.hasError) {
|
||||
this.deleteCard(NO_CURSOR_MOVEMENT);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{{#koenig-card
|
||||
class="flex flex-column"
|
||||
class="flex flex-column nl2 nr2"
|
||||
isSelected=isSelected
|
||||
isEditing=isEditing
|
||||
selectCard=(action selectCard)
|
||||
|
@ -17,7 +17,7 @@
|
|||
{{#if payload.html}}
|
||||
<div class="kg-card-hover">
|
||||
<div class="koenig-embed-{{payload.type}} flex justify-center relative" data-kg-embed>
|
||||
{{{payload.html}}}
|
||||
<iframe class="bn miw-100" scrolling="no"></iframe>
|
||||
<div class="koenig-card-click-overlay ba b--white" data-kg-overlay></div>
|
||||
</div>
|
||||
|
||||
|
@ -54,4 +54,4 @@
|
|||
onkeydown={{action "urlKeydown"}}>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/koenig-card}}
|
||||
{{/koenig-card}}
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
"ember-cli-cjs-transform": "1.3.0",
|
||||
"ember-cli-code-coverage": "0.4.2",
|
||||
"ember-cli-dependency-checker": "2.1.1",
|
||||
"ember-cli-es6-transform": "^0.0.3",
|
||||
"ember-cli-eslint": "4.2.3",
|
||||
"ember-cli-ghost-spirit": "0.0.6",
|
||||
"ember-cli-htmlbars": "2.0.3",
|
||||
|
@ -115,6 +116,7 @@
|
|||
"mobiledoc-kit": "0.10.21",
|
||||
"normalize.css": "3.0.3",
|
||||
"password-generator": "2.2.0",
|
||||
"reframe.js": "2.2.1",
|
||||
"simplemde": "https://github.com/kevinansfield/simplemde-markdown-editor.git#ghost",
|
||||
"testem": "2.6.0",
|
||||
"top-gh-contribs": "2.0.4",
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -3369,6 +3369,12 @@ ember-cli-dependency-checker@2.1.1:
|
|||
resolve "^1.5.0"
|
||||
semver "^5.3.0"
|
||||
|
||||
ember-cli-es6-transform@^0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-es6-transform/-/ember-cli-es6-transform-0.0.3.tgz#99238305c72f533cc1cd3c85b15c6d7a842b8fdf"
|
||||
dependencies:
|
||||
ember-cli-babel "^6.6.0"
|
||||
|
||||
ember-cli-eslint@4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-eslint/-/ember-cli-eslint-4.2.3.tgz#2844d3f5e8184f19b2d7132ba99eb0b370b55598"
|
||||
|
@ -8632,6 +8638,10 @@ reduce-css-calc@^2.0.0:
|
|||
css-unit-converter "^1.1.1"
|
||||
postcss-value-parser "^3.3.0"
|
||||
|
||||
reframe.js@2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/reframe.js/-/reframe.js-2.2.1.tgz#c4df52c815152b57458843d53d0246cb6b954d59"
|
||||
|
||||
regenerate@^1.2.1:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
|
||||
|
|
Loading…
Reference in New Issue