1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 sc_require('css/css_rule') ; 9 10 /** 11 @class SC.CSSStyleSheet 12 13 A style sheet object wraps a document style sheet object. `C.CSSStyleSheet` 14 will re-use stylesheet objects as needed. 15 16 @extends SC.Object 17 */ 18 SC.CSSStyleSheet = SC.Object.extend( 19 /** @scope SC.CSSStyleSheet.prototype */ { 20 21 init: function() { 22 sc_super() ; 23 24 var ss = this.styleSheet ; 25 if (!ss) { 26 // create the stylesheet object the hard way (works everywhere) 27 ss = this.styleSheet = document.createElement('style') ; 28 ss.type = 'text/css' ; 29 var head = document.getElementsByTagName('head')[0] ; 30 if (!head) head = document.documentElement ; // fix for Opera 31 head.appendChild(ss) ; 32 } 33 34 // cache this object for later 35 var ssObjects = this.constructor.styleSheets ; 36 if (!ssObjects) ssObjects = this.constructor.styleSheets = {} ; 37 ssObjects[SC.guidFor(ss)] ; 38 39 // create rules array 40 var rules = ss.rules || SC.EMPTY_ARRAY ; 41 var array = SC.SparseArray.create(rules.length) ; 42 array.delegate = this ; 43 this.rules = array ; 44 45 return this ; 46 }, 47 48 /** 49 @type Boolean YES if the stylesheet is enabled. 50 */ 51 isEnabled: function(key, val) { 52 if (val !== undefined) { 53 this.styleSheet.disabled = !val ; 54 } 55 return !this.styleSheet.disabled ; 56 }.property(), 57 isEnabledBindingDefault: SC.Binding.bool(), 58 59 /** 60 **DO NOT MODIFY THIS OBJECT DIRECTLY!!!!** Use the methods defined on this 61 object to update properties of the style sheet; otherwise, your changes 62 will not be reflected. 63 64 @type CSSStyleSheet RO 65 */ 66 styleSheet: null, 67 68 /** 69 @type String 70 */ 71 href: function(key, val) { 72 if (val !== undefined) { 73 this.styleSheet.href = val ; 74 } 75 else return this.styleSheet.href ; 76 }.property(), 77 78 /** 79 @type String 80 */ 81 title: function(key, val) { 82 if (val !== undefined) { 83 this.styleSheet.title = val ; 84 } 85 else return this.styleSheet.title ; 86 }.property(), 87 88 /** 89 @type SC.Array contains SC.CSSRule objects 90 */ 91 rules: null, 92 93 /** 94 You can also insert and remove rules on the rules property array. 95 */ 96 insertRule: function(rule) { 97 var rules = this.get('rules') ; 98 }, 99 100 /** 101 You can also insert and remove rules on the rules property array. 102 */ 103 deleteRule: function(rule) { 104 var rules = this.get('rules') ; 105 rules.removeObject(rule) ; 106 }, 107 108 // TODO: implement a destroy method 109 110 /** 111 @private 112 113 Invoked by the sparse array whenever it needs a particular index 114 provided. Provide the content for the index. 115 */ 116 sparseArrayDidRequestIndex: function(array, idx) { 117 // sc_assert(this.rules === array) ; 118 var rules = this.styleSheet.rules || SC.EMPTY_ARRAY ; 119 var rule = rules[idx] ; 120 if (rule) { 121 array.provideContentAtIndex(idx, SC.CSSRule.create({ 122 rule: rule, 123 styleSheet: this 124 })); 125 } 126 }, 127 128 /** @private synchronize the browser's rules array with our own */ 129 sparseArrayDidReplace: function(array, idx, amt, objects) { 130 var cssRules = objects.collect(function(obj) { return obj.rule; }) ; 131 this.styleSheet.rules.replace(idx, amt, cssRules) ; 132 } 133 134 }); 135 136 SC.mixin(SC.CSSStyleSheet, 137 /** SC.CSSStyleSheet */{ 138 139 /** 140 Find a stylesheet object by name or href. If by name, `.css` will be 141 appended automatically. 142 143 var ss = SC.CSSStyleSheet.find('style.css') ; 144 var ss2 = SC.CSSStyleSheet.find('style') ; // same thing 145 sc_assert(ss === ss2) ; // SC.CSSStyleSheet objects are stable 146 147 @param {String} nameOrUrl a stylesheet name or href to find 148 @returns {SC.CSSStyleSheet} null if not found 149 */ 150 find: function(nameOrUrl) { 151 var isUrl = nameOrUrl ? nameOrUrl.indexOf('/') >= 0 : NO ; 152 153 if (!nameOrUrl) return null ; // no name or url? fail! 154 155 if (!isUrl && nameOrUrl.indexOf('.css') == -1) { 156 nameOrUrl = nameOrUrl + '.css' ; 157 } 158 159 // initialize styleSheet cache 160 var ssObjects = this.styleSheets ; 161 if (!ssObjects) ssObjects = this.styleSheets = {} ; 162 163 var styleSheets = document.styleSheets ; 164 var ss, ssName, ssObject, guid ; 165 for (var idx=0, len=styleSheets.length; idx < len; ++idx) { 166 ss = styleSheets[idx] ; 167 if (isUrl) { 168 if (ss.href === nameOrUrl) { 169 guid = SC.guidFor(ss) ; 170 ssObject = ssObjects[guid] ; 171 if (!ssObject) { 172 // cache for later 173 ssObject = ssObjects[guid] = this.create({ styleSheet: ss }) ; 174 } 175 return ssObject ; 176 } 177 } 178 else { 179 if (ssName = ss.href) { 180 ssName = ssName.split('/') ; // break up URL 181 ssName = ssName[ssName.length-1] ; // get last component 182 if (ssName == nameOrUrl) { 183 guid = SC.guidFor(ss) ; 184 ssObject = ssObjects[guid] ; 185 if (!ssObject) { 186 // cache for later 187 ssObject = ssObjects[guid] = this.create({ styleSheet: ss }) ; 188 } 189 return ssObject ; 190 } 191 } 192 } 193 } 194 return null ; // stylesheet not found 195 }, 196 197 styleSheets: null 198 199 }); 200