-
Notifications
You must be signed in to change notification settings - Fork 5
Page designer i frame implementation #129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: new_iframe
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,13 @@ | ||
|
|
||
| { | ||
| "name": "Cloudinary Image Config Form", | ||
| "description": "Configure Image", | ||
| "resources": { | ||
| "scripts": [ | ||
| "https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.2.10/iframeResizer.min.js", | ||
| "/experience/editors/cloudinary/utils.js", | ||
| "/experience/editors/cloudinary/imageForm.js" | ||
| "https://media-library.cloudinary.com/global/all.js", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can this be fetched dynamically from the BM? |
||
| "/experience/editors/cloudinary/imageFormWidget.js" | ||
| ], | ||
| "styles": [ | ||
| "/experience/editors/cloudinary/form.css" | ||
| "/experience/editors/cloudinary/videoFormWidget.css" | ||
| ] | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| 'use strict'; | ||
|
|
||
| var cloudinaryApi = require('*/cartridge/scripts/cloudinary/cloudinaryApi'); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @asad-rafter please move all require statements to local scope |
||
|
|
||
| module.exports.init = function (editor) { | ||
| editor.configuration.put('cloudName', cloudinaryApi.data.getCloudName()); | ||
| editor.configuration.put('apiKey', cloudinaryApi.data.getAPIKey()); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| { | ||
| "name": "Cloudinary Studio Widget", | ||
| "description": "Advanced image editor via Cloudinary Studio Widget", | ||
| "resources": { | ||
| "scripts": [ | ||
| "https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.2.10/iframeResizer.contentWindow.min.js", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. replace with "open-iframe-resizer" https://github.com/Lemick/open-iframe-resizer |
||
| "https://studio-widget.cloudinary.com/latest/all.js", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can this be fetched dynamically from the BM? |
||
| "/experience/editors/cloudinary/studioWidget.js" | ||
| ], | ||
| "styles": [ | ||
| "/experience/editors/cloudinary/studioWidget.css" | ||
| ] | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,13 +5,32 @@ var HashMap = require('dw/util/HashMap'); | |
| var cloudinaryApi = require('*/cartridge/scripts/cloudinary/cloudinaryApi'); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @asad-rafter please move all require statements to local scope |
||
|
|
||
| module.exports.init = function (editor) { | ||
| var conf = new HashMap(); | ||
| conf.put('type', 'video'); | ||
| editor.configuration.put('cloudName', cloudinaryApi.data.getCloudName()); | ||
| editor.configuration.put('cname', cloudinaryApi.data.getCloudinaryCNAME()); | ||
| var videoSelector = PageMgr.getCustomEditor('cloudinary.mediaSelector', conf); | ||
| var adv = PageMgr.getCustomEditor('cloudinary.advancedVideoForm', conf); | ||
| editor.dependencies.put('advBreakout', adv); | ||
| editor.dependencies.put('breakout', videoSelector); | ||
| editor.configuration.put('iFrameEnv', cloudinaryApi.data.getIframeEnv()); | ||
|
|
||
| // Pass default player option values from site preference to the widget | ||
| var currentSite = require('dw/system/Site').getCurrent(); | ||
| var playerOptionsRaw = currentSite.getCustomPreferenceValue('CloudinaryPageDesignerVideoPlayerOptions'); | ||
| var playerOptions = { autoplay: false, muted: false, loop: false, controls: true }; | ||
| if (playerOptionsRaw) { | ||
| try { | ||
| var parsed = JSON.parse(playerOptionsRaw); | ||
| var keys = ['autoplay', 'muted', 'loop', 'controls']; | ||
| for (var i = 0; i < keys.length; i++) { | ||
| var k = keys[i]; | ||
| if (k in parsed) { | ||
| playerOptions[k] = !!parsed[k]; | ||
| } | ||
| } | ||
| } catch (e) { /* keep defaults on parse error */ } | ||
| } | ||
| editor.configuration.put('playerOptions', JSON.stringify(playerOptions)); | ||
|
|
||
| var conf = new HashMap(); | ||
| conf.put('type', 'video'); | ||
| var mediaPicker = PageMgr.getCustomEditor('cloudinary.mediaSelector', conf); | ||
| editor.dependencies.put('mediaPicker', mediaPicker); | ||
|
|
||
| var advancedConfig = PageMgr.getCustomEditor('cloudinary.advancedVideoForm', new HashMap()); | ||
| editor.dependencies.put('advancedConfig', advancedConfig); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,13 @@ | ||
|
|
||
| { | ||
| "name": "Cloudinary Image Config Form", | ||
| "description": "Configure Image", | ||
| "name": "Cloudinary Video Config Form", | ||
| "description": "Configure Video", | ||
| "resources": { | ||
| "scripts": [ | ||
| "https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.2.10/iframeResizer.min.js", | ||
| "/experience/editors/cloudinary/utils.js", | ||
| "/experience/editors/cloudinary/videoForm.js" | ||
| "https://media-library.cloudinary.com/global/all.js", | ||
| "/experience/editors/cloudinary/videoFormWidget.js" | ||
| ], | ||
| "styles": [ | ||
| "/experience/editors/cloudinary/form.css" | ||
| "/experience/editors/cloudinary/videoFormWidget.css" | ||
| ] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,89 @@ | ||
| /** | ||
| * When the parent editor uses the new per-form-factor format | ||
| * ({ formValues: { desktop, mobile, tablet }, posterMode, playerOptions, transformationOverride }) | ||
| * the iframe still expects formValues.video.asset.public_id to render its preview. | ||
| * | ||
| * cldUtils.cleanValue (called inside dehydrate) strips every top-level key except | ||
| * "formValues" and "breakpoints", so anything outside formValues is lost. | ||
| * We therefore map the resolved asset back into formValues.video.asset which is | ||
| * exactly what the iframe reads. | ||
| */ | ||
| function normalizeValueForIframe(value) { | ||
| if (!value || !value.formValues) return value; | ||
| var fv = value.formValues; | ||
|
|
||
| // Already in the old format – nothing to do | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @asad-rafter please use ascii dashes |
||
| if (fv.video && fv.video.asset) return value; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @asad-rafter please use the optional chain expression .? |
||
|
|
||
| // Resolve current asset from the new per-form-factor format | ||
| var entry = fv.desktop || fv.tablet || fv.mobile; | ||
| if (!entry || !entry.asset) return value; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. optional chain expression |
||
|
|
||
| var asset = entry.asset; | ||
|
|
||
| // If a full Advanced config was previously saved, restore it directly. | ||
| // This preserves every overlay field (font family, size, position, colour, | ||
| // text content, image overlay, player customisations, etc.) so the iframe | ||
| // can repopulate its UI exactly as the user left it. | ||
| if (value.advancedConfig) { | ||
| var restored = JSON.parse(JSON.stringify(value.advancedConfig)); | ||
|
|
||
| // Always sync the asset to the current selection in case the user | ||
| // changed the video between Advanced sessions. | ||
| if (!restored.formValues) restored.formValues = {}; | ||
| if (!restored.formValues.video) restored.formValues.video = {}; | ||
| restored.formValues.video.asset = { | ||
| public_id: asset.public_id, | ||
| format: asset.format || '', | ||
| derived: asset.derived || [] | ||
| }; | ||
|
|
||
| // Ensure formValues.video.transStr is populated for the iframe preview. | ||
| // New saves already have it set (injected in the 'done' handler). | ||
| // For old saves (made before that fix), playerConf was stripped by SFCC | ||
| // storage so it won't be present here — fall back to the widget-level | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. replace to ascii dash |
||
| // transformationOverride which is preserved in the parent value. | ||
| if (!restored.formValues.video.transStr) { | ||
| if (restored.playerConf) { | ||
| try { | ||
| var pc = JSON.parse(restored.playerConf); | ||
| if (pc.transStr) { | ||
| restored.formValues.video.transStr = pc.transStr; | ||
| } | ||
| } catch (e) { /* keep empty if playerConf is unparseable */ } | ||
| } | ||
| if (!restored.formValues.video.transStr && value.transformationOverride) { | ||
| restored.formValues.video.transStr = value.transformationOverride; | ||
| } | ||
| } | ||
|
|
||
| return restored; | ||
| } | ||
|
|
||
| // First-time open – no previous Advanced config, start from scratch. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. replace to ascii dash |
||
| return { | ||
| formValues: { | ||
| video: { | ||
| asset: { | ||
| public_id: asset.public_id, | ||
| format: asset.format || '', | ||
| derived: asset.derived || [] | ||
| }, | ||
| transStr: value.transformationOverride || '' | ||
| } | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| (() => { | ||
| subscribe('sfcc:ready', async ({ value, config }) => { | ||
| console.log('[CLD AdvancedVideo] sfcc:ready raw value:', JSON.stringify(value)); | ||
| let iFrame = document.createElement('iframe'); | ||
| let val = encodeURIComponent(JSON.stringify(cldUtils.dehydrate(value))); | ||
| let iframeValue = normalizeValueForIframe(value); | ||
| console.log('[CLD AdvancedVideo] iframeValue (before dehydrate):', JSON.stringify(iframeValue)); | ||
| let dehydrated = cldUtils.dehydrate(iframeValue); | ||
| console.log('[CLD AdvancedVideo] dehydrated (goes into URL):', JSON.stringify(dehydrated)); | ||
| let val = encodeURIComponent(JSON.stringify(dehydrated)); | ||
| iFrame.src = config.iFrameEnv + '/video?cloudName=' + config.cloudName + '&value=' + val; | ||
| iFrame.id = 'video-form'; | ||
| iFrame.setAttribute('frameborder', 0); | ||
|
|
@@ -14,7 +96,9 @@ | |
| let ifrm = document.querySelector('iframe'); | ||
| window.addEventListener('message', (event) => { | ||
| if (event.origin === config.iFrameEnv) { | ||
| handleIframeMessage(event.data, ifrm, value, config); | ||
| // Pass iframeValue (old-format shape) so the 'ready' handler | ||
| // posts back data the iframe can actually parse for its UI. | ||
| handleIframeMessage(event.data, ifrm, iframeValue, config); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @asad-rafter handleIframeMessage is defined with the first 3 params only |
||
| } | ||
| }); | ||
| parentIFrame.getPageInfo((i) => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @asad-rafter use Number.parseFloat instead of parseFloat |
||
|
|
@@ -45,11 +129,29 @@ const handleIframeMessage = (message, ifrm, value = null) => { | |
| break; | ||
| case 'ready': | ||
| value.origin = 'ready'; | ||
| console.log('[CLD AdvancedVideo] posting ready value to iframe:', JSON.stringify(value)); | ||
| ifrm.contentWindow.postMessage(value, '*'); | ||
| break; | ||
| case 'done': | ||
| delete message.action; | ||
| var val = Object.assign({}, message); | ||
| // playerConf.transStr is the authoritative transformation string. | ||
| // formValues.video.transStr is always empty in the iframe's done | ||
| // payload. SFCC strips every top-level key except formValues/ | ||
| // breakpoints before delivering the value to the parent breakout | ||
| // callback, so playerConf itself disappears after storage. | ||
| // Inject transStr into formValues.video NOW, while playerConf is | ||
| // still available locally, so it survives the round-trip. | ||
| if (val.playerConf) { | ||
| try { | ||
| var doneConf = JSON.parse(val.playerConf); | ||
| if (doneConf.transStr) { | ||
| if (!val.formValues) val.formValues = {}; | ||
| if (!val.formValues.video) val.formValues.video = {}; | ||
| val.formValues.video.transStr = doneConf.transStr; | ||
| } | ||
| } catch (e) { /* keep as-is on parse error */ } | ||
| } | ||
| emit({ | ||
| type: 'sfcc:value', | ||
| payload: val | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@asad-rafter please move all require statements to local scope