forked from lojjic/PIE
/
Length.js
153 lines (138 loc) · 5.23 KB
/
Length.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
152
153
/**
* Wrapper for length and percentage style values. The value is immutable. A singleton instance per unique
* value is returned from PIE.getLength() - always use that instead of instantiating directly.
* @constructor
* @param {string} val The CSS string representing the length. It is assumed that this will already have
* been validated as a valid length or percentage syntax.
*/
PIE.Length = (function() {
var lengthCalcEl = doc.createElement( 'length-calc' ),
parent = doc.body || doc.documentElement,
s = lengthCalcEl.style,
conversions = {},
units = [ 'mm', 'cm', 'in', 'pt', 'pc' ],
i = units.length,
instances = {};
s.position = 'absolute';
s.top = s.left = '-9999px';
parent.appendChild( lengthCalcEl );
while( i-- ) {
s.width = '100' + units[i];
conversions[ units[i] ] = lengthCalcEl.offsetWidth / 100;
}
parent.removeChild( lengthCalcEl );
// All calcs from here on will use 1em
s.width = '1em';
function Length( val ) {
this.val = val;
}
Length.prototype = {
/**
* Regular expression for matching the length unit
* @private
*/
unitRE: /(px|em|ex|mm|cm|in|pt|pc|%)$/,
/**
* Get the numeric value of the length
* @return {number} The value
*/
getNumber: function() {
var num = this.num,
UNDEF;
if( num === UNDEF ) {
num = this.num = parseFloat( this.val );
}
return num;
},
/**
* Get the unit of the length
* @return {string} The unit
*/
getUnit: function() {
var unit = this.unit,
m;
if( !unit ) {
m = this.val.match( this.unitRE );
unit = this.unit = ( m && m[0] ) || 'px';
}
return unit;
},
/**
* Determine whether this is a percentage length value
* @return {boolean}
*/
isPercentage: function() {
return this.getUnit() === '%';
},
/**
* Resolve this length into a number of pixels.
* @param {Element} el - the context element, used to resolve font-relative values
* @param {(function():number|number)=} pct100 - the number of pixels that equal a 100% percentage. This can be either a number or a
* function which will be called to return the number.
*/
pixels: function( el, pct100 ) {
var num = this.getNumber(),
unit = this.getUnit();
switch( unit ) {
case "px":
return num;
case "%":
return num * ( typeof pct100 === 'function' ? pct100() : pct100 ) / 100;
case "em":
return num * this.getEmPixels( el );
case "ex":
return num * this.getEmPixels( el ) / 2;
default:
return num * conversions[ unit ];
}
},
/**
* The em and ex units are relative to the font-size of the current element,
* however if the font-size is set using non-pixel units then we get that value
* rather than a pixel conversion. To get around this, we keep a floating element
* with width:1em which we insert into the target element and then read its offsetWidth.
* For elements that won't accept a child we insert into the parent node and perform
* additional calculation. If the font-size *is* specified in pixels, then we use that
* directly to avoid the expensive DOM manipulation.
* @param {Element} el
* @return {number}
*/
getEmPixels: function( el ) {
var fs = el.currentStyle.fontSize,
px, parent, me;
if( fs.indexOf( 'px' ) > 0 ) {
return parseFloat( fs );
}
else if( el.tagName in PIE.childlessElements ) {
me = this;
parent = el.parentNode;
return PIE.getLength( fs ).pixels( parent, function() {
return me.getEmPixels( parent );
} );
}
else {
el.appendChild( lengthCalcEl );
px = lengthCalcEl.offsetWidth;
if( lengthCalcEl.parentNode === el ) { //not sure how this could be false but it sometimes is
el.removeChild( lengthCalcEl );
}
return px;
}
}
};
/**
* Convert a pixel length into a point length
*/
Length.pxToPt = function( px ) {
return px / conversions[ 'pt' ];
};
/**
* Retrieve a PIE.Length instance for the given value. A shared singleton instance is returned for each unique value.
* @param {string} val The CSS string representing the length. It is assumed that this will already have
* been validated as a valid length or percentage syntax.
*/
PIE.getLength = function( val ) {
return instances[ val ] || ( instances[ val ] = new Length( val ) );
};
return Length;
})();