Bundled Templates :: Table Layout

Disclaimer: This extension depends on client-side javascript and attaches JQuery to documents that use it.

Reference

This extension re-arranges selected document sections (hierarchical headlines in Doxia) using a table of columns and rows.

The actual layout is created using a specified number of columns and rows and does gracefully fallback to the standard layout if client side javascript is not available.

Parameters:

Parameter Name Description
Input Parameters (set with the macro call)
"table-layout" Enables this extension and sets a regular expression matching the section titles that should be transformed into a tabs.

Note: Setting this property has no effect in case of "source" or "source-class" were specified with the macro call. This behaviour ensures that the parameter "tabbed-panel" can still be used with ordinary macro calls where a source or source-class was set.

This parameter expects a regular expression that is used to select what sections are placed inside the table layout by matching the given section titles (headlines).
By default the javascript looks for the first section that follows the include position and continues with all siblings (sections of the same level). Neither parent nor child sections are processed, therefore it's mostly safe to use the match all expression .* if all sub-sequent sections of the same nesting level should be formatted with table layout.

Examples:
  • "%{include|table-layout=.*|columns=2}" - Transforms all following sections (of the same level) to tabs.
  • "%{include|table-layout=.*(Example).*|columns=*}" - Formats all sections containing the word "Example" using a side by side view.
"columns" Specifies the number of columns to render (defaults to '*').

If this parameter is set to asterisk ("*") the number of columns is unbounded and calculates out of the number of sections divided by the defined rows. If both columns and rows is set to asterisk the number of columns equals the number of sections while the number of rows becomes "1".
"rows" Specifies the number of rows to render (defaults to '*').

If this parameter is set to asterisk ("*") the number of rows is unbounded and calculates out of the number of sections divided by the defined columns.
"direction" Defines the direction for placing the sections in the table layout (defaults to 'top-down').

Possible values:
  • top-down - Places sections below each other and wraps when "rows" number of sections were placed.
  • left-right or ltr - Places sections next to each other starting from left and wraps when "columns" number of sections were placed.
  • right-left or rtl - Places sections next to each other starting from right and wraps when "columns" number of sections were placed.
"gap" Defines the gab (margin) between the table cells (defaults to '10pt').

Note: The gap can also be defined for the horizontal or vertical direction using horizontal-gap and vertical-gap.
"width" Defines the width of the layout (defaults to '100%').
"style" Optional Parameter: Defines the CSS style of the container hosting the elements.

Example - Creates a right aligned floating box of all "Hints":
%{include|table-layout=.*(Hint).*|columns=1|width=200|style=border: 1px solid gray; background-color:#eee; float: right;}
"style-class" Optional Parameter: Defines the CSS style class of the container hosting the elements.
"cell-style" Optional Parameter: Defines the CSS style of the TD elements.
"column-widths" Optional Parameter: Specifies a comma separates list of width values (CSS format) to apply with the columns. The number of width values doesn't need to match the number of actual columns as missing values are interpolated.
"column-classes" Optional Parameter: Specifies a comma separates list of classes to apply with the columns. The number of classes doesn't need to match the number of actual columns as missing values are interpolated.
"row-heights" Optional Parameter: Specifies a comma separates list of height values (CSS format) to apply with the rows. The number of height values doesn't need to match the number of actual rows as missing values are interpolated.
"row-classes" Optional Parameter: Specifies a comma separates list of classes to apply with the rows. The number of classes doesn't need to match the number of actual rows as missing values are interpolated.
Implementation "org.tinyjee.maven.dim.extensions.TableLayoutParameterTransformer": ApiDoc | Source

Usage & Template Source

Examples

2 Columns

Default layout with 2 columns and custom border around the table cell:

%{include|table-layout=.*|columns=2|cell-style=border: 1px solid #ddd}
First Section

First section content.

Second Section

Second section content.

Third Section

Third section content.

LTR

Default layout with 2 columns and left to right ordering:

%{include|table-layout=.*|columns=2|direction=ltr}
First Section

First section content.

Second Section

Second section content.

Third Section

Third section content.

LTR

Default layout with 2 columns and right to left ordering:

%{include|table-layout=.*|columns=2|direction=rtl}
First Section

First section content.

Second Section

Second section content.

Third Section

Third section content.

Styling

%{include|table-layout=^Floating.*|width=200|style=float:left; background-color: #dfd; margin-right: 20pt;}

        Some text that floats around the floating box on the left.

%{include|table-layout=^(?!Floating)|rows=2|column-classes=bold,border,border bold|column-widths=,,100px|row-classes=,gray-box}
%{include|inline-css=.bold [ font-weight: bold; ] td.border [ border-left: 1px solid #ddd; ] .gray-box [ background-color: #bbb; ]}

Some text that floats around the floating box on the left.

First Section

First section content.

Second Section

Second section content.

Floating Box Section

Floating Box section content.

Third Section

Third section content.

Forth Section

Forth section content.

Fifth Section

Fifth section content.

Sixth Section

Sixth section content.

Page Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
    ------
    Bundled Templates :: Table Layout
    ------
    Juergen Kellerer
    ------
    2011-11-06
    ------
  
Bundled Templates :: Table Layout
  
%{include|source=disclaimers.xdoc.vm|clientSideJavaScript=JQuery}
  
* Reference
  
%{include|source=addon-interface-table.xdoc.vm|source-java=org.tinyjee.maven.dim.extensions.TableLayoutParameterTransformer}
  
* Usage & Template Source
  
%{include|tabbed-panel=.*}
  
* Examples
  
%{include|tabbed-panel=.*|min-height=400px}
  
** 2 Columns
  
    Default layout with 2 columns and custom border around the table cell:
  
----------------
%{include|table-layout=.*|columns=2|cell-style=border: 1px solid #ddd}
----------------
  
%{include|table-layout=.*|columns=2|cell-style=border: 1px solid #ddd}
  
  
*** First Section
  
    <First> section content.
  
*** Second Section
  
    <Second> section content.
  
*** Third Section
  
    <Third> section content.
  
  
** LTR
  
    Default layout with 2 columns and left to right ordering:
  
----------------
%{include|table-layout=.*|columns=2|direction=ltr}
----------------
  
%{include|table-layout=.*|columns=2|direction=ltr}
  
  
*** First Section
  
    <First> section content.
  
*** Second Section
  
    <Second> section content.
  
*** Third Section
  
    <Third> section content.
  
  
  
** LTR
  
    Default layout with 2 columns and right to left ordering:
  
----------------
%{include|table-layout=.*|columns=2|direction=rtl}
----------------
  
%{include|table-layout=.*|columns=2|direction=rtl}
  
  
*** First Section
  
    <First> section content.
  
*** Second Section
  
    <Second> section content.
  
*** Third Section
  
    <Third> section content.
  
  
  
** Styling
  
----------------
%{include|table-layout=^Floating.*|width=200|style=float:left; background-color: #dfd; margin-right: 20pt;}
  
    Some text that floats around the floating box on the left.
  
%{include|table-layout=^(?!Floating)|rows=2|column-classes=bold,border,border bold|column-widths=,,100px|row-classes=,gray-box}
%{include|inline-css=.bold [ font-weight: bold; ] td.border [ border-left: 1px solid #ddd; ] .gray-box [ background-color: #bbb; ]}
----------------
  
%{include|table-layout=^Floating.*|width=200|style=float:left; background-color: #dfd; margin-right: 20pt;}
  
    Some text that floats around the floating box on the left.
  
%{include|table-layout=^(?!Floating)|rows=2|column-classes=bold,border,border bold|column-widths=,,100px|row-classes=,gray-box}
  
%{include|inline-css=.bold [ font-weight: bold; ] td.border [ border-left: 1px solid #ddd; ] .gray-box [ background-color: #bbb; ]}
  
*** First Section
  
    <First> section content.
  
*** Second Section
  
    <Second> section content.
  
*** Floating Box Section
  
    <Floating Box> section content.
  
*** Third Section
  
    <Third> section content.
  
*** Forth Section
  
    <Forth> section content.
  
*** Fifth Section
  
    <Fifth> section content.
  
*** Sixth Section
  
    <Sixth> section content.
  
  
  
* Page Source
  
%{include|source=apt/templatesTableLayout.apt|source-content-type=text}
  
* Template Sources
  
%{include|tabbed-panel=.*}
  
** Velocity Template
  
%{include|source=classpath:/templates/dim/table-layout.html.vm|source-is-template=false}
  
** JS
  
%{include|tabbed-panel=.*}
  
*** table-layout.js
  
%{include|source=classpath:/templates/dim/js/table-layout.js}
  
*** section-finder.js
  
%{include|source=classpath:/templates/dim/js/section-finder.js}

Template Sources

Velocity Template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#*
    Implements a table layout manager using JQuery.
    When used, the javascript code looks after section titles that match the given regular expression and
    transforms them to elements placed inside the table layout grid.
  
    The lookup strategy for the first section title is:
    - Find a div of class "section" below the DOM tree whose section title matches the regular expression.
    - If no title matched, traverse up the DOM tree and find a section title that matches the regular expression.
  
    Once the first title is found, all matching siblings are selected as well.
  
    Notes:
    - When printing the document, the tabbed panel is hidden and all tags are shown.
    - The tabbed panel is not created at all when used with a non HTML sink.
  
    "sections"     RegExp    A regular expression that is used to select the section titles to include.
*#
#dim_attachJs(["jquery", "dim/js/section-finder", "dim/js/table-layout"], $jsSuccess)
  
#if($jsSuccess)
    #if (!$sections || $sections == "*") #set($sections = ".*") #end
    #if (!$columns) #set($columns = "*") #end
    #if (!$rows) #set($rows = "*") #end
    #if (!$gap) #set($gap = "10pt") #end
    #if (!$horizontal-gap) #set($horizontal-gap = "0") #end
    #if (!$vertical-gap) #set($vertical-gap = "") #end
    #if (!$width) #set($width = "100%") #end
  
    #if (!$direction) #set($direction = "") #end
    #if (!$column-widths) #set($column-widths = "") #end
    #if (!$column-classes) #set($column-classes = "") #end
    #if (!$row-heights) #set($row-heights = "") #end
    #if (!$row-classes) #set($row-classes = "") #end
  
    #if (!$style) #set($style = "") #end
    #if (!$style-class) #set($style-class = "") #end
    #if (!$cell-style) #set($cell-style = "") #end
  
    #set($layoutId = "table-layout-" + $globals.nextLocalId())
  
<style type="text/css">
    table.table-layout-table, tr.table-layout-table, td.table-layout-table {
        border: 0; padding: 0; margin: 0; width: auto;
    }
  
    #$layoutId {
        width: $width; $style
    }
  
    #$layoutId table.table-layout-table {
        border-collapse: collapse;
        border-spacing: 0;
        width: $width;
    }
  
    #$layoutId td.table-layout-table {
        vertical-align: top;
        $cell-style
    }
</style>
<div id="$layoutId" class="${style-class}">
    <table class="table-layout-table"></table>
    <script type="text/javascript" language="javascript">
        $(function() {
            var layoutElement = $("#$layoutId");
            var selectTitleCallback = function(title) {
                return /${sections}/gi.test(title);
            };
            var configuration = {
                columnWidths: "$column-widths",
                columnClasses: "$column-classes",
                rowHeights: "$row-heights",
                rowClasses: "$row-classes",
                columns: "$columns",
                rows: "$rows",
                gap: "$gap",
                gapHorizontal: "$horizontal-gap",
                gapVertical: "$vertical-gap",
                direction: "$direction"
            };
  
            jsDim.createTableLayout(layoutElement, selectTitleCallback, configuration);
        });
    </script>
</div>
#end

JS

table-layout.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*
 * Copyright 2011 - Doxia :: Include Macro - Juergen Kellerer
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
  
  
/**
 * Creates the method "window.jsDim.createTableLayout(layoutContainerId, selectSectionTitleCallback, configuration)"
 */
(function(window) {
    /**
     * Creates a new table layout for the given container.
     */
    function createTableLayout(layoutElement, selectTitleCallback, configuration) {
        layoutElement = $(layoutElement);
        var sectionContents = jsDim.protectSections(jsDim.findSections(layoutElement, selectTitleCallback));
  
        var columns = Math.ceil(Math.max(1, configuration.columns == "*" ?
                sectionContents.length / (configuration.rows == "*" ? 1 : parseInt(configuration.rows)) :
                configuration.columns));
        var rows = Math.ceil(configuration.rows == "*" ? sectionContents.length / columns : configuration.rows);
  
        var verticalGap = calculateGap(configuration.gapVertical, calculateGap(configuration.gap, "5pt")),
                horizontalGap = calculateGap(configuration.gapHorizontal, calculateGap(configuration.gap, "5pt"));
  
        var layout = [];
        if (configuration.direction == "rtl" || configuration.direction == "ltr") {
            for (var y = 0, i = 0; i < sectionContents.length; i += columns) {
                layout[y] = [];
                for (var index, x = 0; x < columns; x++) {
                    index = i + (configuration.direction == "ltr" ? x : columns - x - 1);
                    layout[y][x] = index < sectionContents.length ? sectionContents[index] : null;
                }
                y++;
            }
        } else {
            for (var x = 0, i = 0; i < sectionContents.length; i += rows) {
                for (var y = 0; y < rows; y++) {
                    if (!layout[y]) layout[y] = [];
                    layout[y][x] = i + y < sectionContents.length ? sectionContents[i + y] : null;
                }
                x++;
            }
        }
  
        var columnWidths = interpolate(configuration.columnWidths ? configuration.columnWidths : [(100 / columns) + "%"], columns);
        var columnClasses = interpolate(configuration.columnClasses, columns);
        var rowHeights = interpolate(configuration.rowHeights, rows);
        var rowClasses = interpolate(configuration.rowClasses, rows);
  
        var table = layoutElement.find("table").first();
        for (var y = 0; y < layout.length; y++) {
            var tr = $("<tr class='table-layout-table'></tr>").appendTo(table);
            if (rowHeights[y]) tr.css("height", rowHeights[y]);
            if (rowClasses[y]) tr.addClass(rowClasses[y]);
  
            for (var content, x = 0, len = layout[y].length; x < len; x++) {
                if (!(content = layout[y][x])) continue;
  
                var width = (100 / columns) + "%";
                var td = $("<td class='table-layout-table'></td>").appendTo(tr).css("width", columnWidths[x]);
                td.css("paddingTop", y == 0 ? 0 : horizontalGap).css("paddingLeft", x == 0 ? 0 : verticalGap);
                td.css("paddingBottom", y == layout.length - 1 ? 0 : horizontalGap).css("paddingRight", x == len - 1 ? 0 : verticalGap);
  
                if (columnClasses[x]) td.addClass(columnClasses[x]);
                td.append(content.detach());
  
                // The last cell in a column can span over all remaining rows.
                var emptyCellsIndex = y + 1;
                for (; emptyCellsIndex < layout.length && !layout[emptyCellsIndex][x]; emptyCellsIndex++);
                if (emptyCellsIndex - y > 1) td.attr("rowSpan", emptyCellsIndex - y);
            }
        }
    }
  
    function calculateGap(inputGap, defaultGap) {
        if (/^([0-9]+)(.*)(;|$)/.test(inputGap)) return (parseInt(RegExp.$1) / 2) + RegExp.$2;
        return defaultGap;
    }
  
    function interpolate(input, newLength) {
        if (!(input instanceof Array)) input = ("" + input).split(/\s*,\s*/);
        for (var result = input; result.length < newLength; result = result.concat(input));
        return result;
    }
  
    // Register in "window.jsDim"
    if (!window.jsDim) window.jsDim = {};
    window.jsDim.createTableLayout = createTableLayout;
})(window);
section-finder.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/*
 * Copyright 2011 - Doxia :: Include Macro - Juergen Kellerer
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
  
  
/**
 * Creates the method "window.jsDim.findSections(jQuery, selectSectionTitleCallback)"
 */
(function(window) {
    /**
     * Creates a new tabbed panel.
     *
     * @param startElement An arbitrary element to start with the search for sections.
     * @param selectSectionTitleCallback A callback of the format <code>function(title) { return true/false; }</code>
     * @return An array of jQuery objects containing the matching sections.
     */
    function findSections(startElement, selectSectionTitleCallback) {
        var theSectionWeAreIn = $(startElement).closest("div.section");
  
        var indexedSections = [], navigationType = 0, sections;
        while (indexedSections.length == 0) {
            switch (navigationType++) {
                case 0:
                    // Find the first section below this and select siblings.
                    sections = theSectionWeAreIn.find("div.section").not(".protected").first().nextAll("div.section").andSelf();
                    break;
                case 1:
                    // Looking for the section we're currently in and select siblings.
                    sections = theSectionWeAreIn.nextAll("div.section").not(".protected");
                    break;
                default:
                    return [];
            }
  
            var index = 0;
            sections.each(function() {
                var tabTitle = $(this).find("h1,h2,h3,h4,h5,h6").first().text();
                if (selectSectionTitleCallback(tabTitle))
                    indexedSections[index++] = $(this);
            });
        }
  
        return indexedSections;
    }
  
    /**
     * Protects the given sections from being selected again.
     * @param sections the sections to protect.
     * @return the given list of sections after protecting them.
     */
    function protectSections(sections) {
        $.each(sections, function(index, section) {
            $(section).addClass("protected");
        });
        return sections;
    }
  
    // Register in "window.jsDim"
    if (!window.jsDim) window.jsDim = {};
    window.jsDim.findSections = findSections;
    window.jsDim.protectSections = protectSections;
})(window);