Skip to content

Instantly share code, notes, and snippets.

@ovitente
Last active August 30, 2025 14:28
Show Gist options
  • Select an option

  • Save ovitente/418def5b6bf0139971873ecbebb588b3 to your computer and use it in GitHub Desktop.

Select an option

Save ovitente/418def5b6bf0139971873ecbebb588b3 to your computer and use it in GitHub Desktop.
function onHomepage(e) {
return buildHomeCard_();
}
function buildHomeCard_() {
var card = CardService.newCardBuilder()
.setHeader(CardService.newCardHeader().setTitle('Italic Highlighter'))
.addSection(
CardService.newCardSection()
.addWidget(CardService.newTextParagraph()
.setText('Подсветить курсив жёлтым в текущем документе.'))
.addWidget(
CardService.newTextButton()
.setText('Подсветить курсив')
.setOnClickAction(CardService.newAction().setFunctionName('runHighlight_'))
)
.addWidget(
CardService.newTextButton()
.setText('Снять подсветку')
.setOnClickAction(CardService.newAction().setFunctionName('runClear_'))
)
)
.build();
return card;
}
function runHighlight_(e) {
var doc = DocumentApp.getActiveDocument();
if (!doc) throw new Error('Нет активного документа');
highlightItalicInDocument_(doc);
return CardService.newActionResponseBuilder()
.setNotification(CardService.newNotification().setText('Готово: курсив подсвечен'))
.build();
}
function runClear_(e) {
var doc = DocumentApp.getActiveDocument();
if (!doc) throw new Error('Нет активного документа');
clearItalicBackground_(doc);
return CardService.newActionResponseBuilder()
.setNotification(CardService.newNotification().setText('Готово: подсветка снята'))
.build();
}
/* ===== ЛОГИКА ОБРАБОТКИ ДОКА ===== */
function highlightItalicInDocument_(doc) {
var body = doc.getBody();
if (body) {
var n = body.getNumChildren();
for (var i = 0; i < n; i++) processElementSafely_(body.getChild(i), true);
}
var header = doc.getHeader(); if (header) processElementSafely_(header, true);
var footer = doc.getFooter(); if (footer) processElementSafely_(footer, true);
}
function clearItalicBackground_(doc) {
var body = doc.getBody();
if (body) {
var n = body.getNumChildren();
for (var i = 0; i < n; i++) processElementSafely_(body.getChild(i), false);
}
var header = doc.getHeader(); if (header) processElementSafely_(header, false);
var footer = doc.getFooter(); if (footer) processElementSafely_(footer, false);
}
function processElementSafely_(el, doHighlight) {
try { processElement_(el, doHighlight); } catch (e) { console.log('Skip element: ' + e); }
}
function processElement_(el, doHighlight) {
var ET = DocumentApp.ElementType;
var t = el.getType();
if (t === ET.TEXT) {
if (doHighlight) highlightItalicInText_(el.asText());
else clearItalicInText_(el.asText());
return;
}
if (t === ET.PARAGRAPH || t === ET.LIST_ITEM) {
var cnt = el.getNumChildren ? el.getNumChildren() : 0;
if (cnt > 0) {
for (var i = 0; i < cnt; i++) processElementSafely_(el.getChild(i), doHighlight);
} else if (el.editAsText) {
var txt = el.editAsText();
if (doHighlight) highlightItalicInText_(txt);
else clearItalicInText_(txt);
}
return;
}
if (t === ET.TABLE) {
var table = el.asTable();
for (var r = 0; r < table.getNumRows(); r++) {
var row = table.getRow(r);
for (var c = 0; c < row.getNumCells(); c++) {
var cell = row.getCell(c);
for (var k = 0; k < cell.getNumChildren(); k++) {
processElementSafely_(cell.getChild(k), doHighlight);
}
}
}
return;
}
if (el.getNumChildren) {
for (var j = 0; j < el.getNumChildren(); j++) {
processElementSafely_(el.getChild(j), doHighlight);
}
}
}
/* ===== Работа с текстом (устойчиво по индексам) ===== */
function highlightItalicInText_(textElement) {
if (!textElement) return;
var text = textElement.getText(); if (!text) return;
var len = text.length; if (len === 0) return;
var indices = textElement.getTextAttributeIndices();
for (var i = 0; i < indices.length; i++) {
var start = indices[i];
var end = (i + 1 < indices.length ? indices[i + 1] - 1 : len - 1); // включительно
if (start > end || start < 0 || end >= len) continue;
if (textElement.isItalic(start)) {
textElement.setBackgroundColor(start, end, '#ffff00'); // жёлтая
}
}
}
function clearItalicInText_(textElement) {
if (!textElement) return;
var text = textElement.getText(); if (!text) return;
var len = text.length; if (len === 0) return;
var indices = textElement.getTextAttributeIndices();
for (var i = 0; i < indices.length; i++) {
var start = indices[i];
var end = (i + 1 < indices.length ? indices[i + 1] - 1 : len - 1); // включительно
if (start > end || start < 0 || end >= len) continue;
if (textElement.isItalic(start)) {
textElement.setBackgroundColor(start, end, null); // снять фон
}
}
}
@ovitente
Copy link
Author

appscript.json

{
  "timeZone": "Europe/Bucharest",
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes": [
    "https://www.googleapis.com/auth/documents.currentonly",
    "https://www.googleapis.com/auth/script.container.ui"
  ],
  "addOns": {
    "common": {
      "name": "Italic Highlighter",
      "logoUrl": "https://ssl.gstatic.com/docs/doclist/images/icon_11_document_list.png"
    },
    "docs": {
      "homepageTrigger": {
        "runFunction": "onHomepage",
        "enabled": true
      }
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment