This commit is contained in:
Markos Gogoulos
2026-02-11 21:34:52 +02:00
parent f226c2fe1e
commit 86f4484c36
11 changed files with 144 additions and 44 deletions

View File

@@ -41,20 +41,36 @@ class text_filter extends \core_filters\text_filter {
// 2. Handle Auto-convert URLs if enabled
if (get_config('filter_mediacms', 'enableautoconvert')) {
// First, protect text-only links from being converted
// by temporarily replacing them with placeholders
$textlink_placeholders = [];
$textlink_pattern = '/<a\s+[^>]*data-mediacms-textlink=["\']true["\'][^>]*>.*?<\/a>/is';
$newtext = preg_replace_callback($textlink_pattern, function($matches) use (&$textlink_placeholders) {
$placeholder = '###MEDIACMS_TEXTLINK_' . count($textlink_placeholders) . '###';
$textlink_placeholders[$placeholder] = $matches[0];
return $placeholder;
}, $newtext);
// Regex for MediaCMS view URLs: https://domain/view?m=TOKEN
// We need to be careful to match the configured domain
$parsed_url = parse_url($mediacmsurl);
$host = preg_quote($parsed_url['host'] ?? '', '/');
$scheme = preg_quote($parsed_url['scheme'] ?? 'https', '/');
// Allow http or https, and optional path prefix
$path_prefix = preg_quote(rtrim($parsed_url['path'] ?? '', '/'), '/');
// Pattern: https://HOST/PREFIX/view?m=TOKEN
// Also handle /embed?m=TOKEN
$pattern_url = '/(' . $scheme . ':\/\/' . $host . $path_prefix . '\/(view|embed)\?m=([a-zA-Z0-9]+)(?:&[^\s<]*)?)/';
$newtext = preg_replace_callback($pattern_url, [$this, 'callback_url'], $newtext);
// Restore protected text-only links
foreach ($textlink_placeholders as $placeholder => $original) {
$newtext = str_replace($placeholder, $original, $newtext);
}
}
return $newtext;
@@ -71,7 +87,17 @@ class text_filter extends \core_filters\text_filter {
* Callback for URLs
*/
public function callback_url($matches) {
// matches[0] is the full matched string
// matches[1] is full URL, matches[3] is token
// Check if this URL is inside a text-only link
// by looking at the context around the match
$fullmatch = $matches[0];
// If this is already inside an <a> tag with data-mediacms-textlink="true",
// return the original URL unchanged
// We'll check this in the main filter method instead
$token = $matches[3];
return $this->generate_iframe($token);
}

View File

@@ -12,6 +12,7 @@ A TinyMCE editor plugin for Moodle that provides media embedding capabilities wi
# i've noticed that this fails, so this should work: npx grunt amd
5. To test the output:
cp lib/editor/tiny/plugins/mediacms/* ../../lms-plugins/mediacms-moodle/tiny/mediacms/ -r
cp * ../../../../../../../lms-plugins/mediacms-moodle/tiny/mediacms/ -r
6. Then copy to Moodle server and purge caches

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,3 @@
define("tiny_mediacms/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_imagecms_urlentrysubmit",imageBrowser:".openimagecmsbrowser",addUrl:".tiny_imagecms_addurl",deleteImage:".tiny_imagecms_deleteicon"},elements:{form:"form.tiny_imagecms_form",alignSettings:".tiny_imagecms_button",alt:".tiny_imagecms_altentry",altWarning:".tiny_imagecms_altwarning",height:".tiny_imagecms_heightentry",width:".tiny_imagecms_widthentry",url:".tiny_imagecms_urlentry",urlWarning:".tiny_imagecms_urlwarning",size:".tiny_imagecms_size",presentation:".tiny_imagecms_presentation",constrain:".tiny_imagecms_constrain",customStyle:".tiny_imagecms_customstyle",preview:".tiny_imagecms_preview",previewBox:".tiny_imagecms_preview_box",loaderIcon:".tiny_imagecms_loader",loaderIconContainer:".tiny_imagecms_loader_container",insertImage:".tiny_imagecms_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_imagecms_dropzone_container",fileInput:"#tiny_imagecms_fileinput",fileNameLabel:".tiny_imagecms_filename",sizeOriginal:".tiny_imagecms_sizeoriginal",sizeCustom:".tiny_imagecms_sizecustom",properties:".tiny_imagecms_properties"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_mediacms_submit",mediaBrowser:".openmediacmsbrowser"},elements:{form:"form.tiny_mediacms_form",source:".tiny_mediacms_source",track:".tiny_mediacms_track",mediaSource:".tiny_mediacms_media_source",linkSource:".tiny_mediacms_link_source",linkSize:".tiny_mediacms_link_size",posterSource:".tiny_mediacms_poster_source",posterSize:".tiny_mediacms_poster_size",displayOptions:".tiny_mediacms_display_options",name:".tiny_mediacms_name_entry",title:".tiny_mediacms_title_entry",url:".tiny_mediacms_url_entry",width:".tiny_mediacms_width_entry",height:".tiny_mediacms_height_entry",trackSource:".tiny_mediacms_track_source",trackKind:".tiny_mediacms_track_kind_entry",trackLabel:".tiny_mediacms_track_label_entry",trackLang:".tiny_mediacms_track_lang_entry",trackDefault:".tiny_mediacms_track_default",mediaControl:".tiny_mediacms_controls",mediaAutoplay:".tiny_mediacms_autoplay",mediaMute:".tiny_mediacms_mute",mediaLoop:".tiny_mediacms_loop",advancedSettings:".tiny_mediacms_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}},IFRAME:{actions:{remove:'[data-action="remove"]'},elements:{form:"form.tiny_iframecms_form",url:".tiny_iframecms_url",urlWarning:".tiny_iframecms_url_warning",showTitle:".tiny_iframecms_showtitle",linkTitle:".tiny_iframecms_linktitle",showRelated:".tiny_iframecms_showrelated",showUserAvatar:".tiny_iframecms_showuseravatar",responsive:".tiny_iframecms_responsive",startAt:".tiny_iframecms_startat",startAtEnabled:".tiny_iframecms_startat_enabled",aspectRatio:".tiny_iframecms_aspectratio",width:".tiny_iframecms_width",height:".tiny_iframecms_height",preview:".tiny_iframecms_preview",previewContainer:".tiny_iframecms_preview_container",tabs:".tiny_iframecms_tabs",tabUrlBtn:".tiny_iframecms_tab_url_btn",tabIframeLibraryBtn:".tiny_iframecms_tab_iframe_library_btn",paneUrl:".tiny_iframecms_pane_url",paneIframeLibrary:".tiny_iframecms_pane_iframe_library",iframeLibraryContainer:".tiny_iframecms_iframe_library_container",iframeLibraryPlaceholder:".tiny_iframecms_iframe_library_placeholder",iframeLibraryLoading:".tiny_iframecms_iframe_library_loading",iframeLibraryFrame:".tiny_iframecms_iframe_library_frame"},aspectRatios:{"16:9":{width:560,height:315},"4:3":{width:560,height:420},"1:1":{width:400,height:400},custom:null}}},_exports.default}));
define("tiny_mediacms/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_imagecms_urlentrysubmit",imageBrowser:".openimagecmsbrowser",addUrl:".tiny_imagecms_addurl",deleteImage:".tiny_imagecms_deleteicon"},elements:{form:"form.tiny_imagecms_form",alignSettings:".tiny_imagecms_button",alt:".tiny_imagecms_altentry",altWarning:".tiny_imagecms_altwarning",height:".tiny_imagecms_heightentry",width:".tiny_imagecms_widthentry",url:".tiny_imagecms_urlentry",urlWarning:".tiny_imagecms_urlwarning",size:".tiny_imagecms_size",presentation:".tiny_imagecms_presentation",constrain:".tiny_imagecms_constrain",customStyle:".tiny_imagecms_customstyle",preview:".tiny_imagecms_preview",previewBox:".tiny_imagecms_preview_box",loaderIcon:".tiny_imagecms_loader",loaderIconContainer:".tiny_imagecms_loader_container",insertImage:".tiny_imagecms_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_imagecms_dropzone_container",fileInput:"#tiny_imagecms_fileinput",fileNameLabel:".tiny_imagecms_filename",sizeOriginal:".tiny_imagecms_sizeoriginal",sizeCustom:".tiny_imagecms_sizecustom",properties:".tiny_imagecms_properties"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_mediacms_submit",mediaBrowser:".openmediacmsbrowser"},elements:{form:"form.tiny_mediacms_form",source:".tiny_mediacms_source",track:".tiny_mediacms_track",mediaSource:".tiny_mediacms_media_source",linkSource:".tiny_mediacms_link_source",linkSize:".tiny_mediacms_link_size",posterSource:".tiny_mediacms_poster_source",posterSize:".tiny_mediacms_poster_size",displayOptions:".tiny_mediacms_display_options",name:".tiny_mediacms_name_entry",title:".tiny_mediacms_title_entry",url:".tiny_mediacms_url_entry",width:".tiny_mediacms_width_entry",height:".tiny_mediacms_height_entry",trackSource:".tiny_mediacms_track_source",trackKind:".tiny_mediacms_track_kind_entry",trackLabel:".tiny_mediacms_track_label_entry",trackLang:".tiny_mediacms_track_lang_entry",trackDefault:".tiny_mediacms_track_default",mediaControl:".tiny_mediacms_controls",mediaAutoplay:".tiny_mediacms_autoplay",mediaMute:".tiny_mediacms_mute",mediaLoop:".tiny_mediacms_loop",advancedSettings:".tiny_mediacms_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}},IFRAME:{actions:{remove:'[data-action="remove"]'},elements:{form:"form.tiny_iframecms_form",url:".tiny_iframecms_url",urlWarning:".tiny_iframecms_url_warning",showTitle:".tiny_iframecms_showtitle",linkTitle:".tiny_iframecms_linktitle",showRelated:".tiny_iframecms_showrelated",showUserAvatar:".tiny_iframecms_showuseravatar",responsive:".tiny_iframecms_responsive",textLinkOnly:".tiny_iframecms_textlinkonly",startAt:".tiny_iframecms_startat",startAtEnabled:".tiny_iframecms_startat_enabled",aspectRatio:".tiny_iframecms_aspectratio",width:".tiny_iframecms_width",height:".tiny_iframecms_height",preview:".tiny_iframecms_preview",previewContainer:".tiny_iframecms_preview_container",tabs:".tiny_iframecms_tabs",tabUrlBtn:".tiny_iframecms_tab_url_btn",tabIframeLibraryBtn:".tiny_iframecms_tab_iframe_library_btn",paneUrl:".tiny_iframecms_pane_url",paneIframeLibrary:".tiny_iframecms_pane_iframe_library",iframeLibraryContainer:".tiny_iframecms_iframe_library_container",iframeLibraryPlaceholder:".tiny_iframecms_iframe_library_placeholder",iframeLibraryLoading:".tiny_iframecms_iframe_library_loading",iframeLibraryFrame:".tiny_iframecms_iframe_library_frame"},aspectRatios:{"16:9":{width:560,height:315},"4:3":{width:560,height:420},"1:1":{width:400,height:400},custom:null}}},_exports.default}));
//# sourceMappingURL=selectors.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -237,6 +237,7 @@ export default class IframeEmbed {
showRelated: getDefault('showRelated'),
showUserAvatar: getDefault('showUserAvatar'),
responsive: data.responsive !== false,
textLinkOnly: data.textLinkOnly || false,
startAtEnabled: data.startAtEnabled || false,
startAt: data.startAt || '0:00',
width: data.width || 560,
@@ -350,6 +351,8 @@ export default class IframeEmbed {
).checked,
responsive: form.querySelector(Selectors.IFRAME.elements.responsive)
.checked,
textLinkOnly: form.querySelector(Selectors.IFRAME.elements.textLinkOnly)
.checked,
startAtEnabled: form.querySelector(
Selectors.IFRAME.elements.startAtEnabled,
).checked,
@@ -371,7 +374,7 @@ export default class IframeEmbed {
}
/**
* Generate the iframe HTML.
* Generate the iframe HTML or text link.
*
* @param {Object} values - Form values
* @returns {Promise<string>} Generated HTML
@@ -382,6 +385,32 @@ export default class IframeEmbed {
return '';
}
// If user selected "text link only", generate a link instead of iframe
if (values.textLinkOnly) {
// Build the view URL (not embed URL) for the link
let viewUrl;
if (parsed.isGeneric) {
viewUrl = parsed.rawUrl;
} else {
viewUrl = `${parsed.baseUrl}/view?m=${parsed.videoId}`;
}
// Use the full URL as the link text
// Escape HTML entities in the URL for safe display
const escapeHtml = (str) => {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
};
const linkText = escapeHtml(viewUrl);
const hrefUrl = viewUrl.replace(/"/g, '&quot;');
// Add data attribute to mark this as a text-only link
// This prevents the filter from converting it to an LTI launch iframe
return `<p><a href="${hrefUrl}" target="_blank" data-mediacms-textlink="true">${linkText}</a></p>`;
}
const embedUrl = this.buildEmbedUrl(parsed, values);
// Calculate aspect ratio values for CSS
@@ -448,21 +477,49 @@ export default class IframeEmbed {
urlInput.value = embedUrl;
}
// Show a scaled preview
const previewWidth = Math.min(values.width, 400);
const scale = previewWidth / values.width;
const previewHeight = Math.round(values.height * scale);
// If text link only is selected, show link preview
if (values.textLinkOnly) {
let viewUrl;
if (parsed.isGeneric) {
viewUrl = parsed.rawUrl;
} else {
viewUrl = `${parsed.baseUrl}/view?m=${parsed.videoId}`;
}
previewContainer.innerHTML = `
<iframe
src="${embedUrl}"
width="${previewWidth}"
height="${previewHeight}"
frameborder="0"
allowfullscreen
style="max-width: 100%;">
</iframe>
`;
// Escape HTML entities for safe display
const escapeHtml = (str) => {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
};
const linkText = escapeHtml(viewUrl);
const hrefUrl = viewUrl.replace(/"/g, '&quot;');
previewContainer.innerHTML = `
<div class="alert alert-info">
<strong>Text link preview:</strong><br>
<a href="${hrefUrl}" target="_blank" data-mediacms-textlink="true">${linkText}</a>
<br><small class="text-muted mt-2 d-block">This link will not be auto-converted by the MediaCMS filter.</small>
</div>
`;
} else {
// Show a scaled preview
const previewWidth = Math.min(values.width, 400);
const scale = previewWidth / values.width;
const previewHeight = Math.round(values.height * scale);
previewContainer.innerHTML = `
<iframe
src="${embedUrl}"
width="${previewWidth}"
height="${previewHeight}"
frameborder="0"
allowfullscreen
style="max-width: 100%;">
</iframe>
`;
}
}
/**
@@ -609,6 +666,11 @@ export default class IframeEmbed {
this.handleInputChange(root, false),
);
// Text link only checkbox - doesn't affect URL, only output format
form.querySelector(Selectors.IFRAME.elements.textLinkOnly).addEventListener('change', () =>
this.handleInputChange(root, false),
);
// Start at time input - should update URL field
form.querySelector(Selectors.IFRAME.elements.startAt).addEventListener(
'input',
@@ -695,8 +757,15 @@ export default class IframeEmbed {
// Iframe library event listeners
this.registerIframeLibraryEventListeners(root);
// Since My Media tab is now default/active, load it immediately on modal open
setTimeout(() => this.handleIframeLibraryTabClick(root), 100);
// If editing, Configure tab is active - update preview immediately
// If inserting, My Media tab is active - load the library
if (this.isUpdating) {
// When editing, Configure tab is already active, just update preview
setTimeout(() => this.updatePreview(root), 100);
} else {
// When inserting, load My Media library tab
setTimeout(() => this.handleIframeLibraryTabClick(root), 100);
}
}
/**

View File

@@ -132,6 +132,7 @@ export default {
showRelated: '.tiny_iframecms_showrelated',
showUserAvatar: '.tiny_iframecms_showuseravatar',
responsive: '.tiny_iframecms_responsive',
textLinkOnly: '.tiny_iframecms_textlinkonly',
startAt: '.tiny_iframecms_startat',
startAtEnabled: '.tiny_iframecms_startat_enabled',
aspectRatio: '.tiny_iframecms_aspectratio',

View File

@@ -124,6 +124,7 @@ $string['linktitle'] = 'Link title';
$string['showrelated'] = 'Show related';
$string['showuseravatar'] = 'Show user avatar';
$string['responsive'] = 'Responsive';
$string['textlinkonly'] = 'Insert text link only';
$string['startat'] = 'Start at';
$string['aspectratio'] = 'Aspect Ratio';
$string['aspectratio_16_9'] = '16:9';

View File

@@ -31,19 +31,19 @@
<form class="tiny_iframecms_form" id="{{elementid}}_tiny_iframecms_form">
<!-- Tab Navigation -->
<ul class="nav nav-tabs mb-3 tiny_iframecms_tabs" role="tablist">
<!-- My Media tab (first, active by default) -->
<!-- My Media tab (first, active by default when not editing) -->
<li class="nav-item" role="presentation">
<button class="nav-link active tiny_iframecms_tab_iframe_library_btn" id="{{elementid}}_tab_iframe_library"
<button class="nav-link {{^isupdating}}active{{/isupdating}} tiny_iframecms_tab_iframe_library_btn" id="{{elementid}}_tab_iframe_library"
data-bs-toggle="tab" data-bs-target="#{{elementid}}_pane_iframe_library"
type="button" role="tab" aria-controls="{{elementid}}_pane_iframe_library" aria-selected="true">
type="button" role="tab" aria-controls="{{elementid}}_pane_iframe_library" aria-selected="{{^isupdating}}true{{/isupdating}}{{#isupdating}}false{{/isupdating}}">
{{#str}} tabvideolibraryiframe, tiny_mediacms {{/str}}
</button>
</li>
<!-- Configure tab (second, hidden initially) -->
<li class="nav-item tiny_iframecms_tab_url_item" role="presentation" style="display: none;">
<button class="nav-link tiny_iframecms_tab_url_btn" id="{{elementid}}_tab_url"
<!-- Configure tab (second, hidden initially when inserting, visible when editing) -->
<li class="nav-item tiny_iframecms_tab_url_item" role="presentation" {{^isupdating}}style="display: none;"{{/isupdating}}>
<button class="nav-link {{#isupdating}}active{{/isupdating}} tiny_iframecms_tab_url_btn" id="{{elementid}}_tab_url"
data-bs-toggle="tab" data-bs-target="#{{elementid}}_pane_url"
type="button" role="tab" aria-controls="{{elementid}}_pane_url" aria-selected="false">
type="button" role="tab" aria-controls="{{elementid}}_pane_url" aria-selected="{{#isupdating}}true{{/isupdating}}{{^isupdating}}false{{/isupdating}}">
{{#str}} tabembedurl, tiny_mediacms {{/str}}
</button>
</li>
@@ -51,8 +51,8 @@
<!-- Tab Content -->
<div class="tab-content">
<!-- Tab 1: My Media (now first and active) -->
<div class="tab-pane fade show active tiny_iframecms_pane_iframe_library" id="{{elementid}}_pane_iframe_library" role="tabpanel" aria-labelledby="{{elementid}}_tab_iframe_library">
<!-- Tab 1: My Media (active when not editing) -->
<div class="tab-pane fade {{^isupdating}}show active{{/isupdating}} tiny_iframecms_pane_iframe_library" id="{{elementid}}_pane_iframe_library" role="tabpanel" aria-labelledby="{{elementid}}_tab_iframe_library">
<div class="tiny_iframecms_iframe_library_container" style="min-height: 500px;">
<div class="tiny_iframecms_iframe_library_placeholder text-center py-5">
<p class="text-muted">{{#str}} libraryloading, tiny_mediacms {{/str}}</p>
@@ -73,8 +73,8 @@
</div>
</div>
<!-- Tab 2: Configure (now second) -->
<div class="tab-pane fade tiny_iframecms_pane_url" id="{{elementid}}_pane_url" role="tabpanel" aria-labelledby="{{elementid}}_tab_url">
<!-- Tab 2: Configure (active when editing) -->
<div class="tab-pane fade {{#isupdating}}show active{{/isupdating}} tiny_iframecms_pane_url" id="{{elementid}}_pane_url" role="tabpanel" aria-labelledby="{{elementid}}_tab_url">
<div class="container-fluid p-0">
<div class="row">
<!-- Left column: Options only (URL field removed) -->
@@ -117,11 +117,6 @@
{{#str}} insertiframe, tiny_mediacms {{/str}}
{{/isupdating}}
</button>
{{#isupdating}}
<button type="button" class="btn btn-danger tiny_iframecms_remove_btn" data-action="remove">
{{#str}} removeiframe, tiny_mediacms {{/str}}
</button>
{{/isupdating}}
<button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel, moodle {{/str}}</button>
{{/footer}}

View File

@@ -62,26 +62,33 @@
</div>
<div class="col-6">
<div class="form-check mb-2">
<input type="checkbox" class="form-check-input tiny_iframecms_showuseravatar"
<input type="checkbox" class="form-check-input tiny_iframecms_showuseravatar"
id="{{elementid}}_showuseravatar" {{#showUserAvatar}}checked{{/showUserAvatar}}>
<label class="form-check-label" for="{{elementid}}_showuseravatar">
{{#str}} showuseravatar, tiny_mediacms {{/str}}
</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" class="form-check-input tiny_iframecms_responsive"
<input type="checkbox" class="form-check-input tiny_iframecms_responsive"
id="{{elementid}}_responsive" {{#responsive}}checked{{/responsive}}>
<label class="form-check-label" for="{{elementid}}_responsive">
{{#str}} responsive, tiny_mediacms {{/str}}
</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" class="form-check-input tiny_iframecms_textlinkonly"
id="{{elementid}}_textlinkonly" {{#textLinkOnly}}checked{{/textLinkOnly}}>
<label class="form-check-label" for="{{elementid}}_textlinkonly">
{{#str}} textlinkonly, tiny_mediacms {{/str}}
</label>
</div>
<div class="form-check mb-2 d-flex align-items-center">
<input type="checkbox" class="form-check-input tiny_iframecms_startat_enabled"
<input type="checkbox" class="form-check-input tiny_iframecms_startat_enabled"
id="{{elementid}}_startat_enabled" {{#startAtEnabled}}checked{{/startAtEnabled}}>
<label class="form-check-label ms-2 me-2" for="{{elementid}}_startat_enabled">
{{#str}} startat, tiny_mediacms {{/str}}
</label>
<input type="text" class="form-control form-control-sm tiny_iframecms_startat"
<input type="text" class="form-control form-control-sm tiny_iframecms_startat"
id="{{elementid}}_startat" value="{{startAt}}" placeholder="0:00" style="width: 70px;">
</div>
</div>