diff options
Diffstat (limited to 'js/controllers/styles-controller.js')
-rw-r--r-- | js/controllers/styles-controller.js | 1302 |
1 files changed, 1302 insertions, 0 deletions
diff --git a/js/controllers/styles-controller.js b/js/controllers/styles-controller.js new file mode 100644 index 00000000..d462bc98 --- /dev/null +++ b/js/controllers/styles-controller.js | |||
@@ -0,0 +1,1302 @@ | |||
1 | /* <copyright> | ||
2 | This file contains proprietary software owned by Motorola Mobility, Inc.<br/> | ||
3 | No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/> | ||
4 | (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. | ||
5 | </copyright> */ | ||
6 | |||
7 | var Montage = require("montage/core/core").Montage, | ||
8 | Component = require("montage/ui/component").Component, | ||
9 | cssShorthandMap = require("js/panels/CSSPanel/css-shorthand-map").CSS_SHORTHAND_MAP, | ||
10 | nj = require("js/lib/NJUtils.js").NJUtils; | ||
11 | |||
12 | /* | ||
13 | |||
14 | Styles Manager | ||
15 | ________________________________________ | ||
16 | Interface for dealing with stylesheets | ||
17 | Properties: | ||
18 | - Stage stylesheet | ||
19 | - Default user stylesheet (e.g. styles.css) | ||
20 | |||
21 | Methods: | ||
22 | - Rules: | ||
23 | - Get matching rules for element | ||
24 | - Add rule | ||
25 | - Delete rule | ||
26 | - Enable rule | ||
27 | - Disable rule | ||
28 | - Styles: | ||
29 | - Add styles of existing rules | ||
30 | - Delete styles of existing rules | ||
31 | - Enable style | ||
32 | - Disable style | ||
33 | - Stylesheets: | ||
34 | - Add local or external stylesheets (needs file I/O for creating external sheets) | ||
35 | - Delete stylesheets | ||
36 | - Enable stylesheets | ||
37 | - Disable stylesheets | ||
38 | |||
39 | use case : set background color | ||
40 | - needs to know most specific rule WITH that property | ||
41 | - | ||
42 | |||
43 | */ | ||
44 | |||
45 | var stylesController = exports.StylesController = Montage.create(Component, { | ||
46 | |||
47 | ///// Initialize after the active document has been set, and | ||
48 | ///// bind the document to prop w/ setter. The setter calls to find | ||
49 | ///// the stage and default css files. | ||
50 | |||
51 | handleAppLoaded : { | ||
52 | value: function() { | ||
53 | ///// Bind app's activeDocument property to | ||
54 | ///// styles controller's _activeDocument property | ||
55 | |||
56 | Object.defineBinding(this, "activeDocument", { | ||
57 | boundObject: this.application.ninja, | ||
58 | boundObjectPropertyPath: "currentDocument", | ||
59 | oneway: true | ||
60 | }); | ||
61 | } | ||
62 | }, | ||
63 | |||
64 | deserializedFromTemplate : { | ||
65 | value: function() { | ||
66 | this.eventManager.addEventListener( "appLoaded", this, false); | ||
67 | }, | ||
68 | enumerable : false | ||
69 | }, | ||
70 | ///// Active document gets automatically set when the | ||
71 | ///// document controller changes it | ||
72 | _activeDocument : { | ||
73 | value : null, | ||
74 | enumerable : false | ||
75 | }, | ||
76 | |||
77 | activeDocument : { | ||
78 | get : function() { | ||
79 | return this._activeDocument; | ||
80 | }, | ||
81 | set : function(document) { | ||
82 | |||
83 | ///// If the document is null set default stylesheets to null | ||
84 | |||
85 | if(!document) { | ||
86 | return false; | ||
87 | } | ||
88 | |||
89 | ///// setting document via binding | ||
90 | this._activeDocument = document; | ||
91 | |||
92 | ///// Stage stylesheet should always be found | ||
93 | this._stageStylesheet = this.getSheetFromElement(this.CONST.STAGE_SHEET_ID); | ||
94 | // Returns null if sheet not found (as in non-ninja projects) | ||
95 | // Setter will handle null case | ||
96 | this.defaultStylesheet = this.getSheetFromElement(this.CONST.DEFAULT_SHEET_ID); | ||
97 | |||
98 | //debugger; | ||
99 | }, | ||
100 | enumerable : false | ||
101 | }, | ||
102 | _stageStylesheet : { | ||
103 | value : null | ||
104 | }, | ||
105 | _defaultStylesheet : { | ||
106 | value : null | ||
107 | }, | ||
108 | defaultStylesheet : { | ||
109 | get : function() { | ||
110 | return this._defaultStylesheet; | ||
111 | }, | ||
112 | set : function(sheet) { | ||
113 | if(sheet) { | ||
114 | this._defaultStylesheet = sheet; | ||
115 | } else { | ||
116 | |||
117 | ///// Use the last stylesheet in the document as the default | ||
118 | |||
119 | var sheets = this._activeDocument._document.styleSheets, | ||
120 | lastIndex = sheets.length-1; | ||
121 | |||
122 | ///// If the only sheet is the stage stylesheet, this will be true | ||
123 | ///// in which case, we want to create a stylesheet to hold the | ||
124 | ///// user's style rules | ||
125 | |||
126 | if(sheets[lastIndex] === this._stageStyleSheet) { | ||
127 | this._defaultStylesheet = this.createStylesheet('nj-default'); | ||
128 | } else { | ||
129 | this._defaultStylesheet = sheets[lastIndex]; | ||
130 | } | ||
131 | |||
132 | } | ||
133 | } | ||
134 | }, | ||
135 | |||
136 | /* ----------------- Rule methods ----------------- */ | ||
137 | |||
138 | ///// Add Rule | ||
139 | ///// Passed in rule will be appended to the default stylesheet | ||
140 | ///// The rule can be in the form of a string (one argument), or | ||
141 | ///// the selector string and declaration string (two arguments), or | ||
142 | ///// the selector string and a declaration object. | ||
143 | ///// Optionally pass in the rule index (defaults to end of sheet) | ||
144 | |||
145 | /* | ||
146 | Signature 1 : | ||
147 | addRule( "#div1", "color:blue; width:100px;", 3) | ||
148 | [str] [str] [num] | ||
149 | |||
150 | Signature 2 (w/ styles object literal): | ||
151 | addRule( "#div1", { color:"blue", width:"100px" }, 3) | ||
152 | [str] [obj] [num] | ||
153 | |||
154 | Signature 3 (w/ full rule as one string) : | ||
155 | addRule( "#div1 { color:blue; width:100px; }", 3) | ||
156 | [str] [num] | ||
157 | |||
158 | */ | ||
159 | |||
160 | addRule : { | ||
161 | value : function(selector, declaration, stylesheet, index) { | ||
162 | //console.log("Add rule"); | ||
163 | var rulesLength = this._defaultStylesheet.rules.length, | ||
164 | argType = (typeof declaration), | ||
165 | ruleText = selector, | ||
166 | stylesheet = stylesheet || this._defaultStylesheet, | ||
167 | property, rule; | ||
168 | |||
169 | index = index || (argType === 'number') ? declaration : rulesLength; | ||
170 | |||
171 | if(argType === 'string') { | ||
172 | ruleText += '{' + declaration + '}'; | ||
173 | } else if(argType === 'object') { | ||
174 | ruleText += '{' + nj.cssFromObject(declaration) + '}'; | ||
175 | } | ||
176 | |||
177 | stylesheet.insertRule(ruleText, index); | ||
178 | |||
179 | rule = stylesheet.rules[index]; | ||
180 | |||
181 | ///// attach specificity to rule object | ||
182 | rule[this.CONST.SPECIFICITY_KEY] = this.getSpecificity(rule.selectorText); | ||
183 | |||
184 | ///// return the rule we just inserted | ||
185 | return rule; | ||
186 | } | ||
187 | }, | ||
188 | |||
189 | ///// Create Override Rule | ||
190 | ///// Takes a given rule and creates a rule with a selector of equal | ||
191 | ///// or greater specificity, and inserts it after the original rule | ||
192 | ///// This function will use a class to create the overriding selector, | ||
193 | ///// and the class will have to be applied to the element in order for | ||
194 | ///// the rule to stick | ||
195 | ///// Returns an object containing this classname and the rule itself | ||
196 | |||
197 | createOverrideRule : { | ||
198 | value : function(ruleToOverride, element) { | ||
199 | |||
200 | ///// Locally-scoped function to de-clutter variable declarations | ||
201 | function getSelector(el, rule) { | ||
202 | |||
203 | return this._getMostSpecificSelectorForElement(el, rule[this.CONST.SPECIFICITY_KEY]).selector; | ||
204 | } | ||
205 | |||
206 | var selectorToOverride = getSelector.bind(this)(element, ruleToOverride), | ||
207 | tokens = selectorToOverride.split(/\s/), | ||
208 | newClass = this.generateClassName(element.nodeName), | ||
209 | lastToken, pseudoSplit, base, pseudo, newToken, newSelector, rule; | ||
210 | |||
211 | ///// Creating an overriding selector by replacing the last | ||
212 | ///// class, attribute or type selector in passed-in rule's selector | ||
213 | |||
214 | ///// Grab the last token | ||
215 | lastToken = tokens[tokens.length-1]; | ||
216 | pseudoSplit = lastToken.split(':'); | ||
217 | ///// The last token can have pseudo class. Let's preserve it | ||
218 | base = pseudoSplit[0]; | ||
219 | pseudo = (pseudoSplit[1]) ? ':'+pseudoSplit[1] : ''; | ||
220 | |||
221 | ///// Now, all we want to do is replace the last token with a | ||
222 | ///// generated class name, except if the last token is an ID selector, | ||
223 | ///// in which case we append the generated class name to the ID selector | ||
224 | if(base.indexOf('#') !== -1) { | ||
225 | newToken = base + '.' + newClass + pseudo; | ||
226 | } else { | ||
227 | ///// Replace last class or attribute selector | ||
228 | ///// Get everything right before the last class or attribute selector | ||
229 | ///// to support compound selector values: (i.e. .firstClass.secondClass) | ||
230 | newToken = base.substring(0, Math.max(base.lastIndexOf('.'), base.lastIndexOf('['))); | ||
231 | ///// Append the generated class | ||
232 | newToken += '.' + newClass + pseudo; | ||
233 | } | ||
234 | |||