I’ll build this as a standalone advanced chunk-031-schema-helper.js helper for RX Theme, focused on safe JSON-LD schema creation, validation, insertion, updating, and medical/article/breadcrumb/FAQ schema support.
Below is a strong advanced-level file for:
rx-theme/assets/static-js/static-chunks-js/chunk-031-schema-helper.js
It creates a global helper:
window.RXThemeSchemaHelper
It can generate and inject JSON-LD schema for:
Article, MedicalWebPage, MedicalCondition, FAQPage, BreadcrumbList, WebSite, Organization, Person/Author, ImageObject, HowTo, Speakable, Review, AggregateRating, VideoObject, and generic custom schema.
/*!
* RX Theme - Chunk 031 Schema Helper
* File: assets/static-js/static-chunks-js/chunk-031-schema-helper.js
* Version: 1.0.0
*
* Purpose:
* Advanced JSON-LD schema helper for RX Theme.
* Adds structured data safely, avoids duplicates, validates required fields,
* supports medical article schema, FAQ schema, breadcrumb schema, website schema,
* organization schema, author schema, image schema, video schema, and custom schema.
*/
(function RXThemeSchemaHelperFactory(window, document) {
'use strict';
if (!window || !document) {
return;
}
var RX_SCHEMA_GLOBAL = 'RXThemeSchemaHelper';
var RX_SCHEMA_VERSION = '1.0.0';
var RX_SCHEMA_SCRIPT_PREFIX = 'rx-schema-jsonld-';
var RX_SCHEMA_DEFAULT_CONTEXT = 'https://schema.org';
if (window[RX_SCHEMA_GLOBAL] && window[RX_SCHEMA_GLOBAL].version) {
return;
}
var RXSchemaUtils = {
isObject: function isObject(value) {
return Object.prototype.toString.call(value) === '[object Object]';
},
isArray: function isArray(value) {
return Array.isArray(value);
},
isString: function isString(value) {
return typeof value === 'string';
},
isNumber: function isNumber(value) {
return typeof value === 'number' && !Number.isNaN(value);
},
isBoolean: function isBoolean(value) {
return typeof value === 'boolean';
},
isFunction: function isFunction(value) {
return typeof value === 'function';
},
isEmpty: function isEmpty(value) {
if (value === null || value === undefined) {
return true;
}
if (typeof value === 'string' && value.trim() === '') {
return true;
}
if (Array.isArray(value) && value.length === 0) {
return true;
}
if (this.isObject(value) && Object.keys(value).length === 0) {
return true;
}
return false;
},
trim: function trim(value) {
return typeof value === 'string' ? value.trim() : value;
},
removeHtml: function removeHtml(value) {
if (!value || typeof value !== 'string') {
return '';
}
var div = document.createElement('div');
div.innerHTML = value;
return (div.textContent || div.innerText || '').replace(/\s+/g, ' ').trim();
},
truncate: function truncate(value, maxLength) {
if (!value || typeof value !== 'string') {
return '';
}
var cleanValue = value.trim();
var limit = maxLength || 300;
if (cleanValue.length <= limit) {
return cleanValue;
}
return cleanValue.substring(0, limit).replace(/\s+\S*$/, '') + '...';
},
normalizeUrl: function normalizeUrl(url) {
if (!url || typeof url !== 'string') {
return '';
}
var value = url.trim();
if (!value) {
return '';
}
try {
return new URL(value, window.location.origin).href;
} catch (error) {
return value;
}
},
isValidUrl: function isValidUrl(url) {
if (!url || typeof url !== 'string') {
return false;
}
try {
new URL(url, window.location.origin);
return true;
} catch (error) {
return false;
}
},
normalizeDate: function normalizeDate(value) {
if (!value) {
return '';
}
if (value instanceof Date && !Number.isNaN(value.getTime())) {
return value.toISOString();
}
if (typeof value === 'string') {
var date = new Date(value);
if (!Number.isNaN(date.getTime())) {
return date.toISOString();
}
return value;
}
return '';
},
toArray: function toArray(value) {
if (Array.isArray(value)) {
return value;
}
if (value === null || value === undefined || value === '') {
return [];
}
return [value];
},
uniqueArray: function uniqueArray(arr) {
var output = [];
var seen = {};
this.toArray(arr).forEach(function eachItem(item) {
var key = typeof item === 'string' ? item : JSON.stringify(item);
if (!seen[key]) {
seen[key] = true;
output.push(item);
}
});
return output;
},
deepMerge: function deepMerge(target) {
var output = target && this.isObject(target) ? target : {};
var self = this;
for (var i = 1; i < arguments.length; i += 1) {
var source = arguments[i];
if (!self.isObject(source)) {
continue;
}
Object.keys(source).forEach(function eachKey(key) {
var sourceValue = source[key];
if (self.isObject(sourceValue)) {
if (!self.isObject(output[key])) {
output[key] = {};
}
output[key] = self.deepMerge(output[key], sourceValue);
} else if (Array.isArray(sourceValue)) {
output[key] = sourceValue.slice();
} else {
output[key] = sourceValue;
}
});
}
return output;
},
cleanObject: function cleanObject(obj) {
var self = this;
if (Array.isArray(obj)) {
return obj
.map(function mapItem(item) {
return self.cleanObject(item);
})
.filter(function filterItem(item) {
return !self.isEmpty(item);
});
}
if (!self.isObject(obj)) {
return obj;
}
var cleaned = {};
Object.keys(obj).forEach(function eachKey(key) {
var value = obj[key];
if (value === null || value === undefined || value === '') {
return;
}
if (Array.isArray(value)) {
var cleanArray = self.cleanObject(value);
if (cleanArray.length > 0) {
cleaned[key] = cleanArray;
}
return;
}
if (self.isObject(value)) {
var cleanChild = self.cleanObject(value);
if (Object.keys(cleanChild).length > 0) {
cleaned[key] = cleanChild;
}
return;
}
cleaned[key] = value;
});
return cleaned;
},
slugify: function slugify(value) {
if (!value || typeof value !== 'string') {
return '';
}
return value
.toLowerCase()
.replace(/<[^>]*>/g, '')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.substring(0, 80);
},
getCanonicalUrl: function getCanonicalUrl() {
var canonical = document.querySelector('link[rel="canonical"]');
if (canonical && canonical.href) {
return canonical.href;
}
return window.location.href.split('#')[0];
},
getMetaContent: function getMetaContent(selectors) {
var list = this.toArray(selectors);
for (var i = 0; i < list.length; i += 1) {
var element = document.querySelector(list[i]);
if (element && element.getAttribute('content')) {
return element.getAttribute('content').trim();
}
}
return '';
},
getTitle: function getTitle() {
var ogTitle = this.getMetaContent([
'meta[property="og:title"]',
'meta[name="twitter:title"]'
]);
if (ogTitle) {
return ogTitle;
}
if (document.title) {
return document.title.trim();
}
var h1 = document.querySelector('h1');
return h1 ? h1.textContent.trim() : '';
},
getDescription: function getDescription() {
return this.getMetaContent([
'meta[name="description"]',
'meta[property="og:description"]',
'meta[name="twitter:description"]'
]);
},
getImage: function getImage() {
return this.getMetaContent([
'meta[property="og:image"]',
'meta[name="twitter:image"]'
]);
},
getLanguage: function getLanguage() {
var html = document.documentElement;
if (html && html.getAttribute('lang')) {
return html.getAttribute('lang');
}
return 'en';
},
getSiteName: function getSiteName() {
return this.getMetaContent([
'meta[property="og:site_name"]',
'meta[name="application-name"]'
]) || window.location.hostname.replace(/^www\./, '');
},
parseJson: function parseJson(value, fallback) {
try {
return JSON.parse(value);
} catch (error) {
return fallback || null;
}
},
safeStringify: function safeStringify(value) {
try {
return JSON.stringify(value, null, 2);
} catch (error) {
return '';
}
},
dispatchEvent: function dispatchEvent(name, detail) {
try {
window.dispatchEvent(
new CustomEvent(name, {
detail: detail || {}
})
);
} catch (error) {
var event = document.createEvent('CustomEvent');
event.initCustomEvent(name, true, true, detail || {});
window.dispatchEvent(event);
}
}
};
var RXSchemaDefaults = {
organization: {
name: 'RxHarun',
url: window.location.origin,
logo: '',
sameAs: []
},
author: {
name: 'RxHarun',
url: window.location.origin,
type: 'Person'
},
publisher: {
name: 'RxHarun',
logo: ''
},
medical: {
medicalSpecialty: 'MedicalSpecialty',
audience: 'Patient',
reviewingAuthority: '',
lastReviewed: ''
}
};
var RXSchemaState = {
inserted: {},
registry: {},
warnings: [],
errors: [],
config: {
debug: false,
autoClean: true,
allowOverwrite: true,
useGraph: false,
idBase: RXSchemaUtils.getCanonicalUrl()
}
};
function logDebug() {
if (!RXSchemaState.config.debug || !window.console) {
return;
}
window.console.log.apply(window.console, ['[RX Schema Helper]'].concat(Array.prototype.slice.call(arguments)));
}
function logWarn(message, data) {
RXSchemaState.warnings.push({
message: message,
data: data || null,
time: new Date().toISOString()
});
if (RXSchemaState.config.debug && window.console) {
window.console.warn('[RX Schema Helper]', message, data || '');
}
}
function logError(message, data) {
RXSchemaState.errors.push({
message: message,
data: data || null,
time: new Date().toISOString()
});
if (window.console) {
window.console.error('[RX Schema Helper]', message, data || '');
}
}
function createId(type, key) {
var cleanType = RXSchemaUtils.slugify(type || 'schema');
var cleanKey = RXSchemaUtils.slugify(key || RXSchemaUtils.getCanonicalUrl());
return RX_SCHEMA_SCRIPT_PREFIX + cleanType + '-' + cleanKey;
}
function createSchemaId(fragment) {
var base = RXSchemaState.config.idBase || RXSchemaUtils.getCanonicalUrl();
return base.replace(/#.*$/, '') + '#' + RXSchemaUtils.slugify(fragment || 'schema');
}
function ensureContext(schema) {
if (!RXSchemaUtils.isObject(schema)) {
return schema;
}
if (!schema['@context']) {
schema['@context'] = RX_SCHEMA_DEFAULT_CONTEXT;
}
return schema;
}
function ensureType(schema, type) {
if (!RXSchemaUtils.isObject(schema)) {
return schema;
}
if (!schema['@type'] && type) {
schema['@type'] = type;
}
return schema;
}
function normalizePerson(person) {
if (!person) {
return null;
}
if (RXSchemaUtils.isString(person)) {
return {
'@type': 'Person',
name: person
};
}
if (!RXSchemaUtils.isObject(person)) {
return null;
}
return RXSchemaUtils.cleanObject({
'@type': person.type || person['@type'] || 'Person',
'@id': person.id || person['@id'] || '',
name: person.name || '',
url: RXSchemaUtils.normalizeUrl(person.url || ''),
image: person.image || '',
jobTitle: person.jobTitle || '',
description: person.description || '',
sameAs: RXSchemaUtils.toArray(person.sameAs),
affiliation: person.affiliation || '',
worksFor: person.worksFor || ''
});
}
function normalizeOrganization(org) {
if (!org) {
return null;
}
if (RXSchemaUtils.isString(org)) {
return {
'@type': 'Organization',
name: org
};
}
if (!RXSchemaUtils.isObject(org)) {
return null;
}
var logo = org.logo || org.image || '';
var output = {
'@type': org.type || org['@type'] || 'Organization',
'@id': org.id || org['@id'] || createSchemaId('organization'),
name: org.name || RXSchemaDefaults.organization.name,
url: RXSchemaUtils.normalizeUrl(org.url || RXSchemaDefaults.organization.url),
logo: logo ? createImageObject(logo, org.name || RXSchemaDefaults.organization.name + ' Logo') : '',
image: org.image || '',
description: org.description || '',
sameAs: RXSchemaUtils.uniqueArray(org.sameAs || RXSchemaDefaults.organization.sameAs),
contactPoint: org.contactPoint || '',
address: org.address || '',
email: org.email || '',
telephone: org.telephone || ''
};
return RXSchemaUtils.cleanObject(output);
}
function createImageObject(image, caption, width, height) {
if (!image) {
return null;
}
if (RXSchemaUtils.isObject(image)) {
return RXSchemaUtils.cleanObject({
'@type': 'ImageObject',
'@id': image.id || image['@id'] || '',
url: RXSchemaUtils.normalizeUrl(image.url || image.src || ''),
contentUrl: RXSchemaUtils.normalizeUrl(image.contentUrl || image.url || image.src || ''),
caption: image.caption || caption || '',
width: image.width || width || '',
height: image.height || height || '',
representativeOfPage: image.representativeOfPage || ''
});
}
if (RXSchemaUtils.isString(image)) {
return RXSchemaUtils.cleanObject({
'@type': 'ImageObject',
url: RXSchemaUtils.normalizeUrl(image),
contentUrl: RXSchemaUtils.normalizeUrl(image),
caption: caption || '',
width: width || '',
height: height || ''
});
}
return null;
}
function createRatingObject(rating) {
if (!rating) {
return null;
}
if (!RXSchemaUtils.isObject(rating)) {
return null;
}
return RXSchemaUtils.cleanObject({
'@type': 'AggregateRating',
ratingValue: rating.ratingValue || rating.value || '',
bestRating: rating.bestRating || 5,
worstRating: rating.worstRating || 1,
ratingCount: rating.ratingCount || rating.count || '',
reviewCount: rating.reviewCount || ''
});
}
function createReviewObject(review) {
if (!review) {
return null;
}
if (!RXSchemaUtils.isObject(review)) {
return null;
}
return RXSchemaUtils.cleanObject({
'@type': 'Review',
author: normalizePerson(review.author || ''),
datePublished: RXSchemaUtils.normalizeDate(review.datePublished || review.date || ''),
reviewBody: RXSchemaUtils.removeHtml(review.reviewBody || review.body || ''),
name: review.name || '',
reviewRating: review.reviewRating
? {
'@type': 'Rating',
ratingValue: review.reviewRating.ratingValue || review.reviewRating.value || '',
bestRating: review.reviewRating.bestRating || 5,
worstRating: review.reviewRating.worstRating || 1
}
: ''
});
}
function validateSchema(schema) {
var result = {
valid: true,
warnings: [],
errors: []
};
if (!RXSchemaUtils.isObject(schema)) {
result.valid = false;
result.errors.push('Schema must be an object.');
return result;
}
if (!schema['@context']) {
result.warnings.push('Missing @context. It will be added automatically.');
}
if (!schema['@type'] && !schema['@graph']) {
result.valid = false;
result.errors.push('Missing @type or @graph.');
}
if (schema.url && !RXSchemaUtils.isValidUrl(schema.url)) {
result.warnings.push('Schema URL may be invalid.');
}
if (schema.image && RXSchemaUtils.isString(schema.image) && !RXSchemaUtils.isValidUrl(schema.image)) {
result.warnings.push('Schema image URL may be invalid.');
}
return result;
}
function injectSchema(schema, options) {
var config = RXSchemaUtils.deepMerge(
{
id: '',
key: '',
type: '',
overwrite: RXSchemaState.config.allowOverwrite,
clean: RXSchemaState.config.autoClean
},
options || {}
);
if (!RXSchemaUtils.isObject(schema)) {
logError('Cannot inject schema. Schema is not an object.', schema);
return false;
}
var finalSchema = RXSchemaUtils.deepMerge({}, schema);
ensureContext(finalSchema);
if (config.clean) {
finalSchema = RXSchemaUtils.cleanObject(finalSchema);
}
var validation = validateSchema(finalSchema);
if (!validation.valid) {
logError('Schema validation failed.', validation.errors);
return false;
}
validation.warnings.forEach(function eachWarning(warning) {
logWarn(warning, finalSchema);
});
var scriptId = config.id || createId(config.type || finalSchema['@type'] || 'schema', config.key || finalSchema.name || finalSchema.url || location.pathname);
var oldScript = document.getElementById(scriptId);
if (oldScript && !config.overwrite) {
logWarn('Schema already exists and overwrite is disabled.', scriptId);
return false;
}
if (oldScript && oldScript.parentNode) {
oldScript.parentNode.removeChild(oldScript);
}
var script = document.createElement('script');
script.type = 'application/ld+json';
script.id = scriptId;
script.setAttribute('data-rx-schema', 'true');
script.setAttribute('data-rx-schema-version', RX_SCHEMA_VERSION);
var json = RXSchemaUtils.safeStringify(finalSchema);
if (!json) {
logError('Could not stringify schema.', finalSchema);
return false;
}
script.text = json;
document.head.appendChild(script);
RXSchemaState.inserted[scriptId] = {
id: scriptId,
type: finalSchema['@type'] || 'Graph',
schema: finalSchema,
insertedAt: new Date().toISOString()
};
RXSchemaUtils.dispatchEvent('rx:schema:inserted', {
id: scriptId,
schema: finalSchema
});
logDebug('Schema inserted:', scriptId, finalSchema);
return scriptId;
}
function removeSchema(id) {
if (!id) {
return false;
}
var script = document.getElementById(id);
if (!script) {
return false;
}
script.parentNode.removeChild(script);
delete RXSchemaState.inserted[id];
RXSchemaUtils.dispatchEvent('rx:schema:removed', {
id: id
});
return true;
}
function clearSchemas() {
var scripts = document.querySelectorAll('script[data-rx-schema="true"]');
scripts.forEach(function eachScript(script) {
if (script && script.parentNode) {
script.parentNode.removeChild(script);
}
});
RXSchemaState.inserted = {};
RXSchemaUtils.dispatchEvent('rx:schema:cleared', {});
}
function createWebSiteSchema(data) {
var input = data || {};
var siteName = input.name || RXSchemaUtils.getSiteName();
var siteUrl = RXSchemaUtils.normalizeUrl(input.url || window.location.origin);
var schema = {
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': 'WebSite',
'@id': input.id || createSchemaId('website'),
name: siteName,
alternateName: input.alternateName || '',
url: siteUrl,
description: input.description || RXSchemaUtils.getDescription(),
inLanguage: input.inLanguage || RXSchemaUtils.getLanguage(),
publisher: normalizeOrganization(input.publisher || RXSchemaDefaults.organization),
potentialAction: input.searchUrl
? {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
urlTemplate: input.searchUrl
},
'query-input': input.queryInput || 'required name=search_term_string'
}
: ''
};
return RXSchemaUtils.cleanObject(schema);
}
function createOrganizationSchema(data) {
var schema = normalizeOrganization(data || RXSchemaDefaults.organization);
ensureContext(schema);
return RXSchemaUtils.cleanObject(schema);
}
function createPersonSchema(data) {
var schema = normalizePerson(data || RXSchemaDefaults.author);
ensureContext(schema);
return RXSchemaUtils.cleanObject(schema);
}
function createArticleSchema(data) {
var input = data || {};
var title = input.headline || input.title || RXSchemaUtils.getTitle();
var description = input.description || RXSchemaUtils.getDescription();
var canonical = RXSchemaUtils.normalizeUrl(input.url || RXSchemaUtils.getCanonicalUrl());
var image = input.image || RXSchemaUtils.getImage();
var schema = {
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': input.type || input.articleType || 'Article',
'@id': input.id || createSchemaId('article'),
mainEntityOfPage: {
'@type': 'WebPage',
'@id': canonical
},
headline: RXSchemaUtils.truncate(title, 110),
alternativeHeadline: input.alternativeHeadline || '',
description: RXSchemaUtils.truncate(RXSchemaUtils.removeHtml(description), 500),
image: image ? createImageObject(image, title, input.imageWidth, input.imageHeight) : '',
url: canonical,
datePublished: RXSchemaUtils.normalizeDate(input.datePublished || input.publishedTime || ''),
dateModified: RXSchemaUtils.normalizeDate(input.dateModified || input.modifiedTime || input.datePublished || ''),
author: normalizePerson(input.author || RXSchemaDefaults.author),
publisher: normalizeOrganization(input.publisher || RXSchemaDefaults.organization),
articleSection: input.articleSection || input.section || '',
articleBody: input.articleBody ? RXSchemaUtils.removeHtml(input.articleBody) : '',
wordCount: input.wordCount || '',
keywords: RXSchemaUtils.toArray(input.keywords),
inLanguage: input.inLanguage || RXSchemaUtils.getLanguage(),
isAccessibleForFree: input.isAccessibleForFree !== undefined ? input.isAccessibleForFree : true,
copyrightHolder: normalizeOrganization(input.copyrightHolder || input.publisher || RXSchemaDefaults.organization),
copyrightYear: input.copyrightYear || new Date().getFullYear(),
speakable: input.speakable || '',
about: input.about || '',
mentions: input.mentions || '',
reviewedBy: input.reviewedBy ? normalizePerson(input.reviewedBy) : '',
lastReviewed: RXSchemaUtils.normalizeDate(input.lastReviewed || '')
};
return RXSchemaUtils.cleanObject(schema);
}
function createMedicalWebPageSchema(data) {
var input = data || {};
var canonical = RXSchemaUtils.normalizeUrl(input.url || RXSchemaUtils.getCanonicalUrl());
var title = input.name || input.headline || RXSchemaUtils.getTitle();
var description = input.description || RXSchemaUtils.getDescription();
var schema = {
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': 'MedicalWebPage',
'@id': input.id || createSchemaId('medical-web-page'),
name: RXSchemaUtils.truncate(title, 110),
headline: RXSchemaUtils.truncate(title, 110),
description: RXSchemaUtils.truncate(RXSchemaUtils.removeHtml(description), 500),
url: canonical,
mainEntityOfPage: canonical,
inLanguage: input.inLanguage || RXSchemaUtils.getLanguage(),
datePublished: RXSchemaUtils.normalizeDate(input.datePublished || ''),
dateModified: RXSchemaUtils.normalizeDate(input.dateModified || input.datePublished || ''),
lastReviewed: RXSchemaUtils.normalizeDate(input.lastReviewed || ''),
reviewedBy: input.reviewedBy ? normalizePerson(input.reviewedBy) : '',
author: normalizePerson(input.author || RXSchemaDefaults.author),
publisher: normalizeOrganization(input.publisher || RXSchemaDefaults.organization),
medicalAudience: input.medicalAudience || {
'@type': 'MedicalAudience',
audienceType: input.audienceType || 'Patient'
},
specialty: input.specialty || input.medicalSpecialty || '',
about: input.about || '',
mainEntity: input.mainEntity || '',
primaryImageOfPage: input.image ? createImageObject(input.image, title) : '',
breadcrumb: input.breadcrumb || ''
};
return RXSchemaUtils.cleanObject(schema);
}
function createMedicalConditionSchema(data) {
var input = data || {};
var name = input.name || input.conditionName || RXSchemaUtils.getTitle();
var schema = {
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': 'MedicalCondition',
'@id': input.id || createSchemaId('medical-condition-' + name),
name: name,
alternateName: RXSchemaUtils.toArray(input.alternateName),
description: RXSchemaUtils.truncate(RXSchemaUtils.removeHtml(input.description || RXSchemaUtils.getDescription()), 1000),
code: input.code || '',
guideline: input.guideline || '',
associatedAnatomy: input.associatedAnatomy || '',
possibleTreatment: input.possibleTreatment || '',
possibleComplication: input.possibleComplication || '',
signOrSymptom: input.signOrSymptom || input.symptoms || '',
riskFactor: input.riskFactor || '',
cause: input.cause || input.causes || '',
differentialDiagnosis: input.differentialDiagnosis || '',
epidemiology: input.epidemiology || '',
expectedPrognosis: input.expectedPrognosis || '',
naturalProgression: input.naturalProgression || '',
pathophysiology: input.pathophysiology || '',
primaryPrevention: input.primaryPrevention || '',
secondaryPrevention: input.secondaryPrevention || '',
typicalTest: input.typicalTest || input.diagnosticTests || '',
drug: input.drug || input.drugs || '',
recognizingAuthority: input.recognizingAuthority || '',
relevantSpecialty: input.relevantSpecialty || input.specialty || '',
study: input.study || '',
url: RXSchemaUtils.normalizeUrl(input.url || RXSchemaUtils.getCanonicalUrl())
};
return RXSchemaUtils.cleanObject(schema);
}
function createFAQSchema(faqs) {
var list = RXSchemaUtils.toArray(faqs);
var mainEntity = list
.map(function mapFAQ(item) {
if (!item) {
return null;
}
var question = '';
var answer = '';
if (RXSchemaUtils.isString(item)) {
return null;
}
if (RXSchemaUtils.isObject(item)) {
question = item.question || item.q || item.name || '';
answer = item.answer || item.a || item.text || item.acceptedAnswer || '';
}
question = RXSchemaUtils.removeHtml(question);
answer = RXSchemaUtils.removeHtml(answer);
if (!question || !answer) {
return null;
}
return {
'@type': 'Question',
name: question,
acceptedAnswer: {
'@type': 'Answer',
text: answer
}
};
})
.filter(Boolean);
return RXSchemaUtils.cleanObject({
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': 'FAQPage',
'@id': createSchemaId('faq'),
mainEntity: mainEntity
});
}
function createBreadcrumbSchema(items) {
var list = RXSchemaUtils.toArray(items);
if (!list.length) {
list = extractBreadcrumbsFromDOM();
}
var itemListElement = list
.map(function mapCrumb(item, index) {
if (!item) {
return null;
}
if (RXSchemaUtils.isString(item)) {
return {
'@type': 'ListItem',
position: index + 1,
name: RXSchemaUtils.removeHtml(item)
};
}
if (RXSchemaUtils.isObject(item)) {
return RXSchemaUtils.cleanObject({
'@type': 'ListItem',
position: item.position || index + 1,
name: RXSchemaUtils.removeHtml(item.name || item.title || ''),
item: item.url || item.item ? RXSchemaUtils.normalizeUrl(item.url || item.item) : ''
});
}
return null;
})
.filter(Boolean);
return RXSchemaUtils.cleanObject({
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': 'BreadcrumbList',
'@id': createSchemaId('breadcrumb'),
itemListElement: itemListElement
});
}
function extractBreadcrumbsFromDOM() {
var selectors = [
'.breadcrumb a',
'.breadcrumbs a',
'.rank-math-breadcrumb a',
'.yoast-breadcrumb a',
'nav[aria-label="breadcrumb"] a'
];
var links = [];
selectors.some(function eachSelector(selector) {
var found = document.querySelectorAll(selector);
if (found && found.length) {
found.forEach(function eachLink(link) {
links.push({
name: link.textContent.trim(),
url: link.href
});
});
return true;
}
return false;
});
if (!links.length) {
links.push({
name: 'Home',
url: window.location.origin
});
links.push({
name: RXSchemaUtils.getTitle(),
url: RXSchemaUtils.getCanonicalUrl()
});
}
return links;
}
function createHowToSchema(data) {
var input = data || {};
var steps = RXSchemaUtils.toArray(input.steps).map(function mapStep(step, index) {
if (RXSchemaUtils.isString(step)) {
return {
'@type': 'HowToStep',
position: index + 1,
name: 'Step ' + (index + 1),
text: RXSchemaUtils.removeHtml(step)
};
}
if (RXSchemaUtils.isObject(step)) {
return RXSchemaUtils.cleanObject({
'@type': 'HowToStep',
position: step.position || index + 1,
name: step.name || step.title || 'Step ' + (index + 1),
text: RXSchemaUtils.removeHtml(step.text || step.description || ''),
url: step.url || '',
image: step.image ? createImageObject(step.image, step.name || '') : ''
});
}
return null;
}).filter(Boolean);
var schema = {
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': 'HowTo',
'@id': input.id || createSchemaId('how-to'),
name: input.name || RXSchemaUtils.getTitle(),
description: RXSchemaUtils.removeHtml(input.description || RXSchemaUtils.getDescription()),
image: input.image ? createImageObject(input.image, input.name || RXSchemaUtils.getTitle()) : '',
totalTime: input.totalTime || '',
estimatedCost: input.estimatedCost || '',
supply: input.supply || '',
tool: input.tool || '',
step: steps
};
return RXSchemaUtils.cleanObject(schema);
}
function createVideoSchema(data) {
var input = data || {};
var schema = {
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': 'VideoObject',
'@id': input.id || createSchemaId('video-' + (input.name || RXSchemaUtils.getTitle())),
name: input.name || RXSchemaUtils.getTitle(),
description: RXSchemaUtils.removeHtml(input.description || RXSchemaUtils.getDescription()),
thumbnailUrl: RXSchemaUtils.toArray(input.thumbnailUrl || input.thumbnail || RXSchemaUtils.getImage()).map(function mapThumb(url) {
return RXSchemaUtils.normalizeUrl(url);
}),
uploadDate: RXSchemaUtils.normalizeDate(input.uploadDate || input.datePublished || ''),
duration: input.duration || '',
contentUrl: RXSchemaUtils.normalizeUrl(input.contentUrl || ''),
embedUrl: RXSchemaUtils.normalizeUrl(input.embedUrl || ''),
publisher: normalizeOrganization(input.publisher || RXSchemaDefaults.organization),
author: normalizePerson(input.author || RXSchemaDefaults.author),
transcript: input.transcript || ''
};
return RXSchemaUtils.cleanObject(schema);
}
function createSpeakableSchema(data) {
var input = data || {};
var schema = {
'@type': 'SpeakableSpecification',
cssSelector: RXSchemaUtils.toArray(input.cssSelector || ['h1', '.entry-content p']),
xpath: RXSchemaUtils.toArray(input.xpath)
};
return RXSchemaUtils.cleanObject(schema);
}
function createSearchActionSchema(searchUrl) {
if (!searchUrl) {
searchUrl = window.location.origin + '/?s={search_term_string}';
}
return {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
urlTemplate: searchUrl
},
'query-input': 'required name=search_term_string'
};
}
function createGraphSchema(items) {
var graph = RXSchemaUtils.toArray(items)
.filter(function filterItem(item) {
return RXSchemaUtils.isObject(item);
})
.map(function mapItem(item) {
var clean = RXSchemaUtils.deepMerge({}, item);
delete clean['@context'];
return RXSchemaUtils.cleanObject(clean);
});
return RXSchemaUtils.cleanObject({
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@graph': graph
});
}
function extractFAQFromDOM(options) {
var config = RXSchemaUtils.deepMerge(
{
itemSelector: '.rx-faq-item, .faq-item, details',
questionSelector: '.rx-faq-question, .faq-question, summary, h2, h3',
answerSelector: '.rx-faq-answer, .faq-answer, p, div',
maxItems: 20
},
options || {}
);
var items = document.querySelectorAll(config.itemSelector);
var faqs = [];
items.forEach(function eachFAQ(item) {
if (faqs.length >= config.maxItems) {
return;
}
var questionElement = item.querySelector(config.questionSelector);
var answerElement = item.querySelector(config.answerSelector);
if (!questionElement || !answerElement) {
return;
}
var question = RXSchemaUtils.removeHtml(questionElement.textContent || '');
var answer = RXSchemaUtils.removeHtml(answerElement.textContent || '');
if (question && answer && question !== answer) {
faqs.push({
question: question,
answer: answer
});
}
});
return faqs;
}
function detectPageType() {
var body = document.body;
if (!body) {
return 'webpage';
}
var classes = body.className || '';
if (/\bsingle-post\b/.test(classes)) {
return 'article';
}
if (/\bpage\b/.test(classes)) {
return 'webpage';
}
if (/\barchive\b/.test(classes)) {
return 'collection';
}
if (/\bsearch\b/.test(classes)) {
return 'search';
}
if (/\bhome\b|\bfront-page\b/.test(classes)) {
return 'home';
}
return 'webpage';
}
function autoBuildBasicSchemas(options) {
var config = RXSchemaUtils.deepMerge(
{
website: true,
organization: true,
breadcrumb: true,
article: true,
faq: true,
medical: false,
graph: true,
data: {}
},
options || {}
);
var schemas = [];
var pageType = detectPageType();
if (config.website) {
schemas.push(createWebSiteSchema(config.data.website || {}));
}
if (config.organization) {
schemas.push(createOrganizationSchema(config.data.organization || RXSchemaDefaults.organization));
}
if (config.breadcrumb) {
schemas.push(createBreadcrumbSchema(config.data.breadcrumb || []));
}
if (config.article && pageType === 'article') {
schemas.push(createArticleSchema(config.data.article || {}));
}
if (config.medical) {
schemas.push(createMedicalWebPageSchema(config.data.medical || {}));
}
if (config.faq) {
var faqs = config.data.faq || extractFAQFromDOM();
if (faqs.length) {
schemas.push(createFAQSchema(faqs));
}
}
if (config.graph) {
return injectSchema(createGraphSchema(schemas), {
type: 'graph',
key: 'auto-basic'
});
}
schemas.forEach(function eachSchema(schema) {
injectSchema(schema, {
type: schema['@type'],
key: schema['@id'] || schema.name
});
});
return schemas;
}
function readExistingJsonLd() {
var scripts = document.querySelectorAll('script[type="application/ld+json"]');
var schemas = [];
scripts.forEach(function eachScript(script) {
var json = RXSchemaUtils.parseJson(script.textContent || script.innerText || '', null);
if (json) {
schemas.push({
id: script.id || '',
schema: json
});
}
});
return schemas;
}
function findSchemaByType(type) {
if (!type) {
return [];
}
var existing = readExistingJsonLd();
return existing.filter(function filterSchema(item) {
var schema = item.schema;
if (schema['@type'] === type) {
return true;
}
if (Array.isArray(schema['@graph'])) {
return schema['@graph'].some(function someGraph(node) {
return node['@type'] === type;
});
}
return false;
});
}
function updateConfig(config) {
if (!RXSchemaUtils.isObject(config)) {
return RXSchemaState.config;
}
RXSchemaState.config = RXSchemaUtils.deepMerge(RXSchemaState.config, config);
return RXSchemaState.config;
}
function setDefaults(defaults) {
if (!RXSchemaUtils.isObject(defaults)) {
return RXSchemaDefaults;
}
RXSchemaUtils.deepMerge(RXSchemaDefaults, defaults);
return RXSchemaDefaults;
}
function registerSchemaBuilder(name, builder) {
if (!name || !RXSchemaUtils.isFunction(builder)) {
return false;
}
RXSchemaState.registry[name] = builder;
return true;
}
function buildRegisteredSchema(name, data) {
if (!name || !RXSchemaState.registry[name]) {
logWarn('Registered schema builder not found.', name);
return null;
}
try {
return RXSchemaState.registry[name](data || {}, RXThemeSchemaHelper);
} catch (error) {
logError('Registered schema builder failed.', {
name: name,
error: error.message
});
return null;
}
}
function injectRegisteredSchema(name, data, options) {
var schema = buildRegisteredSchema(name, data);
if (!schema) {
return false;
}
return injectSchema(schema, RXSchemaUtils.deepMerge(
{
type: name,
key: data && data.key ? data.key : name
},
options || {}
));
}
function getStatus() {
return {
version: RX_SCHEMA_VERSION,
inserted: RXSchemaState.inserted,
warnings: RXSchemaState.warnings,
errors: RXSchemaState.errors,
config: RXSchemaState.config,
existingJsonLdCount: readExistingJsonLd().length
};
}
var RXThemeSchemaHelper = {
version: RX_SCHEMA_VERSION,
utils: RXSchemaUtils,
defaults: RXSchemaDefaults,
state: RXSchemaState,
config: updateConfig,
setDefaults: setDefaults,
createId: createId,
createSchemaId: createSchemaId,
ensureContext: ensureContext,
ensureType: ensureType,
normalizePerson: normalizePerson,
normalizeOrganization: normalizeOrganization,
createImageObject: createImageObject,
createRatingObject: createRatingObject,
createReviewObject: createReviewObject,
createWebSiteSchema: createWebSiteSchema,
createOrganizationSchema: createOrganizationSchema,
createPersonSchema: createPersonSchema,
createArticleSchema: createArticleSchema,
createMedicalWebPageSchema: createMedicalWebPageSchema,
createMedicalConditionSchema: createMedicalConditionSchema,
createFAQSchema: createFAQSchema,
createBreadcrumbSchema: createBreadcrumbSchema,
createHowToSchema: createHowToSchema,
createVideoSchema: createVideoSchema,
createSpeakableSchema: createSpeakableSchema,
createSearchActionSchema: createSearchActionSchema,
createGraphSchema: createGraphSchema,
extractFAQFromDOM: extractFAQFromDOM,
extractBreadcrumbsFromDOM: extractBreadcrumbsFromDOM,
detectPageType: detectPageType,
validateSchema: validateSchema,
inject: injectSchema,
remove: removeSchema,
clear: clearSchemas,
autoBuild: autoBuildBasicSchemas,
readExisting: readExistingJsonLd,
findByType: findSchemaByType,
register: registerSchemaBuilder,
buildRegistered: buildRegisteredSchema,
injectRegistered: injectRegisteredSchema,
status: getStatus
};
registerSchemaBuilder('MedicalArticle', function MedicalArticleBuilder(data) {
var article = createArticleSchema(
RXSchemaUtils.deepMerge(
{
type: 'MedicalScholarlyArticle'
},
data || {}
)
);
article.about = data && data.condition ? createMedicalConditionSchema(data.condition) : article.about;
article.reviewedBy = data && data.reviewedBy ? normalizePerson(data.reviewedBy) : article.reviewedBy;
article.lastReviewed = data && data.lastReviewed ? RXSchemaUtils.normalizeDate(data.lastReviewed) : article.lastReviewed;
article.medicalAudience = data && data.medicalAudience
? data.medicalAudience
: {
'@type': 'MedicalAudience',
audienceType: 'Patient'
};
return RXSchemaUtils.cleanObject(article);
});
registerSchemaBuilder('DoctorProfile', function DoctorProfileBuilder(data) {
var input = data || {};
return RXSchemaUtils.cleanObject({
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': 'Physician',
'@id': input.id || createSchemaId('doctor-profile-' + (input.name || 'doctor')),
name: input.name || '',
description: input.description || '',
image: input.image ? createImageObject(input.image, input.name || '') : '',
url: RXSchemaUtils.normalizeUrl(input.url || RXSchemaUtils.getCanonicalUrl()),
medicalSpecialty: input.medicalSpecialty || input.specialty || '',
hospitalAffiliation: input.hospitalAffiliation || '',
alumniOf: input.alumniOf || '',
knowsAbout: RXSchemaUtils.toArray(input.knowsAbout),
sameAs: RXSchemaUtils.toArray(input.sameAs),
address: input.address || '',
telephone: input.telephone || '',
email: input.email || '',
availableService: input.availableService || '',
openingHoursSpecification: input.openingHoursSpecification || ''
});
});
registerSchemaBuilder('MedicalClinic', function MedicalClinicBuilder(data) {
var input = data || {};
return RXSchemaUtils.cleanObject({
'@context': RX_SCHEMA_DEFAULT_CONTEXT,
'@type': 'MedicalClinic',
'@id': input.id || createSchemaId('medical-clinic-' + (input.name || 'clinic')),
name: input.name || '',
description: input.description || '',
url: RXSchemaUtils.normalizeUrl(input.url || RXSchemaUtils.getCanonicalUrl()),
image: input.image ? createImageObject(input.image, input.name || '') : '',
logo: input.logo ? createImageObject(input.logo, input.name || '') : '',
medicalSpecialty: input.medicalSpecialty || '',
address: input.address || '',
geo: input.geo || '',
telephone: input.telephone || '',
email: input.email || '',
priceRange: input.priceRange || '',
openingHoursSpecification: input.openingHoursSpecification || '',
sameAs: RXSchemaUtils.toArray(input.sameAs),
aggregateRating: createRatingObject(input.aggregateRating || ''),
review: RXSchemaUtils.toArray(input.review).map(createReviewObject).filter(Boolean)
});
});
window[RX_SCHEMA_GLOBAL] = RXThemeSchemaHelper;
RXSchemaUtils.dispatchEvent('rx:schema:ready', {
helper: RXThemeSchemaHelper,
version: RX_SCHEMA_VERSION
});
logDebug('Ready.');
})(window, document);
Example use in another JS file:
window.RXThemeSchemaHelper.inject(
window.RXThemeSchemaHelper.createArticleSchema({
headline: 'High Blood Pressure: Causes, Symptoms, Diagnosis and Treatment',
description: 'A simple medical article about high blood pressure.',
datePublished: '2026-05-18',
dateModified: '2026-05-18',
author: {
name: 'RxHarun',
url: 'https://rxharun.com'
},
publisher: {
name: 'RxHarun',
url: 'https://rxharun.com',
logo: 'https://rxharun.com/logo.png'
}
}),
{
type: 'Article',
key: 'high-blood-pressure'
}
);
Best enqueue/defer idea for this helper:
wp_enqueue_script(
'rx-schema-helper',
get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-031-schema-helper.js',
array(),
RX_THEME_VERSION,
true
);
wp_script_add_data( 'rx-schema-helper', 'defer', true );