PluginProbe
Customify / 2.10.9
Customify v2.10.9
2.10.9 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.7.1 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.6.0 1.6.0.1 1.6.5 1.7.0 1.7.1 All 77 releases
customify / js / vendor / CSSOM.js

CSSOM.js in Customify 2.10.9, at js/vendor/CSSOM.js

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