import * as decamelize from 'decamelize'; import * as XMLWriter from 'xml-writer'; import * as process from 'process'; // import { XlsfoComponent } from './xslfoComponent'; import { XlsfoComponent } from './xslfoComponent'; import { TagProps, XslfoElement, XslfoNode } from './elements'; import { Elements } from './fopTypes'; // tslint:disable-next-line:variable-name const XSLFOElementType = Symbol('xslfo.element'); const twoPartProperties = [ 'block-progression-dimension.maximum', 'block-progression-dimension.minimum', 'block-progression-dimension.optimum', 'border-before-width.conditional', 'border-before-width.length', 'border-separation.block-progression-direction', 'border-separation.inline-progression-direction', 'inline-progression-dimension.maximum', 'inline-progression-dimension.minimum', 'inline-progression-dimension.optimum', 'keep-together.within-column', 'keep-together.within-line', 'keep-together.within-page', 'keep-with-next.within-line', 'keep-with-previous.within-line', 'leader-length.maximum', 'leader-length.minimum', 'leader-length.optimum', 'line-height.conditionality', 'line-height.maximum', 'line-height.minimum', 'line-height.optimum', 'line-height.precedence', 'space-after.precedence', 'space-before.conditionality', 'space-before.maximum', 'space-before.minimum', 'space-before.optimum', 'space-before.precedence', 'space.minimum' ].map(p => p.replace(/[.].*/, '')); function fixAttributeName(attributeName) { attributeName = decamelize(attributeName, '-'); const splitFrom = twoPartProperties.find(p => attributeName.indexOf(p) === 0); if (splitFrom) { return `${splitFrom}.${attributeName.substring(splitFrom.length + 1)}`; } else { return attributeName; } } function renderAttributes(attributes) { if (!attributes) { return; } return Object.keys(attributes).reduce((prev, curr) => { return prev + (attributes[curr] !== undefined ? ' ' + fixAttributeName(curr) + '="' + attributes[curr] + '"' : ''); }, ''); } export function createElement

(type, props, ...children: Array): XslfoElement

{ const element = { $$typeof: XSLFOElementType, type, props: { ...props } }; if (children) { element.props.children = children.length === 1 ? children[0] : children; } return element; } export namespace createElement { export namespace JSX { export interface Element extends XslfoElement {} export interface ElementClass extends XlsfoComponent { render(): XslfoNode; } export interface ElementAttributesProperty { props: {}; } export interface IntrinsicAttributes { // key?: string | number; } export interface IntrinsicClassAttributes {} export interface IntrinsicElements extends Elements {} // interface IntrinsicElements { // [name: string]: any; // } } } function elementToStream(element, writer) { if (!element) { return; } if (typeof(element) === 'string') { writer.text(element); } else if (Array.isArray(element)) { Array.prototype.forEach.call(element, (e) => elementToStream(e, writer)); } else { writer.startElementNS('fo', element.tag); let innerXML; for (const attributeName in element.attributes) { if (attributeName === 'dangerouslySetInnerXML') { innerXML = element.attributes[attributeName].__xml; } else { writer.writeAttribute(fixAttributeName(attributeName), element.attributes[attributeName]); } } if (innerXML) { writer.writeRaw(innerXML); } else { elementToStream(element.children, writer); } writer.endElement(); } } function renderToXmlWriter(element, writer) { const elementTree = processElement(element); writer.startDocument('1.0', 'UTF-8'); elementToStream(elementTree, writer); writer.endDocument(); } export function renderToString(element) { const writer = new XMLWriter(true); renderToXmlWriter(element, writer); return writer.toString(); } export function renderToStream(element: XslfoElement, stream: NodeJS.WritableStream) { const writer = new XMLWriter(true, (a, b) => { stream.write(a, 'utf8'); }); renderToXmlWriter(element, writer); } // tslint:disable-next-line:variable-name export const Children = { map(children, fn, thisArg?: any) { if (Array.isArray(children)) { Array.prototype.forEach.call(children, (child) => Children.map(child, fn, thisArg)); } else { return fn.call(thisArg, children); } }, only(children) { if (Array.isArray(children)) { throw new Error('XSLFO.Children.only should only be passed a children with exactly one child.'); } if (typeof(children) === 'undefined') { throw new Error('XSLFO.Children.only should only be passed a children with exactly one child.'); } return children; }, // prolly not the most performant count(children) { let count = 0; this.map(children, x => count++); return count; } }; export function processElement(element) { if (!element) { return element; } if (typeof(element) === 'string') { return element; } else if (typeof(element) === 'number') { return element.toString(); } else if (Array.isArray(element)) { return element.map(processElement); } else { if (element.$$typeof !== XSLFOElementType) { throw Error(`Not an XSLFOElement, instead of ${typeof(element)}, ${element.$$typeof}`); } if (typeof(element.type) === 'string') { const { children, ...attributes } = element.props; const processedChildren = processElement(children); return { tag: decamelize(element.type, '-'), attributes, children: processedChildren }; } else { let childTree; if (typeof(element.type === 'function')) { let type; try { type = new element.type(element.props); } catch (ex) { type = element.type(element.props); } if (type.render) { childTree = type.render(); } else { childTree = type; } } else { throw new Error("I don't know what this is..."); } return processElement(childTree); } } } export function cloneElement(element, props, ...children) { const { props: originalProps, children: originalChildren, ...rest } = element; return { ...rest, props: Object.assign(Object.create(null), originalProps, props), children: (children ? children : originalChildren) }; }