<!--
  ~ Copyright (C) 2024 Xibo Signage Ltd
  ~
  ~ Xibo - Digital Signage - https://xibosignage.com
  ~
  ~ This file is part of Xibo.
  ~
  ~ Xibo is free software: you can redistribute it and/or modify
  ~ it under the terms of the GNU Affero General Public License as published by
  ~ the Free Software Foundation, either version 3 of the License, or
  ~ any later version.
  ~
  ~ Xibo is distributed in the hope that it will be useful,
  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  ~ GNU Affero General Public License for more details.
  ~
  ~ You should have received a copy of the GNU Affero General Public License
  ~ along with Xibo.  If not, see <http://www.gnu.org/licenses/>.
  -->
<templates>
    <template>
        <id>dataset_table_custom_html</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <showIn>none</showIn>
        <properties>
            <property id="styleSheet" type="code" allowLibraryRefs="true" variant="css">
                <title>Optional Stylesheet Template</title>
            </property>
            <property id="javaScript" type="code" allowLibraryRefs="true" variant="javascript">
                <title>Optional JavaScript</title>
                <helpText>Add JavaScript to be included immediately before the closing body tag. Do not use [] array notation as this is reserved for library references. Do not include script tags.</helpText>
            </property>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="columns" type="datasetColumnSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showHeadings" type="checkbox">
                <title>Show the table headings?</title>
                <helpText>Should the Table headings be shown?</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="rowsPerPage" type="number">
                <title>Rows per page</title>
                <helpText>Please enter the number of rows per page. 0 for no pages.</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontFamily" type="fontSelector">
                <title>Font</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontSize" type="number">
                <title>Font Size</title>
                <helpText>Set the font size</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="header" variant="main">
                <title>Colours</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
                <property id="backgroundColor" type="color">
                    <title>Background Colour</title>
                    <helpText>Use the colour picker to select the background colour</helpText>
                    <visibility>
                        <test>
                            <condition field="dataSetId" type="neq"></condition>
                        </test>
                    </visibility>
                </property>
                <property id="borderColor" type="color">
                    <title>Border Colour</title>
                    <helpText>Use the colour picker to select the border colour</helpText>
                    <visibility>
                        <test>
                            <condition field="dataSetId" type="neq"></condition>
                        </test>
                    </visibility>
                </property>
                <property id="fontColor" type="color">
                    <title>Font Colour</title>
                    <helpText>Use the colour picker to select the font colour</helpText>
                    <visibility>
                        <test>
                            <condition field="dataSetId" type="neq"></condition>
                        </test>
                    </visibility>
                </property>
            </properties>
            <stencil>
            <twig><![CDATA[
{% if javaScript %}<script type="text/javascript">{{javaScript|raw}}</script>{% endif %}

<div id="DataSetTableContainer"></div>
            ]]></twig>
            <style><![CDATA[
{{styleSheet|raw}}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Columns to be shown in the table
var columnIndex = JSON.parse(properties.columns);
var columns = [];
if (meta && meta.mapping) {
    meta.mapping.forEach((column) => {
        if (column.dataSetColumnId !== '' && columnIndex.indexOf(column.dataSetColumnId) !== -1) {
            columns[columnIndex.indexOf(column.dataSetColumnId)] = column;
        }
    });
}

// Get table element
var $datasetTableContainer = $(target).find('#DataSetTableContainer');

// Clear the table container
$datasetTableContainer.empty();

// Calculate number of pages
var totalPages = (properties.rowsPerPage > 0) ? Math.ceil(items.length / properties.rowsPerPage) : 1;

// Set the number of pages to the table
$datasetTableContainer.data('totalPages', totalPages);

// Create a table for each page
for (var i = 0; i < totalPages; i++) {
    // Create a new table
    var $newTable = properties.rowsPerPage > 0 ?
        $('<table class="DataSetTable" data-page="' + i + '">') :
        $('<table class="DataSetTable">');

    // Show the table headings if required
    if (properties.showHeadings == 1) {
        // Build the headings
        var headings = columns.map(function (column, colIdx) {
            return '<th class="DataSetColumnHeaderCell">' + column.heading + '</th>';
        });

        // Add the headings to the table
        $newTable.append(
            $('<thead>')
                .append(
                    $('<tr class="HeaderRow">')
                        .append(headings)
                )
        );
    }

    // Append table body
    $newTable.append(
        $('<tbody>')
    );

    // Add the table to the container
    $datasetTableContainer.append($newTable);
}

// Add rows to the tables per page
for (var i = 0; i < items.length; i++) {
    // Get the table for this row
    var $table = (properties.rowsPerPage > 0) ?
        $datasetTableContainer.find('.DataSetTable[data-page="' + Math.floor(i / properties.rowsPerPage) + '"]') :
        $datasetTableContainer.find('.DataSetTable');

    // Build the row content based on the columns
    var rowContent = columns.map(function (column, colIdx) {
        var value = items[i][column.heading];

        // If it's a date and we have date format
        if (column.dataTypeId === 3 && properties.dateFormat) {
            value = moment(value).format(properties.dateFormat);
        }

        // If this is an image column, wrap it in an image tag
        if (column.dataTypeId === 4 || column.dataTypeId === 5) {
            value = value ? '<img src="' + value + '" />' : '';
        }

        // Empty string if value is null
        if (value === null) {
            value = '';
        }

        return '<td class="DataSetColumn DataSetColumn_' + colIdx + '" id="column_' + (colIdx + 1) + '"><span class="DataSetCellSpan DataSetCellSpan_' + i + '_' + colIdx + '" id="span_' + i + '_' + (colIdx + 1) + '">' + value + '</span></td>';
    }).join('');

    // Add the row to the table's body
    $table.find('tbody').append(
        '<tr class="DataSetRow DataSetRow' + ((i % 2) ? 'Odd' : 'Even') + '" id="row_' + i + '">' +
        rowContent +
        '</tr>'
    );
}

// Move table container into content
$datasetTableContainer.appendTo($(target).find('#content'));

// Scale the layout
$(target).xiboLayoutScaler(properties);

// Call render
$datasetTableContainer.dataSetRender(properties);

// Image render
$datasetTableContainer.find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <assets>
            <asset id="empty" type="path" mimeType="image/png" cmsOnly="true" path="/modules/assets/template-thumbnails/dataset/0-empty.png" />
        </assets>
    </template>
    <template>
        <id>dataset_custom_html</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <showIn>none</showIn>
        <title>Dataset Custom HTML</title>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="template" type="code" allowLibraryRefs="true" variant="html">
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dataSetSnippets" type="snippet" mode="dataSet" target="template">
                <title>Snippets</title>
                <helpText>Choose data set snippet</helpText>
                <dependsOn>dataSetId</dependsOn>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="styleSheet" type="code" allowLibraryRefs="true" variant="css">
                <title>Optional Stylesheet Template</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="javaScript" type="code" allowLibraryRefs="true" variant="javascript">
                <title>Optional JavaScript</title>
                <helpText>Add JavaScript to be included immediately before the closing body tag. Do not use [] array notation as this is reserved for library references. Do not include script tags.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="itemsSideBySide" type="checkbox">
                <title>Show items side by side?</title>
                <helpText>Should items be shown side by side?</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="backgroundColor" type="color">
                <title>Background Colour</title>
                <helpText>The selected effect works best with a background colour. Optionally add one here.</helpText>
                <default></default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="effect" type="effectSelector" variant="all">
                <title>Effect</title>
                <helpText>Please select the effect that will be used to transition between items.</helpText>
                <default>noTransition</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="speed" type="number">
                <title>Speed</title>
                <helpText>The transition speed of the selected effect in milliseconds (normal = 1000) or the Marquee Speed in a low to high scale (normal = 1)</helpText>
                <default>1000</default>
                <visibility>
                    <test type="and">
                        <condition field="effect" type="neq">none</condition>
                        <condition field="effect" type="neq">noTransition</condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="itemsPerPage" type="number">
                <title>Items per page</title>
                <helpText>If an effect has been selected, how many pages should we split the items across? If you don't enter anything here 1 item will be put on each page.</helpText>
                <default>1</default>
                <visibility>
                    <test type="and">
                        <condition field="effect" type="neq">none</condition>
                        <condition field="effect" type="neq">marqueeLeft</condition>
                        <condition field="effect" type="neq">marqueeRight</condition>
                        <condition field="effect" type="neq">marqueeUp</condition>
                        <condition field="effect" type="neq">marqueeDown</condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <preview></preview>
        <stencil>
            <twig><![CDATA[
{% if javaScript %}
<script type="text/javascript">
    {{javaScript|raw}}
</script>
{% endif %}
        ]]></twig>
            <style><![CDATA[
{% if itemsSideBySide %}
.item, .page {
    float: left;
}
{% endif %}

{% if backgroundColor != '' %}
body {
    background-color: {{ backgroundColor }} !important;
}
{% endif %}

{{styleSheet|raw}}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Module renderer options
// id: The id of the widget
// target: The target element to render
// items: The items to render
// properties: The properties for the widget

// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Clear the content container
$(target).find('#content').empty();

// If we don't have metadata, we can't render anything
if (!meta || !meta.mapping) {
    return;
}

// Get dataset columns from meta.mapping
var columnTypes = {};
for (var i = 0; i < meta.mapping.length; i++) {
    var column = meta.mapping[i];
    columnTypes[column.dataSetColumnId] = {
        type: column.dataTypeId
    };
}

// Add content for each item
// make replacements
// and wrap it in a div with the item class
for (var i = 0; i < items.length; i++) {
    var item = items[i];

    // Replace the template with the item content
    var content = properties.template.replace(/\[(.*?)\]/g, function (match, column) {
        var itemId = column.split('|')[0];
        var itemCol = column.split('|')[1];
        var itemType = (itemCol) ? columnTypes[itemCol].type : '';
        var itemValue = item[itemId];

        // If this is an image column, wrap it in an image tag
        if (itemType === 4 || itemType === 5) {
            itemValue = itemValue ? '<img src="' + itemValue + '" />' : '';
        }

        return itemValue ? itemValue : '';
    });

    // Add the content to the target
    $(target).find('#content').append(
        $('<div>')
            .addClass('item')
            .append(content)
    );
}

// Scale the layout
$('body').xiboLayoutScaler(properties);

$(target).xiboTextRender(Object.assign(properties, globalOptions), $(target).find('#content > *'));

// Image render
$(target).find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <onTemplateVisible><![CDATA[
// Start effects for this template
$(target).xiboLayoutAnimate(properties);
        ]]></onTemplateVisible>
    </template>
    <template>
        <id>dataset_table_1</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <title>Plain Table (Customisable)</title>
        <thumbnail>empty</thumbnail>
        <startWidth>600</startWidth>
        <startHeight>350</startHeight>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="columns" type="datasetColumnSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showHeadings" type="checkbox">
                <title>Show the table headings?</title>
                <helpText>Should the Table headings be shown?</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="rowsPerPage" type="number">
                <title>Rows per page</title>
                <helpText>Please enter the number of rows per page. 0 for no pages.</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontFamily" type="fontSelector">
                <title>Font</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontSize" type="number">
                <title>Font Size</title>
                <helpText>Set the font size</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>

            <property type="header" variant="main">
                <title>Colours</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
                <property id="fontColor" type="color">
                    <title>Font Colour</title>
                    <helpText>Use the colour picker to select the font colour</helpText>
                    <default>#111</default>
                    <visibility>
                        <test>
                            <condition field="dataSetId" type="neq"></condition>
                        </test>
                    </visibility>
                </property>
                <property id="backgroundColor" type="color">
                    <title>Background Colour</title>
                    <helpText>Use the colour picker to select the background colour</helpText>
                    <visibility>
                        <test>
                            <condition field="dataSetId" type="neq"></condition>
                        </test>
                    </visibility>
                </property>
                <property id="borderColor" type="color">
                    <title>Border Colour</title>
                    <helpText>Use the colour picker to select the border colour</helpText>
                    <visibility>
                        <test>
                            <condition field="dataSetId" type="neq"></condition>
                        </test>
                    </visibility>
                </property>
                <property id="headerFontColor" type="color">
                    <title>Header Font Colour</title>
                    <helpText>Use the colour picker to select the header font colour</helpText>
                    <default>#111</default>
                    <visibility>
                        <test>
                            <condition field="dataSetId" type="neq"></condition>
                        </test>
                    </visibility>
                </property>
                <property id="headerBackgroundColor" type="color">
                    <title>Header Background Colour</title>
                    <helpText>Use the colour picker to select the header background colour</helpText>
                    <visibility>
                        <test>
                            <condition field="dataSetId" type="neq"></condition>
                        </test>
                    </visibility>
                </property>
            </properties>
            <stencil>
            <twig><![CDATA[
<div class="DataSetTableContainer"></div>
            ]]></twig>
            <style><![CDATA[
/* Template Styles */
table.DataSetTable {
    width: 100%;
    font-size: 14px;
    table-layout: fixed;
    border-collapse: collapse;
}

td.DataSetColumn img {
    height: 80px;
}

/* Constructed Styles */
{% if backgroundColor %}
table.DataSetTable {
    background-color: {{backgroundColor}};
}
{% endif %}

{% if borderColor %}
table.DataSetTable, table.DataSetTable tr, table.DataSetTable th, table.DataSetTable td {
    border: 1px solid {{borderColor}};
}
{% endif %}

{% if fontColor %}
table.DataSetTable {
    color: {{fontColor}};
}
{% endif %}

{% if headerFontColor %}
table.DataSetTable .HeaderRow {
    color: {{headerFontColor}};
}
{% endif %}

{% if headerBackgroundColor %}
table.DataSetTable .HeaderRow {
    background-color: {{headerBackgroundColor}};
}
{% endif %}

{% if fontFamily %}
table.DataSetTable {
    font-family: {{fontFamily}};
}
{% endif %}

{% if fontSize %}
table.DataSetTable {
    font-size: {{fontSize}}px;
}
{% endif %}

/* Table display CSS fix */
table.DataSetTable.cycle-slide {
    display: table !important;
}

/* If we are going to cycle between pages, make sure we hide all of the tables initially */
{% if rowsPerPage > 0 %}
table.DataSetTable {visibility:hidden;}
{% endif %}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Columns to be shown in the table
var columnIndex = JSON.parse(properties.columns);
var columns = [];
if (meta && meta.mapping) {
    meta.mapping.forEach((column) => {
        if (column.dataSetColumnId !== '' && columnIndex.indexOf(column.dataSetColumnId) !== -1) {
            columns[columnIndex.indexOf(column.dataSetColumnId)] = column;
        }
    });
}

// Get table element
var $datasetTableContainer = $(target).find('.DataSetTableContainer');

// Clear the table container
$datasetTableContainer.empty();

// Calculate number of pages
var totalPages = (properties.rowsPerPage > 0) ? Math.ceil(items.length / properties.rowsPerPage) : 1;

// Set the number of pages to the table
$datasetTableContainer.data('totalPages', totalPages);

// Create a table for each page
for (var i = 0; i < totalPages; i++) {
    // Create a new table
    var $newTable = properties.rowsPerPage > 0 ?
        $('<table class="DataSetTable" data-page="' + i + '">') :
        $('<table class="DataSetTable">');

    // Show the table headings if required
    if (properties.showHeadings === 1) {
        // Build the headings
        var headings = columns.map(function (column, colIdx) {
            return '<th class="DataSetColumnHeaderCell">' + column.heading + '</th>';
        });

        // Add the headings to the table
        $newTable.append(
            $('<thead>')
                .append(
                    $('<tr class="HeaderRow">')
                        .append(headings)
                )
        );
    }

    // Append table body
    $newTable.append(
        $('<tbody>')
    );

    // Add the table to the container
    $datasetTableContainer.append($newTable);
}

// Add rows to the tables per page
for (var i = 0; i < items.length; i++) {
    // Get the table for this row
    var $table = (properties.rowsPerPage > 0) ?
        $datasetTableContainer.find('.DataSetTable[data-page="' + Math.floor(i / properties.rowsPerPage) + '"]') :
        $datasetTableContainer.find('.DataSetTable');

    // Build the row content based on the columns
    var rowContent = columns.map(function (column, colIdx) {
        var value = items[i][column.heading];

        // If it's a date and we have date format
        if (column.dataTypeId === 3 && properties.dateFormat) {
            value = moment(value).format(properties.dateFormat);
        }

        // If this is an image column, wrap it in an image tag
        if (column.dataTypeId === 4 || column.dataTypeId === 5) {
            value = value ? '<img src="' + value + '" />' : '';
        }

        // Empty string if value is null
        if (value === null) {
            value = '';
        }

        return '<td class="DataSetColumn DataSetColumn_' + colIdx + '" id="column_' + (colIdx + 1) + '"><span class="DataSetCellSpan DataSetCellSpan_' + i + '_' + colIdx + '" id="span_' + i + '_' + (colIdx + 1) + '">' + value + '</span></td>';
    }).join('');

    // Add the row to the table's body
    $table.find('tbody').append(
        '<tr class="DataSetRow DataSetRow' + ((i % 2) ? 'Odd' : 'Even') + '" id="row_' + i + '">' +
        rowContent +
        '</tr>'
    );
}

// Move table container into content
$datasetTableContainer.appendTo($(target).find('#content'));

// Scale the layout
$(target).xiboLayoutScaler(properties);

// Call render
$datasetTableContainer.dataSetRender(properties);

// Image render
$datasetTableContainer.find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <assets>
            <asset id="empty" type="path" mimeType="image/png" cmsOnly="true" path="/modules/assets/template-thumbnails/dataset/0-empty.png" />
        </assets>
    </template>
    <template>
        <id>dataset_table_2</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <title>A light green background with darker green borders. White heading text.</title>
        <thumbnail>light-green</thumbnail>
        <startWidth>600</startWidth>
        <startHeight>350</startHeight>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="columns" type="datasetColumnSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showHeadings" type="checkbox">
                <title>Show the table headings?</title>
                <helpText>Should the Table headings be shown?</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="rowsPerPage" type="number">
                <title>Rows per page</title>
                <helpText>Please enter the number of rows per page. 0 for no pages.</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontFamily" type="fontSelector">
                <title>Font</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontSize" type="number">
                <title>Font Size</title>
                <helpText>Set the font size</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <stencil>
            <twig><![CDATA[
<div class="DataSetTableContainer"></div>
            ]]></twig>
            <style><![CDATA[
/* Template Styles */
table.DataSetTable {
    font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
    font-size: 14px;
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
}

tr.HeaderRow {
    font-size: 1.1em;
    text-align: center;
    padding-top: 5px;
    padding-bottom: 4px;
    background-color: #A7C942;
    color: #ffffff;
}

tr#row_1 {
    color: #000000;
    background-color: #EAF2D3;
}

td#col_1 {
    color: #000000;
    background-color: #EAF2D3;
}

td.DataSetColumn {
    color: #000000;
    background-color: #EAF2D3;
    border: 1px solid #98bf21
}

td.DataSetColumn img {
    max-width: 100%;
    max-height: 50px;
    display: block;
    margin-left: auto;
    margin-right: auto;
}

tr.DataSetRow {
    text-align: center;
    color: #000000;
    background-color: #EAF2D3;
    border: 1px solid #98bf21 padding-top:5px;
    padding-bottom: 4px;
}

th.DataSetColumnHeaderCell {
    font-size: 1em;
    border: 1px solid #98bf21;
    padding: 3px 7px 2px 7px;
}

/* Constructed Styles */
{% if fontFamily %}
table.DataSetTable {
    font-family: {{fontFamily}};
}
{% endif %}

{% if fontSize %}
table.DataSetTable {
    font-size: {{fontSize}}px;
}
{% endif %}

/* Table display CSS fix */
table.DataSetTable.cycle-slide {
    display: table !important;
}

/* If we are going to cycle between pages, make sure we hide all of the tables initially */
{% if rowsPerPage > 0 %}
table.DataSetTable {visibility:hidden;}
{% endif %}
            ]]></style>   
        </stencil>
        <onTemplateRender><![CDATA[
// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Columns to be shown in the table
var columnIndex = JSON.parse(properties.columns);
var columns = [];
if (meta && meta.mapping) {
    meta.mapping.forEach((column) => {
        if (column.dataSetColumnId !== '' && columnIndex.indexOf(column.dataSetColumnId) !== -1) {
            columns[columnIndex.indexOf(column.dataSetColumnId)] = column;
        }
    });
}

// Get table element
var $datasetTableContainer = $(target).find('.DataSetTableContainer');

// Clear the table container
$datasetTableContainer.empty();

// Calculate number of pages
var totalPages = (properties.rowsPerPage > 0) ? Math.ceil(items.length / properties.rowsPerPage) : 1;

// Set the number of pages to the table
$datasetTableContainer.data('totalPages', totalPages);

// Create a table for each page
for (var i = 0; i < totalPages; i++) {
    // Create a new table
    var $newTable = properties.rowsPerPage > 0 ?
        $('<table class="DataSetTable" data-page="' + i + '">') :
        $('<table class="DataSetTable">');

    // Show the table headings if required
    if (properties.showHeadings === 1) {
        // Build the headings
        var headings = columns.map(function (column, colIdx) {
            return '<th class="DataSetColumnHeaderCell">' + column.heading + '</th>';
        });

        // Add the headings to the table
        $newTable.append(
            $('<thead>')
                .append(
                    $('<tr class="HeaderRow">')
                        .append(headings)
                )
        );
    }

    // Append table body
    $newTable.append(
        $('<tbody>')
    );

    // Add the table to the container
    $datasetTableContainer.append($newTable);
}

// Add rows to the tables per page
for (var i = 0; i < items.length; i++) {
    // Get the table for this row
    var $table = (properties.rowsPerPage > 0) ?
        $datasetTableContainer.find('.DataSetTable[data-page="' + Math.floor(i / properties.rowsPerPage) + '"]') :
        $datasetTableContainer.find('.DataSetTable');

    // Build the row content based on the columns
    var rowContent = columns.map(function (column, colIdx) {
        var value = items[i][column.heading];

        // If it's a date and we have date format
        if (column.dataTypeId === 3 && properties.dateFormat) {
            value = moment(value).format(properties.dateFormat);
        }

        // If this is an image column, wrap it in an image tag
        if (column.dataTypeId === 4 || column.dataTypeId === 5) {
            value = value ? '<img src="' + value + '" />' : '';
        }

        // Empty string if value is null
        if (value === null) {
            value = '';
        }

        return '<td class="DataSetColumn DataSetColumn_' + colIdx + '" id="column_' + (colIdx + 1) + '"><span class="DataSetCellSpan DataSetCellSpan_' + i + '_' + colIdx + '" id="span_' + i + '_' + (colIdx + 1) + '">' + value + '</span></td>';
    }).join('');

    // Add the row to the table's body
    $table.find('tbody').append(
        '<tr class="DataSetRow DataSetRow' + ((i % 2) ? 'Odd' : 'Even') + '" id="row_' + i + '">' +
        rowContent +
        '</tr>'
    );
}

// Move table container into content
$datasetTableContainer.appendTo($(target).find('#content'));

// Scale the layout
$(target).xiboLayoutScaler(properties);

// Call render
$datasetTableContainer.dataSetRender(properties);

// Image render
$datasetTableContainer.find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <assets>
            <asset id="light-green" type="path" mimeType="image/png" cmsOnly="true" path="/modules/assets/template-thumbnails/dataset/1-light-green.png" />
        </assets>
    </template>
    <template>
        <id>dataset_table_3</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <title>Simple white table with rounded rows.</title>
        <thumbnail>simple-round-table</thumbnail>
        <startWidth>600</startWidth>
        <startHeight>350</startHeight>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="columns" type="datasetColumnSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showHeadings" type="checkbox">
                <title>Show the table headings?</title>
                <helpText>Should the Table headings be shown?</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="rowsPerPage" type="number">
                <title>Rows per page</title>
                <helpText>Please enter the number of rows per page. 0 for no pages.</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontFamily" type="fontSelector">
                <title>Font</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontSize" type="number">
                <title>Font Size</title>
                <helpText>Set the font size</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <stencil>
            <twig><![CDATA[
<div class="DataSetTableContainer"></div>
            ]]></twig>
            <style><![CDATA[
/* Template Styles */
table.DataSetTable {
    font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
    width: 100%;
    border-collapse: collapse;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
    table-layout: fixed;
    font-size: 14px;
    color: #3b3b3b;
}

tr.HeaderRow {
    font-size: 1.1em;
    font-weight: 900;
    color: #5e5e5e;
    text-align: left;
}

td.DataSetColumn {
    padding: 8px;
}

td.DataSetColumn:first-child {
    border-radius: 8px 0 0 8px;
}

td.DataSetColumn:last-child {
    border-radius: 0 8px 8px 0;
}

td.DataSetColumn img {
    max-width: 100%;
    max-height: 50px;
    display: block;
    margin-left: auto;
    margin-right: auto;
}

tr.DataSetRow {
    background-color: #f6f6f6;
    border-top: 2px solid #d3d3d3;
}

th.DataSetColumnHeaderCell {
    padding: 8px 8px 0px 8px;
}

/* Constructed Styles */
{% if fontFamily %}
table.DataSetTable {
    font-family: {{fontFamily}};
}
{% endif %}

{% if fontSize %}
table.DataSetTable {
    font-size: {{fontSize}}px;
}
{% endif %}

/* Table display CSS fix */
table.DataSetTable.cycle-slide {
    display: table !important;
}

/* If we are going to cycle between pages, make sure we hide all of the tables initially */
{% if rowsPerPage > 0 %}
table.DataSetTable {visibility:hidden;}
{% endif %}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Columns to be shown in the table
var columnIndex = JSON.parse(properties.columns);
var columns = [];
if (meta && meta.mapping) {
    meta.mapping.forEach((column) => {
        if (column.dataSetColumnId !== '' && columnIndex.indexOf(column.dataSetColumnId) !== -1) {
            columns[columnIndex.indexOf(column.dataSetColumnId)] = column;
        }
    });
}

// Get table element
var $datasetTableContainer = $(target).find('.DataSetTableContainer');

// Clear the table container
$datasetTableContainer.empty();

// Calculate number of pages
var totalPages = (properties.rowsPerPage > 0) ? Math.ceil(items.length / properties.rowsPerPage) : 1;

// Set the number of pages to the table
$datasetTableContainer.data('totalPages', totalPages);

// Create a table for each page
for (var i = 0; i < totalPages; i++) {
    // Create a new table
    var $newTable = properties.rowsPerPage > 0 ?
        $('<table class="DataSetTable" data-page="' + i + '">') :
        $('<table class="DataSetTable">');

    // Show the table headings if required
    if (properties.showHeadings === 1) {
        // Build the headings
        var headings = columns.map(function (column, colIdx) {
            return '<th class="DataSetColumnHeaderCell">' + column.heading + '</th>';
        });

        // Add the headings to the table
        $newTable.append(
            $('<thead>')
                .append(
                    $('<tr class="HeaderRow">')
                        .append(headings)
                )
        );
    }

    // Append table body
    $newTable.append(
        $('<tbody>')
    );

    // Add the table to the container
    $datasetTableContainer.append($newTable);
}

// Add rows to the tables per page
for (var i = 0; i < items.length; i++) {
    // Get the table for this row
    var $table = (properties.rowsPerPage > 0) ?
        $datasetTableContainer.find('.DataSetTable[data-page="' + Math.floor(i / properties.rowsPerPage) + '"]') :
        $datasetTableContainer.find('.DataSetTable');

    // Build the row content based on the columns
    var rowContent = columns.map(function (column, colIdx) {
        var value = items[i][column.heading];

        // If it's a date and we have date format
        if (column.dataTypeId === 3 && properties.dateFormat) {
            value = moment(value).format(properties.dateFormat);
        }

        // If this is an image column, wrap it in an image tag
        if (column.dataTypeId === 4 || column.dataTypeId === 5) {
            value = value ? '<img src="' + value + '" />' : '';
        }

        // Empty string if value is null
        if (value === null) {
            value = '';
        }

        return '<td class="DataSetColumn DataSetColumn_' + colIdx + '" id="column_' + (colIdx + 1) + '"><span class="DataSetCellSpan DataSetCellSpan_' + i + '_' + colIdx + '" id="span_' + i + '_' + (colIdx + 1) + '">' + value + '</span></td>';
    }).join('');

    // Add the row to the table's body
    $table.find('tbody').append(
        '<tr class="DataSetRow DataSetRow' + ((i % 2) ? 'Odd' : 'Even') + '" id="row_' + i + '">' +
        rowContent +
        '</tr>'
    );
}

// Move table container into content
$datasetTableContainer.appendTo($(target).find('#content'));

// Scale the layout
$(target).xiboLayoutScaler(properties);

// Call render
$datasetTableContainer.dataSetRender(properties);

// Image render
$datasetTableContainer.find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <assets>
            <asset id="simple-round-table" type="path" mimeType="image/png" cmsOnly="true" path="/modules/assets/template-thumbnails/dataset/2-simple-round-table.png" />
        </assets>
    </template>
    <template>
        <id>dataset_table_4</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <title>Striped blue table with darker blue header.</title>
        <thumbnail>transparent-blue</thumbnail>
        <startWidth>600</startWidth>
        <startHeight>350</startHeight>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="columns" type="datasetColumnSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showHeadings" type="checkbox">
                <title>Show the table headings?</title>
                <helpText>Should the Table headings be shown?</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="rowsPerPage" type="number">
                <title>Rows per page</title>
                <helpText>Please enter the number of rows per page. 0 for no pages.</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontFamily" type="fontSelector">
                <title>Font</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontSize" type="number">
                <title>Font Size</title>
                <helpText>Set the font size</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <stencil>
            <twig><![CDATA[
<div class="DataSetTableContainer"></div>
            ]]></twig>
            <style><![CDATA[
/* Template Styles */
table.DataSetTable {
    font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
    font-size: 14px;
}

tr.HeaderRow {
    font-size: 1.1em;
    text-align: center;
    background-color: #30426a;
    color: #f9f9f9;
}

td.DataSetColumn {
    padding: 6px;
}

td.DataSetColumn img {
    max-width: 100%;
    max-height: 50px;
    display: block;
    margin-left: auto;
    margin-right: auto;
}

tr.DataSetRow {
    text-align: center;
    color: #000000;
    background-color: #96acde;
    background-color: #96acde8e;
}

tr.DataSetRow:nth-child(even) {
    background-color: #758cc1;
    background-color: #758cc18e;
}

th.DataSetColumnHeaderCell {
    padding-top: 8px;
    padding-bottom: 6px;
}

/* Constructed Styles */
{% if fontFamily %}
table.DataSetTable {
    font-family: {{fontFamily}};
}
{% endif %}

{% if fontSize %}
table.DataSetTable {
    font-size: {{fontSize}}px;
}
{% endif %}

/* Table display CSS fix */
table.DataSetTable.cycle-slide {
    display: table !important;
}

/* If we are going to cycle between pages, make sure we hide all of the tables initially */
{% if rowsPerPage > 0 %}
table.DataSetTable {visibility:hidden;}
{% endif %}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Columns to be shown in the table
var columnIndex = JSON.parse(properties.columns);
var columns = [];
if (meta && meta.mapping) {
    meta.mapping.forEach((column) => {
        if (column.dataSetColumnId !== '' && columnIndex.indexOf(column.dataSetColumnId) !== -1) {
            columns[columnIndex.indexOf(column.dataSetColumnId)] = column;
        }
    });
}

// Get table element
var $datasetTableContainer = $(target).find('.DataSetTableContainer');

// Clear the table container
$datasetTableContainer.empty();

// Calculate number of pages
var totalPages = (properties.rowsPerPage > 0) ? Math.ceil(items.length / properties.rowsPerPage) : 1;

// Set the number of pages to the table
$datasetTableContainer.data('totalPages', totalPages);

// Create a table for each page
for (var i = 0; i < totalPages; i++) {
    // Create a new table
    var $newTable = properties.rowsPerPage > 0 ?
        $('<table class="DataSetTable" data-page="' + i + '">') :
        $('<table class="DataSetTable">');

    // Show the table headings if required
    if (properties.showHeadings === 1) {
        // Build the headings
        var headings = columns.map(function (column, colIdx) {
            return '<th class="DataSetColumnHeaderCell">' + column.heading + '</th>';
        });

        // Add the headings to the table
        $newTable.append(
            $('<thead>')
                .append(
                    $('<tr class="HeaderRow">')
                        .append(headings)
                )
        );
    }

    // Append table body
    $newTable.append(
        $('<tbody>')
    );

    // Add the table to the container
    $datasetTableContainer.append($newTable);
}

// Add rows to the tables per page
for (var i = 0; i < items.length; i++) {
    // Get the table for this row
    var $table = (properties.rowsPerPage > 0) ?
        $datasetTableContainer.find('.DataSetTable[data-page="' + Math.floor(i / properties.rowsPerPage) + '"]') :
        $datasetTableContainer.find('.DataSetTable');

    // Build the row content based on the columns
    var rowContent = columns.map(function (column, colIdx) {
        var value = items[i][column.heading];

        // If it's a date and we have date format
        if (column.dataTypeId === 3 && properties.dateFormat) {
            value = moment(value).format(properties.dateFormat);
        }

        // If this is an image column, wrap it in an image tag
        if (column.dataTypeId === 4 || column.dataTypeId === 5) {
            value = value ? '<img src="' + value + '" />' : '';
        }

        // Empty string if value is null
        if (value === null) {
            value = '';
        }

        return '<td class="DataSetColumn DataSetColumn_' + colIdx + '" id="column_' + (colIdx + 1) + '"><span class="DataSetCellSpan DataSetCellSpan_' + i + '_' + colIdx + '" id="span_' + i + '_' + (colIdx + 1) + '">' + value + '</span></td>';
    }).join('');

    // Add the row to the table's body
    $table.find('tbody').append(
        '<tr class="DataSetRow DataSetRow' + ((i % 2) ? 'Odd' : 'Even') + '" id="row_' + i + '">' +
        rowContent +
        '</tr>'
    );
}

// Move table container into content
$datasetTableContainer.appendTo($(target).find('#content'));

// Scale the layout
$(target).xiboLayoutScaler(properties);

// Call render
$datasetTableContainer.dataSetRender(properties);

// Image render
$datasetTableContainer.find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <assets>
            <asset id="transparent-blue" type="path" mimeType="image/png" cmsOnly="true" path="/modules/assets/template-thumbnails/dataset/3-transparent-blue.png" />
        </assets>
    </template>
    <template>
        <id>dataset_table_5</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <title>White striped table with orange header.</title>
        <thumbnail>orange-grey-striped</thumbnail>
        <startWidth>600</startWidth>
        <startHeight>350</startHeight>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="columns" type="datasetColumnSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showHeadings" type="checkbox">
                <title>Show the table headings?</title>
                <helpText>Should the Table headings be shown?</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="rowsPerPage" type="number">
                <title>Rows per page</title>
                <helpText>Please enter the number of rows per page. 0 for no pages.</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontFamily" type="fontSelector">
                <title>Font</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontSize" type="number">
                <title>Font Size</title>
                <helpText>Set the font size</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <stencil>
            <twig><![CDATA[
<div class="DataSetTableContainer"></div>
            ]]></twig>
            <style><![CDATA[
/* Template Styles */
table.DataSetTable {
    font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
    width: 100%;
    border-collapse: collapse;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
    table-layout: fixed;
    font-size: 14px;
    color: #3b3b3b;
}

tr.HeaderRow {
    font-size: 1.1em;
    font-weight: 900;
    background-color: #ea6153;
    color: #fff;
}

td.DataSetColumn {
    padding: 6px;
}

td.DataSetColumn img {
    max-width: 100%;
    max-height: 50px;
    display: block;
    margin-left: auto;
    margin-right: auto;
}

tr.DataSetRow {
    background-color: #f6f6f6;
}

tr.DataSetRow:nth-child(even) {
    background-color: #e9e9e9;
}

th.DataSetColumnHeaderCell {
    padding-top: 8px;
    padding-bottom: 6px;
}

/* Constructed Styles */
{% if fontFamily %}
table.DataSetTable {
    font-family: {{fontFamily}};
}
{% endif %}

{% if fontSize %}
table.DataSetTable {
    font-size: {{fontSize}}px;
}
{% endif %}

/* Table display CSS fix */
table.DataSetTable.cycle-slide {
    display: table !important;
}

/* If we are going to cycle between pages, make sure we hide all of the tables initially */
{% if rowsPerPage > 0 %}
table.DataSetTable {visibility:hidden;}
{% endif %}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Columns to be shown in the table
var columnIndex = JSON.parse(properties.columns);
var columns = [];
if (meta && meta.mapping) {
    meta.mapping.forEach((column) => {
        if (column.dataSetColumnId !== '' && columnIndex.indexOf(column.dataSetColumnId) !== -1) {
            columns[columnIndex.indexOf(column.dataSetColumnId)] = column;
        }
    });
}

// Get table element
var $datasetTableContainer = $(target).find('.DataSetTableContainer');

// Clear the table container
$datasetTableContainer.empty();

// Calculate number of pages
var totalPages = (properties.rowsPerPage > 0) ? Math.ceil(items.length / properties.rowsPerPage) : 1;

// Set the number of pages to the table
$datasetTableContainer.data('totalPages', totalPages);

// Create a table for each page
for (var i = 0; i < totalPages; i++) {
    // Create a new table
    var $newTable = properties.rowsPerPage > 0 ?
        $('<table class="DataSetTable" data-page="' + i + '">') :
        $('<table class="DataSetTable">');

    // Show the table headings if required
    if (properties.showHeadings === 1) {
        // Build the headings
        var headings = columns.map(function (column, colIdx) {
            return '<th class="DataSetColumnHeaderCell">' + column.heading + '</th>';
        });

        // Add the headings to the table
        $newTable.append(
            $('<thead>')
                .append(
                    $('<tr class="HeaderRow">')
                        .append(headings)
                )
        );
    }

    // Append table body
    $newTable.append(
        $('<tbody>')
    );

    // Add the table to the container
    $datasetTableContainer.append($newTable);
}

// Add rows to the tables per page
for (var i = 0; i < items.length; i++) {
    // Get the table for this row
    var $table = (properties.rowsPerPage > 0) ?
        $datasetTableContainer.find('.DataSetTable[data-page="' + Math.floor(i / properties.rowsPerPage) + '"]') :
        $datasetTableContainer.find('.DataSetTable');

    // Build the row content based on the columns
    var rowContent = columns.map(function (column, colIdx) {
        var value = items[i][column.heading];

        // If it's a date and we have date format
        if (column.dataTypeId === 3 && properties.dateFormat) {
            value = moment(value).format(properties.dateFormat);
        }

        // If this is an image column, wrap it in an image tag
        if (column.dataTypeId === 4 || column.dataTypeId === 5) {
            value = value ? '<img src="' + value + '" />' : '';
        }

        // Empty string if value is null
        if (value === null) {
            value = '';
        }

        return '<td class="DataSetColumn DataSetColumn_' + colIdx + '" id="column_' + (colIdx + 1) + '"><span class="DataSetCellSpan DataSetCellSpan_' + i + '_' + colIdx + '" id="span_' + i + '_' + (colIdx + 1) + '">' + value + '</span></td>';
    }).join('');

    // Add the row to the table's body
    $table.find('tbody').append(
        '<tr class="DataSetRow DataSetRow' + ((i % 2) ? 'Odd' : 'Even') + '" id="row_' + i + '">' +
        rowContent +
        '</tr>'
    );
}

// Move table container into content
$datasetTableContainer.appendTo($(target).find('#content'));

// Scale the layout
$(target).xiboLayoutScaler(properties);

// Call render
$datasetTableContainer.dataSetRender(properties);

// Image render
$datasetTableContainer.find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <assets>
            <asset id="orange-grey-striped" type="path" mimeType="image/png" cmsOnly="true" path="/modules/assets/template-thumbnails/dataset/4-orange-grey-striped.png" />
        </assets>
    </template>
    <template>
        <id>dataset_table_6</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <title>White and grey table with split rows.</title>
        <thumbnail>split-rows-</thumbnail>
        <startWidth>600</startWidth>
        <startHeight>350</startHeight>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="columns" type="datasetColumnSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showHeadings" type="checkbox">
                <title>Show the table headings?</title>
                <helpText>Should the Table headings be shown?</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="rowsPerPage" type="number">
                <title>Rows per page</title>
                <helpText>Please enter the number of rows per page. 0 for no pages.</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontFamily" type="fontSelector">
                <title>Font</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontSize" type="number">
                <title>Font Size</title>
                <helpText>Set the font size</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <stencil>
            <twig><![CDATA[
<div class="DataSetTableContainer"></div>
            ]]></twig>
            <style><![CDATA[
/* Template Styles */
table.DataSetTable {
    font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
    width: 100%;
    border-collapse: collapse;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
    table-layout: fixed;
    font-size: 14px;
    color: #3b3b3b;
}

tr.HeaderRow {
    font-size: 1.1em;
    font-weight: 900;
    background-color: #95A5A6;
    color: #222;
    text-align: left;
}

td.DataSetColumn {
    padding: 8px;
}

td.DataSetColumn img {
    max-width: 100%;
    max-height: 50px;
    display: block;
    margin-left: auto;
    margin-right: auto;
}

tr.DataSetRow {
    background-color: #f6f6f6;
    border-bottom: 6px solid #95a5a6;
}

th.DataSetColumnHeaderCell {
    padding: 8px;
    padding-top: 8px;
}

/* Constructed Styles */
{% if fontFamily %}
table.DataSetTable {
    font-family: {{fontFamily}};
}
{% endif %}

{% if fontSize %}
table.DataSetTable {
    font-size: {{fontSize}}px;
}
{% endif %}

/* Table display CSS fix */
table.DataSetTable.cycle-slide {
    display: table !important;
}

/* If we are going to cycle between pages, make sure we hide all of the tables initially */
{% if rowsPerPage > 0 %}
table.DataSetTable {visibility:hidden;}
{% endif %}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Columns to be shown in the table
var columnIndex = JSON.parse(properties.columns);
var columns = [];
if (meta && meta.mapping) {
    meta.mapping.forEach((column) => {
        if (column.dataSetColumnId !== '' && columnIndex.indexOf(column.dataSetColumnId) !== -1) {
            columns[columnIndex.indexOf(column.dataSetColumnId)] = column;
        }
    });
}

// Get table element
var $datasetTableContainer = $(target).find('.DataSetTableContainer');

// Clear the table container
$datasetTableContainer.empty();

// Calculate number of pages
var totalPages = (properties.rowsPerPage > 0) ? Math.ceil(items.length / properties.rowsPerPage) : 1;

// Set the number of pages to the table
$datasetTableContainer.data('totalPages', totalPages);

// Create a table for each page
for (var i = 0; i < totalPages; i++) {
    // Create a new table
    var $newTable = properties.rowsPerPage > 0 ?
        $('<table class="DataSetTable" data-page="' + i + '">') :
        $('<table class="DataSetTable">');

    // Show the table headings if required
    if (properties.showHeadings === 1) {
        // Build the headings
        var headings = columns.map(function (column, colIdx) {
            return '<th class="DataSetColumnHeaderCell">' + column.heading + '</th>';
        });

        // Add the headings to the table
        $newTable.append(
            $('<thead>')
                .append(
                    $('<tr class="HeaderRow">')
                        .append(headings)
                )
        );
    }

    // Append table body
    $newTable.append(
        $('<tbody>')
    );

    // Add the table to the container
    $datasetTableContainer.append($newTable);
}

// Add rows to the tables per page
for (var i = 0; i < items.length; i++) {
    // Get the table for this row
    var $table = (properties.rowsPerPage > 0) ?
        $datasetTableContainer.find('.DataSetTable[data-page="' + Math.floor(i / properties.rowsPerPage) + '"]') :
        $datasetTableContainer.find('.DataSetTable');

    // Build the row content based on the columns
    var rowContent = columns.map(function (column, colIdx) {
        var value = items[i][column.heading];

        // If it's a date and we have date format
        if (column.dataTypeId === 3 && properties.dateFormat) {
            value = moment(value).format(properties.dateFormat);
        }

        // If this is an image column, wrap it in an image tag
        if (column.dataTypeId === 4 || column.dataTypeId === 5) {
            value = value ? '<img src="' + value + '" />' : '';
        }

        // Empty string if value is null
        if (value === null) {
            value = '';
        }

        return '<td class="DataSetColumn DataSetColumn_' + colIdx + '" id="column_' + (colIdx + 1) + '"><span class="DataSetCellSpan DataSetCellSpan_' + i + '_' + colIdx + '" id="span_' + i + '_' + (colIdx + 1) + '">' + value + '</span></td>';
    }).join('');

    // Add the row to the table's body
    $table.find('tbody').append(
        '<tr class="DataSetRow DataSetRow' + ((i % 2) ? 'Odd' : 'Even') + '" id="row_' + i + '">' +
        rowContent +
        '</tr>'
    );
}

// Move table container into content
$datasetTableContainer.appendTo($(target).find('#content'));

// Scale the layout
$(target).xiboLayoutScaler(properties);

// Call render
$datasetTableContainer.dataSetRender(properties);

// Image render
$datasetTableContainer.find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <assets>
            <asset id="split-rows-" type="path" mimeType="image/png" cmsOnly="true" path="/modules/assets/template-thumbnails/dataset/5-split-rows-.png" />
        </assets>
    </template>
    <template>
        <id>dataset_table_7</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <title>A dark table with round borders and yellow heading text.</title>
        <thumbnail>dark-round</thumbnail>
        <startWidth>600</startWidth>
        <startHeight>350</startHeight>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="columns" type="datasetColumnSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showHeadings" type="checkbox">
                <title>Show the table headings?</title>
                <helpText>Should the Table headings be shown?</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="rowsPerPage" type="number">
                <title>Rows per page</title>
                <helpText>Please enter the number of rows per page. 0 for no pages.</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontFamily" type="fontSelector">
                <title>Font</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontSize" type="number">
                <title>Font Size</title>
                <helpText>Set the font size</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <stencil>
            <twig><![CDATA[
<div class="DataSetTableContainer"></div>
            ]]></twig>
            <style><![CDATA[
/* Template Styles */
table.DataSetTable {
    font-family: Montserrat, sans-serif;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-size: 14px;
    width: 100%;
    table-layout: fixed;
    border-radius: 12px;
    background-color: #333333;
    padding: 8px;
    overflow: hidden;
    opacity: 0.85;
}

tr.HeaderRow {
    font-size: 1.1em;
    text-align: left;
    color: #dd5;
}

td.DataSetColumn {
    padding: 6px !important;
    color: #f9f9f9;
}

td.DataSetColumn img {
    max-width: 100%;
    max-height: 50px;
    display: block;
}

th.DataSetColumnHeaderCell {
    padding: 4px 6px !important;
}

/* Constructed Styles */
{% if fontFamily %}
table.DataSetTable {
    font-family: {{fontFamily}};
}
{% endif %}

{% if fontSize %}
table.DataSetTable {
    font-size: {{fontSize}}px;
}
{% endif %}

/* Table display CSS fix */
table.DataSetTable.cycle-slide {
    display: table !important;
}

/* If we are going to cycle between pages, make sure we hide all of the tables initially */
{% if rowsPerPage > 0 %}
table.DataSetTable {visibility:hidden;}
{% endif %}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Columns to be shown in the table
var columnIndex = JSON.parse(properties.columns);
var columns = [];
if (meta && meta.mapping) {
    meta.mapping.forEach((column) => {
        if (column.dataSetColumnId !== '' && columnIndex.indexOf(column.dataSetColumnId) !== -1) {
            columns[columnIndex.indexOf(column.dataSetColumnId)] = column;
        }
    });
}

// Get table element
var $datasetTableContainer = $(target).find('.DataSetTableContainer');

// Clear the table container
$datasetTableContainer.empty();

// Calculate number of pages
var totalPages = (properties.rowsPerPage > 0) ? Math.ceil(items.length / properties.rowsPerPage) : 1;

// Set the number of pages to the table
$datasetTableContainer.data('totalPages', totalPages);

// Create a table for each page
for (var i = 0; i < totalPages; i++) {
    // Create a new table
    var $newTable = properties.rowsPerPage > 0 ?
        $('<table class="DataSetTable" data-page="' + i + '">') :
        $('<table class="DataSetTable">');

    // Show the table headings if required
    if (properties.showHeadings === 1) {
        // Build the headings
        var headings = columns.map(function (column, colIdx) {
            return '<th class="DataSetColumnHeaderCell">' + column.heading + '</th>';
        });

        // Add the headings to the table
        $newTable.append(
            $('<thead>')
                .append(
                    $('<tr class="HeaderRow">')
                        .append(headings)
                )
        );
    }

    // Append table body
    $newTable.append(
        $('<tbody>')
    );

    // Add the table to the container
    $datasetTableContainer.append($newTable);
}

// Add rows to the tables per page
for (var i = 0; i < items.length; i++) {
    // Get the table for this row
    var $table = (properties.rowsPerPage > 0) ?
        $datasetTableContainer.find('.DataSetTable[data-page="' + Math.floor(i / properties.rowsPerPage) + '"]') :
        $datasetTableContainer.find('.DataSetTable');

    // Build the row content based on the columns
    var rowContent = columns.map(function (column, colIdx) {
        var value = items[i][column.heading];

        // If it's a date and we have date format
        if (column.dataTypeId === 3 && properties.dateFormat) {
            value = moment(value).format(properties.dateFormat);
        }

        // If this is an image column, wrap it in an image tag
        if (column.dataTypeId === 4 || column.dataTypeId === 5) {
            value = value ? '<img src="' + value + '" />' : '';
        }

        // Empty string if value is null
        if (value === null) {
            value = '';
        }

        return '<td class="DataSetColumn DataSetColumn_' + colIdx + '" id="column_' + (colIdx + 1) + '"><span class="DataSetCellSpan DataSetCellSpan_' + i + '_' + colIdx + '" id="span_' + i + '_' + (colIdx + 1) + '">' + value + '</span></td>';
    }).join('');

    // Add the row to the table's body
    $table.find('tbody').append(
        '<tr class="DataSetRow DataSetRow' + ((i % 2) ? 'Odd' : 'Even') + '" id="row_' + i + '">' +
        rowContent +
        '</tr>'
    );
}

// Move table container into content
$datasetTableContainer.appendTo($(target).find('#content'));

// Scale the layout
$(target).xiboLayoutScaler(properties);

// Call render
$datasetTableContainer.dataSetRender(properties);

// Image render
$datasetTableContainer.find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <assets>
            <asset id="dark-round" type="path" mimeType="image/png" cmsOnly="true" path="/modules/assets/template-thumbnails/dataset/6-dark-round.png" />
        </assets>
    </template>
    <template>
        <id>dataset_table_8</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <title>Round cells with multi colours and a full coloured header.</title>
        <thumbnail>pill-colored</thumbnail>
        <startWidth>600</startWidth>
        <startHeight>350</startHeight>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="columns" type="datasetColumnSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showHeadings" type="checkbox">
                <title>Show the table headings?</title>
                <helpText>Should the Table headings be shown?</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="rowsPerPage" type="number">
                <title>Rows per page</title>
                <helpText>Please enter the number of rows per page. 0 for no pages.</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontFamily" type="fontSelector">
                <title>Font</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="fontSize" type="number">
                <title>Font Size</title>
                <helpText>Set the font size</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <stencil>
            <twig><![CDATA[
<div class="DataSetTableContainer"></div>
            ]]></twig>
            <style><![CDATA[

/* Template Styles */
table.DataSetTable {
    font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
    width: 100%;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
    table-layout: fixed;
    font-size: 14px;
    color: #3b3b3b;
    text-align: center;
}

tr.HeaderRow {
    font-size: 1.1em;
    font-weight: 900;
    color: #222;
}

td.DataSetColumn {
    padding: 6px;
    border-radius: 8px;
    border-width: 2px;
    border-style: solid;
}

td.DataSetColumn:nth-child(4n) {
    border-color: #ee6c6c;
}

td.DataSetColumn:nth-child(4n+1) {
    border-color: #9b99f3;
}

td.DataSetColumn:nth-child(4n+2) {
    border-color: #5be280;
}

td.DataSetColumn:nth-child(4n+3) {
    border-color: #fbdc61;
}

td.DataSetColumn img {
    max-width: 100%;
    max-height: 50px;
    display: block;
    margin-left: auto;
    margin-right: auto;
}

tr.DataSetRow {
    background-color: #f6f6f6;
    border-bottom: 6px solid #95a5a6;
}

th.DataSetColumnHeaderCell {
    padding: 6px;
    border-radius: 8px;
    border-width: 2px;
    border-style: solid;
    border-color: transparent;
}

th.DataSetColumnHeaderCell:nth-child(4n) {
    background-color: #ee6c6c;
}

th.DataSetColumnHeaderCell:nth-child(4n+1) {
    background-color: #9b99f3;
}

th.DataSetColumnHeaderCell:nth-child(4n+2) {
    background-color: #5be280;
}

th.DataSetColumnHeaderCell:nth-child(4n+3) {
    background-color: #fbdc61;
}

/* Constructed Styles */
{% if fontFamily %}
table.DataSetTable {
    font-family: {{fontFamily}};
}
{% endif %}

{% if fontSize %}
table.DataSetTable {
    font-size: {{fontSize}}px;
}
{% endif %}

/* Table display CSS fix */
table.DataSetTable.cycle-slide {
    display: table !important;
}

/* If we are going to cycle between pages, make sure we hide all of the tables initially */
{% if rowsPerPage > 0 %}
table.DataSetTable {visibility:hidden;}
{% endif %}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Columns to be shown in the table
var columnIndex = JSON.parse(properties.columns);
var columns = [];
if (meta && meta.mapping) {
    meta.mapping.forEach((column) => {
        if (column.dataSetColumnId !== '' && columnIndex.indexOf(column.dataSetColumnId) !== -1) {
            columns[columnIndex.indexOf(column.dataSetColumnId)] = column;
        }
    });
}

// Get table element
var $datasetTableContainer = $(target).find('.DataSetTableContainer');

// Clear the table container
$datasetTableContainer.empty();

// Calculate number of pages
var totalPages = (properties.rowsPerPage > 0) ? Math.ceil(items.length / properties.rowsPerPage) : 1;

// Set the number of pages to the table
$datasetTableContainer.data('totalPages', totalPages);

// Create a table for each page
for (var i = 0; i < totalPages; i++) {
    // Create a new table
    var $newTable = properties.rowsPerPage > 0 ?
        $('<table class="DataSetTable" data-page="' + i + '">') :
        $('<table class="DataSetTable">');

    // Show the table headings if required
    if (properties.showHeadings === 1) {
        // Build the headings
        var headings = columns.map(function (column, colIdx) {
            return '<th class="DataSetColumnHeaderCell">' + column.heading + '</th>';
        });

        // Add the headings to the table
        $newTable.append(
            $('<thead>')
                .append(
                    $('<tr class="HeaderRow">')
                        .append(headings)
                )
        );
    }

    // Append table body
    $newTable.append(
        $('<tbody>')
    );

    // Add the table to the container
    $datasetTableContainer.append($newTable);
}

// Add rows to the tables per page
for (var i = 0; i < items.length; i++) {
    // Get the table for this row
    var $table = (properties.rowsPerPage > 0) ?
        $datasetTableContainer.find('.DataSetTable[data-page="' + Math.floor(i / properties.rowsPerPage) + '"]') :
        $datasetTableContainer.find('.DataSetTable');

    // Build the row content based on the columns
    var rowContent = columns.map(function (column, colIdx) {
        var value = items[i][column.heading];

        // If it's a date and we have date format
        if (column.dataTypeId === 3 && properties.dateFormat) {
            value = moment(value).format(properties.dateFormat);
        }

        // If this is an image column, wrap it in an image tag
        if (column.dataTypeId === 4 || column.dataTypeId === 5) {
            value = value ? '<img src="' + value + '" />' : '';
        }

        // Empty string if value is null
        if (value === null) {
            value = '';
        }

        return '<td class="DataSetColumn DataSetColumn_' + colIdx + '" id="column_' + (colIdx + 1) + '"><span class="DataSetCellSpan DataSetCellSpan_' + i + '_' + colIdx + '" id="span_' + i + '_' + (colIdx + 1) + '">' + value + '</span></td>';
    }).join('');

    // Add the row to the table's body
    $table.find('tbody').append(
        '<tr class="DataSetRow DataSetRow' + ((i % 2) ? 'Odd' : 'Even') + '" id="row_' + i + '">' +
        rowContent +
        '</tr>'
    );
}

// Move table container into content
$datasetTableContainer.appendTo($(target).find('#content'));

// Scale the layout
$(target).xiboLayoutScaler(properties);

// Call render
$datasetTableContainer.dataSetRender(properties);

// Image render
$datasetTableContainer.find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <assets>
            <asset id="pill-colored" type="path" mimeType="image/png" cmsOnly="true" path="/modules/assets/template-thumbnails/dataset/7-pill-colored.png" />
        </assets>
    </template>
    <template>
        <id>dataset_slideshow</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <showIn>playlist</showIn>
        <icon>fas fa-film</icon>
        <title>Image Slideshow</title>
        <properties>
            <property id="dataTypeId" type="hidden">
                <default>4,5</default>
            </property>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>No image field is available for the selected DataSet.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                        <condition field="datasetField" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="datasetField" type="datasetField">
                <dependsOn>dataSetId</dependsOn>
                <title>Select DataSet Field</title>
                <helpText>Please choose a DataSet field for this element.</helpText>
                <default></default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="effect" type="effectSelector" variant="showPaged">
                <title>Effect</title>
                <helpText>Please select the effect that will be used to transition between items.</helpText>
                <default>tileBlind</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="speed" type="number">
                <title>Speed</title>
                <helpText>The transition speed of the selected effect in milliseconds (normal = 1000)</helpText>
                <default>1000</default>
                <visibility>
                    <test type="and">
                        <condition field="effect" type="neq">noTransition</condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <preview></preview>
        <stencil>
            <style><![CDATA[
img {
  height: 100%;
  width: 100%;
  object-fit: contain;
}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Module renderer options
// id: The id of the widget
// target: The target element to render
// items: The items to render
// properties: The properties for the widget

// Clear the content container
$(target).find('#content').empty();

// If we don't have metadata, we can't render anything
if (!meta || !meta.mapping) {
    return;
}

// Get dataset columns from meta.mapping
var columnTypes = {};
for (var i = 0; i < meta.mapping.length; i++) {
    var column = meta.mapping[i];
    columnTypes[column.dataSetColumnId] = {
        type: column.dataTypeId
    };
}

// Add content for each item
// make replacements
// and wrap it in a div with the item class
for (var i = 0; i < items.length; i++) {
    var item = items[i];
    var itemValue = item[properties.datasetField];

    // Only add item if we have a value for it
    if(itemValue) {
        var content = '<img src="' + itemValue  + '" />';

        // Add the content to the target
        $(target).find('#content').append(
            $('<div>')
                .addClass('item')
                .append(content)
        );
    }
}

// Scale the layout
$('body').xiboLayoutScaler(properties);

$(target).xiboTextRender(Object.assign(properties, globalOptions), $(target).find('#content > *'));

// Image render
$(target).find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <onTemplateVisible><![CDATA[
// Start effects for this template
$(target).xiboLayoutAnimate(properties);
        ]]></onTemplateVisible>
    </template>
    <template>
        <id>dataset_string_template</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <showIn>playlist</showIn>
        <icon>fa fa-font</icon>
        <title>String template with placeholders</title>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="template" type="richText" allowLibraryRefs="true" variant="html">
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dataSetSnippets" type="snippet" mode="dataSet" target="template">
                <title>Snippets</title>
                <helpText>Choose data set snippet</helpText>
                <dependsOn>dataSetId</dependsOn>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="itemsSideBySide" type="checkbox">
                <title>Show items side by side?</title>
                <helpText>Should items be shown side by side?</helpText>
                <default>0</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="backgroundColor" type="color">
                <title>Background Colour</title>
                <helpText>The selected effect works best with a background colour. Optionally add one here.</helpText>
                <default></default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="effect" type="effectSelector" variant="all">
                <title>Effect</title>
                <helpText>Please select the effect that will be used to transition between items.</helpText>
                <default>noTransition</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="speed" type="number">
                <title>Speed</title>
                <helpText>The transition speed of the selected effect in milliseconds (normal = 1000) or the Marquee Speed in a low to high scale (normal = 1)</helpText>
                <default>1000</default>
                <visibility>
                    <test type="and">
                        <condition field="effect" type="neq">none</condition>
                        <condition field="effect" type="neq">noTransition</condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="itemsPerPage" type="number">
                <title>Items per page</title>
                <helpText>If an effect has been selected, how many pages should we split the items across? If you don't enter anything here 1 item will be put on each page.</helpText>
                <default>1</default>
                <visibility>
                    <test type="and">
                        <condition field="effect" type="neq">none</condition>
                        <condition field="effect" type="neq">marqueeLeft</condition>
                        <condition field="effect" type="neq">marqueeRight</condition>
                        <condition field="effect" type="neq">marqueeUp</condition>
                        <condition field="effect" type="neq">marqueeDown</condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <preview></preview>
        <stencil>
            <style><![CDATA[
{% if itemsSideBySide %}
.item, .page {
    float: left;
}
{% endif %}

{% if backgroundColor != '' %}
body {
    background-color: {{ backgroundColor }} !important;
}
{% endif %}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// Module renderer options
// id: The id of the widget
// target: The target element to render
// items: The items to render
// properties: The properties for the widget

// Show no data message
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    $(target).html(properties.noDataMessage);

    // Scale the layout
    $(target).xiboLayoutScaler(properties);

    // Image render
    $(target).find('img').xiboImageRender(properties);

    // Don't render the rest
    return;
}

// Clear the content container
$(target).find('#content').empty();

// If we don't have metadata, we can't render anything
if (!meta || !meta.mapping) {
    return;
}

// Get dataset columns from meta.mapping
var columnTypes = {};
for (var i = 0; i < meta.mapping.length; i++) {
    var column = meta.mapping[i];
    columnTypes[column.dataSetColumnId] = {
        type: column.dataTypeId
    };
}

// Add content for each item
// make replacements
// and wrap it in a div with the item class
for (var i = 0; i < items.length; i++) {
    var item = items[i];

    // Replace the template with the item content
    var content = properties.template.replace(/\[(.*?)\]/g, function (match, column) {
        var itemId = column.split('|')[0];
        var itemCol = column.split('|')[1];
        var itemType = (itemCol) ? columnTypes[itemCol].type : '';
        var itemValue = item[itemId];

        // If this is an image column, wrap it in an image tag
        if (itemType === 4 || itemType === 5) {
            itemValue = itemValue ? '<img src="' + itemValue + '" />' : '';
        }

        return itemValue ? itemValue : '';
    });

    // Add the content to the target
    $(target).find('#content').append(
        $('<div>')
            .addClass('item')
            .append(content)
    );
}

// Scale the layout
$('body').xiboLayoutScaler(properties);

$(target).xiboTextRender(Object.assign(properties, globalOptions), $(target).find('#content > *'));

// Image render
$(target).find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <onTemplateVisible><![CDATA[
// Start effects for this template
$(target).xiboLayoutAnimate(properties);
        ]]></onTemplateVisible>
    </template>
    <template>
        <id>dataset_marquee</id>
        <type>static</type>
        <dataType>dataset</dataType>
        <title>Dataset shown in a marquee</title>
        <startWidth>1200</startWidth>
        <startHeight>80</startHeight>
        <properties>
            <property type="message">
                <title>Select a dataset to display appearance options.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="eq"></condition>
                    </test>
                </visibility>
            </property>
            <property type="message">
                <title>Below you can select the columns to be shown in the table - drag and drop to reorder and to move between lists.</title>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="selectedColumns" type="datasetColStyleSelector">
                <dependsOn>dataSetId</dependsOn>
                <title>Item Template</title>
                <helpText>Enter text in the box below, used to display each article.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="effect" type="effectSelector" variant="showAll">
                <title>Effect</title>
                <helpText>Please select the effect that will be used to transition between items.</helpText>
                <default>marqueeLeft</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="speed" type="number">
                <title>Speed</title>
                <helpText>Marquee Speed in a low to high scale (normal = 1)</helpText>
                <default>1</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="dateFormat" type="text" variant="dateFormat">
                <title>Date Format</title>
                <helpText>The format to apply to all dates returned by the Widget.</helpText>
                <default>#DATE_FORMAT#</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="gapTags" type="number">
                <title>Gap between tags</title>
                <helpText>Value (in pixels) to set a gap between each item's tags.</helpText>
                <default>6</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="gapItems" type="number">
                <title>Gap between items</title>
                <helpText>Value (in pixels) to set a gap between each item.</helpText>
                <default>6</default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="backgroundColor" type="color">
                <title>Background Colour</title>
                <helpText>The selected effect works best with a background colour. Optionally add one here.</helpText>
                <default></default>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="textDirection" type="dropdown" mode="single">
                <title>Text direction</title>
                <helpText>Which direction does the text in the feed use?</helpText>
                <default>ltr</default>
                <options>
                    <option name="ltr">Left to Right (LTR)</option>
                    <option name="rtl">Right to Left (RTL)</option>
                </options>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="showSeparator" type="checkbox">
                <title>Show a separator between items?</title>
                <default>0</default>
                <visibility>
                    <test type="and">
                        <condition field="effect" type="neq">none</condition>
                        <condition field="effect" type="neq">noTransition</condition>
                        <condition field="effect" type="neq">fade</condition>
                        <condition field="effect" type="neq">fadeout</condition>
                        <condition field="effect" type="neq">scrollHorz</condition>
                        <condition field="effect" type="neq">scrollVert</condition>
                        <condition field="effect" type="neq">flipHorz</condition>
                        <condition field="effect" type="neq">flipVert</condition>
                        <condition field="effect" type="neq">shuffle</condition>
                        <condition field="effect" type="neq">tileSlide</condition>
                        <condition field="effect" type="neq">tileBlind</condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="separator" type="richText" allowLibraryRefs="false" variant="html">
                <title>Separator</title>
                <helpText>A separator to show between marquee items</helpText>
                <default>
                <![CDATA[
                    <p><span style="font-size: 16px;">/</span></p>
                ]]></default>
                <visibility>
                    <test type="and">
                        <condition field="effect" type="neq">none</condition>
                        <condition field="effect" type="neq">noTransition</condition>
                        <condition field="effect" type="neq">fade</condition>
                        <condition field="effect" type="neq">fadeout</condition>
                        <condition field="effect" type="neq">scrollHorz</condition>
                        <condition field="effect" type="neq">scrollVert</condition>
                        <condition field="effect" type="neq">flipHorz</condition>
                        <condition field="effect" type="neq">flipVert</condition>
                        <condition field="effect" type="neq">shuffle</condition>
                        <condition field="effect" type="neq">tileSlide</condition>
                        <condition field="effect" type="neq">tileBlind</condition>
                        <condition field="showSeparator" type="eq">1</condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="copyright" type="text">
                <title>Copyright</title>
                <helpText>Copyright information to display as the last item in this feed.</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="copyrightFontFamily" type="fontSelector">
                <title>Copyright Font Family</title>
                <helpText>Select a custom font - leave empty to use the default font.</helpText>
                <visibility>
                    <test type="and">
                        <condition field="copyright" type="neq"></condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="copyrightFontColor" type="color">
                <title>Copyright Font Colour</title>
                <default></default>
                <visibility>
                    <test type="and">
                        <condition field="copyright" type="neq"></condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="copyrightFontSize" type="number">
                <title>Copyright Font Size</title>
                <default></default>
                <visibility>
                    <test type="and">
                        <condition field="copyright" type="neq"></condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="copyrightBold" type="checkbox">
                <title>Bold</title>
                <helpText>Should the copyright text be bold?</helpText>
                <default>0</default>
                <visibility>
                    <test type="and">
                        <condition field="copyright" type="neq"></condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="copyrightItalics" type="checkbox">
                <title>Italics</title>
                <helpText>Should the copyright text be italicised?</helpText>
                <default>0</default>
                <visibility>
                    <test type="and">
                        <condition field="copyright" type="neq"></condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="copyrightUnderline" type="checkbox">
                <title>Underline</title>
                <helpText>Should the copyright text be underlined?</helpText>
                <default>0</default>
                <visibility>
                    <test type="and">
                        <condition field="copyright" type="neq"></condition>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
            <property id="noDataMessage" type="richText" allowLibraryRefs="true" variant="html">
                <title>No data message</title>
                <helpText>A message to display when no data is returned from the source</helpText>
                <visibility>
                    <test>
                        <condition field="dataSetId" type="neq"></condition>
                    </test>
                </visibility>
            </property>
        </properties>
        <stencil>
            <twig><![CDATA[
{% if javaScript %}<script type="text/javascript">{{javaScript|raw}}</script>{% endif %}
            ]]></twig>
            <style><![CDATA[
{% if textDirection == "rtl" %}#content { direction: rtl; }{% endif %}
{% if backgroundColor %}body { background-color: {{backgroundColor}} !important; }{% endif %}
{% if (effect == "marqueeLeft") or (effect == "marqueeRight") %}
    {% if gapTags %}.marquee-item { margin-right: {{gapTags}}px; }{% endif %}
    {% if gapItems %}.item.text-render-item, .separator { margin-right: {{gapItems}}px; }{% endif %}
    .is-media-item { display: inline-block; }
    .marquee-item { vertical-align: middle; }
{% endif %}

{% if (effect == "marqueeUp") or (effect == "marqueeDown") %}
    {% if gapTags %}.marquee-item { margin-bottom: {{gapTags}}px; }{% endif %}
    {% if gapItems %}.item.text-render-item, .separator { margin-bottom: {{gapItems}}px; }{% endif %}
    .marquee-item { display: block; }
{% endif %}
            ]]></style>
        </stencil>
        <onTemplateRender><![CDATA[
// id: The id of the widget
// target: The target element to render
// items: The items to render
// properties: The properties for the widget
// -------------------------------------------

// Get selected columns
var selectedColumns = (properties.selectedColumns) ? JSON.parse(properties.selectedColumns) : [];

// Convert to array and sort by play order
selectedColumns = Object.values(selectedColumns);
selectedColumns.sort(function(a, b) {return a.playOrder - b.playOrder;});

// Get dataset columns from meta.mapping
var columnInfo = {};
if (meta) {
    for (var i = 0; i < meta.mapping.length; i++) {
        var column = meta.mapping[i];
        columnInfo[column.dataSetColumnId] = {
            name: column.heading,
            type: column.dataTypeId
        };
    }
}

// Generate template from chosen fields
properties.template = '';
for (var col in selectedColumns) {
    var column = selectedColumns[col];
    var isMedia = (columnInfo[column.colId].type == 5 || columnInfo[column.colId].type === 4);

    properties.template += '<span class="marquee-item marquee-' + column.colId;

    if (isMedia) {
        properties.template += ' is-media-item';
    }

    properties.template += '" style="';

    for (var style in selectedColumns[col]) {
        if (style === 'fontSize' && selectedColumns[col][style]) {
            properties.template += 'font-size:' + selectedColumns[col][style] + 'px;';
        }
        if (style === 'fontColor' && selectedColumns[col][style]) {
            properties.template += 'color:' + selectedColumns[col][style] + ';';
        }
        if (style === 'fontFamily' && selectedColumns[col][style]) {
            properties.template += 'font-family:' + selectedColumns[col][style] + ';';
        }
        if (style === 'bold' && selectedColumns[col][style]) {
            properties.template += 'font-weight: bold;';
        }
        if (style === 'italics' && selectedColumns[col][style]) {
            properties.template += 'font-style: italic;';
        }
        if (style === 'underline' && selectedColumns[col][style]) {
            properties.template += 'text-decoration: underline;';
        }
        if (style === 'width' && selectedColumns[col][style]) {
            properties.template += 'width:' + selectedColumns[col][style] + 'px;';
        }
        if (style === 'height' && selectedColumns[col][style]) {
            properties.template += 'height:' + selectedColumns[col][style] + 'px;';
        }
        if (style === 'opacity' && selectedColumns[col][style]) {
            properties.template += 'opacity:' + selectedColumns[col][style] + ';';
        }
    }

    properties.template += '">[' + columnInfo[column.colId].name + '|' + column.colId + ']</span>';
}


// Clear containers
$(target).find('#content').empty();
$(target).find('.no-data-message').remove();

var hasAddedItems = false;
if (items.length <= 0 && properties.noDataMessage && properties.noDataMessage !== '') {
    // No data message
    // Add message to the target content
    $(target).find('#content').after(
        $('<div>')
            .addClass('no-data-message')
            .append(properties.noDataMessage)
    );
} else {
    // Add items to container
    for (var i = 0; i < items.length; i++) {
        var item = items[i];

        // Replace the template with the item content
        var content = properties.template.replace(/\[(.*?)\]/g, function(match, column) {
            var itemId = column.split('|')[0];
            var itemCol = column.split('|')[1];
            var itemType = (itemCol) ? columnInfo[itemCol].type : '';
            var itemValue = item[itemId];

            // If it's a date and we have date format
            if (itemType === 3 && properties.dateFormat) {
                itemValue = moment(itemValue).format(properties.dateFormat);
            }

            // If this is an image column, wrap it in an image tag
            if (itemType === 4 || itemType === 5) {
                itemValue = itemValue ? '<img style="width: 100%; height: 100%;" src="' + itemValue + '" />' : '';
            }

            return itemValue ? itemValue : '';
        });

        // Add the content to the target
        if(content != "") {
            hasAddedItems = true;
            $(target).find('#content').append(
                $('<div>')
                    .addClass('item')
                    .append(content)
            );
        }
    }
}

// Copyright
if (hasAddedItems && properties.copyright) {
    var copyrightTemplate = '<span class="marquee-item marquee-copyright" style="';

    if (properties.copyrightFontFamily) {
        copyrightTemplate += 'font-family:' + properties.copyrightFontFamily + ';';
    }
    if (properties.copyrightFontColor) {
        copyrightTemplate += 'color:' + properties.copyrightFontColor + ';';
    }
    if (properties.copyrightFontSize) {
        copyrightTemplate += 'font-size:' + properties.copyrightFontSize + 'px;';
    }
    if (properties.copyrightBold) {
        copyrightTemplate += 'font-weight: bold;';
    }
    if (properties.copyrightItalics) {
        copyrightTemplate += 'font-style: italic;';
    }
    if (properties.copyrightUnderline) {
        copyrightTemplate += 'text-decoration: underline;';
    }

    copyrightTemplate += '">' + properties.copyright + '</span>';

    $(target).find('#content').append(
        $('<div>')
            .addClass('item')
            .append(copyrightTemplate)
    );
}

// Add separator
if (
    (
        properties.effect == 'marqueeLeft' ||
        properties.effect == 'marqueeRight' ||
        properties.effect == 'marqueeUp' ||
        properties.effect == 'marqueeDown'
    ) && properties.showSeparator == 1 &&
    properties.separator != ''
) {
    var $separator = $(properties.separator);
    $separator.addClass('separator');
    $(target).find('.item').after($separator);
}

// Render
$(target).xiboLayoutScaler(properties);
$(target).xiboTextRender(properties, $(target).find('#content > *'));
$(target).find('img').xiboImageRender(properties);
        ]]></onTemplateRender>
        <onTemplateVisible><![CDATA[
// Start effects for this template
$(target).xiboLayoutAnimate(properties);
        ]]></onTemplateVisible>
    </template>
</templates>
