Okay so I'm here trying to get myself acquainted with Adobe Acrobat's Javascript API -- I feel like I may be missing some easy ways of doing certain things, but let's find that out together.
The Question:
How would I go about finding the amount of pages that belong to a bookmark?
For example, I have the following Bookmark layout:
Intro [3 pages]
Factions [2 pages]
Character [3 pages]
End [1 page]
(would have posted a picture, but I don't have the permission to do so :/)
Essentially I would like to be able to automate the extraction of the # of pages each bookmark has, for a little project I'm working on to speed stuff up at work.
My code thus far:
/* Count Bookmark Children
TODO: Count Pages of each Bookmark */
function CountBm(bm) {
var count = 0;
console.println("Bookmark name: " + bm.name);
bm.execute(); // goto bm -- not necessary, just for personal reasons
console.println("Bookmark Start Page: " + (this.pageNum+1));
/* This would only work if each page in the bookmark was a child
of the bookmark being checked */
if (bm.children != null) {
for (var i = 0; i < bm.children.length; i++)
count++;
}
console.println("Pages in Bookmark: " + count);
}
var bkmk = bookmarkRoot.children[2]; // Character Bookmark
CountBm(bkmk);
Also, for the last two lines of that code, is there a better way to reference specific bookmarks? By name, perhaps?
I have done this by using the current bookmark's execute() destination relative to the next bookmark's execute() destination. So assuming the bookmarks follow the flow of the document, just run execute() on the next bookmark, and use this.pageNum to figure out how many pages you have jumped forward.
Something like:
this.pageNum = 0;
for (var i = 1; i < this.bookmarkRoot.children.length; i++) {
page = this.pageNum;
this.bookmarkRoot.children[i].execute();
console.println("This bookmark is " + (this.pageNum-page) + " pages long");
}
You can add handling for grandchildren bookmarks as well, depending on your application. The right solution is dependent upon the structure of your bookmarks. The print to console line above could be replaced with this.extractPages(...) for your application.
Unfortunately that's the only way to reference bookmarks. If you wanted to find a bookmark by it's name, you could store all the bookmark names in an object with their child indexes. It's a hack, but it can be helpful when you have a document with a large number of bookmarks.
Related
Hey guys I'm new to programming, I need to make a website for school.
I want to open the next page when pressing the arrow keys. So I thougt,
I can put the URLs into an array an increment the index, when the button is pressed.
Unfortunately I get some random numbers from 1 to 3 when I press the down key.
const url = ['/#t1', '/#t2', '/#t3', './contact.html', './404.html'];
var index = 0;
$(document).keydown(function(e) {
switch(e.which) {
case 38: // up
up();
break;
case 40: // down
down();
break;
default: return;
}
e.preventDefault();
});
function up() {
index = index - 1;
alert(index);
window.location.href = url[index];
}
function down() {
index = index + 1;
alert(index);
window.location.href = url[index];
}
A quick way to solve this without using storage is to find the index in the array rather than keeping track of it. You can do that like so:
const url = ['/list', '/of', '/urls'];
//find index of current url in array
let index = url.indexOf(window.location.pathname)
if(index == -1){
//url is not in the array
windows.alert("invalid url")
}
Edit: I agree with Michael Geary that is is a bad idea for a real world website
Don't do this!
What you're doing is a fun exercise, but it breaks the normal page scrolling that any visitor to your site may expect to work.
When I view a web page, I scroll through it using the up and down arrow keys. For example, open the Stack Overflow home page or Hacker News and hit the down arrow a few times, then the up arrow. That scrolling is how web pages are supposed to work.
There are some exceptions. The Google home page opens with the the text cursor in the main input field, and the down arrow opens a list of recent searches. That's perfectly reasonable.
But having the down arrow jump to a whole new section of the page - or another page entirely! - is not what visitors expect. Don't do it unless you have a really good reason.
If you do have a good reason, I will be curious to hear it! :-)
Having a lot of trouble finding this and as a very beginner programmer, I can't quite troubleshoot my way through this.
What I want to do:
Automatically log the word count of a google doc in a google sheets cell.
The code I've been playing with to try and make it happen that is probably super wrong:
function countWords() {
var doc = DocumentApp.openByURL().getBody().getText();
var punctuationless = doc.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()"?“”]/g," ");
var finalString = punctuationless.replace(/\s{2,}/g," ");
var count = finalString.trim().split(/\s+/).length;
return count;
Ideally, what I'd like to do is, in sheets, set it up so there's a column with links to google docs and be able to just put in a function that will return the wordcount from that doc.
Answer:
You can not create a custom function to do this, as reading another document requires authentication. You can however do this with an in-sheet button which runs the script.
More Information:
As per the documentation on custom functions, it is not possible to run methods which require authentication such as DocumentApp:
Unlike most other types of Apps Scripts, custom functions never ask users to authorize access to personal data. Consequently, they can only call services that do not have access to personal data
As a result, you will instead have to manually run the script - but this can be done from a button in the Sheet.
Code:
Assuming that you have the Document links in column A and wish for the word count to be in column B (starting in row 2):
function countWords() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var linkRange = ss.getRange("A2:A");
try {
linkRange.getValues().forEach(function(cell, index) {
if (cell[0] == "") {
throw "Cell A" + (index + 2) + " is empty"
}
let doc = DocumentApp.openByUrl(cell[0]).getBody().getText();
let count = (doc.match(/\b\S+\b/g) || []).length;
ss.getRange(index + 2, 2).setValue(count);
});
}
catch (err) {
console.log(err);
return;
}
}
Rundown of this function:
Open the sheet containing the document links (remember to change the sheet name!)
Get the range of links down column A
Loop through each link and obtain the Document's text
Obtain all instances of word-boundary/non-whitespace/word-boundary in the document, puts them all into an array, and gets the length of the array.
In this step, if the document is empty, then an empty array is given
Sets the cell in column B adjacent to the link to the result of the count.
This is all wrapped inside a try/catch so that the script stops execution when it reaches an empty cell in column A.
Assigning to a Button:
Now, you can create an in-sheet button which will run the script whenever you click it.
Go to the Insert > Drawing menu item and create a shape; any shape will do, this will act as your button.
Press Save and Close to add this to your sheet.
Move the newly-added drawing to where you would like. In the top-right of the drawing, you will see the vertical ellipsis menu (⋮). Click this, and then click Assign script.
In the new window, type countWords and press OK.
Now, each time you click the button, the script will run.
Visual Example:
References:
Custom Functions in Google Sheets | Apps Script | Google Developers
Solved! - didn't update my random number generation after changing from switch statement to array... ups. - Thanks!
Problem
Building a web comic and wanted to have one of those "random" buttons, where you jump to any of the strips. I'm assuming the best way to do this would be something on the back end (PHP or such), but I want to do it with JavaScript.
I got as far as picking a random page, but had the problem that it would sometimes redirect to the page it's already on (or rather often until I have more pages). I tried to make it take the page out of the array if the current page is the same as the target page, but instead I end up getting redirected to "http://bcitcomp.ca/students/hsloman/Comp1850/final/undefined"
I even made sure to use splice instead of delete. Doesn't that re-index the list?
Code
var pickRandomPage = function () {
// random Pages available
var links = [
"construction.html",
"placeholder.html",
"noplaymobil.html"];
// current Page
var currentURL = window.location.href;
var currentPage = currentURL.substr(currentURL.lastIndexOf('/')+1);
// get rid of current page from array of options
for(var i = 0; i < links.length; i++){
if(links[i] == currentPage){
links.splice(i,1);
}
}
// get a random number, rounded number between 0 and number of links
var randomPage = Math.floor((Math.random() * 3) + 1);
var link = 'http://bcitcomp.ca/students/hsloman/Comp1850/final/' + links[randomPage];
// open it
window.open(link,"_self");
};
Resources used sofar
Get current URL in web browser
window.open() should open the link in same tab
but instead I end up getting redirected to "http://bcitcomp.ca/students/hsloman/Comp1850/final/undefined"
The undefined is because your randomPage variable will contain a number between 1 and 3, but the actual valid indices in your links array are only either 0 or 1 because it will only have two elements after you remove the current page URL.
Change:
var randomPage = Math.floor((Math.random() * 3) + 1);
to:
var randomPage = Math.floor(Math.random() * links.length);
put this right before the window.open(...) line:
if(link === window.location+"") return pickRandomPage();
This is saying, "if the chosen link is the page we're already on, run the function again" ..so it will keep trying until a new page is given. This is easier than trying to splice the array.
See: recursion for more info.
I am trying to find occurrences of a string in another string that has been pulled from the HTML document. The page is an SNMP monitor but we have been having issues in the past with CTRL + F because it only wants to find the string within the current viewport of the browser. My attempt at getting around this and not having to look through things manually was to write a script.
The issue here is that it appears the docHTML variable is only able to hold so much data and anything else is truncated. I have looked around on Stack Overflow and found that my string size is significantly less than other people have tried, so that shouldn't be the issue.
All of the IP addresses in the 'ipArray' variable do exist on the page in different locations and are in the docHTML variable when I look through it myself. When I run the doSearch function at various points in the page (viewport dependent) it gives me different results.
I really don't know what has gone wrong here as the code does work sometimes, and not other times. My goal is to have the code go through the whole page and find all missing IP's and add them to the array so that we can go ahead and add them instead of having to compare 490 IP's on a spreadsheet to up to 490 in the monitoring utility.
Thanks in advance!
var docHTML = document.documentElement.outerHTML;
var missing = [];
function doSearch(text) {
if (docHTML.search(text) == -1){
missing.push(text);
}
}
var ipArray = [
"192.168.64.236",
"192.168.64.237",
"192.168.64.238",
"192.168.64.10",
"192.168.64.11",
"192.168.64.12",
"192.168.65.40",
"192.168.65.47"
];
var Total = ipArray.length;
for(i=0;i<Total;i++){
doSearch(ipArray[i]);
}
console.log("Missing IP's: " + (Total - missing.length));
console.log(missing);
Here is the solution, not much of change, just a tweak to your logging statement. You were printing "total-missing" which is wrong. What we need is the missing count-
var docHTML = document.documentElement.outerHTML;
var missing = [];
function doSearch(text) {
if (docHTML.search(text) == -1){
missing.push(text);
}
}
var ipArray = [
"69.171.224.11",
"199.59.149.230",
"174.121.194.34",
"209.200.154.225",
"69.174.244.50",
"67.201.54.151"
];
var Total = ipArray.length;
console.log(Total);
for(i=0;i<Total;i++){
doSearch(ipArray[i]);
}
console.log("Missing IP's: " + (missing.length)); /***HERE***/
console.log(missing);
Other than this, the whole code worked for me as expected. Let me know what else/exactly is the issue. Happy to help.
The code works as intended. The issue happened to be the SNMP monitor it is running on top of. Everything on the page seems to be loaded by POST requests as you scroll. It seems to grab a few before and after which was why I was able to see it in the code and not when executing.
Similar to my earlier problems with finding a textFrame on a page based on its geometricBounds or by part of its name, I now am running into the problem of finding textFrames if they are inside groups. If I use an array of all textFrames, such as:
var textFramesArray = document.textFrames.everyItem().getElements();
it will not find any textFrames that are inside of a group. How can I figure out how to reference a textFrame if it's inside a group? Even if the group has to be un-grouped, that's fine, but I cannot even figure out how to find groups on the page!
Groups on a page are page.groups ... but you don't need this anyway. Fabian's answer is good, but it doesn't take groups-in-groups into account -- nor clipping masks, nor text frames inside tables and footnotes (etc.).
Here is an alternative approach: allPageItems is pretty much guaranteed to return all page items, of all kinds and persuasion, inside groups or other frames or whatnot. You can inspect, then process, each of them in turn, or build an array of text frames to work with at leisure:
allframes = app.activeDocument.allPageItems;
textframes = [];
for (i=0; i<allframes.length; i++)
{
if (allframes[i] instanceof TextFrame)
textframes.push(allframes[i]);
}
alert (textframes.length);
Try this:
// this script needs:
// - a document with one page
// - some groups with textframes in it on the first page
var pg = app.activeDocument.pages[0];
var groups = pg.groups;
var tf_ingroup_counter = 0;
for(var g = 0; g < groups.length;g++){
var grp = groups[g];
for(var t = 0; t < grp.textFrames.length;t++){
var tf = grp.textFrames[t];
if(tf instanceof TextFrame){
tf_ingroup_counter++;
}
}
}
alert("I found on page " + pg.name +"\n" + pg.textFrames.length
+" textframes\nOh and there are also "
+tf_ingroup_counter+ " hidden in groups");
Your task to get all the text frames in the layer or the document. Whether those text frames are in a group or not. This is done through the property of allPageItems. For instance use this:-
var items=app.activeDocument.allPageItems;
this will give you all the text frames in the item and within group also. Now you can do any manupulations. You can check the items on the debug console which will give all the types of the objects.And then you can check for the textframe
items[i].constructor.name =='TextFrame'
and now you can store each object in type array.