/* <copyright>
This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/>
(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved.
</copyright> */
var Montage = require("montage/core/core").Montage,
Component = require("montage/ui/component").Component,
cssShorthandMap = require("js/panels/CSSPanel/css-shorthand-map").CSS_SHORTHAND_MAP,
nj = require("js/lib/NJUtils").NJUtils;
/*
Styles Manager
________________________________________
Interface for dealing with stylesheets
Properties:
- Stage stylesheet
- Default user stylesheet (e.g. styles.css)
Methods:
- Rules:
- Get matching rules for element
- Add rule
- Delete rule
- Enable rule
- Disable rule
- Styles:
- Add styles of existing rules
- Delete styles of existing rules
- Enable style
- Disable style
- Stylesheets:
- Add local or external stylesheets (needs file I/O for creating external sheets)
- Delete stylesheets
- Enable stylesheets
- Disable stylesheets
use case : set background color
- needs to know most specific rule WITH that property
-
*/
var stylesController = exports.StylesController = Montage.create(Component, {
///// Initialize after the active document has been set, and
///// bind the document to prop w/ setter. The setter calls to find
///// the stage and default css files.
handleAppLoaded : {
value: function() {
///// Bind app's activeDocument property to
///// styles controller's _activeDocument property
Object.defineBinding(this, "activeDocument", {
boundObject: this.application.ninja,
boundObjectPropertyPath: "currentDocument",
oneway: true
});
}
},
deserializedFromTemplate : {
value: function() {
this.eventManager.addEventListener( "appLoaded", this, false);
},
enumerable : false
},
///// Active document gets automatically set when the
///// document controller changes it
_activeDocument : {
value : null,
enumerable : false
},
activeDocument : {
get : function() {
return this._activeDocument;
},
set : function(document) {
///// If the document is null set default stylesheets to null
if(!document) {
return false;
}
///// setting document via binding
this._activeDocument = document;
///// Stage stylesheet should always be found
this._stageStylesheet = this.getSheetFromElement(this.CONST.STAGE_SHEET_ID);
// Returns null if sheet not found (as in non-ninja projects)
// Setter will handle null case
this.defaultStylesheet = this.getSheetFromElement(this.CONST.DEFAULT_SHEET_ID);
this.userStyleSheets = nj.toArray(document._document.styleSheets).filter(function(sheet) {
return sheet !== this._stageStylesheet;
}, this);
NJevent('styleSheetsReady', this);
},
enumerable : false
},
userStyleSheets : {
value : null
},
_stageStylesheet : {
value : null
},
_defaultStylesheet : {
value : null
},
defaultStylesheet : {
get : function() {
return this._defaultStylesheet;
},
set : function(sheet) {
if(sheet) {
this._defaultStylesheet = sheet;
} else {
///// Use the last stylesheet in the document as the default
var sheets = this._activeDocument._document.styleSheets,
lastIndex = sheets.length-1;
///// If the only sheet is the stage stylesheet, this will be true
///// in which case, we want to create a stylesheet to hold the
///// user's style rules
if(sheets[lastIndex] === this._stageStyleSheet) {
this._defaultStylesheet = this.createStylesheet('nj-default');
} else {
this._defaultStylesheet = sheets[lastIndex];
}
}
}
},
/* ----------------- Rule methods ----------------- */
///// Add Rule
///// Passed in rule will be appended to the default stylesheet
///// The rule can be in the form of a string (one argument), or
///// the selector string and declaration string (two arguments), or
///// the selector string and a declaration object.
///// Optionally pass in the rule index (defaults to end of sheet)
/*
Signature 1 :
addRule( "#div1", "color:blue; width:100px;", 3)
[str] [str] [num]
Signature 2 (w/ styles object literal):
addRule( "#div1", { color:"blue", width:"100px" }, 3)
[str] [obj] [num]
Signature 3 (w/ full rule as one string) :
addRule( "#div1 { color:blue; width:100px; }", 3)
[str] [num]
*/
addRule : {
value : function(selector, declaration, stylesheet, index) {
//console.log("Add rule");
var rulesLength = this._defaultStylesheet.rules.length,
argType = (typeof declaration),
ruleText = selector,
stylesheet = stylesheet || this._defaultStylesheet,
property, rule;
index = index || (argType === 'number') ? declaration : rulesLength;
if(argType === 'string') {
ruleText += '{' + declaration + '}';
} else if(argType === 'object') {
ruleText += '{' + this.cssFromObject(declaration) + '}';
}
stylesheet.insertRule(ruleText, index);
this.styleSheetModified(stylesheet);
rule = stylesheet.rules[index];
///// attach specificity to rule object
///// if rule is css keyframes, return rule and don't attach specificity
if (rule instanceof WebKitCSSKeyframesRule) {
return rule;
}
rule[this.CONST.SPECIFICITY_KEY] = this.getSpecificity(rule.selectorText);
///// return the rule we just inserted
return rule;
}
},
///// Create Override Rule
///// Takes a given rule and creates a rule with a selector of equal
///// or greater specificity, and inserts it after the original rule
///// This function will use a class to create the overriding selector,
///// and the class will have to be applied to the element in order for
///// the rule to stick
///// Returns an object containing this classname and the rule itself
createOverrideRule : {
value : function(ruleToOverride, element) {
///// Locally-scoped function to de-clutter variable declarations
function getSelector(el,
|