aboutsummaryrefslogtreecommitdiff
path: root/js/controllers/styles-controller.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/controllers/styles-controller.js')
-rw-r--r--js/controllers/styles-controller.js1302
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>
2This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
3No 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
7var 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
14Styles Manager
15________________________________________
16Interface for dealing with stylesheets
17Properties:
18 - Stage stylesheet
19 - Default user stylesheet (e.g. styles.css)
20
21Methods:
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
45var 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