mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Library rewrite
This commit is contained in:
committed by
Niklas von Hertzen
parent
83e9b85e1e
commit
8a6fb5f733
366
src/parsing/background.js
Normal file
366
src/parsing/background.js
Normal file
@@ -0,0 +1,366 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
import type {Bounds} from '../Bounds';
|
||||
import type ImageLoader from '../ImageLoader';
|
||||
|
||||
import Color from '../Color';
|
||||
import Length from '../Length';
|
||||
import Size from '../Size';
|
||||
import Vector from '../Vector';
|
||||
|
||||
export type Background = {
|
||||
backgroundImage: Array<BackgroundImage>,
|
||||
backgroundColor: Color
|
||||
};
|
||||
|
||||
export type BackgroundRepeat = $Values<typeof BACKGROUND_REPEAT>;
|
||||
export type BackgroundSizeTypes = $Values<typeof BACKGROUND_SIZE>;
|
||||
|
||||
export type BackgroundSource = {
|
||||
prefix: string,
|
||||
method: string,
|
||||
args: Array<string>
|
||||
};
|
||||
|
||||
export type BackgroundImage = {
|
||||
source: BackgroundSource,
|
||||
position: [Length, Length],
|
||||
size: [BackgroundSize, BackgroundSize],
|
||||
repeat: BackgroundRepeat
|
||||
};
|
||||
|
||||
export const BACKGROUND_REPEAT = {
|
||||
REPEAT: 0,
|
||||
NO_REPEAT: 1,
|
||||
REPEAT_X: 2,
|
||||
REPEAT_Y: 3
|
||||
};
|
||||
|
||||
export const BACKGROUND_SIZE = {
|
||||
AUTO: 0,
|
||||
CONTAIN: 1,
|
||||
COVER: 2,
|
||||
LENGTH: 3
|
||||
};
|
||||
|
||||
const AUTO = 'auto';
|
||||
|
||||
class BackgroundSize {
|
||||
size: ?BackgroundSizeTypes;
|
||||
value: ?Length;
|
||||
|
||||
constructor(size: string) {
|
||||
switch (size) {
|
||||
case 'contain':
|
||||
this.size = BACKGROUND_SIZE.CONTAIN;
|
||||
break;
|
||||
case 'cover':
|
||||
this.size = BACKGROUND_SIZE.COVER;
|
||||
break;
|
||||
case 'auto':
|
||||
this.size = BACKGROUND_SIZE.AUTO;
|
||||
break;
|
||||
default:
|
||||
this.value = new Length(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const calculateBackgroundSize = (
|
||||
backgroundImage: BackgroundImage,
|
||||
image: HTMLImageElement,
|
||||
bounds: Bounds
|
||||
): Size => {
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
const size = backgroundImage.size;
|
||||
if (size[0].size === BACKGROUND_SIZE.CONTAIN || size[0].size === BACKGROUND_SIZE.COVER) {
|
||||
const targetRatio = bounds.width / bounds.height;
|
||||
const currentRatio = image.width / image.height;
|
||||
return targetRatio < currentRatio !== (size[0].size === BACKGROUND_SIZE.COVER)
|
||||
? new Size(bounds.height * currentRatio, bounds.height)
|
||||
: new Size(bounds.width, bounds.width / currentRatio);
|
||||
}
|
||||
|
||||
if (size[0].value) {
|
||||
width = size[0].value.getAbsoluteValue(bounds.width);
|
||||
}
|
||||
|
||||
if (size[0].size === BACKGROUND_SIZE.AUTO && size[1].size === BACKGROUND_SIZE.AUTO) {
|
||||
height = image.height;
|
||||
} else if (size[1].size === BACKGROUND_SIZE.AUTO) {
|
||||
height = width / image.width * image.height;
|
||||
} else if (size[1].value) {
|
||||
height = size[1].value.getAbsoluteValue(bounds.height);
|
||||
}
|
||||
|
||||
if (size[0].size === BACKGROUND_SIZE.AUTO) {
|
||||
width = height / image.height * image.width;
|
||||
}
|
||||
|
||||
return new Size(width, height);
|
||||
};
|
||||
|
||||
const AUTO_SIZE = new BackgroundSize(AUTO);
|
||||
|
||||
export const calculateBackgroundPosition = (
|
||||
position: [Length, Length],
|
||||
size: Size,
|
||||
bounds: Bounds
|
||||
): Vector => {
|
||||
return new Vector(
|
||||
position[0].getAbsoluteValue(bounds.width - size.width),
|
||||
position[1].getAbsoluteValue(bounds.height - size.height)
|
||||
);
|
||||
};
|
||||
|
||||
export const calculateBackgroundRepeatPath = (
|
||||
background: BackgroundImage,
|
||||
position: Vector,
|
||||
size: Size,
|
||||
bounds: Bounds
|
||||
) => {
|
||||
const repeat = background.repeat;
|
||||
switch (repeat) {
|
||||
case BACKGROUND_REPEAT.REPEAT_X:
|
||||
return [
|
||||
new Vector(Math.round(bounds.left), Math.round(bounds.top + position.y)),
|
||||
new Vector(
|
||||
Math.round(bounds.left + bounds.width),
|
||||
Math.round(bounds.top + position.y)
|
||||
),
|
||||
new Vector(
|
||||
Math.round(bounds.left + bounds.width),
|
||||
Math.round(size.height + bounds.top + position.y)
|
||||
),
|
||||
new Vector(
|
||||
Math.round(bounds.left),
|
||||
Math.round(size.height + bounds.top + position.y)
|
||||
)
|
||||
];
|
||||
case BACKGROUND_REPEAT.REPEAT_Y:
|
||||
return [
|
||||
new Vector(Math.round(bounds.left + position.x), Math.round(bounds.top)),
|
||||
new Vector(
|
||||
Math.round(bounds.left + position.x + size.width),
|
||||
Math.round(bounds.top)
|
||||
),
|
||||
new Vector(
|
||||
Math.round(bounds.left + position.x + size.width),
|
||||
Math.round(bounds.height + bounds.top)
|
||||
),
|
||||
new Vector(
|
||||
Math.round(bounds.left + position.x),
|
||||
Math.round(bounds.height + bounds.top)
|
||||
)
|
||||
];
|
||||
case BACKGROUND_REPEAT.NO_REPEAT:
|
||||
return [
|
||||
new Vector(
|
||||
Math.round(bounds.left + position.x),
|
||||
Math.round(bounds.top + position.y)
|
||||
),
|
||||
new Vector(
|
||||
Math.round(bounds.left + position.x + size.width),
|
||||
Math.round(bounds.top + position.y)
|
||||
),
|
||||
new Vector(
|
||||
Math.round(bounds.left + position.x + size.width),
|
||||
Math.round(bounds.top + position.y + size.height)
|
||||
),
|
||||
new Vector(
|
||||
Math.round(bounds.left + position.x),
|
||||
Math.round(bounds.top + position.y + size.height)
|
||||
)
|
||||
];
|
||||
default:
|
||||
return [
|
||||
new Vector(Math.round(bounds.left), Math.round(bounds.top)),
|
||||
new Vector(Math.round(bounds.left + bounds.width), Math.round(bounds.top)),
|
||||
new Vector(
|
||||
Math.round(bounds.left + bounds.width),
|
||||
Math.round(bounds.height + bounds.top)
|
||||
),
|
||||
new Vector(Math.round(bounds.left), Math.round(bounds.height + bounds.top))
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
export const parseBackground = (
|
||||
style: CSSStyleDeclaration,
|
||||
imageLoader: ImageLoader
|
||||
): Background => {
|
||||
return {
|
||||
backgroundImage: parseBackgroundImages(style, imageLoader),
|
||||
backgroundColor: new Color(style.backgroundColor)
|
||||
};
|
||||
};
|
||||
|
||||
const parseBackgroundRepeat = (backgroundRepeat: string): BackgroundRepeat => {
|
||||
switch (backgroundRepeat) {
|
||||
case 'no-repeat':
|
||||
return BACKGROUND_REPEAT.NO_REPEAT;
|
||||
case 'repeat-x':
|
||||
case 'repeat no-repeat':
|
||||
return BACKGROUND_REPEAT.REPEAT_X;
|
||||
case 'repeat-y':
|
||||
case 'no-repeat repeat':
|
||||
return BACKGROUND_REPEAT.REPEAT_Y;
|
||||
case 'repeat':
|
||||
return BACKGROUND_REPEAT.REPEAT;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
console.error(`Invalid background-repeat value "${backgroundRepeat}"`);
|
||||
}
|
||||
|
||||
return BACKGROUND_REPEAT.REPEAT;
|
||||
};
|
||||
|
||||
const parseBackgroundImages = (
|
||||
style: CSSStyleDeclaration,
|
||||
imageLoader: ImageLoader
|
||||
): Array<BackgroundImage> => {
|
||||
const sources: Array<BackgroundSource> = parseBackgroundImage(
|
||||
style.backgroundImage,
|
||||
imageLoader
|
||||
);
|
||||
const positions = style.backgroundPosition.split(',');
|
||||
const repeats = style.backgroundRepeat.split(',');
|
||||
const sizes = style.backgroundSize.split(',');
|
||||
|
||||
return sources.map((source, index) => {
|
||||
const size = (sizes[index] || AUTO).trim().split(' ').map(parseBackgroundSize);
|
||||
const position = (positions[index] || AUTO).trim().split(' ').map(parseBackgoundPosition);
|
||||
|
||||
return {
|
||||
source,
|
||||
repeat: parseBackgroundRepeat(repeats[index]),
|
||||
size: size.length < 2 ? [size[0], AUTO_SIZE] : [size[0], size[1]],
|
||||
position: position.length < 2 ? [position[0], position[0]] : [position[0], position[1]]
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const parseBackgroundSize = (size: string): BackgroundSize =>
|
||||
size === 'auto' ? AUTO_SIZE : new BackgroundSize(size);
|
||||
|
||||
const parseBackgoundPosition = (position: string): Length => {
|
||||
switch (position) {
|
||||
case 'bottom':
|
||||
case 'right':
|
||||
return new Length('100%');
|
||||
case 'left':
|
||||
case 'top':
|
||||
return new Length('0%');
|
||||
case 'auto':
|
||||
return new Length('0');
|
||||
}
|
||||
return new Length(position);
|
||||
};
|
||||
|
||||
const parseBackgroundImage = (image: string, imageLoader: ImageLoader): Array<BackgroundSource> => {
|
||||
const whitespace = /^\s$/;
|
||||
const results = [];
|
||||
|
||||
let args = [];
|
||||
let method = '';
|
||||
let quote = null;
|
||||
let definition = '';
|
||||
let mode = 0;
|
||||
let numParen = 0;
|
||||
|
||||
const appendResult = () => {
|
||||
let prefix = '';
|
||||
if (method) {
|
||||
if (definition.substr(0, 1) === '"') {
|
||||
definition = definition.substr(1, definition.length - 2);
|
||||
}
|
||||
|
||||
if (definition) {
|
||||
args.push(definition);
|
||||
}
|
||||
|
||||
const prefix_i = method.indexOf('-', 1) + 1;
|
||||
if (method.substr(0, 1) === '-' && prefix_i > 0) {
|
||||
prefix = method.substr(0, prefix_i).toLowerCase();
|
||||
method = method.substr(prefix_i);
|
||||
}
|
||||
method = method.toLowerCase();
|
||||
if (method === 'url') {
|
||||
const key = imageLoader.loadImage(args[0]);
|
||||
args = key ? [key] : [];
|
||||
}
|
||||
results.push({
|
||||
prefix,
|
||||
method,
|
||||
args
|
||||
});
|
||||
}
|
||||
args = [];
|
||||
method = definition = '';
|
||||
};
|
||||
|
||||
image.split('').forEach(c => {
|
||||
if (mode === 0 && whitespace.test(c)) {
|
||||
return;
|
||||
}
|
||||
switch (c) {
|
||||
case '"':
|
||||
if (!quote) {
|
||||
quote = c;
|
||||
} else if (quote === c) {
|
||||
quote = null;
|
||||
}
|
||||
break;
|
||||
case '(':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if (mode === 0) {
|
||||
mode = 1;
|
||||
return;
|
||||
} else {
|
||||
numParen++;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if (mode === 1) {
|
||||
if (numParen === 0) {
|
||||
mode = 0;
|
||||
appendResult();
|
||||
return;
|
||||
} else {
|
||||
numParen--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if (quote) {
|
||||
break;
|
||||
} else if (mode === 0) {
|
||||
appendResult();
|
||||
return;
|
||||
} else if (mode === 1) {
|
||||
if (numParen === 0 && !method.match(/^url$/i)) {
|
||||
args.push(definition);
|
||||
definition = '';
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode === 0) {
|
||||
method += c;
|
||||
} else {
|
||||
definition += c;
|
||||
}
|
||||
});
|
||||
|
||||
appendResult();
|
||||
return results;
|
||||
};
|
||||
49
src/parsing/border.js
Normal file
49
src/parsing/border.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
import Color from '../Color';
|
||||
|
||||
export const BORDER_STYLE = {
|
||||
NONE: 0,
|
||||
SOLID: 1
|
||||
};
|
||||
|
||||
export type BorderStyle = $Values<typeof BORDER_STYLE>;
|
||||
|
||||
export type Border = {
|
||||
borderColor: Color,
|
||||
borderStyle: BorderStyle,
|
||||
borderWidth: number
|
||||
};
|
||||
|
||||
export const BORDER_SIDES = {
|
||||
TOP: 0,
|
||||
RIGHT: 1,
|
||||
BOTTOM: 2,
|
||||
LEFT: 3
|
||||
};
|
||||
|
||||
export type BorderSide = $Values<typeof BORDER_SIDES>;
|
||||
|
||||
const SIDES = Object.keys(BORDER_SIDES).map(s => s.toLowerCase());
|
||||
|
||||
const parseBorderStyle = (style: string): BorderStyle => {
|
||||
switch (style) {
|
||||
case 'none':
|
||||
return BORDER_STYLE.NONE;
|
||||
}
|
||||
return BORDER_STYLE.SOLID;
|
||||
};
|
||||
|
||||
export const parseBorder = (style: CSSStyleDeclaration): Array<Border> => {
|
||||
return SIDES.map(side => {
|
||||
const borderColor = new Color(style.getPropertyValue(`border-${side}-color`));
|
||||
const borderStyle = parseBorderStyle(style.getPropertyValue(`border-${side}-style`));
|
||||
const borderWidth = parseInt(style.getPropertyValue(`border-${side}-width`), 10);
|
||||
return {
|
||||
borderColor,
|
||||
borderStyle,
|
||||
borderWidth: isNaN(borderWidth) ? 0 : borderWidth
|
||||
};
|
||||
});
|
||||
};
|
||||
15
src/parsing/borderRadius.js
Normal file
15
src/parsing/borderRadius.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
import Length from '../Length';
|
||||
|
||||
const SIDES = ['top-left', 'top-right', 'bottom-right', 'bottom-left'];
|
||||
|
||||
export type BorderRadius = Array<Length>;
|
||||
|
||||
export const parseBorderRadius = (style: CSSStyleDeclaration): Array<BorderRadius> => {
|
||||
return SIDES.map(side => {
|
||||
const value = style.getPropertyValue(`border-${side}-radius`);
|
||||
const [horizontal, vertical] = value.split(' ').map(Length.create);
|
||||
return typeof vertical === 'undefined' ? [horizontal, horizontal] : [horizontal, vertical];
|
||||
});
|
||||
};
|
||||
111
src/parsing/display.js
Normal file
111
src/parsing/display.js
Normal file
@@ -0,0 +1,111 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
export const DISPLAY = {
|
||||
NONE: 1 << 0,
|
||||
BLOCK: 1 << 1,
|
||||
INLINE: 1 << 2,
|
||||
RUN_IN: 1 << 3,
|
||||
FLOW: 1 << 4,
|
||||
FLOW_ROOT: 1 << 5,
|
||||
TABLE: 1 << 6,
|
||||
FLEX: 1 << 7,
|
||||
GRID: 1 << 8,
|
||||
RUBY: 1 << 9,
|
||||
SUBGRID: 1 << 10,
|
||||
LIST_ITEM: 1 << 11,
|
||||
TABLE_ROW_GROUP: 1 << 12,
|
||||
TABLE_HEADER_GROUP: 1 << 13,
|
||||
TABLE_FOOTER_GROUP: 1 << 14,
|
||||
TABLE_ROW: 1 << 15,
|
||||
TABLE_CELL: 1 << 16,
|
||||
TABLE_COLUMN_GROUP: 1 << 17,
|
||||
TABLE_COLUMN: 1 << 18,
|
||||
TABLE_CAPTION: 1 << 19,
|
||||
RUBY_BASE: 1 << 20,
|
||||
RUBY_TEXT: 1 << 21,
|
||||
RUBY_BASE_CONTAINER: 1 << 22,
|
||||
RUBY_TEXT_CONTAINER: 1 << 23,
|
||||
CONTENTS: 1 << 24,
|
||||
INLINE_BLOCK: 1 << 25,
|
||||
INLINE_LIST_ITEM: 1 << 26,
|
||||
INLINE_TABLE: 1 << 27,
|
||||
INLINE_FLEX: 1 << 28,
|
||||
INLINE_GRID: 1 << 29
|
||||
};
|
||||
|
||||
export type Display = $Values<typeof DISPLAY>;
|
||||
export type DisplayBit = number;
|
||||
|
||||
const parseDisplayValue = (display: string): Display => {
|
||||
switch (display) {
|
||||
case 'block':
|
||||
return DISPLAY.BLOCK;
|
||||
case 'inline':
|
||||
return DISPLAY.INLINE;
|
||||
case 'run-in':
|
||||
return DISPLAY.RUN_IN;
|
||||
case 'flow':
|
||||
return DISPLAY.FLOW;
|
||||
case 'flow-root':
|
||||
return DISPLAY.FLOW_ROOT;
|
||||
case 'table':
|
||||
return DISPLAY.TABLE;
|
||||
case 'flex':
|
||||
return DISPLAY.FLEX;
|
||||
case 'grid':
|
||||
return DISPLAY.GRID;
|
||||
case 'ruby':
|
||||
return DISPLAY.RUBY;
|
||||
case 'subgrid':
|
||||
return DISPLAY.SUBGRID;
|
||||
case 'list-item':
|
||||
return DISPLAY.LIST_ITEM;
|
||||
case 'table-row-group':
|
||||
return DISPLAY.TABLE_ROW_GROUP;
|
||||
case 'table-header-group':
|
||||
return DISPLAY.TABLE_HEADER_GROUP;
|
||||
case 'table-footer-group':
|
||||
return DISPLAY.TABLE_FOOTER_GROUP;
|
||||
case 'table-row':
|
||||
return DISPLAY.TABLE_ROW;
|
||||
case 'table-cell':
|
||||
return DISPLAY.TABLE_CELL;
|
||||
case 'table-column-group':
|
||||
return DISPLAY.TABLE_COLUMN_GROUP;
|
||||
case 'table-column':
|
||||
return DISPLAY.TABLE_COLUMN;
|
||||
case 'table-caption':
|
||||
return DISPLAY.TABLE_CAPTION;
|
||||
case 'ruby-base':
|
||||
return DISPLAY.RUBY_BASE;
|
||||
case 'ruby-text':
|
||||
return DISPLAY.RUBY_TEXT;
|
||||
case 'ruby-base-container':
|
||||
return DISPLAY.RUBY_BASE_CONTAINER;
|
||||
case 'ruby-text-container':
|
||||
return DISPLAY.RUBY_TEXT_CONTAINER;
|
||||
case 'contents':
|
||||
return DISPLAY.CONTENTS;
|
||||
case 'inline-block':
|
||||
return DISPLAY.INLINE_BLOCK;
|
||||
case 'inline-list-item':
|
||||
return DISPLAY.INLINE_LIST_ITEM;
|
||||
case 'inline-table':
|
||||
return DISPLAY.INLINE_TABLE;
|
||||
case 'inline-flex':
|
||||
return DISPLAY.INLINE_FLEX;
|
||||
case 'inline-grid':
|
||||
return DISPLAY.INLINE_GRID;
|
||||
}
|
||||
|
||||
return DISPLAY.NONE;
|
||||
};
|
||||
|
||||
const setDisplayBit = (bit: DisplayBit, display: string): DisplayBit => {
|
||||
return bit | parseDisplayValue(display);
|
||||
};
|
||||
|
||||
export const parseDisplay = (display: string): Display => {
|
||||
return display.split(' ').reduce(setDisplayBit, 0);
|
||||
};
|
||||
26
src/parsing/float.js
Normal file
26
src/parsing/float.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
export const FLOAT = {
|
||||
NONE: 0,
|
||||
LEFT: 1,
|
||||
RIGHT: 2,
|
||||
INLINE_START: 3,
|
||||
INLINE_END: 4
|
||||
};
|
||||
|
||||
export type Float = $Values<typeof FLOAT>;
|
||||
|
||||
export const parseCSSFloat = (float: string): Float => {
|
||||
switch (float) {
|
||||
case 'left':
|
||||
return FLOAT.LEFT;
|
||||
case 'right':
|
||||
return FLOAT.RIGHT;
|
||||
case 'inline-start':
|
||||
return FLOAT.INLINE_START;
|
||||
case 'inline-end':
|
||||
return FLOAT.INLINE_END;
|
||||
}
|
||||
return FLOAT.NONE;
|
||||
};
|
||||
38
src/parsing/font.js
Normal file
38
src/parsing/font.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
export type Font = {
|
||||
fontFamily: string,
|
||||
fontSize: string,
|
||||
fontStyle: string,
|
||||
fontVariant: string,
|
||||
fontWeight: number
|
||||
};
|
||||
|
||||
const parseFontWeight = (weight: string): number => {
|
||||
switch (weight) {
|
||||
case 'normal':
|
||||
return 400;
|
||||
case 'bold':
|
||||
return 700;
|
||||
}
|
||||
|
||||
const value = parseInt(weight, 10);
|
||||
return isNaN(value) ? 400 : value;
|
||||
};
|
||||
|
||||
export const parseFont = (style: CSSStyleDeclaration): Font => {
|
||||
const fontFamily = style.fontFamily;
|
||||
const fontSize = style.fontSize;
|
||||
const fontStyle = style.fontStyle;
|
||||
const fontVariant = style.fontVariant;
|
||||
const fontWeight = parseFontWeight(style.fontWeight);
|
||||
|
||||
return {
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontStyle,
|
||||
fontVariant,
|
||||
fontWeight
|
||||
};
|
||||
};
|
||||
10
src/parsing/letterSpacing.js
Normal file
10
src/parsing/letterSpacing.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
export const parseLetterSpacing = (letterSpacing: string): number => {
|
||||
if (letterSpacing === 'normal') {
|
||||
return 0;
|
||||
}
|
||||
const value = parseInt(letterSpacing, 10);
|
||||
return isNaN(value) ? 0 : value;
|
||||
};
|
||||
11
src/parsing/padding.js
Normal file
11
src/parsing/padding.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
import Length from '../Length';
|
||||
|
||||
const SIDES = ['top', 'right', 'bottom', 'left'];
|
||||
|
||||
export type Padding = Array<Length>;
|
||||
|
||||
export const parsePadding = (style: CSSStyleDeclaration): Padding => {
|
||||
return SIDES.map(side => new Length(style.getPropertyValue(`padding-${side}`)));
|
||||
};
|
||||
27
src/parsing/position.js
Normal file
27
src/parsing/position.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
export const POSITION = {
|
||||
STATIC: 0,
|
||||
RELATIVE: 1,
|
||||
ABSOLUTE: 2,
|
||||
FIXED: 3,
|
||||
STICKY: 4
|
||||
};
|
||||
|
||||
export type Position = $Values<typeof POSITION>;
|
||||
|
||||
export const parsePosition = (position: string): Position => {
|
||||
switch (position) {
|
||||
case 'relative':
|
||||
return POSITION.RELATIVE;
|
||||
case 'absolute':
|
||||
return POSITION.ABSOLUTE;
|
||||
case 'fixed':
|
||||
return POSITION.FIXED;
|
||||
case 'sticky':
|
||||
return POSITION.STICKY;
|
||||
}
|
||||
|
||||
return POSITION.STATIC;
|
||||
};
|
||||
88
src/parsing/textDecoration.js
Normal file
88
src/parsing/textDecoration.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
import Color from '../Color';
|
||||
|
||||
export const TEXT_DECORATION_STYLE = {
|
||||
SOLID: 0,
|
||||
DOUBLE: 1,
|
||||
DOTTED: 2,
|
||||
DASHED: 3,
|
||||
WAVY: 4
|
||||
};
|
||||
|
||||
export const TEXT_DECORATION = {
|
||||
NONE: null
|
||||
};
|
||||
|
||||
export const TEXT_DECORATION_LINE = {
|
||||
UNDERLINE: 1,
|
||||
OVERLINE: 2,
|
||||
LINE_THROUGH: 3,
|
||||
BLINK: 4
|
||||
};
|
||||
|
||||
export type TextDecorationStyle = $Values<typeof TEXT_DECORATION_STYLE>;
|
||||
export type TextDecorationLine = $Values<typeof TEXT_DECORATION_LINE>;
|
||||
type TextDecorationLineType = Array<TextDecorationLine> | null;
|
||||
export type TextDecoration =
|
||||
| {
|
||||
textDecorationLine: Array<TextDecorationLine>,
|
||||
textDecorationStyle: TextDecorationStyle,
|
||||
textDecorationColor: Color | null
|
||||
}
|
||||
| $Values<typeof TEXT_DECORATION>;
|
||||
|
||||
const parseLine = (line: string): TextDecorationLine => {
|
||||
switch (line) {
|
||||
case 'underline':
|
||||
return TEXT_DECORATION_LINE.UNDERLINE;
|
||||
case 'overline':
|
||||
return TEXT_DECORATION_LINE.OVERLINE;
|
||||
case 'line-through':
|
||||
return TEXT_DECORATION_LINE.LINE_THROUGH;
|
||||
}
|
||||
return TEXT_DECORATION_LINE.BLINK;
|
||||
};
|
||||
|
||||
const parseTextDecorationLine = (line: string): TextDecorationLineType => {
|
||||
if (line === 'none') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return line.split(' ').map(parseLine);
|
||||
};
|
||||
|
||||
const parseTextDecorationStyle = (style: string): TextDecorationStyle => {
|
||||
switch (style) {
|
||||
case 'double':
|
||||
return TEXT_DECORATION_STYLE.DOUBLE;
|
||||
case 'dotted':
|
||||
return TEXT_DECORATION_STYLE.DOTTED;
|
||||
case 'dashed':
|
||||
return TEXT_DECORATION_STYLE.DASHED;
|
||||
case 'wavy':
|
||||
return TEXT_DECORATION_STYLE.WAVY;
|
||||
}
|
||||
return TEXT_DECORATION_STYLE.SOLID;
|
||||
};
|
||||
|
||||
export const parseTextDecoration = (style: CSSStyleDeclaration): TextDecoration => {
|
||||
const textDecorationLine = parseTextDecorationLine(
|
||||
style.textDecorationLine ? style.textDecorationLine : style.textDecoration
|
||||
);
|
||||
if (textDecorationLine === null) {
|
||||
return TEXT_DECORATION.NONE;
|
||||
}
|
||||
|
||||
const textDecorationColor = style.textDecorationColor
|
||||
? new Color(style.textDecorationColor)
|
||||
: null;
|
||||
const textDecorationStyle = parseTextDecorationStyle(style.textDecorationStyle);
|
||||
|
||||
return {
|
||||
textDecorationLine,
|
||||
textDecorationColor,
|
||||
textDecorationStyle
|
||||
};
|
||||
};
|
||||
24
src/parsing/textTransform.js
Normal file
24
src/parsing/textTransform.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
export const TEXT_TRANSFORM = {
|
||||
NONE: 0,
|
||||
LOWERCASE: 1,
|
||||
UPPERCASE: 2,
|
||||
CAPITALIZE: 3
|
||||
};
|
||||
|
||||
export type TextTransform = $Values<typeof TEXT_TRANSFORM>;
|
||||
|
||||
export const parseTextTransform = (textTransform: string): TextTransform => {
|
||||
switch (textTransform) {
|
||||
case 'uppercase':
|
||||
return TEXT_TRANSFORM.UPPERCASE;
|
||||
case 'lowercase':
|
||||
return TEXT_TRANSFORM.LOWERCASE;
|
||||
case 'capitalize':
|
||||
return TEXT_TRANSFORM.CAPITALIZE;
|
||||
}
|
||||
|
||||
return TEXT_TRANSFORM.NONE;
|
||||
};
|
||||
49
src/parsing/transform.js
Normal file
49
src/parsing/transform.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
import Length from '../Length';
|
||||
|
||||
const toFloat = (s: string): number => parseFloat(s.trim());
|
||||
|
||||
export type Matrix = [number, number, number, number, number, number];
|
||||
export type TransformOrigin = [Length, Length];
|
||||
export type Transform = {
|
||||
transform: Matrix,
|
||||
transformOrigin: TransformOrigin
|
||||
} | null;
|
||||
|
||||
const MATRIX = /(matrix|matrix3d)\((.+)\)/;
|
||||
|
||||
export const parseTransform = (style: CSSStyleDeclaration): Transform => {
|
||||
// TODO get prefixed values
|
||||
const transform = parseTransformMatrix(style.transform);
|
||||
if (transform === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
transform,
|
||||
transformOrigin: parseTransformOrigin(style.transformOrigin)
|
||||
};
|
||||
};
|
||||
|
||||
const parseTransformOrigin = (origin: string): TransformOrigin => {
|
||||
const values = origin.split(' ').map(Length.create);
|
||||
return [values[0], values[1]];
|
||||
};
|
||||
|
||||
const parseTransformMatrix = (transform: string): Matrix | null => {
|
||||
if (transform === 'none') {
|
||||
return null;
|
||||
}
|
||||
const match = transform.match(MATRIX);
|
||||
if (match) {
|
||||
if (match[1] === 'matrix') {
|
||||
const matrix = match[2].split(',').map(toFloat);
|
||||
return [matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]];
|
||||
} else {
|
||||
const matrix3d = match[2].split(',').map(toFloat);
|
||||
return [matrix3d[0], matrix3d[1], matrix3d[4], matrix3d[5], matrix3d[12], matrix3d[13]];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
15
src/parsing/zIndex.js
Normal file
15
src/parsing/zIndex.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
export type zIndex = {
|
||||
auto: boolean,
|
||||
order: number
|
||||
};
|
||||
|
||||
export const parseZIndex = (zIndex: string): zIndex => {
|
||||
const auto = zIndex === 'auto';
|
||||
return {
|
||||
auto,
|
||||
order: auto ? 0 : parseInt(zIndex, 10)
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user