Разберемся что это, как это работает и напишем небольшое приложение которое позволит нам переводить текст документа не выходя из Google Docs.
Немного теории.
Google Docs Аdd-on — приложение, написанное на javascript’е, существующее и работающее в Google Drive (сейчас пока только для Google Docs), имеющее свой UI (‘sidebar’ справа от документа или модальное окно поверх), расширяющее функционал Google Docs редактора. Google Docs Аdd-on является следующим шагом в развитии Google Apps Script. По сути это Google Apps Script проект с возможностью распространения и установки его другими пользователями Google Docs.
Существует несколько вида Google Apps Script проектов, основные из них — отдельный файл-проект в Google Drive (В Google Drive вы видите его как отдельный файл, ‘standalone script’) или проект с документом-контейнером (‘container-bounds script’). Его вы можете увидеть открыв документ-контейнер, меню ‘Tools’ -> ‘Script editor’. Google Docs Аdd-on — как раз второй вариант, для его создания необходим документ-контейнер, который вы будете использовать для тестирования и отладки вашего приложения.
Распространение
Распространяются Google Docs Аdd-on через Chrome Web store (хотя и не являются расширением браузера). Перед добавлением вашего add-on’a в Chrome Web Store необходимо пройти review со стороны Google. Для пользователей Google Docs все add-on’ы доступны для установки через новый пункт меню — ‘Add-ons’ -> ‘Get Add-ons’.
Среда разработки
Для Google Apps Script проектов Google предоставляет среду разработки, интегрированную в Google Drive, с возможностью запуска, отладки и автодополнения кода. Есть ли возможность использовать другую среду разработки и хранить код не в Google Drive, а в CVS? Это позволяло бы работать над проектом нескольким разработчикам одновременно и поддерживать версионирование. Для ‘standalone’ проектов такая возможность есть, для ‘сontainer-bounds’ — к сожалению пока нет. Поэтому для разработки Google Docs Add-on’a приходится использовать интегрированную среду разработки. Вот пример python скрипта, который позволяет «выкачать» ‘standalone’ проект на файловую систему и обновить его в Google Drive после изменения.
Что внутри
Немного подробнее о том, как работает Google Apps Script. Файлы проекта содержат «серверную» (.gs) и «клиентскую» (.html) части приложения. «Серверная» часть — набор javascript функций, которые могут обращаться к сторонним сервисам, различным Google API (Drive, GMail, Calendar), в том числе к API документа, в котором установлен данный add-on.
Также ‘.gs’ файлы содержат предопределенные функции, которые вызываются когда пользователь устанавливает/открывает add-on, что позволяет создавать UI вашего приложения внутри стандартного редактора Google Docs. В свою очередь «клиесткая часть» представляет собой стандартные html файлы, с возможностью вызова любой функции из любого «серверного» файла. А также, естественно, с возможностью подключения сторонних CSS/javascript файлов.
Пример
Напишем приложение, которое будет переводить выделенный текст в документе с английского на русский язык. Создаем новый Google Docs документ в Google Drive, открываем меню ‘Tools’ -> ‘Script editor’. В появившемся диалоге выбираем ‘Create script for -> Document’. После этого вы увидите ту самую интегрированную среду разработки. В проекте по умолчанию создается файл ‘Code.gs’, содержащий пример приложения от Google. Нам он пока не нужен, поэтому я сразу предлагаю очистить его содержимое, а сам файл переименовать в ‘Server.gs’. Также лучше сразу переименовать проект из ‘Untitled project’, скажем в ‘Translate example’.
Итак, в ‘Server.gs’ мы определим функции, которые будут создавать UI нашего add-on’a. Для этого достаточно объявить функцию ‘onOpen’.
function onOpen() { DocumentApp.getUi().createAddonMenu() .addItem('Translate', 'openSidebar') .addToUi(); } function openSidebar( ) {}
Теперь, при открытии документа, в котором установлен данный add-on, в специальном меню ‘Add-ons’ появиться пункт ‘Translate’. Нажатие на данный пункт меню будет вызывать функцию ‘openSidebar’, которая пока ничего не делает. Чтобы проверить это, в среде разработки выберите функцию «onOpen» и нажмите ‘Run’
Теперь в вашем документе появился пункт меню ‘Add-ons’ -> ‘Translate example’ (название вашего проекта) -> ‘Translate’
Займемся разработкой UI для нашего add-on’a. Так как UI add-on’a представляет собой html страницу, то есть смысл сразу разделить HTML разметку, CSS и javascript код на разные файлы. Для этого мы создадим файлы ‘Sidebar.html’, ‘Styles.html’, ‘Scripts.html’ и ‘Content.html’. ‘Sidebar.html’ будет файлом-темплейтом, включающем в себя остальные файлы.
UI всех add-on’ов должен следовать определенному style guide, поэтому Google предоставляет специальный CSS файл с предопределенными стилями, ссылку на который вы видите в примере.
Открываем ‘Content.html’ и ‘Styles.html’ и создаем разметку
<div class="content"> <p>Select text and click 'Translate'</p> <button class="action btn-block">Translate</button> <p class="result"></p> </div>
и стили
<style> .content { padding: 20px; margin : 20px; } .btn-block { display: block; width: 100%; padding-left: 0; padding-right: 0; } </style>
а в ‘Server.gs’ дописываем тело функции ‘openSidebar’
function onOpen() { DocumentApp.getUi().createAddonMenu() .addItem('Translate', 'openSidebar') .addToUi(); } function openSidebar( ) { var html = HtmlService.createTemplateFromFile('Sidebar') .evaluate() .setSandboxMode(HtmlService.SandboxMode.NATIVE) .setTitle('Translate example') .setWidth(300); DocumentApp.getUi().showSidebar(html); }
Теперь по нажатию пункт меню «Translate» в документе, слева, откроется ‘sidebar’.
Осталось добавить логику для перевода текста. Для этого мы будем использовать API различных Google сервисов, доступных нам в ‘.gs’ файлах и позволяющих находить выделенный в документе текст и переводить его на выбранный язык.
‘Server.gs’
function translate(pollingCounter) { var text = getSelectedText(getSingleSeletetedElement()); if (text) { return LanguageApp.translate(text, 'en', 'ru'); } } function getSelectedText(rangeElement) { if (!rangeElement) { return; } var element = rangeElement.getElement(); var from = rangeElement.getStartOffset(); var to = rangeElement.getEndOffsetInclusive() + 1; if (element.getType() === DocumentApp.ElementType.TEXT || element.getType() === DocumentApp.ElementType.PARAGRAPH) { var text = element.getText(); return text.substring(from, to); } } function getSingleSeletetedElement() { var selection = DocumentApp.getActiveDocument().getSelection(); if (selection) { var elements = selection.getSelectedElements(); if (elements.length === 1) { return elements[0]; } } }
Функция ‘translate’ ищет выделенный элемент в документе, и если это текст или текстовый блок — получает выделенную часть текста и переводит ее с английского на русский язык. Теперь нам нужно как-то вызвать эту функцию из нашего UI. Для этого javascript код, запущенный в ‘sidebar’ имеют доступ к глобальному объекту ‘google.script.run’, позволяющему вызвать любую функцию из любого ‘.gs’ файла.
‘Scripts.html’
$(function() { $('.action').click(function() { google.script.run.withSuccessHandler(function(text) { if (text) { $('.result').text('Result: ' + text); } else { $('.result').text('Please, select text to translate'); } }).translate(); }); });
Готово, теперь наш add-on может переводить выделенный текст по нажатию на клавишу ‘Translate’.
С ходу можно внести 2 улучшения. Во-первых, при открытии ‘sidebar’, пока загружаются все необходимые для него файлы, было бы неплохо добавить какой-нибудь индикатор загрузки. Создадим файл ‘Loading.html’, добавим ссылку на него в наш ‘Sidebar.html’, а основное содержимое спрячем до полной загрузки всех ресурсов. Как только все готово — мы прячем индикатор и показываем наш UI.
‘Loading.html’
<div class="app-loading" style="text-align:center"> <br><br><br><br><br><br> Loading... </div>
Content.html
<div class="content" style="display:none"> <p>Select text and click 'Translate'</p> <button class="action btn-block">Translate</button> <p class="result"></p> </div>
Sidebar.html
<!-- styles --> <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css"> <?!= HtmlService.createHtmlOutputFromFile('Styles').getContent(); ?> <!-- layout --> <?!= HtmlService.createHtmlOutputFromFile('Loading').getContent(); ?> <?!= HtmlService.createHtmlOutputFromFile('Content').getContent(); ?> <!--3rd party scripts --> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <!-- application scripts --> <?!= HtmlService.createHtmlOutputFromFile('Scripts').getContent(); ?>
и наконец в Scripts.html
$(function() { $('.app-loading').hide(); $('.content').show(); $('.action').click(function() { google.script.run.withSuccessHandler(function(text) { if (text) { $('.result').text('Result: ' + text); } else { $('.result').text('Please, select text to translate'); } }).translate(); }); });
А во-вторых, было бы неплохо автоматически получать перевод выделенного текста из документа, без нажатия кнопки ‘Translate’. Для решения этой задачи нужно придумать как обойти одно из ограничений, существующее сейчас в Google Docs Аdd-on — нет event модели, которая позволяла бы Google Script Аdd-on’у реагировать на действия пользователя в документе. Нам прийдется имитировать интерактивность add-on’a используя long polling — постоянные запросы со стороны UI, которые будут вынуждать наш ‘ Server.gs’ проверять документ на наличие выделения и переводить текст. Для этого напишем в ‘Script.html’ функцию, которая будет запускать 2 процесса с определенным интервалом. Первый процесс будет вызывать ‘translate’ функцию и просто сохранять результат перевода. Второй процесс будет проверять на наличие результат перевода и обновлять UI.
‘Scripts.html’
var LongPollingManager = (function() { var process = function(options) { var pollingTimeout = 1500, notificationTimeout = 1500, pollingCounter = 0, pollingStopped = false, pollingResult, pollingProcess, notificationProcess; var start = function() { pollingStopped = false; pollingProcess = setInterval(function() { if (pollingStopped) { return; } pollingCounter++; google.script.run.withSuccessHandler(function(response) { if (pollingStopped) { return; } if (!pollingResult || response.pollingCounter > pollingResult.pollingCounter) { pollingResult = response; } })[options.method](pollingCounter); }, pollingTimeout); setTimeout(function() { notificationProcess = setInterval(function() { if (pollingStopped) { return; } options.notification(getLastResult()); }, notificationTimeout); }, notificationTimeout/2); }; var getLastResult = function() { return pollingResult; }; var stop = function() { pollingResult = null; pollingStopped = true; clearInterval(pollingProcess); clearInterval(notificationProcess); }; return { start: start, stop : stop, isActive: function() { return !pollingStopped; }, getLastResult : getLastResult };; }; return { process: process }; })(); var showResult = function(result) { if (result && result.text) { $('.result').text('Result: ' + result.text); } else { $('.result').text(''); } }; $(function() { $('.loading').hide(); $('.content').show(); $('.action').click(function() { google.script.run.withSuccessHandler(function(text) { if (text) { $('.result').text('Result: ' + text); } else { $('.result').text('Please, select text to translate'); } }).translate(); }); LongPollingManager.process({ method: 'translate', notification: showResult }).start(); });
‘Server.gs’
function translate(pollingCounter) { var result = { pollingCounter : pollingCounter }; var text = getSelectedText(getSingleSeletetedElement()); if (text) { result.text = LanguageApp.translate(text, 'en', 'ru'); } return result; }
Готово, теперь наш add-on автоматически переводит выделенный текст.
Документ + код проекта на Google Drive
Код на github
Заключение
Итак, Google Docs add-on — новая платформа, позволяющая создавать приложения, расширяющие возможности базового редактора Google Docs.
Плюсы и минусы, на мой взгляд:
- + Огромная аудитория Google Docs пользователей.
- + Разработка приложения полностью на javascript.
- + Доступ к многочисленным Google API и любому стороннему сервису (HTTP запросы).
- — Разработка проекта внутри Google Docs, нет возможности вести разработку локально, трудно разделить работу между несколькими разработчиками.
- — Отсутствие интерактивного взаимодействия с документом (нет возможности получать оповещения о каких-либо действия пользователя в документе.
P. S.
Я думаю что в соперничестве MS Office — Google Drive, Google решил сделать ставку на простоту, удобство и сообщество разработчиков. Поясню на примере MS Word vs Goodle Docs. MS Word имеет огромное количество доступных пользователю функций (и как следствие — перегруженный и сложный UI) и не имеет открытого API. Google Docs — наоборот, обладает минимально необходимым набором инструментов и открытым API. Теперь любую недостающую вам функцию вы можете найти в галлерее Google Docs add-on’ов или написать сами. А пользователь может добавить в свой документ только нужные ему функции.
ссылка на оригинал статьи http://habrahabr.ru/post/215843/
Добавить комментарий