TL; DR: используйте код фрагмент ниже.
Похоже, что то, что Google Docs делает при выборе «Вставка»> «Таблица», состоит в том, чтобы разбить текущий абзац на два абзаца, а затем вставить новую таблицу между ними.
Трудная часть состоит в том, чтобы разделить абзац на два. Я пытался добиться этого несколькими способами. Я не мог найти какой-либо API-запрос из документации Google Apps Script, которая делает это (API-интерфейс Spreadsheet API имеет метод moveTo()
на объекте Range
, но это не может помочь нам здесь). Я надеялся, что смогу скопировать элементы из данной позиции в абзаце дальше к другому абзацу и удалить оригиналы. Однако, так как Document Service does not allow explicit insertion of several element types, но может ими управлять только на месте, например, Equation
элементов, копирование этих один за другим невозможно.
К счастью, Paragraph
имеет метод copy()
, который выполняет глубокую копию. Поэтому я беру на себя внимание, чтобы скопировать весь абзац, удалить все из положения курсора вперед в исходном абзаце и удалить все до того места, где находился курсор в копии абзаца. Таким образом, вы разделяете абзац посередине, и вы можете вставить таблицу в нужное положение. Он работает одинаково для ListItem
.
Здесь код функции splitParagraphAt()
, который возвращает только что созданный абзац (или элемент списка, являющийся следующим родственником оригинала). Я бросил некоторые дополнительные проверки в коде, чтобы убедиться, что он делает то, что вы думаете, что он делает. После этого я добавил короткую выдержку кода о том, как это можно использовать для вставки таблицы в текущую позицию курсора. Можно использовать splitParagraphAt()
так же, как вставить любой элемент в позицию курсора. Я не тестировал это полностью, поэтому любой вход приветствуется.
/**
* Splits the contents of the paragraph (or list item) at the given position,
* producing two adjacent paragraphs (or list items). This function may be used
* to insert any kind of element at an arbitrary document position, but placing
* it immediately before the second paragraph (or list item).
*
* @param {Position} pos The position where the paragraph (or list item) should
* be split. `pos.getElement()` should be either a Text, Paragraph or
* ListItem object.
*
* @returns {ContainerElement} The second (newly created) Paragraph or ListItem
* object.
*
*/
function splitParagraphAt(pos) {
var el = pos.getElement(), offset = pos.getOffset();
var inParagraph = (el.getType() == DocumentApp.ElementType.PARAGRAPH || el.getType() == DocumentApp.ElementType.LIST_ITEM);
if (!inParagraph && (el.getType() != DocumentApp.ElementType.TEXT)) {
throw new Error("Position must be inside text or paragraph.");
}
var par;
if (inParagraph) {
// in this case, `offset` is the number of child elements before this
// Position within the same container element
par = el;
if (offset == par.getNumChildren()) {
// we're at the end of the paragraph
return par.getParent().insertParagraph(
par.getParent().getChildIndex(par) + 1, "");
}
el = par.getChild(offset);
}
else {
par = el.getParent();
if (par == null || (par.getType() != DocumentApp.ElementType.PARAGRAPH && par.getType() != DocumentApp.ElementType.LIST_ITEM)) {
throw new Error("Parent of text is not a paragraph or a list item.");
}
}
var parContainer = par.getParent();
if (!("insertParagraph" in parContainer)) {
throw new Error("Cannot insert another paragraph in this container.");
}
// This assumes the given position is in the current document.
// alternatively, one may traverse through parents of par until document
// root is reached.
var doc = DocumentApp.getActiveDocument();
var elIndex = par.getChildIndex(el);
var newPar = par.copy();
var newEl = newPar.getChild(elIndex);
// remove everything up to position from the new element
if (!inParagraph && (offset != 0)) {
newEl.deleteText(0, offset-1);
}
newEl = newEl.getPreviousSibling();
while (newEl != null) {
// get the previous sibling before we remove the element.
var prevEl = newEl.getPreviousSibling();
newEl.removeFromParent();
newEl = prevEl;
}
// since we might remove el itself, we get the next sibling here already
var nextEl = el.getNextSibling();
// remove everything from position onwards in the original element
if (!inParagraph && (offset != 0)) {
el.deleteText(offset, el.getText().length-1);
}
else {
// we're at the beginning of the text (or just before a paragraph
// subelement) and need to remove the entire text/subelement.
el.removeFromParent();
}
el = nextEl;
while (el != null) {
// get the next sibling before we remove the element.
nextEl = el.getNextSibling();
el.removeFromParent();
el = nextEl;
}
// actually insert the newly created paragraph into the document tree.
switch (par.getType()) {
case DocumentApp.ElementType.PARAGRAPH:
parContainer.insertParagraph(parContainer.getChildIndex(par)+1, newPar);
break;
case DocumentApp.ElementType.LIST_ITEM:
parContainer.insertListItem(parContainer.getChildIndex(par)+1, newPar);
break;
}
return newPar;
}
Вот фрагмент кода для вставки таблицы в позиции курсора и установки положения курсора в первой ячейке таблицы:
var doc = DocumentApp.getActiveDocument();
var cursor = doc.getCursor();
var el = (cursor.getOffset() == 0? cursor.getElement() : splitParagraphAt(cursor));
var parentEl = el.getParent();
var table = parentEl.insertTable(parentEl.getChildIndex(el), [['ಠ‿ಠ']]);
doc.setCursor(doc.newPosition(table.getCell(0, 0), 0));
Обратите внимание, что там еще нужно быть некоторые дополнительные проверки на посмотрите, есть ли выбор или нет, и т. д. В частности, это принятое cursor
не будет null
.