diff options
-rw-r--r-- | js/controllers/styles-controller.js | 102 |
1 files changed, 86 insertions, 16 deletions
diff --git a/js/controllers/styles-controller.js b/js/controllers/styles-controller.js index afd298c9..8fc90e7d 100644 --- a/js/controllers/styles-controller.js +++ b/js/controllers/styles-controller.js | |||
@@ -171,7 +171,7 @@ var stylesController = exports.StylesController = Montage.create(Component, { | |||
171 | if(argType === 'string') { | 171 | if(argType === 'string') { |
172 | ruleText += '{' + declaration + '}'; | 172 | ruleText += '{' + declaration + '}'; |
173 | } else if(argType === 'object') { | 173 | } else if(argType === 'object') { |
174 | ruleText += '{' + nj.cssFromObject(declaration) + '}'; | 174 | ruleText += '{' + this.cssFromObject(declaration) + '}'; |
175 | } | 175 | } |
176 | 176 | ||
177 | stylesheet.insertRule(ruleText, index); | 177 | stylesheet.insertRule(ruleText, index); |
@@ -199,25 +199,42 @@ var stylesController = exports.StylesController = Montage.create(Component, { | |||
199 | 199 | ||
200 | ///// Locally-scoped function to de-clutter variable declarations | 200 | ///// Locally-scoped function to de-clutter variable declarations |
201 | function getSelector(el, rule) { | 201 | function getSelector(el, rule) { |
202 | |||
203 | return this._getMostSpecificSelectorForElement(el, rule[this.CONST.SPECIFICITY_KEY]).selector; | 202 | return this._getMostSpecificSelectorForElement(el, rule[this.CONST.SPECIFICITY_KEY]).selector; |
204 | } | 203 | } |
205 | 204 | ||
206 | var selectorToOverride = getSelector.bind(this)(element, ruleToOverride), | 205 | var selectorToOverride = getSelector.bind(this)(element, ruleToOverride), |
207 | tokens = selectorToOverride.split(/\s/), | 206 | overrideData, rule; |
208 | newClass = this.generateClassName(element.nodeName), | 207 | |
209 | lastToken, pseudoSplit, base, pseudo, newToken, newSelector, rule; | 208 | ///// Get the overriding selector and className |
209 | overrideData = this.createOverrideSelector(selectorToOverride, element.nodeName); | ||
210 | |||
211 | ///// Create new rule with selector and insert it after the rule we're overriding | ||
212 | rule = this.addRule(overrideData.selector + ' { }', this.getRuleIndex(ruleToOverride)+1); | ||
213 | |||
214 | return { | ||
215 | className : overrideData.className, | ||
216 | rule : rule | ||
217 | }; | ||
218 | |||
219 | } | ||
220 | }, | ||
221 | |||
222 | createOverrideSelector : { | ||
223 | value: function(selectorToOverride, classPrefix, className) { | ||
224 | var tokens = selectorToOverride.split(/\s/), | ||
225 | newClass = className || this.generateClassName(classPrefix, true), | ||
226 | lastToken, pseudoSplit, base, pseudo, newToken, newSelector; | ||
210 | 227 | ||
211 | ///// Creating an overriding selector by replacing the last | 228 | ///// Creating an overriding selector by replacing the last |
212 | ///// class, attribute or type selector in passed-in rule's selector | 229 | ///// class, attribute or type selector in passed-in rule's selector |
213 | 230 | ||
214 | ///// Grab the last token | 231 | ///// Grab the last token |
215 | lastToken = tokens[tokens.length-1]; | 232 | lastToken = tokens[tokens.length-1]; |
216 | pseudoSplit = lastToken.split(':'); | 233 | pseudoSplit = lastToken.split(':'); |
217 | ///// The last token can have pseudo class. Let's preserve it | 234 | ///// The last token can have pseudo class. Let's preserve it |
218 | base = pseudoSplit[0]; | 235 | base = pseudoSplit[0]; |
219 | pseudo = (pseudoSplit[1]) ? ':'+pseudoSplit[1] : ''; | 236 | pseudo = (pseudoSplit[1]) ? ':'+pseudoSplit[1] : ''; |
220 | 237 | ||
221 | ///// Now, all we want to do is replace the last token with a | 238 | ///// 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, | 239 | ///// 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 | 240 | ///// in which case we append the generated class name to the ID selector |
@@ -231,18 +248,15 @@ var stylesController = exports.StylesController = Montage.create(Component, { | |||
231 | ///// Append the generated class | 248 | ///// Append the generated class |
232 | newToken += '.' + newClass + pseudo; | 249 | newToken += '.' + newClass + pseudo; |
233 | } | 250 | } |
234 | 251 | ||
235 | ///// Now we can build the new selector by replacing the last token | 252 | ///// Now we can build the new selector by replacing the last token |
236 | tokens[tokens.length-1] = newToken; | 253 | tokens[tokens.length-1] = newToken; |
237 | newSelector = tokens.join(' '); | 254 | newSelector = tokens.join(' '); |
238 | 255 | ||
239 | rule = this.addRule(newSelector + ' { }', this.getRuleIndex(ruleToOverride)+1); | ||
240 | |||
241 | return { | 256 | return { |
242 | className : newClass, | 257 | className : newClass, |
243 | rule : rule | 258 | selector : newSelector |
244 | }; | 259 | }; |
245 | |||
246 | } | 260 | } |
247 | }, | 261 | }, |
248 | 262 | ||
@@ -370,7 +384,7 @@ var stylesController = exports.StylesController = Montage.create(Component, { | |||
370 | ///// from which an overriding rule can be created. | 384 | ///// from which an overriding rule can be created. |
371 | 385 | ||
372 | getDominantRuleForGroup : { | 386 | getDominantRuleForGroup : { |
373 | value : function(elements, property) { | 387 | value : function(elements, property, forceOverride) { |
374 | var selectorsToOverride = [], | 388 | var selectorsToOverride = [], |
375 | commonRules, dominantRules, useImportant; | 389 | commonRules, dominantRules, useImportant; |
376 | 390 | ||
@@ -639,7 +653,7 @@ var stylesController = exports.StylesController = Montage.create(Component, { | |||
639 | }, | 653 | }, |
640 | 654 | ||
641 | ///// Get Most Specific Selector For Element | 655 | ///// Get Most Specific Selector For Element |
642 | ///// Given a selector+specificty array, find the most specific | 656 | ///// Given a selector+specificity array, find the most specific |
643 | ///// selector for the passed-in element | 657 | ///// selector for the passed-in element |
644 | 658 | ||
645 | _getMostSpecificSelectorForElement : { | 659 | _getMostSpecificSelectorForElement : { |
@@ -721,7 +735,11 @@ var stylesController = exports.StylesController = Montage.create(Component, { | |||
721 | ///// Calculate specificity | 735 | ///// Calculate specificity |
722 | ///// Returns the specificity value of passed-in selector | 736 | ///// Returns the specificity value of passed-in selector |
723 | ///// WARNING: Do not pass in grouped selectors! | 737 | ///// WARNING: Do not pass in grouped selectors! |
724 | ///// Helpful for determining precedence of style rules | 738 | ///// Helpful for determining precedence of style rules |
739 | ///// Calculation javascript code courtesy of David Owens: | ||
740 | ///// http://gbradley.com/2009/10/02/css-specificity-in-javascript | ||
741 | ///// Used with author's permission | ||
742 | |||
725 | calculateSpecificity : { | 743 | calculateSpecificity : { |
726 | value : function(selector) { | 744 | value : function(selector) { |
727 | var s = selector.replace(/\([^\)]+\)/,''), | 745 | var s = selector.replace(/\([^\)]+\)/,''), |
@@ -922,6 +940,43 @@ var stylesController = exports.StylesController = Montage.create(Component, { | |||
922 | } | 940 | } |
923 | } | 941 | } |
924 | }, | 942 | }, |
943 | |||
944 | setGroupStyles : { | ||
945 | value : function(elements, styles) { | ||
946 | var properties = Object.keys(styles), | ||
947 | newClass = this.generateClassName(null, true), | ||
948 | selectors; | ||
949 | |||
950 | ///// TODO: move this: Locally-scoped function to de-clutter variable declarations | ||
951 | function getSelector(el, rule) { | ||
952 | return this._getMostSpecificSelectorForElement(el, rule[this.CONST.SPECIFICITY_KEY]).selector; | ||
953 | } | ||
954 | |||
955 | selectors = elements.map(function(el) { | ||
956 | ///// for each element, we want to find the most specific selector | ||
957 | var matchingRules = this.getMatchingRules(el, true); | ||
958 | |||
959 | this.addClass(el, newClass); | ||
960 | |||
961 | if(matchingRules.length === 0) { | ||
962 | return null; | ||
963 | } | ||
964 | |||
965 | var mostSpecificRule = matchingRules[0], // TODO: iterate over properties to find most specific | ||
966 | selectorToOverride = getSelector.bind(this)(el, mostSpecificRule), | ||
967 | override = this.createOverrideSelector(selectorToOverride, null, newClass); | ||
968 | |||
969 | return override.selector; | ||
970 | |||
971 | }, this); | ||
972 | |||
973 | selectors.filter(function(item) { | ||
974 | return item !== null; | ||
975 | }); | ||
976 | |||
977 | this.addRule(selectors.join(', '), styles); | ||
978 | } | ||
979 | }, | ||
925 | 980 | ||
926 | ///// Get Element Style | 981 | ///// Get Element Style |
927 | ///// Gets the style value that is currently applied to the element | 982 | ///// Gets the style value that is currently applied to the element |
@@ -1058,6 +1113,21 @@ var stylesController = exports.StylesController = Montage.create(Component, { | |||
1058 | } | 1113 | } |
1059 | }, | 1114 | }, |
1060 | 1115 | ||
1116 | ///// CSS From Object | ||
1117 | ///// Returns css text from object with key/value pairs | ||
1118 | ///// representing css styles | ||
1119 | |||
1120 | cssFromObject : { | ||
1121 | value : function(obj) { | ||
1122 | var cssText = ''; | ||
1123 | ///// For each key/value pair, create css text | ||
1124 | for(var prop in obj) { | ||
1125 | cssText += prop + ':' + obj[prop] + ';'; | ||
1126 | } | ||
1127 | return cssText; | ||
1128 | } | ||
1129 | }, | ||
1130 | |||
1061 | /* ----------------- Element model (rule cache) related methods ----------------- */ | 1131 | /* ----------------- Element model (rule cache) related methods ----------------- */ |
1062 | 1132 | ||
1063 | ///// Get Cached Rule For Property | 1133 | ///// Get Cached Rule For Property |