Add coexistence checks to all enqueue methods to prevent loading both React and Grid.js assets simultaneously. Changes: - ReactAdmin.php: Only enqueue React assets when ?react=1 - Init.php: Skip Grid.js when React active on admin pages - Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0 - Customer.php, Product.php, License.php: Add coexistence checks Now the toggle between Classic and React versions works correctly. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
195 lines
4.3 KiB
JavaScript
195 lines
4.3 KiB
JavaScript
/**
|
|
* All phrasing content elements.
|
|
*
|
|
* @see https://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#phrasing-content-0
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Record<string,SemanticElementDefinition>} ContentSchema
|
|
*/
|
|
|
|
/**
|
|
* @typedef SemanticElementDefinition
|
|
* @property {string[]} [attributes] Content attributes
|
|
* @property {ContentSchema} [children] Content attributes
|
|
*/
|
|
|
|
/**
|
|
* All text-level semantic elements.
|
|
*
|
|
* @see https://html.spec.whatwg.org/multipage/text-level-semantics.html
|
|
*
|
|
* @type {ContentSchema}
|
|
*/
|
|
const textContentSchema = {
|
|
strong: {},
|
|
em: {},
|
|
s: {},
|
|
del: {},
|
|
ins: {},
|
|
a: {
|
|
attributes: ['href', 'target', 'rel', 'id']
|
|
},
|
|
code: {},
|
|
abbr: {
|
|
attributes: ['title']
|
|
},
|
|
sub: {},
|
|
sup: {},
|
|
br: {},
|
|
small: {},
|
|
// To do: fix blockquote.
|
|
// cite: {},
|
|
q: {
|
|
attributes: ['cite']
|
|
},
|
|
dfn: {
|
|
attributes: ['title']
|
|
},
|
|
data: {
|
|
attributes: ['value']
|
|
},
|
|
time: {
|
|
attributes: ['datetime']
|
|
},
|
|
var: {},
|
|
samp: {},
|
|
kbd: {},
|
|
i: {},
|
|
b: {},
|
|
u: {},
|
|
mark: {},
|
|
ruby: {},
|
|
rt: {},
|
|
rp: {},
|
|
bdi: {
|
|
attributes: ['dir']
|
|
},
|
|
bdo: {
|
|
attributes: ['dir']
|
|
},
|
|
wbr: {},
|
|
'#text': {}
|
|
};
|
|
|
|
// Recursion is needed.
|
|
// Possible: strong > em > strong.
|
|
// Impossible: strong > strong.
|
|
const excludedElements = ['#text', 'br'];
|
|
Object.keys(textContentSchema).filter(element => !excludedElements.includes(element)).forEach(tag => {
|
|
const {
|
|
[tag]: removedTag,
|
|
...restSchema
|
|
} = textContentSchema;
|
|
textContentSchema[tag].children = restSchema;
|
|
});
|
|
|
|
/**
|
|
* Embedded content elements.
|
|
*
|
|
* @see https://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#embedded-content-0
|
|
*
|
|
* @type {ContentSchema}
|
|
*/
|
|
const embeddedContentSchema = {
|
|
audio: {
|
|
attributes: ['src', 'preload', 'autoplay', 'mediagroup', 'loop', 'muted']
|
|
},
|
|
canvas: {
|
|
attributes: ['width', 'height']
|
|
},
|
|
embed: {
|
|
attributes: ['src', 'type', 'width', 'height']
|
|
},
|
|
img: {
|
|
attributes: ['alt', 'src', 'srcset', 'usemap', 'ismap', 'width', 'height']
|
|
},
|
|
object: {
|
|
attributes: ['data', 'type', 'name', 'usemap', 'form', 'width', 'height']
|
|
},
|
|
video: {
|
|
attributes: ['src', 'poster', 'preload', 'playsinline', 'autoplay', 'mediagroup', 'loop', 'muted', 'controls', 'width', 'height']
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Phrasing content elements.
|
|
*
|
|
* @see https://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#phrasing-content-0
|
|
*/
|
|
const phrasingContentSchema = {
|
|
...textContentSchema,
|
|
...embeddedContentSchema
|
|
};
|
|
|
|
/**
|
|
* Get schema of possible paths for phrasing content.
|
|
*
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content
|
|
*
|
|
* @param {string} [context] Set to "paste" to exclude invisible elements and
|
|
* sensitive data.
|
|
*
|
|
* @return {Partial<ContentSchema>} Schema.
|
|
*/
|
|
export function getPhrasingContentSchema(context) {
|
|
if (context !== 'paste') {
|
|
return phrasingContentSchema;
|
|
}
|
|
|
|
/**
|
|
* @type {Partial<ContentSchema>}
|
|
*/
|
|
const {
|
|
u,
|
|
// Used to mark misspelling. Shouldn't be pasted.
|
|
abbr,
|
|
// Invisible.
|
|
data,
|
|
// Invisible.
|
|
time,
|
|
// Invisible.
|
|
wbr,
|
|
// Invisible.
|
|
bdi,
|
|
// Invisible.
|
|
bdo,
|
|
// Invisible.
|
|
...remainingContentSchema
|
|
} = {
|
|
...phrasingContentSchema,
|
|
// We shouldn't paste potentially sensitive information which is not
|
|
// visible to the user when pasted, so strip the attributes.
|
|
ins: {
|
|
children: phrasingContentSchema.ins.children
|
|
},
|
|
del: {
|
|
children: phrasingContentSchema.del.children
|
|
}
|
|
};
|
|
return remainingContentSchema;
|
|
}
|
|
|
|
/**
|
|
* Find out whether or not the given node is phrasing content.
|
|
*
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content
|
|
*
|
|
* @param {Node} node The node to test.
|
|
*
|
|
* @return {boolean} True if phrasing content, false if not.
|
|
*/
|
|
export function isPhrasingContent(node) {
|
|
const tag = node.nodeName.toLowerCase();
|
|
return getPhrasingContentSchema().hasOwnProperty(tag) || tag === 'span';
|
|
}
|
|
|
|
/**
|
|
* @param {Node} node
|
|
* @return {boolean} Node is text content
|
|
*/
|
|
export function isTextContent(node) {
|
|
const tag = node.nodeName.toLowerCase();
|
|
return textContentSchema.hasOwnProperty(tag) || tag === 'span';
|
|
}
|
|
//# sourceMappingURL=phrasing-content.js.map
|