@odoo/o-spreadsheet 17.0.54 → 17.0.55
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/dist/o-spreadsheet.cjs.js +38 -15
- package/dist/o-spreadsheet.esm.js +38 -15
- package/dist/o-spreadsheet.iife.js +38 -15
- package/dist/o-spreadsheet.iife.min.js +75 -73
- package/dist/o_spreadsheet.xml +6 -3
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 17.0.
|
|
6
|
-
* @date 2025-03-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.0.55
|
|
6
|
+
* @date 2025-03-26T12:44:29.690Z
|
|
7
|
+
* @hash c3a4769
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -949,7 +949,10 @@ function rgbaStringToHex(color) {
|
|
|
949
949
|
}
|
|
950
950
|
else if (stringVals.length === 4) {
|
|
951
951
|
const alpha = parseFloat(stringVals.pop() || "1");
|
|
952
|
-
|
|
952
|
+
if (isNaN(alpha)) {
|
|
953
|
+
throw new Error("invalid alpha value");
|
|
954
|
+
}
|
|
955
|
+
alphaHex = Math.round(alpha * 255);
|
|
953
956
|
}
|
|
954
957
|
const vals = stringVals.map((val) => parseInt(val, 10));
|
|
955
958
|
if (alphaHex !== 255) {
|
|
@@ -8570,7 +8573,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
|
|
|
8570
8573
|
}
|
|
8571
8574
|
}
|
|
8572
8575
|
else if (dataSets.length === 1) {
|
|
8573
|
-
|
|
8576
|
+
const dataLength = getData(getters, dataSets[0]).length;
|
|
8577
|
+
for (let i = 0; i < dataLength; i++) {
|
|
8574
8578
|
labels.formattedValues.push("");
|
|
8575
8579
|
labels.values.push("");
|
|
8576
8580
|
}
|
|
@@ -30371,9 +30375,9 @@ const XLSX_CHART_TYPES = [
|
|
|
30371
30375
|
/** In XLSX color format (no #) */
|
|
30372
30376
|
const AUTO_COLOR = "000000";
|
|
30373
30377
|
const XLSX_ICONSET_MAP = {
|
|
30374
|
-
|
|
30378
|
+
arrows: "3Arrows",
|
|
30375
30379
|
smiley: "3Symbols",
|
|
30376
|
-
|
|
30380
|
+
dots: "3TrafficLights1",
|
|
30377
30381
|
};
|
|
30378
30382
|
const NAMESPACE = {
|
|
30379
30383
|
styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
|
@@ -30789,6 +30793,7 @@ const ICON_SET_CONVERSION_MAP = {
|
|
|
30789
30793
|
};
|
|
30790
30794
|
/** Map between legend position in XLSX file and human readable position */
|
|
30791
30795
|
const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
|
|
30796
|
+
none: "none",
|
|
30792
30797
|
b: "bottom",
|
|
30793
30798
|
t: "top",
|
|
30794
30799
|
l: "left",
|
|
@@ -33021,7 +33026,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
33021
33026
|
? "right"
|
|
33022
33027
|
: "left",
|
|
33023
33028
|
legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
|
|
33024
|
-
default: "
|
|
33029
|
+
default: "none",
|
|
33025
33030
|
}).asString()],
|
|
33026
33031
|
stacked: barChartGrouping === "stacked",
|
|
33027
33032
|
fontColor: "000000",
|
|
@@ -53482,7 +53487,9 @@ css /* scss */ `
|
|
|
53482
53487
|
margin-top: -1px;
|
|
53483
53488
|
border: 1px solid;
|
|
53484
53489
|
|
|
53485
|
-
|
|
53490
|
+
/* In readonly we always show the fx icon if the composer is empty, not matter the focus */
|
|
53491
|
+
.o-composer:empty:not(:focus):not(.active)::before,
|
|
53492
|
+
&.o-topbar-composer-readonly .o-composer:empty::before {
|
|
53486
53493
|
content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
|
|
53487
53494
|
position: relative;
|
|
53488
53495
|
top: 20%;
|
|
@@ -56483,10 +56490,14 @@ function addIconSetRule(cf, rule) {
|
|
|
56483
56490
|
continue;
|
|
56484
56491
|
}
|
|
56485
56492
|
const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
|
|
56493
|
+
const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
|
|
56494
|
+
if (isIconSetReversed(rule.icons)) {
|
|
56495
|
+
iconSetAttrs.push(["reverse", "1"]);
|
|
56496
|
+
}
|
|
56486
56497
|
conditionalFormats.push(escapeXml /*xml*/ `
|
|
56487
56498
|
<conditionalFormatting sqref="${range}">
|
|
56488
56499
|
<cfRule ${formatAttributes(ruleAttributes)}>
|
|
56489
|
-
<iconSet
|
|
56500
|
+
<iconSet ${formatAttributes(iconSetAttrs)}>
|
|
56490
56501
|
${joinXmlNodes(cfValueObjectNodes)}
|
|
56491
56502
|
</iconSet>
|
|
56492
56503
|
</cfRule>
|
|
@@ -56504,9 +56515,21 @@ function commonCfAttributes(cf) {
|
|
|
56504
56515
|
["stopIfTrue", cf.stopIfTrue ? 1 : 0],
|
|
56505
56516
|
];
|
|
56506
56517
|
}
|
|
56518
|
+
function isIconSetReversed(iconSet) {
|
|
56519
|
+
const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
|
|
56520
|
+
return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
|
|
56521
|
+
}
|
|
56507
56522
|
function getIconSet(iconSet) {
|
|
56508
|
-
return XLSX_ICONSET_MAP[
|
|
56509
|
-
|
|
56523
|
+
return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
|
|
56524
|
+
}
|
|
56525
|
+
/**
|
|
56526
|
+
* Partial detection based on "upper" point only.
|
|
56527
|
+
* We support any arbitrary icon in the set, while excel doesn't allow
|
|
56528
|
+
* mixing icons from different types.
|
|
56529
|
+
*/
|
|
56530
|
+
function detectIconsType(iconSet) {
|
|
56531
|
+
const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
|
|
56532
|
+
return type;
|
|
56510
56533
|
}
|
|
56511
56534
|
function thresholdAttributes(threshold, position) {
|
|
56512
56535
|
const type = getExcelThresholdType(threshold.type, position);
|
|
@@ -58082,6 +58105,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
58082
58105
|
exports.tokenize = tokenize;
|
|
58083
58106
|
|
|
58084
58107
|
|
|
58085
|
-
__info__.version = "17.0.
|
|
58086
|
-
__info__.date = "2025-03-
|
|
58087
|
-
__info__.hash = "
|
|
58108
|
+
__info__.version = "17.0.55";
|
|
58109
|
+
__info__.date = "2025-03-26T12:44:29.690Z";
|
|
58110
|
+
__info__.hash = "c3a4769";
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 17.0.
|
|
6
|
-
* @date 2025-03-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.0.55
|
|
6
|
+
* @date 2025-03-26T12:44:29.690Z
|
|
7
|
+
* @hash c3a4769
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
|
|
@@ -947,7 +947,10 @@ function rgbaStringToHex(color) {
|
|
|
947
947
|
}
|
|
948
948
|
else if (stringVals.length === 4) {
|
|
949
949
|
const alpha = parseFloat(stringVals.pop() || "1");
|
|
950
|
-
|
|
950
|
+
if (isNaN(alpha)) {
|
|
951
|
+
throw new Error("invalid alpha value");
|
|
952
|
+
}
|
|
953
|
+
alphaHex = Math.round(alpha * 255);
|
|
951
954
|
}
|
|
952
955
|
const vals = stringVals.map((val) => parseInt(val, 10));
|
|
953
956
|
if (alphaHex !== 255) {
|
|
@@ -8568,7 +8571,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
|
|
|
8568
8571
|
}
|
|
8569
8572
|
}
|
|
8570
8573
|
else if (dataSets.length === 1) {
|
|
8571
|
-
|
|
8574
|
+
const dataLength = getData(getters, dataSets[0]).length;
|
|
8575
|
+
for (let i = 0; i < dataLength; i++) {
|
|
8572
8576
|
labels.formattedValues.push("");
|
|
8573
8577
|
labels.values.push("");
|
|
8574
8578
|
}
|
|
@@ -30369,9 +30373,9 @@ const XLSX_CHART_TYPES = [
|
|
|
30369
30373
|
/** In XLSX color format (no #) */
|
|
30370
30374
|
const AUTO_COLOR = "000000";
|
|
30371
30375
|
const XLSX_ICONSET_MAP = {
|
|
30372
|
-
|
|
30376
|
+
arrows: "3Arrows",
|
|
30373
30377
|
smiley: "3Symbols",
|
|
30374
|
-
|
|
30378
|
+
dots: "3TrafficLights1",
|
|
30375
30379
|
};
|
|
30376
30380
|
const NAMESPACE = {
|
|
30377
30381
|
styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
|
@@ -30787,6 +30791,7 @@ const ICON_SET_CONVERSION_MAP = {
|
|
|
30787
30791
|
};
|
|
30788
30792
|
/** Map between legend position in XLSX file and human readable position */
|
|
30789
30793
|
const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
|
|
30794
|
+
none: "none",
|
|
30790
30795
|
b: "bottom",
|
|
30791
30796
|
t: "top",
|
|
30792
30797
|
l: "left",
|
|
@@ -33019,7 +33024,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
|
33019
33024
|
? "right"
|
|
33020
33025
|
: "left",
|
|
33021
33026
|
legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
|
|
33022
|
-
default: "
|
|
33027
|
+
default: "none",
|
|
33023
33028
|
}).asString()],
|
|
33024
33029
|
stacked: barChartGrouping === "stacked",
|
|
33025
33030
|
fontColor: "000000",
|
|
@@ -53480,7 +53485,9 @@ css /* scss */ `
|
|
|
53480
53485
|
margin-top: -1px;
|
|
53481
53486
|
border: 1px solid;
|
|
53482
53487
|
|
|
53483
|
-
|
|
53488
|
+
/* In readonly we always show the fx icon if the composer is empty, not matter the focus */
|
|
53489
|
+
.o-composer:empty:not(:focus):not(.active)::before,
|
|
53490
|
+
&.o-topbar-composer-readonly .o-composer:empty::before {
|
|
53484
53491
|
content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
|
|
53485
53492
|
position: relative;
|
|
53486
53493
|
top: 20%;
|
|
@@ -56481,10 +56488,14 @@ function addIconSetRule(cf, rule) {
|
|
|
56481
56488
|
continue;
|
|
56482
56489
|
}
|
|
56483
56490
|
const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
|
|
56491
|
+
const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
|
|
56492
|
+
if (isIconSetReversed(rule.icons)) {
|
|
56493
|
+
iconSetAttrs.push(["reverse", "1"]);
|
|
56494
|
+
}
|
|
56484
56495
|
conditionalFormats.push(escapeXml /*xml*/ `
|
|
56485
56496
|
<conditionalFormatting sqref="${range}">
|
|
56486
56497
|
<cfRule ${formatAttributes(ruleAttributes)}>
|
|
56487
|
-
<iconSet
|
|
56498
|
+
<iconSet ${formatAttributes(iconSetAttrs)}>
|
|
56488
56499
|
${joinXmlNodes(cfValueObjectNodes)}
|
|
56489
56500
|
</iconSet>
|
|
56490
56501
|
</cfRule>
|
|
@@ -56502,9 +56513,21 @@ function commonCfAttributes(cf) {
|
|
|
56502
56513
|
["stopIfTrue", cf.stopIfTrue ? 1 : 0],
|
|
56503
56514
|
];
|
|
56504
56515
|
}
|
|
56516
|
+
function isIconSetReversed(iconSet) {
|
|
56517
|
+
const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
|
|
56518
|
+
return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
|
|
56519
|
+
}
|
|
56505
56520
|
function getIconSet(iconSet) {
|
|
56506
|
-
return XLSX_ICONSET_MAP[
|
|
56507
|
-
|
|
56521
|
+
return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
|
|
56522
|
+
}
|
|
56523
|
+
/**
|
|
56524
|
+
* Partial detection based on "upper" point only.
|
|
56525
|
+
* We support any arbitrary icon in the set, while excel doesn't allow
|
|
56526
|
+
* mixing icons from different types.
|
|
56527
|
+
*/
|
|
56528
|
+
function detectIconsType(iconSet) {
|
|
56529
|
+
const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
|
|
56530
|
+
return type;
|
|
56508
56531
|
}
|
|
56509
56532
|
function thresholdAttributes(threshold, position) {
|
|
56510
56533
|
const type = getExcelThresholdType(threshold.type, position);
|
|
@@ -58045,6 +58068,6 @@ const constants = {
|
|
|
58045
58068
|
export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
|
|
58046
58069
|
|
|
58047
58070
|
|
|
58048
|
-
__info__.version = "17.0.
|
|
58049
|
-
__info__.date = "2025-03-
|
|
58050
|
-
__info__.hash = "
|
|
58071
|
+
__info__.version = "17.0.55";
|
|
58072
|
+
__info__.date = "2025-03-26T12:44:29.690Z";
|
|
58073
|
+
__info__.hash = "c3a4769";
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 17.0.
|
|
6
|
-
* @date 2025-03-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.0.55
|
|
6
|
+
* @date 2025-03-26T12:44:29.690Z
|
|
7
|
+
* @hash c3a4769
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function (exports, owl) {
|
|
@@ -948,7 +948,10 @@
|
|
|
948
948
|
}
|
|
949
949
|
else if (stringVals.length === 4) {
|
|
950
950
|
const alpha = parseFloat(stringVals.pop() || "1");
|
|
951
|
-
|
|
951
|
+
if (isNaN(alpha)) {
|
|
952
|
+
throw new Error("invalid alpha value");
|
|
953
|
+
}
|
|
954
|
+
alphaHex = Math.round(alpha * 255);
|
|
952
955
|
}
|
|
953
956
|
const vals = stringVals.map((val) => parseInt(val, 10));
|
|
954
957
|
if (alphaHex !== 255) {
|
|
@@ -8569,7 +8572,8 @@
|
|
|
8569
8572
|
}
|
|
8570
8573
|
}
|
|
8571
8574
|
else if (dataSets.length === 1) {
|
|
8572
|
-
|
|
8575
|
+
const dataLength = getData(getters, dataSets[0]).length;
|
|
8576
|
+
for (let i = 0; i < dataLength; i++) {
|
|
8573
8577
|
labels.formattedValues.push("");
|
|
8574
8578
|
labels.values.push("");
|
|
8575
8579
|
}
|
|
@@ -30370,9 +30374,9 @@
|
|
|
30370
30374
|
/** In XLSX color format (no #) */
|
|
30371
30375
|
const AUTO_COLOR = "000000";
|
|
30372
30376
|
const XLSX_ICONSET_MAP = {
|
|
30373
|
-
|
|
30377
|
+
arrows: "3Arrows",
|
|
30374
30378
|
smiley: "3Symbols",
|
|
30375
|
-
|
|
30379
|
+
dots: "3TrafficLights1",
|
|
30376
30380
|
};
|
|
30377
30381
|
const NAMESPACE = {
|
|
30378
30382
|
styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
|
@@ -30788,6 +30792,7 @@
|
|
|
30788
30792
|
};
|
|
30789
30793
|
/** Map between legend position in XLSX file and human readable position */
|
|
30790
30794
|
const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
|
|
30795
|
+
none: "none",
|
|
30791
30796
|
b: "bottom",
|
|
30792
30797
|
t: "top",
|
|
30793
30798
|
l: "left",
|
|
@@ -33020,7 +33025,7 @@
|
|
|
33020
33025
|
? "right"
|
|
33021
33026
|
: "left",
|
|
33022
33027
|
legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
|
|
33023
|
-
default: "
|
|
33028
|
+
default: "none",
|
|
33024
33029
|
}).asString()],
|
|
33025
33030
|
stacked: barChartGrouping === "stacked",
|
|
33026
33031
|
fontColor: "000000",
|
|
@@ -53481,7 +53486,9 @@
|
|
|
53481
53486
|
margin-top: -1px;
|
|
53482
53487
|
border: 1px solid;
|
|
53483
53488
|
|
|
53484
|
-
|
|
53489
|
+
/* In readonly we always show the fx icon if the composer is empty, not matter the focus */
|
|
53490
|
+
.o-composer:empty:not(:focus):not(.active)::before,
|
|
53491
|
+
&.o-topbar-composer-readonly .o-composer:empty::before {
|
|
53485
53492
|
content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
|
|
53486
53493
|
position: relative;
|
|
53487
53494
|
top: 20%;
|
|
@@ -56482,10 +56489,14 @@
|
|
|
56482
56489
|
continue;
|
|
56483
56490
|
}
|
|
56484
56491
|
const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
|
|
56492
|
+
const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
|
|
56493
|
+
if (isIconSetReversed(rule.icons)) {
|
|
56494
|
+
iconSetAttrs.push(["reverse", "1"]);
|
|
56495
|
+
}
|
|
56485
56496
|
conditionalFormats.push(escapeXml /*xml*/ `
|
|
56486
56497
|
<conditionalFormatting sqref="${range}">
|
|
56487
56498
|
<cfRule ${formatAttributes(ruleAttributes)}>
|
|
56488
|
-
<iconSet
|
|
56499
|
+
<iconSet ${formatAttributes(iconSetAttrs)}>
|
|
56489
56500
|
${joinXmlNodes(cfValueObjectNodes)}
|
|
56490
56501
|
</iconSet>
|
|
56491
56502
|
</cfRule>
|
|
@@ -56503,9 +56514,21 @@
|
|
|
56503
56514
|
["stopIfTrue", cf.stopIfTrue ? 1 : 0],
|
|
56504
56515
|
];
|
|
56505
56516
|
}
|
|
56517
|
+
function isIconSetReversed(iconSet) {
|
|
56518
|
+
const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
|
|
56519
|
+
return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
|
|
56520
|
+
}
|
|
56506
56521
|
function getIconSet(iconSet) {
|
|
56507
|
-
return XLSX_ICONSET_MAP[
|
|
56508
|
-
|
|
56522
|
+
return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
|
|
56523
|
+
}
|
|
56524
|
+
/**
|
|
56525
|
+
* Partial detection based on "upper" point only.
|
|
56526
|
+
* We support any arbitrary icon in the set, while excel doesn't allow
|
|
56527
|
+
* mixing icons from different types.
|
|
56528
|
+
*/
|
|
56529
|
+
function detectIconsType(iconSet) {
|
|
56530
|
+
const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
|
|
56531
|
+
return type;
|
|
56509
56532
|
}
|
|
56510
56533
|
function thresholdAttributes(threshold, position) {
|
|
56511
56534
|
const type = getExcelThresholdType(threshold.type, position);
|
|
@@ -58081,9 +58104,9 @@
|
|
|
58081
58104
|
exports.tokenize = tokenize;
|
|
58082
58105
|
|
|
58083
58106
|
|
|
58084
|
-
__info__.version = "17.0.
|
|
58085
|
-
__info__.date = "2025-03-
|
|
58086
|
-
__info__.hash = "
|
|
58107
|
+
__info__.version = "17.0.55";
|
|
58108
|
+
__info__.date = "2025-03-26T12:44:29.690Z";
|
|
58109
|
+
__info__.hash = "c3a4769";
|
|
58087
58110
|
|
|
58088
58111
|
|
|
58089
58112
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|