Jak sprawdzić google Doc z dodatku

A udokumentowane ograniczenie z dodatkami do dokumentów i arkuszy polega na tym, że skrypt aplikacji nie może określić, co robi użytkownik poza dodatkiem. Ta kusząca wskazówka jest podana:

Możliwe jest sprawdzenie zmian w zawartości pliku z kodu po stronie klienta sidebara, chociaż zawsze będziesz miał trochę opóźnienie. Ta technika może również ostrzegać skrypt o zmianach w wybrane komórki użytkownika (w arkuszach) oraz kursor lub zaznaczenie (w dokumentach).

Niestety to nie jest wyświetlany w żadnym kodzie demo. Jak mogę to zrobić?

Author: Mogsdad, 2014-07-16

1 answers

Ankieta odbywa się z kodu html w interfejsie użytkownika dodatku, wywołując funkcje skryptów aplikacji po stronie serwera za pomocą google.script.run.

Używanie jQuery upraszcza to, a możemy nawet zacząć od odpowiedzi z jQuery, prostego przykładu ankiety .

function doPoll(){
    $.post('ajax/test.html', function(data) {
        alert(data);  // process results here
        setTimeout(doPoll,5000);
    });
}
[9]} podstawowa idea może zadziałać w Google Apps Script, jeśli zastąpimy wywołania ajax odpowiednikami gazu.

Oto szkielet funkcji ankiety, której użyłbyś w swoim html plik:

  /**
   * On document load, assign click handlers to button(s), add
   * elements that should start hidden (avoids "flashing"), and
   * start polling for document updates.
   */
  $(function() {
    // assign click handler(s)

    // Add elements that should start hidden

    // Start polling for updates
    poll();
  });

  /**
   * Poll a server-side function 'serverFunction' at the given interval 
   * and update DOM elements with results. 
   *
   * @param {Number} interval   (optional) Time in ms between polls.
   *                            Default is 2s (2000ms)
   */
  function poll(interval){
    interval = interval || 2000;
    setTimeout(function(){
      google.script.run
       .withSuccessHandler(
         function(results) {
           $('#some-element').updateWith(results);
           //Setup the next poll recursively
           poll(interval);
         })
       .withFailureHandler(
         function(msg, element) {
           showError(msg, $('#button-bar'));
           element.disabled = false;
         })
       .serverFunction();
    }, interval);
  };

Przykład dodatku, Document Poller

Jest to demonstracja techniki sondażu jQuery wywołującej po stronie serwera funkcje skryptów Google Apps do wykrywania zachowań użytkowników w dokumencie Google. Nie robi nic użytecznego, ale prezentuje kilka rzeczy, które zazwyczaj wymagają wiedzy o aktywności użytkownika i stanie dokumentu, na przykład kontrolę kontekstową nad przyciskiem.

Ta sama zasada może mieć zastosowanie do arkusza kalkulacyjnego lub samodzielnego Gazowa aplikacja internetowa.

Podobnie jak przykład aplikacji UI w to pytanie, ta technika może być używana do obejścia limitów czasu wykonania, przynajmniej dla operacji z interfejsem użytkownika.

Zrzut ekranu

Kod opiera się na przykładowym dodatku z Google 5-minute quickstart. Postępuj zgodnie z instrukcjami z tego przewodnika, używając poniższego kodu zamiast tego w quickstart.

Code.gs

/**
 * Creates a menu entry in the Google Docs UI when the document is opened.
 *
 * @param {object} e The event parameter for a simple onOpen trigger. To
 *     determine which authorization mode (ScriptApp.AuthMode) the trigger is
 *     running in, inspect e.authMode.
 */
function onOpen(e) {
  DocumentApp.getUi().createAddonMenu()
      .addItem('Start', 'showSidebar')
      .addToUi();
}

/**
 * Runs when the add-on is installed.
 *
 * @param {object} e The event parameter for a simple onInstall trigger. To
 *     determine which authorization mode (ScriptApp.AuthMode) the trigger is
 *     running in, inspect e.authMode. (In practice, onInstall triggers always
 *     run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or
 *     AuthMode.NONE.)
 */
function onInstall(e) {
  onOpen(e);
}

/**
 * Opens a sidebar in the document containing the add-on's user interface.
 */
function showSidebar() {
  var ui = HtmlService.createHtmlOutputFromFile('Sidebar')
      .setTitle('Document Poller');
  DocumentApp.getUi().showSidebar(ui);
}

/**
 * Check if there is a current text selection.
 *
 * @return {boolean}  'true' if any document text is selected
 */
function checkSelection() {
  return {isSelection : !!(DocumentApp.getActiveDocument().getSelection()),
          cursorWord : getCursorWord()};
}

/**
 * Gets the text the user has selected. If there is no selection,
 * this function displays an error message.
 *
 * @return {Array.<string>} The selected text.
 */
function getSelectedText() {
  var selection = DocumentApp.getActiveDocument().getSelection();
  if (selection) {
    var text = [];
    var elements = selection.getSelectedElements();
    for (var i = 0; i < elements.length; i++) {
      if (elements[i].isPartial()) {
        var element = elements[i].getElement().asText();
        var startIndex = elements[i].getStartOffset();
        var endIndex = elements[i].getEndOffsetInclusive();

        text.push(element.getText().substring(startIndex, endIndex + 1));
      } else {
        var element = elements[i].getElement();
        // Only translate elements that can be edited as text; skip images and
        // other non-text elements.
        if (element.editAsText) {
          var elementText = element.asText().getText();
          // This check is necessary to exclude images, which return a blank
          // text element.
          if (elementText != '') {
            text.push(elementText);
          }
        }
      }
    }
    if (text.length == 0) {
      throw 'Please select some text.';
    }
    return text;
  } else {
    throw 'Please select some text.';
  }
}

/**
 * Returns the word at the current cursor location in the document.
 *
 * @return {string}   The word at cursor location.
 */
function getCursorWord() {
  var cursor = DocumentApp.getActiveDocument().getCursor();
  var word = "<selection>";
  if (cursor) {
    var offset = cursor.getSurroundingTextOffset();
    var text = cursor.getSurroundingText().getText();
    word = getWordAt(text,offset);
    if (word == "") word = "<whitespace>";
  }
  return word;
}

/**
 * Returns the word at the index 'pos' in 'str'.
 * From https://stackoverflow.com/questions/5173316/finding-the-word-at-a-position-in-javascript/5174867#5174867
 */
function getWordAt(str, pos) {

  // Perform type conversions.
  str = String(str);
  pos = Number(pos) >>> 0;

  // Search for the word's beginning and end.
  var left = str.slice(0, pos + 1).search(/\S+$/),
      right = str.slice(pos).search(/\s/);

  // The last word in the string is a special case.
  if (right < 0) {
    return str.slice(left);
  }

  // Return the word, using the located bounds to extract it from the string.
  return str.slice(left, right + pos);
}

Sidebar.html

<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
<!-- The CSS package above applies Google styling to buttons and other elements. -->


<div class="sidebar branding-below">
  <form>
    <div class="block" id="button-bar">
      <button class="blue" id="get-selection" disabled="disable">Get selection</button>
    </div>
  </form>
</div>

<div class="sidebar bottom">
  <img alt="Add-on logo" class="logo" height="27"
      id="logo"
      src="https://www.gravatar.com/avatar/adad1d8ad010a76a83574b1fff4caa46?s=128&d=identicon&r=PG">
  <span class="gray branding-text">by Mogsdad, D.Bingham</span>
</div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<script>
  /**
   * On document load, assign click handlers to button(s), add
   * elements that should start hidden (avoids "flashing"), and
   * start polling for document selections.
   */
  $(function() {
    // assign click handler(s)
    $('#get-selection').click(getSelection);


    // Add elements that should start hidden
    var newdiv1 = $( "<div class='block' id='cursor-word'/>" ).hide(),
        newdiv2 = $( "<div class='block' id='selected-text'/>" ).hide();
    $('#button-bar').after( newdiv1, newdiv2 );
    $('#cursor-word').html('<H2>Word at cursor:</H2><p id="cursor-word-content"></p>');
    $('#selected-text').html('<H2>Selected text:</H2><p id="selected-text-content"></p>');

    // Start polling for updates        
    poll();
  });

  /**
   * Poll the server-side 'checkSelection' function at the given
   * interval for document selection, and enable or disable the
   * '#get-selection' button.
   *
   * @param {Number} interval   (optional) Time in ms between polls.
   *                            Default is 2s (2000ms)
   */
  function poll(interval){
    interval = interval || 2000;
    setTimeout(function(){
      google.script.run
       .withSuccessHandler(
         function(cursor) {
           if (cursor.isSelection) {
             // Text has been selected: enable button, hide cursor word.
             $('#get-selection').attr('disabled', false);
             $('#cursor-word').hide();
             // $('#selected-text').show();  // Not so fast - wait until button is clicked.
           }
           else {
             $('#get-selection').attr('disabled', true);
             $('#cursor-word').show();
             $('#selected-text').hide();
           }
           $('#cursor-word-content').text(cursor.cursorWord);
           //Setup the next poll recursively
           poll(interval);
         })
       .withFailureHandler(
         function(msg, element) {
           showError(msg, $('#button-bar'));
           element.disabled = false;
         })
       .checkSelection();
    }, interval);
  };

  /**
   * Runs a server-side function to retrieve the currently
   * selected text.
   */
  function getSelection() {
    this.disabled = true;
    $('#error').remove();
    google.script.run
      .withSuccessHandler(
        function(selectedText, element) {
          // Show selected text
          $('#selected-text-content').text(selectedText);
          $('#selected-text').show();
          element.disabled = false;
        })
      .withFailureHandler(
        function(msg, element) {
          showError(msg, $('#button-bar'));
          element.disabled = false;
        })
      .withUserObject(this)
      .getSelectedText();
  }


  /**
   * Inserts a div that contains an error message after a given element.
   *
   * @param msg The error message to display.
   * @param element The element after which to display the error.
   */
  function showError(msg, element) {
    var div = $('<div id="error" class="error">' + msg + '</div>');
    $(element).after(div);
  }
</script>

Ankieta Interwał

Funkcja setTimeout() przyjmuje przedział czasu wyrażony w milisekundach, ale odkryłem poprzez eksperymenty, że dwu-sekundowa odpowiedź była najlepsza, jakiej można było się spodziewać. Dlatego szkielet {[6] } ma domyślnie interwał 2000ms. Jeśli Twoja sytuacja toleruje dłuższe opóźnienie między cyklami ankiety, podaj większą wartość wywołaniem onLoad do poll(), np. poll(10000) dla 10-sekundowego cyklu ankiety.

Arkusze

Przykład arkusza zobacz Jak aby pasek boczny wyświetlał wartości z komórek?

 22
Author: Mogsdad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:25