CSSOM.js 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995
  1. var CSSOM = {};
  2. /**
  3. * @constructor
  4. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration
  5. */
  6. CSSOM.CSSStyleDeclaration = function CSSStyleDeclaration(){
  7. this.length = 0;
  8. this.parentRule = null;
  9. // NON-STANDARD
  10. this._importants = {};
  11. };
  12. CSSOM.CSSStyleDeclaration.prototype = {
  13. constructor: CSSOM.CSSStyleDeclaration,
  14. /**
  15. *
  16. * @param {string} name
  17. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue
  18. * @return {string} the value of the property if it has been explicitly set for this declaration block.
  19. * Returns the empty string if the property has not been set.
  20. */
  21. getPropertyValue: function(name) {
  22. return this[name] || "";
  23. },
  24. /**
  25. *
  26. * @param {string} name
  27. * @param {string} value
  28. * @param {string} [priority=null] "important" or null
  29. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty
  30. */
  31. setProperty: function(name, value, priority) {
  32. if (this[name]) {
  33. // Property already exist. Overwrite it.
  34. var index = Array.prototype.indexOf.call(this, name);
  35. if (index < 0) {
  36. this[this.length] = name;
  37. this.length++;
  38. }
  39. } else {
  40. // New property.
  41. this[this.length] = name;
  42. this.length++;
  43. }
  44. this[name] = value + "";
  45. this._importants[name] = priority;
  46. },
  47. /**
  48. *
  49. * @param {string} name
  50. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty
  51. * @return {string} the value of the property if it has been explicitly set for this declaration block.
  52. * Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property.
  53. */
  54. removeProperty: function(name) {
  55. if (!(name in this)) {
  56. return "";
  57. }
  58. var index = Array.prototype.indexOf.call(this, name);
  59. if (index < 0) {
  60. return "";
  61. }
  62. var prevValue = this[name];
  63. this[name] = "";
  64. // That's what WebKit and Opera do
  65. Array.prototype.splice.call(this, index, 1);
  66. // That's what Firefox does
  67. //this[index] = ""
  68. return prevValue;
  69. },
  70. getPropertyCSSValue: function() {
  71. //FIXME
  72. },
  73. /**
  74. *
  75. * @param {String} name
  76. */
  77. getPropertyPriority: function(name) {
  78. return this._importants[name] || "";
  79. },
  80. /**
  81. * element.style.overflow = "auto"
  82. * element.style.getPropertyShorthand("overflow-x")
  83. * -> "overflow"
  84. */
  85. getPropertyShorthand: function() {
  86. //FIXME
  87. },
  88. isPropertyImplicit: function() {
  89. //FIXME
  90. },
  91. // Doesn't work in IE < 9
  92. get cssText(){
  93. var properties = [];
  94. for (var i=0, length=this.length; i < length; ++i) {
  95. var name = this[i];
  96. var value = this.getPropertyValue(name);
  97. var priority = this.getPropertyPriority(name);
  98. if (priority) {
  99. priority = " !" + priority;
  100. }
  101. properties[i] = name + ": " + value + priority + ";";
  102. }
  103. return properties.join(" ");
  104. },
  105. set cssText(text){
  106. var i, name;
  107. for (i = this.length; i--;) {
  108. name = this[i];
  109. this[name] = "";
  110. }
  111. Array.prototype.splice.call(this, 0, this.length);
  112. this._importants = {};
  113. var dummyRule = CSSOM.parse('#bogus{' + text + '}').cssRules[0].style;
  114. var length = dummyRule.length;
  115. for (i = 0; i < length; ++i) {
  116. name = dummyRule[i];
  117. this.setProperty(dummyRule[i], dummyRule.getPropertyValue(name), dummyRule.getPropertyPriority(name));
  118. }
  119. }
  120. };
  121. /**
  122. * @constructor
  123. * @see http://dev.w3.org/csswg/cssom/#the-cssrule-interface
  124. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule
  125. */
  126. CSSOM.CSSRule = function CSSRule() {
  127. this.parentRule = null;
  128. this.parentStyleSheet = null;
  129. };
  130. CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete
  131. CSSOM.CSSRule.STYLE_RULE = 1;
  132. CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete
  133. CSSOM.CSSRule.IMPORT_RULE = 3;
  134. CSSOM.CSSRule.MEDIA_RULE = 4;
  135. CSSOM.CSSRule.FONT_FACE_RULE = 5;
  136. CSSOM.CSSRule.PAGE_RULE = 6;
  137. CSSOM.CSSRule.KEYFRAMES_RULE = 7;
  138. CSSOM.CSSRule.KEYFRAME_RULE = 8;
  139. CSSOM.CSSRule.MARGIN_RULE = 9;
  140. CSSOM.CSSRule.NAMESPACE_RULE = 10;
  141. CSSOM.CSSRule.COUNTER_STYLE_RULE = 11;
  142. CSSOM.CSSRule.SUPPORTS_RULE = 12;
  143. CSSOM.CSSRule.DOCUMENT_RULE = 13;
  144. CSSOM.CSSRule.FONT_FEATURE_VALUES_RULE = 14;
  145. CSSOM.CSSRule.VIEWPORT_RULE = 15;
  146. CSSOM.CSSRule.REGION_STYLE_RULE = 16;
  147. CSSOM.CSSRule.CONTAINER_RULE = 17;
  148. CSSOM.CSSRule.LAYER_BLOCK_RULE = 18;
  149. CSSOM.CSSRule.STARTING_STYLE_RULE = 1002;
  150. CSSOM.CSSRule.prototype = {
  151. constructor: CSSOM.CSSRule,
  152. //FIXME
  153. };
  154. exports.CSSRule = CSSOM.CSSRule;
  155. ///CommonJS
  156. /**
  157. * @constructor
  158. * @see https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface
  159. */
  160. CSSOM.CSSGroupingRule = function CSSGroupingRule() {
  161. CSSOM.CSSRule.call(this);
  162. this.cssRules = [];
  163. };
  164. CSSOM.CSSGroupingRule.prototype = new CSSOM.CSSRule();
  165. CSSOM.CSSGroupingRule.prototype.constructor = CSSOM.CSSGroupingRule;
  166. /**
  167. * Used to insert a new CSS rule to a list of CSS rules.
  168. *
  169. * @example
  170. * cssGroupingRule.cssText
  171. * -> "body{margin:0;}"
  172. * cssGroupingRule.insertRule("img{border:none;}", 1)
  173. * -> 1
  174. * cssGroupingRule.cssText
  175. * -> "body{margin:0;}img{border:none;}"
  176. *
  177. * @param {string} rule
  178. * @param {number} [index]
  179. * @see https://www.w3.org/TR/cssom-1/#dom-cssgroupingrule-insertrule
  180. * @return {number} The index within the grouping rule's collection of the newly inserted rule.
  181. */
  182. CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule, index) {
  183. if (index < 0 || index > this.cssRules.length) {
  184. throw new RangeError("INDEX_SIZE_ERR");
  185. }
  186. var cssRule = CSSOM.parse(rule).cssRules[0];
  187. cssRule.parentRule = this;
  188. this.cssRules.splice(index, 0, cssRule);
  189. return index;
  190. };
  191. /**
  192. * Used to delete a rule from the grouping rule.
  193. *
  194. * cssGroupingRule.cssText
  195. * -> "img{border:none;}body{margin:0;}"
  196. * cssGroupingRule.deleteRule(0)
  197. * cssGroupingRule.cssText
  198. * -> "body{margin:0;}"
  199. *
  200. * @param {number} index within the grouping rule's rule list of the rule to remove.
  201. * @see https://www.w3.org/TR/cssom-1/#dom-cssgroupingrule-deleterule
  202. */
  203. CSSOM.CSSGroupingRule.prototype.deleteRule = function deleteRule(index) {
  204. if (index < 0 || index >= this.cssRules.length) {
  205. throw new RangeError("INDEX_SIZE_ERR");
  206. }
  207. this.cssRules.splice(index, 1)[0].parentRule = null;
  208. };
  209. /**
  210. * @constructor
  211. * @see https://www.w3.org/TR/css-conditional-3/#the-cssconditionrule-interface
  212. */
  213. CSSOM.CSSConditionRule = function CSSConditionRule() {
  214. CSSOM.CSSGroupingRule.call(this);
  215. this.cssRules = [];
  216. };
  217. CSSOM.CSSConditionRule.prototype = new CSSOM.CSSGroupingRule();
  218. CSSOM.CSSConditionRule.prototype.constructor = CSSOM.CSSConditionRule;
  219. CSSOM.CSSConditionRule.prototype.conditionText = ''
  220. CSSOM.CSSConditionRule.prototype.cssText = ''
  221. /**
  222. * @constructor
  223. * @see http://dev.w3.org/csswg/cssom/#cssstylerule
  224. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule
  225. */
  226. CSSOM.CSSStyleRule = function CSSStyleRule() {
  227. CSSOM.CSSRule.call(this);
  228. this.selectorText = "";
  229. this.style = new CSSOM.CSSStyleDeclaration();
  230. this.style.parentRule = this;
  231. };
  232. CSSOM.CSSStyleRule.prototype = new CSSOM.CSSRule();
  233. CSSOM.CSSStyleRule.prototype.constructor = CSSOM.CSSStyleRule;
  234. CSSOM.CSSStyleRule.prototype.type = 1;
  235. Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", {
  236. get: function() {
  237. var text;
  238. if (this.selectorText) {
  239. text = this.selectorText + " {" + this.style.cssText + "}";
  240. } else {
  241. text = "";
  242. }
  243. return text;
  244. },
  245. set: function(cssText) {
  246. var rule = CSSOM.CSSStyleRule.parse(cssText);
  247. this.style = rule.style;
  248. this.selectorText = rule.selectorText;
  249. }
  250. });
  251. /**
  252. * NON-STANDARD
  253. * lightweight version of parse.js.
  254. * @param {string} ruleText
  255. * @return CSSStyleRule
  256. */
  257. CSSOM.CSSStyleRule.parse = function(ruleText) {
  258. var i = 0;
  259. var state = "selector";
  260. var index;
  261. var j = i;
  262. var buffer = "";
  263. var SIGNIFICANT_WHITESPACE = {
  264. "selector": true,
  265. "value": true
  266. };
  267. var styleRule = new CSSOM.CSSStyleRule();
  268. var name, priority="";
  269. for (var character; (character = ruleText.charAt(i)); i++) {
  270. switch (character) {
  271. case " ":
  272. case "\t":
  273. case "\r":
  274. case "\n":
  275. case "\f":
  276. if (SIGNIFICANT_WHITESPACE[state]) {
  277. // Squash 2 or more white-spaces in the row into 1
  278. switch (ruleText.charAt(i - 1)) {
  279. case " ":
  280. case "\t":
  281. case "\r":
  282. case "\n":
  283. case "\f":
  284. break;
  285. default:
  286. buffer += " ";
  287. break;
  288. }
  289. }
  290. break;
  291. // String
  292. case '"':
  293. j = i + 1;
  294. index = ruleText.indexOf('"', j) + 1;
  295. if (!index) {
  296. throw '" is missing';
  297. }
  298. buffer += ruleText.slice(i, index);
  299. i = index - 1;
  300. break;
  301. case "'":
  302. j = i + 1;
  303. index = ruleText.indexOf("'", j) + 1;
  304. if (!index) {
  305. throw "' is missing";
  306. }
  307. buffer += ruleText.slice(i, index);
  308. i = index - 1;
  309. break;
  310. // Comment
  311. case "/":
  312. if (ruleText.charAt(i + 1) === "*") {
  313. i += 2;
  314. index = ruleText.indexOf("*/", i);
  315. if (index === -1) {
  316. throw new SyntaxError("Missing */");
  317. } else {
  318. i = index + 1;
  319. }
  320. } else {
  321. buffer += character;
  322. }
  323. break;
  324. case "{":
  325. if (state === "selector") {
  326. styleRule.selectorText = buffer.trim();
  327. buffer = "";
  328. state = "name";
  329. }
  330. break;
  331. case ":":
  332. if (state === "name") {
  333. name = buffer.trim();
  334. buffer = "";
  335. state = "value";
  336. } else {
  337. buffer += character;
  338. }
  339. break;
  340. case "!":
  341. if (state === "value" && ruleText.indexOf("!important", i) === i) {
  342. priority = "important";
  343. i += "important".length;
  344. } else {
  345. buffer += character;
  346. }
  347. break;
  348. case ";":
  349. if (state === "value") {
  350. styleRule.style.setProperty(name, buffer.trim(), priority);
  351. priority = "";
  352. buffer = "";
  353. state = "name";
  354. } else {
  355. buffer += character;
  356. }
  357. break;
  358. case "}":
  359. if (state === "value") {
  360. styleRule.style.setProperty(name, buffer.trim(), priority);
  361. priority = "";
  362. buffer = "";
  363. } else if (state === "name") {
  364. break;
  365. } else {
  366. buffer += character;
  367. }
  368. state = "selector";
  369. break;
  370. default:
  371. buffer += character;
  372. break;
  373. }
  374. }
  375. return styleRule;
  376. };
  377. /**
  378. * @constructor
  379. * @see http://dev.w3.org/csswg/cssom/#the-medialist-interface
  380. */
  381. CSSOM.MediaList = function MediaList(){
  382. this.length = 0;
  383. };
  384. CSSOM.MediaList.prototype = {
  385. constructor: CSSOM.MediaList,
  386. /**
  387. * @return {string}
  388. */
  389. get mediaText() {
  390. return Array.prototype.join.call(this, ", ");
  391. },
  392. /**
  393. * @param {string} value
  394. */
  395. set mediaText(value) {
  396. var values = value.split(",");
  397. var length = this.length = values.length;
  398. for (var i=0; i<length; i++) {
  399. this[i] = values[i].trim();
  400. }
  401. },
  402. /**
  403. * @param {string} medium
  404. */
  405. appendMedium: function(medium) {
  406. if (Array.prototype.indexOf.call(this, medium) === -1) {
  407. this[this.length] = medium;
  408. this.length++;
  409. }
  410. },
  411. /**
  412. * @param {string} medium
  413. */
  414. deleteMedium: function(medium) {
  415. var index = Array.prototype.indexOf.call(this, medium);
  416. if (index !== -1) {
  417. Array.prototype.splice.call(this, index, 1);
  418. }
  419. }
  420. };
  421. /**
  422. * @constructor
  423. * @see http://dev.w3.org/csswg/cssom/#cssmediarule
  424. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule
  425. */
  426. CSSOM.CSSMediaRule = function CSSMediaRule() {
  427. CSSOM.CSSConditionRule.call(this);
  428. this.media = new CSSOM.MediaList();
  429. };
  430. CSSOM.CSSMediaRule.prototype = new CSSOM.CSSConditionRule();
  431. CSSOM.CSSMediaRule.prototype.constructor = CSSOM.CSSMediaRule;
  432. CSSOM.CSSMediaRule.prototype.type = 4;
  433. // https://opensource.apple.com/source/WebCore/WebCore-7611.1.21.161.3/css/CSSMediaRule.cpp
  434. Object.defineProperties(CSSOM.CSSMediaRule.prototype, {
  435. "conditionText": {
  436. get: function() {
  437. return this.media.mediaText;
  438. },
  439. set: function(value) {
  440. this.media.mediaText = value;
  441. },
  442. configurable: true,
  443. enumerable: true
  444. },
  445. "cssText": {
  446. get: function() {
  447. var cssTexts = [];
  448. for (var i=0, length=this.cssRules.length; i < length; i++) {
  449. cssTexts.push(this.cssRules[i].cssText);
  450. }
  451. return "@media " + this.media.mediaText + " {" + cssTexts.join("") + "}";
  452. },
  453. configurable: true,
  454. enumerable: true
  455. }
  456. });
  457. /**
  458. * @constructor
  459. * @see https://drafts.csswg.org/css-contain-3/
  460. * @see https://www.w3.org/TR/css-contain-3/
  461. */
  462. CSSOM.CSSContainerRule = function CSSContainerRule() {
  463. CSSOM.CSSConditionRule.call(this);
  464. };
  465. CSSOM.CSSContainerRule.prototype = new CSSOM.CSSConditionRule();
  466. CSSOM.CSSContainerRule.prototype.constructor = CSSOM.CSSContainerRule;
  467. CSSOM.CSSContainerRule.prototype.type = 17;
  468. Object.defineProperties(CSSOM.CSSContainerRule.prototype, {
  469. "conditionText": {
  470. get: function() {
  471. return this.containerText;
  472. },
  473. set: function(value) {
  474. this.containerText = value;
  475. },
  476. configurable: true,
  477. enumerable: true
  478. },
  479. "cssText": {
  480. get: function() {
  481. var cssTexts = [];
  482. for (var i=0, length=this.cssRules.length; i < length; i++) {
  483. cssTexts.push(this.cssRules[i].cssText);
  484. }
  485. return "@container " + this.containerText + " {" + cssTexts.join("") + "}";
  486. },
  487. configurable: true,
  488. enumerable: true
  489. }
  490. });
  491. /**
  492. * @constructor
  493. * @see https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface
  494. */
  495. CSSOM.CSSSupportsRule = function CSSSupportsRule() {
  496. CSSOM.CSSConditionRule.call(this);
  497. };
  498. CSSOM.CSSSupportsRule.prototype = new CSSOM.CSSConditionRule();
  499. CSSOM.CSSSupportsRule.prototype.constructor = CSSOM.CSSSupportsRule;
  500. CSSOM.CSSSupportsRule.prototype.type = 12;
  501. Object.defineProperty(CSSOM.CSSSupportsRule.prototype, "cssText", {
  502. get: function() {
  503. var cssTexts = [];
  504. for (var i = 0, length = this.cssRules.length; i < length; i++) {
  505. cssTexts.push(this.cssRules[i].cssText);
  506. }
  507. return "@supports " + this.conditionText + " {" + cssTexts.join("") + "}";
  508. }
  509. });
  510. /**
  511. * @constructor
  512. * @see http://dev.w3.org/csswg/cssom/#cssimportrule
  513. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule
  514. */
  515. CSSOM.CSSImportRule = function CSSImportRule() {
  516. CSSOM.CSSRule.call(this);
  517. this.href = "";
  518. this.media = new CSSOM.MediaList();
  519. this.styleSheet = new CSSOM.CSSStyleSheet();
  520. };
  521. CSSOM.CSSImportRule.prototype = new CSSOM.CSSRule();
  522. CSSOM.CSSImportRule.prototype.constructor = CSSOM.CSSImportRule;
  523. CSSOM.CSSImportRule.prototype.type = 3;
  524. Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
  525. get: function() {
  526. var mediaText = this.media.mediaText;
  527. return "@import url(" + this.href + ")" + (mediaText ? " " + mediaText : "") + ";";
  528. },
  529. set: function(cssText) {
  530. var i = 0;
  531. /**
  532. * @import url(partial.css) screen, handheld;
  533. * || |
  534. * after-import media
  535. * |
  536. * url
  537. */
  538. var state = '';
  539. var buffer = '';
  540. var index;
  541. for (var character; (character = cssText.charAt(i)); i++) {
  542. switch (character) {
  543. case ' ':
  544. case '\t':
  545. case '\r':
  546. case '\n':
  547. case '\f':
  548. if (state === 'after-import') {
  549. state = 'url';
  550. } else {
  551. buffer += character;
  552. }
  553. break;
  554. case '@':
  555. if (!state && cssText.indexOf('@import', i) === i) {
  556. state = 'after-import';
  557. i += 'import'.length;
  558. buffer = '';
  559. }
  560. break;
  561. case 'u':
  562. if (state === 'url' && cssText.indexOf('url(', i) === i) {
  563. index = cssText.indexOf(')', i + 1);
  564. if (index === -1) {
  565. throw i + ': ")" not found';
  566. }
  567. i += 'url('.length;
  568. var url = cssText.slice(i, index);
  569. if (url[0] === url[url.length - 1]) {
  570. if (url[0] === '"' || url[0] === "'") {
  571. url = url.slice(1, -1);
  572. }
  573. }
  574. this.href = url;
  575. i = index;
  576. state = 'media';
  577. }
  578. break;
  579. case '"':
  580. if (state === 'url') {
  581. index = cssText.indexOf('"', i + 1);
  582. if (!index) {
  583. throw i + ": '\"' not found";
  584. }
  585. this.href = cssText.slice(i + 1, index);
  586. i = index;
  587. state = 'media';
  588. }
  589. break;
  590. case "'":
  591. if (state === 'url') {
  592. index = cssText.indexOf("'", i + 1);
  593. if (!index) {
  594. throw i + ': "\'" not found';
  595. }
  596. this.href = cssText.slice(i + 1, index);
  597. i = index;
  598. state = 'media';
  599. }
  600. break;
  601. case ';':
  602. if (state === 'media') {
  603. if (buffer) {
  604. this.media.mediaText = buffer.trim();
  605. }
  606. }
  607. break;
  608. default:
  609. if (state === 'media') {
  610. buffer += character;
  611. }
  612. break;
  613. }
  614. }
  615. }
  616. });
  617. /**
  618. * @constructor
  619. * @see http://dev.w3.org/csswg/cssom/#css-font-face-rule
  620. */
  621. CSSOM.CSSFontFaceRule = function CSSFontFaceRule() {
  622. CSSOM.CSSRule.call(this);
  623. this.style = new CSSOM.CSSStyleDeclaration();
  624. this.style.parentRule = this;
  625. };
  626. CSSOM.CSSFontFaceRule.prototype = new CSSOM.CSSRule();
  627. CSSOM.CSSFontFaceRule.prototype.constructor = CSSOM.CSSFontFaceRule;
  628. CSSOM.CSSFontFaceRule.prototype.type = 5;
  629. //FIXME
  630. //CSSOM.CSSFontFaceRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
  631. //CSSOM.CSSFontFaceRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
  632. // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSFontFaceRule.cpp
  633. Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", {
  634. get: function() {
  635. return "@font-face {" + this.style.cssText + "}";
  636. }
  637. });
  638. /**
  639. * @constructor
  640. * @see http://www.w3.org/TR/shadow-dom/#host-at-rule
  641. */
  642. CSSOM.CSSHostRule = function CSSHostRule() {
  643. CSSOM.CSSRule.call(this);
  644. this.cssRules = [];
  645. };
  646. CSSOM.CSSHostRule.prototype = new CSSOM.CSSRule();
  647. CSSOM.CSSHostRule.prototype.constructor = CSSOM.CSSHostRule;
  648. CSSOM.CSSHostRule.prototype.type = 1001;
  649. //FIXME
  650. //CSSOM.CSSHostRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
  651. //CSSOM.CSSHostRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
  652. Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", {
  653. get: function() {
  654. var cssTexts = [];
  655. for (var i=0, length=this.cssRules.length; i < length; i++) {
  656. cssTexts.push(this.cssRules[i].cssText);
  657. }
  658. return "@host {" + cssTexts.join("") + "}";
  659. }
  660. });
  661. /**
  662. * @constructor
  663. * @see http://www.w3.org/TR/shadow-dom/#host-at-rule
  664. */
  665. CSSOM.CSSStartingStyleRule = function CSSStartingStyleRule() {
  666. CSSOM.CSSRule.call(this);
  667. this.cssRules = [];
  668. };
  669. CSSOM.CSSStartingStyleRule.prototype = new CSSOM.CSSRule();
  670. CSSOM.CSSStartingStyleRule.prototype.constructor = CSSOM.CSSStartingStyleRule;
  671. CSSOM.CSSStartingStyleRule.prototype.type = 1002;
  672. //FIXME
  673. //CSSOM.CSSStartingStyleRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
  674. //CSSOM.CSSStartingStyleRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
  675. Object.defineProperty(CSSOM.CSSStartingStyleRule.prototype, "cssText", {
  676. get: function() {
  677. var cssTexts = [];
  678. for (var i=0, length=this.cssRules.length; i < length; i++) {
  679. cssTexts.push(this.cssRules[i].cssText);
  680. }
  681. return "@starting-style {" + cssTexts.join("") + "}";
  682. }
  683. });
  684. /**
  685. * @constructor
  686. * @see http://dev.w3.org/csswg/cssom/#the-stylesheet-interface
  687. */
  688. CSSOM.StyleSheet = function StyleSheet() {
  689. this.parentStyleSheet = null;
  690. };
  691. /**
  692. * @constructor
  693. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet
  694. */
  695. CSSOM.CSSStyleSheet = function CSSStyleSheet() {
  696. CSSOM.StyleSheet.call(this);
  697. this.cssRules = [];
  698. };
  699. CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet();
  700. CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet;
  701. /**
  702. * Used to insert a new rule into the style sheet. The new rule now becomes part of the cascade.
  703. *
  704. * sheet = new Sheet("body {margin: 0}")
  705. * sheet.toString()
  706. * -> "body{margin:0;}"
  707. * sheet.insertRule("img {border: none}", 0)
  708. * -> 0
  709. * sheet.toString()
  710. * -> "img{border:none;}body{margin:0;}"
  711. *
  712. * @param {string} rule
  713. * @param {number} index
  714. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-insertRule
  715. * @return {number} The index within the style sheet's rule collection of the newly inserted rule.
  716. */
  717. CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
  718. if (index < 0 || index > this.cssRules.length) {
  719. throw new RangeError("INDEX_SIZE_ERR");
  720. }
  721. var cssRule = CSSOM.parse(rule).cssRules[0];
  722. cssRule.parentStyleSheet = this;
  723. this.cssRules.splice(index, 0, cssRule);
  724. return index;
  725. };
  726. /**
  727. * Used to delete a rule from the style sheet.
  728. *
  729. * sheet = new Sheet("img{border:none} body{margin:0}")
  730. * sheet.toString()
  731. * -> "img{border:none;}body{margin:0;}"
  732. * sheet.deleteRule(0)
  733. * sheet.toString()
  734. * -> "body{margin:0;}"
  735. *
  736. * @param {number} index within the style sheet's rule list of the rule to remove.
  737. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-deleteRule
  738. */
  739. CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) {
  740. if (index < 0 || index >= this.cssRules.length) {
  741. throw new RangeError("INDEX_SIZE_ERR");
  742. }
  743. this.cssRules.splice(index, 1);
  744. };
  745. /**
  746. * NON-STANDARD
  747. * @return {string} serialize stylesheet
  748. */
  749. CSSOM.CSSStyleSheet.prototype.toString = function() {
  750. var result = "";
  751. var rules = this.cssRules;
  752. for (var i=0; i<rules.length; i++) {
  753. result += rules[i].cssText + "\n";
  754. }
  755. return result;
  756. };
  757. /**
  758. * @constructor
  759. * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframesRule
  760. */
  761. CSSOM.CSSKeyframesRule = function CSSKeyframesRule() {
  762. CSSOM.CSSRule.call(this);
  763. this.name = '';
  764. this.cssRules = [];
  765. };
  766. CSSOM.CSSKeyframesRule.prototype = new CSSOM.CSSRule();
  767. CSSOM.CSSKeyframesRule.prototype.constructor = CSSOM.CSSKeyframesRule;
  768. CSSOM.CSSKeyframesRule.prototype.type = 7;
  769. //FIXME
  770. //CSSOM.CSSKeyframesRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
  771. //CSSOM.CSSKeyframesRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
  772. // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframesRule.cpp
  773. Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", {
  774. get: function() {
  775. var cssTexts = [];
  776. for (var i=0, length=this.cssRules.length; i < length; i++) {
  777. cssTexts.push(" " + this.cssRules[i].cssText);
  778. }
  779. return "@" + (this._vendorPrefix || '') + "keyframes " + this.name + " { \n" + cssTexts.join("\n") + "\n}";
  780. }
  781. });
  782. /**
  783. * @constructor
  784. * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframeRule
  785. */
  786. CSSOM.CSSKeyframeRule = function CSSKeyframeRule() {
  787. CSSOM.CSSRule.call(this);
  788. this.keyText = '';
  789. this.style = new CSSOM.CSSStyleDeclaration();
  790. this.style.parentRule = this;
  791. };
  792. CSSOM.CSSKeyframeRule.prototype = new CSSOM.CSSRule();
  793. CSSOM.CSSKeyframeRule.prototype.constructor = CSSOM.CSSKeyframeRule;
  794. CSSOM.CSSKeyframeRule.prototype.type = 8;
  795. //FIXME
  796. //CSSOM.CSSKeyframeRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
  797. //CSSOM.CSSKeyframeRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
  798. // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframeRule.cpp
  799. Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "cssText", {
  800. get: function() {
  801. return this.keyText + " {" + this.style.cssText + "} ";
  802. }
  803. });
  804. /**
  805. * @constructor
  806. * @see https://developer.mozilla.org/en/CSS/@-moz-document
  807. */
  808. CSSOM.MatcherList = function MatcherList(){
  809. this.length = 0;
  810. };
  811. CSSOM.MatcherList.prototype = {
  812. constructor: CSSOM.MatcherList,
  813. /**
  814. * @return {string}
  815. */
  816. get matcherText() {
  817. return Array.prototype.join.call(this, ", ");
  818. },
  819. /**
  820. * @param {string} value
  821. */
  822. set matcherText(value) {
  823. // just a temporary solution, actually it may be wrong by just split the value with ',', because a url can include ','.
  824. var values = value.split(",");
  825. var length = this.length = values.length;
  826. for (var i=0; i<length; i++) {
  827. this[i] = values[i].trim();
  828. }
  829. },
  830. /**
  831. * @param {string} matcher
  832. */
  833. appendMatcher: function(matcher) {
  834. if (Array.prototype.indexOf.call(this, matcher) === -1) {
  835. this[this.length] = matcher;
  836. this.length++;
  837. }
  838. },
  839. /**
  840. * @param {string} matcher
  841. */
  842. deleteMatcher: function(matcher) {
  843. var index = Array.prototype.indexOf.call(this, matcher);
  844. if (index !== -1) {
  845. Array.prototype.splice.call(this, index, 1);
  846. }
  847. }
  848. };
  849. /**
  850. * @constructor
  851. * @see https://developer.mozilla.org/en/CSS/@-moz-document
  852. */
  853. CSSOM.CSSDocumentRule = function CSSDocumentRule() {
  854. CSSOM.CSSRule.call(this);
  855. this.matcher = new CSSOM.MatcherList();
  856. this.cssRules = [];
  857. };
  858. CSSOM.CSSDocumentRule.prototype = new CSSOM.CSSRule();
  859. CSSOM.CSSDocumentRule.prototype.constructor = CSSOM.CSSDocumentRule;
  860. CSSOM.CSSDocumentRule.prototype.type = 10;
  861. //FIXME
  862. //CSSOM.CSSDocumentRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
  863. //CSSOM.CSSDocumentRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
  864. Object.defineProperty(CSSOM.CSSDocumentRule.prototype, "cssText", {
  865. get: function() {
  866. var cssTexts = [];
  867. for (var i=0, length=this.cssRules.length; i < length; i++) {
  868. cssTexts.push(this.cssRules[i].cssText);
  869. }
  870. return "@-moz-document " + this.matcher.matcherText + " {" + cssTexts.join("") + "}";
  871. }
  872. });
  873. /**
  874. * @constructor
  875. * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
  876. *
  877. * TODO: add if needed
  878. */
  879. CSSOM.CSSValue = function CSSValue() {
  880. };
  881. CSSOM.CSSValue.prototype = {
  882. constructor: CSSOM.CSSValue,
  883. // @see: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
  884. set cssText(text) {
  885. var name = this._getConstructorName();
  886. throw new Error('DOMException: property "cssText" of "' + name + '" is readonly and can not be replaced with "' + text + '"!');
  887. },
  888. get cssText() {
  889. var name = this._getConstructorName();
  890. throw new Error('getter "cssText" of "' + name + '" is not implemented!');
  891. },
  892. _getConstructorName: function() {
  893. var s = this.constructor.toString(),
  894. c = s.match(/function\s([^\(]+)/),
  895. name = c[1];
  896. return name;
  897. }
  898. };
  899. /**
  900. * @constructor
  901. * @see http://msdn.microsoft.com/en-us/library/ms537634(v=vs.85).aspx
  902. *
  903. */
  904. CSSOM.CSSValueExpression = function CSSValueExpression(token, idx) {
  905. this._token = token;
  906. this._idx = idx;
  907. };
  908. CSSOM.CSSValueExpression.prototype = new CSSOM.CSSValue();
  909. CSSOM.CSSValueExpression.prototype.constructor = CSSOM.CSSValueExpression;
  910. /**
  911. * parse css expression() value
  912. *
  913. * @return {Object}
  914. * - error:
  915. * or
  916. * - idx:
  917. * - expression:
  918. *
  919. * Example:
  920. *
  921. * .selector {
  922. * zoom: expression(documentElement.clientWidth > 1000 ? '1000px' : 'auto');
  923. * }
  924. */
  925. CSSOM.CSSValueExpression.prototype.parse = function() {
  926. var token = this._token,
  927. idx = this._idx;
  928. var character = '',
  929. expression = '',
  930. error = '',
  931. info,
  932. paren = [];
  933. for (; ; ++idx) {
  934. character = token.charAt(idx);
  935. // end of token
  936. if (character === '') {
  937. error = 'css expression error: unfinished expression!';
  938. break;
  939. }
  940. switch(character) {
  941. case '(':
  942. paren.push(character);
  943. expression += character;
  944. break;
  945. case ')':
  946. paren.pop(character);
  947. expression += character;
  948. break;
  949. case '/':
  950. if ((info = this._parseJSComment(token, idx))) { // comment?
  951. if (info.error) {
  952. error = 'css expression error: unfinished comment in expression!';
  953. } else {
  954. idx = info.idx;
  955. // ignore the comment
  956. }
  957. } else if ((info = this._parseJSRexExp(token, idx))) { // regexp
  958. idx = info.idx;
  959. expression += info.text;
  960. } else { // other
  961. expression += character;
  962. }
  963. break;
  964. case "'":
  965. case '"':
  966. info = this._parseJSString(token, idx, character);
  967. if (info) { // string
  968. idx = info.idx;
  969. expression += info.text;
  970. } else {
  971. expression += character;
  972. }
  973. break;
  974. default:
  975. expression += character;
  976. break;
  977. }
  978. if (error) {
  979. break;
  980. }
  981. // end of expression
  982. if (paren.length === 0) {
  983. break;
  984. }
  985. }
  986. var ret;
  987. if (error) {
  988. ret = {
  989. error: error
  990. };
  991. } else {
  992. ret = {
  993. idx: idx,
  994. expression: expression
  995. };
  996. }
  997. return ret;
  998. };
  999. /**
  1000. *
  1001. * @return {Object|false}
  1002. * - idx:
  1003. * - text:
  1004. * or
  1005. * - error:
  1006. * or
  1007. * false
  1008. *
  1009. */
  1010. CSSOM.CSSValueExpression.prototype._parseJSComment = function(token, idx) {
  1011. var nextChar = token.charAt(idx + 1),
  1012. text;
  1013. if (nextChar === '/' || nextChar === '*') {
  1014. var startIdx = idx,
  1015. endIdx,
  1016. commentEndChar;
  1017. if (nextChar === '/') { // line comment
  1018. commentEndChar = '\n';
  1019. } else if (nextChar === '*') { // block comment
  1020. commentEndChar = '*/';
  1021. }
  1022. endIdx = token.indexOf(commentEndChar, startIdx + 1 + 1);
  1023. if (endIdx !== -1) {
  1024. endIdx = endIdx + commentEndChar.length - 1;
  1025. text = token.substring(idx, endIdx + 1);
  1026. return {
  1027. idx: endIdx,
  1028. text: text
  1029. };
  1030. } else {
  1031. var error = 'css expression error: unfinished comment in expression!';
  1032. return {
  1033. error: error
  1034. };
  1035. }
  1036. } else {
  1037. return false;
  1038. }
  1039. };
  1040. /**
  1041. *
  1042. * @return {Object|false}
  1043. * - idx:
  1044. * - text:
  1045. * or
  1046. * false
  1047. *
  1048. */
  1049. CSSOM.CSSValueExpression.prototype._parseJSString = function(token, idx, sep) {
  1050. var endIdx = this._findMatchedIdx(token, idx, sep),
  1051. text;
  1052. if (endIdx === -1) {
  1053. return false;
  1054. } else {
  1055. text = token.substring(idx, endIdx + sep.length);
  1056. return {
  1057. idx: endIdx,
  1058. text: text
  1059. };
  1060. }
  1061. };
  1062. /**
  1063. * parse regexp in css expression
  1064. *
  1065. * @return {Object|false}
  1066. * - idx:
  1067. * - regExp:
  1068. * or
  1069. * false
  1070. */
  1071. /*
  1072. all legal RegExp
  1073. /a/
  1074. (/a/)
  1075. [/a/]
  1076. [12, /a/]
  1077. !/a/
  1078. +/a/
  1079. -/a/
  1080. * /a/
  1081. / /a/
  1082. %/a/
  1083. ===/a/
  1084. !==/a/
  1085. ==/a/
  1086. !=/a/
  1087. >/a/
  1088. >=/a/
  1089. </a/
  1090. <=/a/
  1091. &/a/
  1092. |/a/
  1093. ^/a/
  1094. ~/a/
  1095. <</a/
  1096. >>/a/
  1097. >>>/a/
  1098. &&/a/
  1099. ||/a/
  1100. ?/a/
  1101. =/a/
  1102. ,/a/
  1103. delete /a/
  1104. in /a/
  1105. instanceof /a/
  1106. new /a/
  1107. typeof /a/
  1108. void /a/
  1109. */
  1110. CSSOM.CSSValueExpression.prototype._parseJSRexExp = function(token, idx) {
  1111. var before = token.substring(0, idx).replace(/\s+$/, ""),
  1112. legalRegx = [
  1113. /^$/,
  1114. /\($/,
  1115. /\[$/,
  1116. /\!$/,
  1117. /\+$/,
  1118. /\-$/,
  1119. /\*$/,
  1120. /\/\s+/,
  1121. /\%$/,
  1122. /\=$/,
  1123. /\>$/,
  1124. /<$/,
  1125. /\&$/,
  1126. /\|$/,
  1127. /\^$/,
  1128. /\~$/,
  1129. /\?$/,
  1130. /\,$/,
  1131. /delete$/,
  1132. /in$/,
  1133. /instanceof$/,
  1134. /new$/,
  1135. /typeof$/,
  1136. /void$/
  1137. ];
  1138. var isLegal = legalRegx.some(function(reg) {
  1139. return reg.test(before);
  1140. });
  1141. if (!isLegal) {
  1142. return false;
  1143. } else {
  1144. var sep = '/';
  1145. // same logic as string
  1146. return this._parseJSString(token, idx, sep);
  1147. }
  1148. };
  1149. /**
  1150. *
  1151. * find next sep(same line) index in `token`
  1152. *
  1153. * @return {Number}
  1154. *
  1155. */
  1156. CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {
  1157. var startIdx = idx,
  1158. endIdx;
  1159. var NOT_FOUND = -1;
  1160. while(true) {
  1161. endIdx = token.indexOf(sep, startIdx + 1);
  1162. if (endIdx === -1) { // not found
  1163. endIdx = NOT_FOUND;
  1164. break;
  1165. } else {
  1166. var text = token.substring(idx + 1, endIdx),
  1167. matched = text.match(/\\+$/);
  1168. if (!matched || matched[0] % 2 === 0) { // not escaped
  1169. break;
  1170. } else {
  1171. startIdx = endIdx;
  1172. }
  1173. }
  1174. }
  1175. // boundary must be in the same line(js sting or regexp)
  1176. var nextNewLineIdx = token.indexOf('\n', idx + 1);
  1177. if (nextNewLineIdx < endIdx) {
  1178. endIdx = NOT_FOUND;
  1179. }
  1180. return endIdx;
  1181. };
  1182. /**
  1183. * @constructor
  1184. * @see https://drafts.csswg.org/css-cascade-5/#csslayerblockrule
  1185. */
  1186. CSSOM.CSSLayerBlockRule = function CSSLayerBlockRule() {
  1187. CSSOM.CSSGroupingRule.call(this);
  1188. this.layerName = "";
  1189. this.cssRules = [];
  1190. };
  1191. CSSOM.CSSLayerBlockRule.prototype = new CSSOM.CSSGroupingRule();
  1192. CSSOM.CSSLayerBlockRule.prototype.constructor = CSSOM.CSSLayerBlockRule;
  1193. CSSOM.CSSLayerBlockRule.prototype.type = 18;
  1194. Object.defineProperties(CSSOM.CSSLayerBlockRule.prototype, {
  1195. layerNameText: {
  1196. get: function () {
  1197. return this.layerName;
  1198. },
  1199. set: function (value) {
  1200. this.layerName = value;
  1201. },
  1202. configurable: true,
  1203. enumerable: true,
  1204. },
  1205. cssText: {
  1206. get: function () {
  1207. var cssTexts = [];
  1208. for (var i = 0, length = this.cssRules.length; i < length; i++) {
  1209. cssTexts.push(this.cssRules[i].cssText);
  1210. }
  1211. return "@layer " + this.layerNameText + " {" + cssTexts.join("") + "}";
  1212. },
  1213. configurable: true,
  1214. enumerable: true,
  1215. },
  1216. });
  1217. /**
  1218. * @param {string} token
  1219. */
  1220. CSSOM.parse = function parse(token) {
  1221. var i = 0;
  1222. /**
  1223. "before-selector" or
  1224. "selector" or
  1225. "atRule" or
  1226. "atBlock" or
  1227. "conditionBlock" or
  1228. "before-name" or
  1229. "name" or
  1230. "before-value" or
  1231. "value"
  1232. */
  1233. var state = "before-selector";
  1234. var index;
  1235. var buffer = "";
  1236. var valueParenthesisDepth = 0;
  1237. var SIGNIFICANT_WHITESPACE = {
  1238. "selector": true,
  1239. "value": true,
  1240. "value-parenthesis": true,
  1241. "atRule": true,
  1242. "importRule-begin": true,
  1243. "importRule": true,
  1244. "atBlock": true,
  1245. "containerBlock": true,
  1246. "conditionBlock": true,
  1247. 'documentRule-begin': true,
  1248. "layerBlock": true
  1249. };
  1250. var styleSheet = new CSSOM.CSSStyleSheet();
  1251. // @type CSSStyleSheet|CSSMediaRule|CSSContainerRule|CSSSupportsRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule
  1252. var currentScope = styleSheet;
  1253. // @type CSSMediaRule|CSSContainerRule|CSSSupportsRule|CSSKeyframesRule|CSSDocumentRule
  1254. var parentRule;
  1255. var ancestorRules = [];
  1256. var hasAncestors = false;
  1257. var prevScope;
  1258. var name, priority="", styleRule, mediaRule, containerRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, layerBlockRule;
  1259. var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g;
  1260. var parseError = function(message) {
  1261. var lines = token.substring(0, i).split('\n');
  1262. var lineCount = lines.length;
  1263. var charCount = lines.pop().length + 1;
  1264. var error = new Error(message + ' (line ' + lineCount + ', char ' + charCount + ')');
  1265. error.line = lineCount;
  1266. /* jshint sub : true */
  1267. error['char'] = charCount;
  1268. error.styleSheet = styleSheet;
  1269. throw error;
  1270. };
  1271. for (var character; (character = token.charAt(i)); i++) {
  1272. switch (character) {
  1273. case " ":
  1274. case "\t":
  1275. case "\r":
  1276. case "\n":
  1277. case "\f":
  1278. if (SIGNIFICANT_WHITESPACE[state]) {
  1279. buffer += character;
  1280. }
  1281. break;
  1282. // String
  1283. case '"':
  1284. index = i + 1;
  1285. do {
  1286. index = token.indexOf('"', index) + 1;
  1287. if (!index) {
  1288. parseError('Unmatched "');
  1289. }
  1290. } while (token[index - 2] === '\\');
  1291. buffer += token.slice(i, index);
  1292. i = index - 1;
  1293. switch (state) {
  1294. case 'before-value':
  1295. state = 'value';
  1296. break;
  1297. case 'importRule-begin':
  1298. state = 'importRule';
  1299. break;
  1300. }
  1301. break;
  1302. case "'":
  1303. index = i + 1;
  1304. do {
  1305. index = token.indexOf("'", index) + 1;
  1306. if (!index) {
  1307. parseError("Unmatched '");
  1308. }
  1309. } while (token[index - 2] === '\\');
  1310. buffer += token.slice(i, index);
  1311. i = index - 1;
  1312. switch (state) {
  1313. case 'before-value':
  1314. state = 'value';
  1315. break;
  1316. case 'importRule-begin':
  1317. state = 'importRule';
  1318. break;
  1319. }
  1320. break;
  1321. // Comment
  1322. case "/":
  1323. if (token.charAt(i + 1) === "*") {
  1324. i += 2;
  1325. index = token.indexOf("*/", i);
  1326. if (index === -1) {
  1327. parseError("Missing */");
  1328. } else {
  1329. i = index + 1;
  1330. }
  1331. } else {
  1332. buffer += character;
  1333. }
  1334. if (state === "importRule-begin") {
  1335. buffer += " ";
  1336. state = "importRule";
  1337. }
  1338. break;
  1339. // At-rule
  1340. case "@":
  1341. if (token.indexOf("@-moz-document", i) === i) {
  1342. state = "documentRule-begin";
  1343. documentRule = new CSSOM.CSSDocumentRule();
  1344. documentRule.__starts = i;
  1345. i += "-moz-document".length;
  1346. buffer = "";
  1347. break;
  1348. } else if (token.indexOf("@media", i) === i) {
  1349. state = "atBlock";
  1350. mediaRule = new CSSOM.CSSMediaRule();
  1351. mediaRule.__starts = i;
  1352. i += "media".length;
  1353. buffer = "";
  1354. break;
  1355. } else if (token.indexOf("@container", i) === i) {
  1356. state = "containerBlock";
  1357. containerRule = new CSSOM.CSSContainerRule();
  1358. containerRule.__starts = i;
  1359. i += "container".length;
  1360. buffer = "";
  1361. break;
  1362. } else if (token.indexOf("@layer", i) === i) {
  1363. state = "layerBlock"
  1364. layerBlockRule = new CSSOM.CSSLayerBlockRule();
  1365. layerBlockRule.__starts = i;
  1366. i += "layer".length;
  1367. buffer = "";
  1368. break;
  1369. } else if (token.indexOf("@supports", i) === i) {
  1370. state = "conditionBlock";
  1371. supportsRule = new CSSOM.CSSSupportsRule();
  1372. supportsRule.__starts = i;
  1373. i += "supports".length;
  1374. buffer = "";
  1375. break;
  1376. } else if (token.indexOf("@host", i) === i) {
  1377. state = "hostRule-begin";
  1378. i += "host".length;
  1379. hostRule = new CSSOM.CSSHostRule();
  1380. hostRule.__starts = i;
  1381. buffer = "";
  1382. break;
  1383. } else if (token.indexOf("@starting-style", i) === i) {
  1384. state = "startingStyleRule-begin";
  1385. i += "starting-style".length;
  1386. startingStyleRule = new CSSOM.CSSStartingStyleRule();
  1387. startingStyleRule.__starts = i;
  1388. buffer = "";
  1389. break;
  1390. } else if (token.indexOf("@import", i) === i) {
  1391. state = "importRule-begin";
  1392. i += "import".length;
  1393. buffer += "@import";
  1394. break;
  1395. } else if (token.indexOf("@font-face", i) === i) {
  1396. state = "fontFaceRule-begin";
  1397. i += "font-face".length;
  1398. fontFaceRule = new CSSOM.CSSFontFaceRule();
  1399. fontFaceRule.__starts = i;
  1400. buffer = "";
  1401. break;
  1402. } else {
  1403. atKeyframesRegExp.lastIndex = i;
  1404. var matchKeyframes = atKeyframesRegExp.exec(token);
  1405. if (matchKeyframes && matchKeyframes.index === i) {
  1406. state = "keyframesRule-begin";
  1407. keyframesRule = new CSSOM.CSSKeyframesRule();
  1408. keyframesRule.__starts = i;
  1409. keyframesRule._vendorPrefix = matchKeyframes[1]; // Will come out as undefined if no prefix was found
  1410. i += matchKeyframes[0].length - 1;
  1411. buffer = "";
  1412. break;
  1413. } else if (state === "selector") {
  1414. state = "atRule";
  1415. }
  1416. }
  1417. buffer += character;
  1418. break;
  1419. case "{":
  1420. if (state === "selector" || state === "atRule") {
  1421. styleRule.selectorText = buffer.trim();
  1422. styleRule.style.__starts = i;
  1423. buffer = "";
  1424. state = "before-name";
  1425. } else if (state === "atBlock") {
  1426. mediaRule.media.mediaText = buffer.trim();
  1427. if (parentRule) {
  1428. ancestorRules.push(parentRule);
  1429. }
  1430. currentScope = parentRule = mediaRule;
  1431. mediaRule.parentStyleSheet = styleSheet;
  1432. buffer = "";
  1433. state = "before-selector";
  1434. } else if (state === "containerBlock") {
  1435. containerRule.containerText = buffer.trim();
  1436. if (parentRule) {
  1437. ancestorRules.push(parentRule);
  1438. }
  1439. currentScope = parentRule = containerRule;
  1440. containerRule.parentStyleSheet = styleSheet;
  1441. buffer = "";
  1442. state = "before-selector";
  1443. } else if (state === "conditionBlock") {
  1444. supportsRule.conditionText = buffer.trim();
  1445. if (parentRule) {
  1446. ancestorRules.push(parentRule);
  1447. }
  1448. currentScope = parentRule = supportsRule;
  1449. supportsRule.parentStyleSheet = styleSheet;
  1450. buffer = "";
  1451. state = "before-selector";
  1452. } else if (state === "layerBlock") {
  1453. layerBlockRule.layerNameText = buffer.trim();
  1454. if (parentRule) {
  1455. ancestorRules.push(parentRule);
  1456. }
  1457. currentScope = parentRule = layerBlockRule;
  1458. layerBlockRule.parentStyleSheet = styleSheet;
  1459. buffer = "";
  1460. state = "before-selector";
  1461. } else if (state === "hostRule-begin") {
  1462. if (parentRule) {
  1463. ancestorRules.push(parentRule);
  1464. }
  1465. currentScope = parentRule = hostRule;
  1466. hostRule.parentStyleSheet = styleSheet;
  1467. buffer = "";
  1468. state = "before-selector";
  1469. } else if (state === "startingStyleRule-begin") {
  1470. if (parentRule) {
  1471. ancestorRules.push(parentRule);
  1472. }
  1473. currentScope = parentRule = startingStyleRule;
  1474. startingStyleRule.parentStyleSheet = styleSheet;
  1475. buffer = "";
  1476. state = "before-selector";
  1477. } else if (state === "fontFaceRule-begin") {
  1478. if (parentRule) {
  1479. fontFaceRule.parentRule = parentRule;
  1480. }
  1481. fontFaceRule.parentStyleSheet = styleSheet;
  1482. styleRule = fontFaceRule;
  1483. buffer = "";
  1484. state = "before-name";
  1485. } else if (state === "keyframesRule-begin") {
  1486. keyframesRule.name = buffer.trim();
  1487. if (parentRule) {
  1488. ancestorRules.push(parentRule);
  1489. keyframesRule.parentRule = parentRule;
  1490. }
  1491. keyframesRule.parentStyleSheet = styleSheet;
  1492. currentScope = parentRule = keyframesRule;
  1493. buffer = "";
  1494. state = "keyframeRule-begin";
  1495. } else if (state === "keyframeRule-begin") {
  1496. styleRule = new CSSOM.CSSKeyframeRule();
  1497. styleRule.keyText = buffer.trim();
  1498. styleRule.__starts = i;
  1499. buffer = "";
  1500. state = "before-name";
  1501. } else if (state === "documentRule-begin") {
  1502. // FIXME: what if this '{' is in the url text of the match function?
  1503. documentRule.matcher.matcherText = buffer.trim();
  1504. if (parentRule) {
  1505. ancestorRules.push(parentRule);
  1506. documentRule.parentRule = parentRule;
  1507. }
  1508. currentScope = parentRule = documentRule;
  1509. documentRule.parentStyleSheet = styleSheet;
  1510. buffer = "";
  1511. state = "before-selector";
  1512. }
  1513. break;
  1514. case ":":
  1515. if (state === "name") {
  1516. name = buffer.trim();
  1517. buffer = "";
  1518. state = "before-value";
  1519. } else {
  1520. buffer += character;
  1521. }
  1522. break;
  1523. case "(":
  1524. if (state === 'value') {
  1525. // ie css expression mode
  1526. if (buffer.trim() === 'expression') {
  1527. var info = (new CSSOM.CSSValueExpression(token, i)).parse();
  1528. if (info.error) {
  1529. parseError(info.error);
  1530. } else {
  1531. buffer += info.expression;
  1532. i = info.idx;
  1533. }
  1534. } else {
  1535. state = 'value-parenthesis';
  1536. //always ensure this is reset to 1 on transition
  1537. //from value to value-parenthesis
  1538. valueParenthesisDepth = 1;
  1539. buffer += character;
  1540. }
  1541. } else if (state === 'value-parenthesis') {
  1542. valueParenthesisDepth++;
  1543. buffer += character;
  1544. } else {
  1545. buffer += character;
  1546. }
  1547. break;
  1548. case ")":
  1549. if (state === 'value-parenthesis') {
  1550. valueParenthesisDepth--;
  1551. if (valueParenthesisDepth === 0) state = 'value';
  1552. }
  1553. buffer += character;
  1554. break;
  1555. case "!":
  1556. if (state === "value" && token.indexOf("!important", i) === i) {
  1557. priority = "important";
  1558. i += "important".length;
  1559. } else {
  1560. buffer += character;
  1561. }
  1562. break;
  1563. case ";":
  1564. switch (state) {
  1565. case "value":
  1566. styleRule.style.setProperty(name, buffer.trim(), priority);
  1567. priority = "";
  1568. buffer = "";
  1569. state = "before-name";
  1570. break;
  1571. case "atRule":
  1572. buffer = "";
  1573. state = "before-selector";
  1574. break;
  1575. case "importRule":
  1576. importRule = new CSSOM.CSSImportRule();
  1577. importRule.parentStyleSheet = importRule.styleSheet.parentStyleSheet = styleSheet;
  1578. importRule.cssText = buffer + character;
  1579. styleSheet.cssRules.push(importRule);
  1580. buffer = "";
  1581. state = "before-selector";
  1582. break;
  1583. default:
  1584. buffer += character;
  1585. break;
  1586. }
  1587. break;
  1588. case "}":
  1589. switch (state) {
  1590. case "value":
  1591. styleRule.style.setProperty(name, buffer.trim(), priority);
  1592. priority = "";
  1593. /* falls through */
  1594. case "before-name":
  1595. case "name":
  1596. styleRule.__ends = i + 1;
  1597. if (parentRule) {
  1598. styleRule.parentRule = parentRule;
  1599. }
  1600. styleRule.parentStyleSheet = styleSheet;
  1601. currentScope.cssRules.push(styleRule);
  1602. buffer = "";
  1603. if (currentScope.constructor === CSSOM.CSSKeyframesRule) {
  1604. state = "keyframeRule-begin";
  1605. } else {
  1606. state = "before-selector";
  1607. }
  1608. break;
  1609. case "keyframeRule-begin":
  1610. case "before-selector":
  1611. case "selector":
  1612. // End of media/supports/document rule.
  1613. if (!parentRule) {
  1614. parseError("Unexpected }");
  1615. }
  1616. // Handle rules nested in @media or @supports
  1617. hasAncestors = ancestorRules.length > 0;
  1618. while (ancestorRules.length > 0) {
  1619. parentRule = ancestorRules.pop();
  1620. if (
  1621. parentRule.constructor.name === "CSSMediaRule"
  1622. || parentRule.constructor.name === "CSSSupportsRule"
  1623. || parentRule.constructor.name === "CSSContainerRule"
  1624. || parentRule.constructor.name === "CSSLayerBlockRule"
  1625. || parentRule.constructor.name === "CSSStartingStyleRule"
  1626. ) {
  1627. prevScope = currentScope;
  1628. currentScope = parentRule;
  1629. currentScope.cssRules.push(prevScope);
  1630. break;
  1631. }
  1632. if (ancestorRules.length === 0) {
  1633. hasAncestors = false;
  1634. }
  1635. }
  1636. if (!hasAncestors) {
  1637. currentScope.__ends = i + 1;
  1638. styleSheet.cssRules.push(currentScope);
  1639. currentScope = styleSheet;
  1640. parentRule = null;
  1641. }
  1642. buffer = "";
  1643. state = "before-selector";
  1644. break;
  1645. }
  1646. break;
  1647. default:
  1648. switch (state) {
  1649. case "before-selector":
  1650. state = "selector";
  1651. styleRule = new CSSOM.CSSStyleRule();
  1652. styleRule.__starts = i;
  1653. break;
  1654. case "before-name":
  1655. state = "name";
  1656. break;
  1657. case "before-value":
  1658. state = "value";
  1659. break;
  1660. case "importRule-begin":
  1661. state = "importRule";
  1662. break;
  1663. }
  1664. buffer += character;
  1665. break;
  1666. }
  1667. }
  1668. return styleSheet;
  1669. };
  1670. /**
  1671. * Produces a deep copy of stylesheet — the instance variables of stylesheet are copied recursively.
  1672. * @param {CSSStyleSheet|CSSOM.CSSStyleSheet} stylesheet
  1673. * @nosideeffects
  1674. * @return {CSSOM.CSSStyleSheet}
  1675. */
  1676. CSSOM.clone = function clone(stylesheet) {
  1677. var cloned = new CSSOM.CSSStyleSheet();
  1678. var rules = stylesheet.cssRules;
  1679. if (!rules) {
  1680. return cloned;
  1681. }
  1682. for (var i = 0, rulesLength = rules.length; i < rulesLength; i++) {
  1683. var rule = rules[i];
  1684. var ruleClone = cloned.cssRules[i] = new rule.constructor();
  1685. var style = rule.style;
  1686. if (style) {
  1687. var styleClone = ruleClone.style = new CSSOM.CSSStyleDeclaration();
  1688. for (var j = 0, styleLength = style.length; j < styleLength; j++) {
  1689. var name = styleClone[j] = style[j];
  1690. styleClone[name] = style[name];
  1691. styleClone._importants[name] = style.getPropertyPriority(name);
  1692. }
  1693. styleClone.length = style.length;
  1694. }
  1695. if (rule.hasOwnProperty('keyText')) {
  1696. ruleClone.keyText = rule.keyText;
  1697. }
  1698. if (rule.hasOwnProperty('selectorText')) {
  1699. ruleClone.selectorText = rule.selectorText;
  1700. }
  1701. if (rule.hasOwnProperty('mediaText')) {
  1702. ruleClone.mediaText = rule.mediaText;
  1703. }
  1704. if (rule.hasOwnProperty('conditionText')) {
  1705. ruleClone.conditionText = rule.conditionText;
  1706. }
  1707. if (rule.hasOwnProperty('layerName')) {
  1708. ruleClone.layerName = rule.layerName;
  1709. }
  1710. if (rule.hasOwnProperty('cssRules')) {
  1711. ruleClone.cssRules = clone(rule).cssRules;
  1712. }
  1713. }
  1714. return cloned;
  1715. };