Google Page Speed Tips - Part 3 - Override System Velocity Macros

This is an advanced technique that provides a Patch / Upgrade safe technique to customize / override the markup generated by all Percussion CM1 Templates. Using this technique it’s possible to fully control the JavaScript and CSS references that are loaded by all CM1 published Pages and Templates.

Override System Velocity Macros
Percussion uses an overlay system for the Velocity templates used by the system. A very simple technique can be used to override system defaults to accomplish specific website goals like Page Speed before a built-in solution is available. User Velocity Macros trump System Velocity macros by design, and they are upgrade and Patch friendly. Knowing that System Velocity Macros can be overridden, it makes sense to review the available Macros and assessing how they relate to Page Speed.

System Velocity Macros
System Velocity Macros are considered core product and will always be overwritten on upgrade or patching. For this reason, the contents of the sys_assembly.vm file should not be customized or overwritten. In contrast, User Velocity Macros are located in rx_resources/vm and are never overwritten on upgrade / patching (with the exception of rx_assembly.vm - we recommend leaving that file to itself).

Location: sys_resources/vm/sys_assembly.vm

There are a number of macros defined in the sys_assmbly.vm file, however a subset of those macros are responsible for rendering CM1 Page Templates and Widget display templates. We’ll go through each macro, including the macro source as of this writing in the section below.

See the System Velocity Macro reference post for details on each available system macro.

How Velocity Macro Overrides Work In Percussion
When the Percussion Server starts, the Velocity template engine is initialized. The engine first loads all .vm files from the sys_resources/vm folder. After the System Velocity macros are loaded, the system loads all User Velocity macros from the rx_resources/vm folder. Macro names in Velocity are global by design, and when the system detects a User Velocity Macro with the same name as a System Velocity macro, the User Velocity Macro will Overlay or Override the System Macro of the same name.

The mechanism is upgrade and patch safe as the installer / patch tools are not designed to overwrite user created resources in the rx_resources/vm folder.

So what does this mean for Page Speed?
It means that we can override any of the default Percussion System Velocity Macros with fine tuned versions of the macros that are tailored for your websites design and usage with a fully supported and supportable technique. In the next section we’ll break down the page header macro and go over creating an override version of it.


Breaking Down the System Header Macro
The system header macro is our biggest Page speed problem. In some cases it includes JavaScript that we don’t need or use, it pushes everything into the header, and it may or may not be including tags or other content that we don’t need on our site.

Macro Directive:

#macro(perc_templateHeader)##

The macro directive just says this is a macro and indicates any parameters (separated by spaces). Macros are called using #macro_name(param1 param2).

DocType Declaration:

#set($selectedDoctype = $perc.template.docType.selected)##
#foreach($option in $perc.template.docType.options)##
#if($option.option.equals($selectedDoctype))##
#perc_displayText("$!option.value")##
#end##
#end##

This block of code sets a variable named $selectedDoctype to the Document Type Declaration selected in the Metadata of the Template in the user interface. We probably want to keep this block when overriding the macro. Otherwise if someone changed the declaration in the UI, it wouldn’t show up on published pages.

Head:

<head>

This defines the start of the head on the page.

XUA Compatible Block:

##See IE10 Required for drag drop CMS-79 and CMS-3403
#if($perc.isEditMode())
<meta http-equiv="X-UA-Compatible" content="IE=10"/>
#else
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
#end
**Charset Directive:**
<meta content="text/html; charset=UTF-8" http-equiv="content-type"/>

Canonical Link Block:

##
#set($percPageID = $perc.getPage().getId())
#if($perc.linkContext.getSite().isCanonical() && "$!{percPageID}" != "")##
#set($renderCanonical = true)##
#if($perc.linkContext.getSite().isCanonicalReplace())##
#set($stripCustomCanonical = true)##
#elseif($rx.pageutils.checkLinkCanonical($!perc.page.additionalHeadContent) || $rx.pageutils.checkLinkCanonical($!perc.template.additionalHeadContent))##
#set($renderCanonical = false)##
#end##
#if($renderCanonical)##
#set($canLink = $rx.pageutils.itemLink($perc.linkContext, $perc.getPage()).toString())##
#if($perc.linkContext.getMode().name() == "PREVIEW")##
#set($linkPref = "/Sites/${perc.linkContext.getSite().getName()}")##
#set($canLink = $canLink.substring($linkPref.length()))##
#end##
<link rel="canonical" href="${perc.linkContext.getSite().getSiteProtocol()}://${perc.linkContext.getSite().getName()}${canLink}" />
#end##
#end##

Browser Title:
<title>$tools.esc.html($!perc.page.title)</title>

Global Error Handler:

<script type="text/javascript">
var percGlobalErrors = [];         
window.onerror=function(msg, url, linenumber){
var jsErrorMsg = 'JavaScript error -- ' + msg;  
percGlobalErrors.push(jsErrorMsg);
return false;
}
</script>

CSS Resource Loading Block:

#foreach($css_link in $rx.pageutils.cssLinks($perc.linkContext, $sys.assemblyItem))##
<link rel="stylesheet" href="$css_link" type="text/css" media="all" />
#end##

Main Theme CSS Loading Block:

Region Override CSS Block:

#set($__perc_region_css = $rx.pageutils.themeRegionCssLink($perc.linkContext, $perc.template.theme, $perc.editMode, $perc.editType).toString().trim())##
#if("$!__perc_region_css" != "")##
<link rel="stylesheet" href="${__perc_region_css}" type="text/css" media="all" />
#end##

Region Style Overrides Block:

#set($__style = $!perc.template.cssRegion.replaceAll("$tools.esc.q", "").trim())##
#if("$!__style" != "")##
<style type="text/css">
$__style
</style>
#end##
#set($__style = $!perc.template.cssOverride.trim())##
#if("$!{__style}" != "")
<style type="text/css">
$__style
</style>
#end##

JavaScript Resource Loading Block:

#foreach($js_link in $rx.pageutils.javascriptLinks($perc.linkContext, $sys.assemblyItem))##
<script type="text/javascript" src="$js_link"></script>
#end##

Exclude Page From Search Engines Block:

#if("$!perc.page.noindex" == "true")##
<meta name="robots" content="noindex" />
#end##

Page Description Block:

#if("$!perc.page.description" != "")##
<meta name="description" content="$tools.esc.html($!perc.page.description)" />
#end##

Dublin Core Metadata Block:

#if($sys.item.hasProperty('rx:page_authorname') && $sys.item.getProperty('rx:page_authorname').getString() != "")##
<meta property="dcterms:author" content="$tools.esc.html($sys.item.getProperty('rx:page_authorname').getString())" />
## Use the correct dcterms property
<meta property="dcterms:creator" content="$tools.esc.html($sys.item.getProperty('rx:page_authorname').getString())" />
#end##
<meta property="dcterms:type" content="page"/>
<meta property="dcterms:source" content="$tools.esc.html($!perc.template.name)"/>
<meta property="dcterms:created" content="#perc_displayXdsDateTime('rx:sys_contentpostdate')"/>
<meta property="dcterms:modified" content="#perc_displayXdsDateTime('rx:sys_contentlastmodifieddate')"/> 
<meta property="dcterms:alternative" content="$tools.esc.html($!perc.page.linkTitle)"/>

Percussion Tags & Categories Block:

#if($sys.item.hasProperty("page_tags"))##
#set( $tagValues = $sys.item.getProperty('page_tags').getValues() )##
#foreach($tag in $tagValues)##
<meta property="perc:tags" content="$tools.esc.html($tag.String)"/>
#end##
#end##
#if($sys.item.hasProperty("page_categories_tree"))##
#set( $categoryValues = $sys.item.getProperty('page_categories_tree').getValues() )##
#foreach($category in $categoryValues)##
#set($categoryLabel = $rx.pageutils.getCategoryLabel($category.String))##
<meta property="perc:category" content="$tools.esc.html($!{categoryLabel})"/>
#end##
#end##

Percussion Calendar Block:

#if($sys.item.hasProperty("page_calendar"))##
#set( $calendarValues = $sys.item.getProperty('page_calendar').getValues())##
#foreach($calendar in $calendarValues)##
<meta property="perc:calendar" content="$tools.esc.html($calendar.String)"/>
#end##
#end##

Page Start & End Date Metadata Block:

#if($sys.item.hasProperty("page_start_date"))##
<meta property="perc:start_date" content="#perc_displayXdsDateTime('page_start_date')"/> 
#end##
#if($sys.item.hasProperty("page_end_date"))##
<meta property="perc:end_date" content="#perc_displayXdsDateTime('page_end_date')"/> 
#end##
##

Canonical Duplicate Scrub of Head Content Block:

#if ($stripCustomCanonical)##
#set($tmplAdditionalHeadContent = $rx.pageutils.stripLinkCanonical($!perc.template.additionalHeadContent))##
#set($pageAdditionalHeadContent = $rx.pageutils.stripLinkCanonical($!perc.page.additionalHeadContent))##

Render Template Head Content Block:
#perc_displayText("$!tmplAdditionalHeadContent")##

Render Page Head Content Block:

#perc_displayText("$!pageAdditionalHeadContent")##
#else##

Render Template Head Content Block:
#perc_displayText("$!perc.template.additionalHeadContent")##

Render Page Head Content Block:

#perc_displayText("$!perc.page.additionalHeadContent")##
#end##
##

Render Percussion DTS Delivery Base Block:
#perc_addDeliveryJSFunctions()##

Render Google Analytics Block:

## The analytics script additions must be the last thing added to the header
##perc_addGoogleAnalyticsScript()##

Render LiveFirst Pages not done importing on Preview Block:
#suppressCatalogedLinks()##

Render the Preview Mobile Toolbar Block:
#addMobilePreviewToolbar()##

Render Foundation JavaScript if this is a Responsive Template Block:
#addResponsiveResources()##

Close the Head!
</head>
Start the Body!
<body>
Render Linkback Block
<input type="hidden" id="perc_linkback_id" value="$perc.page.id"/>
Render Hummingbird Editor Block (deprecated)

#if ($perc.isPreviewMode() && !$perc.isEditMode() && $sys.isHummingbirdEnabled)##
<div data-perc-title="$sys.item.getProperty('sys_title').String" data-perc-contentid="$sys.assemblyItem.getId().getUUID()"></div>##
#end##

Render “Protected Region” Block

#if($perc.linkContext.mode == "PUBLISH" && $perc.template.protectedRegion && $perc.template.protectedRegion.String != "")##
#set($loginPage = $rx.pageutils.getSiteLoginPage($perc.linkContext.site.name))##
<div id="protectedRegionInformation" data="{&quot;protectedRegion&quot;:&quot;$!perc.template.protectedRegion&quot;,&quot;protectedRegionText&quot;:&quot;$!perc.template.protectedRegionText&quot;,&quot;siteLoginPage&quot;:&quot;$!loginPage&quot;}" style="display:none"></div>
#if("" != "$!{perc.template.protectedRegion}"))##
<style>
#$!perc.template.protectedRegion{
display:none;
}
</style>
#end##
#end##

Render Hidden Page Summary Block:

#if($perc.page.name && $sys.item.getProperty("rx:page_summary").String != "")##
#set($summaryField="#displayfield('rx:page_summary')")##
#set($summaryField2=$summaryField.replace("<!-- morelink -->", '&nbsp;<a class="perc-more-link"></a>'))##
<div property="dcterms:abstract" style="display:none" datatype="rdf:XMLLiteral">$summaryField2</div>
#end##
#perc_displayText("$!perc.template.afterBodyStartContent")##
#perc_displayText("$!perc.page.afterBodyStartContent")##
#end##

The End of the Header

With the blocks broken down in the Header, we need to do the same to the Footer. Then we can pick the blocks we need to keep, the blocks we need to move, and the blocks we want to exclude for our override macro.

Macro Directive:
#macro(perc_templateFooter)##
Render Page Before Body Close:
#perc_displayText("$!perc.page.beforeBodyCloseContent")##
Render Template Before Body Close:
#perc_displayText("$!perc.template.beforeBodyCloseContent")##
Hummingbird (Deprecated):
#if ($perc.isPreviewMode() && !$perc.isEditMode() && $sys.isHummingbirdEnabled)##

#end##
</body>
End Body!
</html>
End HTML!
#end##

Checkpoint
This is a long post but necessary to understand how the Template macro’s work before we optimized them for a given site. We have broken down the Percussion stock Velocity Macros. We know we want to update these to get better speed scores. So now what? We will now go through the process of creating the overrides and optimizing existing Templates and Pages. Before continuing make sure that you have updated any inline JavaScript to be Async or Deferred on your Pages and Templates.

JQuery to Defer or Not To Defer
Note: Edited.
jQuery is used by many Percussion widgets and by many customer custom scripts and Widgets as well. When jQuery is deferred, all scripts that rely on jQuery must also be deferred. Otherwise you could end up with “Servicebase Not Defined” errors when non-deferred scripts reference jQuery. The rest of this example assumes that you do not want to defer jQuery or can’t because of Widgets in use.

The following widgets currently inline some JavaScript (resolved in the 5.4 release)

  • Navigation Widget
  • Form Widget
  • Comment Form Widget
  • Image Widget
  • Poll Widget
  • Directory Widget
  • Social Buttons Widget
  • Calendar Widgets
  • Image Slider Widget

Create the User Velocity Macro Override File and Start Testing

  • Pick a few target Pages on the WebSite and test Page Speed recording the results.
  • Create a file named customer_override.vm
  • Copy the following macros from /sys_resources/vm/sys_assembly.vm
    ** perc_templateHeader
    ** perc_templateFooter
    ** perc_addDeliveryJSFunctions
    ** addResponsiveResources
  • Update the macros in the customer_override.vm file. (see example below)
  • Copy the customer_override.vm file to the /rx_resources/vm/customer_overrides.vm
  • Restart the Percussion service
  • Publish Pages and test PageSpeed compare against previous Page Speed, there should be an increase in score.

Example customer_override.vm
The perc_addDeliveryJSFunction includes inline script, so it can’t be deferred. It also needs registered before any inline scripts in the page. This is needed on any Templates that use Percussion DTS Widgets.

## Add the delivery service base function to the page
#macro(perc_addDeliveryJSFunctions)##
#set($dServiceBase = $rx.pageutils.getDeliveryServer($sys.assemblyItem.PubServerId))##
#set($cm1Version = $rx.pageutils.productVersion())##
#if(! $cm1Version )##
#set($cm1Version = "")##
#end
#set($cm1LicenseStatus = $rx.pageutils.licenseStatus())##
#if(! $cm1LicenseStatus)##
#set($cm1License = "")##
#else##
#set($cm1License = $cm1LicenseStatus.licenseId)##
#end##
<script type="text/javascript">
        (function($) {(function() { jQuery.getDeliveryServiceBase = function(){ return '$dServiceBase';};jQuery.getCm1License = function(){ return '$!{cm1License}';};jQuery.getCm1Version = function(){ $

#end##

The addResponsiveResources macro is intended to be called from the head. We need to update this macro to pull out the JavaScript references and move them to the footer macro. Here is the updated macro.

#macro(addResponsiveResources)##
#if ($perc.template.sourceTemplateName.startsWith("perc.resp"))##
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
#if($perc.linkContext.mode == "PUBLISH")##
<link rel="stylesheet" href="/web_resources/cm/foundation/css/foundation.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="/web_resources/cm/foundation/css/normalize.css" type="text/css" media="all" />
#else##
<link rel="stylesheet" href="/Rhythmyx/web_resources/cm/foundation/css/foundation.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="/Rhythmyx/web_resources/cm/foundation/css/normalize.css" type="text/css" media="all" />
#end##
#end##
#end##

The perc_templateFooter macro get’s a few more changes, as this is where we are shifting our JavaScript resources. The updated macro loads dependent scripts first, followed by the responsive framework script, followed by Template Before Body Close scripts and Page Before Body Close scripts. Deferred scripts are guaranteed to be loaded in order. An important note is the #if($perc.editMode)section.

If you are providing your own version of JQuery you could use this location in the macro to load your version of JQuery on Published and Preview views but Percussion’s version of jQuery when in the editor where that version is required. The default code below will load Percussion’s jquery in either case. This will also remove the need for 2 Jqueries to be loaded in the Page. If you take this approach you would remove your version of JQuery from all Templates and Pages and use the macro to inject the script reference.

#macro(perc_templateFooter)##
#foreach($js_link in $rx.pageutils.javascriptLinks($perc.linkContext, $sys.assemblyItem))##
#if( $js_link != "/Rhythmyx/web_resources/cm/jslib/jquery.js" && $js_link != "/web_resources/cm/jslib/jquery.js")##
#if(!$perc.isEditMode())##
<script defer type="text/javascript" src="$js_link"></script>
#else##
<script type="text/javascript" src="$js_link"></script>
#end##
#end##
#end##
#if ($perc.template.sourceTemplateName.startsWith("perc.resp"))##
#if($perc.linkContext.mode == "PUBLISH")##
<script defer type="text/javascript" src="/web_resources/cm/foundation/js/foundation.min.js"></script>
#else##
<script defer type="text/javascript" src="/Rhythmyx/web_resources/cm/foundation/js/foundation.min.js"></script>
#end##
#end##
#perc_displayText("$!perc.page.beforeBodyCloseContent")##
#perc_displayText("$!perc.template.beforeBodyCloseContent")##
<script defer type="text/javascript">
var percGlobalErrors = [];
window.onerror=function(msg, url, linenumber){
var jsErrorMsg = 'JavaScript error -- ' + msg;
percGlobalErrors.push(jsErrorMsg);
return false;
}
</script>
</body>
</html>
#end

The final macro is the perc_templateHeader macro. The suggested override is below. All JavaScript references have been removed and pushed into the Footer macro where they are deferred.

#macro(perc_templateHeader)##
#set($selectedDoctype = $perc.template.docType.selected)##
#foreach($option in $perc.template.docType.options)##
#if($option.option.equals($selectedDoctype))##
#perc_displayText("$!option.value")##
#end##
#end##
<head>
##See IE10 Required for drag drop CMS-79 and CMS-3403
#if($perc.isEditMode())
<meta http-equiv="X-UA-Compatible" content="IE=10"/>
#else
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
#end
<meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
##
#set($percPageID = $perc.getPage().getId())
#if($perc.linkContext.getSite().isCanonical() && "$!{percPageID}" != "")##
#set($renderCanonical = true)##
#if($perc.linkContext.getSite().isCanonicalReplace())##
#set($stripCustomCanonical = true)##
#elseif($rx.pageutils.checkLinkCanonical($!perc.page.additionalHeadContent) || $rx.pageutils.checkLinkCanonical($!perc.template.additionalHeadContent))##
#set($renderCanonical = false)##
#end##
#if($renderCanonical)##
#set($canLink = $rx.pageutils.itemLink($perc.linkContext, $perc.getPage()).toString())##
#if($perc.linkContext.getMode().name() == "PREVIEW")##
#set($linkPref = "/Sites/${perc.linkContext.getSite().getName()}")##
#set($canLink = $canLink.substring($linkPref.length()))##
#end##
<link rel="canonical" href="${perc.linkContext.getSite().getSiteProtocol()}://${perc.linkContext.getSite().getName()}${canLink}" />
#end##
#end##
<title>$tools.esc.html($!perc.page.title)</title>
#foreach($css_link in $rx.pageutils.cssLinks($perc.linkContext, $sys.assemblyItem))##
<link rel="stylesheet" href="$css_link" type="text/css" media="all" />
#end##
<link rel="stylesheet" href="$rx.pageutils.themeLink($perc.linkContext, $perc.template.theme)" type="text/css" media="all" />
#set($__perc_region_css = $rx.pageutils.themeRegionCssLink($perc.linkContext, $perc.template.theme, $perc.editMode, $perc.editType).toString().trim())##
#if("$!__perc_region_css" != "")##
<link rel="stylesheet" href="${__perc_region_css}" type="text/css" media="all" />
#end##
#set($__style = $!perc.template.cssRegion.replaceAll("$tools.esc.q", "").trim())##
#if("$!__style" != "")##
<style type="text/css">
$__style
</style>
#end##
#if("$!perc.page.noindex" == "true")##
<meta name="robots" content="noindex" />
#end##
#if("$!perc.page.description" != "")##
<meta name="description" content="$tools.esc.html($!perc.page.description)" />
#end##
#if($sys.item.hasProperty('rx:page_authorname') && $sys.item.getProperty('rx:page_authorname').getString() != "")##
<meta property="dcterms:author" content="$tools.esc.html($sys.item.getProperty('rx:page_authorname').getString())" />
## Use the correct dcterms property
<meta property="dcterms:creator" content="$tools.esc.html($sys.item.getProperty('rx:page_authorname').getString())" />
#end##
<meta property="dcterms:type" content="page"/>
<meta property="dcterms:source" content="$tools.esc.html($!perc.template.name)"/>
<meta property="dcterms:created" content="#perc_displayXdsDateTime('rx:sys_contentpostdate')"/>
<meta property="dcterms:modified" content="#perc_displayXdsDateTime('rx:sys_contentlastmodifieddate')"/>
<meta property="dcterms:alternative" content="$tools.esc.html($!perc.page.linkTitle)"/>
#if($sys.item.hasProperty("page_tags"))##
#set( $tagValues = $sys.item.getProperty('page_tags').getValues() )##
#foreach($tag in $tagValues)##
<meta property="perc:tags" content="$tools.esc.html($tag.String)"/>
#end##
#end##
#if($sys.item.hasProperty("page_categories_tree"))##
#set( $categoryValues = $sys.item.getProperty('page_categories_tree').getValues() )##
#foreach($category in $categoryValues)##
#set($categoryLabel = $rx.pageutils.getCategoryLabel($category.String))##
<meta property="perc:category" content="$tools.esc.html($!{categoryLabel})"/>
#end##
#end##
#if($sys.item.hasProperty("page_calendar"))##
#set( $calendarValues = $sys.item.getProperty('page_calendar').getValues())##
#foreach($calendar in $calendarValues)##
<meta property="perc:calendar" content="$tools.esc.html($calendar.String)"/>
#end##
#end##
#if($sys.item.hasProperty("page_start_date"))##
<meta property="perc:start_date" content="#perc_displayXdsDateTime('page_start_date')"/>
#end##
#if($sys.item.hasProperty("page_end_date"))##
<meta property="perc:end_date" content="#perc_displayXdsDateTime('page_end_date')"/>
#end##
##
##
#if ($stripCustomCanonical)##
#set($tmplAdditionalHeadContent = $rx.pageutils.stripLinkCanonical($!perc.template.additionalHeadContent))##
#set($pageAdditionalHeadContent = $rx.pageutils.stripLinkCanonical($!perc.page.additionalHeadContent))##
#perc_displayText("$!tmplAdditionalHeadContent")##
#perc_displayText("$!pageAdditionalHeadContent")##
#else##
#perc_displayText("$!perc.template.additionalHeadContent")##
#perc_displayText("$!perc.page.additionalHeadContent")##
#end##
##
##suppressCatalogedLinks()##
##addMobilePreviewToolbar()##
#addResponsiveResources()##
#foreach($js_link in $rx.pageutils.javascriptLinks($perc.linkContext, $sys.assemblyItem))##
#if($js_link =="/Rhythmyx/web_resources/cm/jslib/jquery.js" || $js_link =="/web_resources/cm/jslib/jquery.js")##
<script type="text/javascript" src="$js_link"></script>
#perc_addDeliveryJSFunctions()##
#end##
#end##
</head>
<body>
<input type="hidden" id="perc_linkback_id" value="$perc.page.id"/>
#if($perc.linkContext.mode == "PUBLISH" && $perc.template.protectedRegion && $perc.template.protectedRegion.String != "")##
#set($loginPage = $rx.pageutils.getSiteLoginPage($perc.linkContext.site.name))##
<div id="protectedRegionInformation" data="{&quot;protectedRegion&quot;:&quot;$!perc.template.protectedRegion&quot;,&quot;protectedRegionText&quot;:&quot;$!perc.template.protectedRegionText&quot;,&quot$
#if("" != "$!{perc.template.protectedRegion}"))##
<style>
#$!perc.template.protectedRegion{
display:none;
}
</style>
#end##
#end##
#if($perc.page.name && $sys.item.getProperty("rx:page_summary").String != "")##
#set($summaryField="#displayfield('rx:page_summary')")##
#set($summaryField2=$summaryField.replace("<!-- morelink -->", '&nbsp;<a class="perc-more-link"></a>'))##
<div property="dcterms:abstract" style="display:none" datatype="rdf:XMLLiteral">$summaryField2</div>
#end##
#perc_displayText("$!perc.template.afterBodyStartContent")##
#perc_displayText("$!perc.page.afterBodyStartContent")##
##
#end

That concludes the technique for Part 3. Some of these optimizations will ship with the 5.4 release of CM1 at which time you could simply comment out or remove youUser Velocity Macro overrides. Or you can continue to use your overrides to more fully control the markup that CM1 templates generate.

Comments / Feedback welcome!

If we are using our own version of jquery, could you help me understand how I would reference that in the customer_override.vm file? I know in the documentation it says we can make that change in the perc_templateFooter override code (if using the example), and that we would need to remove our jquery reference from templates and pages, but it is unclear exactly how to then add the reference to our version of jquery to the override file. Would we add the reference directly into this code? If so, where in this code? Or, does the $js_link variable access a list of script references? And, if so, would we need to add our jquery reference there? I apologize for all the questions, but I’m just trying to make sure I don’t break anything. Speaking of which, one last question… if I add this override and something breaks, can I simply delete the override document and it will all go back to the way it was originally set up after restarting the Percussion service? I appreciate it.

One other question… is there a way to remove the references to jquery-ui.js and jquery-ui-1.8.9.custom.css for published versions of the site, or are those necessary for Percussion to work properly?

Hi Kevin,

It looks like the Form widget relies on JQuery UI for it’s Date Picker and several Editor functions rely on JQuery UI. If you aren’t using Date Picker fields in the Form widget, and you aren’t using any of the jquery UI styles / JavaScript functions in your design, you could update the macro’s with a #if block to exclude them.

To exclude jquery-ui-1.8.9.custom.css, that would go in the CSS loop in the Header macro:

#foreach($css_link in $rx.pageutils.cssLinks($perc.linkContext, $sys.assemblyItem))##
#if($css_link != "/web_resources/cm/themes/smoothness/jquery-ui-1.8.9.custom.css" && !$perc.isEditMode())##
<link rel="stylesheet" href="$css_link" type="text/css" media="all" />
#end##
#end##

For Jquery UI that is being added in the Footer macro in the example above, so we could add a condition to that macro too. In the example below, I added an and condition to the if statement that was filtering out jquery.js from the footer. When in Preview or the Editory JQuery UI will get pulled from /Rhyhmyx//web_resources so I just added a not equal to “/web_resources/cm/jslib/jquery-ui.js” to the if statement.

 #foreach($js_link in $rx.pageutils.javascriptLinks($perc.linkContext, $sys.assemblyItem))##
#if( $js_link != "/Rhythmyx/web_resources/cm/jslib/jquery.js" && $js_link != "/web_resources/cm/jslib/jquery.js" && $js_link!="/web_resources/cm/jslib/jquery-ui.js")##
#if(!$perc.isEditMode())##
<script defer type="text/javascript" src="$js_link"></script>
#else##
<script type="text/javascript" src="$js_link"></script>
#end##
#end##
#end##

Hi Kevin,

Thanks for asking! If you are supplying your own version of JQuery, it is most likely in the Metadata->Additional Content area. CM1 is currently adding JQuery to the <head> and you most likely have your version of JQuery in the before body close (</body>).

The CM1 Editor needs it’s version of JQuery but it should not be needed on the Published site. We can use the $perc.isEditMode() condition to check if the macro is being run in the Editor or if in Preview / Publish contexts / modes.

Unless you have been careful not to use jQuery in inline scripts on your Pages and Templates you will most likely need to put your version of Jquery in the head to account for any inline scripts, and to account for any Percussion Widgets that inline scripts that use JQuery. Easiest thing to do would be to remove the reference to Jquery in your Metadata->Additional Content fields on your templates and update the header macro as below. In my testing, I did not get reliable results with Jquery only in the before body close or when it was deferred, so I pushed it to the head in the examples.

Here is an example updated block from the Header macro that swaps out Jquery on the published page:

#if($js_link =="/Rhythmyx/web_resources/cm/jslib/jquery.js" || $js_link =="/web_resources/cm/jslib/jquery.js")##
#if($perc.isEditMode))##
<script type="text/javascript" src="$js_link"></script>
#else##
<script src="CHANGE TO YOUR PUBLISHED JQUERY"></script>
#perc_addDeliveryJSFunctions()##
#end##
#end##

You would replace CHANGE TO YOUR PUBLISHED JQUERY to your published jquery path or replace that script tag with a public CDN link like:

<script
  src="https://code.jquery.com/jquery-1.12.4.min.js"
  integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="
  crossorigin="anonymous"></script>

The CDN link may be better as if the browser has already downloaded from another website site, JQuery would be loaded from the browser cache.

Hope this helps,

-n

Thanks so much, Nate! This gives me a much better understanding of how this code works. I will begin experimenting, and let you know if I have any questions. I really appreciate all your efforts.