Get All Links in a Document

后端 未结 7 1321
余生分开走
余生分开走 2020-12-01 08:33

Given a \"normal document\" in Google Docs/Drive (e.g. paragraphs, lists, tables) which contains external links scattered throughout the content, how do you compile a list o

7条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-01 09:08

    I was playing around and incorporated @Mogsdad's answer -- here's the really complicated version:

    var _ = Underscorejs.load(); // loaded via http://googleappsdeveloper.blogspot.com/2012/11/using-open-source-libraries-in-apps.html, rolled my own
    var ui = DocumentApp.getUi();
    
    // #region --------------------- Utilities -----------------------------
    
    var gDocsHelper = (function(P, un) {
      // heavily based on answer https://stackoverflow.com/a/18731628/1037948
    
      var updatedLinkText = function(link, offset) {
        return function() { return 'Text: ' + link.getText().substring(offset,100) + ((link.getText().length-offset) > 100 ? '...' : ''); }
      }
    
      P.updateLink = function updateLink(link, oldText, newText, start, end) {
        var oldLink = link.getLinkUrl(start);
    
        if(0 > oldLink.indexOf(oldText)) return false;
    
        var newLink = oldLink.replace(new RegExp(oldText, 'g'), newText);
        link.setLinkUrl(start || 0, (end || oldLink.length), newLink);
        log(true, "Updating Link: ", oldLink, newLink, start, end, updatedLinkText(link, start) );
    
        return { old: oldLink, "new": newLink, getText: updatedLinkText(link, start) };
      };
    
      // moving this reused block out to 'private' fn
      var updateLinkResult = function(text, oldText, newText, link, urls, sidebar, updateResult) {
        // and may as well update the link while we're here
        if(false !== (updateResult = P.updateLink(text, oldText, newText, link.start, link.end))) {
           sidebar.append('
  • ' + updateResult['old'] + ' → ' + updateResult['new'] + ' at ' + updateResult['getText']() + '
  • '); } urls.push(link.url); // so multiple links get added to list }; P.updateLinksMenu = function() { // https://developers.google.com/apps-script/reference/base/prompt-response var oldText = ui.prompt('Old link text to replace').getResponseText(); var newText = ui.prompt('New link text to replace with').getResponseText(); log('Replacing: ' + oldText + ', ' + newText); var sidebar = gDocUiHelper.createSidebar('Update All Links', '

    Replacing

    ' + oldText + '' + newText + '


      '); // current doc available to script var doc = DocumentApp.getActiveDocument().getBody();//.getActiveSection(); // Search until a link is found var links = P.findAllElementsFor(doc, function(text) { var i = -1, n = text.getText().length, link = false, url, urls = [], updateResult; // note: the following only gets the FIRST link in the text -- while(i < n && !(url = text.getLinkUrl(i++))); // scan the text element for links while(++i < n) { // getLinkUrl will continue to get a link while INSIDE the stupid link, so only do this once if(url = text.getLinkUrl(i)) { if(false === link) { link = { start: i, end: -1, url: url }; // log(true, 'Type: ' + text.getType(), 'Link: ' + url, function() { return 'Text: ' + text.getText().substring(i,100) + ((n-i) > 100 ? '...' : '')}); } else { link.end = i; // keep updating the end position until we leave } } // just left the link -- reset link tracking else if(false !== link) { // and may as well update the link while we're here updateLinkResult(text, oldText, newText, link, urls, sidebar); link = false; // reset "counter" } } // once we've reached the end of the text, must also check to see if the last thing we found was a link if(false !== link) updateLinkResult(text, oldText, newText, link, urls, sidebar); return urls; }); sidebar.append('

    ' + links.length + ' links reviewed

    '); gDocUiHelper.attachSidebar(sidebar); log(links); }; P.findAllElementsFor = function(el, test) { // generic utility function to recursively find all elements; heavily based on https://stackoverflow.com/a/18731628/1037948 var results = [], searchResult = null, i, result; // https://developers.google.com/apps-script/reference/document/body#findElement(ElementType) while (searchResult = el.findElement(DocumentApp.ElementType.TEXT, searchResult)) { var t = searchResult.getElement().editAsText(); // .asParagraph() // check to add to list if(test && (result = test(t))) { if( _.isArray(result) ) results = results.concat(result); // could be big? http://jsperf.com/self-concatenation/ else results.push(result); } } // recurse children if not plain text item if(el.getType() !== DocumentApp.ElementType.TEXT) { i = el.getNumChildren(); var result; while(--i > 0) { result = P.findAllElementsFor(el.getChild(i)); if(result && result.length > 0) results = results.concat(result); } } return results; }; return P; })({}); // really? it can't handle object properties? function gDocsUpdateLinksMenu() { gDocsHelper.updateLinksMenu(); } gDocUiHelper.addMenu('Zaus', [ ['Update links', 'gDocsUpdateLinksMenu'] ]); // #endregion --------------------- Utilities -----------------------------

    And I'm including the "extra" utility classes for creating menus, sidebars, etc below for completeness:

    var log = function() {
      // return false;
    
      var args = Array.prototype.slice.call(arguments);
    
      // allowing functions delegates execution so we can save some non-debug cycles if code left in?
    
      if(args[0] === true) Logger.log(_.map(args, function(v) { return _.isFunction(v) ? v() : v; }).join('; '));
      else
        _.each(args, function(v) {
          Logger.log(_.isFunction(v) ? v() : v);
        });
    }
    
    // #region --------------------- Menu -----------------------------
    
    var gDocUiHelper = (function(P, un) {
    
      P.addMenuToSheet = function addMenu(spreadsheet, title, items) {
        var menu = ui.createMenu(title);
        // make sure menu items are correct format
        _.each(items, function(v,k) {
          var err = [];
    
          // provided in format [ [name, fn],... ] instead
          if( _.isArray(v) ) {
            if ( v.length === 2 ) {
              menu.addItem(v[0], v[1]);
            }
            else {
              err.push('Menu item ' + k + ' missing name or function: ' + v.join(';'))
            }
          }
          else {
            if( !v.name ) err.push('Menu item ' + k + ' lacks name');
            if( !v.functionName ) err.push('Menu item ' + k + ' lacks function');
    
            if(!err.length) menu.addItem(v.name, v.functionName);
          }
    
          if(err.length) {
            log(err);
            ui.alert(err.join('; '));
          }
    
        });
    
        menu.addToUi();
      };
    
      // list of things to hook into
      var initializers = {};
    
      P.addMenu = function(menuTitle, menuItems) {
        if(initializers[menuTitle] === un) {
          initializers[menuTitle] = [];
        }
        initializers[menuTitle] = initializers[menuTitle].concat(menuItems);
      };
    
      P.createSidebar = function(title, content, options) {
        var sidebar = HtmlService
        .createHtmlOutput()
        .setTitle(title)
        .setWidth( (options && options.width) ? width : 350 /* pixels */);
    
        sidebar.append(content);
    
        if(options && options.on) DocumentApp.getUi().showSidebar(sidebar);
        // else { sidebar.attach = function() { DocumentApp.getUi().showSidebar(this); }; } // should really attach to prototype...
    
        return sidebar;
      };
    
      P.attachSidebar = function(sidebar) {
        DocumentApp.getUi().showSidebar(sidebar);
      };
    
    
      P.onOpen = function() {
        var spreadsheet = SpreadsheetApp.getActive();
        log(initializers);
        _.each(initializers, function(v,k) {
          P.addMenuToSheet(spreadsheet, k, v);
        });
      };
    
      return P;
    })({});
    
    // #endregion --------------------- Menu -----------------------------
    
    /**
     * A special function that runs when the spreadsheet is open, used to add a
     * custom menu to the spreadsheet.
     */
    function onOpen() {
      gDocUiHelper.onOpen();
    }
    

提交回复
热议问题