Created
December 29, 2023 16:53
-
-
Save idStar/76882ef0387c1f247161b624e91a4a8d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Heading Level Numbering for Google Docs | |
// Created by Sohail Ahmed | |
// Created On October 3, 2023 | |
// This series of functions will install an 'Auto Numbering' menu with a couple of options in it. | |
// The most important is 'Update Heading Numbers', which will generate/update outline numbering. | |
function onOpen() { | |
updateMenu(); | |
} | |
function updateMenu() { | |
var ui = DocumentApp.getUi(); | |
var numberLevel1 = isNumberLevel1(); | |
var menuItemText = numberLevel1 ? 'Hide Level 1 Numbers' : 'Show Level 1 Numbers'; | |
ui.createMenu('Auto Numbering') | |
.addItem('Update Heading Numbers', 'initiateHeadingUpdate') | |
.addItem(menuItemText, 'toggleNumberLevel1') | |
.addToUi(); | |
} | |
function isNumberLevel1() { | |
return PropertiesService.getScriptProperties().getProperty('numberLevel1') === 'true'; | |
} | |
function toggleNumberLevel1() { | |
var numberLevel1 = isNumberLevel1(); | |
PropertiesService.getScriptProperties().setProperty('numberLevel1', !numberLevel1); | |
updateMenu(); | |
} | |
function initiateHeadingUpdate() { | |
var doc = DocumentApp.getActiveDocument(); | |
var body = doc.getBody(); | |
var bookmark; | |
try { | |
// Create a temporary bookmark at the current cursor position. | |
var selection = doc.getSelection(); | |
if (selection) { | |
var rangeElements = selection.getRangeElements(); | |
if (rangeElements.length > 0) { | |
var position = rangeElements[0].getStartOffset(); | |
var element = rangeElements[0].getElement(); | |
if (doc.createBookmark) { | |
bookmark = doc.createBookmark(element, position); | |
} else { | |
console.warn('doc.createBookmark method not available.'); | |
} | |
} | |
} | |
} catch (e) { | |
console.error("Bookmark creation failed: ", e); | |
} | |
// Update the headings | |
updateHeadings(); | |
// Open the sidebar, which forces Google Docs to navigate to the bookmark's position. | |
DocumentApp.getUi().showSidebar(HtmlService.createHtmlOutput("<script>window.onload = function() { google.script.run.closeSidebar(); }</script>")); | |
try { | |
// Remove the bookmark. | |
if (bookmark) { | |
bookmark.remove(); | |
} | |
} catch (e) { | |
console.error("Bookmark removal failed: ", e); | |
} | |
} | |
function closeSidebar() { | |
DocumentApp.getUi().closeDialog(); | |
} | |
function updateHeadings() { | |
var doc = DocumentApp.getActiveDocument(); | |
var body = doc.getBody(); | |
var paragraphs = body.getParagraphs(); | |
var numberLevel1 = isNumberLevel1(); | |
var currentLevelNumbers = [0, 0, 0, 0, 0, 0]; // supports up to 6 levels of headings | |
for (var i = 0; i < paragraphs.length; i++) { | |
var paragraph = paragraphs[i]; | |
var heading = paragraph.getHeading(); | |
// Skip empty paragraphs | |
if (!paragraph.getText().trim()) { | |
continue; | |
} | |
// If it's a heading (not NORMAL or TITLE or SUBTITLE) | |
if (heading !== DocumentApp.ParagraphHeading.NORMAL && | |
heading !== DocumentApp.ParagraphHeading.TITLE && | |
heading !== DocumentApp.ParagraphHeading.SUBTITLE) { | |
var level; | |
switch(heading) { | |
case DocumentApp.ParagraphHeading.HEADING1: | |
level = 1; | |
break; | |
case DocumentApp.ParagraphHeading.HEADING2: | |
level = 2; | |
break; | |
case DocumentApp.ParagraphHeading.HEADING3: | |
level = 3; | |
break; | |
case DocumentApp.ParagraphHeading.HEADING4: | |
level = 4; | |
break; | |
case DocumentApp.ParagraphHeading.HEADING5: | |
level = 5; | |
break; | |
default: | |
level = 0; | |
} | |
// Increment the current level number and reset all sub-levels to 0 | |
currentLevelNumbers[level - 1]++; | |
for (var j = level; j < currentLevelNumbers.length; j++) { | |
currentLevelNumbers[j] = 0; | |
} | |
var numbering = currentLevelNumbers.slice(0, level).join('.'); | |
if (level !== 1 || (level === 1 && numberLevel1)) { | |
// Set the updated text for all levels when numberLevel1 is true, | |
// or for levels other than 1 when numberLevel1 is false | |
var text = paragraph.getText(); | |
text = text.replace(/^\d+(\.\d+)*(\s*\.\s*)?\s*/, ''); | |
paragraph.setText(numbering + ' ' + text.trim()); | |
} else if (level === 1 && !numberLevel1) { | |
// Remove numbering from Level 1 headings when numberLevel1 is false | |
var text = paragraph.getText(); | |
text = text.replace(/^\d+(\.\d+)*(\s*\.\s*)?\s*/, ''); | |
paragraph.setText(text.trim()); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment