1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | var et = require('elementtree');
|
21 | var xml = require('../util/xml-helpers');
|
22 | var CordovaError = require('../CordovaError/CordovaError');
|
23 | var fs = require('fs');
|
24 | var events = require('../events');
|
25 |
|
26 |
|
27 | function ConfigParser (path) {
|
28 | this.path = path;
|
29 | try {
|
30 | this.doc = xml.parseElementtreeSync(path);
|
31 | this.cdvNamespacePrefix = getCordovaNamespacePrefix(this.doc);
|
32 | et.register_namespace(this.cdvNamespacePrefix, 'http://cordova.apache.org/ns/1.0');
|
33 | } catch (e) {
|
34 | events.emit('error', 'Parsing ' + path + ' failed');
|
35 | throw e;
|
36 | }
|
37 | var r = this.doc.getroot();
|
38 | if (r.tag !== 'widget') {
|
39 | throw new CordovaError(path + ' has incorrect root node name (expected "widget", was "' + r.tag + '")');
|
40 | }
|
41 | }
|
42 |
|
43 | function getNodeTextSafe (el) {
|
44 | return el && el.text && el.text.trim();
|
45 | }
|
46 |
|
47 | function findOrCreate (doc, name) {
|
48 | var ret = doc.find(name);
|
49 | if (!ret) {
|
50 | ret = new et.Element(name);
|
51 | doc.getroot().append(ret);
|
52 | }
|
53 | return ret;
|
54 | }
|
55 |
|
56 | function getCordovaNamespacePrefix (doc) {
|
57 | var rootAtribs = Object.getOwnPropertyNames(doc.getroot().attrib);
|
58 | var prefix = 'cdv';
|
59 | for (var j = 0; j < rootAtribs.length; j++) {
|
60 | if (rootAtribs[j].indexOf('xmlns:') === 0 &&
|
61 | doc.getroot().attrib[rootAtribs[j]] === 'http://cordova.apache.org/ns/1.0') {
|
62 | var strings = rootAtribs[j].split(':');
|
63 | prefix = strings[1];
|
64 | break;
|
65 | }
|
66 | }
|
67 | return prefix;
|
68 | }
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 | function findElementAttributeValue (attributeName, elems) {
|
77 |
|
78 | elems = Array.isArray(elems) ? elems : [ elems ];
|
79 |
|
80 | var value = elems.filter(function (elem) {
|
81 | return elem.attrib.name.toLowerCase() === attributeName.toLowerCase();
|
82 | }).map(function (filteredElems) {
|
83 | return filteredElems.attrib.value;
|
84 | }).pop();
|
85 |
|
86 | return value || '';
|
87 | }
|
88 |
|
89 | ConfigParser.prototype = {
|
90 | getAttribute: function (attr) {
|
91 | return this.doc.getroot().attrib[attr];
|
92 | },
|
93 |
|
94 | packageName: function (id) {
|
95 | return this.getAttribute('id');
|
96 | },
|
97 | setPackageName: function (id) {
|
98 | this.doc.getroot().attrib['id'] = id;
|
99 | },
|
100 | android_packageName: function () {
|
101 | return this.getAttribute('android-packageName');
|
102 | },
|
103 | android_activityName: function () {
|
104 | return this.getAttribute('android-activityName');
|
105 | },
|
106 | ios_CFBundleIdentifier: function () {
|
107 | return this.getAttribute('ios-CFBundleIdentifier');
|
108 | },
|
109 | name: function () {
|
110 | return getNodeTextSafe(this.doc.find('name'));
|
111 | },
|
112 | setName: function (name) {
|
113 | var el = findOrCreate(this.doc, 'name');
|
114 | el.text = name;
|
115 | },
|
116 | shortName: function () {
|
117 | return this.doc.find('name').attrib['short'] || this.name();
|
118 | },
|
119 | setShortName: function (shortname) {
|
120 | var el = findOrCreate(this.doc, 'name');
|
121 | if (!el.text) {
|
122 | el.text = shortname;
|
123 | }
|
124 | el.attrib['short'] = shortname;
|
125 | },
|
126 | description: function () {
|
127 | return getNodeTextSafe(this.doc.find('description'));
|
128 | },
|
129 | setDescription: function (text) {
|
130 | var el = findOrCreate(this.doc, 'description');
|
131 | el.text = text;
|
132 | },
|
133 | version: function () {
|
134 | return this.getAttribute('version');
|
135 | },
|
136 | windows_packageVersion: function () {
|
137 | return this.getAttribute('windows-packageVersion');
|
138 | },
|
139 | android_versionCode: function () {
|
140 | return this.getAttribute('android-versionCode');
|
141 | },
|
142 | ios_CFBundleVersion: function () {
|
143 | return this.getAttribute('ios-CFBundleVersion');
|
144 | },
|
145 | setVersion: function (value) {
|
146 | this.doc.getroot().attrib['version'] = value;
|
147 | },
|
148 | author: function () {
|
149 | return getNodeTextSafe(this.doc.find('author'));
|
150 | },
|
151 | getGlobalPreference: function (name) {
|
152 | return findElementAttributeValue(name, this.doc.findall('preference'));
|
153 | },
|
154 | setGlobalPreference: function (name, value) {
|
155 | var pref = this.doc.find('preference[@name="' + name + '"]');
|
156 | if (!pref) {
|
157 | pref = new et.Element('preference');
|
158 | pref.attrib.name = name;
|
159 | this.doc.getroot().append(pref);
|
160 | }
|
161 | pref.attrib.value = value;
|
162 | },
|
163 | getPlatformPreference: function (name, platform) {
|
164 | return findElementAttributeValue(name, this.doc.findall('platform[@name=\'' + platform + '\']/preference'));
|
165 | },
|
166 | getPreference: function (name, platform) {
|
167 |
|
168 | var platformPreference = '';
|
169 |
|
170 | if (platform) {
|
171 | platformPreference = this.getPlatformPreference(name, platform);
|
172 | }
|
173 |
|
174 | return platformPreference || this.getGlobalPreference(name);
|
175 |
|
176 | },
|
177 | |
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | getStaticResources: function (platform, resourceName) {
|
185 | var ret = [];
|
186 | var staticResources = [];
|
187 | if (platform) {
|
188 | this.doc.findall('platform[@name=\'' + platform + '\']/' + resourceName).forEach(function (elt) {
|
189 | elt.platform = platform;
|
190 | staticResources.push(elt);
|
191 | });
|
192 | }
|
193 |
|
194 | staticResources = staticResources.concat(this.doc.findall(resourceName));
|
195 |
|
196 | var that = this;
|
197 | staticResources.forEach(function (elt) {
|
198 | var res = {};
|
199 | res.src = elt.attrib.src;
|
200 | res.target = elt.attrib.target || undefined;
|
201 | res.density = elt.attrib['density'] || elt.attrib[that.cdvNamespacePrefix + ':density'] || elt.attrib['gap:density'];
|
202 | res.platform = elt.platform || null;
|
203 | res.width = +elt.attrib.width || undefined;
|
204 | res.height = +elt.attrib.height || undefined;
|
205 |
|
206 |
|
207 | if (!res.width && !res.height && !res.density) {
|
208 | ret.defaultResource = res;
|
209 | }
|
210 | ret.push(res);
|
211 | });
|
212 |
|
213 | |
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | ret.getBySize = function (width, height) {
|
220 | return ret.filter(function (res) {
|
221 | if (!res.width && !res.height) {
|
222 | return false;
|
223 | }
|
224 | return ((!res.width || (width === res.width)) &&
|
225 | (!res.height || (height === res.height)));
|
226 | })[0] || null;
|
227 | };
|
228 |
|
229 | |
230 |
|
231 |
|
232 |
|
233 |
|
234 | ret.getByDensity = function (density) {
|
235 | return ret.filter(function (res) {
|
236 | return res.density === density;
|
237 | })[0] || null;
|
238 | };
|
239 |
|
240 |
|
241 | ret.getDefault = function () {
|
242 | return ret.defaultResource;
|
243 | };
|
244 |
|
245 | return ret;
|
246 | },
|
247 |
|
248 | |
249 |
|
250 |
|
251 |
|
252 |
|
253 | getIcons: function (platform) {
|
254 | return this.getStaticResources(platform, 'icon');
|
255 | },
|
256 |
|
257 | |
258 |
|
259 |
|
260 |
|
261 |
|
262 | getSplashScreens: function (platform) {
|
263 | return this.getStaticResources(platform, 'splash');
|
264 | },
|
265 |
|
266 | |
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 | getFileResources: function (platform, includeGlobal) {
|
274 | var fileResources = [];
|
275 |
|
276 | if (platform) {
|
277 | fileResources = this.doc.findall('platform[@name=\'' + platform + '\']/resource-file').map(function (tag) {
|
278 | return {
|
279 | platform: platform,
|
280 | src: tag.attrib.src,
|
281 | target: tag.attrib.target,
|
282 | versions: tag.attrib.versions,
|
283 | deviceTarget: tag.attrib['device-target'],
|
284 | arch: tag.attrib.arch
|
285 | };
|
286 | });
|
287 | }
|
288 |
|
289 | if (includeGlobal) {
|
290 | this.doc.findall('resource-file').forEach(function (tag) {
|
291 | fileResources.push({
|
292 | platform: platform || null,
|
293 | src: tag.attrib.src,
|
294 | target: tag.attrib.target,
|
295 | versions: tag.attrib.versions,
|
296 | deviceTarget: tag.attrib['device-target'],
|
297 | arch: tag.attrib.arch
|
298 | });
|
299 | });
|
300 | }
|
301 |
|
302 | return fileResources;
|
303 | },
|
304 |
|
305 | |
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 | getHookScripts: function (hook, platforms) {
|
312 | var self = this;
|
313 | var scriptElements = self.doc.findall('./hook');
|
314 |
|
315 | if (platforms) {
|
316 | platforms.forEach(function (platform) {
|
317 | scriptElements = scriptElements.concat(self.doc.findall('./platform[@name="' + platform + '"]/hook'));
|
318 | });
|
319 | }
|
320 |
|
321 | function filterScriptByHookType (el) {
|
322 | return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
|
323 | }
|
324 |
|
325 | return scriptElements.filter(filterScriptByHookType);
|
326 | },
|
327 | |
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 | getPluginIdList: function () {
|
335 | var plugins = this.doc.findall('plugin');
|
336 | var result = plugins.map(function (plugin) {
|
337 | return plugin.attrib.name;
|
338 | });
|
339 | var features = this.doc.findall('feature');
|
340 | features.forEach(function (element) {
|
341 | var idTag = element.find('./param[@name="id"]');
|
342 | if (idTag) {
|
343 | result.push(idTag.attrib.value);
|
344 | }
|
345 | });
|
346 | return result;
|
347 | },
|
348 | getPlugins: function () {
|
349 | return this.getPluginIdList().map(function (pluginId) {
|
350 | return this.getPlugin(pluginId);
|
351 | }, this);
|
352 | },
|
353 | |
354 |
|
355 |
|
356 |
|
357 |
|
358 |
|
359 |
|
360 | addPlugin: function (attributes, variables) {
|
361 | if (!attributes && !attributes.name) return;
|
362 | var el = new et.Element('plugin');
|
363 | el.attrib.name = attributes.name;
|
364 | if (attributes.spec) {
|
365 | el.attrib.spec = attributes.spec;
|
366 | }
|
367 |
|
368 |
|
369 | if (variables && typeof variables === 'object' && !Array.isArray(variables)) {
|
370 | variables = Object.keys(variables)
|
371 | .map(function (variableName) {
|
372 | return {name: variableName, value: variables[variableName]};
|
373 | });
|
374 | }
|
375 |
|
376 | if (variables) {
|
377 | variables.forEach(function (variable) {
|
378 | el.append(new et.Element('variable', { name: variable.name, value: variable.value }));
|
379 | });
|
380 | }
|
381 | this.doc.getroot().append(el);
|
382 | },
|
383 | |
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 | getPlugin: function (id) {
|
394 | if (!id) {
|
395 | return undefined;
|
396 | }
|
397 | var pluginElement = this.doc.find('./plugin/[@name="' + id + '"]');
|
398 | if (pluginElement === null) {
|
399 | var legacyFeature = this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..');
|
400 | if (legacyFeature) {
|
401 | events.emit('log', 'Found deprecated feature entry for ' + id + ' in config.xml.');
|
402 | return featureToPlugin(legacyFeature);
|
403 | }
|
404 | return undefined;
|
405 | }
|
406 | var plugin = {};
|
407 |
|
408 | plugin.name = pluginElement.attrib.name;
|
409 | plugin.spec = pluginElement.attrib.spec || pluginElement.attrib.src || pluginElement.attrib.version;
|
410 | plugin.variables = {};
|
411 | var variableElements = pluginElement.findall('variable');
|
412 | variableElements.forEach(function (varElement) {
|
413 | var name = varElement.attrib.name;
|
414 | var value = varElement.attrib.value;
|
415 | if (name) {
|
416 | plugin.variables[name] = value;
|
417 | }
|
418 | });
|
419 | return plugin;
|
420 | },
|
421 | |
422 |
|
423 |
|
424 |
|
425 |
|
426 |
|
427 |
|
428 |
|
429 |
|
430 | removePlugin: function (id) {
|
431 | if (id) {
|
432 | var plugins = this.doc.findall('./plugin/[@name="' + id + '"]')
|
433 | .concat(this.doc.findall('./feature/param[@name="id"][@value="' + id + '"]/..'));
|
434 | var children = this.doc.getroot().getchildren();
|
435 | plugins.forEach(function (plugin) {
|
436 | var idx = children.indexOf(plugin);
|
437 | if (idx > -1) {
|
438 | children.splice(idx, 1);
|
439 | }
|
440 | });
|
441 | }
|
442 | },
|
443 |
|
444 |
|
445 | addElement: function (name, attributes) {
|
446 | var el = et.Element(name);
|
447 | for (var a in attributes) {
|
448 | el.attrib[a] = attributes[a];
|
449 | }
|
450 | this.doc.getroot().append(el);
|
451 | },
|
452 |
|
453 | |
454 |
|
455 |
|
456 |
|
457 |
|
458 | addEngine: function (name, spec) {
|
459 | if (!name) return;
|
460 | var el = et.Element('engine');
|
461 | el.attrib.name = name;
|
462 | if (spec) {
|
463 | el.attrib.spec = spec;
|
464 | }
|
465 | this.doc.getroot().append(el);
|
466 | },
|
467 | |
468 |
|
469 |
|
470 |
|
471 | removeEngine: function (name) {
|
472 | var engines = this.doc.findall('./engine/[@name="' + name + '"]');
|
473 | for (var i = 0; i < engines.length; i++) {
|
474 | var children = this.doc.getroot().getchildren();
|
475 | var idx = children.indexOf(engines[i]);
|
476 | if (idx > -1) {
|
477 | children.splice(idx, 1);
|
478 | }
|
479 | }
|
480 | },
|
481 | getEngines: function () {
|
482 | var engines = this.doc.findall('./engine');
|
483 | return engines.map(function (engine) {
|
484 | var spec = engine.attrib.spec || engine.attrib.version;
|
485 | return {
|
486 | 'name': engine.attrib.name,
|
487 | 'spec': spec || null
|
488 | };
|
489 | });
|
490 | },
|
491 |
|
492 | getAccesses: function () {
|
493 | var accesses = this.doc.findall('./access');
|
494 | return accesses.map(function (access) {
|
495 | var minimum_tls_version = access.attrib['minimum-tls-version'];
|
496 | var requires_forward_secrecy = access.attrib['requires-forward-secrecy'];
|
497 | var requires_certificate_transparency = access.attrib['requires-certificate-transparency'];
|
498 | var allows_arbitrary_loads_in_web_content = access.attrib['allows-arbitrary-loads-in-web-content'];
|
499 | var allows_arbitrary_loads_in_media = access.attrib['allows-arbitrary-loads-in-media'];
|
500 | var allows_arbitrary_loads_for_media = access.attrib['allows-arbitrary-loads-for-media'];
|
501 | var allows_local_networking = access.attrib['allows-local-networking'];
|
502 |
|
503 | return {
|
504 | 'origin': access.attrib.origin,
|
505 | 'minimum_tls_version': minimum_tls_version,
|
506 | 'requires_forward_secrecy': requires_forward_secrecy,
|
507 | 'requires_certificate_transparency': requires_certificate_transparency,
|
508 | 'allows_arbitrary_loads_in_web_content': allows_arbitrary_loads_in_web_content,
|
509 | 'allows_arbitrary_loads_in_media': allows_arbitrary_loads_in_media,
|
510 | 'allows_arbitrary_loads_for_media': allows_arbitrary_loads_for_media,
|
511 | 'allows_local_networking': allows_local_networking
|
512 | };
|
513 | });
|
514 | },
|
515 |
|
516 | getAllowNavigations: function () {
|
517 | var allow_navigations = this.doc.findall('./allow-navigation');
|
518 | return allow_navigations.map(function (allow_navigation) {
|
519 | var minimum_tls_version = allow_navigation.attrib['minimum-tls-version'];
|
520 | var requires_forward_secrecy = allow_navigation.attrib['requires-forward-secrecy'];
|
521 | var requires_certificate_transparency = allow_navigation.attrib['requires-certificate-transparency'];
|
522 |
|
523 | return {
|
524 | 'href': allow_navigation.attrib.href,
|
525 | 'minimum_tls_version': minimum_tls_version,
|
526 | 'requires_forward_secrecy': requires_forward_secrecy,
|
527 | 'requires_certificate_transparency': requires_certificate_transparency
|
528 | };
|
529 | });
|
530 | },
|
531 |
|
532 | getAllowIntents: function () {
|
533 | var allow_intents = this.doc.findall('./allow-intent');
|
534 | return allow_intents.map(function (allow_intent) {
|
535 | return {
|
536 | 'href': allow_intent.attrib.href
|
537 | };
|
538 | });
|
539 | },
|
540 |
|
541 | getEditConfigs: function (platform) {
|
542 | var platform_tag = this.doc.find('./platform[@name="' + platform + '"]');
|
543 | var platform_edit_configs = platform_tag ? platform_tag.findall('edit-config') : [];
|
544 |
|
545 | var edit_configs = this.doc.findall('edit-config').concat(platform_edit_configs);
|
546 |
|
547 | return edit_configs.map(function (tag) {
|
548 | var editConfig =
|
549 | {
|
550 | file: tag.attrib['file'],
|
551 | target: tag.attrib['target'],
|
552 | mode: tag.attrib['mode'],
|
553 | id: 'config.xml',
|
554 | xmls: tag.getchildren()
|
555 | };
|
556 | return editConfig;
|
557 | });
|
558 | },
|
559 |
|
560 |
|
561 | getConfigFiles: function (platform) {
|
562 | var platform_tag = this.doc.find('./platform[@name="' + platform + '"]');
|
563 | var platform_config_files = platform_tag ? platform_tag.findall('config-file') : [];
|
564 |
|
565 | var config_files = this.doc.findall('config-file').concat(platform_config_files);
|
566 |
|
567 | return config_files.map(function (tag) {
|
568 | var configFile =
|
569 | {
|
570 | target: tag.attrib['target'],
|
571 | parent: tag.attrib['parent'],
|
572 | after: tag.attrib['after'],
|
573 | xmls: tag.getchildren(),
|
574 |
|
575 | versions: tag.attrib['versions'],
|
576 | deviceTarget: tag.attrib['device-target']
|
577 | };
|
578 | return configFile;
|
579 | });
|
580 | },
|
581 |
|
582 | write: function () {
|
583 | fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8');
|
584 | }
|
585 | };
|
586 |
|
587 | function featureToPlugin (featureElement) {
|
588 | var plugin = {};
|
589 | plugin.variables = [];
|
590 | var pluginVersion,
|
591 | pluginSrc;
|
592 |
|
593 | var nodes = featureElement.findall('param');
|
594 | nodes.forEach(function (element) {
|
595 | var n = element.attrib.name;
|
596 | var v = element.attrib.value;
|
597 | if (n === 'id') {
|
598 | plugin.name = v;
|
599 | } else if (n === 'version') {
|
600 | pluginVersion = v;
|
601 | } else if (n === 'url' || n === 'installPath') {
|
602 | pluginSrc = v;
|
603 | } else {
|
604 | plugin.variables[n] = v;
|
605 | }
|
606 | });
|
607 |
|
608 | var spec = pluginSrc || pluginVersion;
|
609 | if (spec) {
|
610 | plugin.spec = spec;
|
611 | }
|
612 |
|
613 | return plugin;
|
614 | }
|
615 | module.exports = ConfigParser;
|