1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2005, 2006, 2007, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Default constructor.
 26  * @constructor
 27  * @class
 28  * This is a static class that defines a number of constants and helper methods that
 29  * support the working with CSS.
 30  * 
 31  * @author Ross Dargahi
 32  * 
 33  * @private
 34  */
 35 DwtCssStyle = function() {
 36 }
 37 
 38 // Common class name constants used in Dwt
 39 
 40 /**
 41  * "mouseOver": transitory state while mouse is over the item.
 42  */
 43 DwtCssStyle.HOVER = "hover";
 44 
 45 /**
 46  * "mouseDown": transitory state while left mouse button is being pressed on the item.
 47  */
 48 DwtCssStyle.ACTIVE = "active";
 49 
 50 /**
 51  * item is "on", (for example: selected tab, select item(s) in list, or button that stays depressed).
 52  */
 53 DwtCssStyle.SELECTED = "selected";
 54 
 55 /**
 56  * Currently used for item that is currently viewed, but not selected (other checkboxes are checked, or a right click action is on a different item).
 57  */
 58 DwtCssStyle.ALT_SELECTED = "altSelected";
 59 
 60 /**
 61  * "disabled": item is not actionable (for example: because not appropriate or some other condition needs to be true).
 62  */
 63 DwtCssStyle.DISABLED = "disabled";
 64 
 65 /**
 66  * "focused": item has keyboard focus.
 67  */
 68 DwtCssStyle.FOCUSED = "focused";
 69 
 70 /**
 71  * UI component is target of some external action, for example:
 72  * <ul>
 73  * <li>item is the target of right-click (for example: show menu)</li>
 74  * <li>item is the thing being dragged</li>
 75  * </ul>
 76  */
 77 DwtCssStyle.ACTIONED = "actioned";
 78 
 79 /**
 80  * Matched item in a list (for example: in conv list view, items that match the search. NOT used if *all* items match the search).
 81  */
 82 DwtCssStyle.MATCHED	 = "matched";
 83 
 84 /**
 85  * UI component is the current, valid drop target.
 86  */
 87 DwtCssStyle.DRAG_OVER = "dragOver";
 88 
 89 /**
 90  * Item being dragged is over a valid drop target.
 91  */
 92 DwtCssStyle.DROPPABLE = "droppable";
 93 
 94 /**
 95  * Item being dragged is NOT over a valid drop target.
 96  */
 97 DwtCssStyle.NOT_DROPPABLE = "notDroppable";
 98 
 99 /**
100  * Represents of an item *as it is being dragged* (for example: thing moving around the screen).
101  */
102 DwtCssStyle.DRAG_PROXY = "dragProxy";
103 
104 /**
105  * Class applies only to linux browsers.
106  */
107 DwtCssStyle.LINUX = "linux";
108 
109 
110 DwtCssStyle.getProperty = 
111 function(htmlElement, cssPropName) {
112 	var result;
113 	if (htmlElement.ownerDocument == null) {
114 		// IE5.5 does not support ownerDocument
115 		for (var parent = htmlElement.parentNode; parent.parentNode != null; parent = parent.parentNode) {}
116 		var doc = parent;
117 	} else {
118 		var doc = htmlElement.ownerDocument;
119 	}
120 
121 	if (doc.defaultView && doc.defaultView.getComputedStyle) {
122 		var cssDecl = doc.defaultView.getComputedStyle(htmlElement, "");
123 		if (cssDecl && cssDecl.length > 0) { //on Chrome/Safari it returns cssDecl with length 0 for some elements for some reason. (a wild guess could be invisible items, as it happens with invite toolbar when it's invisible) So in that case fall back on the other ways.
124 			return cssDecl.getPropertyValue(cssPropName);
125 		}
126 	}
127 	
128 	// Convert CSS -> DOM name for IE etc
129 	var tokens = cssPropName.split("-");
130 	// Shift one word off the array and capitalize the rest
131 	var propName = tokens.shift() + AjxUtil.map(tokens, AjxStringUtil.capitalize).join("");
132 
133 	if (htmlElement.currentStyle) {
134 		return htmlElement.currentStyle[propName];
135 	} else if (htmlElement.style) {
136 		return htmlElement.style[propName];
137 	}
138 };
139 
140 DwtCssStyle.getComputedStyleObject = 
141 function(htmlElement) {
142 	if (htmlElement.ownerDocument == null) {
143 		// IE5.5 does not suppoert ownerDocument
144 		for (var parent = htmlElement.parentNode; parent.parentNode != null; parent = parent.parentNode) {}
145 		var doc = parent;
146 	} else {
147 		var doc = htmlElement.ownerDocument;
148 	}
149 	
150 	if (doc.defaultView) {
151 		var style = doc.defaultView.getComputedStyle(htmlElement, null);
152 		if (!style && htmlElement.style) {
153 // TODO: destructive ?
154 			htmlElement.style.display = "";
155 			style = doc.defaultView.getComputedStyle(htmlElement, null);
156 		}
157 		return style || {};
158 	} else if (htmlElement.currentStyle)
159 		return htmlElement.currentStyle;
160 	else if (htmlElement.style)
161 		return htmlElement.style;
162 };
163 
164 DwtCssStyle.removeProperty = function(el, prop) {
165 	if (prop instanceof Array) {
166 		for (var i = prop.length; --i >= 0;)
167 			DwtCssStyle.removeProperty(el, prop[i]);
168 	} else {
169 		if (AjxEnv.isIE) {
170 			el.style.removeAttribute(prop, true);
171 		} else {
172 			prop = prop.replace(/([A-Z])/g, "-$1");
173 			el.style.removeProperty(prop);
174 		}
175 	}
176 };
177 
178 /**
179  * Adds a rule to a stylesheet.
180  * 
181  * @param {StyleSheet}	stylesheet		a CSS stylesheet
182  * @param {string}		selector		rule selector
183  * @param {string}		declaration		styles
184  * @param {string}		index			insertion index (optional)
185  * 
186  * @return	index at which rule was inserted (for later removal)
187  */
188 DwtCssStyle.addRule =
189 function(stylesheet, selector, declaration, index) {
190 	if (stylesheet.addRule) {	// IE
191 		//if index is not specified insert at the end so that new rule takes precedence
192 		index = index || (stylesheet.rules.length);
193 		stylesheet.addRule(selector, declaration, index);
194 	}
195 	else {
196 		//if index is not specified insert at the end so that new rule takes precedence
197 		index = index || (stylesheet.cssRules.length);
198 		stylesheet.insertRule(selector + "{" + declaration + "}", index);
199 	}
200 	return index;
201 };
202 
203 /**
204  * Removes the rule at the given index.
205  * 
206  * @param {StyleSheet}	stylesheet		a CSS stylesheet
207  * @param {string}		index			insertion index (optional)
208  */
209 DwtCssStyle.removeRule =
210 function(stylesheet, index) {
211 	if (stylesheet.removeRule) {	// IE
212 		stylesheet.removeRule(index);
213 	}
214 	else {
215 		stylesheet.deleteRule(index);
216 	}
217 };
218 
219 DwtCssStyle.__PIXEL_RE = /^(-?[0-9]+(?:\.[0-9]*)?)px$/;
220 DwtCssStyle.__DIMENSION_RE = /^(-?[0-9]+(?:\.[0-9]*)?)([a-z]*|%)$/;
221 DwtCssStyle.__NUMBER_RE = /^(-?[0-9]+(?:\.[0-9]*)?)+$/
222 
223 /**
224  * Obtain the font size of the root element in pixels.
225  */
226 DwtCssStyle.__getRootFontSize = function() {
227 
228 	var fontsize = DwtCssStyle.getProperty(document.documentElement, 'font-size');
229 
230 	if (!DwtCssStyle.__PIXEL_RE.test(fontsize)) {
231 		DBG.println(AjxDebug.DBG1, 'font size of root element is not in pixels!');
232 		return -1;
233 	}
234 
235 	return parseInt(fontsize);
236 };
237 
238 /**
239  * Convert a CSS value to a pixel count; unhandled units raise an error.
240  *
241  * @param   {String}    val     a font size value in some form
242  *
243  * @return  {Number}    the size in pixels, or -1 if there is an error
244  */
245 DwtCssStyle.asPixelCount = function(val) {
246 
247 	if (!val) {
248 		DBG.println(AjxDebug.DBG1, 'DwtCssStyle.asPixelCount: missing argument');
249 		return -1;
250 	}
251 
252 	var dimension, unit, match;
253 
254 	// assume pixels if no unit is specified
255 	if (typeof val === 'number' || DwtCssStyle.__NUMBER_RE.test(val)) {
256 		dimension = Number(val);
257 		unit = 'px';
258 	} else if ((match = DwtCssStyle.__DIMENSION_RE.exec(val))) {
259 		dimension = Number(match[1]);
260 		unit = match[2];
261 	} else {
262 		DBG.println(AjxDebug.DBG1, 'DwtCssStyle.asPixelCount: unsupported argument: ' + val);
263 		return -1;
264 	}
265 
266 	switch (unit) {
267 		case 'rem': {
268 			var rootFontSize = DwtCssStyle.__getRootFontSize();
269 			return rootFontSize !== -1 ? dimension * rootFontSize : -1;
270 		}
271 
272 		// see http://www.w3.org/TR/css3-values/#absolute-lengths
273 		case 'mm': {
274 			dimension /= 10;
275 		}
276 
277 		case 'cm': {
278 			dimension /= 2.54;
279 		}
280 
281 		case 'in': {
282 			dimension *= 6;
283 		}
284 
285 		case 'pc': {
286 			dimension *= 12;
287 		}
288 
289 		case 'pt': {
290 			dimension /= 0.75;
291 		}
292 
293 		case 'px': {
294 			return dimension;
295 		}
296 
297 		case 'ch':
298 		case 'em':
299 		case 'ex': {
300 			DBG.println(AjxDebug.DBG1, 'DwtCssStyle.asPixelCount: cannot convert context-dependent CSS unit ' + unit);
301 			return -1;
302 		}
303 
304 		default: {
305 			DBG.println(AjxDebug.DBG1, 'DwtCssStyle.asPixelCount: unrecognized CSS unit ' + unit);
306 			return -1;
307 		}
308 	}
309 };
310