How to continue a title if that section bleeds to another page in google docs using app scripts? - google-apps-script

I am working with a google docs file with already populated sections and their section headers. I read the file and replace some text.The issue is depending on the size of the text I replace the section content might move to the other page.In that case I want the section title to re-appear at the start of the next page.
For a example :
The first page section title is - Global History
And if that section goes to the other page the title should be - Global History(Cont.)
The main issue I am having is that I can't find the end of a page. I tried using page break
var paras = editBody.getParagraphs()
for(i=0;i<paras.length;i++){
if (paras[i].findElement(DocumentApp.ElementType.PAGE_BREAK) != null) {
Logger.log('page break')
}
}
But this did not work, even though my document already has 5 pages it returned without a single page break.

Finding the end of a page in a Google document it's quite complex as the Google Apps Scirpt Documents Service and the Google Docs API hasn't a method for this, so you should calculate the end of page considering the page margins, line spacing, font sizes, etc.
A simpler approach might be to estimate how many pages will require each section and set the continuation section title in advance, setting these titles with the "Add page break before" option.

Related

How can I set the page size for a PDF created (from HTML) and emailed using AppMaker?

I have a function in my AppMaker app that works like a charm -- when you click on a button, it takes the data from the current record, applies some logic to it, and mashes it up and styles it as HTML. I want to deliver the results to the user in two ways:
Print -- the function has a return that sends the HTML back to the browser, which opens up a print dialog as expected.
PDF by Email -- the function also generates a PDF which is attached to an email sent to the user.
I heard feedback from users that the PDF version cuts off the bottom two lines, so I'm trying to find out why.
To send the PDF as an email attachment, I use Utilities.newBlob() to first blobify my HTML. Then I get the blob as a PDF, and then I send the PDF as an email attachment. The code looks like this:
var pdfContent = Utilities.newBlob(html, 'text/html', 'PE-' + name + '.html')
var pdf = pdfContent.getAs("application/pdf");
GmailApp.sendEmail(to, subject, body, {attachments: pdf});
The email is delivered correctly with a PDF attached to it, and when you open it up, it looks pretty much like the HTML that gets sent to the print dialog. When you go to print it, though, the last lines clearly get cut off. My advice to users (until I could dig into the problem) was to use the "Fit to Page" function in the print dialog. Looking at it more closely, though, I had an inkling that it might be a page size issue -- there's something off about the proportions when you view the PDF. Sure enough, upon changing the paper size from letter to A4 in the print dialog, the page lays out perfectly.
So now I can't figure out where the default to A4 is happening.
I tried setting the #page attribute for size in the <style> tag, with no effect -- it doesn't seem to survive the transition from HTML to blob to PDF:
html += '<style>#page {size: 215.9mm 279.4mm; margin: 15mm;}</style> etc...';
For size I've used millimeters (as above) and px as units, and I've tried just using the word 'letter'. No version of the above works.
Is there some other place where I can specify page size to make this stop happening?

Template Editor AEM not working as expected

We are using template editor in AEM 6.2 to create templates , and we have followed below steps to create a template-
1.Created page template as done in we-retail site.
2.Create empty page template using above page template.
3.Created template using empty page template.
Did following modifications on top of we retail page component as per our requirement-
As we need to have header and footer as full width parsys i.e 1340 width and body as 940 width-
.site-wrapper {
width:1340px;
}
.container {
width:940px;
}
So we did following modifications in /apps//components/structure/page/partials/body.html -
<div class="site-wrapper">
<div data-sly-resource="${ # path='header', resourceType='/apps/<projectname>/components/structure/header'}"
data-sly-unwrap>
</div>
</div>
<div class="container"
data-sly-use.templatedContainer="com.day.cq.wcm.foundation.TemplatedContainer">
<sly data-sly-repeat="${templatedContainer.structureResources}"
data-sly-resource="${item.path # resourceType=item.resourceType, decorationTagName='div'}" />
</div>
<div class="site-wrapper">
<div data-sly-resource="${ # path='footer', resourceType='/apps/<projectname>/components/structure/footer'}" data-sly-unwrap></div>
</div>
Also we have few more components which will take full width on the page so i have added responsivegrid component under full width div.
But i am not able to edit the header and footer component in template as they are not under templateresource.
Also even if i add design dialog under header and footer i am not able to edit those components directly on the page in design mode even if they are unlocked.
Please let me know if i am doing anything wrong here or we cant customize body.html as in we-retail site.
I thought of using different parsys for full width and for body.
And i don't want to control using css as i have multiple components which are full width.
Regards
Ankur
First, the objective of the Template Editor is precisely to not do any static includes like what you edited on your page component, because the Template Editor now provides a UI to include such components on the page level, allowing to define which included components can be edited or not by authors on the resultant pages. That way, if some page suddenly requires another header or footer, this can be edited through the UI of the Template Editor and doesn't require a development cycle.
So I'd recommend to remove your edits of the body.html, and rather make sure that your Header and Footer components are editable: meaning that the cq:Component node must have a title (jcr:title), a group (componentGroup), as well as an cq:editConfig, a classic dialog or a classic design dialog (in 6.2 and before, the classic dialogs were still needed to make a component editable, but this won't be the case anymore for future versions, where touch dialogs can be provided instead).
Once you're there, here are detailed instructions for setting up your site with the Template Editor UI in AEM 6.2:
Create the template
For now, we'll create the template in the global folder, later however you'll have to make sure that the chosen folder is specific to your site.
Go to the Template Editor (in AEM's global nav, select General > Templates).
Pick the global folder (or your site-specific folder).
Hit the "Create" button and choose "HTML5 Page", hit "Next" and give your template a name, like "Simple Page", then hit "Create" and choose "Open".
Add Client Libraries
Let's configure the page component to load the CSS and JS client libraries that are needed for our site.
In "Structure" mode of the Template Editor, select the second icon from the top-left ("Page Information") and choose "Page Policy". A policy is basically a reusable design configuration that can be shared and reused across multiple templates if desired. What we'll do is duplicate that policy and then edit it to instruct the page to load additional client libraries.
In the "Policy" dialog, duplicate the "Default Page" policy with the double-arrow icon and rename it for instance to " Config". Then click the checkmark at the top-right of the dialog to save.
Again in the top-left ("Page Information") menu, choose this time "Page Design" to edit the actual design configuration.
Edit the list to include all client libraries required by the components that you plan to use. If the template author isn't a developer, this is typically where the developer must instruct the template author what clientlibs must be loaded on the pages of a specific site. In case you have We.Retail installed, you can add a field and paste we-retail.base into it. Save by clicking the top-right checkmark.
Add Structure Components
Structure components are those that were usually hard-coded through static includes on the page components. We typically don't want the structure components to be editable by our page authors later on, unless explicitly defined.
On the page, select the empty parsys and in its toolbar, choose the 1st icon ("Policy") to edit policy.
In the "Policy" dialog, duplicate the "Default Layout Container" policy and rename it for instance to "Template Config". This will be the name of the configuration that tells what components are available in the template for that page-level layout container. Save.
As the page reloads, select the empty parsys again, and this time choose the 2nd icon ("Design") to edit the design configuration.
In the design dialog that opens, select all the components that you want to have available in the template editor (for example, in addition to the few General components already selected, also select the "We.Retail" and the "We.Retail.Structure" component groups at the bottom of the list, or the group name you assigned to your components with the componentGroup property). Make sure to have also the "Layout Container" component selected, as this is the paragraph system (aka parsys) that we'll require later to make the page editable. Save.
You should now see your components showing up in the left "Components" rail, which allows to drag & drop your header and footer components to the page (for We.Retail the header is named "We.Retail Page Header" and is at the very bottom of the component list, and it is similar for the footer).
Also drag a "Layout Container" component between your header and footer components, which is the actual parsys that we'll make editable for our page authors. So on your page, you should now at least have following components: your header (e.g. "We.Retail Page Header"), a "Layout Container", and your footer (e.g. "We.Retail Page Footer").
Make the template editable
We now have to specify which of the components added we want page authors to be able to edit.
Select the parsys placeholder that appears between the header and the footer, and click the last icon of the toolbar ("Unlock structure component").
Once this is done, the placeholder says "No allowed components for Layout Container", the reason is that this parsys has no policy yet assigned to define the list of allowed components for the page authors. Therefore, select this parsys placeholder again and from the toolbar select the second icon ("Policy").
To base our policy on the one previously edited, select the "Template Config", and the hit the duplicate icon and rename it to "Page Config". Save.
The list of allowed components now appear below the parsys placeholder, which looks good, except that we want to remove the components that we want available in for the template structure only. Therefore, let's select the placeholder once more and select the 3rd icon ("Design") from the toolbar.
In the design dialog, uncheck the structure-specific components and groups (for e.g. uncheck the "We.Retail.Structure" group). Save.
Use the template
That's it, we can now enable the template and use it on a site.
On the template admin (which should be still open in a previous browser tab), select your newly created template and hit "Enable" and confirm "Enable".
For pages to be able to be created from templates of the folder we used, one will need to put a cq:allowedTemplates property on a parent page (typically, this property will be set to the root page of the site). However, to create the very first root page, we'll initially just add that property to the /content node by using CRXDE Lite. So go to http://localhost:4502/crx/de/index.jsp#/content and on the root content node set cq:allowedTemplates multi-String property that equals to "/conf/global/settings/wcm/templates/.*" (or whatever folder you created your template in).
Save.
Back in AEM open the global menu and go to Sites and on the root folder, click "Create" and choose "Page".
Choose our "Simple Page" template, hit "Next", give the page a title, like "My Site" and click "Create" and "Open".
You should now see the page with the defined header and footer, but only the parsys in between that is editable. Also the desired components should show up in the left rail under the second "Components" tab.
Create a Template Type
In order to avoid having to repeat all these steps (mainly 1-18) each time one wants to create a new template, we can create a template type out of the template that we created.
In CRXDE Lite, go to the folder where you've created your template, like /conf/global/settings/wcm/templates and copy the template that should become a template type, like the simple-page node in our case.
Move to the template-types folder (e.g. /conf/global/settings/wcm/template-types in our case) and paste there the copied node.
Save.
Now, when creating a template in the global folder, one can choose the "Simple Page" as a starting point.
As you can see, the Template Editor introduces a very big change, not only technically, but also in the process: what required development steps can now be achieved through configuration. I'd therefore recommend to edit the page component only to adapt the technical HTML headers, but not to hard-code any visual items that should be displayed on the page, because surely enough in a few months/years the visual items will change and multiple variations of them will be required, for which you want to lighten the development need to allow faster changes.
Also helpful are following links:
https://docs.adobe.com/docs/en/aem/6-2/develop/templates/page-templates-editable.html
http://fr.slideshare.net/PatrickFauchre/empower-template-authors
https://github.com/Adobe-Marketing-Cloud/aem-sites-example-custom-template-type
If you will check the resources included in template using calls such as <div data-sly-resource="${ # path='footer', resourceType='/apps/<projectname>/components/structure/footer'}" data-sly-unwrap></div> tends to be problematic when it comes to editing, reason so far I have figured out it that if the nodes for the resources don't exist, these resources are not editable. Refer to an article around it here.
What we did was to create a scripts to create nodes on page load (EmbeddedComponent.js) -
"use strict";
/**
* Creates a component node with name, if node does not yet exist.
*/
use([], function () {
var newResourceName = this.name;
var newResourceType = this.type;
var resourceResolver = resource.getResourceResolver();
var newNodePath = resource.path + "/" + newResourceName;
var existingComponentResource = resourceResolver.getResource(newNodePath);
if (existingComponentResource == null) {
var properties = {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType": newResourceType
};
resourceResolver.create(resource, newResourceName, properties);
resourceResolver.commit();
}
});
And in you HTML script you could do something like -
<div data-sly-use="${'Path to script/EmbeddedComponent.js' # name='header', type=resTypHeader}"
data-sly-resource="<header resource type>">
have you tried the data-sly-unwrap="${!wcmmode.edit}" on the header and footer include script?

IE injects VBScript tags in the middle of rendering, causing malformed HTML

For some reason, it seems like IE9 (I believe IE8 too, but not sure), is injecting
<SCRIPT LANGUAGE=VBScript>on error resume next pluginFound = IsObject(CreateObject("DIFFERENT PLUGIN EVERY TIME"))
in the middle of my content without any regards to surrounding context. This means it gets added in the middle of an attribute, or in the middle of some JavaScript, causing the HTML to be malformed and causing all sorts of problems.
This happens on multiple computers with different plugins, so it's not machine specific. And it's also not consistent: the location in which the offending script gets injected varies, the offending script varies. Sometimes you'll get several page loads without a problem and then you'll get the broken HTML.
My page is using a fair amount of JS, but nothing crazy. It's currently using jQuery, Google Maps, Bootstrap, Google Tag Manager, and loading a couple of Twitter, Google+, Facebook Iframes with their own little JS snippets. So, there are some asynchronous callbacks happening, but I wouldn't think this would interfere with how the browser renders the DOM and when it decides to inject plugin code.
You can see the problem if you reload http://www.rew.ca/properties/search/839721 enough times. If you scroll to the bottom of the page, you'll see raw JSON, or sometimes just some random HTML snippet will show in the middle of the page (because of mismatched tags).
Any ideas of why these scripts get injected arbitrarily and how to work around that?
Thanks
[UPDATE]
Here's another example of the script tags getting included in the middle of HTML content:
In my opinion, the problem lies in the http://cn.clickable.net/js/cct.js script, and specifically in the IsIEPlugin method of the __cct_tracker class:
this.IsIEPlugin = function (e) {
var t = !1;
return document.write('<SCRIPT LANGUAGE=VBScript>\n on error resume next \n pluginFound = IsObject(CreateObject("' + e + '")) </SCR' + "IPT>\n"), t ? 1 : 0
}
This method is called several times, with different arguments:
this.pixelRequestParams.cctDir = this.IsIEPlugin("SWCtl.SWCtl.1"),
this.pixelRequestParams.cctFlashPlugin = this.IsIEPlugin("ShockwaveFlash.ShockwaveFlash.1");
if (this.IsIEPlugin("PDF.PdfCtrl.1") == 1 ||
this.IsIEPlugin("PDF.PdfCtrl.5") == 1 ||
this.IsIEPlugin("PDF.PdfCtrl.6") == 1)
this.pixelRequestParams.cctPdf = 1;
this.pixelRequestParams.cctQuickTime = this.IsIEPlugin("Quicktime.Quicktime"),
this.pixelRequestParams.cctRealPlayer = this.IsIEPlugin("rmocx.RealPlayer G2 Control.1"),
this.pixelRequestParams.cctWmPlayer = this.IsIEPlugin("wmplayer.ocx")
I suppose (even I'm not sure) that "cct" stands for "Clickable Conversion Tracking", so it must be some sort of tracking code.
After further investigations, I've determined that the "cct.js" script gets loaded by the Google Tag Manager (GTM) script http://www.googletagmanager.com/gtm.js.
So, if I'm not wrong, by removing the GTM code snippet from your HTML page, you should be able to solve the problem.
It seems that the VBSCRIPT code dynamically injected by the GTM JavaScript code sometimes is not executed, but don't ask me why, since I don't really know.

Page.setHtmlContent error with embedded gadgets

In an attempt to fudge a workaround for the weird "viewers cannot leave comments" issue in google sites*, I wrote the following silly test script:
function addComment(e) {
var currentPage = SitesApp.getActivePage();
var pageHTML = currentPage.getHtmlContent();
var newHTML = pageHTML.replace("BEGIN", "BEGIN "+Session.getActiveUser().getEmail());
currentPage.setHtmlContent(newHTML);
};
When the user presses the button, the current page content should be changed to include the current user's email address right after the word BEGIN (which I manually inserted- if this works I can just stick in a comment tag thingamabob.
This more or less works. The problem is that the setHtmlContent call does all sorts of weird things to the apps script gadget that contains the button in the first place. Here's the gadget before:
<img src="https://www.google.com/chart?chc=sites&cht=d&chdp=sites&chl=%5B%5BGoogle+Apps+Script'%3D20'f%5Cv'a%5C%3D0'10'%3D499'0'dim'%5Cbox1'b%5CF6F6F6'fC%5CF6F6F6'eC%5C0'sk'%5C%5B%22Apps+Script+Gadget%22'%5D'a%5CV%5C%3D12'f%5C%5DV%5Cta%5C%3D10'%3D0'%3D500'%3D197'dim'%5C%3D10'%3D10'%3D500'%3D197'vdim'%5Cbox1'b%5Cva%5CF6F6F6'fC%5CC8C8C8'eC%5C'a%5C%5Do%5CLauto'f%5C&sig=TbGPi2pnqyuhJ_BfSq_CO5U6FOI" data-origsrc="https://sites.google.com/a/macros/kstf.org/s/AKfycbzEsLBQucXCZZJwEh9c3RYhn81uJucvz3R5vHeJ2w/exec" data-type="maestro" data-props="align:left;borderTitle:Apps Script Gadget;height:200;showBorder:false;showBorderTitle:false;" width="500" height="200" style="display:block;text-align:left;margin-right:auto;"></div>
and here it is after:
<img src="https://www.google.com/chart?chc=sites&cht=d&chdp=sites&chl=%5B%5BGoogle+Gadget'%3D20'f%5Cv'a%5C%3D0'10'%3D499'0'dim'%5Cbox1'b%5CF6F6F6'fC%5CF6F6F6'eC%5C0'sk'%5C%5B%22Include+gadget+(iframe)%22'%5D'a%5CV%5C%3D12'f%5C%5DV%5Cta%5C%3D10'%3D0'%3D500'%3D197'dim'%5C%3D10'%3D10'%3D500'%3D197'vdim'%5Cbox1'b%5Cva%5CF6F6F6'fC%5CC8C8C8'eC%5C'a%5C%5Do%5CLauto'f%5C&sig=CvjXRgodwYVKPvmsyZR7EbHx2uM" data-igsrc="http://0.gmodules.com/ig/ifr?mid=0&synd=trogedit&url=http%3A%2F%2Fwww.gstatic.com%2Fsites-gadgets%2Fiframe%2Fiframe.xml&up_iframeURL=%2Fa%2Fmacros%2Fkstf.org%2Fs%2FAKfycbzEsLBQucXCZZJwEh9c3RYhn81uJucvz3R5vHeJ2w%2Fexec%3Fmid%3DACjPJvFOqF88RUUrqDeapp1PHF_lI3Xc3g5Hd3euTifzUYeaILmTTlMfBQ13yI_6%26bc%3Dtransparent%26f%3DArial%2C%2BVerdana%2C%2Bsans-serif%26tc%3D%2523444444%26lc%3D%25230033cc&up_scroll=no&w=100%&h=200" data-type="ggs-gadget" data-props="height:200;igsrc:http#58//0.gmodules.com/ig/ifr?mid=0&synd=trogedit&url=http%3A%2F%2Fwww.gstatic.com%2Fsites-gadgets%2Fiframe%2Fiframe.xml&up_iframeURL=%2Fa%2Fmacros%2Fkstf.org%2Fs%2FAKfycbzEsLBQucXCZZJwEh9c3RYhn81uJucvz3R5vHeJ2w%2Fexec%3Fmid%3DACjPJvFOqF88RUUrqDeapp1PHF_lI3Xc3g5Hd3euTifzUYeaILmTTlMfBQ13yI_6%26bc%3Dtransparent%26f%3DArial%2C%2BVerdana%2C%2Bsans-serif%26tc%3D%2523444444%26lc%3D%25230033cc&up_scroll=no&w=100%&h=200;mid:0;spec:http#58//www.gstatic.com/sites-gadgets/iframe/iframe.xml;up_iframeURL:/a/macros/kstf.org/s/AKfycbzEsLBQucXCZZJwEh9c3RYhn81uJucvz3R5vHeJ2w/exec?mid=ACjPJvFOqF88RUUrqDeapp1PHF_lI3Xc3g5Hd3euTifzUYeaILmTTlMfBQ13yI_6&bc=transparent&f=Arial,+Verdana,+sans-serif&tc=%23444444&lc=%230033cc;up_scroll:no;width:100%;" width="500" height="200" style="display:block;text-align:left;margin-right:auto;" class="igm"></div>
As best as I can tell, the "hey please set this as your HTML" method seems to be doing some chicanery to make certain that the document is properly parsed, but it's getting caught up in a tail-chasing effect of the iframe redirect. If I could hand in some DOM or something similar, this wouldn't be an issue.
Any advice? This was just a kind of exercise to see if I could finesse the visitor comment system anyway, so I'll probably just take another approach.
*: I know about several other ways to handle visitor comments, but this system needs to be able to work on many site pages, for many site authors in an apps domain, without needing complicated setup on the part of the site author. Eventually, I'll use something else (probably a variation on one of the two app engine forum systems I found this morning), but this was a quick stab at an interim solution. Next interim step is to save these data to the site DB and lay out the comments in the gadget itself. Gadget sizing is unsatisfying, however - I'd rather have the comments right in the page instead of in a separate iframe that has its own scroll bars.
First of all, the getEmail() method doesn't work with consumer accounts or when people outside your domain visit the site (unless your script runs as the user accessing the app)
Next, when you change the HTML of a page, it doesn't make the change immediately, but should happen on the next refresh.
Having said that, what you could do is have your script display the comments as well. You can have labels in your script that display previous comments.

Keeping the basic layout same across the web pages

I have a website in which I finished with the basic layout, design, fonts, styles, css etc.
For almost whole of the website, the layout i.e the sidebar, the footer, the background etc. will remain the same. There are custom embedded fonts used as well.
Since this basic layout will remain the same all across the website, I wanted to ask how can I prevent downloading of this content (like side-bar, fonts, or javascript etc.) again for the user, so that the other pages after the start do not take as much time as the start page.
I am sure there would be some mechanism since most of the websites have header/footer/sidebar in common across the pages. My website is in plain html/css, and there's no backend being used. Any ideas?
Your images, fonts, css and other contents will most likely be cached by the client's browser on the first hit, so it will be downloaded just once.
For the html page itself, since you use static html content, the only way I can think of is using AJAX request.
You probably want to use includes. So on each page you'd have a header include, a footer include, a sidebar include and even an include containing links to your css/js files.
Simplest way to do this would be to change your pages to be .php pages and use php includes to pull in the header file, footer file etc.
You can static-site generator like Jekyll.
You may design a basic layout first.
Avoid inline and embedded CSS maximum and add a class (can assign to multiple) or id (can assign to single) to common selectors.
Make a master stylesheet like master.css and attach this to every page.
Hope this helps.
You can do this in two way. You say you don't have a backend, however the server where your website is hosted can be the backend.
Without any backend interaction:
If you really prefer not to use the backend at all, you can make this a single page website, with some javascript to switch the content you have in there. The idea is you have your website structure, and your default data available the way you normally have it right. But you also have the html for your other pages in hidden divs. Then when you want to switch to say the about link you use javascript to get the content from the hidden div with that content and you place that content in the main div.
<!--First lets use jquery thought it can use some other framework-->
<script src="http://code.google.com/apis/ajaxlibs/documentation/index.html#jquery"></script>
<script>
$('a[href=#myAboutPage]').on('click',function(){//Whenever link that points to #myAboutPage is clicked
var getHTMLFROM = document.getElementById('myAboutPageHiddenContent').innerHTML;
//And place it on main div
document.getElementById('mainDivContent').innerHTML = getHTMLFROM
});
</script>
If you wanted to use some ajax interactions:
The process would be the same with the exception that the getHTMLFROM content, would actually be an html file that you request from the server.
Both of this javascript oriented methods will work, but would NOT recommend if you want your information to be SEO friendly. With that said reuse an external piece of css, to minimize redownloading the styling of your interface every single time.
There are definitely many ways to do this. I am a fan of dynamic inclusion. Here is a link to a great tutorial which explains how to set it up for your own page very simply. Dynamic Inclusion Tutorial NOTE: Don't be afaid of PHP, or having to change your file extension to PHP. It won't change your coding experience at all. It will just enhance your abilities.
I also have used the Javascript feature to hide certain elements. Depending on the size of your website, it may be just as easy to reload your CSS and navigation elements. However, if you really don't want your menu and logo to blink momentarily while it is reloading, you can just hide/reveal elements very simply with a bit of JS.
Here is an example function from my website:
function toggleVisible(e){
var i = e.id;
if(e.className == 'collapsed')
{
e.className = 'expanded';
e.innerHTML = 'Hide'
var hiddenArray = document.getElementsByClassName('hidden' + i);
hiddenArray[0].setAttribute('class', 'expanded' + i);
}
else if (e.className == 'expanded')
{
e.className = 'collapsed';
e.innerHTML = 'Show More';
var expandedArray = document.getElementsByClassName('expanded' + i);
expandedArray[0].setAttribute('class', 'hidden' + i);
}
}
The above code will run when the following link is clicked:
ANYWEBSITE.com || <a onClick="toggleVisible(this)" id="4" class="collapsed">Show More</a> || View PDF
Also, another user mentioned caching. Caching appears to be unreliable. Check out the following links for more info:
AJAX cache
HTML5 Application cache
2009 article about browser caching from stackoverflow.com question