]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/tiny_mce/classes/html/Styles.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / tiny_mce / classes / html / Styles.js
1 /**
2  * Styles.js
3  *
4  * Copyright 2010, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://tinymce.moxiecode.com/license
8  * Contributing: http://tinymce.moxiecode.com/contributing
9  */
10
11 /**
12  * This class is used to parse CSS styles it also compresses styles to reduce the output size.
13  *
14  * @example
15  * var Styles = new tinymce.html.Styles({
16  *    url_converter: function(url) {
17  *       return url;
18  *    }
19  * });
20  *
21  * styles = Styles.parse('border: 1px solid red');
22  * styles.color = 'red';
23  *
24  * console.log(new tinymce.html.StyleSerializer().serialize(styles));
25  *
26  * @class tinymce.html.Styles
27  * @version 3.4
28  */
29 tinymce.html.Styles = function(settings, schema) {
30         var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
31                 urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
32                 styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
33                 trimRightRegExp = /\s+$/,
34                 urlColorRegExp = /rgb/,
35                 undef, i, encodingLookup = {}, encodingItems;
36
37         settings = settings || {};
38
39         encodingItems = '\\" \\\' \\; \\: ; : \uFEFF'.split(' ');
40         for (i = 0; i < encodingItems.length; i++) {
41                 encodingLookup[encodingItems[i]] = '\uFEFF' + i;
42                 encodingLookup['\uFEFF' + i] = encodingItems[i];
43         }
44
45         function toHex(match, r, g, b) {
46                 function hex(val) {
47                         val = parseInt(val).toString(16);
48
49                         return val.length > 1 ? val : '0' + val; // 0 -> 00
50                 };
51
52                 return '#' + hex(r) + hex(g) + hex(b);
53         };
54
55         return {
56                 /**
57                  * Parses the specified RGB color value and returns a hex version of that color.
58                  *
59                  * @method toHex
60                  * @param {String} color RGB string value like rgb(1,2,3)
61                  * @return {String} Hex version of that RGB value like #FF00FF.
62                  */
63                 toHex : function(color) {
64                         return color.replace(rgbRegExp, toHex);
65                 },
66
67                 /**
68                  * Parses the specified style value into an object collection. This parser will also
69                  * merge and remove any redundant items that browsers might have added. It will also convert non hex
70                  * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
71                  *
72                  * @method parse
73                  * @param {String} css Style value to parse for example: border:1px solid red;.
74                  * @return {Object} Object representation of that style like {border : '1px solid red'}
75                  */
76                 parse : function(css) {
77                         var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope || this;
78
79                         function compress(prefix, suffix) {
80                                 var top, right, bottom, left;
81
82                                 // Get values and check it it needs compressing
83                                 top = styles[prefix + '-top' + suffix];
84                                 if (!top)
85                                         return;
86
87                                 right = styles[prefix + '-right' + suffix];
88                                 if (top != right)
89                                         return;
90
91                                 bottom = styles[prefix + '-bottom' + suffix];
92                                 if (right != bottom)
93                                         return;
94
95                                 left = styles[prefix + '-left' + suffix];
96                                 if (bottom != left)
97                                         return;
98
99                                 // Compress
100                                 styles[prefix + suffix] = left;
101                                 delete styles[prefix + '-top' + suffix];
102                                 delete styles[prefix + '-right' + suffix];
103                                 delete styles[prefix + '-bottom' + suffix];
104                                 delete styles[prefix + '-left' + suffix];
105                         };
106
107                         /**
108                          * Checks if the specific style can be compressed in other words if all border-width are equal.
109                          */
110                         function canCompress(key) {
111                                 var value = styles[key], i;
112
113                                 if (!value || value.indexOf(' ') < 0)
114                                         return;
115
116                                 value = value.split(' ');
117                                 i = value.length;
118                                 while (i--) {
119                                         if (value[i] !== value[0])
120                                                 return false;
121                                 }
122
123                                 styles[key] = value[0];
124
125                                 return true;
126                         };
127
128                         /**
129                          * Compresses multiple styles into one style.
130                          */
131                         function compress2(target, a, b, c) {
132                                 if (!canCompress(a))
133                                         return;
134
135                                 if (!canCompress(b))
136                                         return;
137
138                                 if (!canCompress(c))
139                                         return;
140
141                                 // Compress
142                                 styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
143                                 delete styles[a];
144                                 delete styles[b];
145                                 delete styles[c];
146                         };
147
148                         // Encodes the specified string by replacing all \" \' ; : with _<num>
149                         function encode(str) {
150                                 isEncoded = true;
151
152                                 return encodingLookup[str];
153                         };
154
155                         // Decodes the specified string by replacing all _<num> with it's original value \" \' etc
156                         // It will also decode the \" \' if keep_slashes is set to fale or omitted
157                         function decode(str, keep_slashes) {
158                                 if (isEncoded) {
159                                         str = str.replace(/\uFEFF[0-9]/g, function(str) {
160                                                 return encodingLookup[str];
161                                         });
162                                 }
163
164                                 if (!keep_slashes)
165                                         str = str.replace(/\\([\'\";:])/g, "$1");
166
167                                 return str;
168                         }
169
170                         if (css) {
171                                 // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
172                                 css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
173                                         return str.replace(/[;:]/g, encode);
174                                 });
175
176                                 // Parse styles
177                                 while (matches = styleRegExp.exec(css)) {
178                                         name = matches[1].replace(trimRightRegExp, '').toLowerCase();
179                                         value = matches[2].replace(trimRightRegExp, '');
180
181                                         if (name && value.length > 0) {
182                                                 // Opera will produce 700 instead of bold in their style values
183                                                 if (name === 'font-weight' && value === '700')
184                                                         value = 'bold';
185                                                 else if (name === 'color' || name === 'background-color') // Lowercase colors like RED
186                                                         value = value.toLowerCase();            
187
188                                                 // Convert RGB colors to HEX
189                                                 value = value.replace(rgbRegExp, toHex);
190
191                                                 // Convert URLs and force them into url('value') format
192                                                 value = value.replace(urlOrStrRegExp, function(match, url, url2, url3, str, str2) {
193                                                         str = str || str2;
194
195                                                         if (str) {
196                                                                 str = decode(str);
197
198                                                                 // Force strings into single quote format
199                                                                 return "'" + str.replace(/\'/g, "\\'") + "'";
200                                                         }
201
202                                                         url = decode(url || url2 || url3);
203
204                                                         // Convert the URL to relative/absolute depending on config
205                                                         if (urlConverter)
206                                                                 url = urlConverter.call(urlConverterScope, url, 'style');
207
208                                                         // Output new URL format
209                                                         return "url('" + url.replace(/\'/g, "\\'") + "')";
210                                                 });
211
212                                                 styles[name] = isEncoded ? decode(value, true) : value;
213                                         }
214
215                                         styleRegExp.lastIndex = matches.index + matches[0].length;
216                                 }
217
218                                 // Compress the styles to reduce it's size for example IE will expand styles
219                                 compress("border", "");
220                                 compress("border", "-width");
221                                 compress("border", "-color");
222                                 compress("border", "-style");
223                                 compress("padding", "");
224                                 compress("margin", "");
225                                 compress2('border', 'border-width', 'border-style', 'border-color');
226
227                                 // Remove pointless border, IE produces these
228                                 if (styles.border === 'medium none')
229                                         delete styles.border;
230                         }
231
232                         return styles;
233                 },
234
235                 /**
236                  * Serializes the specified style object into a string.
237                  *
238                  * @method serialize
239                  * @param {Object} styles Object to serialize as string for example: {border : '1px solid red'}
240                  * @param {String} element_name Optional element name, if specified only the styles that matches the schema will be serialized.
241                  * @return {String} String representation of the style object for example: border: 1px solid red.
242                  */
243                 serialize : function(styles, element_name) {
244                         var css = '', name, value;
245
246                         function serializeStyles(name) {
247                                 var styleList, i, l, value;
248
249                                 styleList = schema.styles[name];
250                                 if (styleList) {
251                                         for (i = 0, l = styleList.length; i < l; i++) {
252                                                 name = styleList[i];
253                                                 value = styles[name];
254
255                                                 if (value !== undef && value.length > 0)
256                                                         css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
257                                         }
258                                 }
259                         };
260
261                         // Serialize styles according to schema
262                         if (element_name && schema && schema.styles) {
263                                 // Serialize global styles and element specific styles
264                                 serializeStyles('*');
265                                 serializeStyles(element_name);
266                         } else {
267                                 // Output the styles in the order they are inside the object
268                                 for (name in styles) {
269                                         value = styles[name];
270
271                                         if (value !== undef && value.length > 0)
272                                                 css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
273                                 }
274                         }
275
276                         return css;
277                 }
278         };
279 };