(function () { if (typeof self === 'undefined' || !self.Prism || !self.document) { return; } var MATCH_ALL_CLASS = /(?:^|\s)match-braces(?:\s|$)/; var BRACE_HOVER_CLASS = /(?:^|\s)brace-hover(?:\s|$)/; var BRACE_SELECTED_CLASS = /(?:^|\s)brace-selected(?:\s|$)/; var NO_BRACE_HOVER_CLASS = /(?:^|\s)no-brace-hover(?:\s|$)/; var NO_BRACE_SELECT_CLASS = /(?:^|\s)no-brace-select(?:\s|$)/; var PARTNER = { '(': ')', '[': ']', '{': '}', }; var NAMES = { '(': 'brace-round', '[': 'brace-square', '{': 'brace-curly', }; var LEVEL_WARP = 12; var pairIdCounter = 0; var BRACE_ID_PATTERN = /^(pair-\d+-)(open|close)$/; /** * 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() { for (var parent = this.parentElement; parent; parent = parent.parentElement) { if (NO_BRACE_HOVER_CLASS.test(parent.className)) { return; } } [this, getPartnerBrace(this)].forEach(function (ele) { ele.className = (ele.className.replace(BRACE_HOVER_CLASS, ' ') + ' brace-hover').replace(/\s+/g, ' '); }); } /** * @this {HTMLElement} */ function leaveBrace() { [this, getPartnerBrace(this)].forEach(function (ele) { ele.className = ele.className.replace(BRACE_HOVER_CLASS, ' '); }); } /** * @this {HTMLElement} */ function clickBrace() { for (var parent = this.parentElement; parent; parent = parent.parentElement) { if (NO_BRACE_SELECT_CLASS.test(parent.className)) { return; } } [this, getPartnerBrace(this)].forEach(function (ele) { ele.className = (ele.className.replace(BRACE_SELECTED_CLASS, ' ') + ' brace-selected').replace(/\s+/g, ' '); }); } 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 = []; for (var ele = code; ele; ele = ele.parentElement) { if (MATCH_ALL_CLASS.test(ele.className)) { toMatch.push('(', '[', '{'); break; } } 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'); Array.prototype.slice.call(code.querySelectorAll('.brace-selected')).forEach(function (element) { element.className = element.className.replace(BRACE_SELECTED_CLASS, ' '); }); }); Object.defineProperty(pre, '__listenerAdded', { value: true }); } /** @type {HTMLSpanElement[]} */ var punctuation = Array.prototype.slice.call(code.querySelectorAll('span.token.punctuation')); /** @type {{ index: number, open: boolean, element: HTMLElement }[]} */ var allBraces = []; toMatch.forEach(function (open) { var close = PARTNER[open]; var name = 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; if (text === open) { allBraces.push({ index: i, open: true, element: element }); element.className += ' ' + name; element.className += ' brace-open'; openStack.push(i); } else if (text === close) { allBraces.push({ index: i, open: false, element: element }); element.className += ' ' + name; element.className += ' brace-close'; if (openStack.length) { pairs.push([i, openStack.pop()]); } } } } pairs.forEach(function (pair) { var pairId = 'pair-' + (pairIdCounter++) + '-'; var openEle = punctuation[pair[0]]; var closeEle = punctuation[pair[1]]; openEle.id = pairId + 'open'; closeEle.id = pairId + 'close'; [openEle, closeEle].forEach(function (ele) { ele.addEventListener('mouseenter', hoverBrace); ele.addEventListener('mouseleave', leaveBrace); ele.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.className += ' brace-level-' + (level % LEVEL_WARP + 1); level++; } else { level = Math.max(0, level - 1); brace.element.className += ' brace-level-' + (level % LEVEL_WARP + 1); } }); }); }());