less 2.1.2 → 2.4.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.
Files changed (148) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +1 -0
  2. package/.idea/jsLinters/jscs.xml +4 -0
  3. package/.idea/jsLinters/jshint.xml +58 -4
  4. package/.idea/workspace.xml +499 -760
  5. package/.jscsrc +73 -0
  6. package/.jshintrc +0 -2
  7. package/.travis.yml +1 -0
  8. package/CHANGELOG.md +59 -0
  9. package/Gruntfile.js +37 -3
  10. package/appveyor.yml +5 -0
  11. package/bin/lessc +383 -316
  12. package/bower.json +1 -1
  13. package/dist/less.js +1638 -1244
  14. package/dist/less.min.js +7 -7
  15. package/lib/less/contexts.js +3 -3
  16. package/lib/less/data/unit-conversions.js +3 -3
  17. package/lib/less/environment/abstract-file-manager.js +8 -8
  18. package/lib/less/environment/environment.js +2 -2
  19. package/lib/less/functions/color-blending.js +3 -3
  20. package/lib/less/functions/color.js +20 -12
  21. package/lib/less/functions/data-uri.js +26 -18
  22. package/lib/less/functions/math.js +1 -1
  23. package/lib/less/functions/number.js +2 -2
  24. package/lib/less/functions/string.js +3 -1
  25. package/lib/less/functions/svg.js +12 -15
  26. package/lib/less/functions/types.js +33 -10
  27. package/lib/less/import-manager.js +14 -9
  28. package/lib/less/index.js +2 -1
  29. package/lib/less/less-error.js +1 -1
  30. package/lib/less/logger.js +2 -2
  31. package/lib/less/parse-tree.js +48 -42
  32. package/lib/less/parse.js +68 -0
  33. package/lib/less/parser/parser-input.js +6 -5
  34. package/lib/less/parser/parser.js +76 -29
  35. package/lib/less/plugin-manager.js +30 -3
  36. package/lib/less/render.js +11 -37
  37. package/lib/less/source-map-builder.js +4 -0
  38. package/lib/less/source-map-output.js +6 -6
  39. package/lib/less/transform-tree.js +4 -4
  40. package/lib/less/tree/anonymous.js +1 -1
  41. package/lib/less/tree/assignment.js +0 -1
  42. package/lib/less/tree/call.js +1 -1
  43. package/lib/less/tree/color.js +6 -6
  44. package/lib/less/tree/condition.js +9 -5
  45. package/lib/less/tree/debug-info.js +6 -2
  46. package/lib/less/tree/dimension.js +5 -5
  47. package/lib/less/tree/directive.js +25 -6
  48. package/lib/less/tree/element.js +1 -1
  49. package/lib/less/tree/expression.js +2 -2
  50. package/lib/less/tree/extend.js +3 -3
  51. package/lib/less/tree/import.js +2 -2
  52. package/lib/less/tree/index.js +1 -0
  53. package/lib/less/tree/javascript.js +2 -2
  54. package/lib/less/tree/media.js +20 -19
  55. package/lib/less/tree/mixin-call.js +5 -6
  56. package/lib/less/tree/mixin-definition.js +33 -11
  57. package/lib/less/tree/quoted.js +3 -3
  58. package/lib/less/tree/rule.js +3 -4
  59. package/lib/less/tree/ruleset.js +290 -177
  60. package/lib/less/tree/selector.js +3 -2
  61. package/lib/less/tree/unit.js +2 -0
  62. package/lib/less/tree/url.js +5 -2
  63. package/lib/less/tree/value.js +2 -2
  64. package/lib/less/tree/variable.js +1 -1
  65. package/lib/less/visitors/extend-visitor.js +72 -36
  66. package/lib/less/visitors/import-sequencer.js +20 -14
  67. package/lib/less/visitors/import-visitor.js +15 -12
  68. package/lib/less/visitors/to-css-visitor.js +41 -9
  69. package/lib/less-browser/add-default-options.js +5 -1
  70. package/lib/less-browser/bootstrap.js +10 -8
  71. package/lib/less-browser/cache.js +3 -3
  72. package/lib/less-browser/file-manager.js +96 -96
  73. package/lib/less-browser/index.js +206 -205
  74. package/lib/less-browser/log-listener.js +2 -2
  75. package/lib/less-browser/utils.js +6 -6
  76. package/lib/less-node/file-manager.js +58 -30
  77. package/lib/less-node/image-size.js +34 -0
  78. package/lib/less-node/index.js +6 -3
  79. package/lib/less-node/lessc-helper.js +3 -2
  80. package/lib/less-node/plugin-loader.js +3 -3
  81. package/lib/less-node/url-file-manager.js +6 -3
  82. package/lib/less-rhino/index.js +15 -13
  83. package/lib/source-map/source-map-0.1.31.js +1 -1
  84. package/package.json +10 -7
  85. package/test/browser/common.js +13 -11
  86. package/test/browser/css/relative-urls/urls.css +1 -0
  87. package/test/browser/css/urls.css +6 -3
  88. package/test/browser/jasmine-jsreporter.js +2 -3
  89. package/test/browser/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  90. package/test/browser/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  91. package/test/browser/less/relative-urls/urls.less +1 -0
  92. package/test/browser/less/urls.less +1 -0
  93. package/test/browser/runner-browser-options.js +23 -23
  94. package/test/browser/runner-errors-options.js +0 -1
  95. package/test/browser/runner-errors-spec.js +1 -2
  96. package/test/browser/runner-global-vars-spec.js +1 -1
  97. package/test/browser/runner-legacy-options.js +0 -1
  98. package/test/browser/runner-main-options.js +5 -5
  99. package/test/browser/runner-main-spec.js +1 -1
  100. package/test/browser/runner-no-js-errors-spec.js +0 -1
  101. package/test/browser/runner-production-options.js +0 -1
  102. package/test/browser/runner-relative-urls-options.js +0 -1
  103. package/test/browser/runner-rootpath-options.js +0 -1
  104. package/test/browser/runner-rootpath-relative-options.js +0 -1
  105. package/test/browser/runner-strict-units-options.js +0 -1
  106. package/test/copy-bom.js +72 -0
  107. package/test/css/comments.css +1 -0
  108. package/test/css/css-3.css +2 -0
  109. package/test/css/functions.css +10 -0
  110. package/test/css/import-interpolation.css +7 -0
  111. package/test/css/import-reference.css +25 -0
  112. package/test/css/include-path/include-path.css +6 -0
  113. package/test/css/mixins-important.css +12 -0
  114. package/test/css/selectors.css +15 -1
  115. package/test/css/static-urls/urls.css +1 -0
  116. package/test/css/url-args/urls.css +3 -3
  117. package/test/css/urls.css +13 -4
  118. package/test/css/variables.css +7 -1
  119. package/test/data/image.svg +3 -0
  120. package/test/index.js +15 -10
  121. package/test/less/comments.less +1 -0
  122. package/test/less/css-3.less +3 -0
  123. package/test/less/css-guards.less +2 -1
  124. package/test/less/errors/detached-ruleset-2.txt +1 -1
  125. package/test/less/errors/import-missing.txt +1 -1
  126. package/test/less/errors/parens-error-1.txt +1 -1
  127. package/test/less/errors/parens-error-2.txt +1 -1
  128. package/test/less/errors/parens-error-3.txt +1 -1
  129. package/test/less/errors/single-character.less +1 -0
  130. package/test/less/errors/single-character.txt +2 -0
  131. package/test/less/functions.less +18 -0
  132. package/test/less/import/import-interpolation.less +1 -0
  133. package/test/less/import/import-reference.less +38 -0
  134. package/test/less/import/imports/logo.less +1 -0
  135. package/test/less/import-reference.less +3 -1
  136. package/test/less/import.less +2 -0
  137. package/test/less/include-path/include-path.less +6 -0
  138. package/test/less/mixins-important.less +28 -0
  139. package/test/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  140. package/test/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  141. package/test/less/selectors.less +29 -4
  142. package/test/less/sourcemaps-empty/empty.less +0 -0
  143. package/test/less/sourcemaps-empty/var-defs.less +1 -0
  144. package/test/less/urls.less +10 -1
  145. package/test/less/variables.less +17 -3
  146. package/test/less-test.js +133 -52
  147. package/test/modify-vars.js +14 -12
  148. package/test/browser/less.js +0 -9473
package/test/less-test.js CHANGED
@@ -2,28 +2,34 @@
2
2
 
3
3
  module.exports = function() {
4
4
  var path = require('path'),
5
- fs = require('fs');
5
+ fs = require('fs'),
6
+ copyBom = require('./copy-bom')(),
7
+ doBomTest = false;
6
8
 
7
9
  var less = require('../lib/less-node');
8
10
  var stylize = require('../lib/less-node/lessc-helper').stylize;
9
11
 
10
12
  var globals = Object.keys(global);
11
13
 
12
- var oneTestOnly = process.argv[2];
14
+ var oneTestOnly = process.argv[2],
15
+ isFinished = false;
13
16
 
14
17
  var isVerbose = process.env.npm_config_loglevel === 'verbose';
15
18
 
19
+ var normalFolder = 'test/less';
20
+ var bomFolder = 'test/less-bom';
21
+
16
22
  less.logger.addListener({
17
23
  info: function(msg) {
18
24
  if (isVerbose) {
19
- console.log(msg);
25
+ process.stdout.write(msg + "\n");
20
26
  }
21
27
  },
22
28
  warn: function(msg) {
23
- console.warn(msg);
29
+ process.stdout.write(msg + "\n");
24
30
  },
25
31
  error: function(msg) {
26
- console.error(msg);
32
+ process.stdout.write(msg + "\n");
27
33
  }
28
34
  });
29
35
 
@@ -31,27 +37,29 @@ module.exports = function() {
31
37
  queueRunning = false;
32
38
  function queue(func) {
33
39
  if (queueRunning) {
40
+ //console.log("adding to queue");
34
41
  queueList.push(func);
35
42
  } else {
43
+ //console.log("first in queue - starting");
36
44
  queueRunning = true;
37
45
  func();
38
46
  }
39
47
  }
40
48
  function release() {
41
49
  if (queueList.length) {
50
+ //console.log("running next in queue");
42
51
  var func = queueList.shift();
43
- func();
52
+ setTimeout(func, 0);
44
53
  } else {
54
+ //console.log("stopping queue");
45
55
  queueRunning = false;
46
56
  }
47
- };
48
-
57
+ }
49
58
 
50
59
  var totalTests = 0,
51
60
  failedTests = 0,
52
61
  passedTests = 0;
53
62
 
54
-
55
63
  less.functions.functionRegistry.addMultiple({
56
64
  add: function (a, b) {
57
65
  return new(less.tree.Dimension)(a.value + b.value);
@@ -64,9 +72,9 @@ module.exports = function() {
64
72
  }
65
73
  });
66
74
 
67
- function testSourcemap(name, err, compiledLess, doReplacements, sourcemap) {
75
+ function testSourcemap(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
68
76
  fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) {
69
- process.stdout.write("- " + name + ": ");
77
+ process.stdout.write("- " + path.join(baseFolder, name) + ": ");
70
78
  if (sourcemap === expectedSourcemap) {
71
79
  ok('OK');
72
80
  } else if (err) {
@@ -81,10 +89,27 @@ module.exports = function() {
81
89
  });
82
90
  }
83
91
 
84
- function testErrors(name, err, compiledLess, doReplacements) {
85
- fs.readFile(path.join('test/less/', name) + '.txt', 'utf8', function (e, expectedErr) {
86
- process.stdout.write("- " + name + ": ");
87
- expectedErr = doReplacements(expectedErr, 'test/less/errors/');
92
+ function testEmptySourcemap(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
93
+ process.stdout.write("- " + path.join(baseFolder, name) + ": ");
94
+ if (err) {
95
+ fail("ERROR: " + (err && err.message));
96
+ } else {
97
+ var expectedSourcemap = undefined;
98
+ if ( compiledLess !== "" ) {
99
+ difference("\nCompiledLess must be empty", "", compiledLess);
100
+
101
+ } else if (sourcemap !== expectedSourcemap) {
102
+ fail("Sourcemap must be undefined");
103
+ } else {
104
+ ok('OK');
105
+ }
106
+ }
107
+ }
108
+
109
+ function testErrors(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
110
+ fs.readFile(path.join(baseFolder, name) + '.txt', 'utf8', function (e, expectedErr) {
111
+ process.stdout.write("- " + path.join(baseFolder, name) + ": ");
112
+ expectedErr = doReplacements(expectedErr, baseFolder);
88
113
  if (!err) {
89
114
  if (compiledLess) {
90
115
  fail("No Error", 'red');
@@ -105,8 +130,8 @@ module.exports = function() {
105
130
  function globalReplacements(input, directory) {
106
131
  var p = path.join(process.cwd(), directory),
107
132
  pathimport = path.join(process.cwd(), directory + "import/"),
108
- pathesc = p.replace(/[.:/\\]/g, function(a) { return '\\' + (a=='\\' ? '\/' : a); }),
109
- pathimportesc = pathimport.replace(/[.:/\\]/g, function(a) { return '\\' + (a=='\\' ? '\/' : a); });
133
+ pathesc = p.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); }),
134
+ pathimportesc = pathimport.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); });
110
135
 
111
136
  return input.replace(/\{path\}/g, p)
112
137
  .replace(/\{pathesc\}/g, pathesc)
@@ -122,34 +147,55 @@ module.exports = function() {
122
147
  }
123
148
 
124
149
  function testSyncronous(options, filenameNoExtension) {
125
- queue(function() {
126
- var isSync = true;
150
+ if (oneTestOnly && ("Test Sync " + filenameNoExtension) !== oneTestOnly) {
151
+ return;
152
+ }
127
153
  totalTests++;
128
- toCSS(options, path.join('test/less/', filenameNoExtension + ".less"), function (err, result) {
129
- process.stdout.write("- Test Sync " + filenameNoExtension + ": ");
154
+ queue(function() {
155
+ var isSync = true;
156
+ toCSS(options, path.join(normalFolder, filenameNoExtension + ".less"), function (err, result) {
157
+ process.stdout.write("- Test Sync " + filenameNoExtension + ": ");
130
158
 
131
- if (isSync) {
132
- ok("OK");
133
- } else {
134
- fail("Not Sync");
135
- }
136
- });
137
- isSync = false;
159
+ if (isSync) {
160
+ ok("OK");
161
+ } else {
162
+ fail("Not Sync");
163
+ }
164
+ release();
165
+ });
166
+ isSync = false;
138
167
  });
139
168
  }
140
169
 
170
+ function prepBomTest() {
171
+ copyBom.copyFolderWithBom(normalFolder, bomFolder);
172
+ doBomTest = true;
173
+ }
174
+
141
175
  function runTestSet(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
176
+ var options2 = options ? JSON.parse(JSON.stringify(options)) : {};
177
+ runTestSetInternal(normalFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
178
+ if (doBomTest) {
179
+ runTestSetInternal(bomFolder, options2, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
180
+ }
181
+ }
182
+
183
+ function runTestSetNormalOnly(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
184
+ runTestSetInternal(normalFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
185
+ }
186
+
187
+ function runTestSetInternal(baseFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
142
188
  foldername = foldername || "";
143
189
 
144
- if(!doReplacements) {
190
+ if (!doReplacements) {
145
191
  doReplacements = globalReplacements;
146
192
  }
147
193
 
148
194
  function getBasename(file) {
149
- return foldername + path.basename(file, '.less');
195
+ return foldername + path.basename(file, '.less');
150
196
  }
151
197
 
152
- fs.readdirSync(path.join('test/less/', foldername)).forEach(function (file) {
198
+ fs.readdirSync(path.join(baseFolder, foldername)).forEach(function (file) {
153
199
  if (! /\.less/.test(file)) { return; }
154
200
 
155
201
  var name = getBasename(file);
@@ -160,23 +206,34 @@ module.exports = function() {
160
206
 
161
207
  totalTests++;
162
208
 
163
- if (options.sourceMap) {
209
+ if (options.sourceMap && !options.sourceMap.sourceMapFileInline) {
164
210
  options.sourceMapOutputFilename = name + ".css";
165
- options.sourceMapBasepath = path.join(process.cwd(), "test/less");
211
+ options.sourceMapBasepath = path.join(process.cwd(), baseFolder);
166
212
  options.sourceMapRootpath = "testweb/";
167
213
  // TODO separate options?
168
214
  options.sourceMap = options;
169
215
  }
170
216
 
171
217
  options.getVars = function(file) {
172
- return JSON.parse(fs.readFileSync(getFilename(getBasename(file), 'vars'), 'utf8'));
218
+ return JSON.parse(fs.readFileSync(getFilename(getBasename(file), 'vars', baseFolder), 'utf8'));
173
219
  };
174
220
 
221
+ var doubleCallCheck = false;
175
222
  queue(function() {
176
- toCSS(options, path.join('test/less/', foldername + file), function (err, result) {
223
+ toCSS(options, path.join(baseFolder, foldername + file), function (err, result) {
224
+ if (doubleCallCheck) {
225
+ totalTests++;
226
+ fail("less is calling back twice");
227
+ process.stdout.write(doubleCallCheck + "\n");
228
+ process.stdout.write((new Error()).stack + "\n");
229
+ return;
230
+ }
231
+ doubleCallCheck = (new Error()).stack;
177
232
 
178
233
  if (verifyFunction) {
179
- return verifyFunction(name, err, result && result.css, doReplacements, result && result.map);
234
+ var verificationResult = verifyFunction(name, err, result && result.css, doReplacements, result && result.map, baseFolder);
235
+ release();
236
+ return verificationResult;
180
237
  }
181
238
  if (err) {
182
239
  fail("ERROR: " + (err && err.message));
@@ -188,11 +245,11 @@ module.exports = function() {
188
245
  return;
189
246
  }
190
247
  var css_name = name;
191
- if(nameModifier) { css_name = nameModifier(name); }
248
+ if (nameModifier) { css_name = nameModifier(name); }
192
249
  fs.readFile(path.join('test/css', css_name) + '.css', 'utf8', function (e, css) {
193
- process.stdout.write("- " + css_name + ": ");
250
+ process.stdout.write("- " + path.join(baseFolder, css_name) + ": ");
194
251
 
195
- css = css && doReplacements(css, 'test/less/' + foldername);
252
+ css = css && doReplacements(css, path.join(baseFolder, foldername));
196
253
  if (result.css === css) { ok('OK'); }
197
254
  else {
198
255
  difference("FAIL", css, result.css);
@@ -206,12 +263,12 @@ module.exports = function() {
206
263
 
207
264
  function diff(left, right) {
208
265
  require('diff').diffLines(left, right).forEach(function(item) {
209
- if(item.added || item.removed) {
210
- var text = item.value.replace("\n", String.fromCharCode(182) + "\n");
211
- process.stdout.write(stylize(text, item.added ? 'green' : 'red'));
212
- } else {
213
- process.stdout.write(item.value);
214
- }
266
+ if (item.added || item.removed) {
267
+ var text = item.value && item.value.replace("\n", String.fromCharCode(182) + "\n").replace('\ufeff', '[[BOM]]');
268
+ process.stdout.write(stylize(text, item.added ? 'green' : 'red'));
269
+ } else {
270
+ process.stdout.write(item.value && item.value.replace('\ufeff', '[[BOM]]'));
271
+ }
215
272
  });
216
273
  process.stdout.write("\n");
217
274
  }
@@ -236,9 +293,15 @@ module.exports = function() {
236
293
  endTest();
237
294
  }
238
295
 
296
+ function finished() {
297
+ isFinished = true;
298
+ endTest();
299
+ }
300
+
239
301
  function endTest() {
240
- var leaked = checkGlobalLeaks();
241
- if (failedTests + passedTests === totalTests) {
302
+ if (isFinished && ((failedTests + passedTests) >= totalTests)) {
303
+ var leaked = checkGlobalLeaks();
304
+
242
305
  process.stdout.write("\n");
243
306
  if (failedTests > 0) {
244
307
  process.stdout.write(failedTests + stylize(" Failed", "red") + ", " + passedTests + " passed\n");
@@ -251,17 +314,28 @@ module.exports = function() {
251
314
  }
252
315
 
253
316
  if (leaked.length || failedTests) {
254
- //process.exit(1);
255
- process.on('exit', function() { process.reallyExit(1) });
317
+ process.on('exit', function() { process.reallyExit(1); });
256
318
  }
257
319
  }
258
320
  }
259
321
 
322
+ function contains(fullArray, obj) {
323
+ for (var i = 0; i < fullArray.length; i++) {
324
+ if (fullArray[i] === obj) {
325
+ return true;
326
+ }
327
+ }
328
+ return false;
329
+ }
330
+
260
331
  function toCSS(options, path, callback) {
261
332
  options = options || {};
262
- var str = fs.readFileSync(path, 'utf8');
333
+ var str = fs.readFileSync(path, 'utf8'), addPath = require('path').dirname(path);
263
334
 
264
- options.paths = [require('path').dirname(path)];
335
+ options.paths = options.paths || [];
336
+ if (!contains(options.paths, addPath)) {
337
+ options.paths.push(addPath);
338
+ }
265
339
  options.filename = require('path').resolve(process.cwd(), path);
266
340
  options.optimization = options.optimization || 0;
267
341
 
@@ -275,6 +349,9 @@ module.exports = function() {
275
349
  }
276
350
 
277
351
  function testNoOptions() {
352
+ if (oneTestOnly && "Integration" !== oneTestOnly) {
353
+ return;
354
+ }
278
355
  totalTests++;
279
356
  try {
280
357
  process.stdout.write("- Integration - creating parser without options: ");
@@ -288,9 +365,13 @@ module.exports = function() {
288
365
 
289
366
  return {
290
367
  runTestSet: runTestSet,
368
+ runTestSetNormalOnly: runTestSetNormalOnly,
291
369
  testSyncronous: testSyncronous,
292
370
  testErrors: testErrors,
293
371
  testSourcemap: testSourcemap,
294
- testNoOptions: testNoOptions
372
+ testEmptySourcemap: testEmptySourcemap,
373
+ testNoOptions: testNoOptions,
374
+ prepBomTest: prepBomTest,
375
+ finished: finished
295
376
  };
296
377
  };
@@ -1,17 +1,19 @@
1
1
  var less = require('../lib/less'),
2
- fs = require('fs')
2
+ fs = require('fs');
3
3
 
4
- var input = fs.readFileSync("./test/less/modifyVars/extended.less", 'utf8')
5
- var expectedCss = fs.readFileSync('./test/css/modifyVars/extended.css', 'utf8')
4
+ var input = fs.readFileSync("./test/less/modifyVars/extended.less", 'utf8');
5
+ var expectedCss = fs.readFileSync('./test/css/modifyVars/extended.css', 'utf8');
6
6
  var options = {
7
- modifyVars: JSON.parse(fs.readFileSync("./test/less/modifyVars/extended.json", 'utf8'))
8
- }
7
+ modifyVars: JSON.parse(fs.readFileSync("./test/less/modifyVars/extended.json", 'utf8'))
8
+ };
9
9
 
10
10
  less.render(input, options, function (err, result) {
11
- if (err) console.log(err);
12
- if (result.css === expectedCss) {
13
- console.log("PASS")
14
- } else {
15
- console.log("FAIL")
16
- }
17
- })
11
+ if (err) {
12
+ console.log(err);
13
+ }
14
+ if (result.css === expectedCss) {
15
+ console.log("PASS");
16
+ } else {
17
+ console.log("FAIL");
18
+ }
19
+ });