123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- (function () {
- if (typeof Prism === 'undefined' || typeof document === 'undefined') {
- return;
- }
- function mapClassName(name) {
- var customClass = Prism.plugins.customClass;
- if (customClass) {
- return customClass.apply(name, 'none');
- } else {
- return name;
- }
- }
- var PARTNER = {
- '(': ')',
- '[': ']',
- '{': '}',
- };
- // The names for brace types.
- // These names have two purposes: 1) they can be used for styling and 2) they are used to pair braces. Only braces
- // of the same type are paired.
- var NAMES = {
- '(': 'brace-round',
- '[': 'brace-square',
- '{': 'brace-curly',
- };
- // A map for brace aliases.
- // This is useful for when some braces have a prefix/suffix as part of the punctuation token.
- var BRACE_ALIAS_MAP = {
- '${': '{', // JS template punctuation (e.g. `foo ${bar + 1}`)
- };
- var LEVEL_WARP = 12;
- var pairIdCounter = 0;
- var BRACE_ID_PATTERN = /^(pair-\d+-)(close|open)$/;
- /**
- * Returns the brace partner given one brace of a brace pair.
- *
- * @param {HTMLElement} brace
- * @returns {HTMLElement}
- */
- function getPartnerBrace(brace) {
- var match = BRACE_ID_PATTERN.exec(brace.id);
- return document.querySelector('#' + match[1] + (match[2] == 'open' ? 'close' : 'open'));
- }
- /**
- * @this {HTMLElement}
- */
- function hoverBrace() {
- if (!Prism.util.isActive(this, 'brace-hover', true)) {
- return;
- }
- [this, getPartnerBrace(this)].forEach(function (e) {
- e.classList.add(mapClassName('brace-hover'));
- });
- }
- /**
- * @this {HTMLElement}
- */
- function leaveBrace() {
- [this, getPartnerBrace(this)].forEach(function (e) {
- e.classList.remove(mapClassName('brace-hover'));
- });
- }
- /**
- * @this {HTMLElement}
- */
- function clickBrace() {
- if (!Prism.util.isActive(this, 'brace-select', true)) {
- return;
- }
- [this, getPartnerBrace(this)].forEach(function (e) {
- e.classList.add(mapClassName('brace-selected'));
- });
- }
- Prism.hooks.add('complete', function (env) {
- /** @type {HTMLElement} */
- var code = env.element;
- var pre = code.parentElement;
- if (!pre || pre.tagName != 'PRE') {
- return;
- }
- // find the braces to match
- /** @type {string[]} */
- var toMatch = [];
- if (Prism.util.isActive(code, 'match-braces')) {
- toMatch.push('(', '[', '{');
- }
- if (toMatch.length == 0) {
- // nothing to match
- return;
- }
- if (!pre.__listenerAdded) {
- // code blocks might be highlighted more than once
- pre.addEventListener('mousedown', function removeBraceSelected() {
- // the code element might have been replaced
- var code = pre.querySelector('code');
- var className = mapClassName('brace-selected');
- Array.prototype.slice.call(code.querySelectorAll('.' + className)).forEach(function (e) {
- e.classList.remove(className);
- });
- });
- Object.defineProperty(pre, '__listenerAdded', { value: true });
- }
- /** @type {HTMLSpanElement[]} */
- var punctuation = Array.prototype.slice.call(
- code.querySelectorAll('span.' + mapClassName('token') + '.' + mapClassName('punctuation'))
- );
- /** @type {{ index: number, open: boolean, element: HTMLElement }[]} */
- var allBraces = [];
- toMatch.forEach(function (open) {
- var close = PARTNER[open];
- var name = mapClassName(NAMES[open]);
- /** @type {[number, number][]} */
- var pairs = [];
- /** @type {number[]} */
- var openStack = [];
- for (var i = 0; i < punctuation.length; i++) {
- var element = punctuation[i];
- if (element.childElementCount == 0) {
- var text = element.textContent;
- text = BRACE_ALIAS_MAP[text] || text;
- if (text === open) {
- allBraces.push({ index: i, open: true, element: element });
- element.classList.add(name);
- element.classList.add(mapClassName('brace-open'));
- openStack.push(i);
- } else if (text === close) {
- allBraces.push({ index: i, open: false, element: element });
- element.classList.add(name);
- element.classList.add(mapClassName('brace-close'));
- if (openStack.length) {
- pairs.push([i, openStack.pop()]);
- }
- }
- }
- }
- pairs.forEach(function (pair) {
- var pairId = 'pair-' + (pairIdCounter++) + '-';
- var opening = punctuation[pair[0]];
- var closing = punctuation[pair[1]];
- opening.id = pairId + 'open';
- closing.id = pairId + 'close';
- [opening, closing].forEach(function (e) {
- e.addEventListener('mouseenter', hoverBrace);
- e.addEventListener('mouseleave', leaveBrace);
- e.addEventListener('click', clickBrace);
- });
- });
- });
- var level = 0;
- allBraces.sort(function (a, b) { return a.index - b.index; });
- allBraces.forEach(function (brace) {
- if (brace.open) {
- brace.element.classList.add(mapClassName('brace-level-' + (level % LEVEL_WARP + 1)));
- level++;
- } else {
- level = Math.max(0, level - 1);
- brace.element.classList.add(mapClassName('brace-level-' + (level % LEVEL_WARP + 1)));
- }
- });
- });
- }());
|