summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/html/styles.js
blob: 8306ed52c46c189ca0c90b0b8e9a54dd4985aa4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
//>>built
define("dojox/html/styles", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/window", "dojo/_base/sniff"], 
	function(lang, ArrayUtil, Window, has) {
	// summary:
	//		Methods for creating and manipulating dynamic CSS Styles and Style Sheets
	//
	// example:
	//		| dojox.html.createStyle("#myDiv input", "font-size:24px");
	//			Creates Style #myDiv input, which can now be applied to myDiv, and
	//			the inner input will be targeted
	//		| dojox.html.createStyle(".myStyle", "color:#FF0000");
	//			Now the class myStyle can be assigned to a node's className
	var dh = lang.getObject("dojox.html",true);
	var dynamicStyleMap = {};
	var pageStyleSheets = {};
	var titledSheets = [];

	dh.insertCssRule = function(/*String*/selector, /*String*/declaration, /*String*/styleSheetName){
		// summary:
		//	Creates a style and attaches it to a dynamically created stylesheet
		//	arguments:
		//		selector:
		//					A fully qualified class name, as it would appear in
		//					a CSS dojo.doc. Start classes with periods, target
		//					nodes with '#'. Large selectors can also be created
		//					like:
		//					| "#myDiv.myClass span input"
		//		declaration:
		//					A single string that would make up a style block, not
		//					including the curly braces. Include semi-colons between
		//					statements. Do not use JavaScript style declarations
		//					in camel case, use as you would in a CSS dojo.doc:
		//					| "color:#ffoooo;font-size:12px;margin-left:5px;"
		//		styleSheetName: ( optional )
		//					Name of the dynamic style sheet this rule should be
		//					inserted into. If is not found by that name, it is
		//					created. If no name is passed, the name "default" is
		//					used.
		//
		var ss = dh.getDynamicStyleSheet(styleSheetName);
		var styleText = selector + " {" + declaration + "}";
		console.log("insertRule:", styleText);
		if(has("ie")){
			// Note: check for if(ss.cssText) does not work
			ss.cssText+=styleText;
			console.log("ss.cssText:", ss.cssText);
		}else if(ss.sheet){
			ss.sheet.insertRule(styleText, ss._indicies.length);
		}else{
			ss.appendChild(Window.doc.createTextNode(styleText));
		}
		ss._indicies.push(selector+" "+declaration);
		return selector; // String
	};

	dh.removeCssRule = function(/*String*/selector, /*String*/declaration, /*String*/styleSheetName){
		// summary:
		//		Removes a cssRule base on the selector and declaration passed
		//		The declaration is needed for cases of dupe selectors
		// description: Only removes DYNAMICALLY created cssRules. If you
		//		created it with dh.insertCssRule, it can be removed.
		//
		var ss;
		var index=-1;
		var nm;
		var i;
		for(nm in dynamicStyleMap){
			if(styleSheetName && styleSheetName !== nm) {continue;}
			ss = dynamicStyleMap[nm];
			for(i=0;i<ss._indicies.length;i++){
				if(selector+" "+declaration === ss._indicies[i]){
					index = i;
					break;
				}
			}
			if(index>-1) { break; }
		}
		if(!ss){
			console.warn("No dynamic style sheet has been created from which to remove a rule.");
			return false;
		}
		if(index===-1){
			console.warn("The css rule was not found and could not be removed.");
			return false;
		}
		ss._indicies.splice(index, 1);
		if(has("ie")){
			// Note: check for if(ss.removeRule) does not work
			ss.removeRule(index);
		}else if(ss.sheet){
			ss.sheet.deleteRule(index);
		}
		return true; //Boolean
	};

	dh.modifyCssRule = function(selector, declaration, styleSheetName){
		//Not implemented - it seems to have some merit for changing some complex
		//selectors. It's not much use for changing simple ones like "span".
		//For now, simply write a new rule which will cascade over the first.
		// summary
		//	Modfies an existing cssRule
	};

	dh.getStyleSheet = function(/*String*/styleSheetName){
		// summary:
		//		Returns a style sheet based on the argument.
		//		Searches dynamic style sheets first. If no matches,
		//		searches document style sheets.
		//
		// argument: (optional)
		//		A title or an href to a style sheet. Title can be
		//		an attribute in a tag, or a dynamic style sheet
		//		reference. Href can be the name of the file.
		//		If no argument, the assumed created dynamic style
		//		sheet is used.
		// try dynamic sheets first
		if(dynamicStyleMap[styleSheetName || "default"]){
			return dynamicStyleMap[styleSheetName || "default"];
		}
		if(!styleSheetName){
			// no arg is nly good for the default style sheet
			// and it has not been created yet.
			return false;
		}
		var allSheets = dh.getStyleSheets();
		// now try document style sheets by name
		if(allSheets[styleSheetName]){
			return dh.getStyleSheets()[styleSheetName];
		}
		// check for partial matches in hrefs (so that a fully
		//qualified name does not have to be passed)
		var nm;
		for ( nm in allSheets){
			if(	allSheets[nm].href && allSheets[nm].href.indexOf(styleSheetName)>-1){
				return allSheets[nm];
			}
		}
		return false; //StyleSheet or false
	};

	dh.getDynamicStyleSheet = function(/*String*/styleSheetName){
		// summary:
		//		Creates and returns a dynamically created style sheet
		//		used for dynamic styles
		//
		//	argument:
		//			styleSheetName /* optional String */
		//			The name given the style sheet so that multiple
		//			style sheets can be created and referenced. If
		//			no argument is given, the name "default" is used.
		//
		if(!styleSheetName){ styleSheetName="default"; }
		if(!dynamicStyleMap[styleSheetName]){
			if(Window.doc.createStyleSheet){ //IE
				dynamicStyleMap[styleSheetName] = Window.doc.createStyleSheet();
				if(has("ie") < 9) {
					// IE9 calls this read-only. Loving the new browser so far.
					dynamicStyleMap[styleSheetName].title = styleSheetName;
				}
			}else{
				dynamicStyleMap[styleSheetName] = Window.doc.createElement("style");
				dynamicStyleMap[styleSheetName].setAttribute("type", "text/css");
				Window.doc.getElementsByTagName("head")[0].appendChild(dynamicStyleMap[styleSheetName]);
				console.log(styleSheetName, " ss created: ", dynamicStyleMap[styleSheetName].sheet);
			}
			dynamicStyleMap[styleSheetName]._indicies = [];
		}
		return dynamicStyleMap[styleSheetName]; //StyleSheet
	};

	dh.enableStyleSheet = function(/*String*/styleSheetName){
		// summary:
		//		Enables the style sheet with the name passed in the
		//		argument. Deafults to the default style sheet.
		//
		var ss = dh.getStyleSheet(styleSheetName);
		if(ss){
			if(ss.sheet){
				ss.sheet.disabled = false;
			}else{
				ss.disabled = false;
			}
		}
	};

	dh.disableStyleSheet = function(styleSheetName){
		// summary:
		//		Disables the dynamic style sheet with the name passed in the
		//		argument. If no arg is passed, defaults to the default style sheet.
		//
		var ss = dh.getStyleSheet(styleSheetName);
		if(ss){
			if(ss.sheet){
				ss.sheet.disabled = true;
			}else{
				ss.disabled = true;
			}
		}
	};

	dh.activeStyleSheet = function(/*?String*/title){
		// summary:
		//		Getter/Setter
		// description:
		//		If passed a title, enables a that style sheet. All other
		//		toggle-able style sheets are disabled.
		//		If no argument is passed, returns currently enabled
		//		style sheet.
		//
		var sheets = dh.getToggledStyleSheets();
		var i;
		if(arguments.length === 1){
			//console.log("sheets:", sheets);
			ArrayUtil.forEach(sheets, function(s){
				s.disabled = (s.title === title) ? false : true;
			});
		}else{
			for(i=0;i<sheets.length;i++){
				if(sheets[i].disabled === false){
					return sheets[i];
				}
			}
		}
		return true; //StyleSheet or Boolean - FIXME - doesn't make a lot of sense
	};

	dh.getPreferredStyleSheet = function(){
		// summary
		//	Returns the style sheet that was initially enabled
		//	on document launch.
		//TODO
	};

	dh.getToggledStyleSheets = function(){
		// summary:
		//		Searches HTML for style sheets that are "toggle-able" -
		//		can be enabled and disabled. These would include sheets
		//		with the title attribute, as well as the REL attribute.
		//	returns:
		//		An array of all toggle-able style sheets
		//	TODO: Sets of style sheets could be grouped according to
		//			an ID and used in sets, much like different
		//			groups of radio buttons. It would not however be
		//			according to W3C spec
		//
		var nm;
		if(!titledSheets.length){
			var sObjects = dh.getStyleSheets();
			for(nm in sObjects){
				if(sObjects[nm].title){
					titledSheets.push(sObjects[nm]);
				}
			}
		}
		return titledSheets; //Array
	};

	dh.getStyleSheets = function(){
		// summary:
		//		Collects all the style sheets referenced in the HTML page,
		//		including any incuded via @import.
		//
		//	returns:
		//		An hash map of all the style sheets.
		//
		//TODO: Does not recursively search for @imports, so it will
		//		only go one level deep.
		//
		if(pageStyleSheets.collected) {return pageStyleSheets;}
		var sheets = Window.doc.styleSheets;
		ArrayUtil.forEach(sheets, function(n){
			var s = (n.sheet) ? n.sheet : n;
			var name = s.title || s.href;
			if(has("ie")){
				// IE attaches a style sheet for VML - do not include this
				if(s.cssText.indexOf("#default#VML") === -1){
					if(s.href){
						// linked
						pageStyleSheets[name] = s;
					}else if(s.imports.length){
						// Imported via @import
						ArrayUtil.forEach(s.imports, function(si){
							pageStyleSheets[si.title || si.href] = si;
						});
					}else{
						//embedded within page
						pageStyleSheets[name] = s;
					}
				}
			}else{
				//linked or embedded
				pageStyleSheets[name] = s;
				pageStyleSheets[name].id = s.ownerNode.id;
				ArrayUtil.forEach(s.cssRules, function(r){
					if(r.href){
						// imported
						pageStyleSheets[r.href] = r.styleSheet;
						pageStyleSheets[r.href].id = s.ownerNode.id;
					}
				});
			}
		});
		//console.log("pageStyleSheets:", pageStyleSheets);
		pageStyleSheets.collected = true;
		return pageStyleSheets; //Object
	};
	
	return dh;
});