This is a simple problem (I am new to JavaScript and have a limited knowledge of the syntax and using arrays etc.), so I am sure someone more knowledgeable will be able to advise the simplest solution fairly easily!
I would like to replace a number of text placeholders in an existing Google Doc template with variable text inputs, which I ultimately plan to populate from one or more external sources via APIs (such as a form).
function replaceAllPlaceholders() {
var body = DocumentApp.getActiveDocument().getBody(); //defines the range within which to replace text
body.replaceText('placeholder1', 'replacement1');
body.replaceText('placeholder2', 'replacement2');
body.replaceText('placeholder3', 'replacement3');
// ...
body.replaceText('placeholder98', 'replacement98');
body.replaceText('placeholder99', 'replacement99'); }
Rather than repeat the replaceText( function for each replacement as I have done above, how can I instead layout the information out as an array of placeholder:replacement pairs, and then loop through each?
// for example something like this (pseudo):
//
// var obj = {
// 'placeholder1': 'replacement1' // I would like to keep open the option to retrieve this array from an external source instead
// 'placeholder2': 'replacement2'
// 'placeholder3': 'replacement3' };
//
// body.replaceText(*all placeholders*,*all replacements*);
I imagine this would allow greater flexibility in editing the set of placeholders and or replacements going forward, either directly within Google Apps Script or by replacing the whole array to one retrieved from an external source (as well as reducing the code required). The problem is I have not been able to figure out the correct method to do this. Any suggestions?
Alternatively, is there a better way to achieve my goal?
I am open to all recommendations!
Try this
var placeholders = [
['placeholder1', 'replacement1'],
['placeholder2', 'replacement2'],
['placeholder3', 'replacement3']
];
placeholders.forEach(function(pair) {
body.replaceText(pair[0], pair[1]);
});
Related
I currently have a function to take a string, break it up into sentences, then extract the sentences that contain a user defined keyword and return them:
function getSentencesWithWord(word, text) {
let sentenceArray = text.replace(/([.])\s*(?=[A-Z])/g, "$1|").split("|")
return sentenceArray.filter(sentence => sentence.includes(word))
}
Currently, this function works. However, the amount of text I need to search through with this function is quite large; it is searching through around 30 google docs that could each be up to 75 pages each, and searching for certain terms (such as the word "the") can take up to a minute for the function to complete. Is there a more optimized way to search through and extract this much text?
Edit:
Because someone asked in the comments, the word variable is just a string obtained from a text input on an HTML page and the text variable is a string obtained via this function in a Google Apps Script:
function getText(docID){
let doc = DocumentApp.openById(docID);
let textToSearch = doc.getBody().getText();
return textToSearch;
}
I have an array of all the google doc IDs I need to access and I just iterate over the array and obtain the text for each doc
The replace you've provided, replace(/([.])\s*(?=[A-Z])/g, "$1|"), didn't work for me == got the same string without change...
How about this:
("|"+text.toLowerCase().replace(/\s/g, "|")+"|").includes("|"+word.toLowerCase()+"|")
Or, this:
("|"+text.toLowerCase().split(" ").join("|")+"|").includes("|"+word.toLowerCase()+"|")
Was unable to find a faster method of achieving this with Google Apps Script so I just went back to Node.js and ended up using the Google Drive API to download the files as .txt files, which are much smaller and so I can search through them much faster now
I need to replace some images in an InDesign document with a given file. This happens using the InDesign server, but scripting is almost the same as with regular InDesign, except no user interaction is possible.
What I have is a InDesign Document, the ID of an Rectangle containing some image and the Path to a new image that should replace the image.
The image should be replaced, but the settings like FitOptions etc. should stay the same. Also, the new file shall be embedded in the InDesign Document. There is already some code that sort of works:
function changeImages(doc) {
var arrayLength = changeImage.length;
for (var i = 0; i < arrayLength; i++) {
var fr = doc.textFrames.itemByID(1 * changeImage[i].id);
if (!fr)
continue;
var file = File(imagePath + changeImage[i].file);
fr.place(file);
fr.fit (FitOptions.CONTENT_TO_FRAME);
fr.fit (FitOptions.PROPORTIONALLY);
fr.fit (FitOptions.CENTER_CONTENT);
}
}
This doesn't seem right. Why is it using doc.textFrames when the object is a rectangle? I am actually confused this even works.
Also it just sets some FitOptions, but I want to keep the existing.
I am very new to InDesign scripting, so I am lost here. I am reading the docs and other resources, but I am confused. e.g why is there doc.textFrames.itemByID but nothing like that for other Frames? Do I have to iterate doc.allPageItems and compare ids?
itemByID is a method available for all pageItems and both textFrames and rectangles are subclass of pageItem. So you have access to this method from both, and it'll give the same result. You should be able to use doc.rectangles.itemByID as well. See: http://www.indesignjs.de/extendscriptAPI/indesign11/#Rectangles.html#d1e201999__d1e202138
But you are right that the description is a bit confusing, it says:
Returns the TextFrame with the specified ID.
which is obviously not the case. If you already have the IDs you want to target, you could use doc.pageItems.itemByID, which is maybe less confusing, since basically you're looking for pageItems when using itemByID.
As for fitting options, they are a property of your rectangle object, so placing a new image shouldn't change the fitting options. If you want to keep the same, simply remove the calls to fit(). See in property list of Rectangle, frameFittingOptions: http://www.indesignjs.de/extendscriptAPI/indesign11/#Rectangle.html
Josef,
I've had the same problem with InDesign CS4 with keeping the original FitOptions. I was never able to figure out how to get the settings currently being used in InDesign CS4.
To get around the problem what I did was to set the value in the Fitting on Empty Frame in the Frame Fitting Options in the InDesign document.
Then in code I used that setting, something like this:
changeImages (app.activeDocument);
function changeImages(doc)
{
with(doc)
{
var rec = doc.rectangles.itemByID(207);
var file = new File("c:\\new_image.png");
rec.place(file);
rec.fit(rec.frameFittingOptions.fittingOnEmptyFrame);
}
}
i must start by saying i only know how to comfortably work with HTML and CSS.
Which would be the easiest way to take the image sources from this, which i'm assuming is a JSON call, http://graph.facebook.com/103184243098195/photos?fields=source&limit=999 and stick them into my HTML in a way that i could format them with CSS?
i have picked around the internet to find out how to do this using jquery and found a few snippets of example code but i do not know how to tell, i'm assuming javascript, what to look for, how to find it and then where to put what it found, ie., the image sources.
i think i need at least this:
var url = 'https://graph.facebook.com/103184243098195/photos?fields=source&limit=999';
$.getJSON(url, function(data) {
});
but i don't know what to put in-between to grab the image sources and then plug them into, let's say, just a div in general. everywhere i go to try and learn which syntax i would need uses terms i do not understand as i am unfamiliar with most languages. i don't even know if this is the right start/direction.
Any help is appreciated!
I've never used this API whatsoever, but this should at least give you a start until someone else comes along. :P
HTML:
<body>
<div id="facebook-stuff"/>
</body>
JS:
$.getJSON('http://graph.facebook.com/103184243098195/photos?fields=source&limit=999', function(url) {
var data = url.data;
var paging = url.paging;
for(var i = 0; i < data.length; i++)
{
$('#facebook-stuff').append('<img src="' + data[i].source + '" class="facebook-pics" id="pic' + i + '"/>');
}
});
http://jsfiddle.net/zV99P/
Basically, the parameter passed into your function is your JSON object. Data is an array of objects(within that object) from which you can extract the id, source and time of creation from. Using Jquery or just plain javascript you can dynamically create img elements with the source attribute and append them to any element created on the page. There's also a pagination object here if you are looking to split your images across multiple pages. As far as I can understand it from a quick look, you'd have to lower your limit at the end of your query string to a lower number, i.e: ?limit=5, and then you could create previous or next links from the paging object, with paging.previous or paging.next.
So, I have some code that should do four things:
remove the ".mp4" extension from every title
change my video category
put the same description in all of the videos
put the same keywords in all of the videos
Note: All of this would be done on the YouTube upload page. I'm using Greasemonkey in Mozilla Firefox.
I wrote this, but my question is: how do I change the HTML title in the actual HTML page to the new title (which is a Javascript variable)?
This is my code:
function remove_mp4()
{
var title = document.getElementsByName("title").value;
var new_title = title.replace(title.match(".mp4"), "");
}
function add_description()
{
var description = document.getElementsByName("description").value;
var new_description = "Subscribe."
}
function add_keywords()
{
var keywords = document.getElementsByName("keywords").value;
var new_keywords = prompt("Enter keywords.", "");
}
function change_category()
{
var category = document.getElementsByName("category").value;
var new_category = "<option value="27">Education</option>"
}
remove_mp4();
add_description();
add_keywords();
change_category();
Note: If you see any mistakes in the JavaScript code, please let me know.
Note 2: If you wonder why I stored the current HTML values in variables, that's because I think I will have to use them in order to replace HTML values (I may be wrong).
A lot of things have been covered already, but still i would like to remind you that if you are looking for cross browser compatibility innerHTML won't be enough, as you may need innerText too or textContent to tackle some old versions of IE or even using some other way to modify the content of an element.
As a side note innerHTML is considered from a great majority of people as deprecated though some others still use it. (i'm not here to debate about is it good or not to use it but this is just a little remark for you to checkabout)
Regarding remarks, i would suggest minimizing the number of functions you create by creating some more generic versions for editing or adding purposes, eg you could do the following :
/*
* #param $affectedElements the collection of elements to be changed
* #param $attribute here means the attribute to be added to each of those elements
* #param $attributeValue the value of that attribute
*/
function add($affectedElements, $attribute, $attributeValue){
for(int i=0; i<$affectedElements.length; i++){
($affectedElements[i]).setAttribute($attribute, $attributeValue);
}
}
If you use a global function to do the work for you, not only your coce is gonna be easier to maintain but also you'll avoid fetching for elements in the DOM many many times, which will considerably make your script run faster. For example, in your previous code you fetch the DOM for a set of specific elements before you can add a value to them, in other words everytime your function is executed you'll have to go through the whole DOM to retrieve your elements, while if you just fetch your elements once then store in a var and just pass them to a function that's focusing on adding or changing only, you're clearly avoiding some repetitive tasks to be done.
Concerning the last function i think code is still incomplete, but i would suggest you use the built in methods for manipulating HTMLOption stuff, if i remember well, using plain JavaScript you'll find yourself typing this :
var category = document.getElem.... . options[put-index-here];
//JavaScript also lets you create <option> elements with the Option() constructor
Anyway, my point is that you would better use JavaScript's available methods to do the work instead of relying on innerHTML fpr anything you may need, i know innerHTML is the simplest and fastest way to get your work done, but if i can say it's like if you built a whole HTML page using and tags only instead of using various semantic tags that would help make everything clearer.
As a last point for future use, if you're interested by jQuery, this will give you a different way to manipulate your DOM through CSS selectors in a much more advanced way than plain JavaScript can do.
you can check out this link too :
replacement for innerHTML
I assume that your question is only about the title changing, and not about the rest; also, I assume you mean changing all elements in the document that have "title" as name attribute, and not the document title.
In that case, you could indeed use document.getElementsByName("title").
To handle the name="title" elements, you could do:
titleElems=document.getElementsByName("title");
for(i=0;i<titleElems.length;i++){
titleInner=titleElems[i].innerHTML;
titleElems[i].innerHTML=titleInner.replace(titleInner.match(".mp4"), "");
}
For the name="description" element, use this: (assuming there's only one name="description" element on the page, or you want the first one)
document.getElementsByName("description")[0].value="Subscribe.";
I wasn't really sure about the keywords (I haven't got a YouTube page in front of me right now), so this assumes it's a text field/area just like the description:
document.getElementsByName("keywords")[0].value=prompt("Please enter keywords:","");
Again, based on your question which just sets the .value of the category thingy:
document.getElementsByName("description")[0].value="<option value='27'>Education</option>";
At the last one, though, note that I changed the "27" into '27': you can't put double quotes inside a double-quoted string assuming they're handled just like any other character :)
Did this help a little more? :)
Sry, but your question is not quite clear. What exactly is your HTML title that you are referring to?
If it's an element that you wish to modify, use this :
element.setAttribute('title', 'new-title-here');
If you want to modify the window title (shown in the browser tab), you can do the following :
document.title = "the new title";
You've reading elements from .value property, so you should write back it too:
document.getElementsByName("title").value = new_title
If you are refering to changing text content in an element called title try using innerHTML
var title = document.getElementsByName("title").value;
document.getElementsByName("title").innerHTML = title.replace(title.match(".mp4"), "");
source: https://developer.mozilla.org/en-US/docs/DOM/element.innerHTML
The <title> element is an invisible one, it is only displayed indirectly - in the window or tab title. This means that you want to change whatever is displayed in the window/tab title and not the HTML code itself. You can do this by changing the document.title property:
function remove_mp4()
{
document.title = document.title.replace(title.match(".mp4"), "");
}
I am trying to populate multiple form fields with JSON data after a user makes a choice from a select box. I am very new to jquery so sorry if I am getting something elementary wrong. I don't want to include the JSON in my html because it will be changing often and it is a very large file. Here is what I have:
<script type="text/javascript">
$.ajax({
url: '../includes/json/data/abbc.json',
success: function(data) {
$("#rig").html("<option >--select--</option>");
$.each(rigdetaillist.rigs,function(){
var rigName=this.rig;
$("#rig").append("<option value=" +rigName + ">" +rigName + "</option>");
});
$("#rig").change(function(){
var rigValue=$(this).val();
$.each(rigdetaillist.rigs,function(i){
var rigName=this.rig;
if (rigName==rigValue){
$(".rigdetail").val("");
$.each(rigdetaillist.rigs[i].rigdteails,function(i){
var rigdetailName=this.rigdetail
$(".rigdetail").eq(i).val(rigdetailName);
});
}
});
});
}
});
</script>
There are a few things here that you want to consider, one of the major things being that you want to do as little DOM manipulation as possible, as its one of the major causes of performance issues.
Unfortunately, I don't have the time to rewrite your code and give you what you should write. Although I do have the time to explain to you what you need to consider and hopefully it'll lead you down the right path :-)
(1) Your data var in success: function (data) { isn't being used. I'm assuming you mean it to be used for the rigdetaillist, most likely something like var rigdetaillist = data['rigdetaillist']; depending on your JSON. In either case, data is your json return value, of which you aren't referencing at all...which you probably need to. :-)
(2) As I said earlier, you want to do as little DOM manipulation as possible. So you probably will want to either pull out the #rig and cache it in javascript (to be put back into the DOM later), or create a new $(<script>) obj and then copy its html into $('#rig')'s when you are done. I would suggest creating the new script tag as detaching is a very nice feature of jquery, but can have its problems (such as when you go to append it back in, it appends at the bottom of the container instead of where you originally had it).
(3) The change function is being created on every json request. There is more than likely a way to globalize that so you don't have to create a new change function every time. You could possibly store the necessary information in a global variable and just reference that variable in change function, or I'm sure you could do some other really cool scope tricks, but that would be the simplist.