Comparing version 0.1.3 to 0.1.4
241
lib/cell.js
@@ -44,3 +44,3 @@ /** | ||
this._value = ValuePool.create(Cell.Types.Null, this); | ||
this._value = Value.create(Cell.Types.Null, this); | ||
@@ -75,2 +75,8 @@ this.style = {}; | ||
}, | ||
get border() { | ||
return this.style.border; | ||
}, | ||
set border(value) { | ||
return this.style.border = value; | ||
}, | ||
@@ -111,3 +117,3 @@ // ========================================================================= | ||
this._value.release(); | ||
this._value = ValuePool.create(Cell.Types.Merge, this, master); | ||
this._value = Value.create(Cell.Types.Merge, this, master); | ||
}, | ||
@@ -117,3 +123,3 @@ unmerge: function() { | ||
this._value.release(); | ||
this._value = ValuePool.create(Cell.Types.Null, this); | ||
this._value = Value.create(Cell.Types.Null, this); | ||
} | ||
@@ -151,3 +157,3 @@ }, | ||
// assign value | ||
this._value = ValuePool.create(Value.getType(v), this, v); | ||
this._value = Value.create(Value.getType(v), this, v); | ||
return v; | ||
@@ -159,3 +165,3 @@ }, | ||
if (this.type == Cell.Types.String) { | ||
this._value = ValuePool.create(Cell.Types.Hyperlink, this, { | ||
this._value = Value.create(Cell.Types.Hyperlink, this, { | ||
text: this._value._value, | ||
@@ -175,3 +181,3 @@ hyperlink: hyperlink | ||
//console.log("Creating from model: " + JSON.stringify(value)); | ||
this._value = ValuePool.create(value.type, this); | ||
this._value = Value.create(value.type, this); | ||
this._value.model = value; | ||
@@ -190,25 +196,17 @@ if (value.style) { | ||
var Value = function(model) { | ||
this.model = model; | ||
var NullValue = function(cell) { | ||
this.model = { | ||
address: cell.address, | ||
type: Cell.Types.Null | ||
}; | ||
} | ||
Value.getType = function(value) { | ||
if ((value === null) || (value === undefined)) { | ||
NullValue.prototype = { | ||
get value() { | ||
return null; | ||
}, | ||
set value(value) { | ||
return value; | ||
}, | ||
get type() { | ||
return Cell.Types.Null; | ||
} else if ((value instanceof String) || (typeof value == "string")) { | ||
return Cell.Types.String; | ||
} else if (typeof value == "number") { | ||
return Cell.Types.Number; | ||
} else if (value instanceof Date) { | ||
return Cell.Types.Date; | ||
} else if (value.text && value.hyperlink) { | ||
return Cell.Types.Hyperlink; | ||
} else if (value.formula) { | ||
return Cell.Types.Formula; | ||
} else { | ||
throw new Error("I could not understand type of value") | ||
} | ||
} | ||
Value.prototype = { | ||
get type() { | ||
return this.model.type; | ||
}, | ||
@@ -222,29 +220,13 @@ get address() { | ||
release: function() { | ||
ValuePool.release(this); | ||
} | ||
} | ||
}; | ||
var NullValue = function(cell) { | ||
Value.call(this, { | ||
address: cell.address, | ||
type: Cell.Types.Null | ||
}); | ||
} | ||
utils.inherits(NullValue, Value, { | ||
get value() { | ||
return null; | ||
}, | ||
set value(value) { | ||
return value; | ||
} | ||
}); | ||
var NumberValue = function(cell, value) { | ||
Value.call(this, { | ||
this.model = { | ||
address: cell.address, | ||
type: Cell.Types.Number, | ||
value: value | ||
}); | ||
}; | ||
} | ||
utils.inherits(NumberValue, Value, { | ||
NumberValue.prototype = { | ||
get value() { | ||
@@ -255,13 +237,24 @@ return this.model.value; | ||
return this.model.value = value; | ||
}, | ||
get type() { | ||
return Cell.Types.Number; | ||
}, | ||
get address() { | ||
return this.model.address; | ||
}, | ||
set address(value) { | ||
return this.model.address = value; | ||
}, | ||
release: function() { | ||
} | ||
}); | ||
}; | ||
var StringValue = function(cell, value) { | ||
Value.call(this, { | ||
this.model = { | ||
address: cell.address, | ||
type: Cell.Types.String, | ||
value: value | ||
}); | ||
}; | ||
} | ||
utils.inherits(StringValue, Value, { | ||
StringValue.prototype = { | ||
get value() { | ||
@@ -272,13 +265,24 @@ return this.model.value; | ||
return this.model.value = value; | ||
}, | ||
get type() { | ||
return Cell.Types.String; | ||
}, | ||
get address() { | ||
return this.model.address; | ||
}, | ||
set address(value) { | ||
return this.model.address = value; | ||
}, | ||
release: function() { | ||
} | ||
}); | ||
}; | ||
var DateValue = function(cell, value) { | ||
Value.call(this, { | ||
this.model = { | ||
address: cell.address, | ||
type: Cell.Types.Date, | ||
value: value | ||
}); | ||
}; | ||
} | ||
utils.inherits(DateValue, Value, { | ||
DateValue.prototype = { | ||
get value() { | ||
@@ -289,7 +293,18 @@ return this.model.value; | ||
return this.model.value = value; | ||
}, | ||
get type() { | ||
return Cell.Types.Date; | ||
}, | ||
get address() { | ||
return this.model.address; | ||
}, | ||
set address(value) { | ||
return this.model.address = value; | ||
}, | ||
release: function() { | ||
} | ||
}); | ||
}; | ||
var HyperlinkValue = function(cell, value) { | ||
Value.call(this, { | ||
this.model = { | ||
address: cell.address, | ||
@@ -299,5 +314,5 @@ type: Cell.Types.Hyperlink, | ||
hyperlink: value ? value.hyperlink : undefined | ||
}); | ||
}; | ||
} | ||
utils.inherits(HyperlinkValue, Value, { | ||
HyperlinkValue.prototype = { | ||
get value() { | ||
@@ -326,11 +341,22 @@ return { | ||
return this.model.hyperlink = value; | ||
}, | ||
get type() { | ||
return Cell.Types.Hyperlink; | ||
}, | ||
get address() { | ||
return this.model.address; | ||
}, | ||
set address(value) { | ||
return this.model.address = value; | ||
}, | ||
release: function() { | ||
} | ||
}); | ||
}; | ||
var MergeValue = function(cell, master) { | ||
Value.call(this, { | ||
this.model = { | ||
address: cell.address, | ||
type: Cell.Types.Merge, | ||
master: master ? master.address : undefined | ||
}); | ||
}; | ||
this._master = master; | ||
@@ -341,3 +367,3 @@ if (master) { | ||
} | ||
utils.inherits(MergeValue, Value, { | ||
MergeValue.prototype = { | ||
get value() { | ||
@@ -357,6 +383,2 @@ return this._master.value; | ||
}, | ||
release: function() { | ||
this._master.releaseMergeRef(); | ||
ValuePool.release(this); | ||
}, | ||
@@ -368,7 +390,19 @@ isMergedTo: function(master) { | ||
return this._master; | ||
}, | ||
get type() { | ||
return Cell.Types.Merge; | ||
}, | ||
get address() { | ||
return this.model.address; | ||
}, | ||
set address(value) { | ||
return this.model.address = value; | ||
}, | ||
release: function() { | ||
this._master.releaseMergeRef(); | ||
} | ||
}); | ||
}; | ||
var FormulaValue = function(cell, value) { | ||
Value.call(this, { | ||
this.model = { | ||
address: cell.address, | ||
@@ -378,7 +412,7 @@ type: Cell.Types.Formula, | ||
result: value ? value.result : undefined | ||
}); | ||
}; | ||
//cell.calcChain.add(this); | ||
} | ||
utils.inherits(FormulaValue, Value, { | ||
FormulaValue.prototype = { | ||
get value() { | ||
@@ -430,9 +464,37 @@ return { | ||
return this.model.result = value; | ||
}, | ||
get type() { | ||
return Cell.Types.Formula; | ||
}, | ||
get address() { | ||
return this.model.address; | ||
}, | ||
set address(value) { | ||
return this.model.address = value; | ||
}, | ||
release: function() { | ||
} | ||
}); | ||
}; | ||
// ============================================================================= | ||
// ValuePool | ||
var ValuePool = { | ||
pools: [], | ||
// Value is a place to hold common static Value type functions | ||
var Value = { | ||
getType: function(value) { | ||
if ((value === null) || (value === undefined)) { | ||
return Cell.Types.Null; | ||
} else if ((value instanceof String) || (typeof value == "string")) { | ||
return Cell.Types.String; | ||
} else if (typeof value == "number") { | ||
return Cell.Types.Number; | ||
} else if (value instanceof Date) { | ||
return Cell.Types.Date; | ||
} else if (value.text && value.hyperlink) { | ||
return Cell.Types.Hyperlink; | ||
} else if (value.formula) { | ||
return Cell.Types.Formula; | ||
} else { | ||
throw new Error("I could not understand type of value") | ||
} | ||
}, | ||
// map valueType to constructor | ||
types: [ | ||
@@ -447,28 +509,11 @@ {t:Cell.Types.Null, f:NullValue}, | ||
].reduce(function(p,t){p[t.t]=t.f; return p;}, []), | ||
getPool: function(type) { | ||
var pool = this.pools[type]; | ||
if (!pool) { | ||
this.pools[type] = pool = []; | ||
} | ||
return pool; | ||
}, | ||
create: function(type, cell, value) { | ||
var pool = this.getPool(type); | ||
var drop; | ||
if (pool.length) { | ||
drop = pool.pop(); | ||
drop.address = cell.address; | ||
drop.value = value; | ||
} else { | ||
var t = this.types[type]; | ||
if (!t) throw new Error("Could not create Value of type " + type); | ||
drop = new t(cell, value); | ||
var t = this.types[type]; | ||
if (!t) { | ||
throw new Error("Could not create Value of type " + type); | ||
} | ||
return drop; | ||
}, | ||
release: function(value) { | ||
var pool = this.getPool(value.type); | ||
pool.push(value); | ||
return new t(cell, value); | ||
} | ||
}; | ||
@@ -47,3 +47,7 @@ /** | ||
Xlsx: 1 | ||
}, | ||
ReadingOrder: { | ||
RightToLeft: 1, | ||
LeftToRight: 2 | ||
} | ||
} |
@@ -134,7 +134,8 @@ /** | ||
}); | ||
}, | ||
validInt: function(value) { | ||
var i = parseInt(value); | ||
return i !== NaN ? i : 0; | ||
} | ||
}; | ||
function escapeXml(unsafe) { | ||
} | ||
@@ -26,2 +26,4 @@ /** | ||
var _ = require("underscore"); | ||
var utils = require("../utils"); | ||
var Enums = require("../enums"); | ||
@@ -34,4 +36,5 @@ // Alignment encapsulates translation from style.alignment model to/from xlsx | ||
this.wrapText = this.validWrapText(model.wrapText); | ||
this.indent = this.validIndent(model.indent); | ||
this.indent = utils.validInt(model.indent); | ||
this.textRotation = this.validTextRotation(model.textRotation); | ||
this.readingOrder = utils.validInt(model.readingOrder); | ||
} | ||
@@ -59,2 +62,5 @@ } | ||
} | ||
if (this.readingOrder) { | ||
hasValues = model.readingOrder = this.readingOrder; | ||
} | ||
return hasValues ? model : null; | ||
@@ -104,2 +110,5 @@ }, | ||
} | ||
if (this.readingOrder) { | ||
xml.push(' readingOrder="' + this.readingOrder + '"'); | ||
} | ||
@@ -117,5 +126,5 @@ xml.push('/>'); | ||
this.wrapText = node.attributes.wrapText ? true : false; | ||
this.indent = parseInt(node.attributes.indent); | ||
this.indent = utils.validInt(node.attributes.indent); | ||
var tr = parseInt(node.attributes.textRotation); | ||
var tr = utils.validInt(node.attributes.textRotation); | ||
if (tr !== undefined) { | ||
@@ -130,23 +139,28 @@ if (tr == 255) { | ||
} | ||
this.readingOrder = utils.validInt(node.attributes.readingOrder); | ||
}, | ||
validHorizontalValues: [ | ||
"left", | ||
"center", | ||
"right", | ||
"fill", | ||
"centerContinuous", | ||
"distributed", | ||
"justify" | ||
].reduce(function(p,v){p[v]=true; return p;}, {}), | ||
validHorizontal: function(value) { | ||
switch(value) { | ||
case "left": | ||
case "center": | ||
case "right": | ||
return value; | ||
default: | ||
return undefined; | ||
} | ||
return this.validHorizontalValues[value] ? value : undefined; | ||
}, | ||
validVerticalValues: [ | ||
"top", | ||
"middle", | ||
"bottom", | ||
"distributed", | ||
"justify" | ||
].reduce(function(p,v){p[v]=true; return p;}, {}), | ||
validVertical: function(value) { | ||
switch(value) { | ||
case "top": | ||
case "middle": | ||
case "bottom": | ||
return value; | ||
default: | ||
return undefined; | ||
} | ||
return this.validVerticalValues[value] ? value : undefined; | ||
}, | ||
@@ -156,6 +170,2 @@ validWrapText: function(value) { | ||
}, | ||
validIndent: function(value) { | ||
value = parseInt(value); | ||
return value > 0 ? value : undefined; | ||
}, | ||
validTextRotation: function(value) { | ||
@@ -166,6 +176,15 @@ switch(value) { | ||
default: | ||
value = parseInt(value); | ||
value = utils.validInt(value); | ||
return (value >= -90) && (value <= 90) ? value : undefined; | ||
} | ||
}, | ||
validReadingOrderValues: [ | ||
{k:"r2l",v:1}, | ||
{k:"l2r",v:2}, | ||
{k:Enums.ReadingOrder.RightToLeft,v:1}, | ||
{k:Enums.ReadingOrder.LeftToRight,v:2} | ||
].reduce(function(p,v) {p[v.k]=v.v; return p;}, {}), | ||
validReadingOrder: function(value) { | ||
return this.validReadingOrderValues[value]; | ||
} | ||
} |
@@ -70,2 +70,6 @@ /** | ||
if (this.borderId) { | ||
xml.push(' applyBorder="1"'); | ||
} | ||
if (this.alignment && this.alignment.xml) { | ||
@@ -72,0 +76,0 @@ xml.push(' applyAlignment="1"'); |
@@ -32,2 +32,3 @@ /** | ||
var Alignment = require("./alignment"); | ||
var Border = require("./border"); | ||
@@ -49,5 +50,10 @@ var utils = require("../utils"); | ||
this.fonts = []; // array of font xml | ||
this.fonts = []; // array of font | ||
this.fontIndex = {}; // hash of xml->fontId | ||
this.borders = []; // array of border | ||
this.borderIndex = {}; // hash of xml->borderId | ||
// Note: aligments stored inside the style objects | ||
// --------------------------------------------------------------- | ||
@@ -59,2 +65,5 @@ // Defaults | ||
// default (zero) border | ||
this._addBorder(new Border()); | ||
// add default (all zero) style | ||
@@ -98,5 +107,7 @@ this._addStyle(new Style()); | ||
var inFonts = false; | ||
var inBorders = false; | ||
var style = null; | ||
var font = null; | ||
var border = null; | ||
@@ -123,2 +134,7 @@ parser.on('opentag', function(node) { | ||
} | ||
} else if (inBorders) { | ||
if (node.name == "border") { | ||
border = new Border(); | ||
} | ||
border.parse(node); | ||
} else { | ||
@@ -135,2 +151,5 @@ switch(node.name) { | ||
break; | ||
case 'borders': | ||
inBorders = true; | ||
break; | ||
} | ||
@@ -166,6 +185,16 @@ } | ||
} | ||
} else if (inBorders) { | ||
switch(name) { | ||
case 'borders': | ||
inBorders = false; | ||
break; | ||
case 'border': | ||
self._addBorder(border); | ||
border = null; | ||
break; | ||
} | ||
} | ||
}); | ||
parser.on('end', function() { | ||
// warning: if style, font, inFonts, inCellXfs, inNumFmts are true! | ||
// warning: if style, font, border, inBorders, inFonts, inCellXfs, inNumFmts are true! | ||
deferred.resolve(self); | ||
@@ -186,3 +215,2 @@ }); | ||
// Note: a WeakMap --> styleId would be very useful here! | ||
var style = new Style(); | ||
@@ -207,2 +235,6 @@ | ||
if (model.border) { | ||
style.borderId = this._addBorder(new Border(model.border)); | ||
} | ||
if (model.alignment) { | ||
@@ -245,2 +277,9 @@ style.alignment = new Alignment(model.alignment); | ||
// ------------------------------------------------------- | ||
// border | ||
var border = this.borders[style.borderId]; | ||
if (border) { | ||
model.border = border.model | ||
} | ||
// ------------------------------------------------------- | ||
// alignment | ||
@@ -295,2 +334,13 @@ if (style.alignment) { | ||
return fontId; | ||
}, | ||
// ========================================================================= | ||
// Borders | ||
_addBorder: function(border) { | ||
var borderId = this.borderIndex[border.xml]; | ||
if (borderId === undefined) { | ||
borderId = this.borderIndex[border.xml] = this.borders.length; | ||
this.borders.push(border); | ||
} | ||
return borderId; | ||
} | ||
@@ -297,0 +347,0 @@ |
@@ -803,4 +803,4 @@ /** | ||
default: | ||
if (entry.path.match(/xl\/worksheets\/sheet\d\.xml/)) { | ||
var match = entry.path.match(/xl\/worksheets\/sheet(\d)\.xml/) | ||
if (entry.path.match(/xl\/worksheets\/sheet\d+\.xml/)) { | ||
var match = entry.path.match(/xl\/worksheets\/sheet(\d+)\.xml/) | ||
var sheetNo = match[1]; | ||
@@ -877,2 +877,11 @@ promise = self.parseWorksheet(entry) | ||
addThemes: function(zip) { | ||
var self = this; | ||
return utils.readModuleFile("./xlsx/theme1.xml") | ||
.then(function(data){ | ||
zip.append(data, { name: "xl/theme/theme1.xml" }); | ||
return zip; | ||
}); | ||
}, | ||
//<Relationship Id="rId1" | ||
@@ -901,3 +910,4 @@ // Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" | ||
relationships: [ | ||
{ rId: "rId" + (count++), type: XLSX.RelType.Styles, target: "styles.xml" } | ||
{ rId: "rId" + (count++), type: XLSX.RelType.Styles, target: "styles.xml" }, | ||
{ rId: "rId" + (count++), type: XLSX.RelType.Theme, target: "theme/theme1.xml" } | ||
] | ||
@@ -1135,2 +1145,3 @@ }; | ||
self.addContentTypes(zip, model), | ||
self.addThemes(zip), | ||
self.addOfficeRels(zip, model) | ||
@@ -1137,0 +1148,0 @@ ]; |
{ | ||
"name": "exceljs", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"description": "Excel Workbook Manager", | ||
@@ -17,3 +17,3 @@ "private": false, | ||
"keywords": [ | ||
"xlsx", "json", "excel", "office", "spreadsheet", "workbook" | ||
"xlsx", "json", "excel", "font", "border", "number", "format", "number format", "alignment", "office", "spreadsheet", "workbook" | ||
], | ||
@@ -20,0 +20,0 @@ "dependencies": { |
# ExcelJS | ||
Read, manipulate and write spreadsheet data to XLSX and JSON. | ||
Read, manipulate and write spreadsheet data and styles to XLSX and JSON. | ||
@@ -14,5 +14,10 @@ Reverse engineered from Excel spreadsheet files as a project. | ||
<ul> | ||
<li><a href="#alignment">Cell Alignment Style</a></li> | ||
<li><a href="#rows">Row Height</a></li> | ||
<li>Some Internal Restructuring</li> | ||
<li> | ||
Bug Fixes | ||
<ul> | ||
<li>Now handles 10 or more worksheets in one workbook</li> | ||
<li>theme1.xml file properly added and referenced</li> | ||
</ul> | ||
</li> | ||
<li><a href="#borders">Cell Borders</a></li> | ||
</ul> | ||
@@ -25,3 +30,2 @@ | ||
<li>Fills</li> | ||
<li>Borders</li> | ||
</ul> | ||
@@ -35,13 +39,18 @@ | ||
<ul> | ||
<li><a href="#create-a-workbook">Create a Workbook</a> | ||
<li><a href="#add-a-worksheet">Add a Worksheet</a> | ||
<li><a href="#access-worksheets">Access Worksheets</a> | ||
<li><a href="#columns">Columns</a> | ||
<li><a href="#rows">Rows</a> | ||
<li><a href="#handling-individual-cells">Handling Individual Cells</a> | ||
<li><a href="#merged-cells">Merged Cells</a> | ||
<li><a href="#number-formats">Number Formats</a> | ||
<li><a href="#fonts">Fonts</a> | ||
<li><a href="#reading-xlsx">Reading XLSX</a> | ||
<li><a href="#writing-xlsx">Writing XLSX</a> | ||
<li><a href="#create-a-workbook">Create a Workbook</a></li> | ||
<li><a href="#add-a-worksheet">Add a Worksheet</a></li> | ||
<li><a href="#access-worksheets">Access Worksheets</a></li> | ||
<li><a href="#columns">Columns</a></li> | ||
<li><a href="#rows">Rows</a></li> | ||
<li><a href="#handling-individual-cells">Handling Individual Cells</a></li> | ||
<li><a href="#merged-cells">Merged Cells</a></li> | ||
<li><a href="#cell-styles">Cell Styles</a> | ||
<ul> | ||
<li><a href="#number-formats">Number Formats</a></li> | ||
<li><a href="#fonts">Fonts</a></li> | ||
<li><a href="#alignment">Alignment</a></li> | ||
</ul> | ||
</li> | ||
<li><a href="#reading-xlsx">Reading XLSX</a></li> | ||
<li><a href="#writing-xlsx">Writing XLSX</a></li> | ||
</ul> | ||
@@ -257,4 +266,4 @@ </li> | ||
| italic | Font *slope* | true, false | | ||
| underline | Font underline style | true, false, "none", "single", "double", "singleAccounting", "doubleAccounting" | | ||
| strike | Font ~~strikethrough~~ | true, false | | ||
| underline | Font <u>underline</u> style | true, false, "none", "single", "double", "singleAccounting", "doubleAccounting" | | ||
| strike | Font <strike>strikethrough</strike> | true, false | | ||
| outline | Font outline | true, false | | ||
@@ -283,4 +292,52 @@ | ||
Note: valid text rotation values range from -90 to +90 or "vertical". Any values outside this range will be ignored. | ||
**Valid Alignment Property Values** | ||
| horizontal | vertical | wrapText | indent | readingOrder | textRotation | | ||
| ---------- | ----------- | -------- | ------- | ------------ | ------------ | | ||
| left | top | true | integer | rtl | 0 to 90 | | ||
| center | middle | false | | ltr | -1 to -90 | | ||
| right | bottom | | | | vertical | | ||
| fill | distributed | | | | | | ||
| justify | justify | | | | | | ||
| centerContinuous | | | | | | | ||
| distributed | | | | | | | ||
### Borders | ||
// set single thin border around A1 | ||
ws.getCell("A1").border = { | ||
top: {style:"thin"}, | ||
left: {style:"thin"}, | ||
bottom: {style:"thin"}, | ||
right: {style:"thin"} | ||
}; | ||
// set double thin green border around A3 | ||
ws.getCell("A3").border = { | ||
top: {style:"double", color: {argb:"FF00FF00"}}, | ||
left: {style:"double", color: {argb:"FF00FF00"}}, | ||
bottom: {style:"double", color: {argb:"FF00FF00"}}, | ||
right: {style:"double", color: {argb:"FF00FF00"}} | ||
}; | ||
// set thick red cross in A5 | ||
ws.getCell("A5").border = { | ||
diagonal: {up: true, down: true, style:"thick", color: {argb:"FFFF0000"}} | ||
}; | ||
**Valid Border Styles** | ||
* thin | ||
* dotted | ||
* dashDot | ||
* hair | ||
* dashDotDot | ||
* slantDashDot | ||
* mediumDashed | ||
* mediumDashDotDot | ||
* mediumDashDot | ||
* medium | ||
* double | ||
* thick | ||
## Reading XLSX | ||
@@ -341,3 +398,5 @@ | ||
| 0.1.3 | <ul><li><a href="#alignment">Cell Alignment Style</a></li><li><a href="#rows">Row Height</a></li><li>Some Internal Restructuring</li></ul> | | ||
| 0.1.4 | <ul><li>Bug Fixes<ul><li>Now handles 10 or more worksheets in one workbook</li><li>theme1.xml file properly added and referenced</li></ul></li><li><a href="#borders">Cell Borders</a></li></ul> | | ||
# Interface Changes | ||
@@ -344,0 +403,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
185472
32
3910
416