替换文档中的字符串并撤消 [英] replacing strings in a document and undo

查看:122
本文介绍了替换文档中的字符串并撤消的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



该接口允许在文档中测试以查看结果是否与预期一致,并且需要有一个UNDO函数来让我的字段处于原始位置,以便我可以将其与其他字段一起使用值(这个脚本被绑定到侧栏中的文档中,参见这篇文章是为了说明

下面的脚本做得非常好保留在记忆中的字段名称替换值。
困扰我的唯一细节是,我必须为当前测试数据中没有值的字段定义一个特殊的空白标签,以防止在文档中丢失其跟踪。
(我使用了一个编号的标识符,例如°vide12°)。

这个功能非常完美,但它并不理想,因为处于测试模式的文档不完全是一种表示因为我使用了这些°videXX°...



问题是:没有人有更好的主意或另一种方法来置换替换没有数据的数据不太明显? (我知道这听起来很奇怪......这就是为什么我解释整个情况的原因: - )

考虑到Google Docs的构建方式,我认为我可以获得完整的元素结构和重建从该信息的文档,但恐怕它是不可能的,因为最小的元素是一个段落,字段主要是单个单词......

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

 函数valuesInDoc(e){//这个函数用数据库值替换字段
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 ... Veuillezinsérerdes identifiants aux endroitssouhaités); return app};
var curData = UserProperties.getProperty('selItem')。split('|');
var Headers = [];
var OriHeaders = UserProperties.getProperty('Headers')。split('|');
for(n = 0; n Headers.push('#'+ OriHeaders [n] +'#');
}
var fctSpe = 0; (var n = 0; n< listVal)中的
{if(Headers [i] .indexOf('SS')> -1){fctSpe = i}}
.length; ++ n){
var realIdx = Number(listVal [n]);
var newField = ChampSpecial(curData,realIdx,fctSpe);
if(newField!=''){replacements.push(newField +'Π'+'#ch'+(n + 1)+'#')};
//Logger.log('+ 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);

$ b $ UserProperties.setProperty('replacements',replacements.join('|')); //记住替换模式
cloakOn(); //隐藏字段
返回应用程序;



$ b函数fieldsInDoc(e){//这个函数完成相反的过程并且恢复字段标识符
cloakOff(); //显示隐藏字段
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);



函数ChampSpecial(curData,idx,ref){//此函数处理特定字段的特殊情况,相关部分在下面,参见注释
if(idx == - 1){return''};
if(curData [idx-1] ==''){return'°vide'+ idx +'°'}; //这是空标识符
if(idx< ref){返回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 +''='+')
if(firstSpace< 4& firstSpace> -1){return curData [idx-1] .substring (firstSpace + 1)}; (< 3& -1){return curData [idx-1] .substring(apos + 1)};

return curData [idx-1];

$ / code $ / pre

$ hr
$ b

编辑: 感谢Mogsdad的出色答案,我编写了这两个函数来隐藏/显示未使用的字段。 Sinc在我的情况下,我使用°XX°(XX = 2位数字)跟踪未使用的字段,我不得不修改他的代码以查找此特定字符串,并使用2个循环来获取所有字段。



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

这可能会浪费时间,因为我迭代了100次,但结果是瞬间的...所以为什么要麻烦呢?
这里是代码,如果它给了某人一个想法。

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




$ b函数cloakOff(){
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var found = []; (var f = 0; f <5; ++ f){
if(f == 0)($ f $ 0)
(var n = 1; n <23; ++ n) ){found [f] = body.findText('°'+ Utilities.formatString(%02d,n)+'°')} else {found [f] = body.findText('°'+ Utilities.formatString (找到[f]!= null){
var elemTxt = found [f] .getElement(找到[f-1])('%02d',n)+'°' ).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(找到[f] .getStartOffset(),找到[f] .getEndOffsetInclusive(),background);
}
}
}
}


解决方案

Serge,我一直在研究同样的问题!



无法在Google文档中嵌入隐藏文字,正如Gill雄辩地指出的那样在旧论坛上。如果有的话,你的邮件合并将是微不足道的。

尽管如何让你的标签或cookies(几乎)不可见?下面是一个为文档添加隐藏功能的脚本。它也有额外的功能;它会向用户查询文本以隐藏文本,然后搜索该文本的所有实例并隐藏它们。我决定的想法是让文本尽可能小(字体大小为0),并将前景色与背景色匹配。

  //在菜单中:.addItem('Text Cloaking','cloakOn')

/ **
*在当前文档中查找目标文本的所有匹配项,并隐藏它们。
*目前,这包括使文本很小,但仍然可见。
*这是一个实验 - 我希望找到一种方法来实现
*类似于文档变量,不会被遗忘的占位符,所以
*值可以被改变,甚至是动态。
*
* @param {String} target(可选)要搜索的文本或正则表达式。
*详细信息请参阅Body.findText()。
* @param {String} background(可选)所需的高亮颜色。
*提供默认橙色。
* /
函数cloakOn(target){
//如果没有提供搜索参数,请求一个
if(arguments.length == 0){
var ui = DocumentApp.getUi();
var result = ui.prompt('Text Cloaking',
'输入文本到cloak:',ui.ButtonSet.OK_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);

//搜索下一个匹配
searchResult = bodyElement.findText(target,searchResult);


code $


在文本替换操作中使用它,替换文本会带有隐形标签(就像你在做的那样)。我想你会想让你的标签尽可能短,这样他们在最终文档中占据的空白区就非常小 - 我正在使用一系列unicode字符作为数字来玩,以提供大范围的2数字'数字',这些数字在任何其他情况下都不太可能出现。


In a mailMerge script I'm working on I use .replaceText() to replace fields with their corresponding values in a database.

The interface allows to test in the document to see if the result is looking as expected and I need to have a 'UNDO' function to get my fields in their original position so that I can use it with other values.(this script is bounded to a document in a side bar, see this post for illustration)

The script below does that pretty well by keeping in memory the field names an their replacement values. The only detail that bothers me is that I had to define a special "empty" label for fields that have no values in the current test data to prevent losing their track in the document. (I used a numbered identifier like °vide12°).

This is working perfectly but it's not ideal since the document in test mode is not exactly a representation of the final document because of these °videXX° that I use...

The question is : does anyone have a better idea or another approach to "localize" the replacement data when there is no data in a less visible way ? (I know this sound weird... that's why I explain the whole situation :-)

Considering the way Google Docs are build I thought that I could get the complete element structure and rebuild the doc from that info but I'm afraid it won't be possible since the smallest element is a paragraph and fields are mainly just single words...

Here is the relevant part of the code I use, I added a few comments to make it (hopefully) clear.

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];
}


EDIT : thanks to Mogsdad's brilliant answer I wrote these 2 functions to hide/show the unused fields. Sinc in my case I use °XX° (XX=2 digit number) to keep track of the unused fields I had to modify his code to look for this particular string and used 2 loops to get all the fields.

I call these function from the menu AND from the two other functions that handle the replacement (I updated the code above as well)

It might appear a waste of time since I iterate more that 100 times but the result is instantaneous... so why bother ? here is the code in case it gives someone an idea.

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);
      }
    }
  }
}

解决方案

Serge, I've been working on the very same problem! I've got a partial workaround to share, and some ideas to take it further.

There is no way to embed hidden text in Google Docs, as eloquently stated by Gill on the old forum. If there was, your mailmerge would be trivial!

How about making your tags or "cookies" (almost) invisible, though? Below is a scriplet that adds a "cloaking" function to a document. It has extras as well; it queries the user for text to cloak, then searches for all instances of that text and cloaks them. The idea I settled on was to make the text as small as possible (fontsize 0) and to match the foreground color to the background color.

// 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);
  }
}

To make use of this in the text-replacement operation, the replacement text would carry a cloaked tag (as you're doing). I think you'd want to make your tags as short as possible, so that the white space they occupy in the final document is very small - I was playing with using a series of unicode characters as digits, to give a large range of 2-digit 'numbers' that would be unlikely to show up in any other context.

这篇关于替换文档中的字符串并撤消的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆