@salesforcedevs/docs-components 1.3.8 → 1.3.9-h2-toc-fix

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 (28) hide show
  1. package/package.json +2 -2
  2. package/src/modules/doc/amfReference/amfReference.css +8 -0
  3. package/src/modules/doc/amfReference/amfReference.html +1 -2
  4. package/src/modules/doc/amfReference/amfReference.ts +26 -39
  5. package/src/modules/doc/breadcrumbItem/breadcrumbItem.ts +17 -10
  6. package/src/modules/doc/breadcrumbs/breadcrumbs.html +7 -9
  7. package/src/modules/doc/breadcrumbs/breadcrumbs.ts +16 -18
  8. package/src/modules/doc/content/content.css +36 -46
  9. package/src/modules/doc/content/content.ts +4 -2
  10. package/src/modules/doc/contentCallout/contentCallout.css +7 -1
  11. package/src/modules/doc/contentCallout/contentCallout.html +10 -3
  12. package/src/modules/doc/contentCallout/contentCallout.ts +7 -0
  13. package/src/modules/doc/contentLayout/contentLayout.html +4 -3
  14. package/src/modules/doc/contentLayout/contentLayout.ts +32 -24
  15. package/src/modules/doc/header/header.html +12 -4
  16. package/src/modules/doc/header/header.ts +24 -0
  17. package/src/modules/doc/heading/heading.css +16 -37
  18. package/src/modules/doc/heading/heading.ts +9 -9
  19. package/src/modules/doc/phase/phase.ts +3 -2
  20. package/src/modules/doc/sprigSurvey/sprigSurvey.html +20 -0
  21. package/src/modules/doc/sprigSurvey/sprigSurvey.scoped.css +16 -0
  22. package/src/modules/doc/sprigSurvey/sprigSurvey.ts +16 -0
  23. package/src/modules/doc/toc/toc.html +1 -3
  24. package/src/modules/doc/toolbar/toolbar.ts +6 -6
  25. package/src/modules/doc/xmlContent/utils.ts +3 -1
  26. package/src/modules/doc/xmlContent/xmlContent.html +2 -3
  27. package/src/modules/doc/xmlContent/xmlContent.ts +61 -10
  28. package/LICENSE +0 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/docs-components",
3
- "version": "1.3.8",
3
+ "version": "1.3.9-h2-toc-fix",
4
4
  "description": "Docs Lightning web components for DSC",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
@@ -24,5 +24,5 @@
24
24
  "@types/lodash.orderby": "^4.6.7",
25
25
  "@types/lodash.uniqby": "^4.7.7"
26
26
  },
27
- "gitHead": "a428b8618c3d33b4a40f8aa3cefde6ac0d04cf0b"
27
+ "gitHead": "4629fdd9ca18a13480044ad43515b91945d16aad"
28
28
  }
@@ -1,5 +1,13 @@
1
1
  @import "docHelpers/phaseContentLayout";
2
2
 
3
+ .api-documentation {
4
+ margin-top: 48px;
5
+ }
6
+
3
7
  .version-picker {
4
8
  margin-left: auto;
5
9
  }
10
+
11
+ .version-picker-text {
12
+ border-bottom: 1px dashed var(--dx-g-blue-vibrant-50);
13
+ }
@@ -22,14 +22,13 @@
22
22
  <div slot="sidebar-header" class="version-picker">
23
23
  <template if:true={isVersionEnabled}>
24
24
  <dx-dropdown
25
- suppress-gtm-nav-headings
26
25
  onchange={handleVersionChange}
27
26
  data-type="version"
28
27
  options={versions}
29
28
  value={selectedVersion.id}
30
29
  width="290px"
31
30
  >
32
- <dx-button variant="inline">{selectedVersion.id}</dx-button>
31
+ <dx-button variant="inline" class="version-picker-text">{selectedVersion.id}</dx-button>
33
32
  </dx-dropdown>
34
33
  </template>
35
34
  </div>
@@ -25,6 +25,7 @@ import {
25
25
  REFERENCE_TYPES,
26
26
  oldReferenceIdNewReferenceIdMap
27
27
  } from "./constants";
28
+ import { restoreScroll } from "dx/scrollManager";
28
29
 
29
30
  export default class AmfReference extends LightningElement {
30
31
  @api breadcrumbs?: string | null | undefined = null;
@@ -138,7 +139,6 @@ export default class AmfReference extends LightningElement {
138
139
  protected _amfConfigMap: Map<string, AmfConfig> = new Map();
139
140
  protected _referenceSetConfig!: ReferenceSetConfig;
140
141
  protected _currentReferenceId = "";
141
- protected _scrollInterval = 0;
142
142
 
143
143
  protected parentReferenceUrls = [];
144
144
  protected amfMap: Record<string, AmfModelRecord> = {};
@@ -167,10 +167,12 @@ export default class AmfReference extends LightningElement {
167
167
  constructor() {
168
168
  super();
169
169
 
170
- this._boundOnApiNavigationChanged =
171
- this.onApiNavigationChanged.bind(this);
172
- this._boundUpdateSelectedItemFromUrlQuery =
173
- this.updateSelectedItemFromUrlQuery.bind(this);
170
+ this._boundOnApiNavigationChanged = this.onApiNavigationChanged.bind(
171
+ this
172
+ );
173
+ this._boundUpdateSelectedItemFromUrlQuery = this.updateSelectedItemFromUrlQuery.bind(
174
+ this
175
+ );
174
176
  }
175
177
 
176
178
  connectedCallback(): void {
@@ -182,9 +184,6 @@ export default class AmfReference extends LightningElement {
182
184
  "popstate",
183
185
  this._boundUpdateSelectedItemFromUrlQuery
184
186
  );
185
- this._scrollInterval = window.setInterval(() => {
186
- this.saveScroll();
187
- }, 1000);
188
187
  }
189
188
 
190
189
  disconnectedCallback(): void {
@@ -196,20 +195,6 @@ export default class AmfReference extends LightningElement {
196
195
  "popstate",
197
196
  this._boundUpdateSelectedItemFromUrlQuery
198
197
  );
199
- window.clearInterval(this._scrollInterval);
200
- }
201
-
202
- saveScroll() {
203
- window.history.replaceState(
204
- { scrollValue: document.body.scrollTop },
205
- "",
206
- window.location.href
207
- );
208
- }
209
-
210
- restoreScroll() {
211
- document.body.scrollTop = document.documentElement.scrollTop =
212
- window.history.state?.scrollValue;
213
198
  }
214
199
 
215
200
  renderedCallback(): void {
@@ -243,8 +228,9 @@ export default class AmfReference extends LightningElement {
243
228
  const updatedReferenceId =
244
229
  oldReferenceIdNewReferenceIdMap[referenceId];
245
230
  const newReferenceId = updatedReferenceId || referenceId;
246
- const referenceItemConfig =
247
- this.getAmfConfigWithId(newReferenceId);
231
+ const referenceItemConfig = this.getAmfConfigWithId(
232
+ newReferenceId
233
+ );
248
234
  if (referenceItemConfig) {
249
235
  hashBasedRedirectUrl = `${referenceItemConfig.href}?meta=${encodedMeta}`;
250
236
  }
@@ -313,8 +299,9 @@ export default class AmfReference extends LightningElement {
313
299
  for (let i = 0; i < allVersions.length; i++) {
314
300
  const versionItem = allVersions[i];
315
301
  const referenceLink = versionItem.link.href;
316
- const referenceId =
317
- this.getReferenceIdFromUrl(referenceLink);
302
+ const referenceId = this.getReferenceIdFromUrl(
303
+ referenceLink
304
+ );
318
305
  if (this._currentReferenceId === referenceId) {
319
306
  // This is to navigate to respective topic in the changed version
320
307
  versionItem.link.href = `${referenceLink}/${currentRefMeta}`;
@@ -841,7 +828,7 @@ export default class AmfReference extends LightningElement {
841
828
  this.loadMarkdownBasedReference();
842
829
  }
843
830
 
844
- this.restoreScroll();
831
+ restoreScroll(); // don't try this at home kids
845
832
  }
846
833
 
847
834
  /**
@@ -849,8 +836,6 @@ export default class AmfReference extends LightningElement {
849
836
  * @param event
850
837
  */
851
838
  protected onApiNavigationChanged(): void {
852
- this.saveScroll();
853
-
854
839
  const specBasedReference = this.isSpecBasedReference(
855
840
  this._currentReferenceId
856
841
  );
@@ -1241,10 +1226,12 @@ export default class AmfReference extends LightningElement {
1241
1226
  }
1242
1227
  if (!isRedirecting) {
1243
1228
  const currentReferenceUrl = window.location.href;
1244
- const referenceMeta =
1245
- this.getMarkdownReferenceMeta(currentReferenceUrl);
1246
- const selectedItemRefId =
1247
- this.getReferenceIdFromUrl(currentReferenceUrl);
1229
+ const referenceMeta = this.getMarkdownReferenceMeta(
1230
+ currentReferenceUrl
1231
+ );
1232
+ const selectedItemRefId = this.getReferenceIdFromUrl(
1233
+ currentReferenceUrl
1234
+ );
1248
1235
  const referenceDetails = this.getRefDetailsForGivenTopicMeta(
1249
1236
  selectedItemRefId,
1250
1237
  referenceMeta
@@ -1280,13 +1267,12 @@ export default class AmfReference extends LightningElement {
1280
1267
  }
1281
1268
 
1282
1269
  onNavSelect(event: CustomEvent): void {
1283
- this.saveScroll();
1284
-
1285
1270
  const name = event.detail.name;
1286
1271
  if (name) {
1287
1272
  const urlReferenceId = this.getReferenceIdFromUrl(name);
1288
- const specBasedReference =
1289
- this.isSpecBasedReference(urlReferenceId);
1273
+ const specBasedReference = this.isSpecBasedReference(
1274
+ urlReferenceId
1275
+ );
1290
1276
  if (specBasedReference) {
1291
1277
  const metaVal = this.getMetaFromUrl(name);
1292
1278
  const currentSelectedMeta = this.selectedTopic
@@ -1336,8 +1322,9 @@ export default class AmfReference extends LightningElement {
1336
1322
  const currentReferenceId = this.getReferenceIdFromUrl(currentUrl);
1337
1323
  //No need to do anything if user is expanding currently selected reference
1338
1324
  if (referenceId !== currentReferenceId) {
1339
- const isSpecBasedReference =
1340
- this.isSpecBasedReference(referenceId);
1325
+ const isSpecBasedReference = this.isSpecBasedReference(
1326
+ referenceId
1327
+ );
1341
1328
  if (isSpecBasedReference) {
1342
1329
  // Perform functionality same as item selection
1343
1330
  this.onNavSelect(event);
@@ -1,6 +1,6 @@
1
1
  import { track } from "dxUtils/analytics";
2
2
  import { LightningElement, api } from "lwc";
3
- import { AnalyticsPayload, BreadcrumbItemVariant } from "typings/custom";
3
+ import { BreadcrumbItemVariant } from "typings/custom";
4
4
 
5
5
  const BREADCRUMB_LONG = "breadcrumb_long";
6
6
  const BREADCRUMB_BACK_ARROW = "breadcrumb_back-arrow";
@@ -8,8 +8,8 @@ const BREADCRUMB_BACK_ARROW = "breadcrumb_back-arrow";
8
8
  const LONG_LABEL_NUMBER = 30;
9
9
  export default class BreadcrumbItem extends LightningElement {
10
10
  @api href?: string;
11
- @api analyticsEvent!: string;
12
- @api analyticsBasePayload!: AnalyticsPayload;
11
+ @api level?: string;
12
+ @api breadcrumbLabels?: string;
13
13
 
14
14
  @api
15
15
  get label() {
@@ -51,14 +51,21 @@ export default class BreadcrumbItem extends LightningElement {
51
51
  }
52
52
 
53
53
  private onLinkClick(event: Event): void {
54
- if (!this.analyticsEvent) {
55
- return;
56
- }
54
+ track(event.target!, "custEv_breadcrumbClick", {
55
+ click_text: this.label,
56
+ click_url: this.href,
57
+ element_type: "link",
58
+ nav_type: "breadcrumb",
59
+ nav_level: this.level ? this.level + 1 : 1,
60
+ nav_item: this.breadcrumbLabels
61
+ });
57
62
 
58
- track(event.target!, this.analyticsEvent, {
59
- ...this.analyticsBasePayload,
60
- clickText: this.label,
61
- clickUrl: this.href
63
+ track(event.target!, "custEv_linkClick", {
64
+ click_text: this.label,
65
+ click_url: this.href,
66
+ element_title: this.label,
67
+ element_type: "link",
68
+ content_category: "cta"
62
69
  });
63
70
  }
64
71
  }
@@ -3,21 +3,19 @@
3
3
  <template if:true={displayCrumbs}>
4
4
  <template if:false={renderSmallVariant}>
5
5
  <doc-breadcrumb-item
6
- analytics-event={analyticsEventName}
7
- analytics-base-payload={analyticsBasePayload}
8
6
  href={firstCrumb.href}
9
7
  label={firstCrumb.label}
8
+ breadcrumb-labels={breadcrumbLabels}
10
9
  ></doc-breadcrumb-item>
11
10
  <span class="breadcrumb-item_slash">/</span>
12
11
  <template if:true={renderDropdown}>
13
12
  <dx-dropdown
13
+ analytics-event="custEv_breadcrumbClick"
14
+ analytics-payload={ANALYTICS_PAYLOAD}
14
15
  if:true={renderDropdown}
15
- analytics-event={analyticsEventName}
16
- analytics-base-payload={analyticsBasePayload}
17
16
  options={dropdownOptions}
18
17
  open-on-hover
19
18
  placement="bottom"
20
- suppress-gtm-nav-headings
21
19
  variant="indented"
22
20
  width="fit-content"
23
21
  >
@@ -32,11 +30,11 @@
32
30
  </template>
33
31
  <template for:each={breadcrumbItems} for:item="breadcrumb">
34
32
  <doc-breadcrumb-item
35
- analytics-event={analyticsEventName}
36
- analytics-base-payload={analyticsBasePayload}
37
33
  href={breadcrumb.href}
38
34
  key={breadcrumb.id}
39
35
  label={breadcrumb.label}
36
+ level={breadcrumb.level}
37
+ breadcrumb-labels={breadcrumbLabels}
40
38
  ></doc-breadcrumb-item>
41
39
  <span class="breadcrumb-item_slash" key={breadcrumb.label}>
42
40
  /
@@ -44,14 +42,14 @@
44
42
  </template>
45
43
  <doc-breadcrumb-item
46
44
  label={lastCrumb.label}
45
+ breadcrumb-labels={breadcrumbLabels}
47
46
  ></doc-breadcrumb-item>
48
47
  </template>
49
48
  <template if:true={renderSmallVariant}>
50
49
  <doc-breadcrumb-item
51
- analytics-event={analyticsEventName}
52
- analytics-base-payload={analyticsBasePayload}
53
50
  href={lastLinkCrump.href}
54
51
  label={lastLinkCrump.label}
52
+ breadcrumb-labels={breadcrumbLabels}
55
53
  variant="back-arrow"
56
54
  ></doc-breadcrumb-item>
57
55
  </template>
@@ -17,13 +17,6 @@ const CONSTANTS = {
17
17
  dropdownWidth: 32
18
18
  };
19
19
 
20
- export const ANALYTICS_EVENT_NAME = "custEv_breadcrumbClick";
21
- export const ANALYTICS_BASE_PAYLOAD = {
22
- elementType: "breadcrumb",
23
- locationOnPage: "breadcrumb",
24
- ctaClick: true
25
- };
26
-
27
20
  export default class Breadcrumbs extends LightningElement {
28
21
  @api ariaLabel: string = "Documentation Breadcrumbs";
29
22
 
@@ -100,18 +93,20 @@ export default class Breadcrumbs extends LightningElement {
100
93
  return this.breadcrumbs[0];
101
94
  }
102
95
 
103
- private get lastCrumb(): Breadcrumb {
104
- return this.breadcrumbs[this.breadcrumbs.length - 1];
96
+ private get breadcrumbLabels(): string {
97
+ return this.breadcrumbs.map((crumb) => crumb.label).join(":");
105
98
  }
106
99
 
107
- private get analyticsEventName() {
108
- return ANALYTICS_EVENT_NAME;
100
+ private get lastCrumb(): Breadcrumb {
101
+ return this.breadcrumbs[this.breadcrumbs.length - 1];
109
102
  }
110
103
 
111
- private get analyticsBasePayload() {
104
+ // this payload is only used for breadcrumb dropdown
105
+ private get ANALYTICS_PAYLOAD() {
112
106
  return {
113
- ...ANALYTICS_BASE_PAYLOAD,
114
- itemTitle: this.breadcrumbs.map((crumb) => crumb.label).join("/")
107
+ element_type: "link",
108
+ nav_type: "breadcrumb",
109
+ nav_level: 1
115
110
  };
116
111
  }
117
112
 
@@ -139,10 +134,13 @@ export default class Breadcrumbs extends LightningElement {
139
134
  return;
140
135
  }
141
136
 
142
- this._breadcrumbs = toJson(breadcrumbs).map((crumb: Breadcrumb) => ({
143
- ...crumb,
144
- id: crumb.id || crumb.href
145
- }));
137
+ this._breadcrumbs = toJson(breadcrumbs).map(
138
+ (crumb: Breadcrumb, index: number) => ({
139
+ ...crumb,
140
+ id: crumb.id || crumb.href,
141
+ level: index
142
+ })
143
+ );
146
144
  }
147
145
 
148
146
  private updateDropdownOptionAmount(): void {
@@ -6,7 +6,6 @@
6
6
 
7
7
  @import "dxHelpers/text";
8
8
  @import "dxHelpers/reset";
9
- @import "dxHelpers/card";
10
9
  @import "dxHelpers/table";
11
10
 
12
11
  .doc-content {
@@ -31,11 +30,6 @@
31
30
  margin-bottom: var(--dx-g-spacing-md);
32
31
  }
33
32
 
34
- .helpHead2 {
35
- margin-top: 36px;
36
- margin-bottom: var(--dx-g-spacing-md);
37
- }
38
-
39
33
  .relinfo {
40
34
  padding-top: var(--dx-g-spacing-sm);
41
35
  padding-bottom: var(--dx-g-spacing-sm);
@@ -64,61 +58,57 @@ td > img.content-image:first-child:last-child[alt="No"] {
64
58
  width: var(--dx-g-spacing-md);
65
59
  }
66
60
 
67
- h1 {
68
- font-family: var(--dx-g-font-display);
69
- font-size: var(--dx-g-text-4xl);
70
-
71
- /* not registered */
61
+ h1,
62
+ h2,
63
+ h3,
64
+ h4,
65
+ h5,
66
+ h6 {
72
67
  color: var(--dx-g-blue-vibrant-20);
73
- letter-spacing: -0.4px;
74
- line-height: 56px;
68
+ font-family: var(--dx-g-font-display);
69
+ font-weight: var(--dx-g-font-demi);
75
70
  }
76
71
 
77
- h2 {
78
- font-family: var(--dx-g-font-display);
72
+ h1 {
79
73
  font-size: var(--dx-g-text-3xl);
80
- color: var(--dx-g-blue-vibrant-20);
81
- letter-spacing: -0.1px;
74
+ letter-spacing: -0.8px;
82
75
  line-height: var(--dx-g-spacing-3xl);
76
+ margin: var(--dx-g-spacing-2xl) 0 var(--dx-g-spacing-md) 0;
83
77
  }
84
78
 
85
- h3 {
86
- font-family: var(--dx-g-font-display);
87
- font-size: 32px;
88
- color: var(--dx-g-blue-vibrant-20);
89
- letter-spacing: -0.1px;
79
+ h2 {
80
+ font-size: var(--dx-g-text-2xl);
81
+ letter-spacing: -0.4px;
90
82
  line-height: var(--dx-g-spacing-2xl);
83
+ margin: var(--dx-g-spacing-2xl) 0 var(--dx-g-spacing-md) 0;
91
84
  }
92
85
 
93
- h4 {
94
- font-family: var(--dx-g-font-display);
86
+ h3 {
95
87
  font-size: var(--dx-g-text-xl);
96
- color: var(--dx-g-blue-vibrant-20);
97
- letter-spacing: 0;
88
+ letter-spacing: -0.4px;
98
89
  line-height: var(--dx-g-spacing-xl);
90
+ margin: var(--dx-g-spacing-xl) 0 var(--dx-g-spacing-md) 0;
99
91
  }
100
92
 
101
- h5 {
102
- font-family: var(--dx-g-font-display);
103
- font-size: var(--dx-g-text-lg);
104
- color: var(--dx-g-blue-vibrant-20);
105
- letter-spacing: 0;
106
- line-height: 28px;
93
+ h4 {
94
+ font-size: var(--dx-g-text-base);
95
+ letter-spacing: -0.5px;
96
+ line-height: var(--dx-g-spacing-lg);
97
+ margin: var(--dx-g-spacing-lg) 0 var(--dx-g-spacing-sm) 0;
107
98
  }
108
99
 
109
- h6 {
110
- font-family: var(--dx-g-font-display);
111
- font-size: var(--dx-g-text-base);
112
- color: var(--dx-g-blue-vibrant-20);
113
- letter-spacing: 0;
100
+ h5 {
101
+ font-size: var(--dx-g-text-sm);
102
+ letter-spacing: -0.3px;
114
103
  line-height: var(--dx-g-spacing-mlg);
104
+ margin: var(--dx-g-spacing-md) 0 var(--dx-g-spacing-sm) 0;
115
105
  }
116
106
 
117
- .dx-text-heading-alt-1 {
118
- color: var(--dx-g-gray-30);
119
- font-family: var(--dx-g-font-sans);
120
- font-size: var(--slds-text-lg);
121
- line-height: 25px;
107
+ h6 {
108
+ font-size: var(--dx-g-text-xs);
109
+ letter-spacing: -0.3px;
110
+ line-height: var(--dx-g-spacing-md);
111
+ margin: var(--dx-g-spacing-sm) 0 var(--dx-g-spacing-sm) 0;
122
112
  }
123
113
 
124
114
  p,
@@ -165,10 +155,6 @@ a,
165
155
  color: var(--dx-g-blue-vibrant-50);
166
156
  }
167
157
 
168
- .helpHead1 {
169
- margin-bottom: var(--dx-g-spacing-lg);
170
- }
171
-
172
158
  .shortdesc {
173
159
  font-family: var(--dx-g-font-sans);
174
160
  font-size: var(--dx-g-text-base);
@@ -270,6 +256,10 @@ li {
270
256
  var(--dx-g-spacing-lg);
271
257
  }
272
258
 
259
+ li table {
260
+ margin-bottom: var(--dx-g-spacing-lg);
261
+ }
262
+
273
263
  li > li {
274
264
  margin-left: var(--dx-g-spacing-2xl);
275
265
  }
@@ -264,6 +264,7 @@ export default class Content extends LightningElement {
264
264
 
265
265
  const img: HTMLImageElement = document.createElement("img");
266
266
  img.src = src;
267
+ img.alt = "";
267
268
  if (alt) {
268
269
  img.alt = alt;
269
270
  }
@@ -323,8 +324,9 @@ export default class Content extends LightningElement {
323
324
  event.preventDefault();
324
325
  // eslint-disable-next-line no-use-before-define
325
326
  const target = event.currentTarget.dataset.id;
326
- const [page, docId, deliverable, tempContentDocumentId] =
327
- target.split("/");
327
+ const [page, docId, deliverable, tempContentDocumentId] = target.split(
328
+ "/"
329
+ );
328
330
  const [contentDocumentId, hash] = tempContentDocumentId.split("#");
329
331
  const newPageReference = {
330
332
  page: page,
@@ -6,11 +6,17 @@
6
6
  border-radius: 4px;
7
7
  }
8
8
 
9
- .dx-callout-base {
9
+ .dx-callout-base,
10
+ .dx-callout-plain {
10
11
  background-color: var(--dx-g-gray-95);
11
12
  border-color: var(--dx-g-gray-50);
12
13
  }
13
14
 
15
+ .dx-callout-plain {
16
+ border: none;
17
+ margin-bottom: var(--dx-g-text-2xl);
18
+ }
19
+
14
20
  .dx-callout-note {
15
21
  background-color: var(--dx-g-blue-vibrant-95);
16
22
  border-color: var(--dx-g-blue-vibrant-50);
@@ -1,8 +1,15 @@
1
1
  <template>
2
2
  <div class={className}>
3
- <div class="doc-status-icon dx-callout-icon">
4
- <dx-icon symbol={iconName} size="large" color={iconColor}></dx-icon>
5
- </div>
3
+ <template if:false={hideIcon}>
4
+ <div class="doc-status-icon dx-callout-icon">
5
+ <dx-icon
6
+ symbol={iconName}
7
+ size="large"
8
+ color={iconColor}
9
+ ></dx-icon>
10
+ </div>
11
+ </template>
12
+
6
13
  <div class="dx-callout-content">
7
14
  <p class="doc-status-title dx-callout-title dx-text-body-3">
8
15
  {title}
@@ -11,6 +11,9 @@ export default class ContentCallout extends LightningElement {
11
11
 
12
12
  connectedCallback() {
13
13
  switch (this.variant) {
14
+ case "plain":
15
+ this.cardVariant = "dx-callout-plain";
16
+ break;
14
17
  case "tip":
15
18
  this.cardVariant = "dx-callout-tip";
16
19
  this.iconColor = "green-vibrant-60";
@@ -49,6 +52,10 @@ export default class ContentCallout extends LightningElement {
49
52
  );
50
53
  }
51
54
 
55
+ get hideIcon() {
56
+ return this.variant === "plain";
57
+ }
58
+
52
59
  private isSlotEmpty: boolean = true;
53
60
  private onSlotChange(e: LightningSlotElement) {
54
61
  // @ts-ignore
@@ -32,9 +32,10 @@
32
32
  if:true={showBreadcrumbs}
33
33
  breadcrumbs={breadcrumbs}
34
34
  ></doc-breadcrumbs>
35
- <div style={docContentStyle}>
36
- <slot onslotchange={onSlotChange}></slot>
37
- </div>
35
+ <slot onslotchange={onSlotChange}></slot>
36
+ <doc-sprig-survey
37
+ if:true={shouldDisplayFeedback}
38
+ ></doc-sprig-survey>
38
39
  </div>
39
40
  <div class="right-nav-bar is-sticky">
40
41
  <dx-toc
@@ -6,6 +6,8 @@ import { SearchSyncer } from "docUtils/SearchSyncer";
6
6
 
7
7
  type AnchorMap = { [key: string]: { intersect: boolean; id: string } };
8
8
 
9
+ declare const Sprig: (eventType: string, eventNme: string) => void;
10
+
9
11
  const TOC_HEADER_TAG = "DOC-HEADING";
10
12
  const HIGHLIGHTABLE_SELECTOR = [
11
13
  "p",
@@ -75,10 +77,16 @@ export default class ContentLayout extends LightningElement {
75
77
  @track
76
78
  private _tocOptions: Array<unknown>;
77
79
 
80
+ private tocOptionIdsSet = new Set();
78
81
  private anchoredElements: AnchorMap = {};
79
82
  private lastScrollPosition: number;
80
83
  private observer?: IntersectionObserver;
81
84
  private hasRendered: boolean = false;
85
+ private contentLoaded: boolean = false;
86
+
87
+ get shouldDisplayFeedback() {
88
+ return this.contentLoaded && typeof Sprig !== "undefined";
89
+ }
82
90
 
83
91
  private searchSyncer = new SearchSyncer({
84
92
  callbacks: {
@@ -117,10 +125,6 @@ export default class ContentLayout extends LightningElement {
117
125
  );
118
126
  }
119
127
 
120
- get docContentStyle(): string {
121
- return this.showBreadcrumbs ? "" : "margin-top: 48px";
122
- }
123
-
124
128
  connectedCallback(): void {
125
129
  const hasParentHighlightListener = closest(
126
130
  "doc-xml-content",
@@ -133,10 +137,6 @@ export default class ContentLayout extends LightningElement {
133
137
  );
134
138
  this.searchSyncer.init();
135
139
  }
136
-
137
- this._scrollInterval = window.setInterval(() => {
138
- this.saveScroll();
139
- }, 1000);
140
140
  }
141
141
 
142
142
  renderedCallback(): void {
@@ -167,14 +167,6 @@ export default class ContentLayout extends LightningElement {
167
167
  window.clearInterval(this._scrollInterval);
168
168
  }
169
169
 
170
- saveScroll() {
171
- window.history.replaceState(
172
- { scrollValue: document.body.scrollTop },
173
- "",
174
- window.location.href
175
- );
176
- }
177
-
178
170
  restoreScroll() {
179
171
  document.body.scrollTop = document.documentElement.scrollTop =
180
172
  window.history.state?.scrollValue;
@@ -224,32 +216,45 @@ export default class ContentLayout extends LightningElement {
224
216
  }
225
217
  };
226
218
 
219
+ onSidebarClick() {
220
+ this.resetScrollThreshold();
221
+ }
222
+
227
223
  onSlotChange(event: Event): void {
228
224
  const slotElements = (
229
225
  event.target as HTMLSlotElement
230
226
  ).assignedElements();
231
227
 
232
228
  if (slotElements.length) {
229
+ this.contentLoaded = true;
233
230
  const slotContentElement = slotElements[0];
234
231
  const headingElements =
235
232
  slotContentElement.ownerDocument?.getElementsByTagName(
236
233
  TOC_HEADER_TAG
237
234
  );
235
+
238
236
  for (const headingElement of headingElements) {
239
237
  // Sometimes elements hash is not being set when slot content is wrapped with div
240
238
  headingElement.hash = headingElement.attributes.hash?.nodeValue;
241
239
  }
240
+
242
241
  const tocOptions = [];
242
+
243
243
  for (const headingElement of headingElements) {
244
244
  headingElement.id = headingElement.hash;
245
245
 
246
- // Update tocOptions from anchorTags
247
- const tocItem = {
248
- anchor: `#${headingElement.hash}`,
249
- id: headingElement.id,
250
- label: headingElement.title
251
- };
252
- tocOptions.push(tocItem);
246
+ // Update tocOptions from anchorTags only for H2
247
+ const isH2 =
248
+ headingElement?.shadowRoot?.querySelectorAll("h2").length;
249
+ if (isH2) {
250
+ const tocItem = {
251
+ anchor: `#${headingElement.hash}`,
252
+ id: headingElement.id,
253
+ label: headingElement.title
254
+ };
255
+ tocOptions.push(tocItem);
256
+ this.tocOptionIdsSet.add(headingElement.id);
257
+ }
253
258
  }
254
259
 
255
260
  this._tocOptions = tocOptions;
@@ -300,7 +305,10 @@ export default class ContentLayout extends LightningElement {
300
305
  }
301
306
 
302
307
  private assignElementId(id: string): void {
303
- this.tocValue = id;
308
+ // Change toc(RNB) highlight only for H2
309
+ if (this.tocOptionIdsSet.has(id)) {
310
+ this.tocValue = id;
311
+ }
304
312
  }
305
313
 
306
314
  private dispatchHighlightChange(term: string): void {
@@ -2,6 +2,10 @@
2
2
  <dx-brand-theme-provider brand={brand}>
3
3
  <header class={className}>
4
4
  <dx-skip-nav-link></dx-skip-nav-link>
5
+ <dx-banner
6
+ if:true={showBanner}
7
+ banner-markup={bannerMarkup}
8
+ ></dx-banner>
5
9
  <div class="header_l1">
6
10
  <div if:true={showMenuButton} class="nav_menu-ctas">
7
11
  <dx-button
@@ -38,16 +42,19 @@
38
42
  class="header-tbid-login"
39
43
  onclick={closeMobileNavMenu}
40
44
  >
41
- <dw-tbid-login-menu></dw-tbid-login-menu>
45
+ <dw-tbid-login-menu
46
+ tbid-api-base-url={tbidApiBaseUrl}
47
+ tbid-base-url={tbidBaseUrl}
48
+ ></dw-tbid-login-menu>
42
49
  </div>
43
50
  <div if:true={showSignup} class="header-login-signup">
44
51
  <dx-button
45
- aria-label="Sign Up For Salesforce Developer Edition"
52
+ aria-label="Browse Trials"
46
53
  size="small"
47
54
  href={signupLink}
48
55
  onclick={handleSignUpClick}
49
56
  >
50
- Sign Up
57
+ Browse Trials
51
58
  </dx-button>
52
59
  </div>
53
60
  <dx-header-mobile-nav-menu
@@ -80,7 +87,7 @@
80
87
  symbol={brand}
81
88
  size="xlarge"
82
89
  ></dx-icon>
83
- <span class="subtitle dx-text-heading-4">
90
+ <span class="subtitle dx-text-display-6">
84
91
  {subtitle}
85
92
  </span>
86
93
  </a>
@@ -143,6 +150,7 @@
143
150
  aria-label={bailLabel}
144
151
  class="header_bail-link"
145
152
  href={bailHref}
153
+ onclick={handleBailClick}
146
154
  variant="tertiary"
147
155
  icon-symbol="new_window"
148
156
  target="_blank"
@@ -4,6 +4,7 @@ import type { OptionWithNested, OptionWithLink } from "typings/custom";
4
4
  import { HeaderBase } from "dxBaseElements/headerBase";
5
5
  import { toJson } from "dxUtils/normalizers";
6
6
  import get from "lodash.get";
7
+ import { track } from "dxUtils/analytics";
7
8
 
8
9
  const TABLET_MATCH = "980px";
9
10
  const MOBILE_MATCH = "880px";
@@ -143,4 +144,27 @@ export default class Header extends HeaderBase {
143
144
  this._language = detail;
144
145
  this.dispatchEvent(new CustomEvent("langchange", { detail }));
145
146
  }
147
+
148
+ private handleBailClick(event: Event) {
149
+ const payload = {
150
+ click_text: "pdf",
151
+ click_url: this.bailHref,
152
+ element_title: "pdf",
153
+ element_type: "link",
154
+ content_category: "download"
155
+ };
156
+ track(event.target!, "custEv_pdfDownload", {
157
+ ...payload,
158
+ file_name: this.getFilename(this.bailHref!),
159
+ file_extension: "pdf"
160
+ });
161
+
162
+ track(event.target!, "custEv_linkClick", {
163
+ ...payload
164
+ });
165
+ }
166
+
167
+ private getFilename = function (path: string) {
168
+ return path.substring(path.lastIndexOf("/") + 1);
169
+ };
146
170
  }
@@ -1,54 +1,33 @@
1
- h1,
2
- h2,
3
- h3,
4
- h4,
5
- h5,
6
- h6 {
7
- color: var(--dx-g-blue-vibrant-20);
8
- font-family: var(--dx-g-font-display);
9
- font-weight: var(--dx-g-font-demi);
10
- }
1
+ @import "dxHelpers/text";
11
2
 
12
- .display-3 {
13
- margin: 0 0 24px 0;
14
- font-size: var(--dx-g-text-3xl);
15
- letter-spacing: -0.85px;
16
- line-height: var(--dx-g-text-4xl);
3
+ h1 {
4
+ margin: var(--dx-g-spacing-2xl) 0 var(--dx-g-spacing-md) 0;
17
5
  }
18
6
 
19
- .display-3 doc-heading-content {
20
- --doc-c-heading-anchor-button-bottom: 8px;
7
+ h1 doc-heading-content {
8
+ --doc-c-heading-anchor-button-bottom: 9.5px;
21
9
  }
22
10
 
23
- .display-4 {
24
- margin: var(--dx-g-spacing-xl) 0 var(--dx-g-spacing-md) 0;
25
- font-size: var(--dx-g-text-xl);
26
- letter-spacing: -0.1px;
27
- line-height: var(--dx-g-spacing-lg);
11
+ h2 {
12
+ margin: var(--dx-g-spacing-2xl) 0 var(--dx-g-spacing-md) 0;
28
13
  }
29
14
 
30
- .display-4 doc-heading-content {
31
- --doc-c-heading-anchor-button-bottom: -3px;
15
+ h2 doc-heading-content {
16
+ --doc-c-heading-anchor-button-bottom: 3px;
32
17
  }
33
18
 
34
- .display-5 {
35
- margin: var(--dx-g-spacing-lg) 0 var(--dx-g-spacing-md) 0;
36
- font-size: var(--dx-g-text-lg);
37
- letter-spacing: -0.1px;
38
- line-height: var(--dx-g-spacing-lg);
19
+ h3 {
20
+ margin: var(--dx-g-spacing-xl) 0 var(--dx-g-spacing-md) 0;
39
21
  }
40
22
 
41
- .display-5 doc-heading-content {
42
- --doc-c-heading-anchor-button-bottom: -4px;
23
+ h3 doc-heading-content {
24
+ --doc-c-heading-anchor-button-bottom: -0.5px;
43
25
  }
44
26
 
45
- .display-6 {
46
- margin: var(--dx-g-spacing-md) 0 var(--dx-g-spacing-sm) 0;
47
- font-size: var(--dx-g-text-base);
48
- letter-spacing: 0;
49
- line-height: var(--dx-g-spacing-mlg);
27
+ h4 {
28
+ margin: var(--dx-g-spacing-lg) 0 var(--dx-g-spacing-sm) 0;
50
29
  }
51
30
 
52
- .display-6 doc-heading-content {
31
+ h4 doc-heading-content {
53
32
  --doc-c-heading-anchor-button-bottom: -6px;
54
33
  }
@@ -1,16 +1,16 @@
1
1
  import { LightningElement, api } from "lwc";
2
2
 
3
- export const displayLevels = ["3", "4", "5", "6"];
4
-
5
- export const ariaLevels = ["1", "2", "3", "4"];
6
-
7
3
  export const ariaDisplayLevels: { [key: string]: string } = {
8
- "1": "3",
9
- "2": "4",
10
- "3": "5",
11
- "4": "6"
4
+ "1": "4",
5
+ "2": "5",
6
+ "3": "6",
7
+ "4": "8"
12
8
  };
13
9
 
10
+ export const ariaLevels = Object.keys(ariaDisplayLevels);
11
+
12
+ export const displayLevels = Object.values(ariaDisplayLevels);
13
+
14
14
  export default class Heading extends LightningElement {
15
15
  @api title: string = "";
16
16
  @api hash: string | null = null;
@@ -60,6 +60,6 @@ export default class Heading extends LightningElement {
60
60
  }
61
61
 
62
62
  private get className(): string {
63
- return `display-${this.displayLevel}`;
63
+ return `dx-text-display-${this.displayLevel}`;
64
64
  }
65
65
  }
@@ -43,8 +43,9 @@ export default class Phase extends LightningElement {
43
43
  }
44
44
 
45
45
  renderedCallback() {
46
- const phaseBodyContainer =
47
- this.template.querySelector(".doc-phase-body");
46
+ const phaseBodyContainer = this.template.querySelector(
47
+ ".doc-phase-body"
48
+ );
48
49
  if (phaseBodyContainer && this.docPhaseInfo) {
49
50
  // eslint-disable-next-line @lwc/lwc/no-inner-html
50
51
  phaseBodyContainer.innerHTML = this.docPhaseInfo.body;
@@ -0,0 +1,20 @@
1
+ <template lwc:render-mode="light">
2
+ <dx-hr no-padding spacing="md"></dx-hr>
3
+ <div class="survey-container">
4
+ <div class="text-container">
5
+ <b>DID THIS ARTICLE SOLVE YOUR ISSUE?</b>
6
+ <br />
7
+ Let us know so we can improve!
8
+ </div>
9
+ <div class="btn-container">
10
+ <dx-button
11
+ variant="secondary"
12
+ aria-label="Share Your Feedback"
13
+ onclick={openSurvey}
14
+ >
15
+ Share your feedback
16
+ </dx-button>
17
+ </div>
18
+ </div>
19
+ <dx-hr no-padding spacing="md"></dx-hr>
20
+ </template>
@@ -0,0 +1,16 @@
1
+ :host .survey-container {
2
+ align-items: center;
3
+ align-content: center;
4
+ width: 100%;
5
+ display: flex;
6
+ flex-direction: row;
7
+ margin: 20px 0 20px 0;
8
+ }
9
+
10
+ dx-hr:first-of-type::part(hr) {
11
+ margin-top: var(--dx-g-spacing-2xl);
12
+ }
13
+
14
+ .text-container {
15
+ width: 100%;
16
+ }
@@ -0,0 +1,16 @@
1
+ import { LightningElement } from "lwc";
2
+ import cx from "classnames";
3
+
4
+ declare const Sprig: (eventType: string, eventNme: string) => void;
5
+
6
+ export default class Phase extends LightningElement {
7
+ static renderMode = "light";
8
+
9
+ get className() {
10
+ return cx("container");
11
+ }
12
+
13
+ openSurvey() {
14
+ Sprig("track", "ProvideFeedback");
15
+ }
16
+ }
@@ -96,9 +96,7 @@
96
96
  <a
97
97
  href={child5.a_attr.href}
98
98
  data-id={child5.id}
99
- class="
100
- nav1
101
- "
99
+ class="nav1"
102
100
  onclick={handleNavClick}
103
101
  >
104
102
  {child5.text}
@@ -40,9 +40,9 @@ export default class Toolbar extends LightningElement {
40
40
  "select[name=languages]"
41
41
  ) as HTMLSelectElement;
42
42
  if (languageEl) {
43
- const languageValue = (
44
- languageEl[languageEl.selectedIndex] as HTMLOptionElement
45
- ).value;
43
+ const languageValue = (languageEl[
44
+ languageEl.selectedIndex
45
+ ] as HTMLOptionElement).value;
46
46
  this.dispatchEvent(
47
47
  new CustomEvent("languageselected", {
48
48
  detail: {
@@ -61,9 +61,9 @@ export default class Toolbar extends LightningElement {
61
61
  "select[name=versions]"
62
62
  ) as HTMLSelectElement;
63
63
  if (versionEl) {
64
- const versionValue = (
65
- versionEl[versionEl.selectedIndex] as HTMLOptionElement
66
- ).value;
64
+ const versionValue = (versionEl[
65
+ versionEl.selectedIndex
66
+ ] as HTMLOptionElement).value;
67
67
  this.dispatchEvent(
68
68
  new CustomEvent("versionselected", {
69
69
  detail: {
@@ -87,7 +87,9 @@ export class FetchContent {
87
87
  return json;
88
88
  }
89
89
 
90
- private normalizeToc(apiToc: Array<ApiNavItem>): {
90
+ private normalizeToc(
91
+ apiToc: Array<ApiNavItem>
92
+ ): {
91
93
  tocMap: { [key: string]: TreeNode };
92
94
  normalizedToc: Array<TreeNode>;
93
95
  } {
@@ -14,8 +14,8 @@
14
14
  <div slot="sidebar-header" class="document-pickers">
15
15
  <dx-dropdown
16
16
  data-type="version"
17
- suppress-gtm-nav-headings
18
- analytics-event={analyticsEvent}
17
+ analytics-event="custEv_ctaLinkClick"
18
+ analytics-payload={ANALYTICS_PAYLOAD}
19
19
  options={versionOptions}
20
20
  value={version.id}
21
21
  width="290px"
@@ -34,7 +34,6 @@
34
34
  docs-data={docContent}
35
35
  page-reference={pageReference}
36
36
  onnavclick={handleNavClick}
37
- style={docContentStyle}
38
37
  ></doc-content>
39
38
  </doc-content-layout>
40
39
  </template>
@@ -63,7 +63,6 @@ export default class DocXmlContent extends LightningElementWithState<{
63
63
  private sidebarContent: Array<TreeNode> = null;
64
64
  private version: DocVersion = null;
65
65
  private docTitle = "";
66
- private analyticsEvent = "custEv_ctaLinkClick";
67
66
  private _pathName = "";
68
67
  private _pageHeader?: Header;
69
68
  private listenerAttached = false;
@@ -281,8 +280,15 @@ export default class DocXmlContent extends LightningElementWithState<{
281
280
  );
282
281
  }
283
282
 
284
- private handlePopState = (): void =>
285
- this.updatePageReference(this.getReferenceFromUrl());
283
+ private get ANALYTICS_PAYLOAD() {
284
+ return {
285
+ element_title: "version picker",
286
+ content_category: "cta"
287
+ };
288
+ }
289
+
290
+ private handlePopState = (event: PopStateEvent): void =>
291
+ this.updatePageReference(this.getReferenceFromUrl(), event);
286
292
 
287
293
  handleSelect(event: CustomEvent<{ name: string }>): void {
288
294
  event.stopPropagation();
@@ -327,7 +333,10 @@ export default class DocXmlContent extends LightningElementWithState<{
327
333
  this.fetchDocument();
328
334
  };
329
335
 
330
- updatePageReference(newPageReference: PageReference): void {
336
+ updatePageReference(
337
+ newPageReference: PageReference,
338
+ event: PopStateEvent | undefined = undefined
339
+ ): void {
331
340
  this.pageReference.hash = newPageReference.hash;
332
341
  this.pageReference.search = newPageReference.search;
333
342
 
@@ -344,7 +353,10 @@ export default class DocXmlContent extends LightningElementWithState<{
344
353
  }
345
354
 
346
355
  this.fetchContent()
347
- .then(() => this.buildBreadcrumbs())
356
+ .then(() => {
357
+ this.buildBreadcrumbs();
358
+ document.body.scrollTop = event?.state?.scroll?.value || 0;
359
+ })
348
360
  .catch(handleContentError);
349
361
  }
350
362
 
@@ -580,10 +592,6 @@ export default class DocXmlContent extends LightningElementWithState<{
580
592
  return this.breadcrumbs && this.breadcrumbs.length > 1;
581
593
  }
582
594
 
583
- get docContentStyle(): string {
584
- return this.showBreadcrumbs ? "" : "margin-top: 48px";
585
- }
586
-
587
595
  private buildBreadcrumbs(): void {
588
596
  const { contentDocumentId } = this.pageReference;
589
597
  if (!contentDocumentId) {
@@ -615,6 +623,20 @@ export default class DocXmlContent extends LightningElementWithState<{
615
623
  return [item];
616
624
  }
617
625
 
626
+ // This method take docId and drops the version from the docId.
627
+ // Example:
628
+ // Takes input string: docId = "atlas.en-us.238.0.b2b_b2c_comm_dev.meta"
629
+ // Output string: filteredDocId = "atlas.en-us.b2b_b2c_comm_dev.meta"
630
+ dropVersionFromDocId(docId: string): string {
631
+ if (!this.version?.id) {
632
+ return docId;
633
+ }
634
+
635
+ const curVersion = this.version.id + ".";
636
+ const filteredDocId = docId.replace(curVersion, "");
637
+ return filteredDocId;
638
+ }
639
+
618
640
  addMetatags(): void {
619
641
  const div = document.createElement("div");
620
642
  div.innerHTML = this.docContent;
@@ -646,14 +668,43 @@ export default class DocXmlContent extends LightningElementWithState<{
646
668
  'link[rel="canonical"]'
647
669
  );
648
670
  if (metadescription) {
671
+ const copyPageReference = { ...this.pageReference };
672
+ copyPageReference.docId = copyPageReference.docId
673
+ ? this.dropVersionFromDocId(copyPageReference.docId)
674
+ : copyPageReference.docId;
649
675
  metadescription.setAttribute(
650
676
  "href",
651
677
  window.location.protocol +
652
678
  "//" +
653
679
  window.location.host +
654
- this.pageReferenceToString(this.pageReference)
680
+ this.pageReferenceToString(copyPageReference)
655
681
  );
656
682
  }
657
683
  }
684
+
685
+ this.addNoIndexMetaForOlderDocVersions();
686
+ }
687
+
688
+ /**
689
+ * Method adds noindex, follow meta tag to the older Couch DB doc pages.
690
+ * Fixes W-12547462.
691
+ */
692
+ private addNoIndexMetaForOlderDocVersions() {
693
+ // eslint-disable-next-line @lwc/lwc/no-document-query
694
+ const headTag = document.getElementsByTagName("head");
695
+ // this checks if the selected version is not the latest version,
696
+ // then it adds the noindex, follow meta tag to the older version pages.
697
+ // assumption is that first version in the availableVersions array is the latest.
698
+ if (
699
+ headTag.length &&
700
+ this.version &&
701
+ this.availableVersions.length &&
702
+ this.version.id !== this.availableVersions[0].id
703
+ ) {
704
+ const robotsMeta = document.createElement("meta");
705
+ robotsMeta.setAttribute("name", "robots");
706
+ robotsMeta.setAttribute("content", "noindex, follow");
707
+ headTag[0].appendChild(robotsMeta);
708
+ }
658
709
  }
659
710
  }
package/LICENSE DELETED
@@ -1,12 +0,0 @@
1
- Copyright (c) 2020, Salesforce.com, Inc.
2
- All rights reserved.
3
-
4
- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
-
6
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
-
8
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
-
10
- * Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11
-
12
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.