@reldens/cms 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/reldens-admin-client.css +1 -1
- package/admin/templates/dashboard.html +1 -1
- package/bin/reldens-cms.js +1 -1
- package/install/index.html +0 -7
- package/install/success.html +30 -0
- package/lib/admin-manager.js +1 -1
- package/lib/admin-templates-loader.js +37 -0
- package/lib/admin-translations.js +4 -218
- package/lib/allowed-extensions.js +11 -0
- package/lib/entities-loader.js +3 -6
- package/lib/frontend.js +4 -13
- package/lib/installer.js +39 -42
- package/lib/loaded-entities-processor.js +30 -0
- package/lib/manager.js +92 -33
- package/lib/mime-types.js +35 -0
- package/lib/templates-list.js +0 -9
- package/lib/templates-to-path-mapper.js +28 -0
- package/package.json +2 -2
- package/templates/assets/web/loading.gif +0 -0
- package/templates/assets/web/reldens-your-logo-mage.png +0 -0
- package/templates/index.js.dist +2 -2
- package/admin/templates/maps-wizard-maps-selection.html +0 -85
- package/admin/templates/maps-wizard.html +0 -341
- package/admin/templates/objects-import.html +0 -143
- package/admin/templates/skills-import.html +0 -201
package/lib/manager.js
CHANGED
|
@@ -4,12 +4,21 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const {
|
|
8
|
-
const {
|
|
7
|
+
const { TemplatesList } = require('./templates-list');
|
|
8
|
+
const { AdminTranslations } = require('./admin-translations');
|
|
9
|
+
const { AdminTemplatesLoader } = require('./admin-templates-loader');
|
|
10
|
+
const { AdminManagerValidator } = require('./admin-manager-validator');
|
|
11
|
+
const { MimeTypes } = require('./mime-types');
|
|
12
|
+
const { AllowedExtensions } = require('./allowed-extensions');
|
|
13
|
+
const { TemplatesToPathMapper } = require('./templates-to-path-mapper');
|
|
14
|
+
const { AdminEntitiesGenerator } = require('./admin-entities-generator');
|
|
15
|
+
const { LoadedEntitiesProcessor } = require('./loaded-entities-processor');
|
|
9
16
|
const { AdminManager } = require('./admin-manager');
|
|
10
17
|
const { Installer } = require('./installer');
|
|
11
18
|
const { Frontend } = require('./frontend');
|
|
12
19
|
const { EventsManagerSingleton, Logger, sc } = require('@reldens/utils');
|
|
20
|
+
const { DriversMap } = require('@reldens/storage');
|
|
21
|
+
const { AppServerFactory, FileHandler, Encryptor } = require('@reldens/server-utils');
|
|
13
22
|
const dotenv = require('dotenv');
|
|
14
23
|
const mustache = require('mustache');
|
|
15
24
|
|
|
@@ -23,14 +32,27 @@ class Manager
|
|
|
23
32
|
this.installLockPath = FileHandler.joinPaths(this.projectRoot, 'install.lock');
|
|
24
33
|
dotenv.config({path: this.envFilePath});
|
|
25
34
|
this.config = this.loadConfigFromEnv();
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
|
|
35
|
+
this.adminEntities = sc.get(props, 'adminEntities', {});
|
|
36
|
+
this.rawRegisteredEntities = sc.get(props, 'rawRegisteredEntities', {});
|
|
29
37
|
this.entitiesTranslations = sc.get(props, 'entitiesTranslations', {});
|
|
38
|
+
this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
|
|
39
|
+
this.processedEntities = sc.get(props, 'processedEntities', {});
|
|
30
40
|
this.authenticationMethod = sc.get(props, 'authenticationMethod', 'db-users');
|
|
31
41
|
this.authenticationCallback = sc.get(props, 'authenticationCallback', false);
|
|
32
42
|
this.events = sc.get(props, 'events', EventsManagerSingleton);
|
|
43
|
+
this.adminTemplatesList = sc.get(props, 'adminTemplatesList', TemplatesList);
|
|
44
|
+
this.projectAdminPath = FileHandler.joinPaths(this.projectRoot, 'admin');
|
|
45
|
+
this.projectAdminTemplatesPath = FileHandler.joinPaths(this.projectAdminPath, 'templates');
|
|
46
|
+
this.mimeTypes = sc.get(props, 'mimeTypes', MimeTypes);
|
|
47
|
+
this.allowedExtensions = sc.get(props, 'allowedExtensions', AllowedExtensions)
|
|
48
|
+
this.adminRoleId = sc.get(props, 'adminRoleId', 99);
|
|
49
|
+
this.stylesFilePath = sc.get(props, 'stylesFilePath', '/css/reldens-admin-client.css');
|
|
50
|
+
this.scriptsFilePath = sc.get(props, 'scriptsFilePath', '/js/reldens-admin-client.js');
|
|
51
|
+
this.companyName = sc.get(props, 'companyName', 'Reldens - CMS');
|
|
52
|
+
this.logo = sc.get(props, 'logo', '/assets/web/reldens-your-logo-mage.png');
|
|
53
|
+
this.favicon = sc.get(props, 'favicon', '/assets/web/favicon.ico');
|
|
33
54
|
this.appServerFactory = new AppServerFactory();
|
|
55
|
+
this.adminEntitiesGenerator = new AdminEntitiesGenerator();
|
|
34
56
|
this.installer = new Installer({
|
|
35
57
|
projectRoot: this.projectRoot,
|
|
36
58
|
postInstallCallback: this.initializeCmsAfterInstall.bind(this)
|
|
@@ -84,36 +106,24 @@ class Manager
|
|
|
84
106
|
return true;
|
|
85
107
|
}
|
|
86
108
|
try {
|
|
87
|
-
await this.
|
|
88
|
-
await this.initializeAdminManager();
|
|
89
|
-
await this.initializeFrontend();
|
|
90
|
-
await this.appServer.listen(this.config.port);
|
|
109
|
+
await this.initializeServices();
|
|
91
110
|
Logger.info('CMS running on '+this.config.host+':'+this.config.port);
|
|
92
111
|
return true;
|
|
93
112
|
} catch (error) {
|
|
94
|
-
Logger.
|
|
113
|
+
Logger.critical('Failed to start CMS: '+error.message);
|
|
95
114
|
return false;
|
|
96
115
|
}
|
|
97
116
|
}
|
|
98
117
|
|
|
99
|
-
async initializeCmsAfterInstall(
|
|
118
|
+
async initializeCmsAfterInstall(loadEntities)
|
|
100
119
|
{
|
|
101
120
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
this.entitiesTranslations = sc.get(entitiesData, 'entitiesTranslations', this.entitiesTranslations);
|
|
107
|
-
}
|
|
121
|
+
this.rawRegisteredEntities = loadEntities.rawRegisteredEntities;
|
|
122
|
+
this.entitiesTranslations = loadEntities.entitiesTranslations;
|
|
123
|
+
this.entitiesConfig = loadEntities.entitiesConfig;
|
|
124
|
+
this.dataServer.rawEntities = loadEntities.rawRegisteredEntities;
|
|
108
125
|
this.config = this.loadConfigFromEnv();
|
|
109
|
-
|
|
110
|
-
Logger.critical('App server creation failed: '+this.appServerFactory.error.message);
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
await this.initializeDataServer();
|
|
114
|
-
await this.initializeAdminManager();
|
|
115
|
-
await this.initializeFrontend();
|
|
116
|
-
await this.appServer.listen(this.config.port);
|
|
126
|
+
await this.initializeServices();
|
|
117
127
|
Logger.info('CMS initialized after installation on '+this.config.host+':'+this.config.port);
|
|
118
128
|
return true;
|
|
119
129
|
} catch (error) {
|
|
@@ -122,6 +132,32 @@ class Manager
|
|
|
122
132
|
}
|
|
123
133
|
}
|
|
124
134
|
|
|
135
|
+
async initializeServices()
|
|
136
|
+
{
|
|
137
|
+
await this.initializeDataServer();
|
|
138
|
+
if (0 === Object.keys(this.adminEntities).length){
|
|
139
|
+
if(0 === Object.keys(this.processedEntities).length){
|
|
140
|
+
this.processedEntities = LoadedEntitiesProcessor.process(
|
|
141
|
+
this.rawRegisteredEntities,
|
|
142
|
+
this.entitiesTranslations,
|
|
143
|
+
this.entitiesConfig
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
if(!this.processedEntities.entities){
|
|
147
|
+
Logger.critical('Processed entities undefined.');
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
await this.dataServer.generateEntities();
|
|
151
|
+
this.adminEntities = this.adminEntitiesGenerator.generate(
|
|
152
|
+
this.processedEntities.entities,
|
|
153
|
+
this.dataServer.entityManager.entities
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
await this.initializeAdminManager();
|
|
157
|
+
await this.initializeFrontend();
|
|
158
|
+
await this.appServer.listen(this.config.port);
|
|
159
|
+
}
|
|
160
|
+
|
|
125
161
|
async initializeDataServer()
|
|
126
162
|
{
|
|
127
163
|
let dbConfig = {
|
|
@@ -133,7 +169,7 @@ class Manager
|
|
|
133
169
|
user: this.config.database.user,
|
|
134
170
|
password: this.config.database.password
|
|
135
171
|
},
|
|
136
|
-
rawEntities: this.
|
|
172
|
+
rawEntities: this.rawRegisteredEntities
|
|
137
173
|
};
|
|
138
174
|
let DriverClass = DriversMap[this.config.database.driver];
|
|
139
175
|
if(!DriverClass){
|
|
@@ -152,35 +188,58 @@ class Manager
|
|
|
152
188
|
async initializeAdminManager()
|
|
153
189
|
{
|
|
154
190
|
let authenticationCallback = this.authenticationCallback;
|
|
155
|
-
if('db-users' === this.authenticationMethod){
|
|
191
|
+
if('db-users' === this.authenticationMethod && !authenticationCallback){
|
|
156
192
|
authenticationCallback = async (email, password, roleId) => {
|
|
193
|
+
Logger.debug('Running default "db-users" authentication.');
|
|
157
194
|
let usersEntity = this.dataServer.getEntity('users');
|
|
158
195
|
if(!usersEntity){
|
|
196
|
+
Logger.critical('No users entity found.');
|
|
159
197
|
return false;
|
|
160
198
|
}
|
|
161
199
|
let user = await usersEntity.loadOneBy('email', email);
|
|
162
200
|
if(!user){
|
|
201
|
+
Logger.debug('User not found by email: '+email+'.', user);
|
|
163
202
|
return false;
|
|
164
203
|
}
|
|
165
204
|
if(Number(user.role_id) !== Number(roleId)){
|
|
205
|
+
Logger.debug('Invalid user role ID: '+roleId+' / '+user.role_id+'.');
|
|
166
206
|
return false;
|
|
167
207
|
}
|
|
168
|
-
|
|
208
|
+
let passwordResult = Encryptor.validatePassword(password, user.password) ? user : false;
|
|
209
|
+
if(!passwordResult){
|
|
210
|
+
Logger.debug('Invalid user password for: '+email+'.');
|
|
211
|
+
}
|
|
212
|
+
return passwordResult;
|
|
169
213
|
};
|
|
170
214
|
}
|
|
171
215
|
let adminConfig = {
|
|
172
216
|
events: this.events,
|
|
173
|
-
renderCallback: this.renderCallback.bind(this),
|
|
174
217
|
dataServer: this.dataServer,
|
|
175
218
|
authenticationCallback,
|
|
176
219
|
app: this.app,
|
|
177
220
|
appServerFactory: this.appServerFactory,
|
|
221
|
+
entities: this.adminEntities,
|
|
222
|
+
validator: new AdminManagerValidator(),
|
|
223
|
+
renderCallback: this.renderCallback.bind(this),
|
|
178
224
|
secret: this.config.adminSecret,
|
|
179
225
|
rootPath: this.config.adminPath,
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
226
|
+
translations: AdminTranslations.appendTranslations(this.entitiesTranslations || {}),
|
|
227
|
+
adminFilesContents: await AdminTemplatesLoader.fetchAdminFilesContents(
|
|
228
|
+
TemplatesToPathMapper.map(this.adminTemplatesList, this.projectAdminTemplatesPath)
|
|
229
|
+
),
|
|
230
|
+
mimeTypes: this.mimeTypes,
|
|
231
|
+
allowedExtensions: this.allowedExtensions,
|
|
232
|
+
adminRoleId: this.adminRoleId,
|
|
233
|
+
stylesFilePath: this.stylesFilePath,
|
|
234
|
+
scriptsFilePath: this.scriptsFilePath,
|
|
235
|
+
branding: {
|
|
236
|
+
companyName: this.companyName,
|
|
237
|
+
logo: this.logo,
|
|
238
|
+
favicon: this.favicon,
|
|
239
|
+
copyRight: await FileHandler.fetchFileContents(
|
|
240
|
+
FileHandler.joinPaths(this.projectAdminTemplatesPath, this.adminTemplatesList.defaultCopyRight)
|
|
241
|
+
)
|
|
242
|
+
}
|
|
184
243
|
};
|
|
185
244
|
this.adminManager = new AdminManager(adminConfig);
|
|
186
245
|
await this.adminManager.setupAdmin();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - MimeTypes
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
module.exports.MimeTypes = {
|
|
8
|
+
audio: [
|
|
9
|
+
'audio/aac',
|
|
10
|
+
'audio/midi',
|
|
11
|
+
'audio/x-midi',
|
|
12
|
+
'audio/mpeg',
|
|
13
|
+
'audio/ogg',
|
|
14
|
+
'application/ogg',
|
|
15
|
+
'audio/opus',
|
|
16
|
+
'audio/wav',
|
|
17
|
+
'audio/webm',
|
|
18
|
+
'audio/3gpp2'
|
|
19
|
+
],
|
|
20
|
+
image: [
|
|
21
|
+
'image/bmp',
|
|
22
|
+
'image/gif',
|
|
23
|
+
'image/jpeg',
|
|
24
|
+
'image/png',
|
|
25
|
+
'image/svg+xml',
|
|
26
|
+
'image/vnd.microsoft.icon',
|
|
27
|
+
'image/tiff',
|
|
28
|
+
'image/webp'
|
|
29
|
+
],
|
|
30
|
+
text: [
|
|
31
|
+
'application/json',
|
|
32
|
+
'application/ld+json',
|
|
33
|
+
'text/plain',
|
|
34
|
+
]
|
|
35
|
+
};
|
package/lib/templates-list.js
CHANGED
|
@@ -8,10 +8,6 @@ module.exports.TemplatesList = {
|
|
|
8
8
|
login: 'login.html',
|
|
9
9
|
dashboard: 'dashboard.html',
|
|
10
10
|
management: 'management.html',
|
|
11
|
-
mapsWizard: 'maps-wizard.html',
|
|
12
|
-
mapsWizardMapsSelection: 'maps-wizard-maps-selection.html',
|
|
13
|
-
objectsImport: 'objects-import.html',
|
|
14
|
-
skillsImport: 'skills-import.html',
|
|
15
11
|
list: 'list.html',
|
|
16
12
|
listContent: 'list-content.html',
|
|
17
13
|
view: 'view.html',
|
|
@@ -41,10 +37,5 @@ module.exports.TemplatesList = {
|
|
|
41
37
|
button: 'button.html',
|
|
42
38
|
file: 'file.html'
|
|
43
39
|
}
|
|
44
|
-
},
|
|
45
|
-
sections: {
|
|
46
|
-
view: {
|
|
47
|
-
rooms: 'rooms.html'
|
|
48
|
-
}
|
|
49
40
|
}
|
|
50
41
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - TemplatesToPathMapper
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { sc } = require('@reldens/utils');
|
|
8
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
9
|
+
|
|
10
|
+
class TemplatesToPathMapper
|
|
11
|
+
{
|
|
12
|
+
|
|
13
|
+
map(templateList, path)
|
|
14
|
+
{
|
|
15
|
+
let result = {};
|
|
16
|
+
for(let templateName of Object.keys(templateList)){
|
|
17
|
+
if(sc.isObject(templateList[templateName])){
|
|
18
|
+
result[templateName] = this.map(templateList[templateName], FileHandler.joinPaths(path, templateName));
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
result[templateName] = FileHandler.joinPaths(path, templateList[templateName]);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports.TemplatesToPathMapper = new TemplatesToPathMapper();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reldens/cms",
|
|
3
3
|
"scope": "@reldens",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"description": "Reldens - CMS",
|
|
6
6
|
"author": "Damian A. Pastorini",
|
|
7
7
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@reldens/server-utils": "^0.16.0",
|
|
36
|
-
"@reldens/storage": "^0.
|
|
36
|
+
"@reldens/storage": "^0.44.0",
|
|
37
37
|
"@reldens/utils": "^0.47.0",
|
|
38
38
|
"dotenv": "^16.5.0",
|
|
39
39
|
"mustache": "^4.2.0"
|
|
Binary file
|
|
Binary file
|
package/templates/index.js.dist
CHANGED
|
@@ -13,7 +13,7 @@ let projectRoot = args[0] || process.cwd();
|
|
|
13
13
|
|
|
14
14
|
let manager = new Manager({
|
|
15
15
|
projectRoot,
|
|
16
|
-
|
|
16
|
+
rawRegisteredEntities,
|
|
17
17
|
entitiesConfig,
|
|
18
18
|
entitiesTranslations
|
|
19
19
|
});
|
|
@@ -27,6 +27,6 @@ manager.start().then((result) => {
|
|
|
27
27
|
Logger.info('Reldens CMS started by command.');
|
|
28
28
|
return true;
|
|
29
29
|
}).catch((error) => {
|
|
30
|
-
Logger.
|
|
30
|
+
Logger.critical('Failed to start CMS: '+error.message);
|
|
31
31
|
process.exit();
|
|
32
32
|
});
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
<h2>Maps Wizard</h2>
|
|
2
|
-
<div class="sub-content maps-wizard">
|
|
3
|
-
<div class="sub-content-box">
|
|
4
|
-
<form class="sub-content-form maps-wizard-form confirmation-required"
|
|
5
|
-
name="maps-wizard-form"
|
|
6
|
-
id="maps-wizard-form"
|
|
7
|
-
action="{{&actionPath}}"
|
|
8
|
-
method="post"
|
|
9
|
-
enctype="multipart/form-data">
|
|
10
|
-
<input type="hidden" name="mainAction" id="mainAction" value="import"/>
|
|
11
|
-
<input type="hidden" name="generatedMapsHandler" id="generatedMapsHandler" value="{{&generatedMapsHandler}}"/>
|
|
12
|
-
<input type="hidden" name="importAssociationsForChangePoints" id="importAssociationsForChangePoints" value="{{&importAssociationsForChangePoints}}"/>
|
|
13
|
-
<input type="hidden" name="importAssociationsRecursively" id="importAssociationsRecursively" value="{{&importAssociationsRecursively}}"/>
|
|
14
|
-
<input type="hidden" name="automaticallyExtrudeMaps" id="automaticallyExtrudeMaps" value="{{&automaticallyExtrudeMaps}}"/>
|
|
15
|
-
<input type="hidden" name="verifyTilesetImage" id="verifyTilesetImage" value="{{&verifyTilesetImage}}"/>
|
|
16
|
-
<input type="hidden" name="handlerParams" id="handlerParams" value="{{handlerParams}}"/>
|
|
17
|
-
<div class="main-action-container maps-selection">
|
|
18
|
-
<h3>Select the maps to be imported</h3>
|
|
19
|
-
<p>Notes:<p>
|
|
20
|
-
<p>
|
|
21
|
-
- The Game Server requires a reboot in order to make the maps available on the game.<br/>
|
|
22
|
-
- If you refresh the page you will get a new set of random maps generated.<br/>
|
|
23
|
-
- Generated sub-maps will be automatically imported, if you don't want to import those you can download all the related maps (the main map and the sub-map) and edit change them to your needs.<br/>
|
|
24
|
-
</p>
|
|
25
|
-
<ul class="wizard-options-container">
|
|
26
|
-
{{#maps}}
|
|
27
|
-
<li class="wizard-map-option-container">
|
|
28
|
-
<div class="selector-box">
|
|
29
|
-
<input class="map-wizard-option" type="checkbox" name="selectedMaps[]" id="maps-wizard-map-option-{{&key}}" value="{{&key}}"/>
|
|
30
|
-
<input type="hidden" name="map-title-{{&key}}" id="map-title-{{&key}}" value="{{&key}}"/>
|
|
31
|
-
<label class="main-option" for="maps-wizard-map-option-{{&key}}">
|
|
32
|
-
{{&key}}
|
|
33
|
-
</label>
|
|
34
|
-
<p>Downloads:</p>
|
|
35
|
-
<p>
|
|
36
|
-
- <a href="{{&mapJson}}" target="_blank">{{&mapJson}}</a><br/>
|
|
37
|
-
- <a href="{{&mapImage}}" target="_blank">{{&mapImage}}</a>
|
|
38
|
-
</p>
|
|
39
|
-
</div>
|
|
40
|
-
<canvas
|
|
41
|
-
class="mapCanvas"
|
|
42
|
-
data-toggle="modal"
|
|
43
|
-
width="{{&mapWidth}}"
|
|
44
|
-
height="{{&mapHeight}}"
|
|
45
|
-
data-tile-width="{{&tileWidth}}"
|
|
46
|
-
data-tile-height="{{&tileHeight}}"
|
|
47
|
-
data-image-key="{{&mapImage}}"
|
|
48
|
-
data-map-json="{{&mapJson}}">
|
|
49
|
-
</canvas>
|
|
50
|
-
{{#hasSubMaps}}
|
|
51
|
-
<div class="sub-maps">
|
|
52
|
-
<p class="sub-map-option-description clickable" data-expand-collapse=".sub-maps-container-{{&key}}">
|
|
53
|
-
<img class="icon-sm" src="/assets/admin/filters.png" alt="Associated maps"/>
|
|
54
|
-
<span>Associated maps generated</span>
|
|
55
|
-
</p>
|
|
56
|
-
<div class="sub-maps-container sub-maps-container-{{&key}} hidden">
|
|
57
|
-
{{#subMaps}}
|
|
58
|
-
<p>{{&key}}</p>
|
|
59
|
-
<p>Downloads:</p>
|
|
60
|
-
<p>
|
|
61
|
-
- <a href="{{&mapJson}}" target="_blank">{{&mapJson}}</a><br/>
|
|
62
|
-
- <a href="{{&mapImage}}" target="_blank">{{&mapImage}}</a>
|
|
63
|
-
</p>
|
|
64
|
-
<canvas
|
|
65
|
-
class="mapCanvas"
|
|
66
|
-
data-toggle="modal"
|
|
67
|
-
width="{{&mapWidth}}"
|
|
68
|
-
height="{{&mapHeight}}"
|
|
69
|
-
data-tile-width="{{&tileWidth}}"
|
|
70
|
-
data-tile-height="{{&tileHeight}}"
|
|
71
|
-
data-image-key="{{&mapImage}}"
|
|
72
|
-
data-map-json="{{&mapJson}}">
|
|
73
|
-
</canvas>
|
|
74
|
-
{{/subMaps}}
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
{{/hasSubMaps}}
|
|
78
|
-
</li>
|
|
79
|
-
{{/maps}}
|
|
80
|
-
</ul>
|
|
81
|
-
</div>
|
|
82
|
-
<input type="submit" class="button button-primary button-maps-wizard" value="Import Selected Maps"/>
|
|
83
|
-
</form>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|