Parcourir la source

Improve card generation code

crobi il y a 10 ans
Parent
révision
287b6b0534
4 fichiers modifiés avec 218 ajouts et 136 suppressions
  1. 3
    7
      generator/css/cards.css
  2. 14
    14
      generator/data/card_data_example.js
  3. 199
    114
      generator/js/cards.js
  4. 2
    1
      generator/js/control.js

+ 3
- 7
generator/css/cards.css Voir le fichier

22
     border-radius: 2mm;
22
     border-radius: 2mm;
23
     margin-top:0px;
23
     margin-top:0px;
24
     background-color: white;
24
     background-color: white;
25
+    border-color: inherit;
25
     display: flex;
26
     display: flex;
26
     flex-direction: column;
27
     flex-direction: column;
27
     flex: 1;
28
     flex: 1;
117
     letter-spacing: 1px;
118
     letter-spacing: 1px;
118
     margin: 0;
119
     margin: 0;
119
     margin-bottom: 0.3em;
120
     margin-bottom: 0.3em;
121
+    border-color: inherit;
120
 }
122
 }
121
 
123
 
122
 p {
124
 p {
124
     margin-bottom: 0.5em;
126
     margin-bottom: 0.5em;
125
 }
127
 }
126
 
128
 
127
-
128
-.fill-1 {flex:1;}
129
-.fill-2 {flex:2;}
130
-.fill-3 {flex:3;}
131
-.fill-4 {flex:4;}
132
-.fill-5 {flex:5;}
133
-.fill-6 {flex:6;}
129
+.fill {flex:1;}
134
 
130
 
135
 .description-line {
131
 .description-line {
136
     margin-bottom: 0.5em;
132
     margin-bottom: 0.5em;

+ 14
- 14
generator/data/card_data_example.js Voir le fichier

12
             "property | Range | Self (15ft cone)",
12
             "property | Range | Self (15ft cone)",
13
             "property | Components | V,S",
13
             "property | Components | V,S",
14
             "rule",
14
             "rule",
15
-            "fill-2",
15
+            "fill | 2",
16
             "text | Each creature in a 15-foot cone must make a Dexterity saving throw. A creature takes <b>3d6 fire damage</b> on a failed save, or half as much damage on a successful one.",
16
             "text | Each creature in a 15-foot cone must make a Dexterity saving throw. A creature takes <b>3d6 fire damage</b> on a failed save, or half as much damage on a successful one.",
17
             "text | The fire ignites any flammable objects in the area that aren’t being worn or carried.",
17
             "text | The fire ignites any flammable objects in the area that aren’t being worn or carried.",
18
-            "fill-3",
18
+            "fill | 3",
19
             "section | At higher levels",
19
             "section | At higher levels",
20
             "text | +1d6 damage for each slot above 1st"
20
             "text | +1d6 damage for each slot above 1st"
21
         ]
21
         ]
29
         "contents": [
29
         "contents": [
30
             "subtitle | Rogue feature",
30
             "subtitle | Rogue feature",
31
             "rule",
31
             "rule",
32
-            "fill-2",
32
+            "fill | 2",
33
             "text | You can take a <b>bonus action on each of your turns</b> in combat. This action can be used only to take the <b>Dash, Disengage, or Hide</b> action.",
33
             "text | You can take a <b>bonus action on each of your turns</b> in combat. This action can be used only to take the <b>Dash, Disengage, or Hide</b> action.",
34
-            "fill-3",
34
+            "fill | 2",
35
             "section | Fast hands (Thief 3rd)",
35
             "section | Fast hands (Thief 3rd)",
36
             "text | You can also use the bonus action to make a Dexterity (<b>Sleight of Hand</b>) check, use your thieves’ tools to <b>disarm a trap</b> or <b>open a lock</b>, or take the <b>Use an Object</b> action.",
36
             "text | You can also use the bonus action to make a Dexterity (<b>Sleight of Hand</b>) check, use your thieves’ tools to <b>disarm a trap</b> or <b>open a lock</b>, or take the <b>Use an Object</b> action.",
37
         ]
37
         ]
48
             "property | Strength required | 15",
48
             "property | Strength required | 15",
49
             "property | Stealth | Disadvantage",
49
             "property | Stealth | Disadvantage",
50
             "rule",
50
             "rule",
51
-            "fill-2",
51
+            "fill | 2",
52
             "description | Heavy | Unless you have the required strength, your speed is reduced by 10 feet.",
52
             "description | Heavy | Unless you have the required strength, your speed is reduced by 10 feet.",
53
             "description | Stealth | You have disadvantage on Dexterity (Stealth) checks.",
53
             "description | Stealth | You have disadvantage on Dexterity (Stealth) checks.",
54
-            "fill-3"
54
+            "fill | 3"
55
         ]
55
         ]
56
     },
56
     },
57
     {
57
     {
66
             "property | Modifier | Strength or Dexterity",
66
             "property | Modifier | Strength or Dexterity",
67
             "property | Properties | Light, Finesse, Thrown (20/60)",
67
             "property | Properties | Light, Finesse, Thrown (20/60)",
68
             "rule",
68
             "rule",
69
-            "fill-2",
69
+            "fill | 2",
70
             "description | Finesse | Use your choice of Strength or Dexterity modifier for attack and damage.",
70
             "description | Finesse | Use your choice of Strength or Dexterity modifier for attack and damage.",
71
             "description | Light | When you attack while dual wielding light weapons, you may use a bonus action to attack with your off hand.",
71
             "description | Light | When you attack while dual wielding light weapons, you may use a bonus action to attack with your off hand.",
72
             "description | Thrown | You can throw the weapon to make a ranged attack with the given range.",
72
             "description | Thrown | You can throw the weapon to make a ranged attack with the given range.",
73
-            "fill-3"
73
+            "fill | 3"
74
         ]
74
         ]
75
     },
75
     },
76
     {
76
     {
85
             "property | Modifier | Strength or Dexterity",
85
             "property | Modifier | Strength or Dexterity",
86
             "property | Properties | Light, Finesse",
86
             "property | Properties | Light, Finesse",
87
             "rule",
87
             "rule",
88
-            "fill-2",
88
+            "fill | 2",
89
             "description | Finesse | Use your choice of Strength or Dexterity modifier for attack and damage.",
89
             "description | Finesse | Use your choice of Strength or Dexterity modifier for attack and damage.",
90
             "description | Light | When you attack while dual wielding light weapons, you may use a bonus action to attack with your off hand.",
90
             "description | Light | When you attack while dual wielding light weapons, you may use a bonus action to attack with your off hand.",
91
-            "fill-3"
91
+            "fill | 3"
92
         ]
92
         ]
93
     },
93
     },
94
     {
94
     {
103
             "property | Recharge | 1d6+1 each day",
103
             "property | Recharge | 1d6+1 each day",
104
             "property | Depletion | If you expend the last charge, roll a d20. On a 1, the item is destroyed.",
104
             "property | Depletion | If you expend the last charge, roll a d20. On a 1, the item is destroyed.",
105
             "rule",
105
             "rule",
106
-            "fill-2",
106
+            "fill | 2",
107
             "description | Spells | You can use your action to cast the following spells:",
107
             "description | Spells | You can use your action to cast the following spells:",
108
             "text | - magic missile, 1st level (1 charge)",
108
             "text | - magic missile, 1st level (1 charge)",
109
             "text | - magic missile, 2nd level (2 charges)",
109
             "text | - magic missile, 2nd level (2 charges)",
110
             "text | - magic missile, 3rd level (3 charges)",
110
             "text | - magic missile, 3rd level (3 charges)",
111
-            "fill-3"
111
+            "fill | 3"
112
         ]
112
         ]
113
     },
113
     },
114
     {
114
     {
122
             "property | Use time | 1 action",
122
             "property | Use time | 1 action",
123
             "property | Hit points restored | 2d4+2",
123
             "property | Hit points restored | 2d4+2",
124
             "rule",
124
             "rule",
125
-            "fill-2",
125
+            "fill | 2",
126
             "text | When you drink this potion, you regain 2d4+2 hitpoints.",
126
             "text | When you drink this potion, you regain 2d4+2 hitpoints.",
127
             "text | Drinking or administering a potion takes 1 action.",
127
             "text | Drinking or administering a potion takes 1 action.",
128
-            "fill-3"
128
+            "fill | 3"
129
         ]
129
         ]
130
     }
130
     }
131
 ];
131
 ];

+ 199
- 114
generator/js/cards.js Voir le fichier

1
-function card_title(text) {
2
-    return '<div class="title">' + text + '</div>';
1
+// ============================================================================
2
+// Card element generating functions
3
+// ============================================================================
4
+
5
+function card_element_title(card_data) {
6
+    var title = card_data.title || "";
7
+    return '<div class="title">' + title + '</div>';
8
+}
9
+
10
+function card_element_icon(card_data) {
11
+    var icon = card_data.icon_front || card_data.icon;
12
+    var result = "";
13
+    if (icon) {
14
+        result += '<div class="title-icon-container">';
15
+        result += '    <div class="title-icon icon-' + icon + '">';
16
+        result += '    </div>';
17
+        result += '</div>';
18
+    }
19
+    return result;
3
 }
20
 }
4
 
21
 
5
-function card_subtitle(text) {
6
-    return '<div class="subtitle">' + text + '</div>';
22
+function card_element_subtitle(params, card_data) {
23
+    var subtitle = params[0] || "";
24
+    return '<div class="subtitle">' + subtitle + '</div>';
7
 }
25
 }
8
 
26
 
9
-function card_ruler() {
27
+function card_element_ruler(params, card_data) {
10
     return '<div class="ruler"></div>';
28
     return '<div class="ruler"></div>';
11
 }
29
 }
12
 
30
 
13
-function card_property(name, text) {
31
+function card_element_property(params, card_data) {
14
     var result = "";
32
     var result = "";
15
     result += '<div class="property-line">';
33
     result += '<div class="property-line">';
16
-    result += '   <h4 class="property-name">' + name.trim() + '</h4>';
17
-    result += '   <p class="property-text">' + text.trim() + '</p>';
34
+    result += '   <h4 class="property-name">' + params[0] + '</h4>';
35
+    result += '   <p class="property-text">' + params[1] + '</p>';
18
     result += '</div>';
36
     result += '</div>';
19
     return result;
37
     return result;
20
 }
38
 }
21
 
39
 
22
-function card_description(name, text) {
40
+function card_element_description(params, card_data) {
23
     var result = "";
41
     var result = "";
24
     result += '<div class="description-line">';
42
     result += '<div class="description-line">';
25
-    result += '   <h4 class="description-name">' + name.trim() + '</h4>';
26
-    result += '   <p class="description-text">' + text.trim() + '</p>';
43
+    result += '   <h4 class="description-name">' + params[0] + '</h4>';
44
+    result += '   <p class="description-text">' + params[1] + '</p>';
27
     result += '</div>';
45
     result += '</div>';
28
     return result;
46
     return result;
29
 }
47
 }
30
 
48
 
31
-function card_section(text) {
32
-    return '<h3>'+text+'</h3>';
49
+function card_element_text(params, card_data) {
50
+    var result = "";
51
+    result += '<div class="description-line">';
52
+    result += '   <p class="description-text">' + params[0] + '</p>';
53
+    result += '</div>';
54
+    return result;
33
 }
55
 }
34
 
56
 
35
-function card_fill1() {
36
-    return '<div class="fill-1"></div>';
57
+function card_element_section(params, card_data) {
58
+    var color = card_data.color_front || card_data.color;
59
+    var section = params[0] || "";
60
+    return '<h3 style="color:' + color + '">' + section + '</h3>';
37
 }
61
 }
38
-function card_fill2() {
39
-    return '<div class="fill-2"></div>';
40
-}
41
-function card_fill3() {
42
-    return '<div class="fill-3"></div>';
43
-}
44
-function card_fill4() {
45
-    return '<div class="fill-4"></div>';
62
+
63
+function card_element_fill(params, card_data) {
64
+    var flex = params[0] || "1";
65
+    return '<div class="fill" style="flex:' + flex + '"></div>';
46
 }
66
 }
47
 
67
 
48
-function card_icon(name) {
49
-    var result = "";
50
-    result += '<div class="title-icon-container">';
51
-    result += '    <div class="title-icon icon-' + name + '">';
52
-    result += '    </div>';
53
-    result += '</div>';
54
-    return result;
68
+function card_element_unknown(params, card_data) {
69
+    return '<div>' + params.join('<br />') + '</div>';
55
 }
70
 }
56
 
71
 
57
-function card_contents(contents) {
72
+var card_element_generators = {
73
+    subtitle: card_element_subtitle,
74
+    property: card_element_property,
75
+    rule: card_element_ruler,
76
+    description: card_element_description,
77
+    text: card_element_text,
78
+    fill: card_element_fill
79
+};
80
+
81
+// ============================================================================
82
+// Card generating functions
83
+// ============================================================================
84
+
85
+function card_generate_contents(contents, card_data) {
58
     var result = "";
86
     var result = "";
59
     result += '<div class="content-container">';
87
     result += '<div class="content-container">';
60
     result += contents.map(function (value) {
88
     result += contents.map(function (value) {
61
         var parts = value.split("|").map(function (str) { return str.trim(); });
89
         var parts = value.split("|").map(function (str) { return str.trim(); });
62
-        switch (parts[0]) {
63
-            case 'subtitle': return card_subtitle(parts[1], parts[2]); break;
64
-            case 'property': return card_property(parts[1], parts[2]); break;
65
-            case 'rule': return card_ruler(); break;
66
-            case 'description': return card_description(parts[1], parts[2]); break;
67
-            case 'text': return card_description("", parts[1]); break;
68
-            case 'fill-1': return card_fill1(); break;
69
-            case 'fill-2': return card_fill2(); break;
70
-            case 'fill-3': return card_fill3(); break;
71
-            case 'fill-4': return card_fill4(); break;
72
-            case 'section': return card_section(parts[1]); break;
73
-            default: return "";
90
+        var element_name = parts[0];
91
+        var element_params = parts.splice(1);
92
+        var element_generator = card_element_generators[element_name];
93
+        if (element_generator) {
94
+            return element_generator(element_params, card_data);
95
+        } else {
96
+            return card_element_unknown(element_params, card_data);
74
         }
97
         }
75
     }).join("\n");
98
     }).join("\n");
76
     result += '</div>';
99
     result += '</div>';
77
     return result;
100
     return result;
78
 }
101
 }
79
 
102
 
80
-var card_default_data = {
81
-    count:1,
82
-    title:"",
83
-    icon:"",
84
-    contents:[],
85
-    color: "white"
103
+function card_repeat(card, count) {
104
+    var result = [];
105
+    for (var i = 0; i < count; ++i) {
106
+        result.push(card);
107
+    }
108
+    return result;
109
+}
110
+
111
+function card_generate_color_style(color) {
112
+    return 'style="color:' + color + '; border-color:' + color + '; background-color:' + color + '"';
86
 }
113
 }
87
 
114
 
88
-function card(data) {
89
-    var front = "";
90
-    var back = "";
115
+function card_generate_color_gradient_style(color) {
116
+    return 'style="background: radial-gradient(ellipse at center, white 20%, ' + color + ' 120%)"';
117
+}
91
 
118
 
92
-    front += '<div class="card color-' + data.color + '">';
93
-    front += card_icon(data.icon);
94
-    front += card_title(data.title);
95
-    front += card_contents(data.contents);
96
-    front += '</div>';
119
+function card_generate_front(data) {
120
+    var color = data.color_front || data.color || "black";
121
+    var style_color = card_generate_color_style(color);
122
+    var icon = data.icon_front || data.icon || "";
123
+    var count = data.count || 1;
97
 
124
 
98
-    var icon_back = data.icon_back || data.icon;
99
-    back += '<div class="card color-' + data.color + '">';
100
-    back += '  <div class="card-back">';
101
-    back += '    <div class="card-back-inner">';
102
-    back += '      <div class="back-icon icon-' + icon_back + '"></div>';
103
-    back += '    </div>';
104
-    back += '  </div>';
105
-    back += '</div>';
125
+    var result = "";
126
+    result += '<div class="card" ' + style_color + '>';
127
+    result += card_element_icon(data);
128
+    result += card_element_title(data);
129
+    result += card_generate_contents(data.contents, data);
130
+    result += '</div>';
106
 
131
 
132
+    return card_repeat(result, count);
133
+}
134
+
135
+function card_generate_back(data) {
136
+    var color = data.color_back || data.color || "black";
137
+    var style_color = card_generate_color_style(color);
138
+    var style_gradient = card_generate_color_gradient_style(color);
139
+    var icon = data.icon_back || data.icon || "ace";
107
     var count = data.count || 1;
140
     var count = data.count || 1;
108
-    var result = { front: [], back: [] };
109
-    for (var i = 0; i < count; ++i) {
110
-        result.front.push(front);
111
-        result.back.push(back);
112
-    }
113
-    return result;
141
+
142
+    var result = "";
143
+    result += '<div class="card" ' + style_color + '>';
144
+    result += '  <div class="card-back" ' + style_gradient + '>';
145
+    result += '    <div class="card-back-inner">';
146
+    result += '      <div class="back-icon icon-' + icon + '" ' + style_color + '></div>';
147
+    result += '    </div>';
148
+    result += '  </div>';
149
+    result += '</div>';
150
+
151
+    return card_repeat(result, count);
152
+}
153
+
154
+function card_generate_empty(count) {
155
+    var style_color = card_generate_color_style("white");
156
+
157
+    var result = "";
158
+    result += '<div class="card" ' + style_color + '>';
159
+    result += '</div>';
160
+
161
+    return card_repeat(result, count);
114
 }
162
 }
115
 
163
 
116
-function card_split_pages(data, cards_per_page) {
164
+// ============================================================================
165
+// Functions that generate pages of cards
166
+// ============================================================================
167
+
168
+function card_pages_split(data, rows, cols) {
169
+    var cards_per_page = rows * cols;
117
     var result = [];
170
     var result = [];
118
     for (var i = 0; i < data.length; i += cards_per_page) {
171
     for (var i = 0; i < data.length; i += cards_per_page) {
119
         var page = data.slice(i, i + cards_per_page);
172
         var page = data.slice(i, i + cards_per_page);
122
     return result;
175
     return result;
123
 }
176
 }
124
 
177
 
125
-function cards_flip_left_right(cards) {
126
-    return [
127
-        cards[2], cards[1], cards[0],
128
-        cards[5], cards[4], cards[3],
129
-        cards[8], cards[7], cards[6]
130
-    ];
178
+function card_pages_merge(front_pages, back_pages) {
179
+    var result = [];
180
+    for (var i = 0; i < front_pages.length; ++i) {
181
+        result.push(front_pages[i]);
182
+        result.push(back_pages[i]);
183
+    }
184
+    return result;
131
 }
185
 }
132
 
186
 
133
-function card_generate_html(datas) {
134
-    var front = [];
135
-    var back = [];
187
+function cards_pages_flip_left_right(cards, rows, cols) {
188
+    var result = [];
189
+    for (var r = 0; r < rows; ++r) {
190
+        for (var c = 0; c < cols; ++c) {
191
+            var i = r*cols + (cols-1-c);
192
+            result.push(cards[i]);
193
+        }
194
+    }
195
+    return result;
196
+}
136
 
197
 
137
-    // Generate HTML for each card
138
-    datas.forEach(function (data) {
139
-        var result = card(data);
140
-        front = front.concat(result.front);
141
-        back = back.concat(result.back);
142
-    });
198
+function card_pages_add_padding(cards, rows, cols) {
199
+    var cards_per_page = rows * cols;
200
+    var last_page_cards = cards.length % cards_per_page;
201
+    if (last_page_cards !== 0) {
202
+        return cards.concat(card_generate_empty(cards_per_page - last_page_cards));
203
+    } else {
204
+        return cards;
205
+    }
206
+}
143
 
207
 
144
-    // Fill the last page with blank cards
145
-    if (front.length % 9 !== 0) {
146
-        var result = card(card_default_data);
147
-        for (var i = front.length % 9; i < 9; ++i) {
148
-            front = front.concat(result.front);
149
-            back = back.concat(result.back);
150
-        }
208
+function card_pages_wrap(pages) {
209
+    var size = "A4";
210
+
211
+    var result = "";
212
+    for (var i = 0; i < pages.length; ++i) {
213
+        result += '<page size="' + size + '">\n';
214
+        result += pages[i].join("\n");
215
+        result += '</page>\n';
151
     }
216
     }
217
+    return result;
218
+}
219
+
220
+function card_pages_generate_html(card_data) {
221
+    var rows = 3;
222
+    var cols = 3;
223
+
224
+    // Generate the HTML for each card
225
+    var front_cards = [];
226
+    var back_cards = [];
227
+    card_data.forEach(function (data) {
228
+        front_cards = front_cards.concat(card_generate_front(data));
229
+        back_cards = back_cards.concat(card_generate_back(data));
230
+    });
231
+
232
+    // Add padding cards so that the last page is full of cards
233
+    front_cards = card_pages_add_padding(front_cards, rows, cols);
234
+    back_cards = card_pages_add_padding(back_cards, rows, cols);
152
 
235
 
153
-    // Split pages
154
-    front_pages = card_split_pages(front, 9);
155
-    back_pages = card_split_pages(back, 9);
236
+    // Split cards to pages
237
+    var front_pages = card_pages_split(front_cards, rows, cols);
238
+    var back_pages = card_pages_split(back_cards, rows, cols);
239
+
240
+    // Shuffle back cards so that they line up with their corresponding front cards
241
+    back_pages = back_pages.map(function (page) {
242
+        return cards_pages_flip_left_right(page, rows, cols);
243
+    });
244
+
245
+    // Interleave front and back pages so that we can print double-sided
246
+    var pages = card_pages_merge(front_pages, back_pages);
247
+
248
+    // Wrap all pages in a <page> element
249
+    return card_pages_wrap(pages);
250
+}
251
+
252
+function card_pages_insert_into(card_data, container) {
156
 
253
 
157
     // Clear the previous content of the document
254
     // Clear the previous content of the document
158
-    var parent_element = document.getElementsByClassName("container")[0];
159
-    while (parent_element.hasChildNodes()) {
160
-        parent_element.removeChild(parent_element.lastChild);
255
+    while (container.hasChildNodes()) {
256
+        container.removeChild(container.lastChild);
161
     }
257
     }
162
 
258
 
163
-    // Add generated HTML to the document
164
-    for (var i = 0; i < front_pages.length; ++i) {
165
-        var page = document.createElement("page");
166
-        page.setAttribute("size", "A4");
167
-        page.innerHTML = front_pages[i].join("\n");
168
-        parent_element.appendChild(page);
169
-
170
-        var page = document.createElement("page");
171
-        page.setAttribute("size", "A4");
172
-        page.innerHTML = cards_flip_left_right(back_pages[i]).join("\n");
173
-        parent_element.appendChild(page);
174
-    }
259
+    // Insert the HTML
260
+    var html = card_pages_generate_html(card_data);
261
+    container.innerHTML = html;
175
 }
262
 }
176
-
177
-//card_generate_html(card_data);

+ 2
- 1
generator/js/control.js Voir le fichier

7
 
7
 
8
 input_button.onclick = function () {
8
 input_button.onclick = function () {
9
     var card_data = JSON.parse(input_data.value);
9
     var card_data = JSON.parse(input_data.value);
10
-    card_generate_html(card_data);
10
+    var container = document.getElementsByClassName("container")[0];
11
+    card_pages_insert_into(card_data, container);
11
 }
12
 }

Loading…
Annuler
Enregistrer