devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
575 lines (482 loc) • 18.3 kB
JavaScript
"use strict";
var extend = require("../../core/utils/extend").extend,
inArray = require("../../core/utils/array").inArray,
windowUtils = require("../../core/utils/window"),
patchFontOptions = require("./utils").patchFontOptions,
_extend = extend,
clientExporter = require("../../client_exporter"),
messageLocalization = require("../../localization/message"),
imageExporter = clientExporter.image,
svgExporter = clientExporter.svg,
pdfExporter = clientExporter.pdf,
isDefined = require("../../core/utils/type").isDefined,
hoverEvent = require("../../events/hover"),
pointerEvents = require("../../events/pointer"),
pointerActions = [pointerEvents.down, pointerEvents.move].join(" "),
BUTTON_SIZE = 35,
ICON_COORDS = [[9, 12, 26, 12, 26, 14, 9, 14], [9, 17, 26, 17, 26, 19, 9, 19], [9, 22, 26, 22, 26, 24, 9, 24]],
LIST_PADDING_TOP = 4,
LIST_WIDTH = 120,
VERTICAL_TEXT_MARGIN = 8,
HORIZONTAL_TEXT_MARGIN = 15,
MENU_ITEM_HEIGHT = 30,
LIST_STROKE_WIDTH = 1,
MARGIN = 10,
SHADOW_OFFSET = 2,
SHADOW_BLUR = 3,
ALLOWED_EXPORT_FORMATS = ["PNG", "PDF", "JPEG", "SVG", "GIF"],
EXPORT_CSS_CLASS = "dx-export-menu",
EXPORT_DATA_KEY = "export-element-type",
FORMAT_DATA_KEY = "export-element-format";
function validateFormat(format) {
var validatedFormat = String(format).toUpperCase();
if (inArray(validatedFormat, ALLOWED_EXPORT_FORMATS) !== -1) {
return validatedFormat;
}
}
function getCreatorFunc(format) {
if (format === "SVG") {
return svgExporter.getData;
} else if (format === "PDF") {
return pdfExporter.getData;
} else {
return imageExporter.getData;
}
}
function doExport(menu, markup, options) {
menu && menu.hide();
clientExporter.export(markup(), options, getCreatorFunc(options.format));
menu && menu.show();
}
function _print(data, backgroundColor) {
var vizWindow = windowUtils.openWindow(),
svg;
if (!vizWindow) {
return;
}
vizWindow.document.open();
vizWindow.document.write(data);
vizWindow.document.close();
svg = vizWindow.document.body.getElementsByTagName("svg")[0];
svg && (svg.style.backgroundColor = backgroundColor);
vizWindow.print();
vizWindow.close();
}
function getItemAttributes(options, type, itemIndex) {
var path,
attr = {},
x = BUTTON_SIZE - LIST_WIDTH,
y = BUTTON_SIZE + LIST_PADDING_TOP + LIST_STROKE_WIDTH + itemIndex * MENU_ITEM_HEIGHT;
attr.rect = {
width: LIST_WIDTH - LIST_STROKE_WIDTH * 2,
height: MENU_ITEM_HEIGHT,
x: x + LIST_STROKE_WIDTH,
y: y
};
attr.text = {
x: x + (options.rtl ? LIST_WIDTH - HORIZONTAL_TEXT_MARGIN : HORIZONTAL_TEXT_MARGIN),
y: y + MENU_ITEM_HEIGHT - VERTICAL_TEXT_MARGIN
};
if (type === "printing") {
path = "M " + x + " " + (y + MENU_ITEM_HEIGHT - LIST_STROKE_WIDTH) + " " + "L " + (x + LIST_WIDTH) + " " + (y + MENU_ITEM_HEIGHT - LIST_STROKE_WIDTH);
attr.separator = {
stroke: options.button.default.borderColor,
"stroke-width": LIST_STROKE_WIDTH,
cursor: "pointer",
sharp: "v",
d: path
};
}
return attr;
}
function createMenuItem(renderer, options, settings) {
var itemData = {},
menuItem,
type = settings.type,
format = settings.format,
attr = getItemAttributes(options, type, settings.itemIndex),
fontStyle = patchFontOptions(options.font),
rect = renderer.rect(),
text = renderer.text(settings.text);
fontStyle["pointer-events"] = "none";
menuItem = renderer.g().attr({ "class": EXPORT_CSS_CLASS + "-list-item" });
itemData[EXPORT_DATA_KEY] = type;
if (format) {
itemData[FORMAT_DATA_KEY] = format;
}
rect.attr(attr.rect).css({
cursor: "pointer",
"pointer-events": "all"
}).data(itemData);
rect.on(hoverEvent.start + ".export", function () {
rect.attr({ fill: options.button.hover.backgroundColor });
}).on(hoverEvent.end + ".export", function () {
rect.attr({ fill: null });
});
rect.append(menuItem);
text.css(fontStyle).attr(attr.text).append(menuItem);
if (type === "printing") {
renderer.path(null, "line").attr(attr.separator).append(menuItem);
}
return {
g: menuItem,
rect: rect,
resetState: function resetState() {
rect.attr({ fill: null });
},
fixPosition: function fixPosition() {
var textBBox = text.getBBox(),
x = attr.text.x - textBBox.x;
options.rtl && (x -= textBBox.width);
text.move(x);
}
};
}
function createMenuItems(renderer, options) {
var formats = options.formats,
items = [];
if (options.printingEnabled) {
items.push(createMenuItem(renderer, options, {
type: "printing",
text: messageLocalization.format("vizExport-printingButtonText"),
itemIndex: items.length
}));
}
items = formats.reduce(function (r, format) {
format = validateFormat(format);
if (format) {
r.push(createMenuItem(renderer, options, {
type: "exporting",
text: messageLocalization.getFormatter("vizExport-exportButtonText")(format),
format: format,
itemIndex: r.length
}));
}
return r;
}, items);
return items;
}
exports.exportFromMarkup = function (markup, options) {
options.format = validateFormat(options.format) || "PNG";
options.fileName = options.fileName || "file";
options.exportingAction = options.onExporting;
options.exportedAction = options.onExported;
options.fileSavingAction = options.onFileSaving;
options.margin = isDefined(options.margin) ? options.margin : MARGIN;
clientExporter.export(markup, options, getCreatorFunc(options.format));
};
exports.getMarkup = function (widgets) {
var svgArr = [],
height = 0,
width = 0;
widgets.forEach(function (widget) {
var size = widget.getSize();
svgArr.push(widget.svg().replace('<svg', '<g transform="translate(0,' + height + ')" ').replace('</svg>', '</g>'));
height += size.height;
width = Math.max(width, size.width);
});
return '<svg height="' + height + '" width="' + width + '" version="1.1" xmlns="http://www.w3.org/2000/svg">' + svgArr.join('') + '</svg>';
};
exports.ExportMenu = function (params) {
var that = this,
renderer = that._renderer = params.renderer;
that._incidentOccurred = params.incidentOccurred;
that._svgMethod = params.svgMethod;
that._shadow = renderer.shadowFilter("-50%", "-50%", "200%", "200%", SHADOW_OFFSET, 6, SHADOW_BLUR);
that._shadow.attr({ opacity: 0.8 });
that._group = renderer.g().attr({ "class": EXPORT_CSS_CLASS }).linkOn(renderer.root, { name: "export-menu", after: "peripheral" });
that._buttonGroup = renderer.g().attr({ "class": EXPORT_CSS_CLASS + "-button" }).append(that._group);
that._listGroup = renderer.g().attr({ "class": EXPORT_CSS_CLASS + "-list" }).append(that._group);
that._overlay = renderer.rect(-LIST_WIDTH + BUTTON_SIZE, BUTTON_SIZE + LIST_PADDING_TOP, LIST_WIDTH, 0);
that._overlay.attr({
"stroke-width": LIST_STROKE_WIDTH,
cursor: "pointer",
rx: 4,
ry: 4,
filter: that._shadow.id
});
that._overlay.data({ "export-element-type": "list" });
that._subscribeEvents();
};
_extend(exports.ExportMenu.prototype, {
getLayoutOptions: function getLayoutOptions() {
if (this._hiddenDueToLayout) {
return { width: 0, height: 0, cutSide: "vertical", cutLayoutSide: "top" };
}
var bBox = this._buttonGroup.getBBox();
bBox.cutSide = "vertical";
bBox.cutLayoutSide = "top";
bBox.height += MARGIN;
bBox.position = {
vertical: "top",
horizontal: "right"
};
bBox.verticalAlignment = "top";
bBox.horizontalAlignment = "right";
return bBox;
},
probeDraw: function probeDraw() {
this._fillSpace();
this.show();
},
shift: function shift(_, y) {
this._group.attr({ translateY: this._group.attr("translateY") + y });
},
draw: function draw(width, height, canvas) {
var layoutOptions;
this._options.exportOptions.width = canvas.width;
this._options.exportOptions.height = canvas.height;
this._group.move(width - BUTTON_SIZE - SHADOW_OFFSET - SHADOW_BLUR + canvas.left, Math.floor(height / 2 - BUTTON_SIZE / 2));
layoutOptions = this.getLayoutOptions();
if (layoutOptions.width > width || layoutOptions.height > height) {
this.freeSpace();
}
return this;
},
show: function show() {
this._group.linkAppend();
},
hide: function hide() {
this._group.linkRemove();
},
setOptions: function setOptions(options) {
this._options = options;
options.formats = options.formats || ALLOWED_EXPORT_FORMATS;
options.printingEnabled = options.printingEnabled === undefined ? true : options.printingEnabled;
if (options.enabled && (options.formats.length || options.printingEnabled)) {
this.show();
this._updateButton();
this._updateList();
this._hideList();
} else {
this.hide();
}
},
dispose: function dispose() {
var that = this;
that._unsubscribeEvents();
that._group.linkRemove().linkOff();
that._group.dispose();
that._shadow.dispose();
that._shadow = that._group = that._listGroup = that._buttonGroup = that._button = null;
that._options = null;
},
// BaseWidget_layout_implementation
layoutOptions: function layoutOptions() {
var options = this._options;
return options.enabled && { horizontalAlignment: "right", verticalAlignment: "top", weak: true };
},
measure: function measure() {
this._fillSpace();
return [BUTTON_SIZE + SHADOW_OFFSET, BUTTON_SIZE];
},
move: function move(rect) {
this._group.attr({ translateX: Math.round(rect[0]), translateY: Math.round(rect[1]) });
},
_fillSpace: function _fillSpace() {
this._hiddenDueToLayout = false;
this.show();
},
freeSpace: function freeSpace() {
this._incidentOccurred("W2107");
this._hiddenDueToLayout = true;
this.hide();
},
// BaseWidget_layout_implementation
_hideList: function _hideList() {
this._listGroup.remove();
this._listShown = false;
this._setButtonState("default");
this._menuItems.forEach(function (item) {
item.resetState();
});
},
_showList: function _showList() {
this._listGroup.append(this._group);
this._listShown = true;
this._menuItems.forEach(function (item) {
item.fixPosition();
});
},
_setButtonState: function _setButtonState(state) {
var that = this,
style = that._options.button[state];
this._button.attr({
stroke: style.borderColor,
fill: style.backgroundColor
});
this._icon.attr({ fill: style.color });
},
_subscribeEvents: function _subscribeEvents() {
var that = this;
that._renderer.root.on(pointerEvents.up + ".export", function (e) {
var elementType = e.target[EXPORT_DATA_KEY],
exportOptions,
options = that._options;
if (!elementType) {
if (that._button) {
that._hideList();
}
return;
}
if (elementType === "button") {
if (that._listShown) {
that._setButtonState("default");
that._hideList();
} else {
that._setButtonState("focus");
that._showList();
}
} else if (elementType === "printing") {
that.hide();
_print(that._svgMethod(), options.backgroundColor);
that.show();
that._hideList();
} else if (elementType === "exporting") {
exportOptions = _extend({}, options.exportOptions, { format: e.target[FORMAT_DATA_KEY] });
doExport(that, function () {
return that._svgMethod();
}, exportOptions);
that._hideList();
}
});
that._listGroup.on(pointerActions, function (e) {
e.stopPropagation();
});
that._buttonGroup.on(pointerEvents.enter, function () {
that._setButtonState("hover");
});
that._buttonGroup.on(pointerEvents.leave, function () {
that._setButtonState(that._listShown ? "focus" : "default");
});
that._buttonGroup.on(pointerEvents.down + ".export", function () {
that._setButtonState("active");
});
},
_unsubscribeEvents: function _unsubscribeEvents() {
this._renderer.root.off(".export");
this._listGroup.off();
this._buttonGroup.off();
},
_updateButton: function _updateButton() {
var that = this,
renderer = that._renderer,
options = that._options,
iconAttr = {
fill: options.button.default.color,
cursor: "pointer"
},
exportData = { "export-element-type": "button" };
if (!that._button) {
that._button = renderer.rect(0, 0, BUTTON_SIZE, BUTTON_SIZE).append(that._buttonGroup);
that._button.attr({
rx: 4,
ry: 4,
fill: options.button.default.backgroundColor,
stroke: options.button.default.borderColor,
"stroke-width": 1,
cursor: "pointer"
});
that._button.data(exportData);
that._icon = renderer.path(ICON_COORDS).append(that._buttonGroup);
that._icon.attr(iconAttr);
that._icon.data(exportData);
that._buttonGroup.setTitle(messageLocalization.format("vizExport-titleMenuText"));
}
},
_updateList: function _updateList() {
var that = this,
options = that._options,
buttonDefault = options.button.default,
listGroup = that._listGroup,
items = createMenuItems(that._renderer, options);
that._shadow.attr({
color: options.shadowColor
});
that._overlay.attr({
height: items.length * MENU_ITEM_HEIGHT + LIST_STROKE_WIDTH * 2,
fill: buttonDefault.backgroundColor,
stroke: buttonDefault.borderColor
});
listGroup.clear();
that._overlay.append(listGroup);
items.forEach(function (item) {
item.g.append(listGroup);
});
that._menuItems = items;
}
});
// BaseWidget.js
function getExportOptions(widget, exportOptions, fileName, format) {
return {
format: validateFormat(format || exportOptions.format) || "PNG",
fileName: fileName || exportOptions.fileName || "file",
proxyUrl: exportOptions.proxyUrl,
backgroundColor: exportOptions.backgroundColor,
width: widget._canvas.width,
height: widget._canvas.height,
margin: exportOptions.margin,
exportingAction: widget._createActionByOption("onExporting"),
exportedAction: widget._createActionByOption("onExported"),
fileSavingAction: widget._createActionByOption("onFileSaving")
};
}
exports.plugin = {
name: "export",
init: function init() {
var that = this;
that._exportMenu = new exports.ExportMenu({
renderer: that._renderer,
svgMethod: function svgMethod() {
return that.svg();
},
incidentOccurred: that._incidentOccurred
});
that._layout.add(that._exportMenu);
},
dispose: function dispose() {
this._exportMenu.dispose();
this._exportMenu = null;
},
extenders: {
_change_LAYOUT: function _change_LAYOUT() {
if (this._exportMenu) {
this._exportMenu.setOptions(this._getExportMenuOptions());
}
}
},
members: {
_getExportMenuOptions: function _getExportMenuOptions() {
var userOptions = this._getOption("export") || {},
options = getExportOptions(this, userOptions);
return _extend({}, userOptions, { exportOptions: options, rtl: this._getOption("rtlEnabled", true) });
},
exportTo: function exportTo(fileName, format) {
var that = this,
exportOptions = getExportOptions(that, that._getOption("export") || {}, fileName, format);
doExport(that._exportMenu, function () {
return that.svg();
}, exportOptions);
},
print: function print() {
_print(this.svg(), this._getOption("export").backgroundColor);
}
},
customize: function customize(constructor) {
var proto = constructor.prototype;
constructor.addChange({
code: "EXPORT",
handler: function handler() {
this._exportMenu.setOptions(this._getExportMenuOptions());
this._change(["LAYOUT"]);
},
isThemeDependent: true,
isOptionChange: true,
option: "export"
});
// TODO: Event options change processing either should be done by the eventTrigger or shouldn't be done at all
proto._optionChangesMap.onExporting = "EXPORT";
proto._optionChangesMap.onExported = "EXPORT";
proto._optionChangesMap.onFileSaving = "EXPORT";
}
};