ace-rtl.js 8.75 KB
//simple script to convert styles to RTL
//it's not bullet proof or anything and some parts need to be manually treated
//it's because it doesn't convert all rules to RTL, only the parts that are needed will be put in a separate file
//ace-rtl.css and overriden


function makeRTL(css_input) {

	function trim(str) {
		return str.replace(/^\s+/, '').replace(/\s+$/, '');
	}

	var defaultify = {
		'left': 'auto',
		'right': 'auto',

		'padding-right': 0,
		'padding-left': 0,

		'margin-right': 'auto',
		'margin-left': 'auto',

		'border-left-color': 'transparent',
		'border-right-color': 'transparent',
		
		'border-left-width': 'transparent',
		'border-right-width': 'transparent',
		
		'border-left-style': 'none',
		'border-right-style': 'none',

		'border-left': 'none',
		'border-right': 'none',
		
		'-moz-border-left-colors': 'none',
		'-moz-border-right-colors': 'none',
		
		'border-bottom-right-radius': 0,
		'border-bottom-left-radius': 0,
		'border-top-right-radius': 0,
		'border-top-left-radius': 0
	};

	function splitted_values(rule, value) {
		value = value.replace(/\!important/g, '');
		if(rule.match(/border-color/i) && value.match(/rgb/i)) {
			//remove extra spaces in border-color
			value = trim(value).replace(/\s\s+/g, ' ').replace(/\s*([\(,])\s*/g, '$1')
		}
		var part = trim(value).split(/\s+/);
		if(part.length >= 4) {
			//if(part[3] == part[1]) return null;
			//swap left & right value		
			if(rule.match(/border-radius/i)) return [part[1], part[0], part[3], part[2]].join(' ');
			return [part[0], part[3], part[2], part[1]].join(' ');
		}
		//if(rule.match(/margin/i)) return part.join(' ');//if we return null, cases like [.rtl ul]'s margin will override that of [.nav-list], so we should have [.rtl .nav-list]'s margin again for safe measures
		return null;
	}


	//first remove all comments
	var selector_regex = /((?:\s*@media\s*[^\{\}]*)?(?:\s*@(?:-(?:moz|webkit|ms|o|khtml)-)?keyframes\s*[^\{\}]*)?(?:\s*[^\{\}]*))(\{)/ig

	var lastCloseBracket = 100000000, media_wrap = false, media_selector = '', pre_media_length = 0;

	var rtl_output = "";
	var content = css_input.replace(/\/\*[^*]*\*+([^/*][^*]*\*+)*\//ig , '');//remove comments


	var result;
	while ((result = selector_regex.exec(content)) !== null) {
	  //we optimistically suppose that no '}' character is inside CSS values such as content:"}" or url('}.jpg')

	  var index = result.index;
	  if(media_wrap && index > lastCloseBracket) {
		//we have reached end of a @media or @keyframe
		if(rtl_output.length > pre_media_length)
			rtl_output = rtl_output.substr(0, pre_media_length) + "\n" + trim(media_selector) + " {\n" + rtl_output.substr(pre_media_length) + "}\n";

		media_selector = '';
		media_wrap = false;
	  }

	  var end = content.indexOf('}', selector_regex.lastIndex);
	  lastCloseBracket = content.indexOf('}', end + 1);

	  var selector = result[1];
	  if(selector.match(/@media/i) || selector.match(/@(?:-(?:moz|webkit|ms|o|khtml)-)?keyframes/i)) {
		media_selector = selector;
		media_wrap = true;

		pre_media_length = rtl_output.length;
	  } else {
			var selector_parts = selector.split(/\,+/);
			var valid_parts = [];
			for(var c = 0 ; c < selector_parts.length; c++) {
				//ignore selectors that have "-left or -right", because they are already directional
				if(selector_parts[c].match(/(?:(?:[_\-\.])(?:left|right))|(?:\.arrowed)|(?:\.input-icon)|(\.rtl)|(\-rtl)|(\.dropdown-menu[^\:])|(\.scroll-)|(\.ace-switch)|(\.lbl)|(\.popover)|(\.table)|(\.ui-slider)|(\.nav-tabs)|(blockquote)|(\.datepicker)|(\.daterangepicker)|(\.dropdown-menu-right)|(\.footer-)|(\.chosen-)|(\.aside)/i)) {
					continue;
				}
				var sel = trim(selector_parts[c]);
				if(sel.match(/(?:\.no\-skin)|(?:\.skin\-\d)/)) valid_parts.push('.rtl'+sel);//.rtl.skin-1
				else valid_parts.push('.rtl '+sel);//.rtl .selector
			}
			if(valid_parts.length == 0) continue;

			var override_rules = {};

			var rules_text = content.substring(selector_regex.lastIndex , end);
			var rule_list = rules_text.match(/(?:([\w\*\-%]+)\s*\:\s*([^\;\}]+))/ig);

			if(rule_list && rule_list.length > 0) {
				for(var r = 0; r < rule_list.length; r++) {
					var $rules = rule_list[r].match(/(?:([\w\*\-%]+)\s*\:\s*([^\;\}]+))/i);

					var rule = $rules[1].toLowerCase();
					var value = $rules[2];

					if(rule.match(/(left|right)/i)) {
						override_rules[rule] = {type: 1, value: value};

					} else if(rule.match(/^(margin|padding|border-color|border-width|border-style|border-radius)$/i)) {
						override_rules[rule] = {type: 2, value: value};
					} else if(rule.match(/(box-shadow)/) && !value.match(/none/)) {
						if(value.match(/^\s*(\d)/)) {
							value = value.replace(/^\s*(\d)/ , '-$1');
							override_rules[rule] = {type: 0, value: value};
						}
						else if(value.match(/^\s*(\-)(\d)/)) {
							value = value.replace(/^\s*(\-)(\d)/ , '$2');
							override_rules[rule] = {type: 0, value: value};
						}
					}


					if(value.match(/(right|left)/i)) {
						if(value.match(/right/i)) value = value.replace(/right/i, 'left');
						 else value = value.replace(/left/i, 'right');
						override_rules[rule] = {type: 0, value: value};
					}
					else if(value.match(/center/i) && rule.match(/text\-align/i)) {
						override_rules[rule] = {type: 0, value: value};
					}
					/**else if(value.match(/none/i) && rule.match(/float/i)) {
						override_rules[rule] = {type: 0, value: value};
					}*/
					else if(value.match(/(rtl|ltr)/i)) {
						if(value.match(/rtl/i)) value = value.replace(/rtl/i, 'ltr');
						 else value = value.replace(/ltr/i, 'rtl');
						override_rules[rule] = {type: 0, value: value};
					}
					else if(value.match(/translateX\(/i)) {
						if(value.match(/translateX\(\-(.*)\)/i))
							value = value.replace(/translateX\(\-(.*)\)/ig , 'translateX($1)');
						else value = value.replace(/translateX\(([^\-]*)\)/ig , 'translateX(-$1)');
						override_rules[rule] = {type: 0, value: value};
					}
					/*else if(value.match(/rotate\(/i)) {
						var val = value.replace(/rotate\(.*\)/i , function(text, found) {
							console.log(text);
						});
					}*/
				}


				for(var rule in override_rules) if(override_rules.hasOwnProperty(rule)) {
					var cur_rule = override_rules[rule];
					if(cur_rule.type == 1) {
						var flip_rule;
						if(rule.match(/right/i)) flip_rule = rule.replace(/right/i, 'left');
						else flip_rule = rule.replace(/left/i, 'right');
					
						if(!(flip_rule in override_rules)) {
							//add a nullify/defaultify value for the opposite side
							override_rules[flip_rule] = {type: 0, value: cur_rule.value};
							
							/**if(rule.match(/border-(left|right)$/i)) {
								console.log(rule);
								delete override_rules[rule];
								rule += '-width';//change border-left to border-left-width
								if(!(rule in override_rules)) override_rules[rule] = {}
								override_rules[rule].value = defaultify[rule] + (cur_rule.value.match(/!important/) ? ' !important' : '');
								override_rules[rule].type = 0;
							}
							else*/ {
								override_rules[rule].value = defaultify[rule] + (cur_rule.value.match(/!important/) ? ' !important' : '');
								override_rules[rule].type = 0;
							}
						} else {
							//flip_rule is already a rule itself
							
							//swap left & right values unless they are equal, in that case we ignore it!
							var tmp1 = override_rules[rule].value;
							var tmp2 = override_rules[flip_rule].value;
							if(tmp1 === tmp2) {
								delete override_rules[rule];
								delete override_rules[flip_rule];
								continue;
							}
							var important = tmp2.match(/!important/) || tmp1.match(/!important/);
							override_rules[rule].value = tmp2 + (important && !tmp2.match(/!important/) ? ' !important' : '');
							override_rules[flip_rule].value = tmp1 + (important && !tmp1.match(/!important/) ? ' !important' : '');
							
							override_rules[flip_rule].type = override_rules[rule].type = 0;
						}
					} else if(cur_rule.type == 2) {
						var new_value = splitted_values(rule, cur_rule.value);
						if(new_value != null) {
							override_rules[rule].value = new_value + (cur_rule.value.match(/!important/) ? ' !important' : '');
							override_rules[rule].type = 0;
						}
					}
				}


				var new_rules_text = '';
				for(var rule in override_rules) if(override_rules.hasOwnProperty(rule)) {
					var new_rule = override_rules[rule];
					if(new_rule.type == 0) {
						new_rules_text += "\t"+ rule+": "+ new_rule.value +";\n";
					}
				}

				if(new_rules_text.length > 0) {
					rtl_output += trim(valid_parts.join(','))+" {\n";
					rtl_output += new_rules_text;
					rtl_output += "}\n";
				}
			}
	  }
	}

	//if there's a remaining open media query, close it
	if(media_wrap)  {
		if(rtl_output.length > pre_media_length)
			rtl_output = rtl_output.substr(0, pre_media_length) + "\n" + trim(media_selector) + " {\n" + rtl_output.substr(pre_media_length) + "}\n";
	}


	return rtl_output;

}