[mirotalk] - refactor: extract renderRoomTemplate into standalone roomTemplate.js
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
# ====================================================
|
||||
# MiroTalk P2P v.1.8.30 - Environment Configuration
|
||||
# MiroTalk P2P v.1.8.31 - Environment Configuration
|
||||
# ====================================================
|
||||
|
||||
# App environment
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/**
|
||||
* ==============================================
|
||||
* MiroTalk P2P v.1.8.30 - Configuration File
|
||||
* MiroTalk P2P v.1.8.31 - Configuration File
|
||||
* ==============================================
|
||||
*
|
||||
* This file is the central configuration source.
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ dependencies: {
|
||||
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
|
||||
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.8.30
|
||||
* @version 1.8.31
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mirotalk",
|
||||
"version": "1.8.30",
|
||||
"version": "1.8.31",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mirotalk",
|
||||
"version": "1.8.30",
|
||||
"version": "1.8.31",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@mattermost/client": "11.6.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mirotalk",
|
||||
"version": "1.8.30",
|
||||
"version": "1.8.31",
|
||||
"description": "A free WebRTC browser-based video call",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ let brand = {
|
||||
},
|
||||
about: {
|
||||
imageUrl: '../images/mirotalk-logo.gif',
|
||||
title: 'WebRTC P2P v1.8.30',
|
||||
title: 'WebRTC P2P v1.8.31',
|
||||
html: `
|
||||
<button
|
||||
id="support-button"
|
||||
|
||||
+3
-48
@@ -15,7 +15,7 @@
|
||||
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
|
||||
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.8.30
|
||||
* @version 1.8.31
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -15752,7 +15752,7 @@ function showAbout() {
|
||||
Swal.fire({
|
||||
background: swBg,
|
||||
position: 'center',
|
||||
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.30',
|
||||
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.31',
|
||||
imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
|
||||
customClass: { image: 'img-about' },
|
||||
html: renderRoomTemplate('tpl-about-modal', {
|
||||
@@ -16883,49 +16883,4 @@ function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render HTML template with provided data
|
||||
* @param {string} templateId - ID of the <template> element
|
||||
* @param {object} data - Data to populate the template
|
||||
* @param {object} data.text - Key-value pairs for text content (data-template-text)
|
||||
* @param {object} data.html - Key-value pairs for HTML content (data-template-html
|
||||
* @param {object} data.attrs - Key-value pairs for attributes (data-template-attr-*)
|
||||
* @returns {string} Rendered HTML string
|
||||
*/
|
||||
function renderRoomTemplate(templateId, { text = {}, html = {}, attrs = {} } = {}) {
|
||||
const template = document.getElementById(templateId);
|
||||
if (!template || !template.content) return '';
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.appendChild(template.content.cloneNode(true));
|
||||
|
||||
wrapper.querySelectorAll('*').forEach((element) => {
|
||||
element.getAttributeNames().forEach((name) => {
|
||||
if (!name.startsWith('data-template-attr-')) return;
|
||||
|
||||
const attrName = name.replace('data-template-attr-', '');
|
||||
const key = element.getAttribute(name);
|
||||
const value = attrs[key];
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
element.removeAttribute(attrName);
|
||||
} else {
|
||||
element.setAttribute(attrName, value);
|
||||
}
|
||||
|
||||
element.removeAttribute(name);
|
||||
});
|
||||
});
|
||||
|
||||
wrapper.querySelectorAll('[data-template-text]').forEach((element) => {
|
||||
const key = element.getAttribute('data-template-text');
|
||||
element.textContent = text[key] ?? '';
|
||||
});
|
||||
|
||||
wrapper.querySelectorAll('[data-template-html]').forEach((element) => {
|
||||
const key = element.getAttribute('data-template-html');
|
||||
element.innerHTML = html[key] ?? '';
|
||||
});
|
||||
|
||||
return wrapper.innerHTML.trim();
|
||||
}
|
||||
// renderRoomTemplate is defined in roomTemplate.js
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Render HTML template with provided data
|
||||
* @param {string} templateId - ID of the <template> element
|
||||
* @param {object} data - Data to populate the template
|
||||
* @param {object} data.text - Key-value pairs for text content (data-template-text)
|
||||
* @param {object} data.html - Key-value pairs for HTML content (data-template-html
|
||||
* @param {object} data.attrs - Key-value pairs for attributes (data-template-attr-*)
|
||||
* @returns {string} Rendered HTML string
|
||||
*/
|
||||
function renderRoomTemplate(templateId, { text = {}, html = {}, attrs = {} } = {}) {
|
||||
const template = document.getElementById(templateId);
|
||||
if (!template || !template.content) return '';
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.appendChild(template.content.cloneNode(true));
|
||||
|
||||
wrapper.querySelectorAll('*').forEach((element) => {
|
||||
element.getAttributeNames().forEach((name) => {
|
||||
if (!name.startsWith('data-template-attr-')) return;
|
||||
|
||||
const attrName = name.replace('data-template-attr-', '');
|
||||
const key = element.getAttribute(name);
|
||||
const value = attrs[key];
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
element.removeAttribute(attrName);
|
||||
} else {
|
||||
element.setAttribute(attrName, value);
|
||||
}
|
||||
|
||||
element.removeAttribute(name);
|
||||
});
|
||||
});
|
||||
|
||||
wrapper.querySelectorAll('[data-template-text]').forEach((element) => {
|
||||
const key = element.getAttribute('data-template-text');
|
||||
element.textContent = text[key] ?? '';
|
||||
});
|
||||
|
||||
wrapper.querySelectorAll('[data-template-html]').forEach((element) => {
|
||||
const key = element.getAttribute('data-template-html');
|
||||
element.innerHTML = html[key] ?? '';
|
||||
});
|
||||
|
||||
return wrapper.innerHTML.trim();
|
||||
}
|
||||
@@ -1843,6 +1843,7 @@ Note</textarea
|
||||
<script defer src="../js/fixWebmDuration.js"></script>
|
||||
<script defer src="../js/screenReader.js"></script>
|
||||
<script defer src="../js/wakeLock.js"></script>
|
||||
<script defer src="../js/roomTemplate.js"></script>
|
||||
<script defer src="../js/client.js"></script>
|
||||
<script defer src="../js/networkStats.js"></script>
|
||||
<script defer src="../js/speechRecognition.js"></script>
|
||||
|
||||
@@ -7,66 +7,7 @@ const path = require('path');
|
||||
const vm = require('vm');
|
||||
const { JSDOM } = require('jsdom');
|
||||
|
||||
function extractNamedFunction(source, functionName) {
|
||||
const signature = `function ${functionName}`;
|
||||
const start = source.indexOf(signature);
|
||||
|
||||
if (start === -1) {
|
||||
throw new Error(`Unable to find ${functionName} in source file`);
|
||||
}
|
||||
|
||||
const paramsStart = source.indexOf('(', start);
|
||||
let paramsDepth = 0;
|
||||
let bodyStart = -1;
|
||||
|
||||
for (let index = paramsStart; index < source.length; index++) {
|
||||
const char = source[index];
|
||||
|
||||
if (char === '(') paramsDepth++;
|
||||
if (char === ')') paramsDepth--;
|
||||
|
||||
if (paramsDepth === 0) {
|
||||
bodyStart = source.indexOf('{', index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bodyStart === -1) {
|
||||
throw new Error(`Unable to find ${functionName} body in source file`);
|
||||
}
|
||||
|
||||
let depth = 0;
|
||||
|
||||
for (let index = bodyStart; index < source.length; index++) {
|
||||
const char = source[index];
|
||||
|
||||
if (char === '{') depth++;
|
||||
if (char === '}') depth--;
|
||||
|
||||
if (depth === 0) {
|
||||
return source.slice(start, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unable to parse ${functionName} from source file`);
|
||||
}
|
||||
|
||||
function loadNamedFunctionSource(functionName, relativePaths) {
|
||||
for (const relativePath of relativePaths) {
|
||||
const sourcePath = path.join(__dirname, '..', ...relativePath.split('/'));
|
||||
const source = fs.readFileSync(sourcePath, 'utf8');
|
||||
|
||||
try {
|
||||
return extractNamedFunction(source, functionName);
|
||||
} catch (error) {
|
||||
if (!error.message.includes(`Unable to find ${functionName}`)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find ${functionName} in source file`);
|
||||
}
|
||||
const templateSource = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'roomTemplate.js'), 'utf8');
|
||||
|
||||
describe('test-RoomTemplates', () => {
|
||||
let renderRoomTemplate;
|
||||
@@ -76,9 +17,9 @@ describe('test-RoomTemplates', () => {
|
||||
const dom = new JSDOM('<!doctype html><html><body></body></html>');
|
||||
document = dom.window.document;
|
||||
|
||||
const functionSource = loadNamedFunctionSource('renderRoomTemplate', ['public/js/client.js']);
|
||||
|
||||
renderRoomTemplate = vm.runInNewContext(`(${functionSource})`, { document });
|
||||
const context = vm.createContext({ document, module: {}, exports: {} });
|
||||
vm.runInContext(templateSource, context);
|
||||
renderRoomTemplate = context.renderRoomTemplate;
|
||||
});
|
||||
|
||||
it('preserves empty string attributes used by placeholder options', () => {
|
||||
|
||||
Reference in New Issue
Block a user