can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
178 lines (145 loc) • 4.73 kB
JavaScript
/*
---
name: Fx.CSS
description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
license: MIT-style license.
requires: [Fx, Element.Style]
provides: Fx.CSS
...
*/
Fx.CSS = new Class({
Extends: Fx,
//prepares the base from/to object
prepare: function(element, property, values){
values = Array.from(values);
var from = values[0], to = values[1];
if (to == null){
to = from;
from = element.getStyle(property);
var unit = this.options.unit;
// adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){
element.setStyle(property, to + unit);
var value = element.getComputedStyle(property);
// IE and Opera support pixelLeft or pixelWidth
if (!(/px$/.test(value))){
value = element.style[('pixel-' + property).camelCase()];
if (value == null){
// adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
var left = element.style.left;
element.style.left = to + unit;
value = element.style.pixelLeft;
element.style.left = left;
}
}
from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
element.setStyle(property, from + unit);
}
}
return {from: this.parse(from), to: this.parse(to)};
},
//parses a value into an array
parse: function(value){
value = Function.from(value)();
value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
return value.map(function(val){
val = String(val);
var found = false;
Object.each(Fx.CSS.Parsers, function(parser, key){
if (found) return;
var parsed = parser.parse(val);
if (parsed || parsed === 0) found = {value: parsed, parser: parser};
});
found = found || {value: val, parser: Fx.CSS.Parsers.String};
return found;
});
},
//computes by a from and to prepared objects, using their parsers.
compute: function(from, to, delta){
var computed = [];
(Math.min(from.length, to.length)).times(function(i){
computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
});
computed.$family = Function.from('fx:css:value');
return computed;
},
//serves the value as settable
serve: function(value, unit){
if (typeOf(value) != 'fx:css:value') value = this.parse(value);
var returned = [];
value.each(function(bit){
returned = returned.concat(bit.parser.serve(bit.value, unit));
});
return returned;
},
//renders the change to an element
render: function(element, property, value, unit){
element.setStyle(property, this.serve(value, unit));
},
//searches inside the page css to find the values for a selector
search: function(selector){
if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
var searchStyles = function(rules){
Array.each(rules, function(rule, i){
if (rule.media){
searchStyles(rule.rules || rule.cssRules);
return;
}
if (!rule.style) return;
var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
return m.toLowerCase();
}) : null;
if (!selectorText || !selectorTest.test(selectorText)) return;
Object.each(Element.Styles, function(value, style){
if (!rule.style[style] || Element.ShortStyles[style]) return;
value = String(rule.style[style]);
to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
});
});
};
Array.each(document.styleSheets, function(sheet, j){
var href = sheet.href;
if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return;
var rules = sheet.rules || sheet.cssRules;
searchStyles(rules);
});
return Fx.CSS.Cache[selector] = to;
}
});
Fx.CSS.Cache = {};
Fx.CSS.Parsers = {
Color: {
parse: function(value){
if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
},
compute: function(from, to, delta){
return from.map(function(value, i){
return Math.round(Fx.compute(from[i], to[i], delta));
});
},
serve: function(value){
return value.map(Number);
}
},
Number: {
parse: parseFloat,
compute: Fx.compute,
serve: function(value, unit){
return (unit) ? value + unit : value;
}
},
String: {
parse: Function.from(false),
compute: function(zero, one){
return one;
},
serve: function(zero){
return zero;
}
}
};
//<1.2compat>
Fx.CSS.Parsers = new Hash(Fx.CSS.Parsers);
//</1.2compat>