var core = require("./core").dom.level2.core,1html = require("./html").dom.level2.html,2utils = require("../utils"),3defineGetter = utils.defineGetter,4defineSetter = utils.defineSetter,5inheritFrom = utils.inheritFrom,6cssom = require("cssom"),7cssstyle = require("cssstyle"),8assert = require('assert');910// What works now:11// - Accessing the rules defined in individual stylesheets12// - Modifications to style content attribute are reflected in style property13// - Modifications to style property are reflected in style content attribute14// TODO15// - Modifications to style element's textContent are reflected in sheet property.16// - Modifications to style element's sheet property are reflected in textContent.17// - Modifications to link.href property are reflected in sheet property.18// - Less-used features of link: disabled19// - Less-used features of style: disabled, scoped, title20// - CSSOM-View21// - getComputedStyle(): requires default stylesheet, cascading, inheritance,22// filtering by @media (screen? print?), layout for widths/heights23// - Load events are not in the specs, but apparently some browsers24// implement something. Should onload only fire after all @imports have been25// loaded, or only the primary sheet?2627core.StyleSheet = cssom.StyleSheet;28core.MediaList = cssom.MediaList;29core.CSSStyleSheet = cssom.CSSStyleSheet;30core.CSSRule = cssom.CSSRule;31core.CSSStyleRule = cssom.CSSStyleRule;32core.CSSMediaRule = cssom.CSSMediaRule;33core.CSSImportRule = cssom.CSSImportRule;34core.CSSStyleDeclaration = cssstyle.CSSStyleDeclaration;3536// Relavant specs37// http://www.w3.org/TR/DOM-Level-2-Style (2000)38// http://www.w3.org/TR/cssom-view/ (2008)39// http://dev.w3.org/csswg/cssom/ (2010) Meant to replace DOM Level 2 Style40// http://www.whatwg.org/specs/web-apps/current-work/multipage/ HTML5, of course41// http://dev.w3.org/csswg/css-style-attr/ not sure what's new here4243// Objects that aren't in cssom library but should be:44// CSSRuleList (cssom just uses array)45// CSSFontFaceRule46// CSSPageRule4748// These rules don't really make sense to implement, so CSSOM draft makes them49// obsolete.50// CSSCharsetRule51// CSSUnknownRule5253// These objects are considered obsolete by CSSOM draft, although modern54// browsers implement them.55// CSSValue56// CSSPrimitiveValue57// CSSValueList58// RGBColor59// Rect60// Counter6162// StyleSheetList -63// http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheetList64// added a push method to help manage the length65core.StyleSheetList = function() {66this._length = 0;67};68core.StyleSheetList.prototype = {69item: function (i) {70return this[i];71},72push: function (sheet) {73this[this._length] = sheet;74this._length++;75},76get length() {77return this._length;78}79};8081defineGetter(core.Document.prototype, 'styleSheets', function() {82if (!this._styleSheets) {83this._styleSheets = new core.StyleSheetList();84}85// TODO: each style and link element should register its sheet on creation86// and remove it on removal.87return this._styleSheets;88});899091/**92* @this {html.HTMLLinkElement|html.HTMLStyleElement}93* @param {string} url94* @param {cssom.CSSStyleSheet} sheet95* @see http://dev.w3.org/csswg/cssom/#requirements-on-user-agents-implementing096*/97function fetchStylesheet(url, sheet) {98html.resourceLoader.load(this, url, function(data, filename) {99// TODO: abort if the content-type is not text/css, and the document is100// in strict mode101sheet.href = html.resourceLoader.resolve(this.ownerDocument, url);102evaluateStylesheet.call(this, data, sheet, url);103});104}105/**106* @this {html.HTMLLinkElement|html.HTMLStyleElement}107* @param {string} data108* @param {cssom.CSSStyleSheet} sheet109* @param {string} baseUrl110*/111function evaluateStylesheet(data, sheet, baseUrl) {112// this is the element113var newStyleSheet = cssom.parse(data);114var spliceArgs = newStyleSheet.cssRules;115spliceArgs.unshift(0, sheet.cssRules.length);116Array.prototype.splice.apply(sheet.cssRules, spliceArgs);117scanForImportRules.call(this, sheet.cssRules, baseUrl);118this.ownerDocument.styleSheets.push(sheet);119}120/**121* @this {html.HTMLLinkElement|html.HTMLStyleElement}122* @param {cssom.CSSStyleSheet} sheet123* @param {string} baseUrl124*/125function scanForImportRules(cssRules, baseUrl) {126if (!cssRules) return;127for (var i = 0; i < cssRules.length; ++i) {128if (cssRules[i].cssRules) {129// @media rule: keep searching inside it.130scanForImportRules.call(this, cssRules[i].cssRules, baseUrl);131} else if (cssRules[i].href) {132// @import rule: fetch the resource and evaluate it.133// See http://dev.w3.org/csswg/cssom/#css-import-rule134// If loading of the style sheet fails its cssRules list is simply135// empty. I.e. an @import rule always has an associated style sheet.136fetchStylesheet.call(this, cssRules[i].href, this.sheet);137}138}139}140141/**142* @param {string} data143* @param {cssstyle.CSSStyleDeclaration} style144*/145function evaluateStyleAttribute(data) {146// this is the element.147148}149150/**151* Subclass of core.Attr that reflects the current cssText.152*/153function StyleAttr(node, value) {154this._node = node;155core.Attr.call(this, node.ownerDocument, 'style');156if (!this._node._ignoreValueOfStyleAttr) {157this.nodeValue = value;158}159}160inheritFrom(core.Attr, StyleAttr, {161get nodeValue() {162if (typeof this._node._style === 'string') {163return this._node._style;164} else {165return this._node.style.cssText;166}167},168set nodeValue(value) {169this._node._style = value;170}171});172173/**174* Overwrite core.AttrNodeMap#setNamedItem to create a StyleAttr instance175* instead of a core.Attr if the name equals 'style'.176*/177utils.intercept(core.AttributeList, '$setNode', function(_super, args, attr) {178if (attr.name == 'style') {179attr = new StyleAttr(this._parentNode, attr.nodeValue);180}181return _super.call(this, attr);182});183184/**185* Lazily create a CSSStyleDeclaration.186*/187defineGetter(html.HTMLElement.prototype, 'style', function() {188if (typeof this._style === 'string') {189// currently, cssom's parse doesn't really work if you pass in190// {state: 'name'}, so instead we just build a dummy sheet.191var styleSheet = cssom.parse('dummy{' + this._style + '}');192this._style = new cssstyle.CSSStyleDeclaration();193if (styleSheet.cssRules.length > 0 && styleSheet.cssRules[0].style) {194var newStyle = styleSheet.cssRules[0].style;195for (var i = 0; i < newStyle.length; ++i) {196var prop = newStyle[i];197this._style.setProperty(198prop,199newStyle.getPropertyValue(prop),200newStyle.getPropertyPriority(prop));201}202}203}204if (!this._style) {205this._style = new cssstyle.CSSStyleDeclaration();206207}208if (!this.getAttributeNode('style')) {209// Tell the StyleAttr constructor to not overwrite this._style210this._ignoreValueOfStyleAttr = true;211this.setAttribute('style');212this._ignoreValueOfStyleAttr = false;213}214return this._style;215});216217assert.equal(undefined, html.HTMLLinkElement._init);218html.HTMLLinkElement._init = function() {219this.addEventListener('DOMNodeInsertedIntoDocument', function() {220if (!/(?:[ \t\n\r\f]|^)stylesheet(?:[ \t\n\r\f]|$)/i.test(this.rel)) {221// rel is a space-separated list of tokens, and the original rel types222// are case-insensitive.223return;224}225if (this.href) {226fetchStylesheet.call(this, this.href, this.sheet);227}228});229this.addEventListener('DOMNodeRemovedFromDocument', function() {230});231};232/**233* @this {HTMLStyleElement|HTMLLinkElement}234*/235var getOrCreateSheet = function() {236if (!this._cssStyleSheet) {237this._cssStyleSheet = new cssom.CSSStyleSheet();238}239return this._cssStyleSheet;240};241defineGetter(html.HTMLLinkElement.prototype, 'sheet', getOrCreateSheet);242243assert.equal(undefined, html.HTMLStyleElement._init);244html.HTMLStyleElement._init = function() {245//console.log('init style')246this.addEventListener('DOMNodeInsertedIntoDocument', function() {247//console.log('style inserted')248//console.log('sheet: ', this.sheet);249if (this.type && this.type !== 'text/css') {250//console.log('bad type: ' + this.type)251return;252}253var content = '';254Array.prototype.forEach.call(this.childNodes, function (child) {255if (child.nodeType === child.TEXT_NODE) { // text node256content += child.nodeValue;257}258});259evaluateStylesheet.call(this, content, this.sheet, this._ownerDocument.URL);260});261};262defineGetter(html.HTMLStyleElement.prototype, 'sheet', getOrCreateSheet);263264exports.dom = {265level2 : {266html : html,267core : core268}269};270271272