This repository has been archived by the owner on Apr 20, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 235
/
TransformStyleInfo.js
151 lines (135 loc) · 5.77 KB
/
TransformStyleInfo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
* Handles parsing, caching, and detecting changes to CSS3 'transform' and 'transform-origin' properties
* @constructor
* @param {Element} el the target element
*/
PIE.TransformStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
cssProperty: 'transform',
styleProperty: 'transform',
transformFunctions: (function() {
var Type = PIE.Tokenizer.Type,
NUMBER = Type.NUMBER,
ANGLE = Type.ANGLE,
LENGTH = Type.LENGTH,
PERCENT = Type.PERCENT,
LengthCls = PIE.Length,
AngleCls = PIE.Angle;
return {
// function_name: [ allowed_type, min_values, max_values, wrapper_class ]
'matrix': [ NUMBER, 6, 6 ], //TODO moz allows (requires?) a length w/units for the final 2 args, should we?
'translate': [ LENGTH | PERCENT, 1, 2, LengthCls ],
'translateX': [ LENGTH | PERCENT, 1, 1, LengthCls ],
'translateY': [ LENGTH | PERCENT, 1, 1, LengthCls ],
'scale': [ NUMBER, 1, 2 ],
'scaleX': [ NUMBER, 1, 1 ],
'scaleY': [ NUMBER, 1, 1 ],
'rotate': [ ANGLE, 1, 1, AngleCls ],
'skew': [ ANGLE, 1, 2, AngleCls ],
'skewX': [ ANGLE, 1, 1, AngleCls ],
'skewY': [ ANGLE, 1, 1, AngleCls ]
};
})(),
xyFunctionRE: /^(.+)(X|Y)$/,
/**
* Returns object in the format:
* {
* origin: <PIE.BgPosition>,
* transforms: [
* { type: 'matrix', value: [ <Number>, <Number>, <Number>, <Number>, <Number>, <Number> ] },
* { type: 'translate', value: [ <PIE.Length>, <PIE.Length> ] },
* { type: 'skew', value: [ <PIE.Angle>, <PIE.Angle> ] },
* { type: 'scale', value: [ <Number>, <Number> ] },
* { type: 'rotate', value: <PIE.Angle> }
* ]
* }
*
* Note that XY-specific functions will be pre-combined into their single counterparts:
* scaleX(n) -> scale(n)
* scaleY(n) -> scale(1, n)
* skewX(a) -> skew(a)
* skewY(a) -> skew(0, a)
* translateX(l) -> translate(l)
* translateY(l) -> translate(0, l)
*/
parseCss: function( css ) {
var tokenizer = new PIE.Tokenizer( css ),
Type = PIE.Tokenizer.Type,
transformFunctions = this.transformFunctions,
xyFunctionRE = this.xyFunctionRE,
YEP = true,
NOPE = false,
NULL = null,
token, tokenType, tokenValue, fnSignature, fnName, fnArgs, hangingArg, hangingComma, xyMatch,
transforms = [];
while( token = tokenizer.next() ) {
tokenType = token.type;
tokenValue = token.value;
// Start of function - set up for collection of args
if( !fnName && ( tokenType & Type.FUNCTION ) && tokenValue in transformFunctions ) {
fnName = tokenValue;
fnSignature = transformFunctions[tokenValue];
fnArgs = [];
}
// End of function - validate number of args is within allowed range, push onto transforms list, and reset
else if ( ( tokenType & Type.CHARACTER ) && tokenValue === ')' && fnName && !hangingComma &&
fnArgs.length >= fnSignature[1] && fnArgs.length <= fnSignature[2] ) {
// Combine XY-specific functions into single functions, e.g. scaleY(n) -> scale(1, n)
if ( xyMatch = fnName.match( xyFunctionRE ) ) {
fnName = xyMatch[1];
// For fooX we use the arg untouched, for fooY we push an x value onto the front of the args
if ( xyMatch[2] === 'Y' ) {
fnArgs.unshift( fnName === 'scale' ? 1 : 0 );
}
}
// Fill in optional second argument
else if ( fnArgs.length < 2 ) {
fnArgs[1] = fnName === 'scale' ? fnArgs[0] : 0;
}
transforms.push( { type: fnName, value: fnArgs } );
fnSignature = fnName = fnArgs = hangingArg = hangingComma = NOPE;
}
// Commas - allowed in between function args
else if ( ( tokenType & Type.CHARACTER ) && tokenValue === ',' && hangingArg ) {
hangingArg = NOPE;
hangingComma = YEP;
}
// Function args - allowed at start of function or after comma
else if ( fnName && ( hangingComma || !hangingArg ) && ( tokenType & fnSignature[0] ) ) {
fnArgs.push( fnSignature[3] ? new fnSignature[3]( tokenValue ) : parseFloat( tokenValue, 10 ) );
hangingArg = YEP;
hangingComma = NOPE;
}
// Something not allowed - FAIL!
else {
return NULL;
}
}
return transforms[0] && !fnName ? {
origin: [],
transforms: transforms
} : NULL;
},
/**
* Return the {@link PIE.Matrix}
*/
getMatrix: PIE.StyleInfoBase.cacheWhenLocked( function( bounds ) {
var me = this,
el = me.targetElement,
props = me.getProps(),
origin = props.origin.coords( el, bounds.w, bounds.h ),
transforms = props.transforms,
matrix = new PIE.Matrix( 1, 0, 0, 1, origin.x, origin.y ),
i = 0, len = transforms.length,
type, value;
// apply each transform in order
for( ; i < len; i++ ) {
type = transforms[i].type;
value = transforms[i].value;
matrix = matrix[ type ].apply(
matrix,
type === 'translate' ? [ value[0].pixels( el ), value[1].pixels( el ) ] : value
);
}
return matrix;
} )
} );