【问题标题】:replacing strings in a document and undo替换文档中的字符串并撤消
【发布时间】:2013-06-11 10:19:57
【问题描述】:

在我正在处理的 mailMerge 脚本中,我使用 .replaceText() 将字段替换为数据库中的相应值。

该界面允许在文档中进行测试,以查看结果是否符合预期,我需要一个“UNDO”功能来让我的字段处于原始位置,以便我可以将它与其他值一起使用。(这个脚本绑定到侧栏中的文档,请参阅this post for illustration)

下面的脚本通过将字段名称及其替换值保存在内存中来很好地做到这一点。 唯一困扰我的细节是我必须为当前测试数据中没有值的字段定义一个特殊的“空”标签,以防止在文档中丢失它们的踪迹。 (我使用了像°vide12°这样的编号标识符)。

这是完美的工作,但它并不理想,因为测试模式下的文档并不完全代表最终文档,因为我使用了这些°videXX°......

问题是:当没有数据以不太明显的方式出现时,是否有人有更好的想法或其他方法来“本地化”替换数据? (我知道这听起来很奇怪......这就是我解释整个情况的原因:-)

考虑到 Google Docs 的构建方式,我认为我可以获得完整的元素结构并根据该信息重建 doc,但我担心这是不可能的,因为最小的元素是一个段落,而字段主要只是单字……

这是我使用的代码的相关部分,我添加了一些 cmets 以使其(希望)清晰。

function valuesInDoc(e){ // this function replaces the fields with database values
  var app = UiApp.getActiveApplication(); 
  var listVal = UserProperties.getProperty('listSel').split(',');
  var replacements = [];
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();
  var find = body.findText('#ch');
  if(find == null){var ui = DocumentApp.getUi() ; ui.alert("Aucun champ (#chX#) trouvé dans le document... Veuillez insérer des identifiants aux endroits souhaités");return app};
  var curData =   UserProperties.getProperty('selItem').split('|');
  var Headers = [];
  var OriHeaders = UserProperties.getProperty('Headers').split('|');
  for(n=0;n<OriHeaders.length;++n){
    Headers.push('#'+OriHeaders[n]+'#');
  }
  var fctSpe = 0 ;
  for(var i in Headers){if(Headers[i].indexOf('SS')>-1){fctSpe = i}}
  for(var n=0;n<listVal.length;++n){
    var realIdx = Number(listVal[n]);
    var newField = ChampSpecial(curData,realIdx,fctSpe);
    if(newField!=''){replacements.push(newField+'∏'+'#ch'+(n+1)+'#')};
    //Logger.log('value in '+n+'='+realIdx+'  >>  '+Headers[realIdx]+'  =  '+ChampSpecial(curData,realIdx,fctSpe))
    app.getElementById('textField'+(n+1)).setHTML(ChampSpecial(curData,realIdx,fctSpe));
    if(e.parameter.source!='dataSelection'){
    body.replaceText('#ch'+(n+1)+'#',newField);
    }
  }
  UserProperties.setProperty('replacements',replacements.join('|'));// memorize the replacement pattern
  cloakOn();// hide hidden fields
  return app;
}



function fieldsInDoc(e){ // this function does the reverse process and restores the field identifiers
  cloakOff();// show hidden fields
  var replacements = UserProperties.getProperty('replacements').split('|');
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();
  for(var n=0;n<replacements.length;++n){
    var field = replacements[n].split('∏')[1];
    var testVal = replacements[n].split('∏')[0];    
    body.replaceText(testVal,field);
    }
}

function ChampSpecial(curData,idx,ref){ // this function handles a special case for a specific field, the relevant part is right below, see comment
  if(idx==-1){return''};
  if(curData[idx-1]==''){return'°vide'+idx+'°'};// this is the "empty" identifier
  if(idx<ref){return curData[idx]};
  if(idx>ref){return curData[idx-1]}
  var firstSpace = curData[idx-1].indexOf(' ');
  var apos = curData[idx-1].indexOf("'");
//Logger.log('firstSpace='+firstSpace+'  apos='+apos)
  if(firstSpace<4&&firstSpace>-1){return curData[idx-1].substring(firstSpace+1)};
  if(apos<3&&apos>-1){return curData[idx-1].substring(apos+1)};
  return curData[idx-1];
}

编辑:感谢 Mogsdad 的精彩回答,我编写了这 2 个函数来隐藏/显示未使用的字段。 Sinc 在我的例子中,我使用°XX°(XX=2 位数字)来跟踪未使用的字段,我必须修改他的代码以查找这个特定的字符串并使用 2 个循环来获取所有字段。

我从菜单和其他两个处理替换的函数中调用这些函数(我也更新了上面的代码)

这似乎是在浪费时间,因为我迭代了 100 次以上,但结果是瞬时的……那何必呢? 这是代码,以防它给某人一个想法。

function cloakOn() {
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();
  var found = [];
  for(var n=1;n<23;++n){
    for(var f=0;f<5;++f){
      if(f==0){found[f] = body.findText('°'+Utilities.formatString("%02d",n)+'°')}else{found[f] = body.findText('°'+Utilities.formatString("%02d",n)+'°',found[f-1])}
      if(found[f]!=null){
        var elemTxt = found[f].getElement().asText();
        elemTxt.setFontSize(found[f].getStartOffset(), found[f].getEndOffsetInclusive(),0)
        var background = elemTxt.getBackgroundColor(found[f].getStartOffset()) || "#ffffff";
        elemTxt.setForegroundColor(found[f].getStartOffset(), found[f].getEndOffsetInclusive(), background);
      }
    }
  }
}

function cloakOff() {
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();
  var found = [];
  for(var n=1;n<23;++n){
    for(var f=0;f<5;++f){
      if(f==0){found[f] = body.findText('°'+Utilities.formatString("%02d",n)+'°')}else{found[f] = body.findText('°'+Utilities.formatString("%02d",n)+'°',found[f-1])}
      if(found[f]!=null){
        var elemTxt = found[f].getElement().asText();
        var size = elemTxt.getParent().getFontSize();
        elemTxt.setFontSize(found[f].getStartOffset(), found[f].getEndOffsetInclusive(),size)
        var background = elemTxt.getBackgroundColor(found[f].getStartOffset()) || "#000000";
        elemTxt.setForegroundColor(found[f].getStartOffset(), found[f].getEndOffsetInclusive(), background);
      }
    }
  }
}

【问题讨论】:

    标签: google-apps-script


    【解决方案1】:

    Serge,我一直在解决同样的问题!我有一个部分解决方法要分享,还有一些想法可以更进一步。

    正如 Gill 在 old forum 上雄辩地指出的那样,无法在 Google 文档中嵌入隐藏文本。如果有,您的邮件合并将是微不足道的!

    但是,如何让您的标签或“cookie”(几乎)不可见?下面是一个向文档添加“隐藏”功能的脚本。它也有附加功能;它向用户查询要隐藏的文本,然后搜索该文本的所有实例并隐藏它们。我决定的想法是使文本尽可能小(fontsize 0)并将前景色与背景色相匹配。

    // in menu:       .addItem('Text Cloaking', 'cloakOn')
    
    /**
     * Find all matches of target text in current document, and cloak them.
     * At this time, that consists of making the text tiny, but still visible.
     * This is an experiment - my hope was to find a way to implement something
     * like document variables, placeholders that would not be forgotten, so
     * that values could be changed, or even dynamic.
     *
     * @param {String} target     (Optional) The text or regex to search for. 
     *                            See Body.findText() for details.
     * @param {String} background (Optional) The desired highlight color.
     *                            A default orange is provided.
     */
    function cloakOn(target) {
      // If no search parameter was provided, ask for one
      if (arguments.length == 0) {
        var ui = DocumentApp.getUi();
        var result = ui.prompt('Text Cloaking',
          'Enter text to cloak:', ui.ButtonSet.OK_CANCEL);
        // Exit if user hit Cancel.
        if (result.getSelectedButton() !== ui.Button.OK) return;
        // else
        target = result.getResponseText();
      }
      var doc = DocumentApp.getActiveDocument();
      var bodyElement = doc.getBody();
      var searchResult = bodyElement.findText(target);
    
      while (searchResult !== null) {
        var thisElement = searchResult.getElement();
        var thisElementText = thisElement.asText();
    
        //Logger.log(url);
        thisElementText.setFontSize(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive(),0);
        var background = thisElementText.getBackgroundColor(searchResult.getStartOffset()) || "#ffffff";
        thisElementText.setForegroundColor(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive(),
                                           background);
    
        // search for next match
        searchResult = bodyElement.findText(target, searchResult);
      }
    }
    

    为了在文本替换操作中使用它,替换文本将带有一个隐藏标签(就像你正在做的那样)。我认为你想让你的标签尽可能短,这样它们在最终文档中占据的空白就非常小——我在玩使用一系列 unicode 字符作为数字,给出大范围的 2 - 不太可能出现在任何其他上下文中的数字“数字”。

    【讨论】:

    • 绝妙的主意 :-) 我没想到用这种方式隐藏字段...我将立即实施,谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多