@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,123 lines (1,003 loc) • 41.9 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ({
/***/ 0:
/***/ (function(module, exports, __webpack_require__) {
__webpack_require__(1523);
module.exports = __webpack_require__(1523);
/***/ }),
/***/ 3:
/***/ (function(module, exports) {
module.exports = function() { throw new Error("define cannot be used indirect"); };
/***/ }),
/***/ 924:
/***/ (function(module, exports) {
module.exports = require("../util/text-metrics");
/***/ }),
/***/ 939:
/***/ (function(module, exports) {
module.exports = require("./range");
/***/ }),
/***/ 1500:
/***/ (function(module, exports) {
module.exports = require("./sheet");
/***/ }),
/***/ 1516:
/***/ (function(module, exports) {
module.exports = require("./references");
/***/ }),
/***/ 1523:
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function(f, define){
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(1524), __webpack_require__(1500), __webpack_require__(939), __webpack_require__(1516), __webpack_require__(1525), __webpack_require__(924) ], __WEBPACK_AMD_DEFINE_FACTORY__ = (f), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
})(function(){
"use strict";
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
return;
}
var spreadsheet = kendo.spreadsheet;
var CellRef = spreadsheet.CellRef;
var kdrw = kendo.drawing;
var formatting = spreadsheet.formatting;
var geo = kendo.geometry;
var GUIDELINE_WIDTH = 0.8;
/* jshint eqnull:true, laxbreak:true, shadow:true, -W054 */
/* jshint latedef: nofunc */
// This takes a list of row heights and the page height, and
// produces a list of Y coordinates for each row, such that rows
// are not truncated across pages. However, the algorithm will
// decide to truncate a row in the event that more than
// `maxEmpty` (default 0.2) of the available space would
// otherwise be left blank.
//
// It will be used for horizontal splitting too (will receive
// column widths and page width, and produce a list of X coords).
//
// If the third argument (headerRows) is not null, it specifies a
// number of rows to repeat on each page. On pages other than the
// first, the necessary space will be skipped at the top. Header
// coordinates (except for the first page) are pushed in
// headerCoords.
function distributeCoords(heights, pageHeight, maxEmpty, headerRows, headerCoords) {
var curr = 0;
var out = [];
var bottom = pageHeight;
var header = 0;
if (pageHeight && maxEmpty) {
maxEmpty *= pageHeight;
}
heights.forEach(function(h, i){
if (headerRows != null && i < headerRows) {
header += h;
}
if (pageHeight && curr + h > bottom) {
if (bottom - curr < maxEmpty) {
// align to next page
curr = pageHeight * Math.ceil(curr / pageHeight) + header;
if (header > 0) {
headerCoords.push(curr - header);
}
}
// update bottom anyway; don't just add pageHeight, as
// we might need multiple pages for the pathological
// case of one row higher than the page.
bottom += pageHeight * Math.ceil(h / pageHeight);
}
out.push(curr);
curr += h;
});
out.push(curr);
return out;
}
function doLayout(sheet, range, options) {
// normalize reference so we don't have to deal with Infinity here.
var grid = sheet._grid;
range = grid.normalize(range);
var wholeRect = grid.rectangle(range);
var drawings = [];
sheet._drawings.forEach(function(d) {
var box = sheet.drawingBoundingBox(d);
if (box.intersects(wholeRect)) {
drawings.push({
drawing: d,
box: box.offset(-wholeRect.left, -wholeRect.top)
});
}
});
// 1. obtain the list of cells that need to be printed, the
// row heights and column widths. Place in each cell row,
// col (relative to range, i.e. first is 0,0), rowspan,
// colspan and merged. also place a list of drawings
// anchored to that cell.
var cells = [];
var rowHeights = [];
var colWidths = [];
var mergedCells = sheet._getMergedCells(range);
var maxRow = -1, maxCol = -1;
sheet.forEach(range, function(row, col, cell){
var relrow = row - range.topLeft.row;
var relcol = col - range.topLeft.col;
var rh = sheet.rowHeight(row);
var cw = sheet.columnWidth(col);
if (!options.forScreen) {
// for printing we'll need the list of anchored
// drawings so that we can adjust them after page
// splitting.
cell.drawings = drawings.filter(function(d){
var tl = d.drawing.topLeftCell;
return tl && tl.row == row && tl.col == col;
});
}
if (!relcol) {
rowHeights.push(rh);
}
if (!relrow) {
colWidths.push(cw);
}
if (sheet.isHiddenColumn(col) || sheet.isHiddenRow(row) || !rh || !cw) {
return;
}
var nonEmpty = options.forScreen || shouldDrawCell(cell);
if (!(options.emptyCells || nonEmpty)) {
return;
}
var id = new CellRef(row, col).print();
if (mergedCells.secondary[id]) {
return;
}
if (nonEmpty) {
maxRow = Math.max(maxRow, relrow);
maxCol = Math.max(maxCol, relcol);
} else {
cell.empty = true;
}
cell.row = relrow;
cell.col = relcol;
var m = mergedCells.primary[id];
if (m) {
delete mergedCells.primary[id];
cell.merged = true;
cell.rowspan = m.height();
cell.colspan = m.width();
if (options.forScreen) {
cell.width = sheet._columns.sum(m.topLeft.col, m.bottomRight.col);
cell.height = sheet._rows.sum(m.topLeft.row, m.bottomRight.row);
}
} else {
cell.rowspan = 1;
cell.colspan = 1;
}
cells.push(cell);
});
// keep only the drawable area
rowHeights = rowHeights.slice(0, maxRow + 1);
colWidths = colWidths.slice(0, maxCol + 1);
var pageWidth = options.pageWidth;
var pageHeight = options.pageHeight;
var scaleFactor = options.scale || 1;
// when fitWidth is requested, we must update the page size
// with the corresponding scale factor; the algorithm below
// (2) will continue to work, just drawing on a bigger page.
if (options.fitWidth) {
var width = colWidths.reduce(sum, 0);
if (width > pageWidth) {
scaleFactor = pageWidth / width;
}
}
pageWidth = Math.ceil(pageWidth / scaleFactor);
pageHeight = Math.ceil(pageHeight / scaleFactor);
// 2. calculate top, left, bottom, right, width and height for
// printable cells. Merged cells will be split across
// pages, unless the first row/col is shifted to next page.
// boxWidth and boxHeight get the complete drawing size.
// Note that cell coordinates keep increasing, i.e. they
// are not reset to zero for a new page. The print
// function translates the view to current page.
var hyCoords = []; // will receive header Y coordinates, if needed
var yCoords = distributeCoords(rowHeights, pageHeight || 0, options.maxEmpty,
options.headerRows, hyCoords);
var xCoords = distributeCoords(colWidths, pageWidth || 0, options.maxEmpty);
var boxWidth = 0;
var boxHeight = 0;
var headerCells = [];
cells = cells.filter(function(cell){
if (cell.empty && (cell.row > maxRow || cell.col > maxCol)) {
return false;
}
if (options.headerRows && cell.row < options.headerRows) {
headerCells.push(cell);
}
cell.left = xCoords[cell.col];
cell.top = yCoords[cell.row];
if (cell.merged) {
if (!options.forScreen) {
cell.right = orlast(xCoords, cell.col + cell.colspan);
cell.bottom = orlast(yCoords, cell.row + cell.rowspan);
cell.width = cell.right - cell.left;
cell.height = cell.bottom - cell.top;
} else {
cell.right = cell.left + cell.width;
cell.bottom = cell.top + cell.height;
}
} else {
cell.width = colWidths[cell.col];
cell.height = rowHeights[cell.row];
cell.bottom = cell.top + cell.height;
cell.right = cell.left + cell.width;
}
if (!options.forScreen) {
// page breaking will shift cell coordinates. adjust
// anchored drawings.
cell.drawings.forEach(function(d){
var box = d.box;
box.left = cell.left + d.drawing.offsetX;
box.top = cell.top + d.drawing.offsetY;
box.right = box.left + box.width;
box.bottom = box.top + box.height;
});
}
boxWidth = Math.max(boxWidth, cell.right);
boxHeight = Math.max(boxHeight, cell.bottom);
return true;
});
// 3. if any merged cells remain in "primary", they start
// outside the printed range and we should still display
// them partially.
Object.keys(mergedCells.primary).forEach(function(id){
var ref = mergedCells.primary[id];
sheet.forEach(ref.topLeft.toRangeRef(), function(row, col, cell){
var relrow = row - range.topLeft.row;
var relcol = col - range.topLeft.col;
cell.merged = true;
cell.colspan = ref.width();
cell.rowspan = ref.height();
if (relrow < 0) {
cell.top = -sheet._rows.sum(row, row - relrow - 1);
} else {
cell.top = yCoords[relrow];
}
if (relcol < 0) {
cell.left = -sheet._columns.sum(col, col - relcol - 1);
} else {
cell.left = xCoords[relcol];
}
cell.height = sheet._rows.sum(ref.topLeft.row, ref.bottomRight.row);
cell.width = sheet._columns.sum(ref.topLeft.col, ref.bottomRight.col);
if (cell.height > 0 && cell.width > 0) {
// zero means a fully hidden merged cell (all rows/columns are hidden)
// https://github.com/telerik/kendo-ui-core/issues/1794
cell.right = cell.left + cell.width;
cell.bottom = cell.top + cell.height;
cell.row = relrow;
cell.col = relcol;
cells.push(cell);
}
});
});
// 4. replicate header rows on all pages
if (options.headerRows) {
hyCoords.forEach(function(y){
headerCells.forEach(function(cell){
cell = clone(cell);
cell.top += y;
cell.bottom = cell.top + cell.height;
cells.push(cell);
});
yCoords.push(y);
});
yCoords.sort(orderCoords);
}
return {
width : boxWidth,
height : boxHeight,
cells : cells.sort(orderCells),
scale : scaleFactor,
xCoords : xCoords,
yCoords : yCoords,
drawings : drawings
};
}
function clone(hash, target) {
if (!target) {
target = {};
}
if (Object.assign) {
return Object.assign(target, hash);
}
return Object.keys(hash).reduce(function(copy, key){
copy[key] = hash[key];
return copy;
}, target);
}
function sameBorder(a, b) {
return a.size === b.size && a.color === b.color;
}
function sum(a, b) {
return a + b;
}
function orlast(a, i) {
return i < a.length ? a[i] : a[a.length - 1];
}
function shouldDrawCell(cell) {
return cell.value != null
|| cell.merged
|| cell.background != null
|| cell.borderRight != null
|| cell.borderBottom != null
|| (cell.validation != null && !cell.validation.value);
}
function orderCells(a, b) {
if (a.top < b.top) {
return -1;
} else if (a.top == b.top) {
if (a.left < b.left) {
return -1;
} else if (a.left == b.left) {
return 0;
} else {
return 1;
}
} else {
return 1;
}
}
function orderCoords(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
function drawLayout(sheet, layout, group, options) {
// options:
// - pageWidth
// - pageHeight
// - fitWidth
// - hCenter
// - vCenter
var ncols = Math.ceil(layout.width / options.pageWidth);
var nrows = Math.ceil(layout.height / options.pageHeight);
var pageWidth = Math.ceil(options.pageWidth / layout.scale);
var pageHeight = Math.ceil(options.pageHeight / layout.scale);
for (var j = 0; j < nrows; ++j) {
for (var i = 0; i < ncols; ++i) {
addPage(j, i);
}
}
function addPage(row, col) {
var left = col * pageWidth;
var right = left + pageWidth;
var top = row * pageHeight;
var bottom = top + pageHeight;
var endbottom = 0, endright = 0;
function isInside(box) {
if (box.right <= left || box.left >= right ||
box.bottom <= top || box.top >= bottom) {
return false;
}
endbottom = Math.max(box.bottom, endbottom);
endright = Math.max(box.right, endright);
return true;
}
// XXX: this can be optimized - discard cells that won't
// be used again, and don't walk cells that stand no
// chance to fit.
var cells = layout.cells.filter(isInside);
var drawings = layout.drawings.filter(function(d){
return isInside(d.box);
});
// merged cells might stretch beyond page; limit to that
endbottom = Math.min(endbottom, bottom);
endright = Math.min(endright, right);
if (cells.length || drawings.length) {
var page = new kdrw.Group();
group.append(page);
// page.clip(drawing.Path.fromRect(
// new geo.Rect([ 0, 0 ],
// [ options.pageWidth, options.pageHeight ])));
var content = new kdrw.Group();
page.append(content);
content.clip(kdrw.Path.fromRect(
new geo.Rect([ left - 1, top - 1 ], [ endright + 1, endbottom + 1 ])
));
var matrix = geo.Matrix.scale(layout.scale, layout.scale)
.multiplyCopy(geo.Matrix.translate(-left, -top));
if (options.hCenter || options.vCenter) {
matrix = matrix.multiplyCopy(
geo.Matrix.translate(
options.hCenter ? (right - endright) / 2 : 0,
options.vCenter ? (bottom - endbottom) / 2 : 0)
);
}
content.transform(matrix);
if (options.guidelines) {
var prev = null;
layout.xCoords.forEach(function(x){
x = Math.min(x, endright);
if (x !== prev && x >= left && x <= right) {
prev = x;
content.append(
new kdrw.Path()
.moveTo(x, top)
.lineTo(x, endbottom)
.close()
.stroke(options.guideColor, GUIDELINE_WIDTH)
);
}
});
var prev = null;
layout.yCoords.forEach(function(y){
y = Math.min(y, endbottom);
if (y !== prev && y >= top && y <= bottom) {
prev = y;
content.append(
new kdrw.Path()
.moveTo(left, y)
.lineTo(endright, y)
.close()
.stroke(options.guideColor, GUIDELINE_WIDTH)
);
}
});
}
var borders = Borders(); // jshint ignore: line
cells.forEach(function(cell){
drawCell(cell, content, options);
borders.add(cell, sheet);
});
var bordersGroup = new kdrw.Group();
borders.vert.forEach(function(a){
a.forEach(function(b){
if (!b.rendered) {
b.rendered = true;
bordersGroup.append(
new kdrw.Path()
.moveTo(b.x, b.top)
.lineTo(b.x, b.bottom)
.close()
.stroke(b.color, b.size)
);
}
});
});
borders.horiz.forEach(function(a){
a.forEach(function(b){
if (!b.rendered) {
b.rendered = true;
bordersGroup.append(
new kdrw.Path()
.moveTo(b.left, b.y)
.lineTo(b.right, b.y)
.close()
.stroke(b.color, b.size)
);
}
});
});
content.append(bordersGroup);
drawings.forEach(function(d){
var drawing = d.drawing;
var image = drawing.image;
if (image != null) {
var box = d.box;
var url = sheet._workbook.imageUrl(image);
content.append(
new kdrw.Image(url, new geo.Rect(
[ box.left, box.top ],
[ box.width, box.height ]
)).opacity(drawing.opacity)
);
}
});
}
}
}
function drawCell(cell, content, options) {
var g = new kdrw.Group();
content.append(g);
var rect = new geo.Rect([ cell.left, cell.top ],
[ cell.width, cell.height ]);
if (cell.background || cell.merged) {
var r2d2 = rect;
if (options.guidelines) {
r2d2 = rect.clone();
r2d2.origin.x += GUIDELINE_WIDTH/2 + 0.1;
r2d2.origin.y += GUIDELINE_WIDTH/2 + 0.1;
r2d2.size.width -= GUIDELINE_WIDTH + 0.2;
r2d2.size.height -= GUIDELINE_WIDTH + 0.2;
}
g.append(
new kdrw.Rect(r2d2)
.fill(cell.background || "#fff")
.stroke(null)
);
}
var val = cell.value;
if (val != null) {
var type = typeof val == "number" ? "number" : null;
var clip = new kdrw.Group();
clip.clip(kdrw.Path.fromRect(rect));
g.append(clip);
var f, format = cell.format;
if (!format && type == "number" && val != Math.floor(val)) {
format = "0.##############";
}
if (format) {
f = formatting.textAndColor(val, format);
val = f.text;
if (f.type) {
type = f.type;
}
} else {
val += "";
}
if (!cell.textAlign) {
switch (type) {
case "number":
case "date":
case "percent":
case "currency":
cell.textAlign = "right";
break;
case "boolean":
cell.textAlign = "center";
break;
}
}
drawText(val, (f && f.color) || cell.color || "#000", cell, clip);
}
}
function applyIndent(cell, style) {
if (cell.indent) {
// OOXML spec states the indent is "An integer value,
// where an increment of 1 represents 3 spaces". This, of
// course, bears no resemblance to what Excel actually
// does, so we need magic numbers here.
var indent = 1.4 * cell.indent;
switch (style.textAlign) {
case null:
case "left":
style.paddingLeft = indent + "ch";
break;
case "right":
style.paddingRight = indent + "ch";
break;
case "center":
style.paddingLeft = indent/2 + "ch";
style.paddingRight = indent/2 + "ch";
break;
}
}
}
var CONT;
function drawText(text, color, cell, group) {
if (!CONT) {
CONT = document.createElement("div");
CONT.style.position = "fixed";
CONT.style.left = "0px";
CONT.style.top = "0px";
CONT.style.visibility = "hidden";
CONT.style.overflow = "hidden";
CONT.style.boxSizing = "border-box";
CONT.style.lineHeight = "normal";
document.body.appendChild(CONT);
}
if (CONT.firstChild) {
CONT.removeChild(CONT.firstChild);
}
CONT.style.padding = "2px 4px";
CONT.style.color = color;
CONT.style.font = makeFontDef(cell);
CONT.style.width = cell.width + "px";
CONT.style.textAlign = cell.textAlign || "left";
CONT.style.textDecoration = cell.underline ? "underline" : "none";
applyIndent(cell, CONT.style);
if (cell.wrap) {
CONT.style.whiteSpace = "pre-wrap";
CONT.style.overflowWrap = CONT.style.wordWrap = "break-word";
} else {
CONT.style.whiteSpace = "pre";
CONT.style.overflowWrap = CONT.style.wordWrap = "normal";
}
CONT.appendChild(document.createTextNode(text));
var vtrans = 0;
switch (cell.verticalAlign) {
case "center":
vtrans = (cell.height - CONT.offsetHeight) >> 1;
break;
case undefined:
case null:
case "bottom":
vtrans = (cell.height - CONT.offsetHeight);
break;
}
if (vtrans < 0) { vtrans = 0; }
var text_group = kendo.drawing.drawDOM.drawText(CONT);
text_group.transform(geo.Matrix.translate(cell.left, cell.top + vtrans));
group.append(text_group);
}
function makeFontDef(cell) {
var font = [];
if (cell.italic) {
font.push("italic");
}
if (cell.bold) {
font.push("bold");
}
font.push((cell.fontSize || 12) + "px");
font.push((cell.fontFamily || "Arial"));
return font.join(" ");
}
function draw(sheet, range, options, callback) {
if (options == null && callback == null) {
callback = range;
options = {};
range = spreadsheet.SHEETREF;
}
if (callback == null) {
callback = options;
if (range instanceof spreadsheet.Range
|| range instanceof spreadsheet.Ref
|| typeof range == "string") {
options = {};
} else {
options = range;
range = spreadsheet.SHEETREF;
}
}
options = kendo.jQuery.extend({
paperSize : "A4",
landscape : true,
margin : "1cm",
guidelines : true,
guideColor : "#aaa",
emptyCells : true,
fitWidth : false,
center : false,
headerRows : null,
maxEmpty : 0.2,
scale : 1
}, options);
var group = new kdrw.Group();
var paper = kendo.pdf.getPaperOptions(options);
group.options.set("pdf", {
author : options.author,
creator : options.creator,
date : options.date,
keywords : options.keywords,
margin : paper.margin,
multiPage : true,
paperSize : paper.paperSize,
subject : options.subject,
title : options.title
});
var pageWidth = paper.paperSize[0];
var pageHeight = paper.paperSize[1];
if (paper.margin) {
pageWidth -= paper.margin.left + paper.margin.right + 1;
pageHeight -= paper.margin.top + paper.margin.bottom + 1;
}
options.pageWidth = pageWidth;
options.pageHeight = pageHeight;
var layout = doLayout(sheet, sheet._ref(range), options);
drawLayout(sheet, layout, group, options);
callback(group);
}
spreadsheet.Sheet.prototype.draw = function(range, options, callback) {
var sheet = this;
if (sheet._workbook) {
sheet.recalc(sheet._workbook._context, function(){
draw(sheet, range, options, callback);
});
} else {
draw(sheet, range, options, callback);
}
};
// Hack: since we index the border arrays by relative row/col we
// could end up with negative indexes, i.e. horiz[-2] = border.
// Array forEach will ignore these, so we provide a simple
// container here (outside code only needs forEach at this time).
function Container() {}
Container.prototype = {
forEach: function(f) {
Object.keys(this).forEach(function(key){
f(this[key], key, this);
}, this);
}
};
function Borders() {
var horiz = new Container();
var vert = new Container();
function add(cell, sheet) {
if (sheet) {
// reset borders here; the propertybag doesn't keep track of merged cells :-/ this
// is ugly, but the inner details of data storage have leaked everywhere anyway.
var pb = sheet._properties;
var grid = sheet._grid;
cell.borderLeft = pb.get("vBorders", grid.index(cell.row, cell.col));
cell.borderRight = pb.get("vBorders", grid.index(cell.row, cell.col + cell.colspan));
cell.borderTop = pb.get("hBorders", grid.index(cell.row, cell.col));
cell.borderBottom = pb.get("hBorders", grid.index(cell.row + cell.rowspan, cell.col));
}
if (cell.borderLeft) {
addVert(cell.row, cell.col, cell.borderLeft,
cell.left, cell.top, cell.bottom);
}
if (cell.borderRight) {
addVert(cell.row, cell.col + cell.colspan, cell.borderRight,
cell.right, cell.top, cell.bottom);
}
if (cell.borderTop) {
addHoriz(cell.row, cell.col, cell.borderTop,
cell.top, cell.left, cell.right);
}
if (cell.borderBottom) {
addHoriz(cell.row + cell.rowspan, cell.col, cell.borderBottom,
cell.bottom, cell.left, cell.right);
}
}
function addVert(row, col, border, x, top, bottom) {
var a = vert[col] || (vert[col] = new Container());
var prev = row > 0 && a[row - 1];
if (prev && sameBorder(prev, border)) {
a[row] = prev;
prev.bottom = bottom;
} else {
a[row] = {
size: border.size,
color: border.color,
x: x,
top: top,
bottom: bottom
};
}
}
function addHoriz(row, col, border, y, left, right) {
var a = horiz[row] || (horiz[row] = new Container());
var prev = col > 0 && a[col - 1];
if (prev && sameBorder(prev, border)) {
a[col] = prev;
prev.right = right;
} else {
a[col] = {
size: border.size,
color: border.color,
y: y,
left: left,
right: right
};
}
}
return { add: add, horiz: horiz, vert: vert };
}
function drawTabularData(options) {
var progress = new $.Deferred();
var promise = progress.promise();
options = clone(options, {
dataSource : null,
guidelines : true,
guideColor : "#000",
columns : null,
headerBackground : "#999",
headerColor : "#000",
oddBackground : null,
evenBackground : null,
fontFamily : "Arial",
fontSize : 12,
paperSize : "A4",
margin : "1cm",
landscape : true,
fitWidth : false,
scale : 1,
rowHeight : 20,
maxEmpty : 1,
useGridFormat : true
});
// retrieve fonts; custom fonts should be already loaded
kendo.drawing.pdf.defineFont(
kendo.drawing.drawDOM.getFontFaces(document)
);
var charWidth = charWidthFunction(options.fontFamily, options.fontSize);
function textWidth(value) {
if (value != null) {
var width = 12; // magic numbers :-/
for (var i = value.length; --i >= 0;) {
width += charWidth(value.charAt(i));
}
return width;
}
return 0;
}
var border = options.guidelines ? { size: 1, color: options.guideColor } : null;
function mkCell(data) {
if (!border) {
return data;
}
return clone(data, {
borderLeft: border,
borderTop: border,
borderRight: border,
borderBottom: border
});
}
options.dataSource.fetch(function(){
var data = options.dataSource.data();
if (!data.length) {
return progress.reject("Empty dataset");
}
// this really must be present
var columns = options.columns.map(function(col){
if (typeof col == "string") {
return { title: col, field: col };
} else {
return col;
}
});
var columnTitles = columns.map(function(col){
return col.title || col.field;
});
var columnWidths = columnTitles.map(textWidth);
// prepare data for a Sheet object's fromJSON method
var rows = data.map(function(row, rowIndex){
return {
cells: columns.map(function(col, colIndex){
var value = row[col.field];
// NOTE: value might not be string. I added option useGridFormat (default
// true), which will use a column's format, if present, to convert the value
// to a string, so that we can measure the width right now.
if (options.useGridFormat) {
if (value != null) {
if (col.format) {
value = kendo.format(col.format, value);
} else {
value += "";
}
}
// adjust the column widths while we're at it
columnWidths[colIndex] = Math.max(
textWidth(value),
columnWidths[colIndex]
);
}
// if options.useGridFormat is false and col.format is present, pass it over
// to the spreadsheet. In that case we should calculate the widths after
// the spreadsheet is created (XXX to be implemented when someone needs it).
return mkCell({
value: value,
format: options.useGridFormat ? null : col.format,
background: rowIndex % 2 ? options.evenBackground : options.oddBackground
});
})
};
});
// insert header line
rows.unshift({
cells: columnTitles.map(function(label){
return mkCell({
value: label,
background: options.headerBackground,
color: options.headerColor
});
})
});
// init a Sheet object. Note that we have to add one
// extra-row and column, because the very last ones can't
// have right/bottom borders (known limitation).
var sheet = new kendo.spreadsheet.Sheet(
rows.length + 1, // rows
columns.length + 1, // columns
options.rowHeight, // row height
50, // column width
20, // header height
20, // header width,
{ // default cell style
fontFamily: options.fontFamily,
fontSize: options.fontSize,
verticalAlign: "center"
}
);
// load data
sheet.fromJSON({
name: "Sheet1",
rows: rows,
columns: columnWidths.map(function(w, i){
return { index: i, width: w };
})
});
sheet.draw({
paperSize : options.paperSize,
landscape : options.landscape,
margin : options.margin,
guidelines : false, // using borders instead (better contrast)
scale : options.scale,
fitWidth : options.fitWidth,
maxEmpty : options.maxEmpty,
headerRows : 1
}, progress.resolve.bind(progress));
});
return promise;
}
var CACHE_CHAR_WIDTH = {};
var charWidthFunction = function(fontFamily, fontSize) {
var id = fontSize + ":" + fontFamily;
var func = CACHE_CHAR_WIDTH[id];
if (!func) {
var span, div = document.createElement("div");
div.style.position = "fixed";
div.style.left = "-10000px";
div.style.top = "-10000px";
div.style.fontFamily = fontFamily;
div.style.fontSize = fontSize + "px";
div.style.whiteSpace = "pre";
for (var i = 32; i < 128; ++i) {
span = document.createElement("span");
span.appendChild(document.createTextNode(String.fromCharCode(i)));
div.appendChild(span);
}
document.body.appendChild(div);
var widths = {};
for (i = 32, span = div.firstChild; i < 128 && span; ++i, span = span.nextSibling) {
widths[i] = span.offsetWidth;
}
while ((span = div.firstChild)) {
div.removeChild(span);
}
func = CACHE_CHAR_WIDTH[id] = function(ch) {
var code = ch.charCodeAt(0);
var width = widths[code];
if (width == null) {
// probably not an ASCII character, let's cache its width as well
span = document.createElement("span");
span.appendChild(document.createTextNode(String.fromCharCode(code)));
div.appendChild(span);
width = widths[code] = span.offsetWidth;
div.removeChild(span);
}
return width;
};
}
return func;
};
spreadsheet.draw = {
Borders : Borders,
doLayout : doLayout,
applyIndent : applyIndent
};
spreadsheet.drawTabularData = drawTabularData;
}, __webpack_require__(3));
/***/ }),
/***/ 1524:
/***/ (function(module, exports) {
module.exports = require("../kendo.pdf");
/***/ }),
/***/ 1525:
/***/ (function(module, exports) {
module.exports = require("./numformat");
/***/ })
/******/ });