@salesforcedevs/dx-components 1.3.218 → 1.3.219-alpha.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforcedevs/dx-components",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.219-alpha.2",
|
|
4
4
|
"description": "DX Lightning web components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -43,6 +43,5 @@
|
|
|
43
43
|
},
|
|
44
44
|
"volta": {
|
|
45
45
|
"node": "16.19.1"
|
|
46
|
-
}
|
|
47
|
-
"gitHead": "4da04a9390fd75ee83751992b259acb60a47fd98"
|
|
46
|
+
}
|
|
48
47
|
}
|
|
@@ -274,18 +274,14 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
.dx-result-title {
|
|
277
|
+
/* override the default Coveo style */
|
|
278
|
+
color: var(--dx-g-blue-vibrant-20) !important;
|
|
277
279
|
font-family: var(--dx-g-font-display);
|
|
278
280
|
font-size: var(--dx-g-text-lg);
|
|
279
281
|
margin: 0;
|
|
280
282
|
margin-bottom: var(--dx-g-spacing-sm);
|
|
281
283
|
}
|
|
282
284
|
|
|
283
|
-
.CoveoResultLink,
|
|
284
|
-
a.CoveoResultLink,
|
|
285
|
-
.CoveoResult a.CoveoResultLink {
|
|
286
|
-
color: var(--dx-g-blue-vibrant-20);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
285
|
.dx-result-excerpt {
|
|
290
286
|
color: var(--dx-g-gray-10);
|
|
291
287
|
font-size: 14px;
|
|
@@ -422,11 +418,17 @@ a.CoveoResultLink,
|
|
|
422
418
|
width: fit-content;
|
|
423
419
|
}
|
|
424
420
|
|
|
425
|
-
.dx-
|
|
426
|
-
display:
|
|
421
|
+
.dx-result-info {
|
|
422
|
+
display: flex;
|
|
423
|
+
gap: 12px;
|
|
427
424
|
margin-bottom: var(--dx-g-spacing-smd);
|
|
428
425
|
}
|
|
429
426
|
|
|
427
|
+
.breadcrumb {
|
|
428
|
+
color: #555;
|
|
429
|
+
font-size: var(--dx-g-text-xs);
|
|
430
|
+
}
|
|
431
|
+
|
|
430
432
|
.no-results {
|
|
431
433
|
display: flex;
|
|
432
434
|
justify-content: center;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
CONTENT_TYPE_ICONS
|
|
7
7
|
} from "dxConstants/contentTypes";
|
|
8
8
|
import { getContentTypeColorVariables } from "dxUtils/contentTypes";
|
|
9
|
+
import { pollUntil } from "dxUtils/async";
|
|
9
10
|
|
|
10
11
|
interface CoveoSearch {
|
|
11
12
|
state: typeof CoveoSDK.state;
|
|
@@ -40,35 +41,74 @@ const resultsTemplatesInnerHtml = `
|
|
|
40
41
|
<script
|
|
41
42
|
id="myDocumentResultTemplate"
|
|
42
43
|
class="result-template"
|
|
43
|
-
type="text/
|
|
44
|
+
type="text/underscore"
|
|
44
45
|
data-field-publicurl=""
|
|
45
46
|
>
|
|
46
47
|
<div class="dx-result">
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class="
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
<div class="dx-result-info">
|
|
49
|
+
<span class="CoveoFieldValue" data-field="@content_type" data-helper="badge" data-html-value="true"></span>
|
|
50
|
+
<% if (!raw.breadcrumbs && !raw.metabreadcrumbs) { %>
|
|
51
|
+
<span class="CoveoFieldValue" data-field="@uri" data-helper="uriBreadcrumbs" data-html-value="true"></span>
|
|
52
|
+
<% } else { %>
|
|
53
|
+
<% if (raw.uri.includes('/references/')) { %>
|
|
54
|
+
<span class="CoveoFieldValue" data-field="@metabreadcrumbs" data-helper="metabreadcrumbs" data-html-value="true"></span>
|
|
55
|
+
<% } else { %>
|
|
56
|
+
<span class="CoveoFieldValue" data-field="@breadcrumbs" data-helper="breadcrumbs" data-html-value="true"></span>
|
|
57
|
+
<% } %>
|
|
58
|
+
<% } %>
|
|
59
|
+
</div>
|
|
60
|
+
<a
|
|
61
|
+
href="<%= raw.uri %>"
|
|
62
|
+
class="dx-result-title"
|
|
63
|
+
>
|
|
64
|
+
<%= title %>
|
|
65
|
+
<% if (!raw.uri.includes('developer.salesforce.com') && !raw.uri.includes('developer-website-s.herokuapp.com')) { %>
|
|
66
|
+
<svg xmlns="http://www.w3.org/2000/svg" style="display: inline; vertical-align: baseline;" fill="var(--dx-g-blue-vibrant-20)" width="20" height="20" part="svg" aria-hidden="true"><use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#new_window"></use></svg>
|
|
67
|
+
<% } %>
|
|
68
|
+
</a>
|
|
54
69
|
<p class="dx-result-excerpt CoveoExcerpt"></p>
|
|
55
70
|
</div>
|
|
56
71
|
</script>
|
|
57
72
|
<script
|
|
58
73
|
id="myDefaultResultTemplate"
|
|
59
74
|
class="result-template"
|
|
60
|
-
type="text/
|
|
75
|
+
type="text/underscore"
|
|
61
76
|
>
|
|
62
77
|
<div class="dx-result">
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
<div class="dx-result-info">
|
|
79
|
+
<span class="CoveoFieldValue" data-field="@content_type" data-helper="badge" data-html-value="true"></span>
|
|
80
|
+
<% if (!raw.breadcrumbs && !raw.metabreadcrumbs) { %>
|
|
81
|
+
<span class="CoveoFieldValue" data-field="@uri" data-helper="uriBreadcrumbs" data-html-value="true"></span>
|
|
82
|
+
<% } else { %>
|
|
83
|
+
<% if (raw.uri.includes('/references/')) { %>
|
|
84
|
+
<span class="CoveoFieldValue" data-field="@metabreadcrumbs" data-helper="metabreadcrumbs" data-html-value="true"></span>
|
|
85
|
+
<% } else { %>
|
|
86
|
+
<span class="CoveoFieldValue" data-field="@breadcrumbs" data-helper="breadcrumbs" data-html-value="true"></span>
|
|
87
|
+
<% } %>
|
|
88
|
+
<% } %>
|
|
89
|
+
</div>
|
|
90
|
+
<a
|
|
91
|
+
href="<%= raw.uri %>"
|
|
92
|
+
class="dx-result-title"
|
|
93
|
+
>
|
|
94
|
+
<%= uri %>
|
|
95
|
+
<% if (!raw.uri.includes('developer.salesforce.com') && !raw.uri.includes('developer-website-s.herokuapp.com')) { %>
|
|
96
|
+
<svg xmlns="http://www.w3.org/2000/svg" style="display: inline; vertical-align: baseline;" fill="var(--dx-g-blue-vibrant-20)" width="20" height="20" part="svg" aria-hidden="true"><use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#new_window"></use></svg>
|
|
97
|
+
<% } %>
|
|
98
|
+
</a>
|
|
67
99
|
<p class="dx-result-excerpt CoveoExcerpt"></p>
|
|
68
100
|
</div>
|
|
69
101
|
</script>
|
|
70
102
|
`;
|
|
71
103
|
|
|
104
|
+
const isInternalDomain = (domain: string) =>
|
|
105
|
+
domain === "developer.salesforce.com" ||
|
|
106
|
+
domain === "developer-website-s.herokuapp.com";
|
|
107
|
+
|
|
108
|
+
const isTrailheadDomain = (domain: string) =>
|
|
109
|
+
domain === "trailhead.salesforce.com" ||
|
|
110
|
+
domain === "dev.trailhead.salesforce.com";
|
|
111
|
+
|
|
72
112
|
const buildTemplateHelperBadge = (value: keyof typeof CONTENT_TYPE_LABELS) => {
|
|
73
113
|
const style = getContentTypeColorVariables(value);
|
|
74
114
|
const label = CONTENT_TYPE_LABELS[value];
|
|
@@ -88,6 +128,107 @@ const buildTemplateHelperBadge = (value: keyof typeof CONTENT_TYPE_LABELS) => {
|
|
|
88
128
|
`;
|
|
89
129
|
};
|
|
90
130
|
|
|
131
|
+
const processParts = (parts: string[], internalFlag = false) => {
|
|
132
|
+
// filter /docs/ breadcrumb item from internal domains
|
|
133
|
+
const filterFn = internalFlag
|
|
134
|
+
? (part: string) => part !== "docs"
|
|
135
|
+
: (part: string) => part;
|
|
136
|
+
|
|
137
|
+
return parts.filter(filterFn).map((part) => {
|
|
138
|
+
// Remove special characters & .htm/.xml extension
|
|
139
|
+
part = part
|
|
140
|
+
.replace(/_/g, " ")
|
|
141
|
+
.replace(/-/g, " ")
|
|
142
|
+
.replace(/.html*/g, " ")
|
|
143
|
+
.replace(/.xml/g, " ")
|
|
144
|
+
.replace(/b2c/g, "B2C");
|
|
145
|
+
|
|
146
|
+
// Capitalize first letter of each word
|
|
147
|
+
part = part.replace(/\w\S*/g, (w) => {
|
|
148
|
+
return w.replace(/^\w/, (c) => c.toUpperCase());
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return `<span class="breadcrumb-item">${decodeURI(part)}</span>`;
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const buildTemplateHelperUriBreadcrumbs = (value: string) => {
|
|
156
|
+
const url = new URL(value);
|
|
157
|
+
|
|
158
|
+
// exclude youtube links from breadcrumbs
|
|
159
|
+
const hostnamePattern = /^((www\.)?(youtube\.com|youtu\.be))$/;
|
|
160
|
+
|
|
161
|
+
// we don't want to show atlas docs because the url structure is mad ugly
|
|
162
|
+
if (hostnamePattern.test(url.hostname) || url.pathname.includes("atlas.")) {
|
|
163
|
+
return "";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let parts = url.pathname.split("/").filter((part) => part !== "");
|
|
167
|
+
|
|
168
|
+
// Remove language prefix from trailhead URLs
|
|
169
|
+
if (isTrailheadDomain(url.hostname)) {
|
|
170
|
+
parts = parts
|
|
171
|
+
.slice(1)
|
|
172
|
+
.filter((part) => part !== "content" && part !== "learn");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const breadcrumbs = processParts(parts, isInternalDomain(url.hostname));
|
|
176
|
+
|
|
177
|
+
if (!isInternalDomain(url.hostname)) {
|
|
178
|
+
// Remove the first breadcrumb item if it's an internal domain (i.e drop developer.salesforce.com from developer.salesforce.com / B2C Commerce / Open Commerce API / Filtering)
|
|
179
|
+
breadcrumbs.unshift(
|
|
180
|
+
`<span class="breadcrumb-item">${url.hostname}</span>`
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return `
|
|
184
|
+
<span class="breadcrumb">
|
|
185
|
+
${breadcrumbs.join(" / ")}
|
|
186
|
+
</span>
|
|
187
|
+
`;
|
|
188
|
+
} else if (breadcrumbs.length === 1) {
|
|
189
|
+
// Hide breadcrumbs if there is only one breadcrumb item
|
|
190
|
+
return "";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// remove the last breadcrumb item (the search result title makes it redundant)
|
|
194
|
+
breadcrumbs.pop();
|
|
195
|
+
|
|
196
|
+
return `<span class="breadcrumb">/ ${breadcrumbs.join(" / ")} /</span>`;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const buildTemplateHelperBreadcrumbs = (value: string) => {
|
|
200
|
+
const parts = value.split("/").filter((part) => part !== "");
|
|
201
|
+
|
|
202
|
+
// Don't show breadcrumbs if there's only one part
|
|
203
|
+
if (parts.length === 1) {
|
|
204
|
+
return "";
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const breadcrumbs = processParts(parts);
|
|
208
|
+
|
|
209
|
+
// remove last breadcrumb item
|
|
210
|
+
breadcrumbs.pop();
|
|
211
|
+
|
|
212
|
+
return `
|
|
213
|
+
<span class="breadcrumb">/ ${breadcrumbs.join(" / ")} /</span>
|
|
214
|
+
`;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const buildTemplateHelperMetaBreadcrumbs = (value: string) => {
|
|
218
|
+
const parts = value.split("/").filter((part) => part !== "");
|
|
219
|
+
|
|
220
|
+
// Don't show breadcrumbs if there's only one part
|
|
221
|
+
if (parts.length === 1) {
|
|
222
|
+
return "";
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const breadcrumbs = processParts(parts);
|
|
226
|
+
|
|
227
|
+
return `
|
|
228
|
+
<span class="breadcrumb">/ ${breadcrumbs.join(" / ")}</span>
|
|
229
|
+
`;
|
|
230
|
+
};
|
|
231
|
+
|
|
91
232
|
// @ts-ignore Dark Magic (TM) we are overriding the 'title' field with a custom getter. We should really stop doing this.
|
|
92
233
|
export default class SearchResults extends LightningElement {
|
|
93
234
|
@api coveoOrganizationId!: string;
|
|
@@ -139,9 +280,12 @@ export default class SearchResults extends LightningElement {
|
|
|
139
280
|
BreadcrumbManager.clearBreadcrumbs();
|
|
140
281
|
}
|
|
141
282
|
|
|
142
|
-
private currentPage: number =
|
|
283
|
+
private currentPage: number = 25;
|
|
143
284
|
private totalPages: number = 1;
|
|
144
285
|
|
|
286
|
+
private originalBreadcrumbs: string[] = [];
|
|
287
|
+
private initialWindowWidth = window.innerWidth;
|
|
288
|
+
|
|
145
289
|
private goToPage(e: CustomEvent) {
|
|
146
290
|
const page = e.detail;
|
|
147
291
|
const Pager = Coveo.get(
|
|
@@ -179,6 +323,7 @@ export default class SearchResults extends LightningElement {
|
|
|
179
323
|
if (Coveo.state(this.root!, "q") === "") {
|
|
180
324
|
Coveo.state(this.root!, "sort", "date descending");
|
|
181
325
|
}
|
|
326
|
+
|
|
182
327
|
this.isInitialized = true;
|
|
183
328
|
}
|
|
184
329
|
);
|
|
@@ -204,6 +349,110 @@ export default class SearchResults extends LightningElement {
|
|
|
204
349
|
|
|
205
350
|
this.trackSearchResults(event, this.query, this.totalResults);
|
|
206
351
|
});
|
|
352
|
+
|
|
353
|
+
Coveo.$$(root).on(Coveo.QueryEvents.deferredQuerySuccess, async () => {
|
|
354
|
+
// wait specified time to ensure breadcrumbs are rendered before processing them
|
|
355
|
+
await pollUntil(
|
|
356
|
+
() => {
|
|
357
|
+
const coveoResults =
|
|
358
|
+
this.root!.querySelector(".CoveoResult");
|
|
359
|
+
|
|
360
|
+
return Boolean(coveoResults);
|
|
361
|
+
},
|
|
362
|
+
20,
|
|
363
|
+
1000
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
this.processBreadcrumbs(this.root!);
|
|
367
|
+
|
|
368
|
+
window.onresize = () => this.processBreadcrumbs(root);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Checks if text is wrapping by comparing it with an element's text that doesn't wrap
|
|
373
|
+
private isTextWrapping = (
|
|
374
|
+
elementOne: HTMLElement,
|
|
375
|
+
elementTwo: HTMLElement
|
|
376
|
+
) => elementOne.offsetHeight > elementTwo.offsetHeight;
|
|
377
|
+
|
|
378
|
+
private truncateBreadcrumbText = (breadcrumbItems: HTMLElement[]) => {
|
|
379
|
+
breadcrumbItems.forEach((breadcrumbItem: HTMLElement) => {
|
|
380
|
+
const breadcrumbItemText = breadcrumbItem.textContent!;
|
|
381
|
+
if (breadcrumbItemText.length > 30) {
|
|
382
|
+
breadcrumbItem.textContent = `${breadcrumbItemText.substring(
|
|
383
|
+
0,
|
|
384
|
+
30
|
|
385
|
+
)}...`;
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
private addBreadcrumbEllipsis = (
|
|
391
|
+
breadcrumbItems: HTMLElement[],
|
|
392
|
+
breadcrumb: HTMLElement
|
|
393
|
+
) => {
|
|
394
|
+
for (let i = 1; i < breadcrumbItems.length; i++) {
|
|
395
|
+
if (this.isTextWrapping(breadcrumb, breadcrumbItems[0])) {
|
|
396
|
+
breadcrumbItems[i].textContent = "...";
|
|
397
|
+
} else {
|
|
398
|
+
break; // Exit the loop if the breadcrumb is no longer overflowing
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
private formatBreadcrumbs = (breadcrumbs: HTMLElement[]) => {
|
|
404
|
+
breadcrumbs?.forEach((breadcrumb: HTMLElement) => {
|
|
405
|
+
// Get all breadcrumb items that are separated by '/'
|
|
406
|
+
const breadcrumbItems: any =
|
|
407
|
+
breadcrumb.querySelectorAll(".breadcrumb-item");
|
|
408
|
+
|
|
409
|
+
// Check if the breadcrumb is overflowing by comparing it's height to the height of the first breadcrumb item
|
|
410
|
+
if (this.isTextWrapping(breadcrumb, breadcrumbItems[0])) {
|
|
411
|
+
// it is overflowing, so we need to truncate long titles to 30 characters
|
|
412
|
+
this.truncateBreadcrumbText(breadcrumbItems);
|
|
413
|
+
|
|
414
|
+
// Iteratively check if the breadcrumb is still overflowing and replace text with '...' starting from the second breadcrumb item
|
|
415
|
+
this.addBreadcrumbEllipsis(breadcrumbItems, breadcrumb);
|
|
416
|
+
|
|
417
|
+
// After processing all breadcrumb items, if it's still overflowing, hide the breadcrumb element
|
|
418
|
+
if (this.isTextWrapping(breadcrumb, breadcrumbItems[0])) {
|
|
419
|
+
breadcrumb.style.display = "none";
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
private restoreBreadcrumbs = (breadcrumbs: HTMLElement[]) => {
|
|
426
|
+
breadcrumbs.forEach((breadcrumb: HTMLElement, index: number) => {
|
|
427
|
+
// eslint-disable-next-line @lwc/lwc/no-inner-html
|
|
428
|
+
breadcrumb.innerHTML = this.originalBreadcrumbs[index];
|
|
429
|
+
});
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
private windowSizeIncreased = () =>
|
|
433
|
+
window.innerWidth > this.initialWindowWidth;
|
|
434
|
+
|
|
435
|
+
private processBreadcrumbs(root: HTMLElement) {
|
|
436
|
+
// Get all breadcrumbs from search results
|
|
437
|
+
const breadcrumbs = Array.from(
|
|
438
|
+
root.querySelectorAll(".breadcrumb")
|
|
439
|
+
) as HTMLElement[];
|
|
440
|
+
|
|
441
|
+
if (this.originalBreadcrumbs.length === 0) {
|
|
442
|
+
this.originalBreadcrumbs = breadcrumbs.map(
|
|
443
|
+
(breadcrumb) => breadcrumb.innerHTML
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (this.windowSizeIncreased()) {
|
|
448
|
+
/*
|
|
449
|
+
Reset the breadcrumbs to their original state and process them again.
|
|
450
|
+
The additional space means we can replace ellipsis with full text.
|
|
451
|
+
*/
|
|
452
|
+
this.restoreBreadcrumbs(breadcrumbs);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
this.formatBreadcrumbs(breadcrumbs);
|
|
207
456
|
}
|
|
208
457
|
|
|
209
458
|
private initializeCoveo() {
|
|
@@ -212,6 +461,7 @@ export default class SearchResults extends LightningElement {
|
|
|
212
461
|
const resultsList = this.template.querySelector(".CoveoResultList");
|
|
213
462
|
|
|
214
463
|
if (resultsList) {
|
|
464
|
+
// eslint-disable-next-line @lwc/lwc/no-inner-html
|
|
215
465
|
resultsList.innerHTML = resultsTemplatesInnerHtml;
|
|
216
466
|
}
|
|
217
467
|
|
|
@@ -227,6 +477,21 @@ export default class SearchResults extends LightningElement {
|
|
|
227
477
|
buildTemplateHelperBadge
|
|
228
478
|
);
|
|
229
479
|
|
|
480
|
+
Coveo.TemplateHelpers.registerTemplateHelper(
|
|
481
|
+
"breadcrumbs",
|
|
482
|
+
buildTemplateHelperBreadcrumbs
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
Coveo.TemplateHelpers.registerTemplateHelper(
|
|
486
|
+
"metabreadcrumbs",
|
|
487
|
+
buildTemplateHelperMetaBreadcrumbs
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
Coveo.TemplateHelpers.registerTemplateHelper(
|
|
491
|
+
"uriBreadcrumbs",
|
|
492
|
+
buildTemplateHelperUriBreadcrumbs
|
|
493
|
+
);
|
|
494
|
+
|
|
230
495
|
Coveo.init(this.root);
|
|
231
496
|
}
|
|
232
497
|
|
|
@@ -235,6 +500,7 @@ export default class SearchResults extends LightningElement {
|
|
|
235
500
|
if (Object.prototype.hasOwnProperty.call(window, "Coveo")) {
|
|
236
501
|
this.initializeCoveo();
|
|
237
502
|
} else {
|
|
503
|
+
// eslint-disable-next-line @lwc/lwc/no-document-query
|
|
238
504
|
const script = document.querySelector("script.coveo-script");
|
|
239
505
|
script?.addEventListener("load", () => {
|
|
240
506
|
this.initializeCoveo();
|
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.
|