| // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information. |
| $(function () { |
| var active = 'active'; |
| var expanded = 'in'; |
| var collapsed = 'collapsed'; |
| var filtered = 'filtered'; |
| var show = 'show'; |
| var hide = 'hide'; |
| var util = new utility(); |
| |
| highlight(); |
| enableSearch(); |
| |
| renderTables(); |
| renderAlerts(); |
| renderLinks(); |
| renderNavbar(); |
| renderSidebar(); |
| renderAffix(); |
| renderFooter(); |
| renderLogo(); |
| |
| breakText(); |
| |
| window.refresh = function (article) { |
| // Update markup result |
| if (typeof article == 'undefined' || typeof article.content == 'undefined') |
| console.error("Null Argument"); |
| $("article.content").html(article.content); |
| |
| highlight(); |
| renderTables(); |
| renderAlerts(); |
| renderAffix(); |
| } |
| |
| function breakText() { |
| $(".xref").addClass("text-break"); |
| var texts = $(".text-break"); |
| texts.each(function () { |
| $(this).text(function (index, text) { |
| return util.breakText(text); |
| }) |
| }); |
| } |
| |
| // Styling for tables in conceptual documents using Bootstrap. |
| // See http://getbootstrap.com/css/#tables |
| function renderTables() { |
| $('table').addClass('table table-bordered table-striped table-condensed'); |
| } |
| |
| // Styling for alerts. |
| function renderAlerts() { |
| $('.NOTE, .TIP').addClass('alert alert-info'); |
| $('.WARNING').addClass('alert alert-warning'); |
| $('.IMPORTANT, .CAUTION').addClass('alert alert-danger'); |
| } |
| |
| // Enable anchors for headings. |
| (function () { |
| anchors.options = { |
| placement: 'left', |
| visible: 'touch' |
| }; |
| anchors.add('article h2:not(.no-anchor), article h3:not(.no-anchor), article h4:not(.no-anchor)'); |
| })(); |
| |
| // Open links to different host in a new window. |
| function renderLinks() { |
| if ($("meta[property='docfx:newtab']").attr("content") === "true") { |
| $(document.links).filter(function () { |
| return this.hostname !== window.location.hostname; |
| }).attr('target', '_blank'); |
| } |
| } |
| |
| // Enable highlight.js |
| function highlight() { |
| $('pre code').each(function (i, block) { |
| hljs.highlightBlock(block); |
| }); |
| $('pre code[highlight-lines]').each(function (i, block) { |
| if (block.innerHTML === "") return; |
| var lines = block.innerHTML.split('\n'); |
| |
| queryString = block.getAttribute('highlight-lines'); |
| if (!queryString) return; |
| |
| var ranges = queryString.split(','); |
| for (var j = 0, range; range = ranges[j++];) { |
| var found = range.match(/^(\d+)\-(\d+)?$/); |
| if (found) { |
| // consider region as `{startlinenumber}-{endlinenumber}`, in which {endlinenumber} is optional |
| var start = +found[1]; |
| var end = +found[2]; |
| if (isNaN(end) || end > lines.length) { |
| end = lines.length; |
| } |
| } else { |
| // consider region as a sigine line number |
| if (isNaN(range)) continue; |
| var start = +range; |
| var end = start; |
| } |
| if (start <= 0 || end <= 0 || start > end || start > lines.length) { |
| // skip current region if invalid |
| continue; |
| } |
| lines[start - 1] = '<span class="line-highlight">' + lines[start - 1]; |
| lines[end - 1] = lines[end - 1] + '</span>'; |
| } |
| |
| block.innerHTML = lines.join('\n'); |
| }); |
| } |
| |
| // Support full-text-search |
| function enableSearch() { |
| var query; |
| var relHref = $("meta[property='docfx\\:rel']").attr("content"); |
| if (typeof relHref === 'undefined') { |
| return; |
| } |
| try { |
| var worker = new Worker(relHref + 'styles/search-worker.js'); |
| if (!worker && !window.worker) { |
| localSearch(); |
| } else { |
| webWorkerSearch(); |
| } |
| |
| renderSearchBox(); |
| highlightKeywords(); |
| addSearchEvent(); |
| } catch (e) { |
| console.error(e); |
| } |
| |
| //Adjust the position of search box in navbar |
| function renderSearchBox() { |
| autoCollapse(); |
| $(window).on('resize', autoCollapse); |
| $(document).on('click', '.navbar-collapse.in', function (e) { |
| if ($(e.target).is('a')) { |
| $(this).collapse('hide'); |
| } |
| }); |
| |
| function autoCollapse() { |
| var navbar = $('#autocollapse'); |
| if (navbar.height() === null) { |
| setTimeout(autoCollapse, 300); |
| } |
| navbar.removeClass(collapsed); |
| if (navbar.height() > 60) { |
| navbar.addClass(collapsed); |
| } |
| } |
| } |
| |
| // Search factory |
| function localSearch() { |
| console.log("using local search"); |
| var lunrIndex = lunr(function () { |
| this.ref('href'); |
| this.field('title', { boost: 50 }); |
| this.field('keywords', { boost: 20 }); |
| }); |
| lunr.tokenizer.seperator = /[\s\-\.]+/; |
| var searchData = {}; |
| var searchDataRequest = new XMLHttpRequest(); |
| |
| var indexPath = relHref + "index.json"; |
| if (indexPath) { |
| searchDataRequest.open('GET', indexPath); |
| searchDataRequest.onload = function () { |
| if (this.status != 200) { |
| return; |
| } |
| searchData = JSON.parse(this.responseText); |
| for (var prop in searchData) { |
| if (searchData.hasOwnProperty(prop)){ |
| lunrIndex.add(searchData[prop]); |
| } |
| } |
| } |
| searchDataRequest.send(); |
| } |
| |
| $("body").bind("queryReady", function () { |
| var hits = lunrIndex.search(query); |
| var results = []; |
| hits.forEach(function (hit) { |
| var item = searchData[hit.ref]; |
| results.push({ 'href': item.href, 'title': item.title, 'keywords': item.keywords }); |
| }); |
| handleSearchResults(results); |
| }); |
| } |
| |
| function webWorkerSearch() { |
| console.log("using Web Worker"); |
| var indexReady = $.Deferred(); |
| |
| worker.onmessage = function (oEvent) { |
| switch (oEvent.data.e) { |
| case 'index-ready': |
| indexReady.resolve(); |
| break; |
| case 'query-ready': |
| var hits = oEvent.data.d; |
| handleSearchResults(hits); |
| break; |
| } |
| } |
| |
| indexReady.promise().done(function () { |
| $("body").bind("queryReady", function () { |
| worker.postMessage({ q: query }); |
| }); |
| }); |
| } |
| |
| // Highlight the searching keywords |
| function highlightKeywords() { |
| var q = url('?q'); |
| if (q !== null) { |
| var keywords = q.split("%20"); |
| keywords.forEach(function (keyword) { |
| if (keyword !== "") { |
| $('.data-searchable *').mark(keyword); |
| $('article *').mark(keyword); |
| } |
| }); |
| } |
| } |
| |
| function addSearchEvent() { |
| $('body').bind("searchEvent", function () { |
| $('#search-query').keypress(function (e) { |
| return e.which !== 13; |
| }); |
| |
| $('#search-query').keyup(function () { |
| query = $(this).val(); |
| if (query.length < 3) { |
| flipContents("show"); |
| } else { |
| flipContents("hide"); |
| $("body").trigger("queryReady"); |
| $('#search-results>.search-list').text('Search Results for "' + query + '"'); |
| } |
| }).off("keydown"); |
| }); |
| } |
| |
| function flipContents(action) { |
| if (action === "show") { |
| $('.hide-when-search').show(); |
| $('#search-results').hide(); |
| } else { |
| $('.hide-when-search').hide(); |
| $('#search-results').show(); |
| } |
| } |
| |
| function relativeUrlToAbsoluteUrl(currentUrl, relativeUrl) { |
| var currentItems = currentUrl.split(/\/+/); |
| var relativeItems = relativeUrl.split(/\/+/); |
| var depth = currentItems.length - 1; |
| var items = []; |
| for (var i = 0; i < relativeItems.length; i++) { |
| if (relativeItems[i] === '..') { |
| depth--; |
| } else if (relativeItems[i] !== '.') { |
| items.push(relativeItems[i]); |
| } |
| } |
| return currentItems.slice(0, depth).concat(items).join('/'); |
| } |
| |
| function extractContentBrief(content) { |
| var briefOffset = 512; |
| var words = query.split(/\s+/g); |
| var queryIndex = content.indexOf(words[0]); |
| var briefContent; |
| if (queryIndex > briefOffset) { |
| return "..." + content.slice(queryIndex - briefOffset, queryIndex + briefOffset) + "..."; |
| } else if (queryIndex <= briefOffset) { |
| return content.slice(0, queryIndex + briefOffset) + "..."; |
| } |
| } |
| |
| function handleSearchResults(hits) { |
| var numPerPage = 10; |
| $('#pagination').empty(); |
| $('#pagination').removeData("twbs-pagination"); |
| if (hits.length === 0) { |
| $('#search-results>.sr-items').html('<p>No results found</p>'); |
| } else { |
| $('#pagination').twbsPagination({ |
| totalPages: Math.ceil(hits.length / numPerPage), |
| visiblePages: 5, |
| onPageClick: function (event, page) { |
| var start = (page - 1) * numPerPage; |
| var curHits = hits.slice(start, start + numPerPage); |
| $('#search-results>.sr-items').empty().append( |
| curHits.map(function (hit) { |
| var currentUrl = window.location.href; |
| var itemRawHref = relativeUrlToAbsoluteUrl(currentUrl, relHref + hit.href); |
| var itemHref = relHref + hit.href + "?q=" + query; |
| var itemTitle = hit.title; |
| var itemBrief = extractContentBrief(hit.keywords); |
| |
| var itemNode = $('<div>').attr('class', 'sr-item'); |
| var itemTitleNode = $('<div>').attr('class', 'item-title').append($('<a>').attr('href', itemHref).attr("target", "_blank").text(itemTitle)); |
| var itemHrefNode = $('<div>').attr('class', 'item-href').text(itemRawHref); |
| var itemBriefNode = $('<div>').attr('class', 'item-brief').text(itemBrief); |
| itemNode.append(itemTitleNode).append(itemHrefNode).append(itemBriefNode); |
| return itemNode; |
| }) |
| ); |
| query.split(/\s+/).forEach(function (word) { |
| if (word !== '') { |
| $('#search-results>.sr-items *').mark(word); |
| } |
| }); |
| } |
| }); |
| } |
| } |
| }; |
| |
| // Update href in navbar |
| function renderNavbar() { |
| var navbar = $('#navbar ul')[0]; |
| if (typeof (navbar) === 'undefined') { |
| loadNavbar(); |
| } else { |
| $('#navbar ul a.active').parents('li').addClass(active); |
| renderBreadcrumb(); |
| } |
| |
| function loadNavbar() { |
| var navbarPath = $("meta[property='docfx\\:navrel']").attr("content"); |
| if (!navbarPath) { |
| return; |
| } |
| navbarPath = navbarPath.replace(/\\/g, '/'); |
| var tocPath = $("meta[property='docfx\\:tocrel']").attr("content") || ''; |
| if (tocPath) tocPath = tocPath.replace(/\\/g, '/'); |
| $.get(navbarPath, function (data) { |
| $(data).find("#toc>ul").appendTo("#navbar"); |
| if ($('#search-results').length !== 0) { |
| $('#search').show(); |
| $('body').trigger("searchEvent"); |
| } |
| var index = navbarPath.lastIndexOf('/'); |
| var navrel = ''; |
| if (index > -1) { |
| navrel = navbarPath.substr(0, index + 1); |
| } |
| $('#navbar>ul').addClass('navbar-nav'); |
| var currentAbsPath = util.getAbsolutePath(window.location.pathname); |
| // set active item |
| $('#navbar').find('a[href]').each(function (i, e) { |
| var href = $(e).attr("href"); |
| if (util.isRelativePath(href)) { |
| href = navrel + href; |
| $(e).attr("href", href); |
| |
| // TODO: currently only support one level navbar |
| var isActive = false; |
| var originalHref = e.name; |
| if (originalHref) { |
| originalHref = navrel + originalHref; |
| if (util.getDirectory(util.getAbsolutePath(originalHref)) === util.getDirectory(util.getAbsolutePath(tocPath))) { |
| isActive = true; |
| } |
| } else { |
| if (util.getAbsolutePath(href) === currentAbsPath) { |
| isActive = true; |
| } |
| } |
| if (isActive) { |
| $(e).addClass(active); |
| } |
| } |
| }); |
| renderNavbar(); |
| }); |
| } |
| } |
| |
| function renderSidebar() { |
| var sidetoc = $('#sidetoggle .sidetoc')[0]; |
| if (typeof (sidetoc) === 'undefined') { |
| loadToc(); |
| } else { |
| registerTocEvents(); |
| if ($('footer').is(':visible')) { |
| $('.sidetoc').addClass('shiftup'); |
| } |
| |
| // Scroll to active item |
| var top = 0; |
| $('#toc a.active').parents('li').each(function (i, e) { |
| $(e).addClass(active).addClass(expanded); |
| $(e).children('a').addClass(active); |
| top += $(e).position().top; |
| }) |
| $('.sidetoc').scrollTop(top - 50); |
| |
| if ($('footer').is(':visible')) { |
| $('.sidetoc').addClass('shiftup'); |
| } |
| |
| renderBreadcrumb(); |
| } |
| |
| function registerTocEvents() { |
| $('.toc .nav > li > .expand-stub').click(function (e) { |
| $(e.target).parent().toggleClass(expanded); |
| }); |
| $('.toc .nav > li > .expand-stub + a:not([href])').click(function (e) { |
| $(e.target).parent().toggleClass(expanded); |
| }); |
| $('#toc_filter_input').on('input', function (e) { |
| var val = this.value; |
| if (val === '') { |
| // Clear 'filtered' class |
| $('#toc li').removeClass(filtered).removeClass(hide); |
| return; |
| } |
| |
| // Get leaf nodes |
| $('#toc li>a').filter(function (i, e) { |
| return $(e).siblings().length === 0 |
| }).each(function (i, anchor) { |
| var text = $(anchor).attr('title'); |
| var parent = $(anchor).parent(); |
| var parentNodes = parent.parents('ul>li'); |
| for (var i = 0; i < parentNodes.length; i++) { |
| var parentText = $(parentNodes[i]).children('a').attr('title'); |
| if (parentText) text = parentText + '.' + text; |
| }; |
| if (filterNavItem(text, val)) { |
| parent.addClass(show); |
| parent.removeClass(hide); |
| } else { |
| parent.addClass(hide); |
| parent.removeClass(show); |
| } |
| }); |
| $('#toc li>a').filter(function (i, e) { |
| return $(e).siblings().length > 0 |
| }).each(function (i, anchor) { |
| var parent = $(anchor).parent(); |
| if (parent.find('li.show').length > 0) { |
| parent.addClass(show); |
| parent.addClass(filtered); |
| parent.removeClass(hide); |
| } else { |
| parent.addClass(hide); |
| parent.removeClass(show); |
| parent.removeClass(filtered); |
| } |
| }) |
| |
| function filterNavItem(name, text) { |
| if (!text) return true; |
| if (name.toLowerCase().indexOf(text.toLowerCase()) > -1) return true; |
| return false; |
| } |
| }); |
| } |
| |
| function loadToc() { |
| var tocPath = $("meta[property='docfx\\:tocrel']").attr("content"); |
| if (!tocPath) { |
| return; |
| } |
| tocPath = tocPath.replace(/\\/g, '/'); |
| $('#sidetoc').load(tocPath + " #sidetoggle > div", function () { |
| var index = tocPath.lastIndexOf('/'); |
| var tocrel = ''; |
| if (index > -1) { |
| tocrel = tocPath.substr(0, index + 1); |
| } |
| var currentHref = util.getAbsolutePath(window.location.pathname); |
| $('#sidetoc').find('a[href]').each(function (i, e) { |
| var href = $(e).attr("href"); |
| if (util.isRelativePath(href)) { |
| href = tocrel + href; |
| $(e).attr("href", href); |
| } |
| |
| if (util.getAbsolutePath(e.href) === currentHref) { |
| $(e).addClass(active); |
| } |
| |
| $(e).text(function (index, text) { |
| return util.breakText(text); |
| }) |
| }); |
| |
| renderSidebar(); |
| }); |
| } |
| } |
| |
| function renderBreadcrumb() { |
| var breadcrumb = []; |
| $('#navbar a.active').each(function (i, e) { |
| breadcrumb.push({ |
| href: e.href, |
| name: e.innerHTML |
| }); |
| }) |
| $('#toc a.active').each(function (i, e) { |
| breadcrumb.push({ |
| href: e.href, |
| name: e.innerHTML |
| }); |
| }) |
| |
| var html = util.formList(breadcrumb, 'breadcrumb'); |
| $('#breadcrumb').html(html); |
| } |
| |
| //Setup Affix |
| function renderAffix() { |
| var hierarchy = getHierarchy(); |
| if (hierarchy.length > 0) { |
| var html = '<h5 class="title">In This Article</h5>' |
| html += util.formList(hierarchy, ['nav', 'bs-docs-sidenav']); |
| $("#affix").empty().append(html); |
| if ($('footer').is(':visible')) { |
| $(".sideaffix").css("bottom", "70px"); |
| } |
| $('#affix').on('activate.bs.scrollspy', function (e) { |
| if (e.target) { |
| if ($(e.target).find('li.active').length > 0) { |
| return; |
| } |
| var top = $(e.target).position().top; |
| $(e.target).parents('li').each(function (i, e) { |
| top += $(e).position().top; |
| }); |
| var container = $('#affix > ul'); |
| var height = container.height(); |
| container.scrollTop(container.scrollTop() + top - height / 2); |
| } |
| }) |
| } |
| |
| function getHierarchy() { |
| // supported headers are h1, h2, h3, and h4 |
| // The topest header is ignored |
| var selector = ".article article"; |
| var affixSelector = "#affix"; |
| var headers = ['h4', 'h3', 'h2', 'h1']; |
| var hierarchy = []; |
| var toppestIndex = -1; |
| var startIndex = -1; |
| // 1. get header hierarchy |
| for (var i = headers.length - 1; i >= 0; i--) { |
| var header = $(selector + " " + headers[i]); |
| var length = header.length; |
| |
| // If contains no header in current selector, find the next one |
| if (length === 0) continue; |
| |
| // If the toppest header contains only one item, e.g. title, ignore |
| if (length === 1 && hierarchy.length === 0 && toppestIndex < 0) { |
| toppestIndex = i; |
| continue; |
| } |
| |
| // Get second level children |
| var nextLevelSelector = i > 0 ? headers[i - 1] : null; |
| var prevSelector; |
| for (var j = length - 1; j >= 0; j--) { |
| var e = header[j]; |
| var id = e.id; |
| if (!id) continue; // For affix, id is a must-have |
| var item = { |
| name: htmlEncode($(e).text()), |
| href: "#" + id, |
| items: [] |
| }; |
| if (nextLevelSelector) { |
| var selector = '#' + cssEscape(id) + "~" + nextLevelSelector; |
| var currentSelector = selector; |
| if (prevSelector) currentSelector += ":not(" + prevSelector + ")"; |
| $(header[j]).siblings(currentSelector).each(function (index, e) { |
| if (e.id) { |
| item.items.push({ |
| name: htmlEncode($(e).text()), // innerText decodes text while innerHTML not |
| href: "#" + e.id |
| }) |
| } |
| }) |
| prevSelector = selector; |
| } |
| hierarchy.push(item); |
| } |
| break; |
| }; |
| hierarchy.reverse(); |
| return hierarchy; |
| } |
| |
| function htmlEncode(str) { |
| if (!str) return str; |
| return str |
| .replace(/&/g, '&') |
| .replace(/"/g, '"') |
| .replace(/'/g, ''') |
| .replace(/</g, '<') |
| .replace(/>/g, '>'); |
| } |
| |
| function htmlDecode(value) { |
| if (!str) return str; |
| return value |
| .replace(/"/g, '"') |
| .replace(/'/g, "'") |
| .replace(/</g, '<') |
| .replace(/>/g, '>') |
| .replace(/&/g, '&'); |
| } |
| |
| function cssEscape(str) { |
| // see: http://stackoverflow.com/questions/2786538/need-to-escape-a-special-character-in-a-jquery-selector-string#answer-2837646 |
| if (!str) return str; |
| return str |
| .replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&"); |
| } |
| } |
| |
| // Show footer |
| function renderFooter() { |
| initFooter(); |
| $(window).on("scroll", showFooterCore); |
| |
| function initFooter() { |
| if (needFooter()) { |
| shiftUpBottomCss(); |
| $("footer").show(); |
| } else { |
| resetBottomCss(); |
| $("footer").hide(); |
| } |
| } |
| |
| function showFooterCore() { |
| if (needFooter()) { |
| shiftUpBottomCss(); |
| $("footer").fadeIn(); |
| } else { |
| resetBottomCss(); |
| $("footer").fadeOut(); |
| } |
| } |
| |
| function needFooter() { |
| var scrollHeight = $(document).height(); |
| var scrollPosition = $(window).height() + $(window).scrollTop(); |
| return (scrollHeight - scrollPosition) < 1; |
| } |
| |
| function resetBottomCss() { |
| $(".sidetoc").removeClass("shiftup"); |
| $(".sideaffix").removeClass("shiftup"); |
| } |
| |
| function shiftUpBottomCss() { |
| $(".sidetoc").addClass("shiftup"); |
| $(".sideaffix").addClass("shiftup"); |
| } |
| } |
| |
| function renderLogo() { |
| // For LOGO SVG |
| // Replace SVG with inline SVG |
| // http://stackoverflow.com/questions/11978995/how-to-change-color-of-svg-image-using-css-jquery-svg-image-replacement |
| jQuery('img.svg').each(function () { |
| var $img = jQuery(this); |
| var imgID = $img.attr('id'); |
| var imgClass = $img.attr('class'); |
| var imgURL = $img.attr('src'); |
| |
| jQuery.get(imgURL, function (data) { |
| // Get the SVG tag, ignore the rest |
| var $svg = jQuery(data).find('svg'); |
| |
| // Add replaced image's ID to the new SVG |
| if (typeof imgID !== 'undefined') { |
| $svg = $svg.attr('id', imgID); |
| } |
| // Add replaced image's classes to the new SVG |
| if (typeof imgClass !== 'undefined') { |
| $svg = $svg.attr('class', imgClass + ' replaced-svg'); |
| } |
| |
| // Remove any invalid XML tags as per http://validator.w3.org |
| $svg = $svg.removeAttr('xmlns:a'); |
| |
| // Replace image with new SVG |
| $img.replaceWith($svg); |
| |
| }, 'xml'); |
| }); |
| } |
| |
| function utility() { |
| this.getAbsolutePath = getAbsolutePath; |
| this.isRelativePath = isRelativePath; |
| this.isAbsolutePath = isAbsolutePath; |
| this.getDirectory = getDirectory; |
| this.formList = formList; |
| this.breakText = breakText; |
| |
| function getAbsolutePath(href) { |
| // Use anchor to normalize href |
| var anchor = $('<a href="' + href + '"></a>')[0]; |
| // Ignore protocal, remove search and query |
| return anchor.host + anchor.pathname; |
| } |
| |
| function isRelativePath(href) { |
| return !isAbsolutePath(href); |
| } |
| |
| function isAbsolutePath(href) { |
| return (/^(?:[a-z]+:)?\/\//i).test(href); |
| } |
| |
| function getDirectory(href) { |
| if (!href) return ''; |
| var index = href.lastIndexOf('/'); |
| if (index == -1) return ''; |
| if (index > -1) { |
| return href.substr(0, index); |
| } |
| } |
| |
| |
| function formList(item, classes) { |
| var level = 1; |
| var model = { |
| items: item |
| }; |
| var cls = [].concat(classes).join(" "); |
| return getList(model, cls); |
| |
| function getList(model, cls) { |
| if (!model || !model.items) return null; |
| var l = model.items.length; |
| if (l === 0) return null; |
| var html = '<ul class="level' + level + ' ' + (cls || '') + '">'; |
| level++; |
| for (var i = 0; i < l; i++) { |
| var item = model.items[i]; |
| var href = item.href; |
| var name = item.name; |
| if (!name) continue; |
| html += href ? '<li><a href="' + href + '">' + name + '</a>' : '<li>' + name; |
| html += getList(item, cls) || ''; |
| html += '</li>'; |
| } |
| html += '</ul>'; |
| return html; |
| } |
| } |
| |
| function breakText(text) { |
| if (!text) return text; |
| return text.replace(/([a-z])([A-Z])|(\.)(\w)/g, '$1$3\u200B$2$4') |
| } |
| } |
| }) |