can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
269 lines (223 loc) • 8.73 kB
JavaScript
/*
---
name: Element.Style
description: Contains methods for interacting with the styles of Elements in a fashionable way.
license: MIT-style license.
requires: Element
provides: Element.Style
...
*/
(function(){
var html = document.html, el;
//<ltIE9>
// Check for oldIE, which does not remove styles when they're set to null
el = document.createElement('div');
el.style.color = 'red';
el.style.color = null;
var doesNotRemoveStyles = el.style.color == 'red';
// check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color)
var border = '1px solid #123abc';
el.style.border = border;
var returnsBordersInWrongOrder = el.style.border != border;
el = null;
//</ltIE9>
var hasGetComputedStyle = !!window.getComputedStyle,
supportBorderRadius = document.createElement('div').style.borderRadius != null;
Element.Properties.styles = {set: function(styles){
this.setStyles(styles);
}};
var hasOpacity = (html.style.opacity != null),
hasFilter = (html.style.filter != null),
reAlpha = /alpha\(opacity=([\d.]+)\)/i;
var setVisibility = function(element, opacity){
element.store('$opacity', opacity);
element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
};
//<ltIE9>
var setFilter = function(element, regexp, value){
var style = element.style,
filter = style.filter || element.getComputedStyle('filter') || '';
style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim();
if (!style.filter) style.removeAttribute('filter');
};
//</ltIE9>
var setOpacity = (hasOpacity ? function(element, opacity){
element.style.opacity = opacity;
} : (hasFilter ? function(element, opacity){
if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
if (opacity == null || opacity == 1){
setFilter(element, reAlpha, '');
if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)');
} else {
setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
}
} : setVisibility));
var getOpacity = (hasOpacity ? function(element){
var opacity = element.style.opacity || element.getComputedStyle('opacity');
return (opacity == '') ? 1 : opacity.toFloat();
} : (hasFilter ? function(element){
var filter = (element.style.filter || element.getComputedStyle('filter')),
opacity;
if (filter) opacity = filter.match(reAlpha);
return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
} : function(element){
var opacity = element.retrieve('$opacity');
if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
return opacity;
}));
var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat',
namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'},
hasBackgroundPositionXY = (html.style.backgroundPositionX != null),
prefixPattern = /^-(ms)-/;
var camelCase = function(property){
return property.replace(prefixPattern, '$1-').camelCase();
}
//<ltIE9>
var removeStyle = function(style, property){
if (property == 'backgroundPosition'){
style.removeAttribute(property + 'X');
property += 'Y';
}
style.removeAttribute(property);
};
//</ltIE9>
Element.implement({
getComputedStyle: function(property){
if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[camelCase(property)];
var defaultView = Element.getDocument(this).defaultView,
computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : '';
},
setStyle: function(property, value){
if (property == 'opacity'){
if (value != null) value = parseFloat(value);
setOpacity(this, value);
return this;
}
property = camelCase(property == 'float' ? floatName : property);
if (typeOf(value) != 'string'){
var map = (Element.Styles[property] || '@').split(' ');
value = Array.from(value).map(function(val, i){
if (!map[i]) return '';
return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
}).join(' ');
} else if (value == String(Number(value))){
value = Math.round(value);
}
this.style[property] = value;
//<ltIE9>
if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
removeStyle(this.style, property);
}
//</ltIE9>
return this;
},
getStyle: function(property){
if (property == 'opacity') return getOpacity(this);
property = camelCase(property == 'float' ? floatName : property);
if (supportBorderRadius && property.indexOf('borderRadius') != -1){
return ['borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'].map(function(corner){
return this.style[corner] || '0px';
}, this).join(' ');
}
var result = this.style[property];
if (!result || property == 'zIndex'){
if (Element.ShortStyles.hasOwnProperty(property)){
result = [];
for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
return result.join(' ');
}
result = this.getComputedStyle(property);
}
if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
return result.replace(/(top|right|bottom|left)/g, function(position){
return namedPositions[position];
}) || '0px';
}
if (!result && property == 'backgroundPosition') return '0px 0px';
if (result){
result = String(result);
var color = result.match(/rgba?\([\d\s,]+\)/);
if (color) result = result.replace(color[0], color[0].rgbToHex());
}
if (!hasGetComputedStyle && !this.style[property]){
if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
values.each(function(value){
size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
}, this);
return this['offset' + property.capitalize()] - size + 'px';
}
if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
return '0px';
}
}
//<ltIE9>
if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
}
//</ltIE9>
return result;
},
setStyles: function(styles){
for (var style in styles) this.setStyle(style, styles[style]);
return this;
},
getStyles: function(){
var result = {};
Array.flatten(arguments).each(function(key){
result[key] = this.getStyle(key);
}, this);
return result;
}
});
Element.Styles = {
left: '@px', top: '@px', bottom: '@px', right: '@px',
width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@', borderRadius: '@px @px @px @px'
};
//<1.3compat>
Element.implement({
setOpacity: function(value){
setOpacity(this, value);
return this;
},
getOpacity: function(){
return getOpacity(this);
}
});
Element.Properties.opacity = {
set: function(opacity){
setOpacity(this, opacity);
setVisibility(this, opacity);
},
get: function(){
return getOpacity(this);
}
};
//</1.3compat>
//<1.2compat>
Element.Styles = new Hash(Element.Styles);
//</1.2compat>
Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
var Short = Element.ShortStyles;
var All = Element.Styles;
['margin', 'padding'].each(function(style){
var sd = style + direction;
Short[style][sd] = All[sd] = '@px';
});
var bd = 'border' + direction;
Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
Short[bd] = {};
Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
});
if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'};
})();