├── img ├── .gitignore ├── bugs.png ├── ascii1.png ├── ascii2.png ├── ascii3.png ├── ascii4.png ├── ascii5.png ├── ascii6.png ├── github.png ├── unicode1.png ├── unicode2.png ├── unicode3.png ├── unicode4.png ├── unicode5.png └── unicode6.png ├── user_manual ├── table_example.png └── README.md ├── .gitignore ├── css └── main.css ├── vendor ├── polyfills │ └── console.js └── boilerplate │ └── main.css ├── LICENSE.md ├── README.md ├── 404.html ├── index.html └── js └── main.js /img/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/bugs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/bugs.png -------------------------------------------------------------------------------- /img/ascii1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/ascii1.png -------------------------------------------------------------------------------- /img/ascii2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/ascii2.png -------------------------------------------------------------------------------- /img/ascii3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/ascii3.png -------------------------------------------------------------------------------- /img/ascii4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/ascii4.png -------------------------------------------------------------------------------- /img/ascii5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/ascii5.png -------------------------------------------------------------------------------- /img/ascii6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/ascii6.png -------------------------------------------------------------------------------- /img/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/github.png -------------------------------------------------------------------------------- /img/unicode1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/unicode1.png -------------------------------------------------------------------------------- /img/unicode2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/unicode2.png -------------------------------------------------------------------------------- /img/unicode3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/unicode3.png -------------------------------------------------------------------------------- /img/unicode4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/unicode4.png -------------------------------------------------------------------------------- /img/unicode5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/unicode5.png -------------------------------------------------------------------------------- /img/unicode6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/img/unicode6.png -------------------------------------------------------------------------------- /user_manual/table_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlainTextTools/plain-text-table/HEAD/user_manual/table_example.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Include your project-specific ignores in this file 2 | # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files 3 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | .navbar-nav a img { 2 | width: 20px; 3 | height: 20px; 4 | } 5 | #gen-trigger { 6 | margin-bottom: 10px; 7 | } 8 | .footer-table th, .footer-table td{ 9 | width: 33%; 10 | } 11 | 12 | .highlighted { 13 | color: red; 14 | background-color: yellow; 15 | } 16 | 17 | #predefined p { 18 | text-align: center; 19 | } -------------------------------------------------------------------------------- /vendor/polyfills/console.js: -------------------------------------------------------------------------------- 1 | // Avoid `console` errors in browsers that lack a console. 2 | (function() { 3 | var method; 4 | var noop = function () {}; 5 | var methods = [ 6 | 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 7 | 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 8 | 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 9 | 'timeStamp', 'trace', 'warn' 10 | ]; 11 | var length = methods.length; 12 | var console = (window.console = window.console || {}); 13 | 14 | while (length--) { 15 | method = methods[length]; 16 | 17 | // Only stub undefined methods. 18 | if (!console[method]) { 19 | console[method] = noop; 20 | } 21 | } 22 | }()); 23 | 24 | // Place any jQuery/helper plugins in here. 25 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) HTML5 Boilerplate 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Plain Text Table 2 | 3 | Interactively create and edit tables and export them to plain text. 4 | 5 | ## Use the tool online 6 | 7 | Please visit : http://plaintexttools.github.io/plain-text-table 8 | 9 | ## Features 10 | 11 | * Interactive input table (using [handsontable](http://handsontable.com/)) 12 | * Unicode or ASCII output 13 | * Flexible border configuration 14 | * Merged cell support (colspan and rowspan) 15 | * Multiline text in the cells 16 | * Text alignement support (horizontal and vertical) 17 | * Predefined style 18 | 19 | All configuration options explained in the [user manual](user_manual/README.md). 20 | 21 | ## Output examples 22 | 23 | Unicode characters with multiline and merged cells: 24 | 25 | ╔════════╦══════╤═══════════╤═════════╤═══════════╤═══════════╤═════════════╗ 26 | ║ Who? ║ Code │ Monday │ Tuesday │ Wednesday │ Thursday │ Friday ║ 27 | ╠════════╬══════╪═══════════╧═════════╧═══════════╪═══════════╪═════════════╣ 28 | ║ Team A ║ 23 │ Proin id nunc │ Fringilla │ Lorem ║ 29 | ║ ║ │ │ │ Ipsum ║ 30 | ╟────────╫──────┼───────────┬─────────┬───────────┴───────────┼─────────────╢ 31 | ║ ║ │ │ Ante │ │ ║ 32 | ║ Team B ║ 4 │ Fermentum │ Ipisum │ Amet │ Lyks ║ 33 | ║ ║ │ │ Primis │ │ ║ 34 | ╟────────╫──────┼───────────┼─────────┼───────────┬───────────┼─────────────╢ 35 | ║ Team C ║ 52 │ Metus ex │ Dxow │ Malesuada │ │ Ullamcorper ║ 36 | ╟────────╫──────┼───────────┴─────────┼───────────┤ Vulputate ├─────────────╢ 37 | ║ Team D ║ 19 │ Ornare │ Tincidunt │ │ Rwe ║ 38 | ╚════════╩══════╧═════════════════════╧═══════════╧═══════════╧═════════════╝ 39 | 40 | Ascii characters with spreadsheet headers: 41 | 42 | +===+========+===========+=======+=====+ 43 | | | A | B | C | D | 44 | +===+========+===========+=======+=====+ 45 | | 1 | Alice | Johnson | | 293 | 46 | +---+--------+-----------+-------+-----+ 47 | | 2 | Bob | Smith | ????? | 2 | 48 | +---+--------+-----------+-------+-----+ 49 | | 3 | Carrie | Sheffield | ? | 42 | 50 | +===+========+===========+=======+=====+ 51 | 52 | ## Get in touch / bug tracker 53 | 54 | Use the [plain-text-table issue tracker](https://github.com/PlainTextTools/plain-text-table/issues) on GitHub. 55 | 56 | ## License 57 | 58 | [HTML5 Boilerplate](LICENSE.md) 59 | -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /vendor/boilerplate/main.css: -------------------------------------------------------------------------------- 1 | /*! HTML5 Boilerplate v4.3.0 | MIT License | http://h5bp.com/ */ 2 | 3 | /* 4 | * What follows is the result of much research on cross-browser styling. 5 | * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, 6 | * Kroc Camen, and the H5BP dev community and team. 7 | */ 8 | 9 | /* ========================================================================== 10 | Base styles: opinionated defaults 11 | ========================================================================== */ 12 | 13 | html, 14 | button, 15 | input, 16 | select, 17 | textarea { 18 | color: #222; 19 | } 20 | 21 | html { 22 | font-size: 1em; 23 | line-height: 1.4; 24 | } 25 | 26 | /* 27 | * Remove text-shadow in selection highlight: h5bp.com/i 28 | * These selection rule sets have to be separate. 29 | * Customize the background color to match your design. 30 | */ 31 | 32 | ::-moz-selection { 33 | background: #b3d4fc; 34 | text-shadow: none; 35 | } 36 | 37 | ::selection { 38 | background: #b3d4fc; 39 | text-shadow: none; 40 | } 41 | 42 | /* 43 | * A better looking default horizontal rule 44 | */ 45 | 46 | hr { 47 | display: block; 48 | height: 1px; 49 | border: 0; 50 | border-top: 1px solid #ccc; 51 | margin: 1em 0; 52 | padding: 0; 53 | } 54 | 55 | /* 56 | * Remove the gap between images, videos, audio and canvas and the bottom of 57 | * their containers: h5bp.com/i/440 58 | */ 59 | 60 | audio, 61 | canvas, 62 | img, 63 | video { 64 | vertical-align: middle; 65 | } 66 | 67 | /* 68 | * Remove default fieldset styles. 69 | */ 70 | 71 | fieldset { 72 | border: 0; 73 | margin: 0; 74 | padding: 0; 75 | } 76 | 77 | /* 78 | * Allow only vertical resizing of textareas. 79 | */ 80 | 81 | textarea { 82 | resize: vertical; 83 | } 84 | 85 | /* ========================================================================== 86 | Browse Happy prompt 87 | ========================================================================== */ 88 | 89 | .browsehappy { 90 | margin: 0.2em 0; 91 | background: #ccc; 92 | color: #000; 93 | padding: 0.2em 0; 94 | } 95 | 96 | /* ========================================================================== 97 | Author's custom styles 98 | ========================================================================== */ 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | /* ========================================================================== 117 | Helper classes 118 | ========================================================================== */ 119 | 120 | /* 121 | * Image replacement 122 | */ 123 | 124 | .ir { 125 | background-color: transparent; 126 | border: 0; 127 | overflow: hidden; 128 | /* IE 6/7 fallback */ 129 | *text-indent: -9999px; 130 | } 131 | 132 | .ir:before { 133 | content: ""; 134 | display: block; 135 | width: 0; 136 | height: 150%; 137 | } 138 | 139 | /* 140 | * Hide from both screenreaders and browsers: h5bp.com/u 141 | */ 142 | 143 | .hidden { 144 | display: none !important; 145 | visibility: hidden; 146 | } 147 | 148 | /* 149 | * Hide only visually, but have it available for screenreaders: h5bp.com/v 150 | */ 151 | 152 | .visuallyhidden { 153 | border: 0; 154 | clip: rect(0 0 0 0); 155 | height: 1px; 156 | margin: -1px; 157 | overflow: hidden; 158 | padding: 0; 159 | position: absolute; 160 | width: 1px; 161 | } 162 | 163 | /* 164 | * Extends the .visuallyhidden class to allow the element to be focusable 165 | * when navigated to via the keyboard: h5bp.com/p 166 | */ 167 | 168 | .visuallyhidden.focusable:active, 169 | .visuallyhidden.focusable:focus { 170 | clip: auto; 171 | height: auto; 172 | margin: 0; 173 | overflow: visible; 174 | position: static; 175 | width: auto; 176 | } 177 | 178 | /* 179 | * Hide visually and from screenreaders, but maintain layout 180 | */ 181 | 182 | .invisible { 183 | visibility: hidden; 184 | } 185 | 186 | /* 187 | * Clearfix: contain floats 188 | * 189 | * For modern browsers 190 | * 1. The space content is one way to avoid an Opera bug when the 191 | * `contenteditable` attribute is included anywhere else in the document. 192 | * Otherwise it causes space to appear at the top and bottom of elements 193 | * that receive the `clearfix` class. 194 | * 2. The use of `table` rather than `block` is only necessary if using 195 | * `:before` to contain the top-margins of child elements. 196 | */ 197 | 198 | .clearfix:before, 199 | .clearfix:after { 200 | content: " "; /* 1 */ 201 | display: table; /* 2 */ 202 | } 203 | 204 | .clearfix:after { 205 | clear: both; 206 | } 207 | 208 | /* 209 | * For IE 6/7 only 210 | * Include this rule to trigger hasLayout and contain floats. 211 | */ 212 | 213 | .clearfix { 214 | *zoom: 1; 215 | } 216 | 217 | /* ========================================================================== 218 | EXAMPLE Media Queries for Responsive Design. 219 | These examples override the primary ('mobile first') styles. 220 | Modify as content requires. 221 | ========================================================================== */ 222 | 223 | @media only screen and (min-width: 35em) { 224 | /* Style adjustments for viewports that meet the condition */ 225 | } 226 | 227 | @media print, 228 | (-o-min-device-pixel-ratio: 5/4), 229 | (-webkit-min-device-pixel-ratio: 1.25), 230 | (min-resolution: 120dpi) { 231 | /* Style adjustments for high resolution devices */ 232 | } 233 | 234 | /* ========================================================================== 235 | Print styles. 236 | Inlined to avoid required HTTP connection: h5bp.com/r 237 | ========================================================================== */ 238 | 239 | @media print { 240 | * { 241 | background: transparent !important; 242 | color: #000 !important; /* Black prints faster: h5bp.com/s */ 243 | box-shadow: none !important; 244 | text-shadow: none !important; 245 | } 246 | 247 | a, 248 | a:visited { 249 | text-decoration: underline; 250 | } 251 | 252 | a[href]:after { 253 | content: " (" attr(href) ")"; 254 | } 255 | 256 | abbr[title]:after { 257 | content: " (" attr(title) ")"; 258 | } 259 | 260 | /* 261 | * Don't show links for images, or javascript/internal links 262 | */ 263 | 264 | .ir a:after, 265 | a[href^="javascript:"]:after, 266 | a[href^="#"]:after { 267 | content: ""; 268 | } 269 | 270 | pre, 271 | blockquote { 272 | border: 1px solid #999; 273 | page-break-inside: avoid; 274 | } 275 | 276 | thead { 277 | display: table-header-group; /* h5bp.com/t */ 278 | } 279 | 280 | tr, 281 | img { 282 | page-break-inside: avoid; 283 | } 284 | 285 | img { 286 | max-width: 100% !important; 287 | } 288 | 289 | @page { 290 | margin: 0.5cm; 291 | } 292 | 293 | p, 294 | h2, 295 | h3 { 296 | orphans: 3; 297 | widows: 3; 298 | } 299 | 300 | h2, 301 | h3 { 302 | page-break-after: avoid; 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /user_manual/README.md: -------------------------------------------------------------------------------- 1 | # Plain Text Table - User Manual 2 | 3 | Interactively create and edit tables and export them to plain text. 4 | The tool can be used [online](http://lorefnon.me/plain-text-table). 5 | 6 | ## Edit the table 7 | 8 | Edit the grid and fill it with the the data you want to represent as text. 9 | 10 | ![An example table filled with data](table_example.png) 11 | 12 | With the context menu in each cell, the text alignment (vertical and horizontal) can be set. 13 | Those properties are reflected in the output. 14 | 15 | Merged cells are supported (colspan and rowspan). 16 | Select the cells you want to merge, use the context menu and select `Merge cells`. 17 | Merged cells can be unmerged the same way (with the menu item `Unmerge cells`) 18 | 19 | ## Charset option 20 | 21 | * Unicode 22 | * Ascii 23 | 24 | ## Headers option 25 | 26 | ### Horizontal Header: 27 | 28 | Type of column headers. 29 | 30 | * __None__: No header. The first line is directly the content. 31 | * __First line__: The first line of the table is used as header. 32 | * __Number__: number are used as column header. 33 | * __Letter__: letters are used as column header (A, B, C.. - like in Excel). 34 | 35 | 36 | ### Vertical Header: 37 | 38 | Type of line headers. 39 | 40 | * __None__: No header. The first column is directly the content. 41 | * __First column__: The first column of the table is used as header. 42 | * __Number__: number are used as line header (1, 2, 3.. - like in Excel). 43 | * __Letter__: letters are used as line header. 44 | 45 | ## Borders option 46 | 47 | For each border you can select: 48 | 49 | * __None__: no border at all 50 | * __Single__: single border 51 | * __Double__: double border 52 | 53 | Depending on the char set (Unicode or ASCII) some combinations are not possible. 54 | 55 | To help you to understand which border your are modifying with one combo box, the border corresponding to the selected combo box is highlighted in the output. 56 | To remove the highlighting just focus somewhere else. 57 | 58 | ### Horizontal Top Border 59 | 60 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 61 | ║ ║ Name │ First Name │ Track Id │ Checked ║ 62 | ╠═══╬═══════════╪════════════╪══════════╪═════════╣ 63 | ║ 1 ║ Sheffield │ Alice │ 347 │ yes ║ 64 | ╟───╫───────────┼────────────┼──────────┼─────────╢ 65 | ║ ║ │ Carrie, │ │ ║ 66 | ║ 2 ║ Smith │ Bob and │ 152 │ ║ 67 | ║ ║ │ Friends │ │ ║ 68 | ╟───╫───────────┼────────────┼──────────┼─────────╢ 69 | ║ 3 ║ Samson │ Dick │ 948 │ yes ║ 70 | ╚═══╩═══════════╧════════════╧══════════╧═════════╝ 71 | 72 | ### Horizontal Inner Header Border 73 | 74 | ╔═══╦═══════════╤════════════╤══════════╤═════════╗ 75 | ║ ║ Name │ First Name │ Track Id │ Checked ║ 76 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 77 | ║ 1 ║ Sheffield │ Alice │ 347 │ yes ║ 78 | ╟───╫───────────┼────────────┼──────────┼─────────╢ 79 | ║ ║ │ Carrie, │ │ ║ 80 | ║ 2 ║ Smith │ Bob and │ 152 │ ║ 81 | ║ ║ │ Friends │ │ ║ 82 | ╟───╫───────────┼────────────┼──────────┼─────────╢ 83 | ║ 3 ║ Samson │ Dick │ 948 │ yes ║ 84 | ╚═══╩═══════════╧════════════╧══════════╧═════════╝ 85 | 86 | This border is only present if column headers are defined (`horizontal header` set to `first line`, `number` or `letter`). 87 | 88 | ### Horizontal Inner Border 89 | 90 | ╔═══╦═══════════╤════════════╤══════════╤═════════╗ 91 | ║ ║ Name │ First Name │ Track Id │ Checked ║ 92 | ╠═══╬═══════════╪════════════╪══════════╪═════════╣ 93 | ║ 1 ║ Sheffield │ Alice │ 347 │ yes ║ 94 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 95 | ║ ║ │ Carrie, │ │ ║ 96 | ║ 2 ║ Smith │ Bob and │ 152 │ ║ 97 | ║ ║ │ Friends │ │ ║ 98 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 99 | ║ 3 ║ Samson │ Dick │ 948 │ yes ║ 100 | ╚═══╩═══════════╧════════════╧══════════╧═════════╝ 101 | 102 | ### Horizontal Bottom Border 103 | 104 | ╔═══╦═══════════╤════════════╤══════════╤═════════╗ 105 | ║ ║ Name │ First Name │ Track Id │ Checked ║ 106 | ╠═══╬═══════════╪════════════╪══════════╪═════════╣ 107 | ║ 1 ║ Sheffield │ Alice │ 347 │ yes ║ 108 | ╟───╫───────────┼────────────┼──────────┼─────────╢ 109 | ║ ║ │ Carrie, │ │ ║ 110 | ║ 2 ║ Smith │ Bob and │ 152 │ ║ 111 | ║ ║ │ Friends │ │ ║ 112 | ╟───╫───────────┼────────────┼──────────┼─────────╢ 113 | ║ 3 ║ Samson │ Dick │ 948 │ yes ║ 114 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 115 | 116 | ### Vertical Left Border 117 | 118 | X═══╦═══════════╤════════════╤══════════╤═════════╗ 119 | X ║ Name │ First Name │ Track Id │ Checked ║ 120 | X═══╬═══════════╪════════════╪══════════╪═════════╣ 121 | X 1 ║ Sheffield │ Alice │ 347 │ yes ║ 122 | X───╫───────────┼────────────┼──────────┼─────────╢ 123 | X ║ │ Carrie, │ │ ║ 124 | X 2 ║ Smith │ Bob and │ 152 │ ║ 125 | X ║ │ Friends │ │ ║ 126 | X───╫───────────┼────────────┼──────────┼─────────╢ 127 | X 3 ║ Samson │ Dick │ 948 │ yes ║ 128 | X═══╩═══════════╧════════════╧══════════╧═════════╝ 129 | 130 | ### Vertical Inner Header Border 131 | 132 | ╔═══X═══════════╤════════════╤══════════╤═════════╗ 133 | ║ X Name │ First Name │ Track Id │ Checked ║ 134 | ╠═══X═══════════╪════════════╪══════════╪═════════╣ 135 | ║ 1 X Sheffield │ Alice │ 347 │ yes ║ 136 | ╟───X───────────┼────────────┼──────────┼─────────╢ 137 | ║ X │ Carrie, │ │ ║ 138 | ║ 2 X Smith │ Bob and │ 152 │ ║ 139 | ║ X │ Friends │ │ ║ 140 | ╟───X───────────┼────────────┼──────────┼─────────╢ 141 | ║ 3 X Samson │ Dick │ 948 │ yes ║ 142 | ╚═══X═══════════╧════════════╧══════════╧═════════╝ 143 | 144 | This border is only present if line headers are defined (`vertical header` set to `first column`, `number` or `letter`). 145 | 146 | ### Vertical Inner Border 147 | 148 | ╔═══╦═══════════X════════════X══════════X═════════╗ 149 | ║ ║ Name X First Name X Track Id X Checked ║ 150 | ╠═══╬═══════════X════════════X══════════X═════════╣ 151 | ║ 1 ║ Sheffield X Alice X 347 X yes ║ 152 | ╟───╫───────────X────────────X──────────X─────────╢ 153 | ║ ║ X Carrie, X X ║ 154 | ║ 2 ║ Smith X Bob and X 152 X ║ 155 | ║ ║ X Friends X X ║ 156 | ╟───╫───────────X────────────X──────────X─────────╢ 157 | ║ 3 ║ Samson X Dick X 948 X yes ║ 158 | ╚═══╩═══════════X════════════X══════════X═════════╝ 159 | 160 | ### Vertical Bottom Border 161 | 162 | ╔═══╦═══════════╤════════════╤══════════╤═════════X 163 | ║ ║ Name │ First Name │ Track Id │ Checked X 164 | ╠═══╬═══════════╪════════════╪══════════╪═════════X 165 | ║ 1 ║ Sheffield │ Alice │ 347 │ yes X 166 | ╟───╫───────────┼────────────┼──────────┼─────────X 167 | ║ ║ │ Carrie, │ │ X 168 | ║ 2 ║ Smith │ Bob and │ 152 │ X 169 | ║ ║ │ Friends │ │ X 170 | ╟───╫───────────┼────────────┼──────────┼─────────X 171 | ║ 3 ║ Samson │ Dick │ 948 │ yes X 172 | ╚═══╩═══════════╧════════════╧══════════╧═════════X 173 | 174 | ## ASCII intersection character 175 | 176 | When the charset is set to `Ascii`, this option allows to configure what the character at the intersection of two borders will be. 177 | 178 | __Plus__ (default): 179 | 180 | +====+========+ 181 | | Id | Name | 182 | +====+========+ 183 | | 1 | Alice | 184 | +----+--------+ 185 | | 2 | Bob | 186 | +----+--------+ 187 | | 3 | Carrie | 188 | +====+========+ 189 | 190 | __Horizontal border__: 191 | 192 | =============== 193 | | Id | Name | 194 | =============== 195 | | 1 | Alice | 196 | --------------- 197 | | 2 | Bob | 198 | --------------- 199 | | 3 | Carrie | 200 | =============== 201 | 202 | __Vertical border__: 203 | 204 | |====|========| 205 | | Id | Name | 206 | |====|========| 207 | | 1 | Alice | 208 | |----|--------| 209 | | 2 | Bob | 210 | |----|--------| 211 | | 3 | Carrie | 212 | |====|========| 213 | 214 | ## Cell padding 215 | 216 | There is a checkbox to configure if additional spaces should be added to ensure a cell padding of one space in each cell. 217 | 218 | __Checked__ (default): 219 | 220 | ┌────┬────────┐ 221 | │ Id │ Name │ 222 | ├────┼────────┤ 223 | │ 1 │ Alice │ 224 | │ 2 │ Bob │ 225 | │ 3 │ Carrie │ 226 | └────┴────────┘ 227 | 228 | __Not checked__: 229 | 230 | ┌──┬──────┐ 231 | │Id│Name │ 232 | ├──┼──────┤ 233 | │1 │Alice │ 234 | ├──┼──────┤ 235 | │2 │Bob │ 236 | ├──┼──────┤ 237 | │3 │Carrie│ 238 | └──┴──────┘ 239 | 240 | ## Predefined style 241 | 242 | In order to help you with the configuration of the tool, you can select one of the predefined styles. 243 | Just click on the image corresponding to the style you want to apply. 244 | 245 | ## Output 246 | 247 | The output is displayed right below the table. -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Plain Text Table 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 52 | 53 |
54 |
55 |

Plain Text Table

56 |

Interactively create and edit tables and export them to plain text

57 |
58 | 59 |

Huh ?

60 | 61 |

62 | At a lot of places, like Stackoverflow questions/answers, source code 63 | comments, plain text emails etc. HTML tables are not an option. We can 64 | always format ascii characters to simulate tables but unless you are a 65 | ninja with keyboard, doing so is a headache. 66 |

67 |

68 | This web based tool provides a dead simple way for doing just that - sans 69 | the PITA. 70 |

71 | 72 |
73 |

Your table

74 |

Click to edit cell, Right click for options

75 |
76 |
77 |
78 |

 79 |             
80 |
81 |

Characters:

85 |
86 |

Horizontal Header:

92 |

Vertical Header:

98 |
99 |

Horizontal Top Border:

104 |

Horizontal Inner Header Border:

109 |

Horizontal Inner Border:

114 |

Horizontal Bottom Border:

119 |
120 |

Vertical Left Border:

125 |

Vertical Inner Header Border:

130 |

Vertical Inner Border:

135 |

Vertical Right Border:

140 |
141 |

ASCII intersection character:

146 |

Ensure a padding of one space in each cell.

147 |
148 |
149 |

Click on the image to select one of the predefined style:

150 |
151 |
152 |

153 |

154 |

155 |

156 |

157 |

158 |

159 |
160 |

161 |

162 |

163 |

164 |

165 |

166 |

167 |
168 |
169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 183 | 188 | 193 | 194 | 195 |
196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | 4 | function createTable() { 5 | $("#table-wrapper").handsontable({ 6 | colHeaders: false, 7 | contextMenu: true, 8 | mergeCells: true, 9 | afterRender: genPTT 10 | }); 11 | } 12 | 13 | createTable(); 14 | 15 | updateAsciiIntersectionVisibility(document.getElementById("charset").value); 16 | updateHorizontalInnerHeaderBorderVisibility(document.getElementById("horizontal_header").value); 17 | updateVerticalInnerHeaderBorderVisibility(document.getElementById("vertical_header").value); 18 | })(); 19 | 20 | function charsetSelectChange(cbbox) { 21 | updateAsciiIntersectionVisibility(cbbox.value); 22 | generateTable(null); 23 | } 24 | 25 | function updateAsciiIntersectionVisibility(charsetValue) { 26 | if ('ascii' == charsetValue) { 27 | $('#p_ascii_intersection').show(); 28 | 29 | if ('double' == $('#vertical_left_border').val()) { 30 | $('#vertical_left_border').val('single'); 31 | } 32 | $("#vertical_left_border option[value='double']").remove(); 33 | 34 | if ('double' == $('#vertical_inner_header_border').val()) { 35 | $('#vertical_inner_header_border').val('single'); 36 | } 37 | $("#vertical_inner_header_border option[value='double']").remove(); 38 | 39 | if ('double' == $('#vertical_inner_border').val()) { 40 | $('#vertical_inner_border').val('single'); 41 | } 42 | $("#vertical_inner_border option[value='double']").remove(); 43 | 44 | if ('double' == $('#vertical_right_border').val()) { 45 | $('#vertical_right_border').val('single'); 46 | } 47 | $("#vertical_right_border option[value='double']").remove(); 48 | 49 | } else { 50 | $('#p_ascii_intersection').hide(); 51 | 52 | if ($("#vertical_left_border option[value='double']").length == 0) { 53 | $("#vertical_left_border").append(''); 54 | } 55 | if ($("#vertical_inner_header_border option[value='double']").length == 0) { 56 | $("#vertical_inner_header_border").append(''); 57 | } 58 | if ($("#vertical_inner_border option[value='double']").length == 0) { 59 | $("#vertical_inner_border").append(''); 60 | } 61 | if ($("#vertical_right_border option[value='double']").length == 0) { 62 | $("#vertical_right_border").append(''); 63 | } 64 | } 65 | } 66 | 67 | function horizontalHeaderSelectChange(cbbox) { 68 | updateHorizontalInnerHeaderBorderVisibility(cbbox.value); 69 | generateTable(null); 70 | } 71 | 72 | function updateHorizontalInnerHeaderBorderVisibility(horizontalHeaderValue) { 73 | if ('none' == horizontalHeaderValue) { 74 | $('#p_horizontal_inner_header_border').hide(); 75 | } else { 76 | $('#p_horizontal_inner_header_border').show(); 77 | } 78 | } 79 | 80 | function verticalHeaderSelectChange(cbbox) { 81 | updateVerticalInnerHeaderBorderVisibility(cbbox.value); 82 | generateTable(null); 83 | } 84 | 85 | function updateVerticalInnerHeaderBorderVisibility(verticalHeaderValue) { 86 | if ('none' == verticalHeaderValue) { 87 | $('#p_vertical_inner_header_border').hide(); 88 | } else { 89 | $('#p_vertical_inner_header_border').show(); 90 | } 91 | } 92 | 93 | function borderSelectGetFocus(cbbox) { 94 | generateTable(cbbox.name.substring(0, cbbox.name.length - 6)); 95 | } 96 | 97 | function borderSelectChange(cbbox) { 98 | generateTable(cbbox.name.substring(0, cbbox.name.length - 6)); 99 | } 100 | 101 | function borderSelectLostFocus(cbbox) { 102 | generateTable(null); 103 | } 104 | 105 | function predefinedStyle(value) { 106 | if ('unicode2' == value) { 107 | $('#charset').val('unicode'); 108 | $('#horizontal_header').val('first_line'); 109 | $('#vertical_header').val('first_column'); 110 | $('#horizontal_top_border').val('double'); 111 | $('#horizontal_inner_header_border').val('single'); 112 | $('#horizontal_inner_border').val('none'); 113 | $('#horizontal_bottom_border').val('double'); 114 | $('#vertical_left_border').val('double'); 115 | $('#vertical_inner_header_border').val('single'); 116 | $('#vertical_inner_border').val('none'); 117 | $('#vertical_right_border').val('double'); 118 | $('#ascii_intersection').val('plus'); 119 | $('#spacePadding').prop('checked', true); 120 | } else if ('unicode3' == value) { 121 | $('#charset').val('unicode'); 122 | $('#horizontal_header').val('first_line'); 123 | $('#vertical_header').val('first_column'); 124 | $('#horizontal_top_border').val('none'); 125 | $('#horizontal_inner_header_border').val('double'); 126 | $('#horizontal_inner_border').val('none'); 127 | $('#horizontal_bottom_border').val('none'); 128 | $('#vertical_left_border').val('none'); 129 | $('#vertical_inner_header_border').val('single'); 130 | $('#vertical_inner_border').val('none'); 131 | $('#vertical_right_border').val('none'); 132 | $('#ascii_intersection').val('plus'); 133 | $('#spacePadding').prop('checked', true); 134 | } else if ('unicode4' == value) { 135 | $('#charset').val('unicode'); 136 | $('#horizontal_header').val('letter'); 137 | $('#vertical_header').val('number'); 138 | $('#horizontal_top_border').val('double'); 139 | $('#horizontal_inner_header_border').val('double'); 140 | $('#horizontal_inner_border').val('single'); 141 | $('#horizontal_bottom_border').val('double'); 142 | $('#vertical_left_border').val('double'); 143 | $('#vertical_inner_header_border').val('double'); 144 | $('#vertical_inner_border').val('single'); 145 | $('#vertical_right_border').val('double'); 146 | $('#ascii_intersection').val('plus'); 147 | $('#spacePadding').prop('checked', true); 148 | } else if ('unicode5' == value) { 149 | $('#charset').val('unicode'); 150 | $('#horizontal_header').val('letter'); 151 | $('#vertical_header').val('number'); 152 | $('#horizontal_top_border').val('none'); 153 | $('#horizontal_inner_header_border').val('double'); 154 | $('#horizontal_inner_border').val('none'); 155 | $('#horizontal_bottom_border').val('none'); 156 | $('#vertical_left_border').val('none'); 157 | $('#vertical_inner_header_border').val('double'); 158 | $('#vertical_inner_border').val('single'); 159 | $('#vertical_right_border').val('none'); 160 | $('#ascii_intersection').val('plus'); 161 | $('#spacePadding').prop('checked', true); 162 | } else if ('unicode6' == value) { 163 | $('#charset').val('unicode'); 164 | $('#horizontal_header').val('first_line'); 165 | $('#vertical_header').val('letter'); 166 | $('#horizontal_top_border').val('none'); 167 | $('#horizontal_inner_header_border').val('single'); 168 | $('#horizontal_inner_border').val('none'); 169 | $('#horizontal_bottom_border').val('none'); 170 | $('#vertical_left_border').val('single'); 171 | $('#vertical_inner_header_border').val('single'); 172 | $('#vertical_inner_border').val('single'); 173 | $('#vertical_right_border').val('single'); 174 | $('#ascii_intersection').val('plus'); 175 | $('#spacePadding').prop('checked', true); 176 | } else if ('ascii1' == value) { 177 | $('#charset').val('ascii'); 178 | $('#horizontal_header').val('first_line'); 179 | $('#vertical_header').val('none'); 180 | $('#horizontal_top_border').val('double'); 181 | $('#horizontal_inner_header_border').val('double'); 182 | $('#horizontal_inner_border').val('single'); 183 | $('#horizontal_bottom_border').val('double'); 184 | $('#vertical_left_border').val('single'); 185 | $('#vertical_inner_header_border').val('single'); 186 | $('#vertical_inner_border').val('single'); 187 | $('#vertical_right_border').val('single'); 188 | $('#ascii_intersection').val('horizontal_border'); 189 | $('#spacePadding').prop('checked', true); 190 | } else if ('ascii2' == value) { 191 | $('#charset').val('ascii'); 192 | $('#horizontal_header').val('first_line'); 193 | $('#vertical_header').val('none'); 194 | $('#horizontal_top_border').val('single'); 195 | $('#horizontal_inner_header_border').val('single'); 196 | $('#horizontal_inner_border').val('none'); 197 | $('#horizontal_bottom_border').val('single'); 198 | $('#vertical_left_border').val('single'); 199 | $('#vertical_inner_header_border').val('single'); 200 | $('#vertical_inner_border').val('single'); 201 | $('#vertical_right_border').val('single'); 202 | $('#ascii_intersection').val('plus'); 203 | $('#spacePadding').prop('checked', true); 204 | } else if ('ascii3' == value) { 205 | $('#charset').val('ascii'); 206 | $('#horizontal_header').val('first_line'); 207 | $('#vertical_header').val('none'); 208 | $('#horizontal_top_border').val('none'); 209 | $('#horizontal_inner_header_border').val('single'); 210 | $('#horizontal_inner_border').val('none'); 211 | $('#horizontal_bottom_border').val('none'); 212 | $('#vertical_left_border').val('none'); 213 | $('#vertical_inner_header_border').val('none'); 214 | $('#vertical_inner_border').val('none'); 215 | $('#vertical_right_border').val('none'); 216 | $('#ascii_intersection').val('vertical_border'); 217 | $('#spacePadding').prop('checked', true); 218 | } else if ('ascii4' == value) { 219 | $('#charset').val('ascii'); 220 | $('#horizontal_header').val('first_line'); 221 | $('#vertical_header').val('none'); 222 | $('#horizontal_top_border').val('none'); 223 | $('#horizontal_inner_header_border').val('single'); 224 | $('#horizontal_inner_border').val('none'); 225 | $('#horizontal_bottom_border').val('none'); 226 | $('#vertical_left_border').val('single'); 227 | $('#vertical_inner_header_border').val('single'); 228 | $('#vertical_inner_border').val('single'); 229 | $('#vertical_right_border').val('single'); 230 | $('#ascii_intersection').val('vertical_border'); 231 | $('#spacePadding').prop('checked', true); 232 | } else if ('ascii5' == value) { 233 | $('#charset').val('ascii'); 234 | $('#horizontal_header').val('letter'); 235 | $('#vertical_header').val('number'); 236 | $('#horizontal_top_border').val('none'); 237 | $('#horizontal_inner_header_border').val('single'); 238 | $('#horizontal_inner_border').val('none'); 239 | $('#horizontal_bottom_border').val('none'); 240 | $('#vertical_left_border').val('none'); 241 | $('#vertical_inner_header_border').val('single'); 242 | $('#vertical_inner_border').val('none'); 243 | $('#vertical_right_border').val('none'); 244 | $('#ascii_intersection').val('plus'); 245 | $('#spacePadding').prop('checked', true); 246 | } else if ('ascii6' == value) { 247 | $('#charset').val('ascii'); 248 | $('#horizontal_header').val('letter'); 249 | $('#vertical_header').val('number'); 250 | $('#horizontal_top_border').val('double'); 251 | $('#horizontal_inner_header_border').val('double'); 252 | $('#horizontal_inner_border').val('single'); 253 | $('#horizontal_bottom_border').val('double'); 254 | $('#vertical_left_border').val('single'); 255 | $('#vertical_inner_header_border').val('single'); 256 | $('#vertical_inner_border').val('single'); 257 | $('#vertical_right_border').val('single'); 258 | $('#ascii_intersection').val('plus'); 259 | $('#spacePadding').prop('checked', true); 260 | } else { 261 | $('#charset').val('unicode'); 262 | $('#horizontal_header').val('first_line'); 263 | $('#vertical_header').val('none'); 264 | $('#horizontal_top_border').val('double'); 265 | $('#horizontal_inner_header_border').val('double'); 266 | $('#horizontal_inner_border').val('single'); 267 | $('#horizontal_bottom_border').val('double'); 268 | $('#vertical_left_border').val('double'); 269 | $('#vertical_inner_header_border').val('none'); 270 | $('#vertical_inner_border').val('single'); 271 | $('#vertical_right_border').val('double'); 272 | $('#ascii_intersection').val('plus'); 273 | $('#spacePadding').prop('checked', true); 274 | } 275 | updateAsciiIntersectionVisibility(document.getElementById("charset").value); 276 | updateHorizontalInnerHeaderBorderVisibility(document.getElementById("horizontal_header").value); 277 | updateVerticalInnerHeaderBorderVisibility(document.getElementById("vertical_header").value); 278 | generateTable(null); 279 | } 280 | 281 | function genPTT() { 282 | generateTable(null); 283 | } 284 | 285 | function generateTable(highlight) { 286 | var unicode = { 287 | none: { 288 | none: { 289 | none: { 290 | none: ' ', 291 | single: ' ', 292 | double: ' ' 293 | }, 294 | single: { 295 | none: ' ', 296 | single: '┐', 297 | double: '╕' 298 | }, 299 | double: { 300 | none: ' ', 301 | single: '╖', 302 | double: '╗' 303 | }, 304 | }, 305 | single: { 306 | none: { 307 | none: ' ', 308 | single: '─', 309 | }, 310 | single: { 311 | none: '┌', 312 | single: '┬', 313 | double: 'Z' 314 | }, 315 | double: { 316 | none: '╓', 317 | single: '╥', 318 | }, 319 | }, 320 | double: { 321 | none: { 322 | none: ' ', 323 | single: 'X', 324 | double: '═' 325 | }, 326 | single: { 327 | none: '╒', 328 | double: '╤' 329 | }, 330 | double: { 331 | none: '╔', 332 | double: '╦' 333 | }, 334 | }, 335 | }, 336 | single: { 337 | none: { 338 | none: { 339 | none: ' ', 340 | single: '┘', 341 | double: '╛' 342 | }, 343 | single: { 344 | none: '│', 345 | single: '┤', 346 | double: '╡' 347 | }, 348 | }, 349 | single: { 350 | none: { 351 | none: '└', 352 | single: '┴', 353 | }, 354 | single: { 355 | none: '├', 356 | single: '┼', 357 | }, 358 | }, 359 | double: { 360 | none: { 361 | none: '╘', 362 | double: '╧' 363 | }, 364 | single: { 365 | none: '╞', 366 | double: '╪' 367 | }, 368 | }, 369 | }, 370 | double: { 371 | none: { 372 | none: { 373 | none: ' ', 374 | single: '╜', 375 | double: '╝' 376 | }, 377 | double: { 378 | none: '║', 379 | single: '╢', 380 | double: '╣' 381 | }, 382 | }, 383 | single: { 384 | none: { 385 | none: '╙', 386 | single: '╨', 387 | }, 388 | single: { 389 | none: 'D', 390 | }, 391 | double: { 392 | none: '╟', 393 | single: '╫', 394 | }, 395 | }, 396 | double: { 397 | none: { 398 | none: '╚', 399 | double: '╩' 400 | }, 401 | double: { 402 | none: '╠', 403 | double: '╬' 404 | } 405 | } 406 | } 407 | }; 408 | var line = { 409 | ascii: { 410 | none: { 411 | vertical: ' ', 412 | horizontal: ' ' 413 | }, 414 | single: { 415 | vertical: '|', 416 | horizontal: '-' 417 | }, 418 | double: { 419 | horizontal: '=' 420 | } 421 | }, 422 | unicode: { 423 | none: { 424 | vertical: ' ', 425 | horizontal: ' ' 426 | }, 427 | single: { 428 | vertical: unicode.single.none.single.none, 429 | horizontal: unicode.none.single.none.single 430 | }, 431 | double: { 432 | vertical: unicode.double.none.double.none, 433 | horizontal: unicode.none.double.none.double 434 | } 435 | } 436 | }; 437 | var spacePadding = document.getElementById("spacePadding").checked; 438 | 439 | var charset = document.getElementById("charset").value; 440 | 441 | var horizontalHeader = document.getElementById("horizontal_header").value; 442 | var verticalHeader = document.getElementById("vertical_header").value; 443 | 444 | var border = { 445 | horizontalTop: document.getElementById("horizontal_top_border").value, 446 | horizontalInnerHeader: document.getElementById("horizontal_inner_header_border").value, 447 | horizontalInner: document.getElementById("horizontal_inner_border").value, 448 | horizontalBottom: document.getElementById("horizontal_bottom_border").value, 449 | 450 | verticalLeft: document.getElementById("vertical_left_border").value, 451 | verticalInnerHeader: document.getElementById("vertical_inner_header_border").value, 452 | verticalInner: document.getElementById("vertical_inner_border").value, 453 | verticalRight: document.getElementById("vertical_right_border").value, 454 | 455 | asciiIntersection: document.getElementById("ascii_intersection").value 456 | }; 457 | 458 | var data = extractData(spacePadding, horizontalHeader, verticalHeader); 459 | var widths = getWidths(data, spacePadding); 460 | var heights = getHeights(data, border, horizontalHeader, spacePadding); 461 | var str = ""; 462 | var i, j, m, offsets; 463 | 464 | // top 465 | str += generateSeparationLine(data, widths, heights, highlight, unicode, line, charset, horizontalHeader, verticalHeader, border, -1); 466 | 467 | 468 | // rows 469 | for (i = 0; i < data.vLen; i++) { 470 | offsets = []; 471 | for (j = 0; j < widths.length; j++) { 472 | offsets[j] = calculateOffset(data, heights, border, horizontalHeader, i, j); 473 | } 474 | 475 | for (m = 0; m < heights[i]; m++) { 476 | str += openHighlighted(highlight, 'verticalLeft'); 477 | str += line[charset][border.verticalLeft].vertical; 478 | str += closeHighlighted(highlight, 'verticalLeft'); 479 | for (j = 0; j < widths.length; j++) { 480 | str += generateCellContent(data, offsets[j] + m, widths, i, j); 481 | j += data.arr[i][j].cell.colspan - 1; 482 | if ('none' != verticalHeader && j == 0 && data.hLen > 1) { 483 | str += openHighlighted(highlight, 'verticalInnerHeader'); 484 | str += line[charset][border.verticalInnerHeader].vertical; 485 | str += closeHighlighted(highlight, 'verticalInnerHeader'); 486 | } else if (j < widths.length - 1) { 487 | str += openHighlighted(highlight, 'verticalInner'); 488 | str += line[charset][border.verticalInner].vertical; 489 | str += closeHighlighted(highlight, 'verticalInner'); 490 | } 491 | } 492 | str += openHighlighted(highlight, 'verticalRight'); 493 | str += line[charset][border.verticalRight].vertical; 494 | str += closeHighlighted(highlight, 'verticalRight'); 495 | str += '\n'; 496 | } 497 | 498 | str += generateSeparationLine(data, widths, heights, highlight, unicode, line, charset, horizontalHeader, verticalHeader, border, i); 499 | } 500 | if (data.vLen == 0) { 501 | str += generateSeparationLine(data, widths, heights, highlight, unicode, line, charset, horizontalHeader, verticalHeader, border, data.vLen); 502 | } 503 | $('#ptt-wrapper').html(str); 504 | } 505 | 506 | function extractData(spacePadding, horizontalHeader, verticalHeader) { 507 | var i, j, k, cell, item, lines, w, meta, vAlign, hAlign, vLen, hLen, mergedData; 508 | var result = []; 509 | var table = $('#table-wrapper').handsontable('getInstance'); 510 | var arr = table.getData(); 511 | var iOffset = 0; 512 | var jOffset = 0; 513 | for (i = 0; i < arr.length; i++) { 514 | if (i == 0 && ('number' == horizontalHeader || 'letter' == horizontalHeader)) { 515 | result.push([]); 516 | if ('number' == verticalHeader || 'letter' == verticalHeader) { 517 | //add an empty item that will be replaced later: 518 | result[0][0] = { 519 | cell: { 520 | x: 0, 521 | y: 0, 522 | colspan: 1, 523 | rowspan: 1 524 | }, 525 | empty: true 526 | }; 527 | jOffset = 1; 528 | } 529 | for (j = 0; j < arr[i].length; j++) { 530 | //add an empty item that will be replaced later: 531 | result[0][j + jOffset] = { 532 | cell: { 533 | x: 0, 534 | y: (j + jOffset), 535 | colspan: 1, 536 | rowspan: 1 537 | }, 538 | empty: true 539 | }; 540 | } 541 | iOffset = 1; 542 | } 543 | result.push([]); 544 | if ('number' == verticalHeader || 'letter' == verticalHeader) { 545 | //add an empty item that will be replaced later: 546 | result[i + iOffset][0] = { 547 | cell: { 548 | x: (i + iOffset), 549 | y: 0, 550 | colspan: 1, 551 | rowspan: 1 552 | }, 553 | empty: true 554 | }; 555 | jOffset = 1; 556 | } 557 | for (j = 0; j < arr[i].length; j++) { 558 | mergedData = table.mergeCells.mergedCellInfoCollection.getInfo(i, j); 559 | if (mergedData) { 560 | cell = { 561 | x: mergedData.row + iOffset, 562 | y: mergedData.col + jOffset, 563 | colspan: mergedData.colspan, 564 | rowspan: mergedData.rowspan 565 | }; 566 | } else { 567 | cell = { 568 | x: i + iOffset, 569 | y: j + jOffset, 570 | colspan: 1, 571 | rowspan: 1 572 | }; 573 | } 574 | item = arr[i][j]; 575 | if (!item) { 576 | result[i + iOffset][j + jOffset] = { 577 | cell: cell, 578 | empty: true 579 | }; 580 | } else { 581 | w = 0; 582 | lines = item.split('\n'); 583 | for (k = 0; k < lines.length; k++) { 584 | if (spacePadding) { 585 | if (lines[k].indexOf(' ', 0) !== 0) { 586 | lines[k] = ' ' + lines[k]; 587 | } 588 | if (lines[k].indexOf(' ', lines[k].length - 1) === -1) { 589 | lines[k] = lines[k] + ' '; 590 | } 591 | } 592 | if (lines[k].length > w) { 593 | w = lines[k].length; 594 | } 595 | } 596 | meta = table.getCellMeta(i, j); 597 | hAlign = 'left'; 598 | vAlign = 'top'; 599 | if (meta.className) { 600 | if (meta.className.indexOf('htCenter') > -1) { 601 | hAlign = 'center'; 602 | } else if (meta.className.indexOf('htRight') > -1) { 603 | hAlign = 'right'; 604 | } else if (meta.className.indexOf('htJustify') > -1) { 605 | hAlign = 'justify'; 606 | } 607 | if (meta.className.indexOf('htMiddle') > -1) { 608 | vAlign = 'middle'; 609 | } else if (meta.className.indexOf('htBottom') > -1) { 610 | vAlign = 'bottom'; 611 | } 612 | } 613 | result[i + iOffset][j + jOffset] = { 614 | cell: cell, 615 | empty: false, 616 | pseudoRows: lines, 617 | maxWidth: w, 618 | vAlign: vAlign, 619 | hAlign: hAlign 620 | }; 621 | } 622 | } 623 | } 624 | vLen = getVLen(result, (i + iOffset - 1), (j + jOffset - 1)); 625 | hLen = getHLen(result, (i + iOffset - 1), (j + jOffset - 1)); 626 | if ('none' != verticalHeader) { 627 | jOffset = 1; 628 | } 629 | if ('number' == horizontalHeader || 'letter' == horizontalHeader) { 630 | for (j = 0; j < hLen - jOffset; j++) { 631 | result[0][j + jOffset] = generateHeader(0, j + jOffset, horizontalHeader, spacePadding, j); 632 | } 633 | } 634 | if ('none' != horizontalHeader) { 635 | iOffset = 1; 636 | } 637 | if ('number' == verticalHeader || 'letter' == verticalHeader) { 638 | for (i = 0; i < vLen - iOffset; i++) { 639 | result[i + iOffset][0] = generateHeader(i + iOffset, 0, verticalHeader, spacePadding, i); 640 | } 641 | } 642 | return { 643 | arr: result, 644 | vLen: vLen, 645 | hLen: hLen 646 | }; 647 | } 648 | 649 | function getVLen(arr, vMax, hMax) { 650 | var i, j, item, v; 651 | var vLen = 0; 652 | 653 | for (i = vMax; i >= 0; i--) { 654 | for (j = 0; j <= hMax; j++) { 655 | item = arr[i][j]; 656 | if (!item.empty) { 657 | v = item.cell.x + item.cell.rowspan; 658 | if (v > vLen) { 659 | vLen = v; 660 | } 661 | } 662 | } 663 | } 664 | return vLen; 665 | } 666 | 667 | function getHLen(arr, vMax, hMax) { 668 | var i, j, item, h; 669 | var hLen = 0; 670 | 671 | for (j = hMax; j >= 0; j--) { 672 | for (i = 0; i <= vMax; i++) { 673 | item = arr[i][j]; 674 | if (!item.empty) { 675 | h = item.cell.y + item.cell.colspan; 676 | if (h > hLen) { 677 | hLen = h; 678 | } 679 | } 680 | } 681 | } 682 | return hLen; 683 | } 684 | 685 | function generateHeader(i, j, headerType, spacePadding, id) { 686 | var str = ""; 687 | var num, s; 688 | if (spacePadding) { 689 | str += ' '; 690 | } 691 | if ('letter' == headerType) { 692 | s = ''; 693 | num = id; 694 | do { 695 | s = String.fromCharCode(65 + (num % 26)) + s; 696 | num = Math.floor(num / 26) - 1; 697 | } while (num > -1); 698 | str += s; 699 | } else { 700 | str += (id + 1).toString(); 701 | } 702 | if (spacePadding) { 703 | str += ' '; 704 | } 705 | return { 706 | cell: { 707 | x: i, 708 | y: j, 709 | colspan: 1, 710 | rowspan: 1 711 | }, 712 | empty: false, 713 | pseudoRows: [str], 714 | maxWidth: str.length, 715 | vAlign: 'middle', 716 | hAlign: 'center' 717 | }; 718 | } 719 | 720 | function getWidths(data, spacePadding) { 721 | var widths = []; 722 | var mergedCells = []; 723 | var i, j, w, item, m, a; 724 | 725 | for (j = 0; j < data.hLen; j++) { 726 | w = 0; 727 | if (spacePadding) { 728 | w = 1; 729 | } 730 | for (i = 0; i < data.vLen; i++) { 731 | item = data.arr[i][j]; 732 | if (!item.empty) { 733 | if (item.cell.colspan == 1 && item.cell.rowspan == 1) { 734 | if (item.maxWidth > w) { 735 | w = item.maxWidth; 736 | } 737 | } else if (i == item.cell.x && j == item.cell.y) { 738 | mergedCells.push(item); 739 | } 740 | } 741 | } 742 | widths[j] = w; 743 | } 744 | if (mergedCells.length > 0) { 745 | m = findNotFittingMergedCellWithWidths(mergedCells, widths); 746 | while (m.hasNext) { 747 | a = divideAsArray(m.len, m.item.cell.colspan); 748 | for (j = 0; j < m.item.cell.colspan; j++) { 749 | widths[m.item.cell.y + j] += a[j]; 750 | } 751 | m = findNotFittingMergedCellWithWidths(mergedCells, widths); 752 | } 753 | } 754 | return widths; 755 | } 756 | 757 | function findNotFittingMergedCellWithWidths(mergedCells, widths) { 758 | var k, item, width, w; 759 | var match = { 760 | hasNext: true, 761 | len: 0 762 | }; 763 | for (k = 0; k < mergedCells.length; k++) { 764 | item = mergedCells[k]; 765 | width = calculateWidth(widths, item); 766 | w = item.maxWidth - width; 767 | if (w > match.len) { 768 | match = { 769 | hasNext: true, 770 | len: w, 771 | item: item 772 | }; 773 | } 774 | } 775 | if (match.len > 0) { 776 | return match; 777 | } 778 | return { 779 | hasNext: false 780 | }; 781 | } 782 | 783 | function getHeights(data, border, horizontalHeader, spacePadding) { 784 | var heights = []; 785 | var mergedCells = []; 786 | var i, j, h, item, m, a; 787 | 788 | for (i = 0; i < data.vLen; i++) { 789 | h = 0; 790 | if (spacePadding) { 791 | h = 1; 792 | } 793 | for (j = 0; j < data.hLen; j++) { 794 | item = data.arr[data.arr[i][j].cell.x][data.arr[i][j].cell.y]; 795 | if (!item.empty) { 796 | if (item.cell.colspan == 1 && item.cell.rowspan == 1) { 797 | if (item.pseudoRows.length > h) { 798 | h = item.pseudoRows.length; 799 | } 800 | } else if (i == item.cell.x && j == item.cell.y) { 801 | mergedCells.push(item); 802 | } 803 | } 804 | } 805 | heights[i] = h; 806 | } 807 | if (mergedCells.length > 0) { 808 | m = findNotFittingMergedCellWithHeights(data, border, horizontalHeader, mergedCells, heights); 809 | while (m.hasNext) { 810 | a = divideAsArray(m.len, m.item.cell.rowspan); 811 | for (i = 0; i < m.item.cell.rowspan; i++) { 812 | heights[m.item.cell.x + i] += a[i]; 813 | } 814 | m = findNotFittingMergedCellWithHeights(data, border, horizontalHeader, mergedCells, heights); 815 | } 816 | } 817 | return heights; 818 | } 819 | 820 | function findNotFittingMergedCellWithHeights(data, border, horizontalHeader, mergedCells, heights) { 821 | var k, item, height, h; 822 | var match = { 823 | hasNext: true, 824 | len: 0 825 | }; 826 | for (k = 0; k < mergedCells.length; k++) { 827 | item = mergedCells[k]; 828 | height = calcultateHeight(data, border, horizontalHeader, heights, item, 0).height; 829 | h = item.pseudoRows.length - height; 830 | if (h > match.len) { 831 | match = { 832 | hasNext: true, 833 | len: h, 834 | item: item 835 | }; 836 | } 837 | } 838 | if (match.len > 0) { 839 | return match; 840 | } 841 | return { 842 | hasNext: false 843 | }; 844 | } 845 | 846 | function generateSeparationLine(data, widths, heights, highlight, unicode, line, charset, horizontalHeader, verticalHeader, border, i) { 847 | var j, k, horizontalBorderKey, generateBorder, item, offset; 848 | var str = ''; 849 | 850 | if (i == -1) { 851 | horizontalBorderKey = 'horizontalTop'; 852 | if ('none' == border.horizontalTop) { 853 | return str; 854 | } 855 | } else if (i >= data.vLen - 1) { 856 | horizontalBorderKey = 'horizontalBottom'; 857 | if ('none' == border.horizontalBottom) { 858 | return str; 859 | } 860 | } else { 861 | if (hasHorizontalInnerHeader(data, border, i, horizontalHeader)) { 862 | horizontalBorderKey = 'horizontalInnerHeader'; 863 | } else if (hasHorizontalInner(data, border, i)) { 864 | horizontalBorderKey = 'horizontalInner'; 865 | } else { 866 | return str; 867 | } 868 | } 869 | var horizontalBorder = border[horizontalBorderKey]; 870 | var horizontalChar = line[charset][horizontalBorder].horizontal; 871 | 872 | str += openHighlighted(highlight, horizontalBorderKey); 873 | str += generateIntersection(data, charset, border, highlight, horizontalHeader, verticalHeader, unicode, line, i, -1); 874 | for (j = 0; j < widths.length; j++) { 875 | generateBorder = true; 876 | if (i > -1) { 877 | item = data.arr[i][j]; 878 | if (item.cell.x + item.cell.rowspan - 1 > i) { 879 | generateBorder = false; 880 | offset = calculateOffset(data, heights, border, horizontalHeader, i + 1, j) - 1; 881 | str += generateCellContent(data, offset, widths, i, j); 882 | j += item.cell.colspan - 1; 883 | } 884 | } 885 | if (generateBorder) { 886 | for (k = 0; k < widths[j]; k++) { 887 | str += horizontalChar; 888 | } 889 | } 890 | str += generateIntersection(data, charset, border, highlight, horizontalHeader, verticalHeader, unicode, line, i, j); 891 | } 892 | if (widths.length == 0) { 893 | str += generateIntersection(data, charset, border, highlight, horizontalHeader, verticalHeader, unicode, line, i, widths.length); 894 | } 895 | str += closeHighlighted(highlight, horizontalBorderKey); 896 | str += '\n'; 897 | return str; 898 | } 899 | 900 | function generateIntersection(data, charset, border, highlight, horizontalHeader, verticalHeader, unicode, line, i, j) { 901 | var top, bottom, left, right, horizontalBorderKey, item, verticalBorderKey, intersectionChar; 902 | var str = ''; 903 | if (i == -1) { 904 | top = true; 905 | bottom = false; 906 | horizontalBorderKey = 'horizontalTop'; 907 | } else if (i >= data.vLen - 1) { 908 | top = false; 909 | bottom = true; 910 | horizontalBorderKey = 'horizontalBottom'; 911 | } else { 912 | top = false; 913 | bottom = false; 914 | if (hasHorizontalInnerHeader(data, border, i, horizontalHeader)) { 915 | horizontalBorderKey = 'horizontalInnerHeader'; 916 | } else if (hasHorizontalInner(data, border, i)) { 917 | horizontalBorderKey = 'horizontalInner'; 918 | } else { 919 | //unexpected: empty string return statement in generateSeparationLine(..) 920 | return str; 921 | } 922 | } 923 | 924 | if (j == -1) { 925 | left = true; 926 | right = false; 927 | verticalBorderKey = 'verticalLeft'; 928 | } else if (j >= data.hLen - 1) { 929 | left = false; 930 | right = true; 931 | verticalBorderKey = 'verticalRight'; 932 | } else { 933 | left = false; 934 | right = false; 935 | if ('none' != verticalHeader && j == 0) { 936 | verticalBorderKey = 'verticalInnerHeader'; 937 | } else if (j < data.hLen - 1) { 938 | verticalBorderKey = 'verticalInner'; 939 | } else { 940 | return str; 941 | } 942 | } 943 | 944 | //handle merged cells (modify the values of top, right, bottom, left): 945 | if (!top && j >= 0) { 946 | item = data.arr[i][j]; 947 | if (item.cell.y + item.cell.colspan - 1 > j) { 948 | top = true; 949 | } 950 | } 951 | if (!bottom && j >= 0) { 952 | item = data.arr[i + 1][j]; 953 | if (item.cell.y + item.cell.colspan - 1 > j) { 954 | bottom = true; 955 | } 956 | } 957 | if (!left && i >= 0) { 958 | item = data.arr[i][j]; 959 | if (item.cell.x + item.cell.rowspan - 1 > i) { 960 | left = true; 961 | } 962 | } 963 | if (!right && i >= 0) { 964 | item = data.arr[i][j + 1]; 965 | if (item.cell.x + item.cell.rowspan - 1 > i) { 966 | right = true; 967 | } 968 | } 969 | 970 | var horizontalBorder = border[horizontalBorderKey]; 971 | var verticalBorder = border[verticalBorderKey]; 972 | if ('ascii' == charset) { 973 | if (top && !right && bottom && !left) { 974 | intersectionChar = line.ascii[horizontalBorder].horizontal; 975 | } else if (!top && right && !bottom && left) { 976 | intersectionChar = line.ascii[verticalBorder].vertical; 977 | } else if ('horizontal_border' == border.asciiIntersection) { 978 | intersectionChar = line.ascii[horizontalBorder].horizontal; 979 | } else if ('vertical_border' == border.asciiIntersection) { 980 | intersectionChar = line.ascii[verticalBorder].vertical; 981 | } else { 982 | intersectionChar = '+'; 983 | } 984 | } else { 985 | intersectionChar = unicode[(top) ? 'none' : verticalBorder][(right) ? 'none' : horizontalBorder][(bottom) ? 'none' : verticalBorder][(left) ? 'none' : horizontalBorder]; 986 | } 987 | 988 | str += openHighlighted(highlight, verticalBorderKey); 989 | str += intersectionChar; 990 | str += closeHighlighted(highlight, verticalBorderKey); 991 | return str; 992 | } 993 | 994 | function calculateOffset(data, heights, border, horizontalHeader, i, j) { 995 | var offset, item, calc; 996 | item = data.arr[data.arr[i][j].cell.x][data.arr[i][j].cell.y]; 997 | calc = calcultateHeight(data, border, horizontalHeader, heights, item, i); 998 | offset = calc.offset; 999 | if ('bottom' == item.vAlign) { 1000 | offset += item.pseudoRows.length - calc.height; 1001 | } else if ('middle' == item.vAlign) { 1002 | offset += Math.ceil((item.pseudoRows.length - calc.height) / 2); 1003 | } else { 1004 | offset += 0; 1005 | } 1006 | return offset; 1007 | } 1008 | 1009 | function calcultateHeight(data, border, horizontalHeader, heights, item, i) { 1010 | var offset, height, k; 1011 | offset = 0; 1012 | height = heights[item.cell.x]; 1013 | for (k = 1; k < item.cell.rowspan; k++) { 1014 | height += (hasHorizontalInnerHeader(data, border, item.cell.x + k - 1, horizontalHeader) || hasHorizontalInner(data, border, item.cell.x + k - 1)) ? 1 : 0; 1015 | if (item.cell.x + k <= i) { 1016 | offset = height; 1017 | } 1018 | height += heights[item.cell.x + k]; 1019 | } 1020 | return { 1021 | height: height, 1022 | offset: offset 1023 | }; 1024 | } 1025 | 1026 | function generateCellContent(data, offset, widths, i, j) { 1027 | var item, width, k, entry, end; 1028 | var str = ''; 1029 | item = data.arr[data.arr[i][j].cell.x][data.arr[i][j].cell.y]; 1030 | width = calculateWidth(widths, item); 1031 | if (item.empty) { 1032 | entry = ''; 1033 | } else { 1034 | entry = item.pseudoRows[offset] || ''; 1035 | } 1036 | if ('right' == item.hAlign) { 1037 | end = width - entry.length; 1038 | } else if ('center' == item.hAlign) { 1039 | end = Math.floor((width - entry.length) / 2); 1040 | } else { 1041 | end = 0; 1042 | } 1043 | for (k = 0; k < end; k++) { 1044 | str += ' '; 1045 | } 1046 | str += escapeHTMLEntities(entry); 1047 | end = width - entry.length - end; 1048 | for (k = 0; k < end; k++) { 1049 | str += ' '; 1050 | } 1051 | return str; 1052 | } 1053 | 1054 | function calculateWidth(widths, item) { 1055 | var width, k; 1056 | width = widths[item.cell.y]; 1057 | for (k = 1; k < item.cell.colspan; k++) { 1058 | width += 1; 1059 | width += widths[item.cell.y + k]; 1060 | } 1061 | return width; 1062 | } 1063 | 1064 | function hasHorizontalInnerHeader(data, border, i, horizontalHeader) { 1065 | return ('none' != border.horizontalInnerHeader && 'none' != horizontalHeader && i == 0 && data.vLen > 1); 1066 | } 1067 | 1068 | function hasHorizontalInner(data, border, i) { 1069 | return ('none' != border.horizontalInner && i < data.vLen - 1); 1070 | } 1071 | 1072 | function openHighlighted(highlight, key) { 1073 | if (key == highlight) { 1074 | return ''; 1075 | } else { 1076 | return ''; 1077 | } 1078 | } 1079 | 1080 | function closeHighlighted(highlight, key) { 1081 | if (key == highlight) { 1082 | return ''; 1083 | } else { 1084 | return ''; 1085 | } 1086 | } 1087 | 1088 | function divideAsArray(number, size) { 1089 | var k, r; 1090 | var result = []; 1091 | var nb = number; 1092 | for (k = 0; k < size; k++) { 1093 | r = Math.ceil(nb / (size - k)); 1094 | result[k] = r; 1095 | nb = nb - r; 1096 | } 1097 | return result; 1098 | } 1099 | 1100 | function escapeHTMLEntities(text) { 1101 | return text.replace(/[<>\&]/g, function(c) { 1102 | return '&#' + c.charCodeAt(0) + ';'; 1103 | }); 1104 | } --------------------------------------------------------------------------------