Skip to content

Commit fdc11b8

Browse files
Nikita VasilyevNikita Vasilyev
authored andcommitted
Merge pull request #32 from papandreou/morekeyframes
Support for @Keyframes and @-moz-keyframes rules.
2 parents dc430dd + f63659b commit fdc11b8

3 files changed

Lines changed: 141 additions & 12 deletions

File tree

lib/CSSKeyframesRule.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ CSSOM.CSSKeyframesRule.prototype.__defineGetter__("cssText", function() {
2828
for (var i=0, length=this.cssRules.length; i < length; i++) {
2929
cssTexts.push(" " + this.cssRules[i].cssText);
3030
}
31-
return "@-webkit-keyframes " + this.name + " { \n" + cssTexts.join("\n") + "\n}";
31+
return "@" + (this._vendorPrefix || '') + "keyframes " + this.name + " { \n" + cssTexts.join("\n") + "\n}";
3232
});
3333

3434

lib/parse.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ CSSOM.parse = function parse(token) {
5252

5353
var selector, name, value, priority="", styleRule, mediaRule, importRule, keyframesRule, keyframeRule;
5454

55+
var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g;
56+
5557
for (var character; character = token.charAt(i); i++) {
5658

5759
switch (character) {
@@ -134,15 +136,20 @@ CSSOM.parse = function parse(token) {
134136
i += "import".length;
135137
buffer += "@import";
136138
break;
137-
} else if (token.indexOf("@-webkit-keyframes", i) === i) {
138-
state = "keyframesRule-begin";
139-
keyframesRule = new CSSOM.CSSKeyframesRule;
140-
keyframesRule.__starts = i;
141-
i += "-webkit-keyframes".length;
142-
buffer = "";
143-
break;
144-
} else if (state === "selector") {
145-
state = "atRule";
139+
} else {
140+
atKeyframesRegExp.lastIndex = i;
141+
var matchKeyframes = atKeyframesRegExp.exec(token);
142+
if (matchKeyframes && matchKeyframes.index === i) {
143+
state = "keyframesRule-begin";
144+
keyframesRule = new CSSOM.CSSKeyframesRule;
145+
keyframesRule.__starts = i;
146+
keyframesRule._vendorPrefix = matchKeyframes[1]; // Will come out as undefined if no prefix was found
147+
i += matchKeyframes[0].length - 1;
148+
buffer = "";
149+
break;
150+
} else if (state == "selector") {
151+
state = "atRule";
152+
}
146153
}
147154
buffer += character;
148155
break;

spec/parse.spec.js

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -707,13 +707,134 @@ var TESTS = [
707707
return result;
708708
})()
709709
},
710+
{
711+
// Non-vendor prefixed @keyframes rule, from Twitter Bootstrap (progress-bars):
712+
input: '@keyframes progress-bar-stripes {\n from { background-position: 0 0; }\n to { background-position: 40px 0; }\n}',
713+
result: (function () {
714+
var result = {
715+
cssRules: [
716+
{
717+
name: "progress-bar-stripes",
718+
_vendorPrefix: undefined,
719+
cssRules: [
720+
{
721+
keyText: "from",
722+
style: {
723+
0: "background-position",
724+
'background-position': "0 0",
725+
length: 1
726+
}
727+
},
728+
{
729+
keyText: "to",
730+
style: {
731+
0: "background-position",
732+
'background-position': "40px 0",
733+
length: 1
734+
}
735+
}
736+
],
737+
parentRule: null
738+
}
739+
],
740+
parentStyleSheet: null
741+
};
742+
result.cssRules[0].parentStyleSheet = result.cssRules[0].cssRules[0].parentStyleSheet = result.cssRules[0].cssRules[1].parentStyleSheet = result;
743+
result.cssRules[0].cssRules[0].parentRule = result.cssRules[0].cssRules[1].parentRule = result.cssRules[0];
744+
result.cssRules[0].cssRules[0].style.parentRule = result.cssRules[0].cssRules[0];
745+
result.cssRules[0].cssRules[1].style.parentRule = result.cssRules[0].cssRules[1];
746+
return result;
747+
})()
748+
},
749+
{
750+
// @keyframes with invalid vendor prefix followed by a valid one (make sure that the RegExp.lastIndex trick works as expected):
751+
input: '@-moz-keyframes foo {} @--keyframes bar {} @-webkit-keyframes quux {}',
752+
result: (function () {
753+
var result = {
754+
cssRules: [
755+
{
756+
name: "foo",
757+
_vendorPrefix: "-moz-",
758+
cssRules: [],
759+
parentRule: null
760+
},
761+
{
762+
selectorText: "@--keyframes bar",
763+
style: {
764+
length: 0
765+
},
766+
parentRule: null,
767+
__starts: 0,
768+
__ends: 14
769+
},
770+
{
771+
name: "quux",
772+
_vendorPrefix: "-webkit-",
773+
cssRules: [],
774+
parentRule: null
775+
}
776+
],
777+
parentStyleSheet: null
778+
};
779+
result.cssRules[0].parentStyleSheet = result.cssRules[1].parentStyleSheet = result.cssRules[2].parentStyleSheet = result;
780+
result.cssRules[1].style.parentRule = result.cssRules[1];
781+
return result;
782+
})()
783+
},
784+
{
785+
input: "@-some-ridiculously-long-vendor-prefix-that-must-be-supported-keyframes therulename /*comment*/{0%{top:0px; left:0px; background:red;}100% {top:4em; left:40px; background:maroon;}}",
786+
result: (function() {
787+
var result = {
788+
cssRules: [
789+
{
790+
name: "therulename",
791+
_vendorPrefix: '-some-ridiculously-long-vendor-prefix-that-must-be-supported-',
792+
cssRules: [
793+
{
794+
keyText: "0%",
795+
style: {
796+
0: "top",
797+
1: "left",
798+
2: "background",
799+
top: "0px",
800+
left: "0px",
801+
background: "red",
802+
length: 3
803+
}
804+
},
805+
{
806+
keyText: "100%",
807+
style: {
808+
0: "top",
809+
1: "left",
810+
2: "background",
811+
top: "4em",
812+
left: "40px",
813+
background: "maroon",
814+
length: 3
815+
}
816+
}
817+
],
818+
parentRule: null
819+
}
820+
],
821+
parentStyleSheet: null
822+
};
823+
result.cssRules[0].parentStyleSheet = result.cssRules[0].cssRules[0].parentStyleSheet = result.cssRules[0].cssRules[1].parentStyleSheet = result;
824+
result.cssRules[0].cssRules[0].parentRule = result.cssRules[0].cssRules[1].parentRule = result.cssRules[0];
825+
result.cssRules[0].cssRules[0].style.parentRule = result.cssRules[0].cssRules[0];
826+
result.cssRules[0].cssRules[1].style.parentRule = result.cssRules[0].cssRules[1];
827+
return result;
828+
})()
829+
},
710830
{
711831
input: "@-webkit-keyframes mymove {\nfrom {top:0px}\nto {top:200px}\n}",
712832
result: (function() {
713833
var result = {
714834
cssRules: [
715835
{
716836
name: "mymove",
837+
_vendorPrefix: '-webkit-',
717838
cssRules: [
718839
{
719840
keyText: "from",
@@ -751,6 +872,7 @@ var TESTS = [
751872
cssRules: [
752873
{
753874
name: "mymovepercent",
875+
_vendorPrefix: '-webkit-',
754876
cssRules: [
755877
{
756878
keyText: "0%",
@@ -813,7 +935,7 @@ describe('parse', function() {
813935
});
814936

815937
/**
816-
* Recurcively remove all keys which start with '_'
938+
* Recursively remove all keys which start with '_', except "_vendorPrefix", which needs to be tested against.
817939
* @param {Object} object
818940
*/
819941
function removeUnderscored(object) {
@@ -823,7 +945,7 @@ function removeUnderscored(object) {
823945
var keys = Object.keys(object);
824946
for (var i = 0, length = keys.length; i < length; i++) {
825947
var key = keys[i];
826-
if (key[0] === '_') {
948+
if (key[0] === '_' && key !== '_vendorPrefix') {
827949
delete object[key];
828950
} else {
829951
var value = object[key];

0 commit comments

Comments
 (0)