Commit 55c2d644 authored by Romain Courteaud's avatar Romain Courteaud

erp5_web_js_style: hide the page until full rendering is done

Lazy loading img are also not loaded, which speed up page rendering.
parent c94ddf28
Pipeline #23964 failed with stage
...@@ -5,6 +5,16 @@ ...@@ -5,6 +5,16 @@
loopEventListener, history, console) { loopEventListener, history, console) {
"use strict"; "use strict";
function hidePage() {
document.documentElement.hidden = true;
document.documentElement.style.display = 'none';
}
function showPage() {
document.documentElement.hidden = false;
document.documentElement.style.display = 'unset';
}
// XXX Copy/paste from renderjs // XXX Copy/paste from renderjs
function ajax(url) { function ajax(url) {
var xhr; var xhr;
...@@ -386,7 +396,7 @@ ...@@ -386,7 +396,7 @@
rJS(window) rJS(window)
.allowPublicAcquisition("reportServiceError", function () { .allowPublicAcquisition("reportServiceError", function () {
this.element.hidden = false; showPage();
throw rJS.AcquisitionError(); throw rJS.AcquisitionError();
}) })
.declareJob("listenURLChange", listenURLChange) .declareJob("listenURLChange", listenURLChange)
...@@ -426,10 +436,16 @@ ...@@ -426,10 +436,16 @@
gadget.listenURLChange(); gadget.listenURLChange();
body.appendChild(style_gadget.element); body.appendChild(style_gadget.element);
gadget.element.hidden = false; // Show the page after the first rendering
// This prevent displaying the original HTML page
// in case cpu/network is too slow
showPage();
scrollToHash(window.location.hash); scrollToHash(window.location.hash);
}, function (error) { }, function (error) {
gadget.element.hidden = false; // Ensure the page is visible in case of error
showPage();
throw error; throw error;
}); });
}, function (error) { }, function (error) {
...@@ -448,5 +464,13 @@ ...@@ -448,5 +464,13 @@
}); });
}); });
// Hide the page as soon as possible
// This prevent displaying the original HTML page
// in case cpu/network is too slow
// (when fetching rendering gadget or if pages containes img elements)
// Hiding the page MUST NOT be done in the HTML, to ensure compatibility
// with browsers without javascript
hidePage();
}(window, document, RSVP, rJS, XMLHttpRequest, DOMParser, URL, }(window, document, RSVP, rJS, XMLHttpRequest, DOMParser, URL,
rJS.loopEventListener, history, console)); rJS.loopEventListener, history, console));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testJsStyleDemoStyleImage</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test JS Style No Style</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test JS Style Image not loaded on recent browsers (expected failure)</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<tr>
<td>open</td>
<td>${base_url}/ERP5Site_createWebJSStyleZuiteTestData?configuration=section</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Web Site created.</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/wait_for_activities" />
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/web_site_module/erp5_web_js_style_test_site/erp5_web_js_style_test_image_contentpage</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//main//img[@id='lazy_img' and @alt='loaded']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//main//img[@id='eager_img' and @alt='error']</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
from DateTime import DateTime from DateTime import DateTime
portal = context.getPortalObject() portal = context.getPortalObject()
now = DateTime()
web_page_portal_type = "Web Page" web_page_portal_type = "Web Page"
web_site_portal_type = "Web Site" web_site_portal_type = "Web Site"
...@@ -34,6 +35,9 @@ web_page_content_en_id = "erp5_web_js_style_test_contentpage_en" ...@@ -34,6 +35,9 @@ web_page_content_en_id = "erp5_web_js_style_test_contentpage_en"
web_page_content_fr_id = "erp5_web_js_style_test_contentpage_fr" web_page_content_fr_id = "erp5_web_js_style_test_contentpage_fr"
web_page_content_zh_id = "erp5_web_js_style_test_contentpage_zh" web_page_content_zh_id = "erp5_web_js_style_test_contentpage_zh"
web_page_image_content_en_id = "erp5_web_js_style_test_image_contentpage_en"
web_page_image_content_reference = 'erp5_web_js_style_test_image_contentpage'
publicate_date = DateTime('2011/12/13 11:22:33 GMT+5') publicate_date = DateTime('2011/12/13 11:22:33 GMT+5')
### English web page ### English web page
...@@ -151,6 +155,24 @@ web_page = module.newContent( ...@@ -151,6 +155,24 @@ web_page = module.newContent(
) )
portal.portal_workflow.doActionFor(web_page, 'publish_action') portal.portal_workflow.doActionFor(web_page, 'publish_action')
### English image web page
module = portal.getDefaultModule(web_page_portal_type)
if getattr(module, web_page_image_content_en_id, None) is not None:
module.manage_delObjects([web_page_image_content_en_id])
web_page = module.newContent(
portal_type=web_page_portal_type,
id=web_page_image_content_en_id,
reference=web_page_image_content_reference,
contributor_value=contributor,
language="en",
version="001",
text_content="""
<img loading="eager" id="eager_img" alt="default alt" src="WebSite_downloadFakeImage?cachekey=eager_%s"></img>
<img loading="lazy" id="lazy_img" alt="default alt" src="WebSite_downloadFakeImage?cachekey=lazy_%s"></img>
""" % (now.HTML4(), now.HTML4())
)
portal.portal_workflow.doActionFor(web_page, 'publish_action')
configuration_dict = { configuration_dict = {
'nostyle': { 'nostyle': {
'title': 'No Style', 'title': 'No Style',
...@@ -251,7 +273,7 @@ web_site = module.newContent( ...@@ -251,7 +273,7 @@ web_site = module.newContent(
criterion_property_list=('reference',), criterion_property_list=('reference',),
**configuration_dict[configuration] **configuration_dict[configuration]
) )
web_site.setCriterion('reference', identity='erp5_web_js_style_test_contentpage') web_site.setCriterion('reference', identity=web_page_content_reference)
web_section = web_site.newContent( web_section = web_site.newContent(
portal_type=web_section_portal_type, portal_type=web_section_portal_type,
......
import random
response = REQUEST.RESPONSE
cache_value = DateTime().HTML4() + str(random.random())
def getCacheValue(cachekey):
return cache_value
from Products.ERP5Type.Cache import CachingMethod
getCacheValue = CachingMethod(getCacheValue, id=script.getId())
old_cache_value = getCacheValue(cachekey)
if cache_value == old_cache_value:
emoji = 'never accessed'
response.setHeader('Content-Type', 'image/svg+xml')
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90%">' + emoji + '</text></svg>'
else:
emoji = 'already accessed'
response.setStatus(410)
return emoji
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>REQUEST, cachekey</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WebSite_downloadFakeImage</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*globals window, document, rJS, domsugar*/ /*globals window, document, rJS, domsugar, URL*/
/*jslint indent: 2, maxlen: 80*/ /*jslint indent: 2, maxlen: 80*/
(function (window, document, rJS, domsugar) { (function (window, document, rJS, domsugar, URL) {
"use strict"; "use strict";
function changeAltLoad(evt) {
evt.target.alt = 'loaded';
}
function changeAltError(evt) {
evt.target.alt = 'error';
}
function renderSitemap(sitemap, element) { function renderSitemap(sitemap, element) {
var child_list = [], var child_list = [],
i; i;
...@@ -54,7 +62,11 @@ ...@@ -54,7 +62,11 @@
language_list, language_list,
document_list, document_list,
child_list, child_list,
i; i,
web_page_element,
element_list,
element,
feed_url;
if (modification_dict.hasOwnProperty('page_title')) { if (modification_dict.hasOwnProperty('page_title')) {
document.title = gadget.state.page_title; document.title = gadget.state.page_title;
...@@ -72,11 +84,30 @@ ...@@ -72,11 +84,30 @@
html: gadget.state.form_html_content html: gadget.state.form_html_content
}); });
} else { } else {
// Try to find the Web Page content only // Try to find the Web Page content only
domsugar(gadget.element.querySelector('main'), { web_page_element = domsugar('div', {html: gadget.state.html_content})
html: domsugar('div', {html: gadget.state.html_content}) .querySelector('div.input').firstChild;
.querySelector('div.input').firstChild.innerHTML
}); // Replace IMG src value, to disable the browser cache
// and force downloading it.
// Used by test to check if original url has been accessed
element_list = web_page_element.querySelectorAll('img');
for (i = 0; i < element_list.length; i += 1) {
element = element_list[i];
feed_url = new URL(element.getAttribute('src'),
window.location.href);
feed_url.search = feed_url.search + '&cachevalue=foo';
// Not renderJS friendly, but that's only for the test...
element.addEventListener('load', changeAltLoad);
element.addEventListener('error', changeAltError);
element.src = feed_url.href;
}
domsugar(
gadget.element.querySelector('main'),
Array.from(web_page_element.childNodes)
);
} }
} }
if (modification_dict.hasOwnProperty('gadget_style_url')) { if (modification_dict.hasOwnProperty('gadget_style_url')) {
...@@ -135,4 +166,4 @@ ...@@ -135,4 +166,4 @@
} }
}); });
}(window, document, rJS, domsugar)); }(window, document, rJS, domsugar, URL));
\ No newline at end of file \ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment