Page number in asp.net core footer (partial view) - asp.net-core

I am using jsreport with my Asp.Net Core MVC project. It is working all right except for page numbers.
I am using a partial view for footer.
Here is the partial view:
<table style="font-size: 10px; padding-bottom: 5px; padding-top: 0in; padding-left: 0.25in; padding-right: 0.25in;">
<tbody>
<tr>
<td style="width: 1.5in; text-align: left;"><b>#($"{DateTime.Now}")</b></td>
<td style="width: 8.2in; text-align: center;">Brand Name</td>
<td style="width: 1.5in; text-align: right;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></td>
</tr>
</tbody>
</table>
Unfortunately, the page number part is not working at all. I just see "Page of " in the rendered PDF.
I am using the Chrome-PDF recipe and do not wish to use pdf-utils for the footer.
Even if I put the following line in the main report body view, I still do not get any page numbers.
Page <span class="pageNumber"></span> of <span class="totalPages"></span>
Any help will be appreciated.

The header html needs to be passed to the Template.Chrome.HeaderTemplate property, you need to also enable header printing using Template.Chrome.DisplayHeaderFooter and also make a space for the header using margins.
Here is how the controller action can look like
[MiddlewareFilter(typeof(JsReportPipeline))]
public async Task<IActionResult> InvoiceWithHeader()
{
var header = await JsReportMVCService.RenderViewToStringAsync(HttpContext, RouteData, "Header", new { });
HttpContext.JsReportFeature()
.Recipe(Recipe.ChromePdf)
.Configure((r) => r.Template.Chrome = new Chrome {
HeaderTemplate = header,
DisplayHeaderFooter = true,
MarginTop = "1cm",
MarginLeft = "1cm",
MarginBottom = "1cm",
MarginRight = "1cm"
});
return View("Invoice", InvoiceModel.Example());
}
And how the Header view
<div style='text-align:center; font-size: 10px; width:100%'>Page number <span class="pageNumber"></span> of <span class="totalPages"></span></div>
You can find an example repository here
https://github.com/jsreport/jsreport-dotnet-example-webapp

Related

Aligned text of the table cells

I would like to know how can I make my text be aligned and centered the same way in every cell. Because if you see there is a slight difference between the first cell who got a link just under . I would my title to be on the same line without disturbing the responsive side and also the "same size cells" side (thanks to the table layout)
$(document).ready(function(){
$(".toggler").click(function(e){
e.preventDefault();
$('.cat'+$(this).attr('data-prod-cat')).toggle();
});
});
td{
display:block;
width:auto;
border:1px dotted red;
background-color:red;
color:white;
margin-bottom:10px;
}
#media only screen and (min-width: 70em) {
td{
display:table-cell;
border:1px dotted red;
background-color:red;
color:white;
margin-bottom:0px;
}
}
p{font-family:'Varela Round';font-weight:bold;text-align:center;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<table style="table-layout: fixed; width:100%" width="100%" cellpadding="0" cellspacing="5">
<tbody>
<tr>
<td><table>
<tr><td><p>SOCIÉTÉS: 230</p></td></tr><tr><td>+ En savoir plus</td></tr>
<tr class="cat1" style="display:none">
<td>Part CAC 40 : 90</td></tr>
<tr class="cat1" style="display:none">
<td>Part Filiales +100MK€: 120</td></tr>
</tr>
</table>
</td>
<td><p>CONTACT</p></td>
<td><p>EMAIL NOMINATIF</p></td>
<td><p>OPT OUT</p></td>
<td><p>LIGNES DIRECTES/MOBILES</p></td>
</tr>
</tbody>
</table>
What you can do is just the element td to the css to have the text-align property as well:
CSS:
p, td{
font-family:'Varela Round';
font-weight:bold;
text-align:center;
}
Also just a side note you for your js if you want to run your js code when the DOM is loaded jquery docs does not suggest you that document ready function instead they suggest:
JS
$(function() {
$(".toggler").click(function(e){
e.preventDefault();
$('.cat'+$(this).attr('data-prod-cat')).toggle();
});
});
Jquery Docs: https://api.jquery.com/ready/

Vaadin with JQuery FileUpload

I would like to create FileUploader with Vaadin . But I need to get more features over normal Vaadin Upload.
beautiful and easy to manage (but optional)
fast and never failed while uploading
include progress bar
show preview
multi file upload support
upload file size and type restriction
drag and drop
client-side image resizable (it is main feature for me because all of my uploaded files were images)
There has an addon MultiFileUpload. Yes , it is perfect for most of my requirements but not for client-size image resizing. So I decided to use JQuery FileUpload because it is support for Client side Image Resizing.
I used vaadin Window for upload image. But I got a problem while creating my window , very hard to create each HTML elements respectively (may be I have less exp). So I used CustomLayout with HTML for easy to create and edit design of my image uploader window.
Below is my custom layout HTML file. (two scripts were templates for image preview)
<script id="template-upload" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-upload">
<td width="100px" align="center">
<span class="preview"></span>
</td>
<td width="400px" align="center">
<p class="name">{%=file.name%}</p>
{% if (!o.files.error) { %}
<div class="progress progress-success progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="bar" style="width:0%;"></div></div>
{% } %}
{% if (file.error) { %}
<div><span class="label label-important">Error</span> {%=file.error%}</div>
{% } %}
</td>
<td width="100px" align="center">
{% if (!i) { %}
<button style="display: none;" class="start" type="button">
<span>Start</span>
</button>
<div class="v-button v-widget cancel" type = "button">
<span class="v-button-wrap" style="color: red;">
<span class="v-button-caption">Cancel</span>
</span>
</div>
{% } %}
<br>
{%=o.formatFileSize(file.size)%}
</td>
</tr>
{% } %}
</script>
<!-- The template to display files available for download -->
<script id="template-download" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-download">
<td width="100px" align="center">
<span class="preview">
{% if (file.path) { %}
<img src="../{%=file.path%}" width="100px">
{% } %}
</span>
</td>
<td width="400px" align="center">
<p class="name">
{%=file.name%}
</p>
{% if (file.error) { %}
<div><span class="label label-important">Error</span> {%=file.error%}</div>
{% } %}
</td>
<td width="100px" align="center">
<span class="size">{%=o.formatFileSize(file.size)%}</span>
</td>
</tr>
{% } %}
</script>
<table cellpadding="5" style="width: 100%;">
<colgroup>
<col>
</colgroup>
<tbody>
<tr>
<td width="90px">
<div style="text-align: right; width: 120px;">UploadPhoto :</div>
</td>
<td>
<div id="pnlProgress" aria-valuenow="0" aria-valuemax="100" aria-valuemin="0" style="display: none;" class="progress progressall progress-success progress-striped active">
<div style="width: 0%;" class="allbar" id="pnlProgressBar"> </div>
</div>
</td>
</tr>
<tr>
<td colspan="3">
<div id="imageForm" style="width: 600px;">
<form id="fileupload">
<div style="margin-bottom: 10px; border: 1px solid #DDD; width: 600px; height: 300px; overflow: scroll">
<table cellspacing="0" cellpadding="5">
<tbody class="files"></tbody>
</table>
</div>
<div style="margin-bottom: 10px;" class="fileupload-buttonbar">
<div class="v-button v-widget btnPlus">
<span class="v-button-caption">Add Files</span>
<input type="file" multiple="" name="files[]">
</div>
<div class="v-button v-widget start" type = "submit">
<span class="v-button-wrap">
<span class="v-button-caption">StartUpload</span>
</span>
</div>
<div class="v-button v-widget cancel" type = "reset">
<span class="v-button-wrap">
<span class="v-button-caption">Cancel All</span>
</span>
</div>
</div>
<div style="border: 1px solid #999; width: 600px; height: 100px;" id="dropZone">
<div class="carPhotoDropMsg">
Draft & Drop Photos<br>(jpg, jpeg, png, gif only)
</div>
</div>
</form>
</div>
</td>
</tr>
</tbody>
Below is for ImageUpload window
public final class ImageUploadDialog extends CustomComponent {
private Window window;
public void show() {
UI.getCurrent().addWindow(window);
// 123 is seq for save in database or other use
Page.getCurrent().getJavaScript().execute("initImageuploader(123)");
}
public ImageUploadDialog() {
CustomLayout layout = new CustomLayout("imageUploadLayout");
window = new Window("Uploading Photos");
window.center();
window.setWidth("615px");
window.setModal(true);
window.setResizable(false);
window.setClosable(true);
window.setContent(layout);
}
}
And below is my upload.js file for initialize my image uploader
function initImageuploader(seq) {
$('#fileupload').fileupload({
url : 'photo/upload.html?s=' + seq,
sequentialUploads : true,
disableImageResize : false,
imageMaxWidth : 1024,
imageMaxHeight : 1024,
previewCrop : true,
dropZone : $("#dropZone"),
acceptFileTypes : /(\.|\/)(gif|jpe?g|png)$/i,
progress : function(e, data) {
if (data.context) {
var progress = data.loaded / data.total * 100;
progress = Math.floor(progress);
$('.progress').attr('aria-valuenow', progress);
$('.progress').css('display', 'block');
$('.bar').css('width', progress + '%');
}
},
progressall : function(e, data) {
var progress = data.loaded / data.total * 100;
progress = Math.floor(progress);
$('.progressall').attr('aria-valuenow', progress);
$('.progressall').css('display', 'block');
$('.allbar').css('width', progress + '%');
if (progress > 20) {
$('.allbar').text(progress + '% Completed');
}
},
stop: function (e) {
return;
}
});
}
And you need additional javascripts files for image uploader and I imported them at my UI class as below
#JavaScript({ "vaadin://themes/myproject/js/load-image.min.js",
"vaadin://themes/myproject/js/tmpl.min.js",
"vaadin://themes/myproject/js/jquery/jquery-1.10.1.min.js",
"vaadin://themes/myproject/js/jquery/vendor/jquery.ui.widget.js",
"vaadin://themes/myproject/js/jquery/jquery.iframe-transport.js",
"vaadin://themes/myproject/js/jquery/jquery.fileupload.js",
"vaadin://themes/myproject/js/jquery/jquery.fileupload-ui.js",
"vaadin://themes/myproject/js/jquery/jquery.fileupload-process.js",
"vaadin://themes/myproject/js/jquery/jquery.fileupload-image.js",
"vaadin://themes/myproject/js/jquery/jquery.fileupload-validate.js",
"vaadin://themes/myproject/js/canvas-to-blob.min.js",
"vaadin://themes/myproject/js/upload.js" })
#StyleSheet({ "vaadin://themes/myproject/css/jquery-ui-1.10.3.custom.min.css",
"vaadin://themes/myproject/css/imageUpload.css" })
public class EntryPoint extends UI {
..............
}
Please Notice for JS Files Order !
Below is my Custom CSS file for image upload window (imageUpload.css)
table.upld-status {
display: none;
}
.fileupload-buttonbar .btnPlus {
float: left;
position: relative;
overflow: hidden;
color: blue;
text-align: center;
margin-right : 10px;
}
.fileupload-buttonbar .btnPlus input {
margin: 0px;
position: absolute;
top: 0px;
right: 0px;
line-height: 30px;
font-size: 23px;
direction: ltr;
opacity: 0;
}
.carPhotoDropMsg {
color: #DDD;
font-size: 20pt;
height: 82%;
padding: 9px;
text-align: center;
}
.progress {
background-color: #F7F7F7;
background-image: linear-gradient(to bottom, #F5F5F5, #F9F9F9);
background-repeat: repeat-x;
border-radius: 4px 4px 4px 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset;
height: 17px;
overflow: hidden;
}
.progress-success.progress-striped .bar, .progress-success.progress-striped .allbar, .progress
striped .bar-success {
background-color: #62C462;
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.progress.active .bar, .progress.active .allbar {
animation: 2s linear 0s normal none infinite progress-bar-stripes;
}
.progress-success .bar, .progress-success .allbar, .progress .bar-success {
background-color: #5EB95E;
background-image: linear-gradient(to bottom, #62C462, #57A957);
background-repeat: repeat-x;
}
.progress-striped .bar, .progress-striped .allbar {
background-color: #149BDF;
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-size: 40px 40px;
}
.progress .bar, .progress .allbar {
-moz-box-sizing: border-box;
background-color: #0E90D2;
background-image: linear-gradient(to bottom, #149BDF, #0480BE);
background-repeat: repeat-x;
box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.15) inset;
color: #FFFFFF;
float: left;
font-size: 12px;
height: 100%;
text-align: center;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
transition: width 0.4s ease 0s;
width: 0;
}
I need server-side control for save image . You need two jars apache-common-io and apache-common-fileupload. Below is for maven repository of these two jars.
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
Finally , below is codes for server-side control .
#WebServlet(value = "/photo/upload.html")
public class UploadServletController extends HttpServlet {
protected final void doPost(final HttpServletRequest request,
final HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
PrintWriter out = response.getWriter();
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> fields = null;
try {
fields = upload.parseRequest(request);
}
catch (FileUploadException e) {
throw new RuntimeException("Error Parsing File Item " + e.getMessage(), e);
}
if (fields != null) {
String message = uploadPhoto(request, fields);
out.write(message);
}
}
public final synchronized String uploadPhoto(final HttpServletRequest request,
final List<FileItem> sessionFiles) {
List<Map<String, Object>> ret = new ArrayList<Map<String, Object>>();
for (FileItem item : sessionFiles) {
if (!item.isFormField()) {
Long seq = Long.parseLong(request.getParameter("s"));
// get from vm arguments (eg:-DstaticDir=/Applications/springsource/workspace/myproject/src/main/webapp)
String staticDir = System.getProperty("staticDir");
Date today = new Date();
SimpleDateFormat fmtYMD = new SimpleDateFormat("/yyyyMMdd/HH");
SimpleDateFormat fmtHMS = new SimpleDateFormat("HHmmssS");
String saveDir = "data/photo" + fmtYMD.format(today);
String format = ".jpg";
try {
format = item.getName().substring(item.getName().lastIndexOf("."), item.getName().length())
.toLowerCase();
}
catch (Exception e) {
// nothing to do!
}
String fileName = seq + "_" + fmtHMS.format(today) + format;
Map<String, Object> res = new HashMap<String, Object>();
// Save image in specify location
String filePath = staticDir + "/" + saveDir;
saveFile(filePath, fileName, item);
res.put("seq", seq);
res.put("path", saveDir + "/" + fileName);
res.put("ext", format.substring(1));
res.put("name", item.getName());
res.put("size", item.getSize());
ret.add(res);
}
}
Map<String, Object> result = new HashMap<String, Object>();
result.put("files", ret);
JSONObject obj = new JSONObject(result);
return obj.toString();
}
public static String saveFile(final String filePath, final String fileName, final FileItem item) {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
File imageFile = new File(file, fileName);
try {
item.write(imageFile);
}
catch (Exception e) {
e.printStackTrace();
}
item.setFieldName(filePath + fileName);
return item.toString();
}
}
I know my codes may have risks and some weakpoints . Every suggestions were welcome . But I believe there has some useful for newbie (I am also a newbie). Sorry for too long and bad format.
The last thing is my problem ....
Why preview image (after upload not before upload) automatically include url instead of filepath ? I got image not found error
"NetworkError: 404 Not Found - http://localhost:8080/myproject/VAADIN/themes/myTheme/data/photo/20140723/23/123_235918346.jpg"
Actually this image path should be data/photo/20140723/23/111_235918346.jpg. I have no idea why prefix url http://localhost:8080/myproject/VAADIN/themes/myTheme/ was automatically include (may be due to my CustomLayout HTML file path) ? File paths were got from HTTP response (with JSON). I think it is due to VAADIN because it works on my GWT project or may be I am wrong. Any suggestions ? Thanks for reading my question.
I fixed it by repairing src value of preview template for after upload image as ...
<script id="template-download" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) {; %}
<tr class="template-download">
<td width="100px" align="center">
<span class="preview">
{% if (file.path) { %}
<img src="/myproject/{%=file.path%}" width="100px">
{% } %}
</span>
</td>
<td width="400px" align="center">
<p class="name">
{%=file.name%}
</p>
{% if (file.error) { %}
<div><span class="label label-important">Error</span> {%=file.error%}</div>
{% } %}
</td>
<td width="100px" align="center">
<span class="size">{%=o.formatFileSize(file.size)%}</span>
</td>
</tr>
{% } %}
</script>
Now everythings were fine. If you didn't see image immidiately , please check your IDE (Eclipse or STS) setting as below
Preference > General > Workspace
and check checkboxes Refresh on access and Refresh using native hooks or polling.

RenderAction Partial View is Caching on Back Button Asp.Net MVC

I have a div with a Partial View on it which i render using RenderAction().
I am using Asp.Net MVC 4.0.
My problem is and it happens only on IE(version 9) , when i go to some other page and click the back button on IE, the partial view is not rendered. The partial View section is blank. Even on Firefox, the Action is not called, but at least the Partial View is rendered (its not blank)
Is there any solution to this ?
UPDATE
#foreach (var item in Model)
{
<article style="margin-top: 10px;">
<h5>
#item.Title
</h5>
<span >
<time>
#item.FromDateMonth
</time>-
<time>
#item.ToDateMonth
</time>
</span>
<div style="margin-top:10px;">
#Html.Label("Notes:", new { #style = "color: #000000; font-size: 13px; font-weight: bold; line-height: 16px;" })
<span style="color: #000000; font-size: 13px; font-weight: normal; line-height: 16px;">
#item.Notes
</span>
</div>
</article>

Rails: How do I autocomplete using the Google Places DB?

My application has an address field, which I want an autocomplete (updates as user types, displays below the entry field) for using the Google Places Autocomplete API. Now, I've seen gems which autocomplete words based on fields in a model, which wont work for me, since I can't store all the addresses on google places locally.
I've also tried a google_places_autocomplete gem which returns a list of suggestions when given an input string to autocomplete, but I don't know how to dynamically update the list as the input changes and present it in a nice format.
Can someone please give me an overview of which gems I should use and how I should go about doing this? I can display autocomplete suggestions using predefined addresses with the google_places_autcomplete gem which should mean I don't need to manually parse JSON.
you can do it directly on the view via Google Places javascript library.
Check out this example:
<!DOCTYPE html>
<html>
<head>
<title>Place Autocomplete Address Form</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
</style>
<link type="text/css" rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=places"></script>
<script>
// This example displays an address form, using the autocomplete feature
// of the Google Places API to help users fill in the information.
var placeSearch, autocomplete;
var componentForm = {
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
function initialize() {
// Create the autocomplete object, restricting the search
// to geographical location types.
autocomplete = new google.maps.places.Autocomplete(
/** #type {HTMLInputElement} */(document.getElementById('autocomplete')),
{ types: ['geocode'] });
// When the user selects an address from the dropdown,
// populate the address fields in the form.
google.maps.event.addListener(autocomplete, 'place_changed', function() {
fillInAddress();
});
}
// [START region_fillform]
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
// [END region_fillform]
// [START region_geolocation]
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = new google.maps.LatLng(
position.coords.latitude, position.coords.longitude);
autocomplete.setBounds(new google.maps.LatLngBounds(geolocation,
geolocation));
});
}
}
// [END region_geolocation]
</script>
<style>
#locationField, #controls {
position: relative;
width: 480px;
}
#autocomplete {
position: absolute;
top: 0px;
left: 0px;
width: 99%;
}
.label {
text-align: right;
font-weight: bold;
width: 100px;
color: #303030;
}
#address {
border: 1px solid #000090;
background-color: #f0f0ff;
width: 480px;
padding-right: 2px;
}
#address td {
font-size: 10pt;
}
.field {
width: 99%;
}
.slimField {
width: 80px;
}
.wideField {
width: 200px;
}
#locationField {
height: 20px;
margin-bottom: 2px;
}
</style>
</head>
<body onload="initialize()">
<div id="locationField">
<input id="autocomplete" placeholder="Enter your address"
onFocus="geolocate()" type="text"></input>
</div>
<table id="address">
<tr>
<td class="label">Street address</td>
<td class="slimField"><input class="field" id="street_number"
disabled="true"></input></td>
<td class="wideField" colspan="2"><input class="field" id="route"
disabled="true"></input></td>
</tr>
<tr>
<td class="label">City</td>
<td class="wideField" colspan="3"><input class="field" id="locality"
disabled="true"></input></td>
</tr>
<tr>
<td class="label">State</td>
<td class="slimField"><input class="field"
id="administrative_area_level_1" disabled="true"></input></td>
<td class="label">Zip code</td>
<td class="wideField"><input class="field" id="postal_code"
disabled="true"></input></td>
</tr>
<tr>
<td class="label">Country</td>
<td class="wideField" colspan="3"><input class="field"
id="country" disabled="true"></input></td>
</tr>
</table>
</body>
</html>
Source, with other examples:
https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete-addressform

Absolute position relative which parent in tables? (IE6 vs Firefox/Chrome)

I have a piece of code that do as I want in IE6 but not in Chrome/Firefox:
In IE6, the img is displayed with absolute position relative the td, as I wanted/expected. In Firefix/Chrome the img is displayed relative the outer div.
<div>
<table>
<tr>
<td class="rel cell">
<img src="style/easypos_mobile/icons/pencil.png" class="icon" onclick="_onclick.newArticle_andraNr();"/>
</td>
</tr>
</table>
</div>
.rel
{
position: relative;
}
.icon
{
position: absolute;
top: 3px;
right: -23px;
}
.cell
{
width: 186px;
}
Found this stuff:
The specs leave it open to the
User-Agent to decide if a table-cell
can act as a container for absolute
positioned objects.
http://www.w3.org/TR/CSS21/visuren.html#propdef-position
(note the 'effect of
'position:relative' on
table-row-group, table- header-group,
table-footer-group, table-row,
table-column-group, table-column,
table-cell, and table-caption elements
is undefined').
This fixed it:
<table><tr>
<td style="position: relative; width: 180px;">
<div style="position:relative;width:100%;height:100%;">
<img src="imageA.gif" class="status">
<img src="imageB.gif" class="status">
</div>
</td>
</tr></table>