├── .github
└── workflows
│ └── node.js.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── demos
├── base.css
├── certificate.html
├── contract.html
├── custom_save.html
├── default.html
├── deliveryslip.html
├── fonts
│ ├── lobster.ttf
│ └── tangerine.ttf
└── invoice.html
├── package-lock.json
├── package.json
├── src
├── Document.js
├── PopupWindow.js
├── ReportBro.js
├── ajaxload.gif
├── commands
│ ├── AddDeleteDocElementCmd.js
│ ├── AddDeleteParameterCmd.js
│ ├── AddDeleteStyleCmd.js
│ ├── Command.js
│ ├── CommandGroupCmd.js
│ ├── MovePanelItemCmd.js
│ └── SetValueCmd.js
├── container
│ ├── Band.js
│ ├── Container.js
│ ├── Frame.js
│ └── Page.js
├── data
│ ├── DocumentProperties.js
│ ├── Parameter.js
│ └── Style.js
├── elements
│ ├── BarCodeElement.js
│ ├── DocElement.js
│ ├── FrameElement.js
│ ├── ImageElement.js
│ ├── LineElement.js
│ ├── PageBreakElement.js
│ ├── SectionBandElement.js
│ ├── SectionElement.js
│ ├── TableBandElement.js
│ ├── TableElement.js
│ ├── TableTextElement.js
│ ├── TextElement.js
│ ├── WatermarkImageElement.js
│ └── WatermarkTextElement.js
├── fonts
│ ├── font_style.css
│ ├── open-sans-v34-latin-300.woff
│ ├── open-sans-v34-latin-300.woff2
│ ├── open-sans-v34-latin-600.woff
│ ├── open-sans-v34-latin-600.woff2
│ ├── open-sans-v34-latin-800.woff
│ ├── open-sans-v34-latin-800.woff2
│ ├── open-sans-v34-latin-regular.woff
│ └── open-sans-v34-latin-regular.woff2
├── i18n
│ ├── locale_de_de.js
│ ├── locale_en_us.js
│ └── locales.js
├── iconfonts
│ ├── reportbro.svg
│ ├── reportbro.ttf
│ ├── reportbro.woff
│ ├── reportbro.woff2
│ └── style.css
├── main.css
├── main.js
├── menu
│ ├── MainPanel.js
│ ├── MainPanelItem.js
│ └── MenuPanel.js
├── panels
│ ├── DocElementPanel.js
│ ├── DocumentPropertiesPanel.js
│ ├── EmptyDetailPanel.js
│ ├── PanelBase.js
│ ├── ParameterPanel.js
│ └── StylePanel.js
├── quill.reportbro.css
├── rb_logo_dark.png
├── rb_logo_white.png
├── toggle-switch.css
└── utils.js
├── webpack.common.js
├── webpack.config.js
└── webpack.config.prod.js
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | release:
8 | types: [released]
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 |
14 | permissions:
15 | contents: write
16 | actions: read
17 | checks: write
18 | issues: read
19 | packages: write
20 | pull-requests: read
21 | repository-projects: read
22 | statuses: read
23 |
24 | strategy:
25 | matrix:
26 | node-version: [20]
27 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
28 |
29 | steps:
30 | - uses: actions/checkout@v4
31 | - name: Use Node.js ${{ matrix.node-version }}
32 | uses: actions/setup-node@v4
33 | with:
34 | node-version: ${{ matrix.node-version }}
35 | cache: 'npm'
36 | - run: npm ci
37 | - run: npm run build-prod
38 | - run: npm pack
39 |
40 | - name: Upload Release Assets
41 | uses: alexellis/upload-assets@0.4.1
42 | env:
43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44 | with:
45 | asset_paths: '["reportbro-designer-*.tgz", "reportbro-designer-*.zip"]'
46 |
47 | - uses: JS-DevTools/npm-publish@v3
48 | with:
49 | token: ${{ secrets.NPM_TOKEN }}
50 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 |
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 |
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # nyc test coverage
23 | .nyc_output
24 |
25 | # IDE
26 | .vs_code
27 | .vscode
28 | .idea
29 |
30 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
31 | .grunt
32 |
33 | # Bower dependency directory (https://bower.io/)
34 | bower_components
35 |
36 | # node-waf configuration
37 | .lock-wscript
38 |
39 | # Compiled binary addons (http://nodejs.org/api/addons.html)
40 | build/Release
41 |
42 | # Dependency directories
43 | node_modules/
44 | jspm_packages/
45 |
46 | # Typescript v1 declaration files
47 | typings/
48 |
49 | # Optional npm cache directory
50 | .npm
51 |
52 | # Optional eslint cache
53 | .eslintcache
54 |
55 | # Optional REPL history
56 | .node_repl_history
57 |
58 | # Output of 'npm pack'
59 | *.tgz
60 | *.zip
61 |
62 | # Yarn Integrity file
63 | .yarn-integrity
64 |
65 | # dotenv environment variables file
66 | .env
67 |
68 | # demos for local testing
69 | demos/temp/
70 |
71 | .DS_Store
72 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [3.10.0] - 2025-01-03
4 |
5 | ### Features
6 | * Support copy & paste of elements across ReportBro instances
7 | * Add "patternLocales" setting to set available pattern locales in document properties
8 |
9 | ### Changes
10 | * Add "pt" locale to pattern locales in document properties
11 |
12 | ### Bug Fixes
13 |
14 | ## [3.9.2] - 2024-10-07
15 |
16 | ### Features
17 | * Add option for watermark elements to show in foreground
18 |
19 | ### Changes
20 | * Do not restrict watermark elements to page boundaries
21 | * Do not allow to delete default parameters "page_count" and "page_number"
22 |
23 | ### Bug Fixes
24 | * Fix error on xlsx download
25 | * Fix deleting watermark element
26 | * Fix validating decimal input value in test data popup
27 |
28 | ## [3.9.1] - 2024-08-28
29 |
30 | ### Bug Fixes
31 | * Fix rendering style select
32 |
33 | ## [3.9.0] - 2024-08-27
34 |
35 | ### Features
36 | * Add text and image watermarks with rotation and transparency (PLUS version)
37 | * Support styles for various element types
38 |
39 | ## [3.8.0] - 2024-05-23
40 |
41 | ### Features
42 | * Support background color for section bands
43 | * Options to set spreadsheet cell type and pattern
44 |
45 | ### Changes
46 | * Allow custom parameters in reportServerUrl
47 |
48 | ### Bug Fixes
49 | * Fix deleting table row
50 |
51 | ## [3.7.0] - 2024-02-09
52 |
53 | ### Features
54 | * Rich Text parameters for formatted content (PLUS version)
55 |
56 | ### Bug Fixes
57 | * Fix display of page dimension errors in document properties
58 | * Fix setting boolean parameter test data and clearing parameter test data image
59 | * Fix setting save button enabled/disabled for undo/redo command after report was saved
60 |
61 | ## [3.6.0] - 2024-01-15
62 |
63 | ### Changes
64 | * Allow Rich Text in table cells (PLUS version)
65 | * Add parent parameter for createParameter API method to allow creation of field inside Collection/List parameter
66 | * Support retrieving list field with getParameterByName API method
67 | * Show error message when text contains characters that are not contained in font
68 |
69 | ### Bug Fixes
70 | * Hide empty test data row for image and boolean parameter
71 | * Show nullable option for image parameter
72 |
73 | ## [3.5.1] - 2023-11-20
74 |
75 | ### Bug Fixes
76 | * Set data source prefix for parameters in popup window when used in parameter expression
77 | * Show parameter name when displaying error message for missing data
78 | * Do not show Sum/Average and evaluated parameters when editing test data for Collection/List parameter
79 |
80 | ## [3.5.0] - 2023-11-16
81 |
82 | ### Changes
83 | * Add prefix for root parameters in popup window when inside an element with a data source
84 |
85 | ### Bug Fixes
86 | * Fix display of error message for invalid collection or list parameter
87 |
88 | ## [3.4.0] - 2023-10-23
89 |
90 | ### Features
91 | * Add autoSaveOnPreview setting, if set to true the "save" method is called when report is previewed
92 | * Add imageMaxSize setting to downscale images in report template
93 | * Convert static images and images for parameter test data to WebP format to reduce report template size
94 |
95 | ### Changes
96 | * Display report errors on xlsx download if errors are available
97 | * Add imageLimit setting to define maximum number of allowed image elements in report template
98 |
99 | ## [3.3.0] - 2023-09-12
100 |
101 | ### Features
102 | * Support multiple conditional styles
103 |
104 | ### Changes
105 | * Add data source prefix when selecting parameter of outer data source in popup window
106 |
107 | ### Bug Fixes
108 | * Show width property for text element
109 | * Show data source parameters in popup window also for data sources inside collection
110 | * Fix scrolling error message into view
111 | * Hide test data rows for "page_count" and "page_number" parameters
112 |
113 | ## [3.2.0] - 2023-05-12
114 |
115 | ### Features
116 | * Add additional barcodes CODE39, EAN-8, EAN-13 and UPC
117 | * Option to rotate barcode
118 |
119 | ### Bug Fixes
120 | * Fix updating barcode when switching from QR Code
121 | * Clear test data when changing parameter type to/from List, Simple List or Collection
122 | * Fix editing test data and preview rendering of "Simple List" parameter when report template
123 | was saved in ReportBro Designer version < 3.0
124 | * Fix parameter panel display and hide add/delete buttons for parameter when adminMode is deactivated
125 |
126 | ## [3.1.0] - 2023-04-19
127 |
128 | ### Features
129 | * Add frame option to align frame to bottom of page
130 | * Add option to set thousands separator symbol for number formatting
131 |
132 | ### Bug Fixes
133 | * Fix display of error messages
134 | * Fix deleting existing elements before loading report
135 |
136 | ## [3.0.0] - 2022-10-10
137 |
138 | ### Features
139 | * Nested parameters (PLUS version)
140 | * Option to specify image file for test data of image parameter
141 | * Option to set bar width for code128 barcode
142 |
143 | ### Changes
144 | * Remove jquery dependency (see README.md for changed initialization)
145 | * Add validation of ReportBro properties to avoid invalid values
146 | * Replace reference to external google fonts with local files
147 |
148 | ### Bug Fixes
149 | * Fix dragging multiple elements to different container
150 |
151 | ## [2.1.1] - 2022-05-17
152 |
153 | ### Changes
154 | * update dependenices
155 | * non functional change generate zip and tgz with pack
156 |
157 | ## [2.1.0] - 2022-04-22
158 |
159 | ### Changes
160 | * add printIf for page break
161 |
162 | ## [2.0.1] - 2022-01-28
163 |
164 | ### Changes
165 | * add requestCallback which is called before a preview request (pdf/xlsx) and
166 | can be used to change request parameters
167 | * support request headers for preview request (pdf/xlsx)
168 | * upgrade dependencies to fix potential security vulnerabilities
169 |
170 | ## [2.0.0] - 2021-08-06
171 |
172 | ### Features
173 | * Rich Text (PLUS version)
174 | * QR Code
175 | * option to set basic auth for report preview request
176 | * option to set custom headers for report preview request
177 | * option to repeat table group on each page
178 | * option to set colors for color palette
179 | * option to set available font sizes
180 |
181 | ### Changes
182 | * update all package dependencies and adapt webpack configuration for webpack 5
183 | to remove build warnings (upgrade to npm v7)
184 | * remove external dependencies for autosize, JsBarcode and spectrum
185 | * add processErrors API method to display report errors
186 | * add clearErrors API method to clear existing report errors
187 |
188 | ## [1.6.0] - 2021-03-19
189 |
190 | ### Features
191 | * zoom buttons for document panel
192 |
193 | ### Changes
194 | * add parameter to cmdExecutedCallback which indicates if command was done or undone
195 | * add "copy" suffix to name of pasted parameter and style for unique names
196 | * only update parameter references on name change if the parameter name is unique
197 | * Allow starting area selection inside container (frame/section) and
198 | do not include container element in area selection
199 |
200 | ### Bug Fixes
201 | * set correct cell height when table is created/updated
202 |
203 | ## [1.5.2] - 2020-10-06
204 |
205 | ### Changes
206 | * option to set default font for new text and style
207 | * update logo and css styling
208 | * option to set css theme
209 | * add getClassName method for introspection to command, data and element classes
210 |
211 | ## [1.5.1] - 2020-07-27
212 |
213 | ### Features
214 | * option to highlight unused parameters on report load
215 |
216 | ### Changes
217 | * add destroy method to remove dom nodes and event handlers
218 | * allow border width in 0.5 steps
219 |
220 | ### Bug Fixes
221 | * update table width after deleting column
222 |
223 | ## [1.5.0] - 2020-07-15
224 |
225 | ### Features
226 | * option to expand column width if there are hidden columns
227 | * option to force page break for each new group in a table
228 | * option to enable text wrap in spreadsheet cell
229 |
230 | ### Changes
231 | * show list parameters in popup window for expression
232 | (this allows to reference fields of the same row in the expression)
233 |
234 | ### Bug Fixes
235 | * fix javascript error when parameter name is empty
236 |
237 | ## [1.4.0] - 2020-04-20
238 |
239 | ### Features
240 | * dynamic document element panel which allows modifying multiple
241 | document elements (also of different kinds) at once
242 | * allow modifying text style settings when a style is selected
243 |
244 | ### Changes
245 | * add selectCallback which is called when an object is selected/deselected
246 | * add isModified API method to return modified flag
247 | * allow image parameter type in list parameter
248 | * add smaller font sizes to drop down (starting from 4)
249 | * show image preview for images specified by url
250 |
251 | ### Bug Fixes
252 | * fix initialization of ReportBro when called without properties
253 | * fix adding new elements when preview tab exists (Chrome on macOS)
254 |
255 | ## [1.3.4] - 2019-12-23
256 |
257 | ### Changes
258 | * option to show menu buttons to log report template to console and load report template from text
259 |
260 | ### Bug Fixes
261 | * fix changing table columns when table contains cells with colspan
262 | * fix loading report with small table (< 200px) with table positioned near right border
263 |
264 | ## [1.3.3] - 2019-11-08
265 |
266 | ### Bug Fixes
267 | * do not show resize mouse cursor in corners when table is selected
268 | * fix setting transparent color
269 | * fix parameter type drop down options in Safari
270 | * fix content area and ReportBro logo alignment when sidebar is active
271 |
272 | ## [1.3.2] - 2019-09-03
273 |
274 | ### Bug Fixes
275 | * fix freeze when inserting table columns left or right to selected table cell
276 | * fix checking bounds when table is dragged over right border
277 |
278 | ## [1.3.1] - 2019-09-02
279 |
280 | ### Changes
281 | * do not allow deletion of internal row_number parameter
282 | * insert internal row_number parameter at top
283 |
284 | ### Bug Fixes
285 | * do not show internal row_number parameter in "Edit test data" popup
286 |
287 | ## [1.3.0] - 2019-08-26
288 |
289 | ### Features
290 | * sizer to change main panel width
291 | * column span field for table text element
292 | * add internal parameter row_number for list parameters
293 | * add locales for separate language files (English and German available)
294 |
295 | ### Changes
296 | * focus text input when text element is double clicked
297 |
298 | ### Bug Fixes
299 | * disable save button depending on modified flag
300 | * do not allow setting invalid color value
301 |
302 | ## [1.2.1] - 2019-07-22
303 |
304 | ### Bug Fixes
305 | * fix drag & drop and resize of document elements in Firefox
306 | * fix npm package
307 |
308 | ## [1.2.0] - 2019-07-05
309 |
310 | ### Features
311 | * basic touch support to drag & drop and resize document elements
312 | * public API methods to get, add and delete objects:
313 | getUniqueId, getDocElementById, getStyleById, getParameterById, getParameterByName,
314 | createDocElement, createParameter, createStyle, deleteDocElement, deleteParameter, deleteStyle
315 |
316 | ### Changes
317 | * add cmdExecutedCallback which is called when a command is exuected
318 |
319 | ### Bug Fixes
320 | * delete existing document elements when loading report
321 | * fix issue when editing image size
322 |
323 | ## [1.1.0] - 2019-01-10
324 |
325 | ### Features
326 | * menu action buttons to add columns to the left/right of selected table column,
327 | and buttons to add content rows above/below of selected table column
328 | * allow dragging of selected section and of section band height
329 | * link property for text and image element to create an external link
330 | * strikethrough text style
331 |
332 | ### Bug Fixes
333 | * fix updating parameter references when renaming a parameter which is used inside a table in a section
334 | * remove references when a style is deleted
335 | * update element using style when style is changed
336 | * fix menu item display in sidebar menu
337 | * fix deletion of section: internal containers for section bands were not deleted,
338 | undo delete action did not work properly (elements inside section bands were not restored)
339 | * fix display of error messages for table band print-if field
340 | * do not ignore test data for boolean parameter in report preview
341 |
342 | ### Changes
343 | * allow up to 99 table content rows
344 | * add printIf and removeEmptyElement fields for table
345 | * section and frame can be selected with double click
346 | * more options for text line spacing
347 | * copy pasted element to current scroll position instead of upper left corner
348 |
349 | ## [1.0.0] - 2018-09-25
350 |
351 | ### Bug Fixes
352 | * allow edit text element pattern field and add button to open pattern popup window
353 | * do not show search field in pattern popup
354 | * fix drag & drop of collection/list parameter in menu panel
355 | * fix updating parameter references when renaming a parameter
356 | * fix dragging element into frame with border
357 |
358 | ### Changes
359 | * show separator for data source parameters in parameter popup window
360 | * make sure there is enough space for popup below input, otherwise just show it over input field
361 | * setModified method to change modified status (defines if save button is enabled)
362 | * return ReportBro instance when ReportBro is initialized
363 |
364 | ## [0.12.1] - 2018-06-06
365 |
366 | ### Features
367 | * section elements to iterate lists
368 | * column range spreadsheet property
369 | * copy & paste of parameters and styles
370 | * search filter in parameter popup
371 | * allow decimal values for border width
372 | * selection of elements by dragging a rectangle in the layout editor
373 | * better design for nested menu panel items
374 |
375 | ### Bug Fixes
376 | * fix drag & drop and resizing of multiple selected elements when an element is not aligned on the grid
377 | * allow undo of pasted elements
378 | * keep position of nested elements when pasting frames
379 | * test if dragging menu panel is allowed to new destination (e.g. element cannot be dragged into table band or table text element)
380 | * only show horizontal/vertical alignment buttons if appropriate (container of selected elements must have same x/y-offset)
381 |
382 | ## [0.11.2] - 2018-04-10
383 |
384 | ### Features
385 | * support for dynamic table column (column containing simple array parameter will be expanded to multiple columns)
386 |
387 | ### Bug Fixes
388 | * text element styling when element uses predefined style with borders
389 | * fix undo of deleted frame element (restore nested elements)
390 | * fix display of table column texts when padding of a column text is modified
391 |
392 | ## [0.11.1] - 2018-03-21
393 |
394 | ### Features
395 | * multiple content row definitions for tables
396 | * group expression and print if for table content rows
397 | * boolean parameter type
398 | * simple list parameter type (list items with basic type like string, number, boolean, date)
399 | * nullable setting for parameter to explicitly allow nullable parameters, non-nullable parameters automatically get default value in case there is no data (e.g. 0 for numbers or '' for strings)
400 |
401 | ### Bug Fixes
402 | * update table column element height when table row height is changed
403 | * copy&paste of frame elements
404 | * save eval setting of table column text
405 |
406 | ## [0.10.1] - 2017-11-02
407 |
408 | ### Features
409 | * frame elements to group document elements
410 |
411 | ## [0.9.9] - 2017-08-19
412 |
413 | Initial release.
414 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ReportBro Designer
2 |
3 | A javascript plugin to create PDF and XLSX report templates.
4 |
5 | ReportBro Designer can be easily integrated into your web application. Everyone can design & edit document templates, and preview them directly in the browser. The reports can be generated with
6 | [ReportBro Lib](https://github.com/jobsta/reportbro-lib) (a Python package) on the server.
7 |
8 | See the ReportBro project website on https://www.reportbro.com for full documentation and demos.
9 |
10 |
11 |
12 |
13 |
14 | ## Installation
15 |
16 | + Download ReportBro Designer (`reportbro-designer-.tgz`) from https://github.com/jobsta/reportbro-designer/releases
17 | + Extract .tgz file and move `dist` folder to your application
18 | + In your .html document reference reportbro.js and reportbro.css. See basic usage below.
19 |
20 | ### Install via npm:
21 |
22 | `npm install reportbro-designer --save`
23 |
24 | ## Basic usage
25 |
26 | Go to the [docs](https://www.reportbro.com/framework/api) for more information. There are demos for different use cases available at: https://www.reportbro.com/demo/invoice.
27 |
28 | Include the ReportBro JavaScript file as well as the ReportBro stylesheet in the `` of your page.
29 |
30 | ```html
31 |
32 |
33 | ```
34 |
35 | Place an empty div within the `` of a web page:
36 | ```html
37 |
38 | ```
39 |
40 | Initialize ReportBro:
41 | ```html
42 |
45 | ````
46 |
47 | ## Build
48 |
49 | ### prerequisites:
50 |
51 | Install Node.js and npm.
52 |
53 | Troubleshooting for Ubuntu/Linux: If you get an error like "/usr/bin/env: node: No such file or directory" you can easily fix it with a symbolic link:
54 |
55 | `ln -s /usr/bin/nodejs /usr/bin/node`
56 |
57 | Go to reportbro-designer root directory and install node modules:
58 |
59 | `npm install`
60 |
61 | ### development:
62 |
63 | `npm run build`
64 |
65 | This is a fast way to build ReportBro Designer and easily debug the code. You can use the generated reportbro.js file from the dist folder in any modern browser supporting ES6 (ECMAScript 2015).
66 |
67 | ### production:
68 |
69 | `npm run build-prod`
70 |
71 | Transpiles javascript code from ES6 to ES5 to support older browsers and minifies the generated js file. Use this build step to generate ReportBro Designer for production environment.
72 |
73 | ## Notes
74 |
75 | ### Running demos from local filesystem
76 |
77 | You need to run
78 |
79 | `npm run build-prod`
80 |
81 | at least once before starting any local demos. This build step creates ReportBro Designer in the `dist` directory which is referenced in the demos.
82 |
83 | ## License
84 |
85 | ### Commercial license
86 |
87 | If you want to use ReportBro to develop commercial applications and projects, the Commercial license is the appropriate license. With this license, your source code is kept proprietary. Purchase a ReportBro Commercial license at https://www.reportbro.com/framework/license
88 |
89 | This license includes ReportBro PLUS with additional features.
90 |
91 | ### Open-source license
92 |
93 | If you are creating an open-source application under a license compatible with the [GNU AGPL license v3](https://www.gnu.org/licenses/agpl-3.0.html), you may use ReportBro under the terms of the AGPLv3.
94 |
95 | Read more about ReportBro's license options at https://www.reportbro.com/framework/license.
96 |
--------------------------------------------------------------------------------
/demos/base.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | margin: 0;
3 | padding: 0;
4 | }
--------------------------------------------------------------------------------
/demos/custom_save.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ReportBro Demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/demos/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ReportBro Demo
5 |
6 |
7 |
8 |
9 |
10 |
20 |
21 |
22 |
23 |
24 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/demos/fonts/lobster.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/demos/fonts/lobster.ttf
--------------------------------------------------------------------------------
/demos/fonts/tangerine.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/demos/fonts/tangerine.ttf
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reportbro-designer",
3 | "version": "3.10.0",
4 | "description": "Designer to create pdf and excel report layouts. The reports can be generated with reportbro-lib (a Python package) on the server.",
5 | "keywords": [
6 | "report",
7 | "reporting",
8 | "designer",
9 | "gui",
10 | "pdf",
11 | "document",
12 | "excel",
13 | "xlsx"
14 | ],
15 | "homepage": "https://www.reportbro.com",
16 | "license": "AGPL-3.0",
17 | "author": {
18 | "name": "jobsta",
19 | "email": "alex@reportbro.com"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "http://github.com/jobsta/reportbro-designer"
24 | },
25 | "files": [
26 | "dist",
27 | "src"
28 | ],
29 | "main": "dist/reportbro.js",
30 | "scripts": {
31 | "build": "webpack",
32 | "build-prod": "webpack --config webpack.config.prod.js"
33 | },
34 | "dependencies": {
35 | "@babel/preset-env": "^7.17.12",
36 | "@babel/runtime": "^7.17.9",
37 | "autosize": "^5.0.1",
38 | "jsbarcode": "^3.11.5",
39 | "qrcode": "^1.4.4",
40 | "quill": "1.3.7"
41 | },
42 | "devDependencies": {
43 | "@babel/cli": "^7.17.10",
44 | "@babel/core": "^7.17.12",
45 | "@babel/plugin-transform-runtime": "^7.17.12",
46 | "babel-loader": "^8.2.5",
47 | "babelify": "^10.0.0",
48 | "browserify": "^17.0.0",
49 | "copy-webpack-plugin": "^9.0.0",
50 | "css-loader": "^6.7.1",
51 | "file-loader": "^6.2.0",
52 | "mini-css-extract-plugin": "^2.6.0",
53 | "style-loader": "^3.1.1",
54 | "webpack": "^5.72.1",
55 | "webpack-cli": "^4.9.2",
56 | "webpack-merge": "^5.10.0"
57 | },
58 | "optionalDependencies": {
59 | "tar-to-zip": "^3.0.0"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/ajaxload.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/ajaxload.gif
--------------------------------------------------------------------------------
/src/commands/AddDeleteDocElementCmd.js:
--------------------------------------------------------------------------------
1 | import Command from './Command';
2 | import BarCodeElement from '../elements/BarCodeElement';
3 | import DocElement from '../elements/DocElement';
4 | import FrameElement from '../elements/FrameElement';
5 | import ImageElement from '../elements/ImageElement';
6 | import LineElement from '../elements/LineElement';
7 | import PageBreakElement from '../elements/PageBreakElement';
8 | import SectionElement from '../elements/SectionElement';
9 | import TableElement from '../elements/TableElement';
10 | import TextElement from '../elements/TextElement';
11 | import WatermarkImageElement from '../elements/WatermarkImageElement';
12 | import WatermarkTextElement from '../elements/WatermarkTextElement';
13 | import MainPanelItem from '../menu/MainPanelItem';
14 |
15 | /**
16 | * Command to add and delete a doc element.
17 | * @class
18 | */
19 | export default class AddDeleteDocElementCmd extends Command {
20 | constructor(add, elementType, initialData, id, parentId, position, rb) {
21 | super();
22 | this.add = add;
23 | this.elementType = elementType;
24 | this.initialData = initialData;
25 | this.parentId = parentId;
26 | this.position = position;
27 | this.rb = rb;
28 | this.id = id;
29 | this.firstExecution = true;
30 | }
31 |
32 | getName() {
33 | if (this.add) {
34 | return 'Add element';
35 | } else {
36 | return 'Delete element';
37 | }
38 | }
39 |
40 | do() {
41 | if (this.add) {
42 | this.addElement();
43 | } else {
44 | this.deleteElement();
45 | }
46 | this.firstExecution = false;
47 | }
48 |
49 | undo() {
50 | if (this.add) {
51 | this.deleteElement();
52 | } else {
53 | this.addElement();
54 | }
55 | }
56 |
57 | addElement() {
58 | let parent = this.rb.getDataObject(this.parentId);
59 | if (parent !== null) {
60 | let element = AddDeleteDocElementCmd.createElement(
61 | this.id, this.initialData, this.elementType, this.position, true, this.rb);
62 |
63 | this.rb.notifyEvent(element, Command.operation.add);
64 | this.rb.selectObject(this.id, true);
65 |
66 | if (this.add && this.firstExecution) {
67 | // in case of add command we serialize initialData on first execution so it contains all data
68 | // created during setup (e.g. ids of table bands and table cells for a table)
69 | this.initialData = element.toJS();
70 | }
71 | }
72 | }
73 |
74 | deleteElement() {
75 | let element = this.rb.getDataObject(this.id);
76 | if (element !== null) {
77 | this.rb.deleteDocElement(element);
78 | }
79 | }
80 |
81 | static createElement(id, data, elementType, panelPos, openPanelItem, rb) {
82 | let element;
83 | let properties = { draggable: true };
84 | if (elementType === DocElement.type.text) {
85 | element = new TextElement(id, data, rb);
86 | } else if (elementType === DocElement.type.line) {
87 | element = new LineElement(id, data, rb);
88 | } else if (elementType === DocElement.type.image) {
89 | element = new ImageElement(id, data, rb);
90 | } else if (elementType === DocElement.type.pageBreak) {
91 | element = new PageBreakElement(id, data, rb);
92 | } else if (elementType === DocElement.type.table) {
93 | element = new TableElement(id, data, rb);
94 | properties.hasChildren = true;
95 | } else if (elementType === DocElement.type.frame) {
96 | element = new FrameElement(id, data, rb);
97 | properties.hasChildren = true;
98 | } else if (elementType === DocElement.type.section) {
99 | element = new SectionElement(id, data, rb);
100 | properties.hasChildren = true;
101 | } else if (elementType === DocElement.type.barCode) {
102 | element = new BarCodeElement(id, data, rb);
103 | } else if (elementType === DocElement.type.watermarkText) {
104 | element = new WatermarkTextElement(id, data, rb);
105 | } else if (elementType === DocElement.type.watermarkImage) {
106 | element = new WatermarkImageElement(id, data, rb);
107 | }
108 | rb.addDocElement(element);
109 | let parentPanel = element.getContainer().getPanelItem();
110 | let panelItem = new MainPanelItem(elementType, parentPanel, element, properties, rb);
111 | element.setPanelItem(panelItem);
112 | parentPanel.insertChild(panelPos, panelItem);
113 | element.setup(openPanelItem);
114 | return element;
115 | }
116 |
117 | /**
118 | * Returns class name.
119 | * This can be useful for introspection when the class names are mangled
120 | * due to the webpack uglification process.
121 | * @returns {string}
122 | */
123 | getClassName() {
124 | return 'AddDeleteDocElementCmd';
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/commands/AddDeleteParameterCmd.js:
--------------------------------------------------------------------------------
1 | import Command from './Command';
2 | import Parameter from '../data/Parameter';
3 | import MainPanelItem from '../menu/MainPanelItem';
4 |
5 | /**
6 | * Command to add and delete a parameter.
7 | * @class
8 | */
9 | export default class AddDeleteParameterCmd extends Command {
10 | constructor(add, initialData, id, parentId, position, rb) {
11 | super();
12 | this.add = add;
13 | this.initialData = initialData;
14 | this.parentId = parentId;
15 | this.position = position;
16 | this.rb = rb;
17 | this.id = id;
18 | }
19 |
20 | getName() {
21 | if (this.add) {
22 | return 'Add parameter';
23 | } else {
24 | return 'Delete parameter';
25 | }
26 | }
27 |
28 | do() {
29 | if (this.add) {
30 | this.addParameter();
31 | } else {
32 | this.deleteParameter();
33 | }
34 | }
35 |
36 | undo() {
37 | if (this.add) {
38 | this.deleteParameter();
39 | } else {
40 | this.addParameter();
41 | }
42 | }
43 |
44 | addParameter() {
45 | let parent = this.rb.getDataObject(this.parentId);
46 | if (parent !== null) {
47 | let parameter = new Parameter(this.id, this.initialData, this.rb);
48 | this.rb.addParameter(parameter);
49 | const showOnlyNameType = parameter.getValue('showOnlyNameType');
50 | let panelItem = new MainPanelItem(
51 | 'parameter', parent.getPanelItem(), parameter, {
52 | hasChildren: !showOnlyNameType, showAdd: !showOnlyNameType, showDelete: !showOnlyNameType,
53 | draggable: true }, this.rb);
54 | panelItem.openParentItems();
55 | parameter.setPanelItem(panelItem);
56 | parent.getPanelItem().insertChild(this.position, panelItem);
57 | parameter.setup();
58 | this.rb.notifyEvent(parameter, Command.operation.add);
59 | }
60 | }
61 |
62 | deleteParameter() {
63 | let parameter = this.rb.getDataObject(this.id);
64 | if (parameter !== null) {
65 | this.initialData = parameter.toJS();
66 | this.rb.deleteParameter(parameter);
67 | }
68 | }
69 |
70 | /**
71 | * Returns class name.
72 | * This can be useful for introspection when the class names are mangled
73 | * due to the webpack uglification process.
74 | * @returns {string}
75 | */
76 | getClassName() {
77 | return 'AddDeleteParameterCmd';
78 | }
79 | }
--------------------------------------------------------------------------------
/src/commands/AddDeleteStyleCmd.js:
--------------------------------------------------------------------------------
1 | import Command from './Command';
2 | import Style from '../data/Style';
3 | import MainPanelItem from '../menu/MainPanelItem';
4 |
5 | /**
6 | * Command to add and delete a style.
7 | * @class
8 | */
9 | export default class AddDeleteStyleCmd extends Command {
10 | constructor(add, initialData, id, parentId, position, rb) {
11 | super();
12 | this.add = add;
13 | this.initialData = initialData;
14 | this.parentId = parentId;
15 | this.position = position;
16 | this.rb = rb;
17 | this.id = id;
18 | }
19 |
20 | getName() {
21 | if (this.add) {
22 | return 'Add style';
23 | } else {
24 | return 'Delete style';
25 | }
26 | }
27 |
28 | do() {
29 | if (this.add) {
30 | this.addStyle();
31 | } else {
32 | this.deleteStyle();
33 | }
34 | }
35 |
36 | undo() {
37 | if (this.add) {
38 | this.deleteStyle();
39 | } else {
40 | this.addStyle();
41 | }
42 | }
43 |
44 | addStyle() {
45 | let parent = this.rb.getDataObject(this.parentId);
46 | if (parent !== null) {
47 | let style = new Style(this.id, this.initialData, this.rb);
48 | let panelItem = new MainPanelItem('style', parent.getPanelItem(), style, { draggable: true }, this.rb);
49 | panelItem.openParentItems();
50 | style.setPanelItem(panelItem);
51 | parent.getPanelItem().insertChild(this.position, panelItem);
52 | this.rb.addStyle(style);
53 | }
54 | }
55 |
56 | deleteStyle() {
57 | let style = this.rb.getDataObject(this.id);
58 | if (style !== null) {
59 | this.initialData = style.toJS();
60 | this.rb.deleteStyle(style);
61 | }
62 | }
63 |
64 | /**
65 | * Returns class name.
66 | * This can be useful for introspection when the class names are mangled
67 | * due to the webpack uglification process.
68 | * @returns {string}
69 | */
70 | getClassName() {
71 | return 'AddDeleteStyleCmd';
72 | }
73 | }
--------------------------------------------------------------------------------
/src/commands/Command.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Base class for all commands.
3 | * @class
4 | */
5 | export default class Command {
6 | constructor() {
7 | }
8 |
9 | getName() {}
10 | do() {}
11 | undo() {}
12 |
13 | /**
14 | * Returns true if the command can replace the given other command.
15 | * @param {Command} otherCmd
16 | * @returns {boolean}
17 | */
18 | allowReplace(otherCmd) {
19 | return false;
20 | }
21 |
22 | /**
23 | * Must be called when the command replaces the other command.
24 |
25 | * This must only be called if allowReplace for the same command returned true.
26 | * @param {Command} otherCmd
27 | */
28 | replace(otherCmd) {
29 | }
30 | }
31 |
32 | Command.operation = {
33 | rename: 'rename',
34 | change: 'change',
35 | add: 'add',
36 | remove: 'remove',
37 | move: 'move'
38 | }
--------------------------------------------------------------------------------
/src/commands/CommandGroupCmd.js:
--------------------------------------------------------------------------------
1 | import Command from './Command';
2 | import SetValueCmd from './SetValueCmd';
3 |
4 | /**
5 | * Command container for multiple commands. All commands of this container will be executed
6 | * in a single do/undo operation.
7 | * @class
8 | */
9 | export default class CommandGroupCmd extends Command {
10 | constructor(name, rb) {
11 | super();
12 | this.name = name;
13 | this.rb = rb;
14 | this.commands = [];
15 | this.selectObjectIds = [];
16 | // command index in commands list for each entry in selectObjectIds
17 | this.selectionCmdIndex = [];
18 | }
19 |
20 | getName() {
21 | return this.name;
22 | }
23 |
24 | do() {
25 | if (this.selectionCmdIndex.length > 0) {
26 | // enable notifyEvent only for SetValue commands of last selected object.
27 | // the change event may only be fired for the last object because in between command execution
28 | // the objects contain different values (although they will be changed to the same value
29 | // with the last command) and this can lead to reseting the cursor caret in an input field
30 | // if the cursor is not at the end of the input text.
31 | let lastSelectionCmdIndex = this.selectionCmdIndex[this.selectionCmdIndex.length - 1];
32 | for (let i=0; i < this.commands.length; i++) {
33 | let cmd = this.commands[i];
34 | if (cmd instanceof SetValueCmd) {
35 | cmd.setNotifyChange(i >= lastSelectionCmdIndex);
36 | }
37 | }
38 | }
39 |
40 | for (let i=0; i < this.commands.length; i++) {
41 | this.commands[i].do();
42 | }
43 | this.selectObjects();
44 | }
45 |
46 | undo() {
47 | if (this.selectionCmdIndex.length > 0) {
48 | // enable notifyEvent only for SetValue commands of last selected object.
49 | // the change event may only be fired for the last object because in between command execution
50 | // the objects contain different values (although they will be changed to the same value
51 | // with the last command) and this can lead to reseting the cursor caret in an input field
52 | // if the cursor is not at the end of the input text.
53 | let secondSelectionCmdIndex = this.selectionCmdIndex.length > 1 ?
54 | this.selectionCmdIndex[1] : this.commands.length;
55 | for (let i=this.commands.length - 1; i >= 0; i--) {
56 | let cmd = this.commands[i];
57 | if (cmd instanceof SetValueCmd) {
58 | cmd.setNotifyChange(i < secondSelectionCmdIndex);
59 | }
60 | }
61 | }
62 |
63 | for (let i=this.commands.length - 1; i >= 0; i--) {
64 | this.commands[i].undo();
65 | }
66 | this.selectObjects();
67 | }
68 |
69 | addCommand(cmd) {
70 | if (cmd instanceof SetValueCmd) {
71 | // disable select for specific command, selection is handled in command group
72 | // when the commands are executed
73 | cmd.disableSelect();
74 | }
75 | this.commands.push(cmd);
76 | }
77 |
78 | /**
79 | * Add id of object which should be selected when this command group is executed.
80 | * @param {Number} objId - object id
81 | */
82 | addSelection(objId) {
83 | if (this.selectObjectIds.indexOf(objId) === -1) {
84 | this.selectObjectIds.push(objId);
85 | }
86 | // notification of change event will only be enabled for commands after
87 | // the last selection
88 | this.selectionCmdIndex.push(this.commands.length);
89 | }
90 |
91 | isEmpty() {
92 | return this.commands.length === 0;
93 | }
94 |
95 | getCommands() {
96 | return this.commands;
97 | }
98 |
99 | selectObjects() {
100 | let allObjectsSelected = true;
101 | for (let objId of this.selectObjectIds) {
102 | if (!this.rb.isSelectedObject(objId)) {
103 | allObjectsSelected = false;
104 | break;
105 | }
106 | }
107 | if (!allObjectsSelected) {
108 | // only select objects if at least one object is not already selected
109 | let firstSelection = true;
110 | for (let objId of this.selectObjectIds) {
111 | this.rb.selectObject(objId, firstSelection);
112 | firstSelection = false;
113 | }
114 | }
115 | }
116 |
117 | /**
118 | * Returns true if the command can replace the given other command.
119 | *
120 | * This information can be useful to avoid separate commands for every keystroke
121 | * in a text field and generate just one command for the whole changed text instead.
122 | * @param {Command} otherCmd
123 | * @returns {boolean}
124 | */
125 | allowReplace(otherCmd) {
126 | if (otherCmd instanceof CommandGroupCmd) {
127 | let otherCommands = otherCmd.getCommands();
128 | if (this.commands.length === otherCommands.length) {
129 | for (let i=0; i < this.commands.length; i++) {
130 | if (!this.commands[i].allowReplace(otherCommands[i])) {
131 | return false;
132 | }
133 | }
134 | // we are allowed to replace all commands of the command group
135 | return true;
136 | }
137 | }
138 | return false;
139 | }
140 |
141 | /**
142 | * Must be called when the command replaces the other command.
143 |
144 | * This must only be called if allowReplace for the same command returned true.
145 | * @param {Command} otherCmd
146 | */
147 | replace(otherCmd) {
148 | let otherCommands = otherCmd.getCommands();
149 | for (let i=0; i < this.commands.length; i++) {
150 | this.commands[i].replace(otherCommands[i]);
151 | }
152 | }
153 |
154 | /**
155 | * Returns class name.
156 | * This can be useful for introspection when the class names are mangled
157 | * due to the webpack uglification process.
158 | * @returns {string}
159 | */
160 | getClassName() {
161 | return 'CommandGroupCmd';
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/commands/MovePanelItemCmd.js:
--------------------------------------------------------------------------------
1 | import Command from './Command';
2 | import DocElement from '../elements/DocElement';
3 |
4 | /**
5 | * Command to move a menu panel item. In case the item is moved to a different container
6 | * (e.g. from content to header band) the corresponding doc element is moved to the new container as well.
7 | * @class
8 | */
9 | export default class MovePanelItemCmd extends Command {
10 | constructor(panelItem, moveToParentPanel, moveToPosition, rb) {
11 | super();
12 | this.objId = panelItem.getId();
13 | this.moveToParentId = moveToParentPanel.getId();
14 | this.moveToPosition = moveToPosition;
15 | this.oldParentId = panelItem.getParent().getId();
16 | this.oldPosition = panelItem.getSiblingPosition();
17 | this.oldContainerId = null;
18 | this.moveToContainerId = null;
19 | if (panelItem.getData() instanceof DocElement) {
20 | let docElement = panelItem.getData();
21 | this.oldContainerId = docElement.getValue('containerId');
22 | let moveToContainer = rb.getMainPanel().getContainerByItem(moveToParentPanel);
23 | if (moveToContainer !== null) {
24 | this.moveToContainerId = moveToContainer.getId();
25 | }
26 | }
27 | this.rb = rb;
28 | }
29 |
30 | getName() {
31 | return 'Move panel item';
32 | }
33 |
34 | do() {
35 | let pos = this.moveToPosition;
36 | if (this.moveToParentId === this.oldParentId && this.oldPosition < pos) {
37 | pos--;
38 | }
39 | this.moveTo(
40 | this.moveToParentId, pos,
41 | (this.moveToContainerId !== this.oldContainerId) ? this.moveToContainerId : null);
42 | }
43 |
44 | undo() {
45 | this.moveTo(
46 | this.oldParentId, this.oldPosition,
47 | (this.moveToContainerId !== this.oldContainerId) ? this.oldContainerId : null);
48 | }
49 |
50 | moveTo(toParentId, toPosition, toContainerId) {
51 | let obj = this.rb.getDataObject(this.objId);
52 | let parent = this.rb.getDataObject(toParentId);
53 | if (obj !== null && parent !== null) {
54 | obj.getPanelItem().moveToPosition(parent.getPanelItem(), toPosition);
55 | obj.getPanelItem().openParentItems();
56 | this.rb.notifyEvent(obj, Command.operation.move);
57 | }
58 | }
59 |
60 | /**
61 | * Returns class name.
62 | * This can be useful for introspection when the class names are mangled
63 | * due to the webpack uglification process.
64 | * @returns {string}
65 | */
66 | getClassName() {
67 | return 'MovePanelItemCmd';
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/commands/SetValueCmd.js:
--------------------------------------------------------------------------------
1 | import Command from './Command';
2 |
3 | /**
4 | * Command to set a single value of a data object.
5 | * @class
6 | */
7 | export default class SetValueCmd extends Command {
8 | constructor(objId, field, value, type, rb) {
9 | super();
10 | this.objId = objId;
11 | this.field = field;
12 | this.value = value;
13 | this.type = type;
14 | this.rb = rb;
15 |
16 | let obj = rb.getDataObject(objId);
17 | this.oldValue = obj.getValue(field);
18 | this.firstExecution = true;
19 | this.select = true;
20 | this.notifyChange = true;
21 | }
22 |
23 | getName() {
24 | return 'Set value';
25 | }
26 |
27 | getObjId() {
28 | return this.objId;
29 | }
30 |
31 | do() {
32 | if (!this.firstExecution && this.select) {
33 | this.rb.selectObject(this.objId, true);
34 | }
35 | this.setValue(this.value);
36 | this.firstExecution = false;
37 | }
38 |
39 | undo() {
40 | if (this.select) {
41 | this.rb.selectObject(this.objId, true);
42 | }
43 | this.setValue(this.oldValue);
44 | }
45 |
46 | setValue(value) {
47 | let obj = this.rb.getDataObject(this.objId);
48 | obj.setValue(this.field, value);
49 |
50 | if (this.field === 'name') {
51 | const elMenuItemName = document.getElementById(`rbro_menu_item_name${this.objId}`);
52 | elMenuItemName.textContent = value;
53 | elMenuItemName.setAttribute('title', value);
54 | this.rb.notifyEvent(obj, Command.operation.rename);
55 | }
56 | // do not send event notification for setting richText value the first time because
57 | // this would loose current status (cursor position) in rich text editor while typing
58 | if (this.notifyChange && (this.type !== SetValueCmd.type.richText || !this.firstExecution)) {
59 | this.rb.notifyEvent(obj, Command.operation.change, this.field);
60 | }
61 | }
62 |
63 | /**
64 | * Disables selection of the element containing the changed field. By default an element is automatically
65 | * selected after one of its fields was changed.
66 | */
67 | disableSelect() {
68 | this.select = false;
69 | }
70 |
71 | setNotifyChange(notify) {
72 | this.notifyChange = notify;
73 | }
74 |
75 | /**
76 | * Returns true if the command can replace the given other command because they target the same field.
77 | *
78 | * This information can be useful to avoid separate commands for every keystroke
79 | * in a text field and generate just one command for the whole changed text instead.
80 | * @param {Command} otherCmd
81 | * @returns {boolean}
82 | */
83 | allowReplace(otherCmd) {
84 | return (otherCmd instanceof SetValueCmd &&
85 | (this.type === SetValueCmd.type.text || this.type === SetValueCmd.type.richText) &&
86 | this.objId === otherCmd.objId && this.field === otherCmd.field);
87 | }
88 |
89 | /**
90 | * Must be called when the command replaces the other command.
91 |
92 | * This must only be called if allowReplace for the same command returned true.
93 | * @param {Command} otherCmd
94 | */
95 | replace(otherCmd) {
96 | this.oldValue = otherCmd.oldValue;
97 | }
98 |
99 | /**
100 | * Returns class name.
101 | * This can be useful for introspection when the class names are mangled
102 | * due to the webpack uglification process.
103 | * @returns {string}
104 | */
105 | getClassName() {
106 | return 'SetValueCmd';
107 | }
108 | }
109 |
110 | SetValueCmd.type = {
111 | text: 'text',
112 | richText: 'richText',
113 | select: 'select',
114 | file: 'file',
115 | filename: 'filename',
116 | checkbox: 'checkbox',
117 | button: 'button',
118 | buttonGroup: 'buttonGroup', // one button inside a group of buttons with only one active button
119 | color: 'color',
120 | internal: 'internal'
121 | };
122 |
--------------------------------------------------------------------------------
/src/container/Band.js:
--------------------------------------------------------------------------------
1 | import Container from './Container';
2 | import DocElement from '../elements/DocElement';
3 | import * as utils from '../utils';
4 |
5 | /**
6 | * Standard band container for header, content and footer band.
7 | * @class
8 | */
9 | export default class Band extends Container {
10 | constructor(bandType, section, id, name, rb) {
11 | super(id, name, rb);
12 | this.panelItem = null;
13 | this.bandType = bandType;
14 | this.section = section;
15 | if (!section) {
16 | if (bandType === Band.bandType.header) {
17 | this.id = '0_header';
18 | this.name = rb.getLabel('bandHeader');
19 | } else if (bandType === Band.bandType.content) {
20 | this.id = '0_content';
21 | this.name = rb.getLabel('bandContent');
22 | this.allowAllElements = true;
23 | } else if (bandType === Band.bandType.footer) {
24 | this.id = '0_footer';
25 | this.name = rb.getLabel('bandFooter');
26 | }
27 | }
28 | this.el = null;
29 | }
30 |
31 | /**
32 | * Called after initialization is finished.
33 | */
34 | setup() {
35 | if (!this.section) {
36 | this.el = this.rb.getDocument().getElement(this.bandType);
37 | this.elContent = this.el;
38 | }
39 | }
40 |
41 | /**
42 | * Returns true if the given element type can be added to this container.
43 | * @param {String} elementType
44 | */
45 | isElementAllowed(elementType) {
46 | if (elementType === DocElement.type.tableText) {
47 | return false;
48 | }
49 | return (this.bandType === Band.bandType.content ||
50 | (elementType !== DocElement.type.pageBreak && elementType !== DocElement.type.table &&
51 | elementType !== DocElement.type.section));
52 | }
53 |
54 | /**
55 | * Returns absolute container offset.
56 | * @returns {Object} x and y offset coordinates.
57 | */
58 | getOffset() {
59 | let y = 0;
60 | if (this.section) {
61 | if (this.owner !== null) {
62 | let absPos = this.owner.getAbsolutePosition();
63 | y = absPos.y;
64 | }
65 | } else {
66 | let docProperties = this.rb.getDocumentProperties();
67 | if (this.bandType === Band.bandType.content && docProperties.getValue('header')) {
68 | y = utils.convertInputToNumber(docProperties.getValue('headerSize'));
69 | } else if (this.bandType === Band.bandType.footer) {
70 | y = this.rb.getDocument().getHeight() -
71 | utils.convertInputToNumber(docProperties.getValue('footerSize'));
72 | }
73 | }
74 | return { x: 0, y: y };
75 | }
76 |
77 | /**
78 | * Returns container size.
79 | * @returns {Object} width and height of container.
80 | */
81 | getSize() {
82 | let documentProperties = this.rb.getDocumentProperties();
83 | let width = documentProperties.getValue('width') -
84 | documentProperties.getValue('marginLeftVal') - documentProperties.getValue('marginRightVal');
85 | let height = 0;
86 | if (this.section) {
87 | if (this.owner !== null) {
88 | height = this.owner.getValue('heightVal');
89 | }
90 | } else if (this.bandType === Band.bandType.header) {
91 | height = documentProperties.getValue('headerSizeVal');
92 | } else if (this.bandType === Band.bandType.content) {
93 | height = documentProperties.getValue('height') - documentProperties.getValue('headerSizeVal') -
94 | documentProperties.getValue('footerSizeVal') -
95 | documentProperties.getValue('marginTopVal') - documentProperties.getValue('marginBottomVal');
96 | } else if (this.bandType === Band.bandType.footer) {
97 | height = documentProperties.getValue('footerSizeVal');
98 | }
99 | return { width: width, height: height };
100 | }
101 |
102 | /**
103 | * Returns container content size. Same as container size.
104 | * @returns {Object} width and height of container.
105 | */
106 | getContentSize() {
107 | return this.getSize();
108 | }
109 |
110 | isInside(posX, posY) {
111 | if (this.section && this.owner !== null && this.owner && !this.owner.isVisible()) {
112 | return false;
113 | }
114 | return super.isInside(posX, posY);
115 | }
116 | }
117 |
118 | Band.bandType = {
119 | header: 'header',
120 | content: 'content',
121 | footer: 'footer'
122 | };
123 |
--------------------------------------------------------------------------------
/src/container/Container.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Container can contain doc elements. The doc elements are always relative to the container offset.
3 | * @class
4 | */
5 | export default class Container {
6 | constructor(id, name, rb) {
7 | this.rb = rb;
8 | this.id = id;
9 | this.panelItem = null;
10 | this.name = name;
11 | this.el = null;
12 | this.elContent = null;
13 | this.owner = null;
14 | this.level = 0; // number of containers "above"
15 | this.parent = null; // parent container
16 | }
17 |
18 | init(owner) {
19 | this.owner = owner;
20 | this.el = owner.getElement();
21 | this.elContent = owner.getContentElement();
22 | this.panelItem = owner.getPanelItem();
23 | this.parent = owner.getContainer();
24 | this.initLevel();
25 | }
26 |
27 | /**
28 | * Set nested level of container.
29 | * Should be alled after initialization and whenever the container is moved to a new parent.
30 | */
31 | initLevel() {
32 | this.level = 0;
33 | let parent = this.parent;
34 | while (parent !== null) {
35 | this.level++;
36 | parent = parent.getParent();
37 | }
38 | }
39 |
40 | /**
41 | * Called after initialization is finished.
42 | */
43 | setup() {
44 | }
45 |
46 | remove() {
47 | }
48 |
49 | appendElement(el) {
50 | if (this.elContent !== null) {
51 | this.elContent.append(el);
52 | }
53 | }
54 |
55 | getId() {
56 | return this.id;
57 | }
58 |
59 | getName() {
60 | return this.name;
61 | }
62 |
63 | getPanelItem() {
64 | return this.panelItem;
65 | }
66 |
67 | setPanelItem(panelItem) {
68 | this.panelItem = panelItem;
69 | }
70 |
71 | getLevel() {
72 | return this.level;
73 | }
74 |
75 | getParent() {
76 | return this.parent;
77 | }
78 |
79 | /**
80 | * Must be called when the container is moved to a new parent
81 | * (i.e. element of container is moved into another container).
82 | * @param {DocElement} parent
83 | */
84 | setParent(parent) {
85 | this.parent = parent;
86 | // because the parent was changed the container can now have a different container level
87 | this.initLevel();
88 | }
89 |
90 | /**
91 | * Return true if this container is a child of the given container.
92 | * @param {Container} container
93 | * @return {Boolean}
94 | */
95 | isChildOf(container) {
96 | let parent = this.getParent();
97 | while (parent !== null) {
98 | if (parent === container) {
99 | return true;
100 | }
101 | parent = parent.getParent();
102 | }
103 | return false;
104 | }
105 |
106 | isSelected() {
107 | if (this.owner !== null && this.rb.isSelectedObject(this.owner.getId())) {
108 | return true;
109 | }
110 | return false;
111 | }
112 |
113 | /**
114 | * Returns true if the given element type can be added to this container.
115 | * @param {String} elementType
116 | */
117 | isElementAllowed(elementType) {
118 | return false;
119 | }
120 |
121 | /**
122 | * Update container style when an element is currently dragged over this container.
123 | */
124 | dragOver() {
125 | if (this.el !== null) {
126 | this.el.classList.add('rbroElementDragOver');
127 | }
128 | }
129 |
130 | /**
131 | * Returns absolute container offset.
132 | * @returns {Object} x and y offset coordinates.
133 | */
134 | getOffset() {
135 | return { x: 0, y: 0 };
136 | }
137 |
138 | /**
139 | * Returns offset relative to other container.
140 | * @param {Container} otherContainer
141 | * @returns {Object} x and y offset coordinates.
142 | */
143 | getOffsetTo(otherContainer) {
144 | if (otherContainer !== null && otherContainer !== this) {
145 | const offset = this.getOffset();
146 | const otherOffset = otherContainer.getOffset();
147 | return { x: offset.x - otherOffset.x, y: offset.y - otherOffset.y };
148 | }
149 | return { x: 0, y: 0 };
150 | }
151 |
152 | /**
153 | * Returns container size.
154 | * @returns {Object} width and height of container.
155 | */
156 | getSize() {
157 | return { width: 0, height: 0 };
158 | }
159 |
160 | /**
161 | * Returns container content size.
162 | * @returns {Object} width and height of container content area.
163 | */
164 | getContentSize() {
165 | return { width: 0, height: 0 };
166 | }
167 |
168 | /**
169 | * Returns true if given absolute position is inside container.
170 | * @param {Number} posX - absolute x coordinate.
171 | * @param {Number} posY - absolute y coordinate.
172 | */
173 | isInside(posX, posY) {
174 | const offset = this.getOffset();
175 | const size = this.getSize();
176 | posX -= offset.x;
177 | posY -= offset.y;
178 | return (posX >= 0 && posY >= 0 && posX < size.width && posY < size.height);
179 | }
180 |
181 | clearErrors() {
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/container/Frame.js:
--------------------------------------------------------------------------------
1 | import Container from './Container';
2 | import DocElement from '../elements/DocElement';
3 |
4 | /**
5 | * A frame container which can contain various doc elements.
6 | * @class
7 | */
8 | export default class Frame extends Container {
9 | constructor(id, name, rb) {
10 | super(id, name, rb);
11 | }
12 |
13 | /**
14 | * Called after initialization is finished.
15 | */
16 | setup() {
17 | this.el = this.rb.getDocument().getElement(this.band);
18 | }
19 |
20 | /**
21 | * Returns true if the given element type can be added to this container.
22 | * @param {String} elementType
23 | */
24 | isElementAllowed(elementType) {
25 | return elementType !== DocElement.type.pageBreak && elementType !== DocElement.type.frame &&
26 | elementType !== DocElement.type.section;
27 | }
28 |
29 | /**
30 | * Returns absolute container offset.
31 | * @returns {Object} x and y offset coordinates.
32 | */
33 | getOffset() {
34 | let x = 0, y = 0;
35 | if (this.owner !== null) {
36 | x = this.owner.getValue('xVal');
37 | y = this.owner.getValue('yVal');
38 | }
39 | if (this.parent !== null) {
40 | let offset = this.parent.getOffset();
41 | x += offset.x;
42 | y += offset.y;
43 | }
44 | return { x: x, y: y };
45 | }
46 |
47 | /**
48 | * Returns container size.
49 | * @returns {Object} width and height of container.
50 | */
51 | getSize() {
52 | let width = 0, height = 0;
53 | if (this.owner !== null) {
54 | width = this.owner.getValue('widthVal');
55 | height = this.owner.getValue('heightVal');
56 | }
57 | return { width: width, height: height };
58 | }
59 |
60 | /**
61 | * Returns container content size.
62 | * This is the container minus optional borders, thus the available area for
63 | * elements inside the frame.
64 | * @returns {Object} width and height of container content area.
65 | */
66 | getContentSize() {
67 | let width = 0, height = 0;
68 | if (this.owner !== null) {
69 | width = this.owner.getValue('widthVal');
70 | height = this.owner.getValue('heightVal');
71 | let borderWidth = this.owner.getValue('borderWidthVal');
72 | if (this.owner.getValue('borderLeft')) {
73 | width -= borderWidth;
74 | }
75 | if (this.owner.getValue('borderRight')) {
76 | width -= borderWidth;
77 | }
78 | if (this.owner.getValue('borderTop')) {
79 | height -= borderWidth;
80 | }
81 | if (this.owner.getValue('borderBottom')) {
82 | height -= borderWidth;
83 | }
84 | }
85 | return { width: width, height: height };
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/container/Page.js:
--------------------------------------------------------------------------------
1 | import Container from './Container';
2 |
3 | /**
4 | * Page container to hold elements for page background, e.g. watermark elements.
5 | * @class
6 | */
7 | export default class Page extends Container {
8 | constructor(id, name, rb) {
9 | super(id, name, rb);
10 | }
11 |
12 | /**
13 | * Called after initialization is finished.
14 | */
15 | setup() {
16 | this.el = this.rb.getDocument().getPageElement();
17 | this.elContent = this.el;
18 | }
19 |
20 | /**
21 | * Returns container size.
22 | * @returns {Object} width and height of container.
23 | */
24 | getSize() {
25 | const documentProperties = this.rb.getDocumentProperties();
26 | return { width: documentProperties.getValue('width'), height: documentProperties.getValue('height') };
27 | }
28 |
29 | /**
30 | * Returns container content size. Same as container size.
31 | * @returns {Object} width and height of container.
32 | */
33 | getContentSize() {
34 | return this.getSize();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/data/DocumentProperties.js:
--------------------------------------------------------------------------------
1 | import SectionElement from '../elements/SectionElement';
2 | import * as utils from '../utils';
3 |
4 | /**
5 | * Data object containing all document properties like page size, margins, etc.
6 | * @class
7 | */
8 | export default class DocumentProperties {
9 | constructor(rb) {
10 | this.rb = rb;
11 | this.id = '0_document_properties';
12 | this.panelItem = null;
13 | this.errors = [];
14 |
15 | this.pageFormat = DocumentProperties.pageFormat.A4;
16 | this.pageWidth = '';
17 | this.pageHeight = '';
18 | this.unit = DocumentProperties.unit.mm;
19 | this.orientation = DocumentProperties.orientation.portrait;
20 | this.contentHeight = '';
21 | this.marginLeft = '';
22 | this.marginLeftVal = 0;
23 | this.marginTop = '';
24 | this.marginTopVal = 0;
25 | this.marginRight = '';
26 | this.marginRightVal = 0;
27 | this.marginBottom = '';
28 | this.marginBottomVal = 0;
29 |
30 | this.header = true;
31 | this.headerSize = '80';
32 | this.headerDisplay = DocumentProperties.display.always;
33 | this.footer = true;
34 | this.footerSize = '80';
35 | this.footerDisplay = DocumentProperties.display.always;
36 |
37 | this.headerSizeVal = this.header ? utils.convertInputToNumber(this.headerSize) : 0;
38 | this.footerSizeVal = this.footer ? utils.convertInputToNumber(this.footerSize) : 0;
39 |
40 | this.watermark = false;
41 |
42 | this.patternLocale = rb.getProperty('patternLocale');
43 | this.patternCurrencySymbol = rb.getProperty('patternCurrencySymbol');
44 | this.patternNumberGroupSymbol = rb.getProperty('patternNumberGroupSymbol');
45 |
46 | // width and height in pixel
47 | this.width = 0;
48 | this.height = 0;
49 | }
50 |
51 | setInitialData(initialData) {
52 | for (let key in initialData) {
53 | if (initialData.hasOwnProperty(key) && this.hasOwnProperty(key)) {
54 | this[key] = initialData[key];
55 | }
56 | }
57 | this.headerSizeVal = this.header ? utils.convertInputToNumber(this.headerSize) : 0;
58 | this.footerSizeVal = this.footer ? utils.convertInputToNumber(this.footerSize) : 0;
59 | this.marginLeftVal = utils.convertInputToNumber(this.marginLeft);
60 | this.marginTopVal = utils.convertInputToNumber(this.marginTop);
61 | this.marginRightVal = utils.convertInputToNumber(this.marginRight);
62 | this.marginBottomVal = utils.convertInputToNumber(this.marginBottom);
63 | }
64 |
65 | /**
66 | * Called after initialization is finished.
67 | */
68 | setup() {
69 | let size = this.getPageSize();
70 | this.updatePageSize(size);
71 | this.rb.getDocument().updatePageMargins();
72 | this.rb.getDocument().updateHeader();
73 | this.rb.getDocument().updateFooter();
74 | this.updateHeader();
75 | this.updateFooter();
76 | this.updateWatermark();
77 | }
78 |
79 | /**
80 | * Returns all data fields of this object. The fields are used when serializing the object.
81 | * @returns {String[]}
82 | */
83 | getFields() {
84 | return [
85 | 'pageFormat', 'pageWidth', 'pageHeight', 'unit', 'orientation',
86 | 'contentHeight', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom',
87 | 'header', 'headerSize', 'headerDisplay', 'footer', 'footerSize', 'footerDisplay',
88 | 'watermark', 'patternLocale', 'patternCurrencySymbol', 'patternNumberGroupSymbol',
89 | ];
90 | }
91 |
92 | /**
93 | * Returns all fields of this object that can be modified in the properties panel.
94 | * @returns {String[]}
95 | */
96 | getProperties() {
97 | return this.getFields();
98 | }
99 |
100 |
101 | getId() {
102 | return this.id;
103 | }
104 |
105 | getName() {
106 | return this.rb.getLabel('documentProperties');
107 | }
108 |
109 | getPanelItem() {
110 | return this.panelItem;
111 | }
112 |
113 | setPanelItem(panelItem) {
114 | this.panelItem = panelItem;
115 | }
116 |
117 | getValue(field) {
118 | return this[field];
119 | }
120 |
121 | setValue(field, value) {
122 | this[field] = value;
123 | if (field === 'marginLeft' || field === 'marginTop' || field === 'marginRight' || field === 'marginBottom') {
124 | this[field + 'Val'] = utils.convertInputToNumber(value);
125 | this.rb.getDocument().updatePageMargins();
126 | this.rb.getDocument().updateHeader();
127 | this.rb.getDocument().updateFooter();
128 | } else if (field === 'header') {
129 | this.updateHeader();
130 | } else if (field === 'footer') {
131 | this.updateFooter();
132 | } else if (field === 'watermark') {
133 | this.updateWatermark();
134 | }
135 |
136 | if (field === 'header' || field === 'headerSize') {
137 | this.rb.getDocument().updateHeader();
138 | this.headerSizeVal = this.header ? utils.convertInputToNumber(this.headerSize) : 0;
139 | } else if (field === 'footer' || field === 'footerSize') {
140 | this.rb.getDocument().updateFooter();
141 | this.footerSizeVal = this.footer ? utils.convertInputToNumber(this.footerSize) : 0;
142 | } else if (field === 'pageFormat' ||field === 'pageWidth' || field === 'pageHeight' || field === 'unit' ||
143 | field === 'orientation' || field === 'contentHeight' ||
144 | field === 'marginTop' || field === 'marginBottom') {
145 | let size = this.getPageSize();
146 | this.updatePageSize(size);
147 | }
148 | }
149 |
150 | /**
151 | * Returns value to use for updating input control.
152 | * Can be overridden in case update value can be different from internal value, e.g.
153 | * width for table cells with colspan > 1.
154 | * @param {String} field - field name.
155 | * @param {String} value - value for update.
156 | */
157 | getUpdateValue(field, value) {
158 | return value;
159 | }
160 |
161 | updatePageSize(size) {
162 | this.width = size.width;
163 | this.height = size.height;
164 | this.rb.getDocument().updatePageSize(size.width, size.height);
165 |
166 | // update width of all elements which cover full width
167 | let docElements = this.rb.getDocElements(true);
168 | for (let docElement of docElements) {
169 | if (docElement instanceof SectionElement) {
170 | docElement.setWidth(size.width);
171 | }
172 | }
173 | this.rb.getDocument().pageSizeChanged();
174 | }
175 |
176 | updateHeader() {
177 | if (this.header) {
178 | this.rb.getMainPanel().showHeader();
179 | } else {
180 | this.rb.getMainPanel().hideHeader();
181 | }
182 | }
183 |
184 | updateFooter() {
185 | if (this.footer) {
186 | this.rb.getMainPanel().showFooter();
187 | } else {
188 | this.rb.getMainPanel().hideFooter();
189 | }
190 | }
191 |
192 | updateWatermark() {
193 | if (this.watermark) {
194 | this.rb.getMainPanel().showWatermarks();
195 | } else {
196 | this.rb.getMainPanel().hideWatermarks();
197 | }
198 | }
199 |
200 | /**
201 | * Returns page size in pixels at 72 dpi.
202 | * @returns {Object} width, height
203 | */
204 | getPageSize() {
205 | let pageWidth;
206 | let pageHeight;
207 | let unit;
208 | let dpi = 72;
209 | if (this.pageFormat === DocumentProperties.pageFormat.A4) {
210 | if (this.orientation === DocumentProperties.orientation.portrait) {
211 | pageWidth = 210;
212 | pageHeight = 297;
213 | } else {
214 | pageWidth = 297;
215 | pageHeight = 210;
216 | }
217 | unit = DocumentProperties.unit.mm;
218 | } else if (this.pageFormat === DocumentProperties.pageFormat.A5) {
219 | if (this.orientation === DocumentProperties.orientation.portrait) {
220 | pageWidth = 148;
221 | pageHeight = 210;
222 | } else {
223 | pageWidth = 210;
224 | pageHeight = 148;
225 | }
226 | unit = DocumentProperties.unit.mm;
227 | } else if (this.pageFormat === DocumentProperties.pageFormat.letter) {
228 | if (this.orientation === DocumentProperties.orientation.portrait) {
229 | pageWidth = 8.5;
230 | pageHeight = 11;
231 | } else {
232 | pageWidth = 11;
233 | pageHeight = 8.5;
234 | }
235 | unit = DocumentProperties.unit.inch;
236 | } else {
237 | pageWidth = utils.convertInputToNumber(this.pageWidth);
238 | pageHeight = utils.convertInputToNumber(this.pageHeight);
239 | unit = this.unit;
240 | }
241 | if (unit === DocumentProperties.unit.mm) {
242 | pageWidth = Math.round((dpi * pageWidth) / 25.4);
243 | pageHeight = Math.round((dpi * pageHeight) / 25.4);
244 | } else {
245 | pageWidth = Math.round(dpi * pageWidth);
246 | pageHeight = Math.round(dpi * pageHeight);
247 | }
248 | if (this.contentHeight.trim() !== '') {
249 | pageHeight = utils.convertInputToNumber(this.contentHeight) +
250 | this.marginTopVal + this.marginBottomVal + this.headerSizeVal + this.footerSizeVal;
251 | }
252 | return { width: pageWidth, height: pageHeight };
253 | }
254 |
255 | /**
256 | * Returns size of content band without any margins.
257 | * @returns {Object} width, height
258 | */
259 | getContentSize() {
260 | let size = this.getPageSize();
261 | let height;
262 | if (this.contentHeight.trim() !== '') {
263 | height = utils.convertInputToNumber(this.contentHeight);
264 | } else {
265 | height = size.height - this.marginTopVal - this.marginBottomVal -
266 | this.headerSizeVal - this.footerSizeVal;
267 | }
268 | return { width: size.width - this.marginLeftVal - this.marginRightVal,
269 | height: height };
270 | }
271 |
272 | addError(error) {
273 | this.errors.push(error);
274 | }
275 |
276 | clearErrors() {
277 | this.errors = [];
278 | }
279 |
280 | getErrors() {
281 | return this.errors;
282 | }
283 |
284 | remove() {
285 | }
286 |
287 | select() {
288 | }
289 |
290 | deselect() {
291 | }
292 |
293 | toJS() {
294 | let ret = {};
295 | for (let field of this.getFields()) {
296 | ret[field] = this.getValue(field);
297 | }
298 | return ret;
299 | }
300 |
301 | /**
302 | * Returns class name.
303 | * This can be useful for introspection when the class names are mangled
304 | * due to the webpack uglification process.
305 | * @returns {string}
306 | */
307 | getClassName() {
308 | return 'DocumentProperties';
309 | }
310 | }
311 |
312 | DocumentProperties.outputFormat = {
313 | pdf: 'pdf',
314 | xlsx: 'xlsx'
315 | };
316 |
317 | DocumentProperties.pageFormat = {
318 | A4: 'A4',
319 | A5: 'A5',
320 | letter: 'letter', // 215.9 x 279.4 mm
321 | userDefined: 'user_defined'
322 | };
323 |
324 | DocumentProperties.unit = {
325 | mm: 'mm',
326 | inch: 'inch'
327 | };
328 |
329 | DocumentProperties.orientation = {
330 | portrait: 'portrait',
331 | landscape: 'landscape'
332 | };
333 |
334 | DocumentProperties.display = {
335 | always: 'always',
336 | notOnFirstPage: 'not_on_first_page'
337 | };
338 |
--------------------------------------------------------------------------------
/src/data/Style.js:
--------------------------------------------------------------------------------
1 | import AddDeleteStyleCmd from "../commands/AddDeleteStyleCmd";
2 | import Command from "../commands/Command";
3 | import SetValueCmd from "../commands/SetValueCmd";
4 | import DocElement from "../elements/DocElement";
5 | import * as utils from "../utils";
6 |
7 | /**
8 | * Style data object. Contains all text styles (alignment, border, etc.):
9 | * @class
10 | */
11 | export default class Style {
12 | constructor(id, initialData, rb) {
13 | this.rb = rb;
14 | this.id = id;
15 | this.name = rb.getLabel('style');
16 | this.panelItem = null;
17 | this.errors = [];
18 |
19 | this.type = Style.type.text;
20 | this.bold = false;
21 | this.italic = false;
22 | this.underline = false;
23 | this.strikethrough = false;
24 | this.horizontalAlignment = Style.alignment.left;
25 | this.verticalAlignment = Style.alignment.top;
26 | this.color = '#000000';
27 | this.textColor = '#000000';
28 | this.backgroundColor = '';
29 | this.alternateBackgroundColor = '';
30 | this.font = rb.getProperty('defaultFont');
31 | this.fontSize = 12;
32 | this.lineSpacing = 1;
33 | this.border = 'grid';
34 | this.borderColor = '#000000';
35 | this.borderWidth = '1';
36 | this.borderAll = false;
37 | this.borderLeft = false;
38 | this.borderTop = false;
39 | this.borderRight = false;
40 | this.borderBottom = false;
41 | this.paddingLeft = '';
42 | this.paddingTop = '';
43 | this.paddingRight = '';
44 | this.paddingBottom = '';
45 |
46 | this.borderWidthVal = 0;
47 |
48 | this.setInitialData(initialData);
49 | }
50 |
51 | setInitialData(initialData) {
52 | for (let key in initialData) {
53 | if (initialData.hasOwnProperty(key) && this.hasOwnProperty(key)) {
54 | this[key] = initialData[key];
55 | }
56 | }
57 | this.borderWidthVal = utils.convertInputToNumber(this.borderWidth);
58 | }
59 |
60 | /**
61 | * Returns all data fields of this object. The fields are used when serializing the object.
62 | * @returns {String[]}
63 | */
64 | getFields() {
65 | const fields = this.getProperties();
66 | fields.splice(0, 0, 'id');
67 | return fields;
68 | }
69 |
70 | /**
71 | * Returns all fields of this object that can be modified in the properties panel.
72 | * @returns {String[]}
73 | */
74 | getProperties() {
75 | return [
76 | 'name', 'type', 'bold', 'italic', 'underline', 'strikethrough',
77 | 'horizontalAlignment', 'verticalAlignment',
78 | 'color', 'textColor', 'backgroundColor', 'alternateBackgroundColor',
79 | 'font', 'fontSize', 'lineSpacing', 'borderColor', 'borderWidth',
80 | 'borderAll', 'borderLeft', 'borderTop', 'borderRight', 'borderBottom', 'border',
81 | 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom'
82 | ];
83 | }
84 |
85 | /**
86 | * Returns all fields of this object that are style properties which are also available .
87 | * @returns {String[]}
88 | */
89 | getStyleProperties() {
90 | // get all properties except name and type
91 | return this.getProperties().slice(2);
92 | }
93 |
94 | getId() {
95 | return this.id;
96 | }
97 |
98 | getName() {
99 | return this.name;
100 | }
101 |
102 | getPanelItem() {
103 | return this.panelItem;
104 | }
105 |
106 | setPanelItem(panelItem) {
107 | this.panelItem = panelItem;
108 | }
109 |
110 | getValue(field) {
111 | return this[field];
112 | }
113 |
114 | setValue(field, value) {
115 | this[field] = value;
116 |
117 | if (field.indexOf('border') !== -1) {
118 | if (field === 'borderWidth') {
119 | this.borderWidthVal = utils.convertInputToNumber(value);
120 | }
121 | Style.setBorderValue(this, field, '', value, this.rb);
122 | }
123 |
124 | if (field !== 'name') {
125 | for (let docElement of this.rb.getDocElements(true)) {
126 | docElement.updateChangedStyle(this.getId());
127 | }
128 | }
129 | }
130 |
131 | /**
132 | * Returns value to use for updating input control.
133 | * Can be overridden in case update value can be different from internal value, e.g.
134 | * width for table cells with colspan > 1.
135 | * @param {String} field - field name.
136 | * @param {String} value - value for update.
137 | */
138 | getUpdateValue(field, value) {
139 | return value;
140 | }
141 |
142 | /**
143 | * Adds commands to command group parameter to set changed property value
144 | * for all document elements using this style.
145 | *
146 | * This should be called when a property of this style was changed so the property
147 | * will be updated for all document elements as well.
148 | *
149 | * @param {String} field - changed field of this style.
150 | * @param {Object} value - new value for given field.
151 | * @param {String} type - property type for SetValueCmd.
152 | * @param {CommandGroupCmd} cmdGroup - commands will be added to this command group.
153 | */
154 | addCommandsForChangedProperty(field, value, type, cmdGroup) {
155 | let strId = '' + this.getId();
156 | for (let docElement of this.rb.getDocElements(true)) {
157 | if (docElement.hasProperty('styleId')) {
158 | if (docElement.getValue('styleId') === strId &&
159 | docElement.getValue(field) !== value) {
160 | let cmd = new SetValueCmd(
161 | docElement.getId(), field, value, type, this.rb);
162 | cmd.disableSelect();
163 | cmdGroup.addCommand(cmd);
164 | }
165 | if (docElement.getValue('cs_styleId') === strId &&
166 | docElement.getValue('cs_' + field) !== value) {
167 | let cmd = new SetValueCmd(
168 | docElement.getId(), 'cs_' + field, value, type, this.rb);
169 | cmd.disableSelect();
170 | cmdGroup.addCommand(cmd);
171 | }
172 | }
173 | }
174 | }
175 |
176 | /**
177 | * Adds commands to command group parameter to delete this style and reset any references to it.
178 | * @param {CommandGroupCmd} cmdGroup - commands for deletion of style will be added to this command group.
179 | */
180 | addCommandsForDelete(cmdGroup) {
181 | let cmd;
182 | let elements = this.rb.getDocElements(true);
183 | for (let element of elements) {
184 | if ((element.getElementType() === DocElement.type.text ||
185 | element.getElementType() === DocElement.type.tableText) && element.getValue('styleId') &&
186 | utils.convertInputToNumber(element.getValue('styleId')) === this.id) {
187 | cmd = new SetValueCmd(
188 | element.getId(), 'styleId', '', SetValueCmd.type.text, this.rb);
189 | cmdGroup.addCommand(cmd);
190 | }
191 | }
192 | cmd = new AddDeleteStyleCmd(
193 | false, this.toJS(), this.getId(), this.getPanelItem().getParent().getId(),
194 | this.getPanelItem().getSiblingPosition(), this.rb);
195 | cmdGroup.addCommand(cmd);
196 | }
197 |
198 | addError(error) {
199 | this.errors.push(error);
200 | }
201 |
202 | clearErrors() {
203 | this.errors = [];
204 | }
205 |
206 | getErrors() {
207 | return this.errors;
208 | }
209 |
210 | remove() {
211 | }
212 |
213 | select() {
214 | }
215 |
216 | deselect() {
217 | }
218 |
219 | toJS() {
220 | let ret = {};
221 | for (let field of this.getFields()) {
222 | ret[field] = this.getValue(field);
223 | }
224 | return ret;
225 | }
226 |
227 | /**
228 | * Updates GUI for border settings and borderAll setting of object.
229 | * @param {Object} obj - document element of which the border settings will be updated.
230 | * @param {String} field - border field which was modified.
231 | * @param {String} fieldPrefix - prefix of field to reuse style settings for different
232 | * sections (e.g. for conditional style).
233 | * @param {Boolean} value - new value for specified field.
234 | * @param {ReportBro} rb - ReportBro instance.
235 | */
236 | static setBorderValue(obj, field, fieldPrefix, value, rb) {
237 | let fieldWithoutPrefix = field;
238 | if (fieldPrefix.length > 0) {
239 | fieldWithoutPrefix = fieldWithoutPrefix.substring(fieldPrefix.length);
240 | }
241 | if (fieldWithoutPrefix === 'borderLeft' || fieldWithoutPrefix === 'borderTop' ||
242 | fieldWithoutPrefix === 'borderRight' || fieldWithoutPrefix === 'borderBottom') {
243 | let borderAll = (obj.getValue(`${fieldPrefix}borderLeft`) && obj.getValue(`${fieldPrefix}borderTop`) &&
244 | obj.getValue(`${fieldPrefix}borderRight`) && obj.getValue(`${fieldPrefix}borderBottom`));
245 | let borderAllField = `${fieldPrefix}borderAll`;
246 | if (borderAll !== obj[borderAllField]) {
247 | obj[borderAllField] = borderAll;
248 | rb.notifyEvent(obj, Command.operation.change, borderAllField);
249 | }
250 | }
251 | }
252 |
253 | /**
254 | * Returns class name.
255 | * This can be useful for introspection when the class names are mangled
256 | * due to the webpack uglification process.
257 | * @returns {string}
258 | */
259 | getClassName() {
260 | return 'Style';
261 | }
262 | }
263 |
264 | Style.type = {
265 | text: 'text',
266 | line: 'line',
267 | image: 'image',
268 | table: 'table',
269 | tableBand: 'tableBand',
270 | frame: 'frame',
271 | sectionBand: 'sectionBand',
272 | }
273 |
274 | // Verdana, Arial
275 | // ['Courier', 'Courier-Bold', 'Courier-BoldOblique', 'Courier-Oblique', 'Helvetica', 'Helvetica-Bold',
276 | // 'Helvetica-BoldOblique', 'Helvetica-Oblique', 'Symbol', 'Times-Bold', 'Times-BoldItalic', 'Times-Italic',
277 | // 'Times-Roman', 'ZapfDingbats']
278 | Style.font = {
279 | courier: 'courier',
280 | helvetica: 'helvetica',
281 | times: 'times'
282 | };
283 |
284 | Style.alignment = {
285 | // horizontal
286 | left: 'left',
287 | center: 'center',
288 | right: 'right',
289 | justify: 'justify',
290 | // vertical
291 | top: 'top',
292 | middle: 'middle',
293 | bottom: 'bottom'
294 | };
295 |
--------------------------------------------------------------------------------
/src/elements/BarCodeElement.js:
--------------------------------------------------------------------------------
1 | import DocElement from './DocElement';
2 | import JsBarcode from 'jsbarcode';
3 | import QRCode from 'qrcode';
4 | import * as utils from '../utils';
5 |
6 | /**
7 | * Barcode doc element. Currently only Code-128 is supported.
8 | * @class
9 | */
10 | export default class BarCodeElement extends DocElement {
11 | constructor(id, initialData, rb) {
12 | super(rb.getLabel('docElementImage'), id, 80, 80, rb);
13 | this.elBarCode = null;
14 | this.elContent = null;
15 | this.content = '';
16 | this.format = 'CODE128';
17 | this.displayValue = false;
18 | this.barWidth = '2';
19 | this.guardBar = false;
20 | this.errorCorrectionLevel = 'M';
21 | this.rotate = false;
22 | this.spreadsheet_hide = false;
23 | this.spreadsheet_column = '';
24 | this.spreadsheet_colspan = '';
25 | this.spreadsheet_addEmptyRow = false;
26 | this.setInitialData(initialData);
27 | this.name = this.rb.getLabel('docElementBarCode');
28 | }
29 |
30 | setup(openPanelItem) {
31 | super.setup(openPanelItem);
32 | this.createElement();
33 | if (this.content !== '') {
34 | this.updateBarCode();
35 | }
36 | this.updateDisplay();
37 | this.updateStyle();
38 | }
39 |
40 | setValue(field, value) {
41 | super.setValue(field, value);
42 | if (field === 'content' ||field === 'format' || field === 'displayValue' ||
43 | field === 'barWidth' || field === 'guardBar' ||
44 | field === 'width' || field === 'height' || field === 'errorCorrectionLevel' || field === 'rotate') {
45 | this.updateBarCode();
46 | this.updateDisplay();
47 | if (field === 'rotate') {
48 | // if rotate setting was changed and object is selected we select it again so the
49 | // sizers are shown correctly (sizers for x axis are only available when bar code is rotated)
50 | if (this.rb.isSelectedObject(this.getId())) {
51 | this.rb.deselectObject(this.getId());
52 | this.rb.selectObject(this.getId());
53 | }
54 | }
55 | }
56 | }
57 |
58 | /**
59 | * Returns all fields of this object that can be modified in the properties panel.
60 | * @returns {String[]}
61 | */
62 | getProperties() {
63 | return [
64 | 'x', 'y', 'width', 'height', 'content', 'format', 'displayValue',
65 | 'barWidth', 'guardBar', 'errorCorrectionLevel',
66 | 'printIf', 'removeEmptyElement', 'rotate',
67 | 'spreadsheet_hide', 'spreadsheet_column', 'spreadsheet_colspan', 'spreadsheet_addEmptyRow'
68 | ];
69 | }
70 |
71 | getElementType() {
72 | return DocElement.type.barCode;
73 | }
74 |
75 | updateDisplayInternal(x, y, width, height) {
76 | if (this.el !== null) {
77 | this.el.style.left = this.rb.toPixel(x);
78 | this.el.style.top = this.rb.toPixel(y);
79 | this.el.style.width = this.rb.toPixel(width);
80 | this.el.style.height = this.rb.toPixel(height);
81 | }
82 | }
83 |
84 | /**
85 | * Returns allowed sizers when element is selected.
86 | * @returns {String[]}
87 | */
88 | getSizers() {
89 | if (this.format !== 'QRCode' && this.rotate) {
90 | // when the bar code is rotated it is possible to set the width (i.e. the actual bar code height)
91 | // and the height, the height is only relevant for layout of following elements since the
92 | // actual height depends on the generated bar code
93 | return ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
94 | } else {
95 | return ['N', 'S'];
96 | }
97 | }
98 |
99 | createElement() {
100 | this.el = utils.createElement('div', { id: `rbro_el${this.id}`, class: 'rbroDocElement rbroBarCodeElement' });
101 | // content element is needed for overflow hidden which is set for rotated bar code
102 | this.elContent = utils.createElement('div', { id: `rbro_el_content${this.id}`, style: 'height: 100%' });
103 | this.elBarCode = utils.createElement('canvas', { id: `rbro_el_barcode${this.id}` } );
104 | this.elContent.append(this.elBarCode);
105 | this.el.append(this.elContent);
106 | this.appendToContainer();
107 | this.updateBarCode();
108 | super.registerEventHandlers();
109 | }
110 |
111 | remove() {
112 | super.remove();
113 | }
114 |
115 | updateBarCode() {
116 | if (this.format === 'QRCode') {
117 | this.widthVal = this.heightVal;
118 | this.width = '' + this.widthVal;
119 | let content = this.content;
120 | if (content === '') {
121 | content = 'https://www.reportbro.com';
122 | }
123 | let options = {
124 | width: this.widthVal,
125 | margin: 0,
126 | errorCorrectionLevel : this.errorCorrectionLevel
127 | };
128 | this.clearRotateStyle();
129 | QRCode.toCanvas(this.elBarCode, content, options);
130 | } else {
131 | let valid = false;
132 | let height = this.rotate ? this.widthVal : this.heightVal;
133 | // height is total height for bar code element,
134 | // remove height for value and guard bars if necessary so the bar code plus value and guard bars
135 | // does not exceed the total height
136 | if (this.displayValue) {
137 | height -= 22;
138 | }
139 | if ((this.format === 'EAN8' || this.format === 'EAN13') && this.guardBar) {
140 | height -= 12;
141 | }
142 | let options = {
143 | format: this.format, height: height,
144 | margin: 0, displayValue: this.displayValue, width: 2
145 | };
146 | if (this.format === 'EAN8' || this.format === 'EAN13') {
147 | options.flat = !this.guardBar;
148 | } else if (this.format === 'UPC') {
149 | options.flat = true; // guard bars are currently not supported in reportbro-lib for UPC
150 | }
151 | const barWidthVal = utils.convertInputToNumber(this.barWidth);
152 | if (barWidthVal) {
153 | options.width = barWidthVal;
154 | }
155 |
156 | // clear width and height which is set on canvas element when QR code is generated
157 | this.elBarCode.style.width = '';
158 | this.elBarCode.style.height = '';
159 |
160 | if (this.content !== '' && this.content.indexOf('${') === -1) {
161 | try {
162 | JsBarcode('#' + this.elBarCode.id, this.content, options);
163 | valid = true;
164 | } catch (ex) {
165 | }
166 | }
167 | if (!valid) {
168 | // in case barcode cannot be created because of invalid input use default content appropriate
169 | // for selected format
170 | let content = '';
171 | if (this.format === 'CODE39' || this.format === 'CODE128') {
172 | content = '12345678';
173 | } else if (this.format === 'EAN13') {
174 | content = '5901234123457';
175 | } else if (this.format === 'EAN8') {
176 | content = '96385074';
177 | } else if (this.format === 'EAN5') {
178 | content = '12345';
179 | } else if (this.format === 'EAN2') {
180 | content = '12';
181 | } else if (this.format === 'ITF14') {
182 | content = '12345678901231';
183 | } else if (this.format === 'UPC') {
184 | content = '036000291452';
185 | } else if (this.format === 'MSI' ||this.format === 'MSI10' || this.format === 'MSI11' ||
186 | this.format === 'MSI1010' || this.format === 'MSI1110' || this.format === 'pharmacode') {
187 | content = '1234';
188 | }
189 | JsBarcode('#' + this.elBarCode.id, content, options);
190 | }
191 | if (this.rotate) {
192 | const offset_x = -(this.elBarCode.clientWidth - this.widthVal) / 2;
193 | const offset_y = -offset_x;
194 | this.elBarCode.style.transform = `translate(${offset_x}px, ${offset_y}px) rotate(90deg)`;
195 | this.elContent.style.overflow = 'hidden';
196 | } else {
197 | this.widthVal = this.elBarCode.clientWidth;
198 | this.width = '' + this.widthVal;
199 | this.clearRotateStyle();
200 | }
201 | }
202 | }
203 |
204 | /**
205 | * Must be called when bar code is created / updated and bar code is not rotated.
206 | */
207 | clearRotateStyle() {
208 | this.elBarCode.style.transform = '';
209 | this.elContent.style.overflow = '';
210 | }
211 |
212 | /**
213 | * Adds SetValue commands to command group parameter in case the specified parameter is used in any of
214 | * the object fields.
215 | * @param {Parameter} parameter - parameter which will be renamed.
216 | * @param {String} newParameterName - new name of the parameter.
217 | * @param {CommandGroupCmd} cmdGroup - possible SetValue commands will be added to this command group.
218 | */
219 | addCommandsForChangedParameterName(parameter, newParameterName, cmdGroup) {
220 | this.addCommandForChangedParameterName(parameter, newParameterName, 'content', cmdGroup);
221 | this.addCommandForChangedParameterName(parameter, newParameterName, 'printIf', cmdGroup);
222 | }
223 |
224 | /**
225 | * Returns class name.
226 | * This can be useful for introspection when the class names are mangled
227 | * due to the webpack uglification process.
228 | * @returns {string}
229 | */
230 | getClassName() {
231 | return 'BarCodeElement';
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/src/elements/FrameElement.js:
--------------------------------------------------------------------------------
1 | import DocElement from './DocElement';
2 | import Frame from '../container/Frame';
3 | import Style from '../data/Style';
4 | import * as utils from '../utils';
5 |
6 | /**
7 | * Frame element. Frames can contain any number of other doc element. These doc elements
8 | * are positioned relative to the frame.
9 | * @class
10 | */
11 | export default class FrameElement extends DocElement {
12 | constructor(id, initialData, rb) {
13 | super(rb.getLabel('docElementFrame'), id, 100, 100, rb);
14 | this.frame = null;
15 | this.setupComplete = false;
16 | this.elContent = null;
17 | this.elContentFrame = null;
18 | this.label = '';
19 | this.backgroundColor = '';
20 | this.borderAll = false;
21 | this.borderLeft = false;
22 | this.borderTop = false;
23 | this.borderRight = false;
24 | this.borderBottom = false;
25 | this.borderColor = '#000000';
26 | this.borderWidth = '1';
27 |
28 | this.shrinkToContentHeight = false;
29 | this.alignToPageBottom = false;
30 |
31 | this.spreadsheet_hide = false;
32 | this.spreadsheet_column = '';
33 | this.spreadsheet_addEmptyRow = false;
34 |
35 | this.setInitialData(initialData);
36 |
37 | this.borderWidthVal = utils.convertInputToNumber(this.borderWidth);
38 | }
39 |
40 | setup(openPanelItem) {
41 | this.borderWidthVal = utils.convertInputToNumber(this.borderWidth);
42 | super.setup();
43 | this.createElement();
44 | this.updateDisplay();
45 |
46 | if (this.linkedContainerId === null) {
47 | this.linkedContainerId = this.rb.getUniqueId();
48 | }
49 | this.frame = new Frame(this.linkedContainerId, 'frame_' + this.linkedContainerId, this.rb);
50 | this.frame.init(this);
51 | this.rb.addContainer(this.frame);
52 |
53 | this.setupComplete = true;
54 | this.updateStyle();
55 | this.updateName();
56 | if (openPanelItem){
57 | this.panelItem.open();
58 | }
59 | }
60 |
61 | /**
62 | * Register event handler for a container element so it can be dragged and
63 | * allow selection on double click.
64 | */
65 | registerEventHandlers() {
66 | super.registerContainerEventHandlers();
67 | }
68 |
69 | /**
70 | * Returns highest id of this component, this is the id of the linked container because it is
71 | * created after the frame element.
72 | * @returns {Number}
73 | */
74 | getMaxId() {
75 | return this.linkedContainerId;
76 | }
77 |
78 | setValue(field, value) {
79 | if (field.indexOf('border') !== -1) {
80 | // Style.setBorderValue needs to be called before super.setValue
81 | // because it calls updateStyle() which expects the correct border settings
82 | this[field] = value;
83 | if (field === 'borderWidth') {
84 | this.borderWidthVal = utils.convertInputToNumber(value);
85 | }
86 | Style.setBorderValue(this, field, '', value, this.rb);
87 | }
88 |
89 | super.setValue(field, value);
90 |
91 | if (field === 'label') {
92 | this.updateName();
93 | }
94 | }
95 |
96 | updateDisplayInternal(x, y, width, height) {
97 | if (this.el !== null) {
98 | this.el.style.left = this.rb.toPixel(x);
99 | this.el.style.top = this.rb.toPixel(y);
100 | this.el.style.width = this.rb.toPixel(width);
101 | this.el.style.height = this.rb.toPixel(height);
102 | }
103 | // update inner frame element size
104 | if (this.borderLeft) {
105 | width -= this.borderWidthVal;
106 | }
107 | if (this.borderRight) {
108 | width -= this.borderWidthVal;
109 | }
110 | if (this.borderTop) {
111 | height -= this.borderWidthVal;
112 | }
113 | if (this.borderBottom) {
114 | height -= this.borderWidthVal;
115 | }
116 |
117 | this.elContentFrame.style.width = this.rb.toPixel(width);
118 | this.elContentFrame.style.height = this.rb.toPixel(height);
119 | }
120 |
121 | updateStyle() {
122 | let borderStyleProperties = {};
123 | let borderStyle;
124 | if (this.getValue('borderLeft') || this.getValue('borderTop') ||
125 | this.getValue('borderRight') || this.getValue('borderBottom')) {
126 | borderStyle = this.getValue('borderTop') ? 'solid' : 'none';
127 | borderStyle += this.getValue('borderRight') ? ' solid' : ' none';
128 | borderStyle += this.getValue('borderBottom') ? ' solid' : ' none';
129 | borderStyle += this.getValue('borderLeft') ? ' solid' : ' none';
130 | this.elContent.style.borderWidth = this.getValue('borderWidthVal') + 'px';
131 | this.elContent.style.borderColor = this.getValue('borderColor');
132 | } else {
133 | borderStyle = 'none';
134 | }
135 | this.elContent.style.borderStyle = borderStyle;
136 | this.el.style.backgroundColor = this.getValue('backgroundColor');
137 | }
138 |
139 | /**
140 | * Returns all data fields of this object. The fields are used when serializing the object.
141 | * @returns {String[]}
142 | */
143 | getFields() {
144 | let fields = this.getProperties();
145 | fields.splice(0, 0, 'id', 'containerId', 'linkedContainerId');
146 | return fields;
147 | }
148 |
149 | /**
150 | * Returns all fields of this object that can be modified in the properties panel.
151 | * @returns {String[]}
152 | */
153 | getProperties() {
154 | return [
155 | 'label', 'x', 'y', 'width', 'height', 'styleId', 'backgroundColor',
156 | 'borderAll', 'borderLeft', 'borderTop', 'borderRight', 'borderBottom', 'borderColor', 'borderWidth',
157 | 'printIf', 'removeEmptyElement', 'shrinkToContentHeight', 'alignToPageBottom',
158 | 'spreadsheet_hide', 'spreadsheet_column', 'spreadsheet_addEmptyRow'
159 | ];
160 | }
161 |
162 | getElementType() {
163 | return DocElement.type.frame;
164 | }
165 |
166 | isAreaSelectionAllowed() {
167 | return false;
168 | }
169 |
170 | createElement() {
171 | this.el = utils.createElement(
172 | 'div', { id: `rbro_el${this.id}`, class: 'rbroDocElement rbroFrameElement rbroElementContainer' });
173 | // rbroContentContainerHelper contains border styles
174 | // rbroDocElementContentFrame contains width and height
175 | this.elContent = utils.createElement(
176 | 'div', { id: `rbro_el_content${this.id}`, class: 'rbroContentContainerHelper' });
177 | this.elContentFrame = utils.createElement(
178 | 'div', { id: `rbro_el_content_frame${this.id}`, class: 'rbroDocElementContentFrame' });
179 | this.elContent.append(this.elContentFrame);
180 | this.el.append(this.elContent);
181 | this.appendToContainer();
182 | this.registerEventHandlers();
183 | }
184 |
185 | getContentElement() {
186 | return this.elContentFrame;
187 | }
188 |
189 | remove() {
190 | super.remove();
191 | this.rb.deleteContainer(this.frame);
192 | }
193 |
194 | updateName() {
195 | if (this.label.trim() !== '') {
196 | this.name = this.label;
197 | } else {
198 | this.name = this.rb.getLabel('docElementFrame');
199 | }
200 | document.getElementById(`rbro_menu_item_name${this.id}`).textContent = this.name;
201 | }
202 |
203 | /**
204 | * Adds SetValue commands to command group parameter in case the specified parameter is used in any of
205 | * the object fields.
206 | * @param {Parameter} parameter - parameter which will be renamed.
207 | * @param {String} newParameterName - new name of the parameter.
208 | * @param {CommandGroupCmd} cmdGroup - possible SetValue commands will be added to this command group.
209 | */
210 | addCommandsForChangedParameterName(parameter, newParameterName, cmdGroup) {
211 | this.addCommandForChangedParameterName(parameter, newParameterName, 'printIf', cmdGroup);
212 | }
213 |
214 | /**
215 | * Returns class name.
216 | * This can be useful for introspection when the class names are mangled
217 | * due to the webpack uglification process.
218 | * @returns {string}
219 | */
220 | getClassName() {
221 | return 'FrameElement';
222 | }
223 | }
--------------------------------------------------------------------------------
/src/elements/ImageElement.js:
--------------------------------------------------------------------------------
1 | import DocElement from './DocElement';
2 | import SetValueCmd from '../commands/SetValueCmd';
3 | import Style from '../data/Style';
4 | import * as utils from '../utils';
5 |
6 | /**
7 | * Image doc element. Supported formats are png and jpg.
8 | * @class
9 | */
10 | export default class ImageElement extends DocElement {
11 | constructor(id, initialData, rb) {
12 | super(rb.getLabel('docElementImage'), id, 80, 80, rb);
13 | this.source = '';
14 | this.image = '';
15 | this.imageWidth = 0;
16 | this.imageHeight = 0;
17 | this.imageRatio = 0;
18 | this.imageFilename = '';
19 | this.elImg = null;
20 | this.elContent = null;
21 | this.horizontalAlignment = Style.alignment.left;
22 | this.verticalAlignment = Style.alignment.top;
23 | this.backgroundColor = '';
24 | this.link = '';
25 | this.spreadsheet_hide = false;
26 | this.spreadsheet_column = '';
27 | this.spreadsheet_addEmptyRow = false;
28 | this.setInitialData(initialData);
29 | }
30 |
31 | setup(openPanelItem) {
32 | super.setup(openPanelItem);
33 | this.createElement();
34 | // setImage must be called after createElement so load event handler of image element is triggered
35 | this.setImage();
36 | this.updateDisplay();
37 | this.updateStyle();
38 | this.updateName();
39 | }
40 |
41 | setValue(field, value) {
42 | super.setValue(field, value);
43 | if (field === 'source' || field === 'imageFilename') {
44 | this.updateName();
45 | }
46 | if (field === 'source' || field === 'image') {
47 | this.setImage();
48 | }
49 | }
50 |
51 | /**
52 | * Returns all fields of this object that can be modified in the properties panel.
53 | * @returns {String[]}
54 | */
55 | getProperties() {
56 | return [
57 | 'x', 'y', 'width', 'height', 'source', 'image', 'imageFilename',
58 | 'styleId', 'horizontalAlignment', 'verticalAlignment', 'backgroundColor',
59 | 'printIf', 'removeEmptyElement', 'link',
60 | 'spreadsheet_hide', 'spreadsheet_column', 'spreadsheet_addEmptyRow'
61 | ];
62 | }
63 |
64 | getElementType() {
65 | return DocElement.type.image;
66 | }
67 |
68 | updateDisplayInternal(x, y, width, height) {
69 | if (this.el !== null) {
70 | this.el.style.left = this.rb.toPixel(x);
71 | this.el.style.top = this.rb.toPixel(y);
72 | this.el.style.width = this.rb.toPixel(width);
73 | this.el.style.height = this.rb.toPixel(height);
74 |
75 | let imgWidth = 0;
76 | let imgHeight = 0;
77 | if (this.imageRatio !== 0) {
78 | imgWidth = (this.imageWidth < width) ? this.imageWidth : width;
79 | imgHeight = (this.imageHeight < height) ? this.imageHeight : height;
80 | if (imgWidth !== this.imageWidth || imgHeight !== this.imageHeight) {
81 | let scaledWidth = Math.floor(imgHeight * this.imageRatio);
82 | if (scaledWidth < width) {
83 | imgWidth = scaledWidth;
84 | } else {
85 | imgHeight = Math.floor(imgWidth / this.imageRatio);
86 | }
87 | }
88 | }
89 | this.elImg.style.width = this.rb.toPixel(imgWidth);
90 | this.elImg.style.height = this.rb.toPixel(imgHeight);
91 | }
92 | }
93 |
94 | updateStyle() {
95 | let horizontalAlignment = this.getValue('horizontalAlignment');
96 | let verticalAlignment = this.getValue('verticalAlignment');
97 | let alignClass = 'rbroDocElementAlign' + horizontalAlignment.charAt(0).toUpperCase() +
98 | horizontalAlignment.slice(1);
99 | let valignClass = 'rbroDocElementVAlign' + verticalAlignment.charAt(0).toUpperCase() +
100 | verticalAlignment.slice(1);
101 | this.elContent.style.textAlign = horizontalAlignment;
102 | this.elContent.style.verticalAlign = verticalAlignment;
103 | this.elContent.style.backgroundColor = this.getValue('backgroundColor');
104 | this.elContent.className = '';
105 | this.elContent.classList.add('rbroContentContainerHelper');
106 | this.elContent.classList.add(alignClass);
107 | this.elContent.classList.add(valignClass);
108 | }
109 |
110 | createElement() {
111 | this.el = utils.createElement('div', { id: `rbro_el${this.id}`, class: 'rbroDocElement rbroImageElement' });
112 | this.elImg = utils.createElement('img', { src: '' });
113 | this.elImg.addEventListener('load', (event) => {
114 | // get image width and height in load event, because width/height are not
115 | // directly available in some browsers after setting src
116 | this.imageWidth = this.elImg.naturalWidth;
117 | this.imageHeight = this.elImg.naturalHeight;
118 | if (this.imageHeight !== 0) {
119 | this.imageRatio = this.imageWidth / this.imageHeight;
120 | } else {
121 | this.imageRatio = 0;
122 | }
123 | this.updateDisplay();
124 | });
125 | this.elContent = utils.createElement(
126 | 'div', { id: `rbro_el_content${this.id}`, class: 'rbroContentContainerHelper' });
127 | this.elContent.append(this.elImg);
128 | this.el.append(this.elContent);
129 | this.appendToContainer();
130 | super.registerEventHandlers();
131 | }
132 |
133 | remove() {
134 | this.elImg = null;
135 | super.remove();
136 | }
137 |
138 | setImage() {
139 | this.elImg.setAttribute('src', '');
140 | if (this.source.startsWith('https://') || this.source.startsWith('http://')) {
141 | // image specified by url
142 | this.elImg.setAttribute('src', this.source);
143 | } else if (this.image !== '') {
144 | // image base64 encoded
145 | this.elImg.setAttribute('src', this.image);
146 | } else {
147 | // no image preview
148 | this.imageWidth = 0;
149 | this.imageHeight = 0;
150 | this.imageRatio = 0;
151 | this.updateDisplay();
152 | }
153 | }
154 |
155 | updateName() {
156 | if (this.getValue('imageFilename').trim() !== '') {
157 | this.name = this.getValue('imageFilename')
158 | } else if (this.getValue('source').trim() !== '') {
159 | this.name = this.getValue('source');
160 | } else {
161 | this.name = this.rb.getLabel('docElementImage');
162 | }
163 | const elMenuItem = document.getElementById(`rbro_menu_item_name${this.id}`);
164 | elMenuItem.textContent = this.name;
165 | elMenuItem.setAttribute('title', this.name);
166 | }
167 |
168 | /**
169 | * Adds SetValue commands to command group parameter in case the specified parameter is used in any of
170 | * the object fields.
171 | * @param {Parameter} parameter - parameter which will be renamed.
172 | * @param {String} newParameterName - new name of the parameter.
173 | * @param {CommandGroupCmd} cmdGroup - possible SetValue commands will be added to this command group.
174 | */
175 | addCommandsForChangedParameterName(parameter, newParameterName, cmdGroup) {
176 | this.addCommandForChangedParameterName(parameter, newParameterName, 'source', cmdGroup);
177 | this.addCommandForChangedParameterName(parameter, newParameterName, 'printIf', cmdGroup);
178 | }
179 |
180 | /**
181 | * Returns class name.
182 | * This can be useful for introspection when the class names are mangled
183 | * due to the webpack uglification process.
184 | * @returns {string}
185 | */
186 | getClassName() {
187 | return 'ImageElement';
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/elements/LineElement.js:
--------------------------------------------------------------------------------
1 | import DocElement from './DocElement';
2 | import * as utils from '../utils'
3 |
4 | /**
5 | * Line doc element. Currently only horizontal lines are supported.
6 | * @class
7 | */
8 | export default class LineElement extends DocElement {
9 | constructor(id, initialData, rb) {
10 | super(rb.getLabel('docElementLine'), id, 100, 1, rb);
11 | this.color = '#000000';
12 | this.setInitialData(initialData);
13 | }
14 |
15 | setup(openPanelItem) {
16 | super.setup(openPanelItem);
17 | this.createElement();
18 | this.updateDisplay();
19 | this.updateStyle();
20 | }
21 |
22 | setValue(field, value) {
23 | super.setValue(field, value);
24 | if (field === 'color') {
25 | this.updateStyle();
26 | }
27 | }
28 |
29 | /**
30 | * Returns all fields of this object that can be modified in the properties panel.
31 | * @returns {String[]}
32 | */
33 | getProperties() {
34 | return ['x', 'y', 'width', 'height', 'styleId', 'color', 'printIf'];
35 | }
36 |
37 | getElementType() {
38 | return DocElement.type.line;
39 | }
40 |
41 | updateStyle() {
42 | this.el.style.backgroundColor = this.getValue('color');
43 | }
44 |
45 | /**
46 | * Returns allowed sizers when element is selected.
47 | * @returns {String[]}
48 | */
49 | getSizers() {
50 | return ['E', 'W'];
51 | }
52 |
53 | createElement() {
54 | this.el = utils.createElement('div', { id: `rbro_el${this.id}`, class: 'rbroDocElement rbroLineElement' });
55 | this.appendToContainer();
56 | super.registerEventHandlers();
57 | }
58 |
59 | /**
60 | * Adds SetValue commands to command group parameter in case the specified parameter is used in any of
61 | * the object fields.
62 | * @param {Parameter} parameter - parameter which will be renamed.
63 | * @param {String} newParameterName - new name of the parameter.
64 | * @param {CommandGroupCmd} cmdGroup - possible SetValue commands will be added to this command group.
65 | */
66 | addCommandsForChangedParameterName(parameter, newParameterName, cmdGroup) {
67 | this.addCommandForChangedParameterName(parameter, newParameterName, 'printIf', cmdGroup);
68 | }
69 |
70 | /**
71 | * Returns class name.
72 | * This can be useful for introspection when the class names are mangled
73 | * due to the webpack uglification process.
74 | * @returns {string}
75 | */
76 | getClassName() {
77 | return 'LineElement';
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/elements/PageBreakElement.js:
--------------------------------------------------------------------------------
1 | import DocElement from './DocElement';
2 | import * as utils from '../utils'
3 |
4 | /**
5 | * Page break doc element. A page break triggers a new page when the document is printed.
6 | * @class
7 | */
8 | export default class PageBreakElement extends DocElement {
9 | constructor(id, initialData, rb) {
10 | super(rb.getLabel('docElementPageBreak'), id, -1, 1, rb);
11 | this.setInitialData(initialData);
12 | }
13 |
14 | setup(openPanelItem) {
15 | super.setup(openPanelItem);
16 | this.createElement();
17 | this.updateDisplay();
18 | this.updateStyle();
19 | }
20 |
21 | setValue(field, value) {
22 | super.setValue(field, value);
23 | }
24 |
25 | /**
26 | * Returns all fields of this object that can be modified in the properties panel.
27 | * @returns {String[]}
28 | */
29 | getProperties() {
30 | return ['y', 'printIf'];
31 | }
32 |
33 | getElementType() {
34 | return DocElement.type.pageBreak;
35 | }
36 |
37 | updateDisplayInternal(x, y, width, height) {
38 | if (this.el !== null) {
39 | this.el.style.left = this.rb.toPixel(0);
40 | this.el.style.top = this.rb.toPixel(y);
41 | this.el.style.width = '100%';
42 | this.el.style.height = this.rb.toPixel(1);
43 | }
44 | }
45 |
46 | /**
47 | * Returns allowed sizers when element is selected.
48 | * @returns {String[]}
49 | */
50 | getSizers() {
51 | return [];
52 | }
53 |
54 | createElement() {
55 | this.el = utils.createElement('div', { id: `rbro_el${this.id}`, class: 'rbroDocElement rbroPageBreakElement' });
56 | this.appendToContainer();
57 | super.registerEventHandlers();
58 | }
59 |
60 | /**
61 | * Returns class name.
62 | * This can be useful for introspection when the class names are mangled
63 | * due to the webpack uglification process.
64 | * @returns {string}
65 | */
66 | getClassName() {
67 | return 'PageBreakElement';
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/elements/SectionBandElement.js:
--------------------------------------------------------------------------------
1 | import DocElement from './DocElement';
2 | import Band from '../container/Band';
3 | import * as utils from '../utils';
4 |
5 | /**
6 | * Section band doc element. This is the header, content or footer of a custom section.
7 | * All Elements inside the band are positioned relative.
8 | * @class
9 | */
10 | export default class SectionBandElement extends DocElement {
11 | constructor(id, initialData, bandType, rb) {
12 | let name = (bandType === Band.bandType.header) ?
13 | rb.getLabel('bandHeader') :
14 | ((bandType === Band.bandType.footer) ? rb.getLabel('bandFooter') : rb.getLabel('bandContent'));
15 | super(name, id, 0, 100, rb);
16 | this.setupComplete = false;
17 | this.band = null;
18 | this.bandType = bandType;
19 | this.repeatHeader = false;
20 | this.backgroundColor = '';
21 | this.alternateBackgroundColor = '';
22 | this.alwaysPrintOnSamePage = true;
23 | this.shrinkToContentHeight = false;
24 | this.parentId = initialData.parentId;
25 |
26 | this.heightVal = 0;
27 | this.visible = (bandType === Band.bandType.content);
28 |
29 | this.setInitialData(initialData);
30 | }
31 |
32 | setup() {
33 | this.createElement();
34 | this.updateDisplay();
35 | this.updateStyle();
36 |
37 | if (this.linkedContainerId === null) {
38 | this.linkedContainerId = this.rb.getUniqueId();
39 | }
40 | this.band = new Band(
41 | this.bandType, true, this.linkedContainerId, 'section_' + this.bandType + '_' + this.linkedContainerId,
42 | this.rb);
43 | this.band.init(this);
44 | this.rb.addContainer(this.band);
45 | this.setupComplete = true;
46 | }
47 |
48 | /**
49 | * Do not register any event handlers so element cannot be selected.
50 | */
51 | registerEventHandlers() {
52 | }
53 |
54 | /**
55 | * Returns highest id of this component, this is the id of the linked container because it is
56 | * created after the band element.
57 | * @returns {Number}
58 | */
59 | getMaxId() {
60 | return this.linkedContainerId;
61 | }
62 |
63 | /**
64 | * Returns absolute position inside document.
65 | * @returns {Object} x and y coordinates.
66 | */
67 | getAbsolutePosition() {
68 | let pos = { x: 0, y: 0 };
69 | let parent = this.getParent();
70 | if (parent !== null) {
71 | pos = parent.getAbsolutePosition();
72 | }
73 | pos.y += this.yVal;
74 | return pos;
75 | }
76 |
77 | setValue(field, value) {
78 | super.setValue(field, value);
79 |
80 | if (field === 'height') {
81 | this[field + 'Val'] = utils.convertInputToNumber(value);
82 | let parent = this.getParent();
83 | if (parent !== null) {
84 | parent.updateBands(this);
85 | }
86 | } else if (field === 'backgroundColor') {
87 | this.updateStyle();
88 | }
89 | }
90 |
91 | /**
92 | * Returns all data fields of this object. The fields are used when serializing the object.
93 | * @returns {String[]}
94 | */
95 | getFields() {
96 | let fields = this.getProperties();
97 | fields.splice(0, 0, 'id', 'containerId', 'linkedContainerId');
98 | return fields;
99 | }
100 |
101 | /**
102 | * Returns all fields of this object that can be modified in the properties panel.
103 | * @returns {String[]}
104 | */
105 | getProperties() {
106 | let fields = ['height', 'styleId', 'backgroundColor', 'shrinkToContentHeight'];
107 | if (this.bandType === Band.bandType.header) {
108 | fields.push('repeatHeader');
109 | } else {
110 | fields.push('alwaysPrintOnSamePage');
111 | fields.push('shrinkToContentHeight');
112 | if (this.bandType === Band.bandType.content) {
113 | fields.push('alternateBackgroundColor');
114 | }
115 | }
116 | return fields;
117 | }
118 |
119 | updateDisplayInternal(x, y, width, height) {
120 | if (this.el !== null) {
121 | this.el.style.top = this.rb.toPixel(y);
122 | this.el.style.width = '100%';
123 | this.el.style.height = this.rb.toPixel(height);
124 | if (this.setupComplete) {
125 | // update section element because section band dividers are contained in section
126 | let parent = this.getParent();
127 | if (parent !== null) {
128 | parent.updateHeight(this, height);
129 | }
130 | }
131 | }
132 | }
133 |
134 | updateStyle() {
135 | this.el.style.backgroundColor = this.backgroundColor;
136 | }
137 |
138 | select() {
139 | super.select();
140 | this.el.classList.add('rbroHighlightBandDescription');
141 | }
142 |
143 | deselect() {
144 | super.deselect();
145 | this.el.classList.remove('rbroHighlightBandDescription');
146 | }
147 |
148 | /**
149 | * Returns allowed sizers when element is selected.
150 | * @returns {String[]}
151 | */
152 | getSizers() {
153 | return ['S'];
154 | }
155 |
156 | getHeight() {
157 | return this.heightVal;
158 | }
159 |
160 | isAreaSelectionAllowed() {
161 | return false;
162 | }
163 |
164 | isDraggingAllowed() {
165 | return false;
166 | }
167 |
168 | createElement() {
169 | this.el = utils.createElement(
170 | 'div', { id: `rbro_el${this.id}`, class: 'rbroSectionBandElement rbroElementContainer' });
171 | this.el.append(utils.createElement(
172 | 'div', {
173 | id: `rbro_el_band_description${this.id}`, class: 'rbroDocumentBandDescription'
174 | }));
175 | document.getElementById(`rbro_el${this.parentId}`).append(this.el);
176 | }
177 |
178 | getContentElement() {
179 | return this.el;
180 | }
181 |
182 | getParent() {
183 | return this.rb.getDataObject(this.parentId);
184 | }
185 |
186 | show(visible) {
187 | this.visible = visible;
188 | if (visible) {
189 | this.el.classList.remove('rbroHidden');
190 | } else {
191 | this.el.classList.add('rbroHidden');
192 | }
193 | }
194 |
195 | isVisible() {
196 | return this.visible;
197 | }
198 |
199 | /**
200 | * Returns class name.
201 | * This can be useful for introspection when the class names are mangled
202 | * due to the webpack uglification process.
203 | * @returns {string}
204 | */
205 | getClassName() {
206 | return 'SectionBandElement';
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/src/elements/SectionElement.js:
--------------------------------------------------------------------------------
1 | import DocElement from './DocElement';
2 | import SectionBandElement from './SectionBandElement';
3 | import Band from '../container/Band';
4 | import MainPanelItem from '../menu/MainPanelItem';
5 | import * as utils from '../utils';
6 |
7 | /**
8 | * Section element. Sections can be added to the content band and contain a content band and optional
9 | * header/footer bands.
10 | * @class
11 | */
12 | export default class SectionElement extends DocElement {
13 | constructor(id, initialData, rb) {
14 | super(rb.getLabel('docElementSection'), id, -1, 60, rb);
15 | this.setupComplete = false;
16 | this.elDividerHeader = null;
17 | this.elDividerFooter = null;
18 | this.elDividerBottom = null;
19 | this.dataSource = '';
20 | this.label = '';
21 | this.header = false;
22 | this.footer = false;
23 | this.headerData = null;
24 | this.contentData = null;
25 | this.footerData = null;
26 |
27 | this.setInitialData(initialData);
28 | }
29 |
30 | setup(openPanelItem) {
31 | super.setup(openPanelItem);
32 | this.createElement();
33 | this.updateDisplay();
34 |
35 | this.headerData = this.createBand(Band.bandType.header, null);
36 | this.contentData = this.createBand(Band.bandType.content, null);
37 | this.footerData = this.createBand(Band.bandType.footer, null);
38 | this.updateHeight(null, -1);
39 |
40 | this.setWidth(this.getContainerContentSize().width);
41 |
42 | this.setupComplete = true;
43 | this.updateName();
44 | if (openPanelItem) {
45 | this.panelItem.open();
46 | }
47 | }
48 |
49 | createBand(bandType, dataValues) {
50 | let data;
51 | let dataKey = bandType + 'Data';
52 | let dataId;
53 | let panelItemProperties = { hasChildren: true, showDelete: false };
54 | if (dataValues) {
55 | data = dataValues;
56 | } else if (this[dataKey]) {
57 | data = this[dataKey];
58 | dataId = data.id;
59 | } else {
60 | data = {};
61 | }
62 | data.parentId = this.id;
63 | data.containerId = this.containerId;
64 | if (!dataId) {
65 | dataId = this.rb.getUniqueId();
66 | }
67 | let y = 0;
68 | if (bandType === Band.bandType.header) {
69 | data.y = '' + y;
70 | } else if (bandType === Band.bandType.content) {
71 | if (this.header && this.headerData !== null) {
72 | y += this.headerData.getValue('heightVal');
73 | }
74 | data.y = '' + y;
75 | } else if (bandType === Band.bandType.footer) {
76 | if (this.header && this.headerData !== null) {
77 | y += this.headerData.getValue('heightVal');
78 | }
79 | if (this.contentData !== null) {
80 | y += this.contentData.getValue('heightVal');
81 | }
82 | data.y = '' + y;
83 | }
84 | if ((bandType === Band.bandType.header && !this.header) ||
85 | (bandType === Band.bandType.footer && !this.footer)) {
86 | panelItemProperties.visible = false;
87 | }
88 | let bandElement = new SectionBandElement(dataId, data, bandType, this.rb);
89 | this.rb.addDataObject(bandElement);
90 | let panelItemBand = new MainPanelItem(
91 | 'sectionBand', this.panelItem, bandElement, panelItemProperties, this.rb);
92 | bandElement.setPanelItem(panelItemBand);
93 | this.panelItem.appendChild(panelItemBand);
94 | bandElement.setup();
95 |
96 | if (bandType === Band.bandType.header) {
97 | bandElement.show(this.header);
98 | } else if (bandType === Band.bandType.footer) {
99 | bandElement.show(this.footer);
100 | }
101 | return bandElement;
102 | }
103 |
104 | /**
105 | * Register event handler for a container element so it can be dragged and
106 | * allow selection on double click.
107 | */
108 | registerEventHandlers() {
109 | super.registerContainerEventHandlers();
110 | }
111 |
112 | /**
113 | * Returns highest id of this component, this is the max id of the footer band because it is created last.
114 | * @returns {Number}
115 | */
116 | getMaxId() {
117 | let id = this.id;
118 | if (this.footerData !== null) {
119 | id = this.footerData.getMaxId();
120 | }
121 | return id;
122 | }
123 |
124 | appendContainerChildren(elements) {
125 | if (this.headerData !== null) {
126 | this.headerData.appendContainerChildren(elements);
127 | }
128 | if (this.contentData !== null) {
129 | this.contentData.appendContainerChildren(elements);
130 | }
131 | if (this.footerData !== null) {
132 | this.footerData.appendContainerChildren(elements);
133 | }
134 | }
135 |
136 | setValue(field, value) {
137 | super.setValue(field, value);
138 |
139 | if (field === 'label' || field === 'dataSource') {
140 | this.updateName();
141 | } else if (field === 'header') {
142 | this.headerData.show(value);
143 | if (value) {
144 | this.headerData.getPanelItem().show();
145 | } else {
146 | this.headerData.getPanelItem().hide();
147 | }
148 | } else if (field === 'footer') {
149 | this.footerData.show(value);
150 | if (value) {
151 | this.footerData.getPanelItem().show();
152 | } else {
153 | this.footerData.getPanelItem().hide();
154 | }
155 | } else if (field === 'containerId') {
156 | this.headerData.containerId = value;
157 | this.contentData.containerId = value;
158 | this.footerData.containerId = value;
159 | }
160 | if (field === 'header' || field === 'footer') {
161 | this.updateBands(null);
162 | }
163 | }
164 |
165 | updateDisplayInternal(x, y, width, height) {
166 | if (this.el !== null) {
167 | this.el.style.top = this.rb.toPixel(y);
168 | this.el.style.width = '100%';
169 | this.el.style.height = this.rb.toPixel(height);
170 | }
171 | }
172 |
173 | /**
174 | * Returns all fields of this object that can be modified in the properties panel.
175 | * @returns {String[]}
176 | */
177 | getProperties() {
178 | return ['y', 'label', 'dataSource', 'header', 'footer', 'printIf'];
179 | }
180 |
181 | getElementType() {
182 | return DocElement.type.section;
183 | }
184 |
185 | select() {
186 | super.select();
187 | let elSizerContainer = this.getSizerContainerElement();
188 | // create sizers (to indicate selection) which do not support resizing
189 | for (let sizer of ['N', 'S']) {
190 | elSizerContainer.append(
191 | utils.createElement('div', { class: `rbroSizer rbroSizer${sizer} rbroSizerMove` }));
192 | }
193 |
194 | if (this.headerData !== null) {
195 | document.getElementById(`rbro_el${this.headerData.getId()}`).classList.add('rbroHighlightBandDescription');
196 | }
197 | if (this.contentData !== null) {
198 | document.getElementById(`rbro_el${this.contentData.getId()}`).classList.add('rbroHighlightBandDescription');
199 | }
200 | if (this.footerData !== null) {
201 | document.getElementById(`rbro_el${this.footerData.getId()}`).classList.add('rbroHighlightBandDescription');
202 | }
203 | }
204 |
205 | deselect() {
206 | super.deselect();
207 | const elBandDescriptions = this.el.querySelectorAll('.rbroSectionBandElement');
208 | for (const elBandDescription of elBandDescriptions) {
209 | elBandDescription.classList.remove('rbroHighlightBandDescription');
210 | }
211 | }
212 |
213 | /**
214 | * Returns allowed sizers when element is selected.
215 | * @returns {String[]}
216 | */
217 | getSizers() {
218 | return [];
219 | }
220 |
221 | isAreaSelectionAllowed() {
222 | return false;
223 | }
224 |
225 | isDroppingAllowed() {
226 | return false;
227 | }
228 |
229 | createElement() {
230 | this.el = utils.createElement('div', { id: `rbro_el${this.id}`, class: 'rbroDocElement rbroSectionElement' });
231 | this.el.append(
232 | utils.createElement('div', {
233 | id: `rbro_divider_section_top${this.id}`,
234 | class: 'rbroDivider rbroDividerSection',
235 | style: 'top: 0px'
236 | })
237 | );
238 | this.elDividerHeader = utils.createElement(
239 | 'div', {
240 | id: `rbro_divider_section_header${this.id}`,
241 | class: 'rbroDivider rbroDividerSectionBand rbroHidden'
242 | });
243 | this.el.append(this.elDividerHeader);
244 | this.elDividerFooter = utils.createElement(
245 | 'div', {
246 | id: `rbro_divider_section_footer${this.id}`,
247 | class: 'rbroDivider rbroDividerSectionBand rbroHidden'
248 | });
249 | this.el.append(this.elDividerFooter);
250 | this.elDividerBottom = utils.createElement(
251 | 'div', {
252 | id: `rbro_divider_section_bottom${this.id}`,
253 | class: 'rbroDivider rbroDividerSection'
254 | });
255 | this.el.append(this.elDividerBottom);
256 | this.appendToContainer();
257 | this.registerEventHandlers();
258 | }
259 |
260 | remove() {
261 | super.remove();
262 | // delete containers of section bands
263 | if (this.headerData !== null) {
264 | this.rb.deleteContainer(this.headerData.getLinkedContainer());
265 | }
266 | if (this.contentData !== null) {
267 | this.rb.deleteContainer(this.contentData.getLinkedContainer());
268 | }
269 | if (this.footerData !== null) {
270 | this.rb.deleteContainer(this.footerData.getLinkedContainer());
271 | }
272 | }
273 |
274 | updateName() {
275 | if (this.label.trim() !== '') {
276 | this.name = this.label;
277 | } else {
278 | this.name = this.rb.getLabel('docElementSection');
279 | if (this.dataSource.trim() !== '') {
280 | this.name += ' ' + this.dataSource;
281 | }
282 | }
283 | if (this.headerData !== null) {
284 | document.getElementById(`rbro_el_band_description${this.headerData.getId()}`).textContent =
285 | this.name + ' ' + this.headerData.getName();
286 | }
287 | if (this.contentData !== null) {
288 | document.getElementById(`rbro_el_band_description${this.contentData.getId()}`).textContent =
289 | this.name + ' ' + this.contentData.getName();
290 | }
291 | if (this.footerData !== null) {
292 | document.getElementById(`rbro_el_band_description${this.footerData.getId()}`).textContent =
293 | this.name + ' ' + this.footerData.getName();
294 | }
295 | document.getElementById(`rbro_menu_item_name${this.id}`).textContent = this.name;
296 | }
297 |
298 | /**
299 | * Set internal width and width of all bands. Should be called whenever the document size changes.
300 | * @param {Number} width - total band width.
301 | */
302 | setWidth(width) {
303 | this.widthVal = width;
304 | this.width = '' + width;
305 | if (this.headerData !== null) {
306 | this.headerData.widthVal = width;
307 | this.headerData.width = '' + width;
308 | }
309 | if (this.contentData !== null) {
310 | this.contentData.widthVal = width;
311 | this.contentData.width = '' + width;
312 | }
313 | if (this.footerData !== null) {
314 | this.footerData.widthVal = width;
315 | this.footerData.width = '' + width;
316 | }
317 | }
318 |
319 | /**
320 | * Update section element height and position, visibility of dividers for header/footer bands.
321 | * @param {SectionBandElement} band - if not null the bandHeight parameter will be used for band height
322 | * instead of the actual stored height value. This is needed to update the divider display during drag
323 | * of section band height.
324 | * @param {Number} bandHeight - used band height for given band parameter instead of stored height value.
325 | */
326 | updateHeight(band, bandHeight) {
327 | let height = 0;
328 | if (this.header && this.headerData !== null) {
329 | if (band === this.headerData) {
330 | height += bandHeight;
331 | } else {
332 | height += this.headerData.getValue('heightVal');
333 | }
334 | this.elDividerHeader.style.top = this.rb.toPixel(height);
335 | this.elDividerHeader.classList.remove('rbroHidden');
336 | } else {
337 | this.elDividerHeader.classList.add('rbroHidden');
338 | }
339 | if (this.contentData !== null) {
340 | if (band === this.contentData) {
341 | height += bandHeight;
342 | } else {
343 | height += this.contentData.getValue('heightVal');
344 | }
345 | }
346 | if (this.footer && this.footerData !== null) {
347 | this.elDividerFooter.style.top = this.rb.toPixel(height);
348 | this.elDividerFooter.classList.remove('rbroHidden');
349 | if (band === this.footerData) {
350 | height += bandHeight;
351 | } else {
352 | height += this.footerData.getValue('heightVal');
353 | }
354 | } else {
355 | document.getElementById(`rbro_divider_section_footer${this.id}`).classList.add('rbroHidden');
356 | }
357 | this.elDividerBottom.style.top = this.rb.toPixel(height);
358 | this.height = '' + height;
359 | this.heightVal = height;
360 | this.updateDisplay();
361 | }
362 |
363 | /**
364 | * Update height and y-coordinate of all sub-bands (header, content, footer).
365 | */
366 | updateBands(ignoreBandData) {
367 | if (this.setupComplete) {
368 | let y = 0;
369 | if (this.header) {
370 | if (this.headerData !== ignoreBandData) {
371 | this.headerData.setValue('y', '' + y);
372 | }
373 | y += this.headerData.getValue('heightVal');
374 | }
375 | if (this.contentData !== ignoreBandData) {
376 | this.contentData.setValue('y', '' + y);
377 | }
378 | y += this.contentData.getValue('heightVal');
379 | if (this.footer && this.footerData !== ignoreBandData) {
380 | this.footerData.setValue('y', '' + y);
381 | }
382 | }
383 | this.updateHeight(null, -1);
384 | }
385 |
386 | /**
387 | * Get linked containers of all bands.
388 | * @returns {Container[]} array with all linked containers of header/content/footer section bands.
389 | */
390 | getLinkedContainers() {
391 | let containers = [];
392 | let container;
393 | for (let band of ['headerData', 'contentData', 'footerData']) {
394 | if (this[band] !== null) {
395 | container = this[band].getLinkedContainer();
396 | if (container !== null) {
397 | containers.push(container);
398 | }
399 | }
400 | }
401 | return containers;
402 | }
403 |
404 | hasDataSource() {
405 | return true;
406 | }
407 |
408 | addChildren(docElements) {
409 | docElements.push(this.headerData);
410 | docElements.push(this.contentData);
411 | docElements.push(this.footerData);
412 | // children of section bands will be added through linked containers of section
413 | }
414 |
415 | /**
416 | * Adds SetValue commands to command group parameter in case the specified parameter is used in any of
417 | * the object fields.
418 | * @param {Parameter} parameter - parameter which will be renamed.
419 | * @param {String} newParameterName - new name of the parameter.
420 | * @param {CommandGroupCmd} cmdGroup - possible SetValue commands will be added to this command group.
421 | */
422 | addCommandsForChangedParameterName(parameter, newParameterName, cmdGroup) {
423 | this.addCommandForChangedParameterName(parameter, newParameterName, 'dataSource', cmdGroup);
424 | this.addCommandForChangedParameterName(parameter, newParameterName, 'printIf', cmdGroup);
425 | }
426 |
427 | toJS() {
428 | const rv = super.toJS();
429 | rv['headerData'] = this.headerData.toJS();
430 | rv['contentData'] = this.contentData.toJS();
431 | rv['footerData'] = this.footerData.toJS();
432 | return rv;
433 | }
434 |
435 | /**
436 | * Returns class name.
437 | * This can be useful for introspection when the class names are mangled
438 | * due to the webpack uglification process.
439 | * @returns {string}
440 | */
441 | getClassName() {
442 | return 'SectionElement';
443 | }
444 | }
445 |
--------------------------------------------------------------------------------
/src/elements/TextElement.js:
--------------------------------------------------------------------------------
1 | import DocElement from './DocElement';
2 | import SetValueCmd from '../commands/SetValueCmd';
3 | import Style from '../data/Style';
4 | import * as utils from '../utils';
5 |
6 | /**
7 | * Text doc element.
8 | * @class
9 | */
10 | export default class TextElement extends DocElement {
11 | constructor(id, initialData, rb) {
12 | super(rb.getLabel('docElementText'), id, 100, 20, rb);
13 | this.elContent = null;
14 | this.elContentText = null;
15 | this.elContentTextData = null;
16 |
17 | this.content = '';
18 | this.richText = false;
19 | this.richTextContent = null;
20 | this.richTextHtml = '';
21 | this.eval = false;
22 |
23 | this.styleId = '';
24 | this.bold = false;
25 | this.italic = false;
26 | this.underline = false;
27 | this.strikethrough = false;
28 | this.horizontalAlignment = Style.alignment.left;
29 | this.verticalAlignment = Style.alignment.top;
30 | this.textColor = '#000000';
31 | this.backgroundColor = '';
32 | this.font = rb.getProperty('defaultFont');
33 | this.fontSize = 12;
34 | this.lineSpacing = 1;
35 | this.borderColor = '#000000';
36 | this.borderWidth = '1';
37 | this.borderAll = false;
38 | this.borderLeft = false;
39 | this.borderTop = false;
40 | this.borderRight = false;
41 | this.borderBottom = false;
42 | this.paddingLeft = '2';
43 | this.paddingTop = '2';
44 | this.paddingRight = '2';
45 | this.paddingBottom = '2';
46 |
47 | this.cs_condition = '';
48 | this.cs_styleId = '';
49 | this.cs_additionalRules = '';
50 | this.cs_bold = false;
51 | this.cs_italic = false;
52 | this.cs_underline = false;
53 | this.cs_strikethrough = false;
54 | this.cs_horizontalAlignment = Style.alignment.left;
55 | this.cs_verticalAlignment = Style.alignment.top;
56 | this.cs_textColor = '#000000';
57 | this.cs_backgroundColor = '';
58 | this.cs_font = Style.font.helvetica;
59 | this.cs_fontSize = 12;
60 | this.cs_lineSpacing = 1;
61 | this.cs_borderColor = '#000000';
62 | this.cs_borderWidth = '1';
63 | this.cs_borderAll = false;
64 | this.cs_borderLeft = false;
65 | this.cs_borderTop = false;
66 | this.cs_borderRight = false;
67 | this.cs_borderBottom = false;
68 | this.cs_paddingLeft = '2';
69 | this.cs_paddingTop = '2';
70 | this.cs_paddingRight = '2';
71 | this.cs_paddingBottom = '2';
72 |
73 | this.alwaysPrintOnSamePage = true;
74 | this.pattern = '';
75 | this.link = '';
76 |
77 | this.spreadsheet_hide = false;
78 | this.spreadsheet_column = '';
79 | this.spreadsheet_colspan = '';
80 | this.spreadsheet_addEmptyRow = false;
81 | this.spreadsheet_type = '';
82 | this.spreadsheet_pattern = '';
83 | this.spreadsheet_textWrap = false;
84 | this.setInitialData(initialData);
85 |
86 | this.borderWidthVal = utils.convertInputToNumber(this.borderWidth);
87 | }
88 |
89 | setup(openPanelItem) {
90 | super.setup(openPanelItem);
91 | this.createElement();
92 | this.updateDisplay();
93 | this.updateStyle();
94 |
95 | if (this.richText) {
96 | this.setupRichText();
97 | } else {
98 | this.updateContent(this.content);
99 | }
100 | }
101 |
102 | handleDoubleClick(event) {
103 | super.handleDoubleClick(event);
104 | // focus text content input element and set caret at end of content
105 | let el = document.getElementById('rbro_doc_element_content');
106 | el.focus();
107 | if (typeof el.selectionStart === 'number') {
108 | el.selectionStart = el.selectionEnd = el.value.length;
109 | }
110 | }
111 |
112 | setValue(field, value) {
113 | if (field.indexOf('border') !== -1) {
114 | // Style.setBorderValue needs to be called before super.setValue
115 | // because it calls updateStyle() which expects the correct border settings
116 | this[field] = value;
117 | if (field.substring(0, 3) === 'cs_') {
118 | if (field === 'cs_borderWidth') {
119 | this.borderWidthVal = utils.convertInputToNumber(value);
120 | }
121 | Style.setBorderValue(this, field, 'cs_', value, this.rb);
122 | } else {
123 | if (field === 'borderWidth') {
124 | this.borderWidthVal = utils.convertInputToNumber(value);
125 | }
126 | Style.setBorderValue(this, field, '', value, this.rb);
127 | }
128 | }
129 |
130 | super.setValue(field, value);
131 |
132 | if (field === 'content') {
133 | this.updateContent(value);
134 | } else if (field === 'width' || field === 'height') {
135 | this.updateDisplay();
136 | } else if (field === 'richText') {
137 | if (value) {
138 | this.setupRichText();
139 | } else {
140 | this.updateContent(this.content);
141 | }
142 | this.updateStyle();
143 | } else if (field === 'richTextContent') {
144 | this.updateRichTextContent(value);
145 | } else if (field === 'richTextHtml') {
146 | document.getElementById(`rbro_el_content_text_data${this.id}`).innerHTML = value;
147 | }
148 | }
149 |
150 | /**
151 | * Returns all fields of this object that can be modified in the properties panel.
152 | * @returns {String[]}
153 | */
154 | getProperties() {
155 | return [
156 | 'x', 'y', 'width', 'height', 'content', 'richText', 'richTextContent', 'richTextHtml', 'eval',
157 | 'styleId', 'bold', 'italic', 'underline', 'strikethrough',
158 | 'horizontalAlignment', 'verticalAlignment', 'textColor', 'backgroundColor', 'font', 'fontSize',
159 | 'lineSpacing', 'borderColor', 'borderWidth',
160 | 'borderAll', 'borderLeft', 'borderTop', 'borderRight', 'borderBottom',
161 | 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom',
162 | 'printIf', 'removeEmptyElement', 'alwaysPrintOnSamePage', 'pattern', 'link',
163 | 'cs_condition', 'cs_styleId', 'cs_additionalRules',
164 | 'cs_bold', 'cs_italic', 'cs_underline', 'cs_strikethrough',
165 | 'cs_horizontalAlignment', 'cs_verticalAlignment',
166 | 'cs_textColor', 'cs_backgroundColor', 'cs_font', 'cs_fontSize',
167 | 'cs_lineSpacing', 'cs_borderColor', 'cs_borderWidth',
168 | 'cs_borderAll', 'cs_borderLeft', 'cs_borderTop', 'cs_borderRight', 'cs_borderBottom',
169 | 'cs_paddingLeft', 'cs_paddingTop', 'cs_paddingRight', 'cs_paddingBottom',
170 | 'spreadsheet_hide', 'spreadsheet_column', 'spreadsheet_colspan',
171 | 'spreadsheet_addEmptyRow', 'spreadsheet_type', 'spreadsheet_pattern', 'spreadsheet_textWrap'
172 | ];
173 | }
174 |
175 | getElementType() {
176 | return DocElement.type.text;
177 | }
178 |
179 | updateDisplayInternal(x, y, width, height) {
180 | if (this.el !== null) {
181 | this.el.style.left = this.rb.toPixel(x);
182 | this.el.style.top = this.rb.toPixel(y);
183 | this.el.style.width = this.rb.toPixel(width);
184 | this.el.style.height = this.rb.toPixel(height);
185 | }
186 | // update inner text element size
187 | let contentSize = this.getContentSize(width, height, this.getStyle());
188 | this.elContentText.style.width = this.rb.toPixel(contentSize.width);
189 | this.elContentText.style.height = this.rb.toPixel(contentSize.height);
190 | }
191 |
192 | getContentSize(width, height, style) {
193 | let borderWidth = style.getValue('borderWidthVal');
194 | width -= utils.convertInputToNumber(style.getValue('paddingLeft')) + utils.convertInputToNumber(style.getValue('paddingRight'));
195 | if (style.getValue('borderLeft')) {
196 | width -= borderWidth;
197 | }
198 | if (style.getValue('borderRight')) {
199 | width -= borderWidth;
200 | }
201 | height -= utils.convertInputToNumber(style.getValue('paddingTop')) + utils.convertInputToNumber(style.getValue('paddingBottom'));
202 | if (style.getValue('borderTop')) {
203 | height -= borderWidth;
204 | }
205 | if (style.getValue('borderBottom')) {
206 | height -= borderWidth;
207 | }
208 | return { width: width, height: height };
209 | }
210 |
211 | updateStyle() {
212 | let styleProperties = {};
213 | let style = this.getStyle();
214 | let borderStyle, borderWidth = '', borderColor = '';
215 | let contentSize = this.getContentSize(this.getDisplayWidth(), this.getDisplayHeight(), style);
216 | styleProperties['width'] = this.rb.toPixel(contentSize.width);
217 | styleProperties['height'] = this.rb.toPixel(contentSize.height);
218 | let horizontalAlignment = style.getValue('horizontalAlignment');
219 | let verticalAlignment = style.getValue('verticalAlignment');
220 | let alignClass = 'rbroDocElementAlign' +
221 | horizontalAlignment.charAt(0).toUpperCase() + horizontalAlignment.slice(1);
222 | let valignClass = 'rbroDocElementVAlign' +
223 | verticalAlignment.charAt(0).toUpperCase() + verticalAlignment.slice(1);
224 | styleProperties['vertical-align'] = verticalAlignment;
225 | styleProperties['background-color'] = style.getValue('backgroundColor');
226 | if (!this.richText) {
227 | styleProperties['font-weight'] = style.getValue('bold') ? 'bold' : '';
228 | styleProperties['font-style'] = style.getValue('italic') ? 'italic' : 'normal';
229 | if (style.getValue('underline') && style.getValue('strikethrough')) {
230 | styleProperties['text-decoration'] = 'underline line-through';
231 | } else if (style.getValue('underline')) {
232 | styleProperties['text-decoration'] = 'underline';
233 | } else if (style.getValue('strikethrough')) {
234 | styleProperties['text-decoration'] = 'line-through';
235 | } else {
236 | styleProperties['text-decoration'] = 'none';
237 | }
238 | styleProperties['text-align'] = horizontalAlignment;
239 | styleProperties['color'] = style.getValue('textColor');
240 | styleProperties['font-family'] = style.getValue('font');
241 | styleProperties['font-size'] = style.getValue('fontSize') + 'px';
242 | } else {
243 | // attributes are set by rich text content itself
244 | styleProperties['font-weight'] = 'unset';
245 | styleProperties['font-style'] = 'normal';
246 | styleProperties['text-decoration'] = 'none';
247 | styleProperties['text-align'] = 'unset';
248 | styleProperties['color'] = '#000000';
249 | styleProperties['font-family'] = this.rb.getProperty('defaultFont');
250 | styleProperties['font-size'] = '12px';
251 | }
252 | styleProperties['line-height'] = style.getValue('lineSpacing');
253 | if (style.getValue('borderLeft') || style.getValue('borderTop') ||
254 | style.getValue('borderRight') || style.getValue('borderBottom')) {
255 | borderStyle = style.getValue('borderTop') ? 'solid' : 'none';
256 | borderStyle += style.getValue('borderRight') ? ' solid' : ' none';
257 | borderStyle += style.getValue('borderBottom') ? ' solid' : ' none';
258 | borderStyle += style.getValue('borderLeft') ? ' solid' : ' none';
259 | borderWidth = style.getValue('borderWidthVal') + 'px';
260 | borderColor = style.getValue('borderColor');
261 | } else {
262 | borderStyle = 'none';
263 | }
264 | if (style.getValue('paddingLeft') !== '' || style.getValue('paddingTop') !== '' ||
265 | style.getValue('paddingRight') !== '' || style.getValue('paddingBottom') !== '') {
266 | styleProperties['padding'] = this.rb.toPixel(style.getValue('paddingTop'));
267 | styleProperties['padding'] += ' ' + this.rb.toPixel(style.getValue('paddingRight'));
268 | styleProperties['padding'] += ' ' + this.rb.toPixel(style.getValue('paddingBottom'));
269 | styleProperties['padding'] += ' ' + this.rb.toPixel(style.getValue('paddingLeft'));
270 | } else {
271 | styleProperties['padding'] = '';
272 | }
273 | this.elContent.style.borderStyle = borderStyle;
274 | this.elContent.style.borderWidth = borderWidth;
275 | this.elContent.style.borderColor = borderColor;
276 | this.elContent.className = '';
277 | this.elContent.classList.add('rbroContentContainerHelper');
278 | this.elContent.classList.add(alignClass);
279 | this.elContent.classList.add(valignClass);
280 | let cssText = '';
281 | for (const styleName in styleProperties) {
282 | cssText += styleName + ': ' + styleProperties[styleName] + ';';
283 | }
284 | this.elContentText.style.cssText = cssText;
285 | }
286 |
287 | hasBorderSettings() {
288 | return true;
289 | }
290 |
291 | createElement() {
292 | this.el = utils.createElement('div', { id: `rbro_el${this.id}`, class: 'rbroDocElement rbroTextElement' });
293 | this.elContent = utils.createElement(
294 | 'div', { id: `rbro_el_content${this.id}`, class: 'rbroContentContainerHelper' });
295 | this.elContentText = utils.createElement(
296 | 'div', { id: `rbro_el_content_text${this.id}`, class: 'rbroDocElementContentText' });
297 | this.elContentTextData = utils.createElement('span', { id: `rbro_el_content_text_data${this.id}` });
298 |
299 | // rbroContentContainerHelper contains border styles and alignment classes
300 | // rbroDocElementContentText contains specific styles
301 | // span is needed to preserve whitespaces and word-wrap of actual text content
302 | this.elContentText.append(this.elContentTextData);
303 | this.elContent.append(this.elContentText);
304 | this.el.append(this.elContent);
305 |
306 | this.appendToContainer();
307 | this.elContentTextData.textContent = this.content;
308 | super.registerEventHandlers();
309 | }
310 |
311 | updateContent(value) {
312 | if (value.trim() === '') {
313 | this.name = this.rb.getLabel('docElementText');
314 | } else {
315 | this.name = value;
316 | }
317 | const elMenuItemName = document.getElementById(`rbro_menu_item_name${this.id}`);
318 | elMenuItemName.textContent = this.name;
319 | elMenuItemName.setAttribute('title', this.name);
320 | this.elContentTextData.textContent = value;
321 | }
322 |
323 | updateRichTextContent(delta) {
324 | let text = '';
325 | if (delta && delta.ops) {
326 | for (let op of delta.ops) {
327 | if ('insert' in op) {
328 | text += op.insert;
329 | }
330 | }
331 | // remove line breaks
332 | text = text.replace(/(?:\r\n|\r|\n)/g, ' ');
333 | // truncate text if it is too long
334 | if (text.length > 80) {
335 | text = text.substring(0, 80);
336 | }
337 | text = text.trim();
338 | }
339 | if (text === '') {
340 | this.name = this.rb.getLabel('docElementText');
341 | } else {
342 | this.name = text;
343 | }
344 | const elMenuItemName = document.getElementById(`rbro_menu_item_name${this.id}`);
345 | elMenuItemName.textContent = this.name;
346 | elMenuItemName.setAttribute('title', this.name);
347 | }
348 |
349 | /**
350 | * Sets name of this element and html using rich text.
351 | *
352 | */
353 | setupRichText() {
354 | this.updateRichTextContent(this.richTextContent);
355 | this.elContentTextData.innerHTML = this.richTextHtml;
356 | }
357 |
358 | /**
359 | * Adds SetValue commands to command group parameter in case the specified parameter is used in any of
360 | * the object fields.
361 | * @param {Parameter} parameter - parameter which will be renamed.
362 | * @param {String} newParameterName - new name of the parameter.
363 | * @param {CommandGroupCmd} cmdGroup - possible SetValue commands will be added to this command group.
364 | */
365 | addCommandsForChangedParameterName(parameter, newParameterName, cmdGroup) {
366 | this.addCommandForChangedParameterName(parameter, newParameterName, 'content', cmdGroup);
367 | this.addCommandForChangedParameterName(parameter, newParameterName, 'richTextContent', cmdGroup);
368 | this.addCommandForChangedParameterName(parameter, newParameterName, 'richTextHtml', cmdGroup);
369 | this.addCommandForChangedParameterName(parameter, newParameterName, 'printIf', cmdGroup);
370 | this.addCommandForChangedParameterName(parameter, newParameterName, 'cs_condition', cmdGroup);
371 | }
372 |
373 | toJS() {
374 | const rv = super.toJS();
375 | const numericFields = ['borderWidth', 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom'];
376 | // watermark text does not have conditional style properties
377 | if (this.getElementType() !== DocElement.type.watermarkText) {
378 | numericFields.push('cs_paddingLeft', 'cs_paddingTop', 'cs_paddingRight', 'cs_paddingBottom');
379 | }
380 | for (const field of numericFields) {
381 | rv[field] = utils.convertInputToNumber(this.getValue(field));
382 | }
383 | return rv;
384 | }
385 |
386 | /**
387 | * Returns class name.
388 | * This can be useful for introspection when the class names are mangled
389 | * due to the webpack uglification process.
390 | * @returns {string}
391 | */
392 | getClassName() {
393 | return 'TextElement';
394 | }
395 | }
396 |
--------------------------------------------------------------------------------
/src/elements/WatermarkImageElement.js:
--------------------------------------------------------------------------------
1 | import ImageElement from './ImageElement';
2 | import DocElement from './DocElement';
3 |
4 | /**
5 | * Watermark image element, the image is displayed on the page background.
6 | * @class
7 | */
8 | export default class WatermarkElement extends ImageElement {
9 | constructor(id, initialData, rb) {
10 | super(id, initialData, rb);
11 | this.rotateDeg = 0;
12 | this.opacity = 30;
13 | this.showInForeground = false;
14 | // watermark properties must be set explicitly because they did not exist in ImageElement constructor
15 | for (const key of ['rotateDeg', 'opacity', 'showInForeground']) {
16 | if (initialData.hasOwnProperty(key) && this.hasOwnProperty(key)) {
17 | this[key] = initialData[key];
18 | }
19 | }
20 | }
21 |
22 | setValue(field, value) {
23 | super.setValue(field, value);
24 |
25 | if (field === 'rotateDeg' || field === 'opacity') {
26 | this.updateDisplay();
27 | }
28 | }
29 |
30 | /**
31 | * Returns all fields of this object that can be modified in the properties panel.
32 | * @returns {String[]}
33 | */
34 | getProperties() {
35 | return [
36 | 'x', 'y', 'width', 'height', 'source', 'image', 'imageFilename',
37 | 'rotateDeg', 'opacity', 'showInForeground',
38 | 'styleId', 'horizontalAlignment', 'verticalAlignment', 'backgroundColor',
39 | 'printIf',
40 | ];
41 | }
42 |
43 | getElementType() {
44 | return DocElement.type.watermarkImage;
45 | }
46 |
47 | updateDisplayInternal(x, y, width, height) {
48 | super.updateDisplayInternal(x, y, width, height);
49 | this.el.style.rotate = this.rotateDeg + 'deg';
50 | this.elContent.style.opacity = this.opacity / 100.0;
51 | }
52 |
53 | /**
54 | * Watermark is only shown when element is selected.
55 | */
56 | select() {
57 | super.select();
58 | this.el.classList.remove('rbroHidden');
59 | this.el.style.zIndex = '';
60 | }
61 |
62 | deselect() {
63 | super.deselect();
64 | this.el.classList.add('rbroHidden');
65 | }
66 |
67 | createElement() {
68 | super.createElement();
69 | this.el.classList.add('rbroHidden');
70 | }
71 |
72 | /**
73 | * Returns true if element is restricted within container boundaries.
74 | *
75 | * Watermark elements are not restricted to any containers.
76 | * @returns {boolean}
77 | */
78 | hasBoundaries() {
79 | return false;
80 | }
81 |
82 | /**
83 | * Returns class name.
84 | * This can be useful for introspection when the class names are mangled
85 | * due to the webpack uglification process.
86 | * @returns {string}
87 | */
88 | getClassName() {
89 | return 'WatermarkImageElement';
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/elements/WatermarkTextElement.js:
--------------------------------------------------------------------------------
1 | import TextElement from './TextElement';
2 | import DocElement from './DocElement';
3 |
4 | /**
5 | * Watermark text element, the text is displayed on the page background.
6 | * @class
7 | */
8 | export default class WatermarkTextElement extends TextElement {
9 | constructor(id, initialData, rb) {
10 | super(id, initialData, rb);
11 | this.rotateDeg = 0;
12 | this.opacity = 30;
13 | this.showInForeground = false;
14 | // watermark properties must be set explicitly because they did not exist in TextElement constructor
15 | for (const key of ['rotateDeg', 'opacity', 'showInForeground']) {
16 | if (initialData.hasOwnProperty(key) && this.hasOwnProperty(key)) {
17 | this[key] = initialData[key];
18 | }
19 | }
20 | }
21 |
22 | setValue(field, value) {
23 | super.setValue(field, value);
24 |
25 | if (field === 'rotateDeg' || field === 'opacity') {
26 | this.updateDisplay();
27 | }
28 | }
29 |
30 | /**
31 | * Returns all fields of this object that can be modified in the properties panel.
32 | * @returns {String[]}
33 | */
34 | getProperties() {
35 | return [
36 | 'x', 'y', 'width', 'height', 'content', 'eval', 'rotateDeg', 'opacity', 'showInForeground',
37 | 'styleId', 'bold', 'italic', 'underline', 'strikethrough',
38 | 'horizontalAlignment', 'verticalAlignment', 'textColor', 'backgroundColor', 'font', 'fontSize',
39 | 'lineSpacing', 'borderColor', 'borderWidth',
40 | 'borderAll', 'borderLeft', 'borderTop', 'borderRight', 'borderBottom',
41 | 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom',
42 | 'printIf',
43 | ];
44 | }
45 |
46 | getElementType() {
47 | return DocElement.type.watermarkText;
48 | }
49 |
50 | updateDisplayInternal(x, y, width, height) {
51 | super.updateDisplayInternal(x, y, width, height);
52 | this.el.style.rotate = this.rotateDeg + 'deg';
53 | this.elContent.style.opacity = this.opacity / 100.0;
54 | }
55 |
56 | /**
57 | * Watermark is only shown when element is selected.
58 | */
59 | select() {
60 | super.select();
61 | this.el.classList.remove('rbroHidden');
62 | this.el.style.zIndex = '';
63 | }
64 |
65 | deselect() {
66 | super.deselect();
67 | this.el.classList.add('rbroHidden');
68 | }
69 |
70 | createElement() {
71 | super.createElement();
72 | this.el.classList.add('rbroHidden');
73 | }
74 |
75 | /**
76 | * Returns true if element is restricted within container boundaries.
77 | *
78 | * Watermark elements are not restricted to any containers.
79 | * @returns {boolean}
80 | */
81 | hasBoundaries() {
82 | return false;
83 | }
84 |
85 | /**
86 | * Returns class name.
87 | * This can be useful for introspection when the class names are mangled
88 | * due to the webpack uglification process.
89 | * @returns {string}
90 | */
91 | getClassName() {
92 | return 'WatermarkTextElement';
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/fonts/font_style.css:
--------------------------------------------------------------------------------
1 | /* open-sans-300 - latin */
2 | @font-face {
3 | font-family: 'Open Sans';
4 | font-style: normal;
5 | font-weight: 300;
6 | src: local(''),
7 | url('open-sans-v34-latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
8 | url('open-sans-v34-latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
9 | }
10 |
11 | /* open-sans-regular - latin */
12 | @font-face {
13 | font-family: 'Open Sans';
14 | font-style: normal;
15 | font-weight: 400;
16 | src: local(''),
17 | url('open-sans-v34-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
18 | url('open-sans-v34-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
19 | }
20 |
21 | /* open-sans-600 - latin */
22 | @font-face {
23 | font-family: 'Open Sans';
24 | font-style: normal;
25 | font-weight: 600;
26 | src: local(''),
27 | url('open-sans-v34-latin-600.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
28 | url('open-sans-v34-latin-600.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
29 | }
30 |
31 | /* open-sans-800 - latin */
32 | @font-face {
33 | font-family: 'Open Sans';
34 | font-style: normal;
35 | font-weight: 800;
36 | src: local(''),
37 | url('open-sans-v34-latin-800.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
38 | url('open-sans-v34-latin-800.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
39 | }
40 |
--------------------------------------------------------------------------------
/src/fonts/open-sans-v34-latin-300.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/fonts/open-sans-v34-latin-300.woff
--------------------------------------------------------------------------------
/src/fonts/open-sans-v34-latin-300.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/fonts/open-sans-v34-latin-300.woff2
--------------------------------------------------------------------------------
/src/fonts/open-sans-v34-latin-600.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/fonts/open-sans-v34-latin-600.woff
--------------------------------------------------------------------------------
/src/fonts/open-sans-v34-latin-600.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/fonts/open-sans-v34-latin-600.woff2
--------------------------------------------------------------------------------
/src/fonts/open-sans-v34-latin-800.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/fonts/open-sans-v34-latin-800.woff
--------------------------------------------------------------------------------
/src/fonts/open-sans-v34-latin-800.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/fonts/open-sans-v34-latin-800.woff2
--------------------------------------------------------------------------------
/src/fonts/open-sans-v34-latin-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/fonts/open-sans-v34-latin-regular.woff
--------------------------------------------------------------------------------
/src/fonts/open-sans-v34-latin-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/fonts/open-sans-v34-latin-regular.woff2
--------------------------------------------------------------------------------
/src/i18n/locale_de_de.js:
--------------------------------------------------------------------------------
1 | const locale_de = {
2 | bandContent: 'Inhalt',
3 | bandFooter: 'Fußzeile',
4 | bandHeader: 'Kopfzeile',
5 | clear: 'Löschen',
6 | contentHeight: 'Inhaltshöhe',
7 | contentHeightInfo: 'Höhe des Inhaltsbereichs, um Elemente zu platzieren (betrifft nicht die tatsächliche Seitengröße)',
8 | differentFiles: 'versch. Dateien...',
9 | differentValues: 'versch. Werte...',
10 | docElementAdditionalStyles: 'Zusätzliche Formatvorlagen',
11 | docElementAlignToPageBottom: 'Am unteren Seitenrand ausrichten',
12 | docElementAlternateBackgroundColor: 'Abwechselnde Hintergrundfarbe',
13 | docElementAlwaysPrintOnSamePage: 'Immer zusammen auf einer Seite',
14 | docElementBarCode: 'Barcode',
15 | docElementBarWidth: 'Balkenbreite',
16 | docElementBorderFrame: 'außen',
17 | docElementBorderFrameRow: 'Rahmen und Zeile',
18 | docElementBorderGrid: 'Alle Rahmenlinien',
19 | docElementBorderNone: 'Keiner',
20 | docElementBorderRow: 'Zeilen',
21 | docElementColor: 'Farbe',
22 | docElementColspan: 'Anz. verbundene Zellen',
23 | docElementColumns: 'Spalten',
24 | docElementConditionalStyle: 'Bedingte Formatierung',
25 | docElementConditionalStyleCondition: 'Bedingung',
26 | docElementContent: 'Text',
27 | docElementContentRows: 'Inhaltszeilen',
28 | docElementDataSource: 'Datenquelle',
29 | docElementDisplayValue: 'Wert anzeigen',
30 | docElementEditMultipleSelectionNotSupported: 'Bearbeiten von Mehrfachauswahl wird für diese Einstellung nicht unterstützt',
31 | docElementErrorCorrectionLevel: 'Fehlerkorrektur',
32 | docElementErrorCorrectionLevelHigh: 'Hoch',
33 | docElementErrorCorrectionLevelLow: 'Niedrig',
34 | docElementErrorCorrectionLevelMedium: 'Mittel',
35 | docElementErrorCorrectionLevelQuartile: 'Quartil',
36 | docElementEval: 'Auswerten',
37 | docElementFormat: 'Format',
38 | docElementFrame: 'Rahmen',
39 | docElementGroupExpression: 'Gruppen Ausdruck',
40 | docElementGrowWeight: 'Wachstums-Gewicht',
41 | docElementGrowWeightInfo: 'Wachstumsfaktor zur Verwendung des Platzes von ausgeblendeten Spalten',
42 | docElementGrowWeightHigh: 'hoch',
43 | docElementGrowWeightLow: 'niedrig',
44 | docElementGuardBar: 'Rand-/Trennzeichen',
45 | docElementHeight: 'Höhe',
46 | docElementImage: 'Bild',
47 | docElementImageCountExceeded: 'Es sind nur bis zu ${count} Bilder erlaubt',
48 | docElementImageFile: 'Bilddatei',
49 | docElementLabel: 'Bezeichnung',
50 | docElementLine: 'Linie',
51 | docElementLink: 'Link',
52 | docElementLoadImageErrorMsg: 'Bild laden fehlgeschlagen',
53 | docElementLoadImageWebPErrorMsg: 'Bild laden fehlgeschlagen, aktueller Browser benötigt',
54 | docElementOpacity: 'Deckkraft %',
55 | docElementPageBreak: 'Seitenumbruch',
56 | docElementPattern: 'Pattern',
57 | docElementPosition: 'Position (x, y)',
58 | docElementPositionX: 'Position (x)',
59 | docElementPositionY: 'Position (y)',
60 | docElementPrintIf: 'Anzeigen wenn',
61 | docElementPrintSettings: 'Anzeige',
62 | docElementRemoveEmptyElement: 'Entfernen wenn nicht vorhanden',
63 | docElementRepeatGroupHeader: 'Gruppe auf jeder Seite wiederholen',
64 | docElementRepeatHeader: 'Auf jeder Seite wiederholen',
65 | docElementRichText: 'Rich Text',
66 | docElementRoot: 'Dokument',
67 | docElementRotate: 'Drehen',
68 | docElementRotateDeg: 'Drehen °',
69 | docElementSection: 'Sektion',
70 | docElementShowInForeground: 'Im Vorderground anzeigen',
71 | docElementShrinkToContentHeight: 'Auf Inhaltshöhe reduzieren',
72 | docElementSize: 'Größe (Breite, Höhe)',
73 | docElementSource: 'Bildquelle',
74 | docElementSpreadsheet: 'Tabellenkalkulation',
75 | docElementSpreadsheetAddEmptyRow: 'Leere Zeile unterhalb einfügen',
76 | docElementSpreadsheetColspan: 'Anz. verbundene Zellen',
77 | docElementSpreadsheetColumn: 'Fixe Spalte',
78 | docElementSpreadsheetHide: 'Ausblenden',
79 | docElementSpreadsheetPattern: 'Pattern',
80 | docElementSpreadsheetTextWrap: 'Zeilenumbruch',
81 | docElementSpreadsheetType: 'Typ',
82 | docElementSpreadsheetTypeDate: 'Datum',
83 | docElementSpreadsheetTypeNone: 'Standard',
84 | docElementSpreadsheetTypeNumber: 'Number',
85 | docElementStyle: 'Formatvorlage',
86 | docElementTable: 'Tabelle',
87 | docElementTableBandPageBreak: 'Seitenumbruch mit jeder neuen Gruppe',
88 | docElementText: 'Text',
89 | docElementWidth: 'Breite',
90 | documentProperties: 'Dokumenteinstellungen',
91 | documentTabClose: 'Schließen',
92 | documentTabPdfLayout: 'PDF Layout',
93 | documentTabPdfPreview: 'PDF Vorschau',
94 | documentTabXlsxDownload: 'XLSX Download',
95 | editData: 'Bearbeiten',
96 | emptyPanel: 'Leer',
97 | errorMsgAddtionalRulesNoStyleSelected: 'Keine Formatvorlage für Regel {info} ausgewählt',
98 | errorMsgDuplicateParameter: 'Parameter existiert bereits',
99 | errorMsgDuplicateParameterField: 'Feld existiert bereits',
100 | errorMsgExternalImageNotAllowed: 'Server unterstützt das Laden von Bildern von externen Resourcen nicht',
101 | errorMsgFontNotAvailable: 'Schrift ist am Server nicht vorhanden',
102 | errorMsgGroupExpressionWithoutDataSource: 'Gruppen Ausdruck benötigt Tabelle mit Datenquelle',
103 | errorMsgLoadingImageFailed: 'Bild laden fehlgeschlagen: ${info}',
104 | errorMsgInvalidArray: 'Ungültige Liste',
105 | errorMsgInvalidAvgSumExpression: 'Ausdruck muss ein Zahl-Feld einer Liste enthalten',
106 | errorMsgInvalidBarCode: 'Ungültiger Barcode Inhalt',
107 | errorMsgInvalidDataSource: 'Ungültige Datenquelle',
108 | errorMsgInvalidDataSourceParameter: 'Parameter muss eine Liste sein',
109 | errorMsgInvalidDate: 'Ungültiges Datum, erwartetes Format ist JJJJ-MM-TT (bzw. JJJJ-MM-TT hh:mm für Datum mit Uhrzeit)',
110 | errorMsgInvalidStringData: 'Ungültiger Inhalt für Text Parameter ${info}',
111 | errorMsgInvalidExpression: 'Ungültiger Ausdruck: ${info}',
112 | errorMsgInvalidExpressionFuncNotDefined: 'Funktion ${info} ist nicht definiert',
113 | errorMsgInvalidExpressionNameNotDefined: 'Name ${info} ist nicht definiert',
114 | errorMsgInvalidExpressionType: 'Ausdruck liefert ungültigen Typ',
115 | errorMsgInvalidLink: 'Link muss mit http:// oder https:// beginnen',
116 | errorMsgInvalidImage: 'Ungültige Bilddaten, Bild muss base64 kodiert sein',
117 | errorMsgInvalidImageSource: 'Ungültige Bildquelle, Url beginnend mit http:// bzw. https:// erwartet',
118 | errorMsgInvalidImageSourceParameter: 'Parameter vom Typ Bild oder String (mit einer Url) notwendig',
119 | errorMsgInvalidMap: 'Ungültige Auflistung',
120 | errorMsgInvalidNumber: 'Ungültige Zahl',
121 | errorMsgInvalidPageSize: 'Ungültige Seitengröße',
122 | errorMsgInvalidParameterData: 'Daten stimmen nicht mit Parameter überein',
123 | errorMsgInvalidParameterName: 'Name muss mit einem Zeichen oder Unterstrich beginnen und darf nur Zeichen, Ziffern und Unterstriche enthalten',
124 | errorMsgInvalidPattern: 'Ungültiges Pattern',
125 | errorMsgInvalidPosition: 'Die Position ist außerhalb des Bereichs',
126 | errorMsgInvalidRichTextFontNotAvailable: 'Der Rich Text enthält eine Schrift, welche am Server nicht vorhanden ist',
127 | errorMsgInvalidRichTextParameter: 'Der Rich Text enthält ungültige Tags',
128 | errorMsgInvalidSize: 'Das Element ist außerhalb des Bereichs',
129 | errorMsgInvalidSpreadsheetDate: 'Inhalt kann nicht zu Datum konvertiert werden, erwartetes Format ist JJJJ-MM-TT (bzw. JJJJ-MM-TT hh:mm für Datum mit Uhrzeit)',
130 | errorMsgInvalidSpreadsheetNumber: 'Inhalt kann nicht zu Zahl konvertiert werden',
131 | errorMsgInvalidTestData: 'Ungültige Testdaten',
132 | errorMsgMissingData: 'Fehlende Daten',
133 | errorMsgMissingDataSourceParameter: 'Datenquelle Parameter nicht gefunden',
134 | errorMsgMissingExpression: 'Ausdruck muss gesetzt sein',
135 | errorMsgMissingGlyph: 'Text enthält Zeichen, die in der ausgewählten Schrift nicht verfügbar sind',
136 | errorMsgMissingImage: 'Fehlendes Bild: Keine Bildquelle oder -datei angegeben',
137 | errorMsgMissingParameter: 'Parameter nicht gefunden',
138 | errorMsgMissingParameterData: 'Daten für Parameter ${info} nicht gefunden',
139 | errorMsgRepeatGroupHeaderAfterContent: 'Nicht erlaubt für Gruppe nach Inhaltszeile',
140 | errorMsgPlusVersionRequired: 'Benötigt kommerzielle PLUS Version',
141 | errorMsgSectionBandNotOnSamePage: 'Abschnittsbereich passt nicht auf eine Seite',
142 | errorMsgSectionBandPageBreakNotAllowed: 'Manueller Seitenumbruch ist nicht erlaubt',
143 | errorMsgUnicodeEncodeError: 'Text enthält nicht druckbare Zeichen',
144 | errorMsgUnsupportedImageType: 'Bildtyp wird nicht unterstützt (nur .jpg und .png erlaubt)',
145 | footer: 'Fußzeile',
146 | footerDisplay: 'Anzeige',
147 | footerSize: 'Höhe Fußzeile',
148 | header: 'Kopfzeile',
149 | headerDisplay: 'Anzeige',
150 | headerFooterDisplayAlways: 'Immer',
151 | headerFooterDisplayNotOnFirstPage: 'Nicht auf erster Seite',
152 | headerSize: 'Höhe Kopfzeile',
153 | menuColumnAddLeft: 'Spalte links hinzufügen',
154 | menuColumnAddRight: 'Spalte rechts hinzufügen',
155 | menuColumnDelete: 'Spalte löschen',
156 | menuAlignBottom: 'Unten ausrichten',
157 | menuAlignCenter: 'Zentriert ausrichten',
158 | menuAlignLeft: 'Links ausrichten',
159 | menuAlignMiddle: 'Mittig ausrichten',
160 | menuAlignRight: 'Rechts ausrichten',
161 | menuAlignTop: 'Oben ausrichten',
162 | menuInsertReport: 'EINF.',
163 | menuInsertReportTip: 'Report-Vorlage einfügen',
164 | menuLogReport: 'LOG',
165 | menuLogReportTip: 'Report-Vorlage in Konsole ausgeben',
166 | menuPreview: 'VORSCHAU',
167 | menuPreviewTip: 'Report-Vorschau',
168 | menuRedo: 'WIEDERH.',
169 | menuRedoTip: 'Letzten rückgängig gemachten Befehl wiederholen',
170 | menuRowAddAbove: 'Zeile oberhalb hinzufügen',
171 | menuRowAddBelow: 'Zeile unterhalb hinzufügen',
172 | menuRowDelete: 'Zeile löschen',
173 | menuSave: 'SPEICHERN',
174 | menuSaveTip: 'Report speichern',
175 | menuToggleGrid: 'Raster ein-/ausblenden',
176 | menuUndo: 'RÜCKG.',
177 | menuUndoTip: 'Letzten Befehl rückgängig machen',
178 | menuZoomIn: 'Vergrößern',
179 | menuZoomOut: 'Verkleinern',
180 | nameCopySuffix: 'Kopie',
181 | orientation: 'Ausrichtung',
182 | orientationBottom: 'unten',
183 | orientationLandscape: 'Querformat',
184 | orientationLeft: 'links',
185 | orientationPortrait: 'Hochformat',
186 | orientationRight: 'rechts',
187 | orientationTop: 'oben',
188 | pageFormat: 'Seitenformat',
189 | pageFormatA4: 'DIN A4 (210 x 297 mm)',
190 | pageFormatA5: 'DIN A5 (148 x 210 mm)',
191 | pageFormatLetter: 'Brief (216 x 279 mm)',
192 | pageFormatUserDefined: 'Eigene Einstellung',
193 | pageHeight: 'Höhe',
194 | pageMargins: 'Seitenränder',
195 | pageWidth: 'Breite',
196 | parameter: 'Parameter',
197 | parameterArrayItemType: 'Listenelement-Typ',
198 | parameterEditTestData: 'Bearbeiten',
199 | parameterEditTestDataArrayNoFields: 'Keine Datenfelder für diese Liste definiert',
200 | parameterEditTestDataMapNoFields: 'Keine Datenfelder für diese Gruppierung definiert',
201 | parameterEval: 'Text auswerten',
202 | parameterExpression: 'Ausdruck',
203 | parameterListType: 'Listen-Typ',
204 | parameterName: 'Name',
205 | parameterNullable: 'NULL-Wert erlaubt',
206 | parameterPattern: 'Pattern',
207 | parameterRowParams: 'Parameter in Zeile',
208 | parameterSearchPlaceholder: 'Parameter durchsuchen...',
209 | parameterTestData: 'Testdaten',
210 | parameterTestDataDatePattern: 'JJJJ-MM-TT',
211 | parameterTestDataImageInfo: 'Bilder werden in der Berichtsvorlage gespeichert und können die Größe der Berichtsvorlage erheblich erhöhen',
212 | parameterTestDataRichTextInfo: 'Rich Text kann Html-Tags zur Formatierung enthalten. Siehe erlaubte Tags',
213 | parameterTestDataRowCount: '${count} Zeilen',
214 | parameterTestDataRowCountEmpty: 'leer',
215 | parameterTestDataRowCountOne: '1 Zeile',
216 | parameterType: 'Typ',
217 | parameterTypeArray: 'Liste',
218 | parameterTypeAverage: 'Durchschnitt',
219 | parameterTypeBoolean: 'Boolean',
220 | parameterTypeDate: 'Datum',
221 | parameterTypeImage: 'Bild',
222 | parameterTypeMap: 'Gruppierung',
223 | parameterTypeNumber: 'Zahl',
224 | parameterTypeRichText: 'Rich Text',
225 | parameterTypeSimpleArray: 'Einfache Liste',
226 | parameterTypeString: 'String',
227 | parameterTypeSum: 'Summe',
228 | parameters: 'Parameter',
229 | parametersDataSource: 'Datenquelle Parameter',
230 | parametersDataSourceName: '${name} Parameter',
231 | patternCurrencySymbol: 'Währungssymbol',
232 | patternDate1: 'Tag.Monat.Jahr, z.B. 1.6.1980',
233 | patternDate2: 'Tag.Monat.Jahr (2-stellig), Stunde(24h):Minute, z.B. 1.6.80, 14:30',
234 | patternDate3: 'Tag/Monat/Jahr (Monat abgekürzt), z.B. 1/Jun/1980',
235 | patternDate4: 'Monat/Tag/Jahr (Tag und Monat mit führender Null, falls einstellig), z.B. 06/01/1980',
236 | patternLocale: 'Pattern Locale',
237 | patternNumber1: 'Tausender-Trennzeichen',
238 | patternNumber2: 'Dezimalpunkt gefolgt von 3 Dezimalstellen',
239 | patternNumber3: 'Dezimalpunkt gefolgt von mind. 2 und max. 4 Dezimalstellen',
240 | patternNumber4: 'Tausender-Trennzeichen und Dezimalpunkt gefolgt von 2 Dezimalstellen',
241 | patternNumber5: 'Währungssymbol vor Zahl',
242 | patternNumberGroupSymbol: 'Tausender-Trennzeichen',
243 | patternNumberGroupSymbolInfo: 'leer lassen für Standardzeichen',
244 | patternSeparatorDates: '--- Datum Pattern ---',
245 | patternSeparatorNumbers: '--- Zahlen Pattern ---',
246 | plusFeatureInfo: 'Benötigt kommerzielle PLUS Version',
247 | plusFeatureInfoNestedParameter: 'Verschachtelter Parameter benötigt kommerzielle PLUS Version',
248 | popupWindowAddDataRow: 'Zeile hinzufügen',
249 | select: 'auswählen...',
250 | style: 'Formatvorlage',
251 | styleAlignment: 'Ausrichtung',
252 | styleBackgroundColor: 'Hintergrundfarbe',
253 | styleBold: 'Fett',
254 | styleBorder: 'Rahmen',
255 | styleBorderAll: 'vollständig',
256 | styleBorderColor: 'Rahmenfarbe',
257 | styleBorderWidth: 'Rahmenbreite',
258 | styleFont: 'Schrift',
259 | styleFontSizeUnit: 'pt',
260 | styleHAlignmentCenter: 'Zentriert',
261 | styleHAlignmentLeft: 'Links',
262 | styleHAlignmentJustify: 'Blocksatz',
263 | styleHAlignmentRight: 'Rechts',
264 | styleItalic: 'Kursiv',
265 | styleLineSpacing: 'Linienabstand',
266 | styleName: 'Name',
267 | styleNone: 'Keine',
268 | stylePadding: 'Innenabstand',
269 | styleStrikethrough: 'Durchgestrichen',
270 | styleTextColor: 'Textfarbe',
271 | styleTextStyle: 'Formatierung',
272 | styleType: 'Element',
273 | styleTypeFrame: 'Rahmen',
274 | styleTypeImage: 'Bild',
275 | styleTypeLine: 'Linie',
276 | styleTypeSectionBand: 'Sektionsbereich',
277 | styleTypeTable: 'Tabelle',
278 | styleTypeTableBand: 'Tabellenzeile',
279 | styleTypeText: 'Text',
280 | styleUnderline: 'Unterstreichen',
281 | styleVAlignmentBottom: 'Unten',
282 | styleVAlignmentMiddle: 'Mittig',
283 | styleVAlignmentTop: 'Oben',
284 | styles: 'Formatvorlagen',
285 | watermarkImages: 'Bild-Wasserzeichen',
286 | watermarkTexts: 'Text-Wasserzeichen',
287 | watermarks: 'Wasserzeichen',
288 | };
289 |
290 | export default locale_de;
291 |
--------------------------------------------------------------------------------
/src/i18n/locale_en_us.js:
--------------------------------------------------------------------------------
1 | const locale_en = {
2 | bandContent: 'Content',
3 | bandFooter: 'Footer',
4 | bandHeader: 'Header',
5 | clear: 'Clear',
6 | contentHeight: 'Content height',
7 | contentHeightInfo: 'affects only GUI size to place elements and not the real page size',
8 | differentFiles: 'different files...',
9 | differentValues: 'different values...',
10 | docElementAdditionalRules: 'Additional rules',
11 | docElementAlignToPageBottom: 'Align to bottom of page',
12 | docElementAlternateBackgroundColor: 'Alternate background color',
13 | docElementAlwaysPrintOnSamePage: 'Always on same page',
14 | docElementBarCode: 'Bar code',
15 | docElementBarWidth: 'Bar width',
16 | docElementBorderFrame: 'Frame',
17 | docElementBorderFrameRow: 'Frame and row',
18 | docElementBorderGrid: 'Grid',
19 | docElementBorderNone: 'None',
20 | docElementBorderRow: 'Row',
21 | docElementColor: 'Color',
22 | docElementColspan: 'Colspan',
23 | docElementColumns: 'Columns',
24 | docElementConditionalStyle: 'Conditional style',
25 | docElementConditionalStyleCondition: 'Condition',
26 | docElementContent: 'Text',
27 | docElementContentRows: 'Content rows',
28 | docElementDataSource: 'Data source',
29 | docElementDisplayValue: 'Display value',
30 | docElementEditMultipleSelectionNotSupported: 'Edit multiple selection is not supported for this setting',
31 | docElementErrorCorrectionLevel: 'Error correction',
32 | docElementErrorCorrectionLevelHigh: 'High',
33 | docElementErrorCorrectionLevelLow: 'Low',
34 | docElementErrorCorrectionLevelMedium: 'Medium',
35 | docElementErrorCorrectionLevelQuartile: 'Quartile',
36 | docElementEval: 'Evaluate',
37 | docElementFormat: 'Format',
38 | docElementFrame: 'Frame',
39 | docElementGroupExpression: 'Group expression',
40 | docElementGrowWeight: 'Grow weight',
41 | docElementGrowWeightInfo: 'Grow factor to take space of hidden columns',
42 | docElementGrowWeightHigh: 'high',
43 | docElementGrowWeightLow: 'low',
44 | docElementGuardBar: 'Guard bars',
45 | docElementHeight: 'Height',
46 | docElementImage: 'Image',
47 | docElementImageCountExceeded: 'Reached maximum number of ${count} allowed images',
48 | docElementImageFile: 'Image file',
49 | docElementLabel: 'Label',
50 | docElementLine: 'Line',
51 | docElementLink: 'Link',
52 | docElementLoadImageErrorMsg: 'Loading image failed',
53 | docElementLoadImageWebPErrorMsg: 'Loading image failed, upgrade browser to latest version',
54 | docElementOpacity: 'Opacity %',
55 | docElementPageBreak: 'Page break',
56 | docElementPattern: 'Pattern',
57 | docElementPosition: 'Position (x, y)',
58 | docElementPositionX: 'Position (x)',
59 | docElementPositionY: 'Position (y)',
60 | docElementPrintIf: 'Print if',
61 | docElementPrintSettings: 'Print settings',
62 | docElementRemoveEmptyElement: 'Remove when empty',
63 | docElementRepeatGroupHeader: 'Repeat group on each page',
64 | docElementRepeatHeader: 'Repeat header',
65 | docElementRichText: 'Rich text',
66 | docElementRoot: 'Document',
67 | docElementRotate: 'Rotate',
68 | docElementRotateDeg: 'Rotate °',
69 | docElementSection: 'Section',
70 | docElementShowInForeground: 'Show in foreground',
71 | docElementShrinkToContentHeight: 'Shrink to content height',
72 | docElementSize: 'Size (width, height)',
73 | docElementSource: 'Source',
74 | docElementSpreadsheet: 'Spreadsheet',
75 | docElementSpreadsheetAddEmptyRow: 'Add empty row below',
76 | docElementSpreadsheetColspan: 'Colspan',
77 | docElementSpreadsheetColumn: 'Fixed column',
78 | docElementSpreadsheetHide: 'Hide',
79 | docElementSpreadsheetPattern: 'Pattern',
80 | docElementSpreadsheetTextWrap: 'Text wrap',
81 | docElementSpreadsheetType: 'Type',
82 | docElementSpreadsheetTypeDate: 'Date',
83 | docElementSpreadsheetTypeNone: 'Default',
84 | docElementSpreadsheetTypeNumber: 'Number',
85 | docElementStyle: 'Style',
86 | docElementTable: 'Table',
87 | docElementTableBandPageBreak: 'Page break with each new group',
88 | docElementText: 'Text',
89 | docElementWidth: 'Width',
90 | documentProperties: 'Document properties',
91 | documentTabClose: 'Close',
92 | documentTabPdfLayout: 'PDF Layout',
93 | documentTabPdfPreview: 'PDF Preview',
94 | documentTabXlsxDownload: 'XLSX Download',
95 | editData: 'Edit',
96 | emptyPanel: 'Empty panel',
97 | errorMsgAddtionalRulesNoStyleSelected: 'No style selected for rule ${info}',
98 | errorMsgDuplicateParameter: 'Parameter already exists',
99 | errorMsgDuplicateParameterField: 'Field already exists',
100 | errorMsgExternalImageNotAllowed: 'Server does not support loading images from external resources',
101 | errorMsgFontNotAvailable: 'Font not available on server',
102 | errorMsgGroupExpressionWithoutDataSource: 'Group expression requires table with data source',
103 | errorMsgLoadingImageFailed: 'Loading image failed: ${info}',
104 | errorMsgInvalidArray: 'Invalid list',
105 | errorMsgInvalidAvgSumExpression: 'Expression must contain number field of a list parameter',
106 | errorMsgInvalidBarCode: 'Invalid bar code content',
107 | errorMsgInvalidDataSource: 'Invalid data source',
108 | errorMsgInvalidDataSourceParameter: 'Parameter must be a list',
109 | errorMsgInvalidDate: 'Invalid date, expected format is YYYY-MM-DD (or YYYY-MM-DD hh:mm for date with time)',
110 | errorMsgInvalidStringData: 'Invalid data for text parameter ${info}',
111 | errorMsgInvalidExpression: 'Invalid expression: ${info}',
112 | errorMsgInvalidExpressionFuncNotDefined: 'Function ${info} not defined',
113 | errorMsgInvalidExpressionNameNotDefined: 'Name ${info} not defined',
114 | errorMsgInvalidExpressionType: 'Expression returns invalid type',
115 | errorMsgInvalidLink: 'Link must start with http:// or https://',
116 | errorMsgInvalidImage: 'Invalid image data, image must be base64 encoded',
117 | errorMsgInvalidImageSource: 'Invalid source, expected url starting with http:// or https:// respectively',
118 | errorMsgInvalidImageSourceParameter: 'Parameter must be an image or string (containing a url)',
119 | errorMsgInvalidMap: 'Invalid collection',
120 | errorMsgInvalidNumber: 'Invalid number',
121 | errorMsgInvalidPageSize: 'Invalid page size',
122 | errorMsgInvalidParameterData: 'Data does not match parameter',
123 | errorMsgInvalidParameterName: 'Name must start with a character or underscore, and must only contain characters, digits and underscores (_)',
124 | errorMsgInvalidPattern: 'Invalid pattern',
125 | errorMsgInvalidPosition: 'The position is outside the area',
126 | errorMsgInvalidRichTextFontNotAvailable: 'The rich text contains a font which is not available on server',
127 | errorMsgInvalidRichTextParameter: 'The rich text contains invalid tags',
128 | errorMsgInvalidSize: 'The element is outside the area',
129 | errorMsgInvalidSpreadsheetDate: 'The data cannot be converted to date, expected format is YYYY-MM-DD (or YYYY-MM-DD hh:mm for date with time)',
130 | errorMsgInvalidSpreadsheetNumber: 'The data cannot be converted to number',
131 | errorMsgInvalidTestData: 'Invalid test data',
132 | errorMsgMissingData: 'Missing data',
133 | errorMsgMissingDataSourceParameter: 'Data source parameter not found',
134 | errorMsgMissingExpression: 'Expression must be set',
135 | errorMsgMissingGlyph: 'Text contains characters that are not available in the selected font',
136 | errorMsgMissingImage: 'Missing image, no source or image file specified',
137 | errorMsgMissingParameter: 'Parameter not found',
138 | errorMsgMissingParameterData: 'Data for parameter ${info} not found',
139 | errorMsgRepeatGroupHeaderAfterContent: 'Not allowed for group after content row',
140 | errorMsgPlusVersionRequired: 'Requires commerical PLUS version',
141 | errorMsgSectionBandNotOnSamePage: 'Section band does not fit on same page',
142 | errorMsgSectionBandPageBreakNotAllowed: 'Manual page break not allowed',
143 | errorMsgUnicodeEncodeError: 'Text contains non printable character',
144 | errorMsgUnsupportedImageType: 'Image does not have supported image type (.jpg, .png)',
145 | footer: 'Footer',
146 | footerDisplay: 'Display',
147 | footerSize: 'Footer size',
148 | header: 'Header',
149 | headerDisplay: 'Display',
150 | headerFooterDisplayAlways: 'Always',
151 | headerFooterDisplayNotOnFirstPage: 'Do not show on first page',
152 | headerSize: 'Header size',
153 | menuColumnAddLeft: 'Add column to the left',
154 | menuColumnAddRight: 'Add column to the right',
155 | menuColumnDelete: 'Delete column',
156 | menuAlignBottom: 'Align bottom',
157 | menuAlignCenter: 'Align center',
158 | menuAlignLeft: 'Align left',
159 | menuAlignMiddle: 'Align middle',
160 | menuAlignRight: 'Align right',
161 | menuAlignTop: 'Align top',
162 | menuInsertReport: 'INSERT',
163 | menuInsertReportTip: 'Insert report template',
164 | menuLogReport: 'LOG',
165 | menuLogReportTip: 'Log report template to console',
166 | menuPreview: 'PREVIEW',
167 | menuPreviewTip: 'Preview report',
168 | menuRedo: 'REDO',
169 | menuRedoTip: 'Repeat last undone command',
170 | menuRowAddAbove: 'Add row above',
171 | menuRowAddBelow: 'Add row below',
172 | menuRowDelete: 'Delete row',
173 | menuSave: 'SAVE',
174 | menuSaveTip: 'Save report',
175 | menuToggleGrid: 'Show/Hide grid',
176 | menuUndo: 'UNDO',
177 | menuUndoTip: 'Undo last command',
178 | menuZoomIn: 'Zoom in',
179 | menuZoomOut: 'Zoom out',
180 | nameCopySuffix: 'copy',
181 | orientation: 'Orientation',
182 | orientationBottom: 'bottom',
183 | orientationLandscape: 'Landscape',
184 | orientationLeft: 'left',
185 | orientationPortrait: 'Portrait',
186 | orientationRight: 'right',
187 | orientationTop: 'top',
188 | pageFormat: 'Page format',
189 | pageFormatA4: 'DIN A4 (210 x 297 mm)',
190 | pageFormatA5: 'DIN A5 (148 x 210 mm)',
191 | pageFormatLetter: 'Letter (8.5 x 11.0 inches)',
192 | pageFormatUserDefined: 'Own dimensions',
193 | pageHeight: 'height',
194 | pageMargins: 'Page margins',
195 | pageWidth: 'width',
196 | parameter: 'Parameter',
197 | parameterArrayItemType: 'List item type',
198 | parameterEditTestData: 'Edit',
199 | parameterEditTestDataArrayNoFields: 'No data fields defined for this list',
200 | parameterEditTestDataMapNoFields: 'No data fields defined for this collection',
201 | parameterEval: 'Evaluate text',
202 | parameterExpression: 'Expression',
203 | parameterListType: 'List type',
204 | parameterName: 'Name',
205 | parameterNullable: 'Nullable',
206 | parameterPattern: 'Pattern',
207 | parameterRowParams: 'Row parameters',
208 | parameterSearchPlaceholder: 'Search parameters...',
209 | parameterTestData: 'Test data',
210 | parameterTestDataDatePattern: 'YYYY-MM-DD',
211 | parameterTestDataImageInfo: 'Images are saved in the report template and could significantly increase report template size',
212 | parameterTestDataRichTextInfo: 'Rich text can contain html tags for formatting. See allowed tags',
213 | parameterTestDataRowCount: '${count} rows',
214 | parameterTestDataRowCountEmpty: 'empty',
215 | parameterTestDataRowCountOne: '1 row',
216 | parameterType: 'Type',
217 | parameterTypeArray: 'List',
218 | parameterTypeAverage: 'Average',
219 | parameterTypeBoolean: 'Boolean',
220 | parameterTypeDate: 'Date',
221 | parameterTypeImage: 'Image',
222 | parameterTypeMap: 'Collection',
223 | parameterTypeNumber: 'Number',
224 | parameterTypeRichText: 'Rich Text',
225 | parameterTypeSimpleArray: 'Simple List',
226 | parameterTypeString: 'Text',
227 | parameterTypeSum: 'Sum',
228 | parameters: 'Parameters',
229 | parametersDataSource: 'Data source parameters',
230 | parametersDataSourceName: '${name} parameters',
231 | patternCurrencySymbol: 'Pattern currency symbol',
232 | patternDate1: 'day.month.year, e.g. 1.6.1980',
233 | patternDate2: 'day.month.year (2-digit), hour(24h):minute, e.g. 1.6.80, 14:30',
234 | patternDate3: 'day/month/year (month abbreviation), e.g. 1/Jun/1980',
235 | patternDate4: 'month/day/year (day and month with leading zero if single digit), e.g. 06/01/1980',
236 | patternLocale: 'Pattern locale',
237 | patternNumber1: 'Show thousand separator',
238 | patternNumber2: 'Show decimal point followed by 3 decimal places',
239 | patternNumber3: 'Show decimal point followed by minimum of 2 and maximum of 4 decimal places',
240 | patternNumber4: 'Show thousand separator and decimal point followed by 2 decimal places',
241 | patternNumber5: 'Show currency symbol in front of number',
242 | patternNumberGroupSymbol: 'Thousands separator',
243 | patternNumberGroupSymbolInfo: 'leave empty for default',
244 | patternSeparatorDates: '--- Date patterns ---',
245 | patternSeparatorNumbers: '--- Number patterns ---',
246 | plusFeatureInfo: 'Requires commerical PLUS version',
247 | plusFeatureInfoNestedParameter: 'Nested parameter requires commerical PLUS version',
248 | popupWindowAddDataRow: 'Add row',
249 | select: 'select...',
250 | style: 'Style',
251 | styleAlignment: 'Alignment',
252 | styleBackgroundColor: 'Background color',
253 | styleBold: 'Bold',
254 | styleBorder: 'Border',
255 | styleBorderAll: 'borders',
256 | styleBorderColor: 'Border color',
257 | styleBorderWidth: 'Border width',
258 | styleFont: 'Font',
259 | styleFontSizeUnit: 'pt',
260 | styleHAlignmentCenter: 'Center',
261 | styleHAlignmentLeft: 'Left',
262 | styleHAlignmentJustify: 'Justify',
263 | styleHAlignmentRight: 'Right',
264 | styleItalic: 'Italic',
265 | styleLineSpacing: 'Line spacing',
266 | styleName: 'Name',
267 | styleNone: 'None',
268 | stylePadding: 'Padding',
269 | styleStrikethrough: 'Strikethrough',
270 | styleTextColor: 'Text color',
271 | styleTextStyle: 'Text style',
272 | styleType: 'Element',
273 | styleTypeFrame: 'Frame',
274 | styleTypeImage: 'Image',
275 | styleTypeLine: 'Line',
276 | styleTypeSectionBand: 'Section band',
277 | styleTypeTable: 'Table',
278 | styleTypeTableBand: 'Table band',
279 | styleTypeText: 'Text',
280 | styleUnderline: 'Underline',
281 | styleVAlignmentBottom: 'Bottom',
282 | styleVAlignmentMiddle: 'Middle',
283 | styleVAlignmentTop: 'Top',
284 | styles: 'Styles',
285 | watermarkImages: 'Image watermarks',
286 | watermarkTexts: 'Text watermarks',
287 | watermarks: 'Watermarks',
288 | };
289 |
290 | export default locale_en;
291 |
--------------------------------------------------------------------------------
/src/i18n/locales.js:
--------------------------------------------------------------------------------
1 | import locale_de_de from './locale_de_de';
2 | import locale_en_us from './locale_en_us';
3 |
4 | let locales = {
5 | de_de: locale_de_de,
6 | en_us: locale_en_us
7 | };
8 |
9 | export default locales;
10 |
--------------------------------------------------------------------------------
/src/iconfonts/reportbro.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/iconfonts/reportbro.ttf
--------------------------------------------------------------------------------
/src/iconfonts/reportbro.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/iconfonts/reportbro.woff
--------------------------------------------------------------------------------
/src/iconfonts/reportbro.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/iconfonts/reportbro.woff2
--------------------------------------------------------------------------------
/src/iconfonts/style.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'reportbro';
3 | src:
4 | url('reportbro.woff2?aahybf') format('woff2'),
5 | url('reportbro.ttf?aahybf') format('truetype'),
6 | url('reportbro.woff?aahybf') format('woff'),
7 | url('reportbro.svg?aahybf#reportbro') format('svg');
8 | font-weight: normal;
9 | font-style: normal;
10 | font-display: block;
11 | }
12 |
13 | [class^="rbroIcon-"], [class*=" rbroIcon-"] {
14 | /* use !important to prevent issues with browser extensions that change fonts */
15 | font-family: 'reportbro' !important;
16 | speak: never;
17 | font-style: normal;
18 | font-weight: normal;
19 | font-variant: normal;
20 | text-transform: none;
21 | line-height: 1;
22 |
23 | /* Better Font Rendering =========== */
24 | -webkit-font-smoothing: antialiased;
25 | -moz-osx-font-smoothing: grayscale;
26 | }
27 |
28 | .rbroIcon-info:before {
29 | content: "\e939";
30 | }
31 | .rbroIcon-check:before {
32 | content: "\e938";
33 | }
34 | .rbroIcon-delete:before {
35 | content: "\e937";
36 | }
37 | .rbroIcon-insert-report:before {
38 | content: "\e936";
39 | }
40 | .rbroIcon-row-delete:before {
41 | content: "\e92f";
42 | }
43 | .rbroIcon-column-delete:before {
44 | content: "\e930";
45 | }
46 | .rbroIcon-row-add-below:before {
47 | content: "\e931";
48 | }
49 | .rbroIcon-row-add-above:before {
50 | content: "\e932";
51 | }
52 | .rbroIcon-column-add-left:before {
53 | content: "\e933";
54 | }
55 | .rbroIcon-column-add-right:before {
56 | content: "\e934";
57 | }
58 | .rbroIcon-section:before {
59 | content: "\e92e";
60 | }
61 | .rbroIcon-frame:before {
62 | content: "\e915";
63 | }
64 | .rbroIcon-xlsx:before {
65 | content: "\e92d";
66 | }
67 | .rbroIcon-arrow-line-up:before {
68 | content: "\e92c";
69 | }
70 | .rbroIcon-barcode:before {
71 | content: "\e92a";
72 | }
73 | .rbroIcon-download:before {
74 | content: "\e92b";
75 | }
76 | .rbroIcon-edit:before {
77 | content: "\e924";
78 | }
79 | .rbroIcon-magnifier:before {
80 | content: "\e926";
81 | }
82 | .rbroIcon-play:before {
83 | content: "\e927";
84 | }
85 | .rbroIcon-preview:before {
86 | content: "\e928";
87 | }
88 | .rbroIcon-select:before {
89 | content: "\e925";
90 | }
91 | .rbroIcon-page-break:before {
92 | content: "\e91f";
93 | }
94 | .rbroIcon-line:before {
95 | content: "\e91e";
96 | }
97 | .rbroIcon-border-table-frame:before {
98 | content: "\e920";
99 | }
100 | .rbroIcon-border-table-frame-row:before {
101 | content: "\e921";
102 | }
103 | .rbroIcon-border-table-row:before {
104 | content: "\e922";
105 | }
106 | .rbroIcon-border-table-none:before {
107 | content: "\e914";
108 | }
109 | .rbroIcon-border-table-grid:before {
110 | content: "\e929";
111 | }
112 | .rbroIcon-border-all:before {
113 | content: "\e91a";
114 | }
115 | .rbroIcon-border-right:before {
116 | content: "\e916";
117 | }
118 | .rbroIcon-border-left:before {
119 | content: "\e917";
120 | }
121 | .rbroIcon-border-top:before {
122 | content: "\e918";
123 | }
124 | .rbroIcon-border-bottom:before {
125 | content: "\e919";
126 | }
127 | .rbroIcon-grid:before {
128 | content: "\e910";
129 | }
130 | .rbroIcon-console:before {
131 | content: "\e911";
132 | }
133 | .rbroIcon-image:before {
134 | content: "\e912";
135 | }
136 | .rbroIcon-text:before {
137 | content: "\e913";
138 | }
139 | .rbroIcon-settings:before {
140 | content: "\e901";
141 | }
142 | .rbroIcon-refresh:before {
143 | content: "\e923";
144 | }
145 | .rbroIcon-save:before {
146 | content: "\e90a";
147 | }
148 | .rbroIcon-undo:before {
149 | content: "\e90e";
150 | }
151 | .rbroIcon-redo:before {
152 | content: "\e90f";
153 | }
154 | .rbroIcon-align-center:before {
155 | content: "\e91b";
156 | }
157 | .rbroIcon-align-middle:before {
158 | content: "\e902";
159 | }
160 | .rbroIcon-align-bottom:before {
161 | content: "\e91c";
162 | }
163 | .rbroIcon-align-left:before {
164 | content: "\e91d";
165 | }
166 | .rbroIcon-align-right:before {
167 | content: "\e903";
168 | }
169 | .rbroIcon-align-top:before {
170 | content: "\e904";
171 | }
172 | .rbroIcon-table:before {
173 | content: "\e905";
174 | }
175 | .rbroIcon-italic:before {
176 | content: "\e906";
177 | }
178 | .rbroIcon-bold:before {
179 | content: "\e907";
180 | }
181 | .rbroIcon-underline:before {
182 | content: "\e908";
183 | }
184 | .rbroIcon-strikethrough:before {
185 | content: "\e900";
186 | }
187 | .rbroIcon-text-align-left:before {
188 | content: "\e909";
189 | }
190 | .rbroIcon-text-align-center:before {
191 | content: "\e90b";
192 | }
193 | .rbroIcon-text-align-right:before {
194 | content: "\e90c";
195 | }
196 | .rbroIcon-text-align-justify:before {
197 | content: "\e90d";
198 | }
199 | .rbroIcon-minus:before {
200 | content: "\e805";
201 | }
202 | .rbroIcon-arrow-right:before {
203 | content: "\e935";
204 | }
205 | .rbroIcon-arrow-down:before {
206 | content: "\e60c";
207 | }
208 | .rbroIcon-cancel:before {
209 | content: "\e604";
210 | }
211 | .rbroIcon-plus:before {
212 | content: "\e611";
213 | }
214 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (C) 2022 jobsta
3 | //
4 | // This file is part of ReportBro, a library to generate PDF and Excel reports.
5 | // Demos can be found at https://www.reportbro.com
6 | //
7 | // Dual licensed under AGPLv3 and ReportBro commercial license:
8 | // https://www.reportbro.com/license
9 | //
10 | // You should have received a copy of the GNU Affero General Public License
11 | // along with this program. If not, see https://www.gnu.org/licenses/
12 | //
13 | // Details for ReportBro commercial license can be found at
14 | // https://www.reportbro.com/license/agreement
15 | //
16 |
17 | import ReportBro from './ReportBro';
18 |
19 | (function(){
20 | if (typeof exports !== 'undefined') exports.ReportBro = ReportBro;
21 | else window.ReportBro = ReportBro;
22 | })();
23 |
--------------------------------------------------------------------------------
/src/menu/MainPanel.js:
--------------------------------------------------------------------------------
1 | import MainPanelItem from './MainPanelItem';
2 | import Container from '../container/Container';
3 |
4 | /**
5 | * Main panel which contains all report elements like doc elements, parameters and styles.
6 | * The main panel shows the structure and all components of the report.
7 | * @class
8 | */
9 | export default class MainPanel {
10 | constructor(rootElement, headerBand, contentBand, footerBand, parameterContainer, styleContainer, rb) {
11 | this.rootElement = rootElement;
12 | this.rb = rb;
13 | this.headerItem = new MainPanelItem(
14 | 'band', null, headerBand, {
15 | hasChildren: true,
16 | showAdd: false,
17 | showDelete: false,
18 | hasDetails: false,
19 | visible: this.rb.getDocumentProperties().getValue('header')
20 | }, rb);
21 |
22 | this.documentItem = new MainPanelItem(
23 | 'band', null, contentBand, {
24 | hasChildren: true,
25 | showAdd: false,
26 | showDelete: false,
27 | hasDetails: false
28 | }, rb);
29 |
30 | this.footerItem = new MainPanelItem(
31 | 'band', null, footerBand, {
32 | hasChildren: true,
33 | showAdd: false,
34 | showDelete: false,
35 | hasDetails: false,
36 | visible: this.rb.getDocumentProperties().getValue('footer')
37 | }, rb);
38 |
39 | this.parametersItem = new MainPanelItem(
40 | 'parameter', null, parameterContainer, {
41 | hasChildren: true,
42 | showAdd: rb.getProperty('adminMode'),
43 | showDelete: false,
44 | hasDetails: false
45 | }, rb);
46 |
47 | this.stylesItem = new MainPanelItem(
48 | 'style', null, styleContainer, {
49 | hasChildren: true,
50 | showAdd: true,
51 | showDelete: false,
52 | hasDetails: false
53 | }, rb);
54 |
55 | this.watermarksItem = new MainPanelItem(
56 | 'watermark', null, null, {
57 | hasChildren: true,
58 | showAdd: false,
59 | showDelete: false,
60 | hasDetails: false,
61 | id: '0_watermarks',
62 | name: rb.getLabel('watermarks'),
63 | // set static flag to prevent child nodes (sub categories) being cleared when report is loaded
64 | static: true,
65 | }, rb);
66 |
67 | this.documentPropertiesItem = new MainPanelItem(
68 | 'documentProperties', null, rb.getDocumentProperties(), {
69 | showDelete: false,
70 | hasDetails: true
71 | }, rb);
72 |
73 | this.items = [
74 | this.headerItem,
75 | this.documentItem,
76 | this.footerItem,
77 | this.parametersItem,
78 | this.stylesItem,
79 | this.watermarksItem,
80 | this.documentPropertiesItem,
81 | ];
82 |
83 | this.dragMainPanelSizer = false;
84 | this.dragMainPanelSizerStartX = 0;
85 | this.mainPanelWidth = 230;
86 | this.mainPanelSizerWidth = 3;
87 |
88 | headerBand.setPanelItem(this.headerItem);
89 | contentBand.setPanelItem(this.documentItem);
90 | footerBand.setPanelItem(this.footerItem);
91 | parameterContainer.setPanelItem(this.parametersItem);
92 | styleContainer.setPanelItem(this.stylesItem);
93 | }
94 |
95 | getHeaderItem() {
96 | return this.headerItem;
97 | }
98 |
99 | getDocumentItem() {
100 | return this.documentItem;
101 | }
102 |
103 | getFooterItem() {
104 | return this.footerItem;
105 | }
106 |
107 | getParametersItem() {
108 | return this.parametersItem;
109 | }
110 |
111 | getStylesItem() {
112 | return this.stylesItem;
113 | }
114 |
115 | getWatermarksItem() {
116 | return this.watermarksItem;
117 | }
118 |
119 | getContainerByItem(item) {
120 | while (item !== null) {
121 | if (item.getData() instanceof Container) {
122 | return item.getData();
123 | }
124 | item = item.getParent();
125 | }
126 | return null;
127 | }
128 |
129 | getDocumentPropertiesItem() {
130 | return this.documentPropertiesItem;
131 | }
132 |
133 | render() {
134 | let panel = document.getElementById('rbro_main_panel_list');
135 | this.appendChildren(panel, this.items);
136 |
137 | document.getElementById('rbro_main_panel_sizer').addEventListener('mousedown', (event) => {
138 | this.dragMainPanelSizer = true;
139 | this.dragMainPanelSizerStartX = event.pageX;
140 | });
141 |
142 | this.updateMainPanelWidth(this.mainPanelWidth);
143 | }
144 |
145 | appendChildren(el, items) {
146 | for (let item of items) {
147 | el.append(item.getElement());
148 | let children = item.getChildren();
149 | if (children.length > 0) {
150 | this.appendChildren(el, children);
151 | }
152 | }
153 | }
154 |
155 | processMouseMove(event) {
156 | if (this.dragMainPanelSizer) {
157 | let mainPanelWidth = this.mainPanelWidth + (event.pageX - this.dragMainPanelSizerStartX);
158 | mainPanelWidth = this.checkMainPanelWidth(mainPanelWidth);
159 | this.updateMainPanelWidth(mainPanelWidth);
160 | return true;
161 | }
162 | return false;
163 | }
164 |
165 | mouseUp(event) {
166 | if (this.dragMainPanelSizer) {
167 | this.dragMainPanelSizer = false;
168 | this.mainPanelWidth = this.mainPanelWidth + (event.pageX - this.dragMainPanelSizerStartX);
169 | this.mainPanelWidth = this.checkMainPanelWidth(this.mainPanelWidth);
170 | this.updateMainPanelWidth(this.mainPanelWidth);
171 | }
172 | }
173 |
174 | /**
175 | * Returns total panel width. This is the width of the main panel (containing the elements),
176 | * the property panel and an optional menu sidebar.
177 | * @returns {Number}
178 | */
179 | getTotalPanelWidth() {
180 | let totalPanelWidth = this.mainPanelWidth + this.mainPanelSizerWidth + 390;
181 | if (this.rb.getProperty('menuSidebar')) {
182 | totalPanelWidth += 92;
183 | }
184 | return totalPanelWidth;
185 | }
186 |
187 | updateMainPanelWidth(mainPanelWidth) {
188 | document.getElementById('rbro_main_panel').style.width = mainPanelWidth + 'px';
189 | document.getElementById('rbro_main_panel_sizer').style.left = mainPanelWidth + 'px';
190 | document.getElementById('rbro_detail_panel').style.left = (mainPanelWidth + this.mainPanelSizerWidth) + 'px';
191 | // calculate width of main panel, detail panel and sidebar (if available)
192 | let totalPanelWidth = mainPanelWidth + this.mainPanelSizerWidth + 390;
193 | if (this.rb.getProperty('menuSidebar')) {
194 | totalPanelWidth += 92;
195 | document.getElementById('reportbro').querySelector('.rbroLogo').style.width = mainPanelWidth + 'px';
196 | }
197 | document.getElementById('rbro_document_panel').style.width = `calc(100% - ${totalPanelWidth}px)`;
198 | }
199 |
200 | checkMainPanelWidth(mainPanelWidth) {
201 | if (mainPanelWidth < 150) {
202 | return 150;
203 | } else if (mainPanelWidth > 500) {
204 | return 500;
205 | }
206 | return mainPanelWidth;
207 | }
208 |
209 | showHeader() {
210 | this.headerItem.show();
211 | }
212 |
213 | hideHeader() {
214 | this.headerItem.hide();
215 | }
216 |
217 | showFooter() {
218 | this.footerItem.show();
219 | }
220 |
221 | hideFooter() {
222 | this.footerItem.hide();
223 | }
224 |
225 | showWatermarks() {
226 | this.watermarksItem.show();
227 | }
228 |
229 | hideWatermarks() {
230 | this.watermarksItem.hide();
231 | }
232 |
233 | clearAll() {
234 | for (const item of this.items) {
235 | item.clear();
236 | }
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/src/panels/EmptyDetailPanel.js:
--------------------------------------------------------------------------------
1 | import * as utils from '../utils';
2 |
3 | /**
4 | * Empty panel which is shown when no data object is selected.
5 | * @class
6 | */
7 | export default class EmptyDetailPanel {
8 | constructor(rootElement, rb) {
9 | this.rootElement = rootElement;
10 | this.rb = rb;
11 | }
12 |
13 | render() {
14 | const elDetailPanel = utils.createElement(
15 | 'div', { id: 'rbro_empty_detail_panel', class: 'rbroEmptyDetailPanel rbroHidden' });
16 | elDetailPanel.append(utils.createElement('div', { class: 'rbroLogo' }));
17 | document.getElementById('rbro_detail_panel').append(elDetailPanel);
18 | }
19 |
20 | destroy() {
21 | }
22 |
23 | show(data) {
24 | document.getElementById('rbro_empty_detail_panel').classList.remove('rbroHidden');
25 | }
26 |
27 | hide() {
28 | document.getElementById('rbro_empty_detail_panel').classList.add('rbroHidden');
29 | }
30 |
31 | isKeyEventDisabled() {
32 | return false;
33 | }
34 |
35 | notifyEvent(obj, operation) {
36 | }
37 |
38 | updateErrors() {
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/rb_logo_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/rb_logo_dark.png
--------------------------------------------------------------------------------
/src/rb_logo_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jobsta/reportbro-designer/7f482651dd25bc6bb968c0402df6c492d03ecde1/src/rb_logo_white.png
--------------------------------------------------------------------------------
/src/toggle-switch.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /*
3 | *
4 | * Based on:
5 | * CSS TOGGLE SWITCH
6 | *
7 | * Ionuț Colceriu - ghinda.net
8 | * https://github.com/ghinda/css-toggle-switch
9 | *
10 | */
11 | /* Hide by default
12 | */
13 | .switch-toggle a,
14 | .switch-light span span { display: none; }
15 |
16 | /* We can't test for a specific feature,
17 | * so we only target browsers with support for media queries.
18 | */
19 | @media only screen {
20 | /* Checkbox
21 | */
22 | .switch-light {
23 | position: relative;
24 | display: block;
25 | /* simulate default browser focus outlines on the switch,
26 | * when the inputs are focused.
27 | */ }
28 | .switch-light::after {
29 | clear: both;
30 | content: '';
31 | display: table; }
32 | .switch-light *,
33 | .switch-light *:before,
34 | .switch-light *:after {
35 | box-sizing: border-box; }
36 | .switch-light a {
37 | display: block;
38 | transition: all 0.2s ease-out; }
39 | .switch-light label,
40 | .switch-light input:focus ~ span a,
41 | .switch-light input:focus + label {
42 | outline:none;
43 | }
44 | }
45 |
46 | @media only screen {
47 | /* don't hide the input from screen-readers and keyboard access
48 | */
49 | .switch-light input[type=checkbox] {
50 | position: absolute;
51 | opacity: 0;
52 | z-index: 3;
53 | }
54 |
55 | .switch-light input:checked ~ span a {
56 | right: 0;
57 | }
58 |
59 | /* inherit from label
60 | */
61 | .switch-light strong {
62 | font-weight: inherit;
63 | }
64 |
65 | .switch-light > span {
66 | position: relative;
67 | overflow: hidden;
68 | display: block;
69 | padding: 0;
70 | text-align: left;
71 | }
72 |
73 | .switch-light span span {
74 | position: relative;
75 | z-index: 2;
76 | display: block;
77 | float: left;
78 | width: 50%;
79 | text-align: center;
80 | user-select: none;
81 | }
82 |
83 | .switch-light a {
84 | position: absolute;
85 | right: 50%;
86 | top: 0;
87 | z-index: 1;
88 | display: block;
89 | width: 50%;
90 | height: 100%;
91 | padding: 0;
92 | }
93 | }
94 |
95 | /* Material Theme
96 | */
97 | /* switch-light
98 | */
99 | @media only screen {
100 | .switch-light.switch-material a {
101 | top: -2px;
102 | width: 26px;
103 | height: 26px;
104 | border-radius: 50%;
105 | background: #fafafa;
106 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 2px -2px rgba(0, 0, 0, 0.2), 0 2px 4px 0 rgba(0, 0, 0, 0.12);
107 | transition: right 0.28s cubic-bezier(0.4, 0, 0.2, 1); }
108 | .switch-material.switch-light {
109 | overflow: visible; }
110 | .switch-material.switch-light::after {
111 | clear: both;
112 | content: '';
113 | display: table; }
114 | .switch-material.switch-light > span {
115 | overflow: visible;
116 | position: relative;
117 | top: 3px;
118 | width: 52px;
119 | height: 24px;
120 | border-radius: 16px;
121 | background: rgba(0, 0, 0, 0.26);
122 | }
123 |
124 | .switch-material.switch-light span span {
125 | position: absolute;
126 | clip: rect(0 0 0 0);
127 | }
128 |
129 | .switch-material.switch-light input:checked ~ span a {
130 | right: 0;
131 | background: #00ad69;
132 | box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.2), 0 1px 6px 0 rgba(0, 0, 0, 0.12);
133 | }
134 |
135 | .switch-material.switch-light input:checked ~ span {
136 | background: rgba(0, 173, 105, 0.3);
137 | }
138 | }
--------------------------------------------------------------------------------
/webpack.common.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
3 |
4 |
5 | module.exports = {
6 | entry: ['./src/main.js', './src/main.css', './src/fonts/font_style.css', './src/iconfonts/style.css', './src/toggle-switch.css', './src/quill.reportbro.css'],
7 | output: {
8 | filename: 'reportbro.js',
9 | path: path.resolve(__dirname, 'dist'),
10 | },
11 | performance : {
12 | hints : false // disable warning for asset size limit (generated js file)
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'], },
18 | {
19 | test: /\.(png|gif)([\?]?.*)$/,
20 | type: 'asset/resource',
21 | generator: {
22 | filename: '[path][name][ext]'
23 | }
24 | },
25 | {
26 | test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
27 | type: 'asset/resource',
28 | generator: {
29 | filename: '[path][name][ext]?[hash]'
30 | }
31 | },
32 | ]
33 | },
34 | plugins: [
35 | new MiniCssExtractPlugin({filename: "reportbro.css"})
36 | ]
37 | };
38 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.common.js');
3 |
4 | module.exports = merge(common, {
5 | mode: 'development',
6 | devtool: 'inline-source-map',
7 | });
8 |
--------------------------------------------------------------------------------
/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const { merge } = require('webpack-merge');
4 | const common = require('./webpack.common.js');
5 |
6 | const banner =
7 | `Copyright (C) 2023 jobsta
8 |
9 | This file is part of ReportBro, a library to generate PDF and Excel reports.
10 | Demos can be found at https://www.reportbro.com
11 |
12 | Dual licensed under AGPLv3 and ReportBro commercial license:
13 | https://www.reportbro.com/license
14 |
15 | You should have received a copy of the GNU Affero General Public License
16 | along with this program. If not, see https://www.gnu.org/licenses/
17 |
18 | Details for ReportBro commercial license can be found at
19 | https://www.reportbro.com/license/agreement
20 | `;
21 |
22 | module.exports = merge(common, {
23 | mode: 'production',
24 | devtool: 'source-map',
25 | output: {
26 | clean: true,
27 | },
28 | module: {
29 | rules: [
30 | {
31 | test: /\.js$/,
32 | include: [path.resolve(__dirname, "src")],
33 | loader: "babel-loader",
34 | options: {
35 | plugins: ['@babel/plugin-transform-runtime'],
36 | presets: ['@babel/preset-env']
37 | },
38 | }
39 | ]
40 | },
41 | plugins: [
42 | new webpack.BannerPlugin({ banner: banner, test: 'reportbro.js' }),
43 | ]
44 | });
45 |
--------------------------------------------------------------------------------