Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80684 views
1
var core = require("./core").dom.level2.core,
2
html = require("./html").dom.level2.html,
3
utils = require("../utils"),
4
defineGetter = utils.defineGetter,
5
defineSetter = utils.defineSetter,
6
inheritFrom = utils.inheritFrom,
7
cssom = require("cssom"),
8
cssstyle = require("cssstyle"),
9
assert = require('assert');
10
11
// What works now:
12
// - Accessing the rules defined in individual stylesheets
13
// - Modifications to style content attribute are reflected in style property
14
// - Modifications to style property are reflected in style content attribute
15
// TODO
16
// - Modifications to style element's textContent are reflected in sheet property.
17
// - Modifications to style element's sheet property are reflected in textContent.
18
// - Modifications to link.href property are reflected in sheet property.
19
// - Less-used features of link: disabled
20
// - Less-used features of style: disabled, scoped, title
21
// - CSSOM-View
22
// - getComputedStyle(): requires default stylesheet, cascading, inheritance,
23
// filtering by @media (screen? print?), layout for widths/heights
24
// - Load events are not in the specs, but apparently some browsers
25
// implement something. Should onload only fire after all @imports have been
26
// loaded, or only the primary sheet?
27
28
core.StyleSheet = cssom.StyleSheet;
29
core.MediaList = cssom.MediaList;
30
core.CSSStyleSheet = cssom.CSSStyleSheet;
31
core.CSSRule = cssom.CSSRule;
32
core.CSSStyleRule = cssom.CSSStyleRule;
33
core.CSSMediaRule = cssom.CSSMediaRule;
34
core.CSSImportRule = cssom.CSSImportRule;
35
core.CSSStyleDeclaration = cssstyle.CSSStyleDeclaration;
36
37
// Relavant specs
38
// http://www.w3.org/TR/DOM-Level-2-Style (2000)
39
// http://www.w3.org/TR/cssom-view/ (2008)
40
// http://dev.w3.org/csswg/cssom/ (2010) Meant to replace DOM Level 2 Style
41
// http://www.whatwg.org/specs/web-apps/current-work/multipage/ HTML5, of course
42
// http://dev.w3.org/csswg/css-style-attr/ not sure what's new here
43
44
// Objects that aren't in cssom library but should be:
45
// CSSRuleList (cssom just uses array)
46
// CSSFontFaceRule
47
// CSSPageRule
48
49
// These rules don't really make sense to implement, so CSSOM draft makes them
50
// obsolete.
51
// CSSCharsetRule
52
// CSSUnknownRule
53
54
// These objects are considered obsolete by CSSOM draft, although modern
55
// browsers implement them.
56
// CSSValue
57
// CSSPrimitiveValue
58
// CSSValueList
59
// RGBColor
60
// Rect
61
// Counter
62
63
// StyleSheetList -
64
// http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheetList
65
// added a push method to help manage the length
66
core.StyleSheetList = function() {
67
this._length = 0;
68
};
69
core.StyleSheetList.prototype = {
70
item: function (i) {
71
return this[i];
72
},
73
push: function (sheet) {
74
this[this._length] = sheet;
75
this._length++;
76
},
77
get length() {
78
return this._length;
79
}
80
};
81
82
defineGetter(core.Document.prototype, 'styleSheets', function() {
83
if (!this._styleSheets) {
84
this._styleSheets = new core.StyleSheetList();
85
}
86
// TODO: each style and link element should register its sheet on creation
87
// and remove it on removal.
88
return this._styleSheets;
89
});
90
91
92
/**
93
* @this {html.HTMLLinkElement|html.HTMLStyleElement}
94
* @param {string} url
95
* @param {cssom.CSSStyleSheet} sheet
96
* @see http://dev.w3.org/csswg/cssom/#requirements-on-user-agents-implementing0
97
*/
98
function fetchStylesheet(url, sheet) {
99
html.resourceLoader.load(this, url, function(data, filename) {
100
// TODO: abort if the content-type is not text/css, and the document is
101
// in strict mode
102
sheet.href = html.resourceLoader.resolve(this.ownerDocument, url);
103
evaluateStylesheet.call(this, data, sheet, url);
104
});
105
}
106
/**
107
* @this {html.HTMLLinkElement|html.HTMLStyleElement}
108
* @param {string} data
109
* @param {cssom.CSSStyleSheet} sheet
110
* @param {string} baseUrl
111
*/
112
function evaluateStylesheet(data, sheet, baseUrl) {
113
// this is the element
114
var newStyleSheet = cssom.parse(data);
115
var spliceArgs = newStyleSheet.cssRules;
116
spliceArgs.unshift(0, sheet.cssRules.length);
117
Array.prototype.splice.apply(sheet.cssRules, spliceArgs);
118
scanForImportRules.call(this, sheet.cssRules, baseUrl);
119
this.ownerDocument.styleSheets.push(sheet);
120
}
121
/**
122
* @this {html.HTMLLinkElement|html.HTMLStyleElement}
123
* @param {cssom.CSSStyleSheet} sheet
124
* @param {string} baseUrl
125
*/
126
function scanForImportRules(cssRules, baseUrl) {
127
if (!cssRules) return;
128
for (var i = 0; i < cssRules.length; ++i) {
129
if (cssRules[i].cssRules) {
130
// @media rule: keep searching inside it.
131
scanForImportRules.call(this, cssRules[i].cssRules, baseUrl);
132
} else if (cssRules[i].href) {
133
// @import rule: fetch the resource and evaluate it.
134
// See http://dev.w3.org/csswg/cssom/#css-import-rule
135
// If loading of the style sheet fails its cssRules list is simply
136
// empty. I.e. an @import rule always has an associated style sheet.
137
fetchStylesheet.call(this, cssRules[i].href, this.sheet);
138
}
139
}
140
}
141
142
/**
143
* @param {string} data
144
* @param {cssstyle.CSSStyleDeclaration} style
145
*/
146
function evaluateStyleAttribute(data) {
147
// this is the element.
148
149
}
150
151
/**
152
* Subclass of core.Attr that reflects the current cssText.
153
*/
154
function StyleAttr(node, value) {
155
this._node = node;
156
core.Attr.call(this, node.ownerDocument, 'style');
157
if (!this._node._ignoreValueOfStyleAttr) {
158
this.nodeValue = value;
159
}
160
}
161
inheritFrom(core.Attr, StyleAttr, {
162
get nodeValue() {
163
if (typeof this._node._style === 'string') {
164
return this._node._style;
165
} else {
166
return this._node.style.cssText;
167
}
168
},
169
set nodeValue(value) {
170
this._node._style = value;
171
}
172
});
173
174
/**
175
* Overwrite core.AttrNodeMap#setNamedItem to create a StyleAttr instance
176
* instead of a core.Attr if the name equals 'style'.
177
*/
178
utils.intercept(core.AttributeList, '$setNode', function(_super, args, attr) {
179
if (attr.name == 'style') {
180
attr = new StyleAttr(this._parentNode, attr.nodeValue);
181
}
182
return _super.call(this, attr);
183
});
184
185
/**
186
* Lazily create a CSSStyleDeclaration.
187
*/
188
defineGetter(html.HTMLElement.prototype, 'style', function() {
189
if (typeof this._style === 'string') {
190
// currently, cssom's parse doesn't really work if you pass in
191
// {state: 'name'}, so instead we just build a dummy sheet.
192
var styleSheet = cssom.parse('dummy{' + this._style + '}');
193
this._style = new cssstyle.CSSStyleDeclaration();
194
if (styleSheet.cssRules.length > 0 && styleSheet.cssRules[0].style) {
195
var newStyle = styleSheet.cssRules[0].style;
196
for (var i = 0; i < newStyle.length; ++i) {
197
var prop = newStyle[i];
198
this._style.setProperty(
199
prop,
200
newStyle.getPropertyValue(prop),
201
newStyle.getPropertyPriority(prop));
202
}
203
}
204
}
205
if (!this._style) {
206
this._style = new cssstyle.CSSStyleDeclaration();
207
208
}
209
if (!this.getAttributeNode('style')) {
210
// Tell the StyleAttr constructor to not overwrite this._style
211
this._ignoreValueOfStyleAttr = true;
212
this.setAttribute('style');
213
this._ignoreValueOfStyleAttr = false;
214
}
215
return this._style;
216
});
217
218
assert.equal(undefined, html.HTMLLinkElement._init);
219
html.HTMLLinkElement._init = function() {
220
this.addEventListener('DOMNodeInsertedIntoDocument', function() {
221
if (!/(?:[ \t\n\r\f]|^)stylesheet(?:[ \t\n\r\f]|$)/i.test(this.rel)) {
222
// rel is a space-separated list of tokens, and the original rel types
223
// are case-insensitive.
224
return;
225
}
226
if (this.href) {
227
fetchStylesheet.call(this, this.href, this.sheet);
228
}
229
});
230
this.addEventListener('DOMNodeRemovedFromDocument', function() {
231
});
232
};
233
/**
234
* @this {HTMLStyleElement|HTMLLinkElement}
235
*/
236
var getOrCreateSheet = function() {
237
if (!this._cssStyleSheet) {
238
this._cssStyleSheet = new cssom.CSSStyleSheet();
239
}
240
return this._cssStyleSheet;
241
};
242
defineGetter(html.HTMLLinkElement.prototype, 'sheet', getOrCreateSheet);
243
244
assert.equal(undefined, html.HTMLStyleElement._init);
245
html.HTMLStyleElement._init = function() {
246
//console.log('init style')
247
this.addEventListener('DOMNodeInsertedIntoDocument', function() {
248
//console.log('style inserted')
249
//console.log('sheet: ', this.sheet);
250
if (this.type && this.type !== 'text/css') {
251
//console.log('bad type: ' + this.type)
252
return;
253
}
254
var content = '';
255
Array.prototype.forEach.call(this.childNodes, function (child) {
256
if (child.nodeType === child.TEXT_NODE) { // text node
257
content += child.nodeValue;
258
}
259
});
260
evaluateStylesheet.call(this, content, this.sheet, this._ownerDocument.URL);
261
});
262
};
263
defineGetter(html.HTMLStyleElement.prototype, 'sheet', getOrCreateSheet);
264
265
exports.dom = {
266
level2 : {
267
html : html,
268
core : core
269
}
270
};
271
272