0
# Utilities
1
2
The utility system provides helper functions for DOM manipulation, language detection, object operations, and other common tasks needed throughout the PrismJS ecosystem. These utilities support both internal operations and external plugin development.
3
4
## Capabilities
5
6
### Utility Namespace
7
8
The main container for all utility functions and helper methods.
9
10
```javascript { .api }
11
/**
12
* Utility methods namespace containing helper functions
13
*/
14
Prism.util;
15
```
16
17
### DOM and Language Utilities
18
19
#### getLanguage
20
21
Extract language identifier from element's CSS classes.
22
23
```javascript { .api }
24
/**
25
* Extract language identifier from element's CSS classes
26
* @param {Element} element - DOM element to examine
27
* @returns {string} Language identifier or 'none' if not found
28
*/
29
Prism.util.getLanguage(element);
30
```
31
32
**Usage Examples:**
33
34
```javascript
35
// Extract language from various class patterns
36
const jsElement = document.querySelector('.language-javascript');
37
console.log(Prism.util.getLanguage(jsElement)); // 'javascript'
38
39
const pyElement = document.querySelector('.lang-python');
40
console.log(Prism.util.getLanguage(pyElement)); // 'python'
41
42
const noLangElement = document.querySelector('.no-language');
43
console.log(Prism.util.getLanguage(noLangElement)); // 'none'
44
45
// Works with multiple classes
46
const multiElement = document.createElement('code');
47
multiElement.className = 'line-numbers language-typescript highlight';
48
console.log(Prism.util.getLanguage(multiElement)); // 'typescript'
49
50
// Dynamic language detection
51
function highlightDynamicCode(element, fallbackLang = 'text') {
52
const detectedLang = Prism.util.getLanguage(element);
53
const language = detectedLang !== 'none' ? detectedLang : fallbackLang;
54
55
if (Prism.languages[language]) {
56
Prism.highlightElement(element);
57
} else {
58
console.warn(`Language '${language}' not available`);
59
}
60
}
61
```
62
63
#### setLanguage
64
65
Set language classes on DOM elements.
66
67
```javascript { .api }
68
/**
69
* Set language classes on DOM element
70
* @param {Element} element - DOM element to modify
71
* @param {string} language - Language identifier to set
72
*/
73
Prism.util.setLanguage(element, language);
74
```
75
76
**Usage Examples:**
77
78
```javascript
79
// Set language on code element
80
const codeElement = document.createElement('code');
81
Prism.util.setLanguage(codeElement, 'javascript');
82
console.log(codeElement.className); // 'language-javascript'
83
84
// Change language dynamically
85
const existingElement = document.querySelector('code.language-python');
86
Prism.util.setLanguage(existingElement, 'typescript');
87
console.log(existingElement.className); // 'language-typescript'
88
89
// Set language on parent and child
90
const preElement = document.createElement('pre');
91
const codeChild = document.createElement('code');
92
preElement.appendChild(codeChild);
93
94
Prism.util.setLanguage(preElement, 'css');
95
Prism.util.setLanguage(codeChild, 'css');
96
97
// Dynamic language switching
98
function switchLanguage(element, newLanguage) {
99
const oldLanguage = Prism.util.getLanguage(element);
100
console.log(`Switching from ${oldLanguage} to ${newLanguage}`);
101
102
Prism.util.setLanguage(element, newLanguage);
103
104
// Re-highlight with new language
105
if (Prism.languages[newLanguage]) {
106
Prism.highlightElement(element);
107
}
108
}
109
```
110
111
#### currentScript
112
113
Get reference to currently executing script element.
114
115
```javascript { .api }
116
/**
117
* Get reference to currently executing script element
118
* @returns {Element|null} Current script element or null
119
*/
120
Prism.util.currentScript();
121
```
122
123
**Usage Examples:**
124
125
```javascript
126
// Get current script for path resolution
127
const currentScript = Prism.util.currentScript();
128
if (currentScript) {
129
const scriptPath = currentScript.src;
130
const basePath = scriptPath.substring(0, scriptPath.lastIndexOf('/'));
131
console.log('Script base path:', basePath);
132
}
133
134
// Plugin initialization with script detection
135
(function() {
136
const script = Prism.util.currentScript();
137
const pluginConfig = {
138
scriptSrc: script ? script.src : 'unknown',
139
loadTime: Date.now()
140
};
141
142
Prism.plugins.myPlugin = pluginConfig;
143
})();
144
145
// Conditional loading based on script location
146
const script = Prism.util.currentScript();
147
if (script && script.hasAttribute('data-manual')) {
148
Prism.manual = true;
149
}
150
```
151
152
### Object Utilities
153
154
#### clone
155
156
Deep clone objects with circular reference handling.
157
158
```javascript { .api }
159
/**
160
* Deep clone object with circular reference handling
161
* @param {*} o - Object to clone
162
* @param {WeakMap} [visited] - Map of visited objects for circular reference detection
163
* @returns {*} Cloned object
164
*/
165
Prism.util.clone(o, visited);
166
```
167
168
**Usage Examples:**
169
170
```javascript
171
// Clone simple objects
172
const original = { a: 1, b: { c: 2 } };
173
const cloned = Prism.util.clone(original);
174
cloned.b.c = 3;
175
console.log(original.b.c); // 2 (unchanged)
176
console.log(cloned.b.c); // 3
177
178
// Clone language grammars
179
const baseGrammar = Prism.languages.javascript;
180
const customGrammar = Prism.util.clone(baseGrammar);
181
customGrammar.keyword = /\b(?:const|let|var|custom)\b/;
182
183
// Clone with circular references
184
const circular = { name: 'test' };
185
circular.self = circular;
186
const clonedCircular = Prism.util.clone(circular);
187
console.log(clonedCircular.self === clonedCircular); // true
188
189
// Clone arrays and complex structures
190
const complexObject = {
191
array: [1, 2, { nested: true }],
192
func: function() { return 'test'; },
193
regex: /pattern/gi,
194
date: new Date()
195
};
196
const clonedComplex = Prism.util.clone(complexObject);
197
198
// Safe grammar extension
199
function extendGrammar(baseId, extensions) {
200
const base = Prism.languages[baseId];
201
if (!base) {
202
throw new Error(`Base language '${baseId}' not found`);
203
}
204
205
const extended = Prism.util.clone(base);
206
Object.assign(extended, extensions);
207
return extended;
208
}
209
```
210
211
#### type
212
213
Get detailed type information for objects.
214
215
```javascript { .api }
216
/**
217
* Get detailed type information for any value
218
* @param {*} o - Value to examine
219
* @returns {string} Type string (Object, Array, String, etc.)
220
*/
221
Prism.util.type(o);
222
```
223
224
**Usage Examples:**
225
226
```javascript
227
// Type detection for various values
228
console.log(Prism.util.type({})); // 'Object'
229
console.log(Prism.util.type([])); // 'Array'
230
console.log(Prism.util.type('string')); // 'String'
231
console.log(Prism.util.type(42)); // 'Number'
232
console.log(Prism.util.type(true)); // 'Boolean'
233
console.log(Prism.util.type(/regex/)); // 'RegExp'
234
console.log(Prism.util.type(null)); // 'Null'
235
console.log(Prism.util.type(undefined)); // 'Undefined'
236
237
// Type-based processing
238
function processGrammarRule(rule) {
239
const ruleType = Prism.util.type(rule);
240
241
switch (ruleType) {
242
case 'RegExp':
243
return { pattern: rule };
244
case 'Object':
245
return rule;
246
case 'Array':
247
return rule.map(processGrammarRule);
248
default:
249
console.warn('Unexpected rule type:', ruleType);
250
return rule;
251
}
252
}
253
254
// Safe property access based on type
255
function safeAccess(obj, property, expectedType) {
256
if (Prism.util.type(obj) === 'Object' && property in obj) {
257
const value = obj[property];
258
return Prism.util.type(value) === expectedType ? value : null;
259
}
260
return null;
261
}
262
```
263
264
#### objId
265
266
Generate unique identifiers for objects (circular reference handling).
267
268
```javascript { .api }
269
/**
270
* Get or generate unique identifier for object
271
* @param {Object} obj - Object to get ID for
272
* @returns {string} Unique object identifier
273
*/
274
Prism.util.objId(obj);
275
```
276
277
**Usage Examples:**
278
279
```javascript
280
// Generate unique IDs for objects
281
const obj1 = { name: 'first' };
282
const obj2 = { name: 'second' };
283
284
console.log(Prism.util.objId(obj1)); // 'O_1' (or similar)
285
console.log(Prism.util.objId(obj2)); // 'O_2' (or similar)
286
console.log(Prism.util.objId(obj1)); // Same as first call
287
288
// Track object references
289
const objectTracker = new Map();
290
291
function trackObject(obj, metadata) {
292
const id = Prism.util.objId(obj);
293
objectTracker.set(id, { object: obj, metadata, accessed: Date.now() });
294
return id;
295
}
296
297
// Use in grammar processing
298
function processGrammarWithTracking(grammar) {
299
const grammarId = Prism.util.objId(grammar);
300
console.log(`Processing grammar ${grammarId}`);
301
302
// Process grammar rules...
303
return grammarId;
304
}
305
```
306
307
### Token and Content Utilities
308
309
#### encode
310
311
HTML encode tokens and content for safe output.
312
313
```javascript { .api }
314
/**
315
* HTML encode tokens and content for safe output
316
* @param {*} tokens - Tokens, strings, or mixed content to encode
317
* @returns {*} Encoded content with same structure
318
*/
319
Prism.util.encode(tokens);
320
```
321
322
**Usage Examples:**
323
324
```javascript
325
// Encode simple strings
326
const htmlString = '<script>alert("XSS")</script>';
327
const encoded = Prism.util.encode(htmlString);
328
console.log(encoded); // '<script>alert("XSS")</script>'
329
330
// Encode token content
331
const token = new Prism.Token('string', '"<html>"');
332
const encodedToken = Prism.util.encode(token);
333
console.log(encodedToken.content); // '"<html>"'
334
335
// Encode token arrays
336
const tokens = [
337
'const html = ',
338
new Prism.Token('string', '"<div>content</div>"'),
339
';'
340
];
341
const encodedTokens = Prism.util.encode(tokens);
342
343
// Recursive encoding of nested structures
344
const nestedToken = new Prism.Token('tag', [
345
'<',
346
new Prism.Token('tag-name', 'script'),
347
'>'
348
]);
349
const encodedNested = Prism.util.encode(nestedToken);
350
351
// Safe content processing
352
function safeHighlight(code, grammar, language) {
353
const tokens = Prism.tokenize(code, grammar);
354
const encodedTokens = Prism.util.encode(tokens);
355
return Prism.Token.stringify(encodedTokens, language);
356
}
357
```
358
359
#### isActive
360
361
Check element state based on CSS classes and configuration.
362
363
```javascript { .api }
364
/**
365
* Check if element has active state based on classes and configuration
366
* @param {Element} element - Element to check
367
* @param {string} className - CSS class name to look for
368
* @param {boolean} [defaultActivation=false] - Default state if class not found
369
* @returns {boolean} Whether element is active for the given class
370
*/
371
Prism.util.isActive(element, className, defaultActivation);
372
```
373
374
**Usage Examples:**
375
376
```javascript
377
// Check plugin activation
378
const codeElement = document.querySelector('code.language-js');
379
380
// Check if line numbers should be shown
381
const hasLineNumbers = Prism.util.isActive(codeElement, 'line-numbers', false);
382
if (hasLineNumbers) {
383
console.log('Line numbers enabled');
384
}
385
386
// Check with default activation
387
const shouldShowToolbar = Prism.util.isActive(codeElement, 'toolbar', true);
388
389
// Plugin conditional activation
390
function conditionalPluginInit(element, pluginClass, pluginHandler) {
391
if (Prism.util.isActive(element, pluginClass)) {
392
pluginHandler(element);
393
}
394
}
395
396
// Usage in plugin development
397
Prism.hooks.add('complete', function(env) {
398
conditionalPluginInit(env.element, 'show-language', function(el) {
399
const language = Prism.util.getLanguage(el);
400
const label = document.createElement('span');
401
label.textContent = language.toUpperCase();
402
label.className = 'language-label';
403
el.parentNode.insertBefore(label, el);
404
});
405
});
406
```
407
408
## Advanced Utility Patterns
409
410
### Utility Combinations
411
412
```javascript
413
// Combine utilities for complex operations
414
function smartLanguageDetection(element) {
415
const detectedLang = Prism.util.getLanguage(element);
416
417
if (detectedLang === 'none') {
418
// Try to detect from content or context
419
const content = element.textContent;
420
if (content.includes('function') && content.includes('{')) {
421
Prism.util.setLanguage(element, 'javascript');
422
return 'javascript';
423
}
424
}
425
426
return detectedLang;
427
}
428
429
// Safe grammar operations
430
function safeGrammarOperation(grammarId, operation) {
431
const grammar = Prism.languages[grammarId];
432
if (!grammar) {
433
console.error(`Grammar '${grammarId}' not found`);
434
return null;
435
}
436
437
const grammarType = Prism.util.type(grammar);
438
if (grammarType !== 'Object') {
439
console.error(`Invalid grammar type: ${grammarType}`);
440
return null;
441
}
442
443
try {
444
const cloned = Prism.util.clone(grammar);
445
return operation(cloned);
446
} catch (error) {
447
console.error('Grammar operation failed:', error);
448
return null;
449
}
450
}
451
```
452
453
### Performance Utilities
454
455
```javascript
456
// Efficient type checking
457
const typeCache = new Map();
458
459
function cachedType(obj) {
460
const id = Prism.util.objId(obj);
461
if (typeCache.has(id)) {
462
return typeCache.get(id);
463
}
464
465
const type = Prism.util.type(obj);
466
typeCache.set(id, type);
467
return type;
468
}
469
470
// Batch language operations
471
function batchSetLanguage(elements, language) {
472
elements.forEach(element => {
473
Prism.util.setLanguage(element, language);
474
});
475
}
476
477
// Memory-conscious cloning
478
function shallowCloneIfNeeded(obj, deep = false) {
479
const objType = Prism.util.type(obj);
480
481
if (objType === 'Object' || objType === 'Array') {
482
return deep ? Prism.util.clone(obj) : Object.assign({}, obj);
483
}
484
485
return obj; // Primitives don't need cloning
486
}
487
```
488
489
### Error Handling Utilities
490
491
```javascript
492
// Safe utility calls with fallbacks
493
function safeGetLanguage(element, fallback = 'text') {
494
try {
495
const language = Prism.util.getLanguage(element);
496
return language !== 'none' ? language : fallback;
497
} catch (error) {
498
console.warn('Language detection failed:', error);
499
return fallback;
500
}
501
}
502
503
function safeClone(obj, fallback = null) {
504
try {
505
return Prism.util.clone(obj);
506
} catch (error) {
507
console.error('Cloning failed:', error);
508
return fallback;
509
}
510
}
511
512
// Validation utilities
513
function validateElement(element, requiredClasses = []) {
514
if (!element || Prism.util.type(element) !== 'Object') {
515
return false;
516
}
517
518
return requiredClasses.every(className =>
519
Prism.util.isActive(element, className)
520
);
521
}
522
```