html-validate 6.1.0 → 6.1.1
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/CHANGELOG.md +6 -0
- package/README.md +10 -2
- package/dist/cjs/cli.js +3 -0
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.d.ts +215 -3
- package/dist/cjs/core.js +122 -4
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/dist/types/browser.d.ts +3 -0
- package/dist/{es → cjs/dist/types}/index.d.ts +6 -5
- package/dist/cjs/dist/types/jest/jest.d.ts +4 -0
- package/dist/{es → cjs/dist/types/transform}/test-utils.d.ts +1 -1
- package/dist/cjs/html-validate.js +1 -1
- package/dist/cjs/html-validate.js.map +1 -1
- package/dist/cjs/jest-lib.d.ts +1 -1
- package/dist/es/cli.js +3 -0
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core.d.ts +215 -3
- package/dist/es/core.js +122 -4
- package/dist/es/core.js.map +1 -1
- package/dist/es/dist/types/browser.d.ts +3 -0
- package/dist/{cjs → es/dist/types}/index.d.ts +6 -5
- package/dist/es/dist/types/jest/jest.d.ts +4 -0
- package/dist/{cjs → es/dist/types/transform}/test-utils.d.ts +1 -1
- package/dist/es/html-validate.js +1 -1
- package/dist/es/html-validate.js.map +1 -1
- package/dist/es/jest-lib.d.ts +1 -1
- package/package.json +39 -36
- package/dist/cjs/browser.d.ts +0 -4
- package/dist/cjs/jest.d.ts +0 -4
- package/dist/es/browser.d.ts +0 -4
- package/dist/es/jest.d.ts +0 -4
package/dist/cjs/core.js
CHANGED
|
@@ -264,6 +264,9 @@ class NestedError extends Error {
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
/**
|
|
268
|
+
* @public
|
|
269
|
+
*/
|
|
267
270
|
class UserError extends NestedError {
|
|
268
271
|
}
|
|
269
272
|
|
|
@@ -274,6 +277,9 @@ function getSummary(schema, obj, errors) {
|
|
|
274
277
|
// istanbul ignore next: for safety only
|
|
275
278
|
return output.length > 0 ? output[0].error : "unknown validation error";
|
|
276
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* @public
|
|
282
|
+
*/
|
|
277
283
|
class SchemaValidationError extends UserError {
|
|
278
284
|
constructor(filename, message, obj, schema, errors) {
|
|
279
285
|
const summary = getSummary(schema, obj, errors);
|
|
@@ -707,6 +713,8 @@ var TextContent$1;
|
|
|
707
713
|
/**
|
|
708
714
|
* Properties listed here can be copied (loaded) onto another element using
|
|
709
715
|
* [[HtmlElement.loadMeta]].
|
|
716
|
+
*
|
|
717
|
+
* @public
|
|
710
718
|
*/
|
|
711
719
|
const MetaCopyableProperty = [
|
|
712
720
|
"metadata",
|
|
@@ -847,16 +855,27 @@ const ajvRegexpKeyword = {
|
|
|
847
855
|
errors: true,
|
|
848
856
|
validate: ajvRegexpValidate,
|
|
849
857
|
};
|
|
858
|
+
/**
|
|
859
|
+
* @public
|
|
860
|
+
*/
|
|
850
861
|
class MetaTable {
|
|
862
|
+
/**
|
|
863
|
+
* @internal
|
|
864
|
+
*/
|
|
851
865
|
constructor() {
|
|
852
866
|
this.elements = {};
|
|
853
867
|
this.schema = clone(schema);
|
|
854
868
|
}
|
|
869
|
+
/**
|
|
870
|
+
* @internal
|
|
871
|
+
*/
|
|
855
872
|
init() {
|
|
856
873
|
this.resolveGlobal();
|
|
857
874
|
}
|
|
858
875
|
/**
|
|
859
876
|
* Extend validation schema.
|
|
877
|
+
*
|
|
878
|
+
* @internal
|
|
860
879
|
*/
|
|
861
880
|
extendValidationSchema(patch) {
|
|
862
881
|
if (patch.properties) {
|
|
@@ -877,6 +896,7 @@ class MetaTable {
|
|
|
877
896
|
/**
|
|
878
897
|
* Load metadata table from object.
|
|
879
898
|
*
|
|
899
|
+
* @internal
|
|
880
900
|
* @param obj - Object with metadata to load
|
|
881
901
|
* @param filename - Optional filename used when presenting validation error
|
|
882
902
|
*/
|
|
@@ -897,6 +917,7 @@ class MetaTable {
|
|
|
897
917
|
/**
|
|
898
918
|
* Load metadata table from filename
|
|
899
919
|
*
|
|
920
|
+
* @internal
|
|
900
921
|
* @param filename - Filename to load
|
|
901
922
|
*/
|
|
902
923
|
loadFromFile(filename) {
|
|
@@ -915,6 +936,7 @@ class MetaTable {
|
|
|
915
936
|
/**
|
|
916
937
|
* Get [[MetaElement]] for the given tag or null if the element doesn't exist.
|
|
917
938
|
*
|
|
939
|
+
* @public
|
|
918
940
|
* @returns A shallow copy of metadata.
|
|
919
941
|
*/
|
|
920
942
|
getMetaFor(tagName) {
|
|
@@ -923,6 +945,8 @@ class MetaTable {
|
|
|
923
945
|
}
|
|
924
946
|
/**
|
|
925
947
|
* Find all tags which has enabled given property.
|
|
948
|
+
*
|
|
949
|
+
* @public
|
|
926
950
|
*/
|
|
927
951
|
getTagsWithProperty(propName) {
|
|
928
952
|
return Object.entries(this.elements)
|
|
@@ -931,6 +955,8 @@ class MetaTable {
|
|
|
931
955
|
}
|
|
932
956
|
/**
|
|
933
957
|
* Find tag matching tagName or inheriting from it.
|
|
958
|
+
*
|
|
959
|
+
* @public
|
|
934
960
|
*/
|
|
935
961
|
getTagsDerivedFrom(tagName) {
|
|
936
962
|
return Object.entries(this.elements)
|
|
@@ -962,6 +988,9 @@ class MetaTable {
|
|
|
962
988
|
ajv.addKeyword({ keyword: "copyable" });
|
|
963
989
|
return ajv.compile(this.schema);
|
|
964
990
|
}
|
|
991
|
+
/**
|
|
992
|
+
* @public
|
|
993
|
+
*/
|
|
965
994
|
getJSONSchema() {
|
|
966
995
|
return this.schema;
|
|
967
996
|
}
|
|
@@ -997,6 +1026,9 @@ class MetaTable {
|
|
|
997
1026
|
merged.attributes = Object.fromEntries(filteredAttrs);
|
|
998
1027
|
return merged;
|
|
999
1028
|
}
|
|
1029
|
+
/**
|
|
1030
|
+
* @internal
|
|
1031
|
+
*/
|
|
1000
1032
|
resolve(node) {
|
|
1001
1033
|
if (node.meta) {
|
|
1002
1034
|
expandProperties(node, node.meta);
|
|
@@ -1091,6 +1123,9 @@ function matchAttribute(node, match) {
|
|
|
1091
1123
|
}
|
|
1092
1124
|
}
|
|
1093
1125
|
|
|
1126
|
+
/**
|
|
1127
|
+
* @public
|
|
1128
|
+
*/
|
|
1094
1129
|
class DynamicValue {
|
|
1095
1130
|
constructor(expr) {
|
|
1096
1131
|
this.expr = expr;
|
|
@@ -1741,6 +1776,8 @@ const TEXT_NODE_NAME = "#text";
|
|
|
1741
1776
|
*
|
|
1742
1777
|
* Text nodes are appended as children of `HtmlElement` and cannot have childen
|
|
1743
1778
|
* of its own.
|
|
1779
|
+
*
|
|
1780
|
+
* @public
|
|
1744
1781
|
*/
|
|
1745
1782
|
class TextNode extends DOMNode {
|
|
1746
1783
|
/**
|
|
@@ -1772,6 +1809,9 @@ class TextNode extends DOMNode {
|
|
|
1772
1809
|
}
|
|
1773
1810
|
}
|
|
1774
1811
|
|
|
1812
|
+
/**
|
|
1813
|
+
* @public
|
|
1814
|
+
*/
|
|
1775
1815
|
exports.NodeClosed = void 0;
|
|
1776
1816
|
(function (NodeClosed) {
|
|
1777
1817
|
NodeClosed[NodeClosed["Open"] = 0] = "Open";
|
|
@@ -1786,6 +1826,9 @@ function isElement(node) {
|
|
|
1786
1826
|
function isValidTagName(tagName) {
|
|
1787
1827
|
return Boolean(tagName !== "" && tagName !== "*");
|
|
1788
1828
|
}
|
|
1829
|
+
/**
|
|
1830
|
+
* @public
|
|
1831
|
+
*/
|
|
1789
1832
|
class HtmlElement extends DOMNode {
|
|
1790
1833
|
constructor(tagName, parent, closed, meta, location) {
|
|
1791
1834
|
const nodeType = tagName ? NodeType.ELEMENT_NODE : NodeType.DOCUMENT_NODE;
|
|
@@ -1811,9 +1854,15 @@ class HtmlElement extends DOMNode {
|
|
|
1811
1854
|
}
|
|
1812
1855
|
}
|
|
1813
1856
|
}
|
|
1857
|
+
/**
|
|
1858
|
+
* @internal
|
|
1859
|
+
*/
|
|
1814
1860
|
static rootNode(location) {
|
|
1815
1861
|
return new HtmlElement(undefined, null, exports.NodeClosed.EndTag, null, location);
|
|
1816
1862
|
}
|
|
1863
|
+
/**
|
|
1864
|
+
* @internal
|
|
1865
|
+
*/
|
|
1817
1866
|
static fromTokens(startToken, endToken, parent, metaTable) {
|
|
1818
1867
|
const tagName = startToken.data[2];
|
|
1819
1868
|
if (!tagName) {
|
|
@@ -2123,6 +2172,8 @@ class HtmlElement extends DOMNode {
|
|
|
2123
2172
|
}
|
|
2124
2173
|
/**
|
|
2125
2174
|
* Visit all nodes from this node and down. Depth first.
|
|
2175
|
+
*
|
|
2176
|
+
* @internal
|
|
2126
2177
|
*/
|
|
2127
2178
|
visitDepthFirst(callback) {
|
|
2128
2179
|
function visit(node) {
|
|
@@ -2135,6 +2186,8 @@ class HtmlElement extends DOMNode {
|
|
|
2135
2186
|
}
|
|
2136
2187
|
/**
|
|
2137
2188
|
* Evaluates callbackk on all descendants, returning true if any are true.
|
|
2189
|
+
*
|
|
2190
|
+
* @internal
|
|
2138
2191
|
*/
|
|
2139
2192
|
someChildren(callback) {
|
|
2140
2193
|
return this.childElements.some(visit);
|
|
@@ -2149,6 +2202,8 @@ class HtmlElement extends DOMNode {
|
|
|
2149
2202
|
}
|
|
2150
2203
|
/**
|
|
2151
2204
|
* Evaluates callbackk on all descendants, returning true if all are true.
|
|
2205
|
+
*
|
|
2206
|
+
* @internal
|
|
2152
2207
|
*/
|
|
2153
2208
|
everyChildren(callback) {
|
|
2154
2209
|
return this.childElements.every(visit);
|
|
@@ -2163,6 +2218,8 @@ class HtmlElement extends DOMNode {
|
|
|
2163
2218
|
* Visit all nodes from this node and down. Breadth first.
|
|
2164
2219
|
*
|
|
2165
2220
|
* The first node for which the callback evaluates to true is returned.
|
|
2221
|
+
*
|
|
2222
|
+
* @internal
|
|
2166
2223
|
*/
|
|
2167
2224
|
find(callback) {
|
|
2168
2225
|
function visit(node) {
|
|
@@ -2770,6 +2827,9 @@ function compareKey(node, key, filename) {
|
|
|
2770
2827
|
}
|
|
2771
2828
|
}
|
|
2772
2829
|
}
|
|
2830
|
+
/**
|
|
2831
|
+
* @public
|
|
2832
|
+
*/
|
|
2773
2833
|
class TemplateExtractor {
|
|
2774
2834
|
constructor(ast, filename, data) {
|
|
2775
2835
|
this.ast = ast;
|
|
@@ -2863,19 +2923,28 @@ var TRANSFORMER_API;
|
|
|
2863
2923
|
TRANSFORMER_API[TRANSFORMER_API["VERSION"] = 1] = "VERSION";
|
|
2864
2924
|
})(TRANSFORMER_API || (TRANSFORMER_API = {}));
|
|
2865
2925
|
|
|
2926
|
+
/* generated file, changes will be overwritten */
|
|
2927
|
+
/** @public */
|
|
2866
2928
|
const name = "html-validate";
|
|
2867
|
-
|
|
2929
|
+
/** @public */
|
|
2930
|
+
const version = "6.1.1";
|
|
2931
|
+
/** @public */
|
|
2868
2932
|
const homepage = "https://html-validate.org";
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
};
|
|
2933
|
+
/** @public */
|
|
2934
|
+
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
2872
2935
|
|
|
2936
|
+
/**
|
|
2937
|
+
* @public
|
|
2938
|
+
*/
|
|
2873
2939
|
exports.Severity = void 0;
|
|
2874
2940
|
(function (Severity) {
|
|
2875
2941
|
Severity[Severity["DISABLED"] = 0] = "DISABLED";
|
|
2876
2942
|
Severity[Severity["WARN"] = 1] = "WARN";
|
|
2877
2943
|
Severity[Severity["ERROR"] = 2] = "ERROR";
|
|
2878
2944
|
})(exports.Severity || (exports.Severity = {}));
|
|
2945
|
+
/**
|
|
2946
|
+
* @internal
|
|
2947
|
+
*/
|
|
2879
2948
|
function parseSeverity(value) {
|
|
2880
2949
|
switch (value) {
|
|
2881
2950
|
case 0:
|
|
@@ -2923,6 +2992,9 @@ function getSchemaValidator(ruleId, properties) {
|
|
|
2923
2992
|
};
|
|
2924
2993
|
return ajv$1.compile(schema);
|
|
2925
2994
|
}
|
|
2995
|
+
/**
|
|
2996
|
+
* @public
|
|
2997
|
+
*/
|
|
2926
2998
|
class Rule {
|
|
2927
2999
|
constructor(options) {
|
|
2928
3000
|
/* faux initialization, properly initialized by init(). This is to keep TS happy without adding null-checks everywhere */
|
|
@@ -3124,6 +3196,9 @@ class Rule {
|
|
|
3124
3196
|
return null;
|
|
3125
3197
|
}
|
|
3126
3198
|
}
|
|
3199
|
+
/**
|
|
3200
|
+
* @internal
|
|
3201
|
+
*/
|
|
3127
3202
|
function ruleDocumentationUrl(filename) {
|
|
3128
3203
|
/* during bundling all "@/rule.ts"'s are converted to paths relative to the src
|
|
3129
3204
|
* folder and with the @/ prefix, by replacing the @ with the dist folder we
|
|
@@ -3398,6 +3473,9 @@ class AriaLabelMisuse extends Rule {
|
|
|
3398
3473
|
}
|
|
3399
3474
|
}
|
|
3400
3475
|
|
|
3476
|
+
/**
|
|
3477
|
+
* @public
|
|
3478
|
+
*/
|
|
3401
3479
|
class ConfigError extends UserError {
|
|
3402
3480
|
}
|
|
3403
3481
|
|
|
@@ -9516,6 +9594,9 @@ const config = {
|
|
|
9516
9594
|
},
|
|
9517
9595
|
};
|
|
9518
9596
|
|
|
9597
|
+
/**
|
|
9598
|
+
* @internal
|
|
9599
|
+
*/
|
|
9519
9600
|
const presets = {
|
|
9520
9601
|
"html-validate:a17y": config$3,
|
|
9521
9602
|
"html-validate:document": config$2,
|
|
@@ -9663,8 +9744,13 @@ function loadFromFile(filename) {
|
|
|
9663
9744
|
* Configuration holder.
|
|
9664
9745
|
*
|
|
9665
9746
|
* Each file being validated will have a unique instance of this class.
|
|
9747
|
+
*
|
|
9748
|
+
* @public
|
|
9666
9749
|
*/
|
|
9667
9750
|
class Config {
|
|
9751
|
+
/**
|
|
9752
|
+
* @internal
|
|
9753
|
+
*/
|
|
9668
9754
|
constructor(options) {
|
|
9669
9755
|
var _a;
|
|
9670
9756
|
this.transformers = [];
|
|
@@ -9727,6 +9813,8 @@ class Config {
|
|
|
9727
9813
|
* Validate configuration data.
|
|
9728
9814
|
*
|
|
9729
9815
|
* Throws SchemaValidationError if invalid.
|
|
9816
|
+
*
|
|
9817
|
+
* @internal
|
|
9730
9818
|
*/
|
|
9731
9819
|
static validate(configData, filename = null) {
|
|
9732
9820
|
var _a;
|
|
@@ -9754,6 +9842,8 @@ class Config {
|
|
|
9754
9842
|
*
|
|
9755
9843
|
* Must be called before trying to use config. Can safely be called multiple
|
|
9756
9844
|
* times.
|
|
9845
|
+
*
|
|
9846
|
+
* @internal
|
|
9757
9847
|
*/
|
|
9758
9848
|
init() {
|
|
9759
9849
|
if (this.initialized) {
|
|
@@ -9773,6 +9863,7 @@ class Config {
|
|
|
9773
9863
|
* Returns a new configuration as a merge of the two. Entries from the passed
|
|
9774
9864
|
* object takes priority over this object.
|
|
9775
9865
|
*
|
|
9866
|
+
* @internal
|
|
9776
9867
|
* @param rhs - Configuration to merge with this one.
|
|
9777
9868
|
*/
|
|
9778
9869
|
merge(rhs) {
|
|
@@ -9865,6 +9956,8 @@ class Config {
|
|
|
9865
9956
|
}
|
|
9866
9957
|
/**
|
|
9867
9958
|
* Get all configured rules, their severity and options.
|
|
9959
|
+
*
|
|
9960
|
+
* @internal
|
|
9868
9961
|
*/
|
|
9869
9962
|
getRules() {
|
|
9870
9963
|
var _a;
|
|
@@ -9887,6 +9980,8 @@ class Config {
|
|
|
9887
9980
|
}
|
|
9888
9981
|
/**
|
|
9889
9982
|
* Get all configured plugins.
|
|
9983
|
+
*
|
|
9984
|
+
* @internal
|
|
9890
9985
|
*/
|
|
9891
9986
|
getPlugins() {
|
|
9892
9987
|
return this.plugins;
|
|
@@ -9952,6 +10047,8 @@ class Config {
|
|
|
9952
10047
|
*
|
|
9953
10048
|
* A resolved configuration will merge all extended configs and load all
|
|
9954
10049
|
* plugins and transformers, and normalize the rest of the configuration.
|
|
10050
|
+
*
|
|
10051
|
+
* @internal
|
|
9955
10052
|
*/
|
|
9956
10053
|
resolve() {
|
|
9957
10054
|
return new ResolvedConfig(this.resolveData());
|
|
@@ -10136,6 +10233,9 @@ class ConfigLoader {
|
|
|
10136
10233
|
}
|
|
10137
10234
|
}
|
|
10138
10235
|
|
|
10236
|
+
/**
|
|
10237
|
+
* @internal
|
|
10238
|
+
*/
|
|
10139
10239
|
class EventHandler {
|
|
10140
10240
|
constructor() {
|
|
10141
10241
|
this.listeners = {};
|
|
@@ -10216,6 +10316,8 @@ class ParserError extends Error {
|
|
|
10216
10316
|
|
|
10217
10317
|
/**
|
|
10218
10318
|
* Parse HTML document into a DOM tree.
|
|
10319
|
+
*
|
|
10320
|
+
* @internal
|
|
10219
10321
|
*/
|
|
10220
10322
|
class Parser {
|
|
10221
10323
|
/**
|
|
@@ -10692,6 +10794,9 @@ class Parser {
|
|
|
10692
10794
|
}
|
|
10693
10795
|
}
|
|
10694
10796
|
|
|
10797
|
+
/**
|
|
10798
|
+
* @internal
|
|
10799
|
+
*/
|
|
10695
10800
|
class Reporter {
|
|
10696
10801
|
constructor() {
|
|
10697
10802
|
this.result = {};
|
|
@@ -10810,6 +10915,9 @@ function messageSort(a, b) {
|
|
|
10810
10915
|
return 0;
|
|
10811
10916
|
}
|
|
10812
10917
|
|
|
10918
|
+
/**
|
|
10919
|
+
* @internal
|
|
10920
|
+
*/
|
|
10813
10921
|
class Engine {
|
|
10814
10922
|
constructor(config, ParserClass) {
|
|
10815
10923
|
this.report = new Reporter();
|
|
@@ -11163,6 +11271,8 @@ class Engine {
|
|
|
11163
11271
|
*
|
|
11164
11272
|
* In practice this means no configuration is fetch by traversing the
|
|
11165
11273
|
* filesystem.
|
|
11274
|
+
*
|
|
11275
|
+
* @public
|
|
11166
11276
|
*/
|
|
11167
11277
|
class StaticConfigLoader extends ConfigLoader {
|
|
11168
11278
|
getConfigFor(handle, configOverride) {
|
|
@@ -11202,6 +11312,8 @@ function isConfigData(value) {
|
|
|
11202
11312
|
* Primary API for using HTML-validate.
|
|
11203
11313
|
*
|
|
11204
11314
|
* Provides high-level abstractions for common operations.
|
|
11315
|
+
*
|
|
11316
|
+
* @public
|
|
11205
11317
|
*/
|
|
11206
11318
|
class HtmlValidate {
|
|
11207
11319
|
constructor(arg) {
|
|
@@ -11406,6 +11518,7 @@ class HtmlValidate {
|
|
|
11406
11518
|
/**
|
|
11407
11519
|
* Create a parser configured for given filename.
|
|
11408
11520
|
*
|
|
11521
|
+
* @internal
|
|
11409
11522
|
* @param source - Source to use.
|
|
11410
11523
|
*/
|
|
11411
11524
|
getParserFor(source) {
|
|
@@ -11449,6 +11562,7 @@ const defaults$1 = {
|
|
|
11449
11562
|
* Tests if plugin is compatible with html-validate library. Unless the `silent`
|
|
11450
11563
|
* option is used a warning is displayed on the console.
|
|
11451
11564
|
*
|
|
11565
|
+
* @public
|
|
11452
11566
|
* @param name - Name of plugin
|
|
11453
11567
|
* @param declared - What library versions the plugin support (e.g. declared peerDependencies)
|
|
11454
11568
|
* @returns - `true` if version is compatible
|
|
@@ -11477,6 +11591,7 @@ const ruleIds = new Set(Object.keys(bundledRules));
|
|
|
11477
11591
|
* Can be used to create forward/backward compatibility by checking if a rule
|
|
11478
11592
|
* exists to enable/disable it.
|
|
11479
11593
|
*
|
|
11594
|
+
* @public
|
|
11480
11595
|
* @param ruleId - Rule id to check
|
|
11481
11596
|
* @returns `true` if rule exists
|
|
11482
11597
|
*/
|
|
@@ -11516,6 +11631,8 @@ function findConfigurationFiles(directory) {
|
|
|
11516
11631
|
* 2. If set in the global config the override is merged into global and
|
|
11517
11632
|
* returned. No configuration files are searched.
|
|
11518
11633
|
* 3. Setting `root` in configuration file only stops directory traversal.
|
|
11634
|
+
*
|
|
11635
|
+
* @public
|
|
11519
11636
|
*/
|
|
11520
11637
|
class FileSystemConfigLoader extends ConfigLoader {
|
|
11521
11638
|
/**
|
|
@@ -11853,6 +11970,7 @@ const availableFormatters = {
|
|
|
11853
11970
|
/**
|
|
11854
11971
|
* Get formatter function by name.
|
|
11855
11972
|
*
|
|
11973
|
+
* @internal
|
|
11856
11974
|
* @param name - Name of formatter.
|
|
11857
11975
|
* @returns Formatter function or null if it doesn't exist.
|
|
11858
11976
|
*/
|