String replacement exercise (CSS formats)

C

Csaba Gabor

Here's a cute little exercise: I needed to convert between
two css formats,
-moz-box-sizing => MozBoxSizing
borderTopWidth => border-Top-Width

Can you write the conversion in either direction compactly?

Csaba Gabor from Vienna

Note: One form is in all lowercase, with the distinct words
preceded by hyphens, while in the other form, there is no
separation between the distinct words, and all the words
following the first one are capitalized.
 
L

Lasse Reichstein Nielsen

Csaba Gabor said:
Here's a cute little exercise: I needed to convert between
two css formats,
-moz-box-sizing => MozBoxSizing
borderTopWidth => border-Top-Width

Can you write the conversion in either direction compactly?

function cssToStyle(cssPropertyName) {
return cssPropertyName.replace(/-[a-z]/g, function(m) {
return m.charAt(1).toUpperCase();
// or:
// return String.fromCharCode(m.charCodeAt(1) & 0xdf);
});
}

function styleToCSS(stylePropertyName) {
return stylePropertyName.replace(/[A-Z]/g, function(m) {
return "-" + m.toLowerCase();
// or:
// return String.fromCharCode(45, m.charCodeAt(0) | 0x20);
});
}
Note: One form is in all lowercase, with the distinct words
preceded by hyphens, while in the other form, there is no
separation between the distinct words, and all the words
following the first one are capitalized.

Actually, in the first example, the first "word" is capitalized too.
This helps. It means that the conversion is between
"upper case letter" and "dash followed by lower case letter"
But then, the second example has upper case letters on the right-hand
side, which I guess is just a typo.


Note that there is one CSS property that cannot can be converted
directly to a DOM style property using this conversion. The css
property "float" (e.g., "float:right") has as DOM style property
"cssFloat" (because "float" is a reserved word in ECMAScript).

/L
 
A

Asen Bozhilov

 function styleToCSS(stylePropertyName) {
   return stylePropertyName.replace(/[A-Z]/g, function(m) {
     return "-" + m.toLowerCase();
     // or:
     // return String.fromCharCode(45, m.charCodeAt(0) | 0x20);
   });
 }

Why you use reference to function for second argumenmt here? In my
Firefox 3.5.3 that is very slow. Please anybody explain that case.
Thanks.

function styleToCSS1(prop)
{
return prop.replace(/[A-Z]/g, '-$&').toLowerCase();
}

For 10000 times execution.

styleToCSS('borderTopWidth') // 350ms
styleToCSS1('borderTopWidth') // 25ms

Regards.
 
V

VK

Csaba said:
Here's a cute little exercise:  I needed to convert between
two css formats,
-moz-box-sizing => MozBoxSizing
borderTopWidth => border-Top-Width

Can you write the conversion in either direction compactly?

Two moments to account, if it is a universal solution and not a fully
controlled own environment:

1) As Lasse Reichstein Nielsen pointed out, CSS float comes to JS
styleFloat (IE) / cssFloat (others). This alone eliminates any really
"cute" regexp one-liners.

2) We also nave to be ready for the most fantastic case choices in
stylesheets like
display: INLINE-BLOCK;
Padding: 10EM;
background-color: Red;
etc.
All this is supported but it will make the script useless unless it
normalizes the cases.

All togeter it may come to something like (useful suggestions are
welcome):

/*@cc_on
/*@if (@_jscript)
var attFloat = 'styleFloat';
@else @*/
var attFloat = 'cssFloat';
/*@end
@*/

var reScriptStyle = new RegExp('(?:)([a-z])','g');

var reSheetStyle = new RegExp('([A-Z])','g');

function getScriptStyle(att) {
var att = att.toLowerCase();
if (att == 'float') {
return attFloat;
}
else {
return att.replace(reScriptStyle, function() {
return arguments[1].toUpperCase();
});
}
}

function getSheetStyle(att) {
if (att == attFloat) {
return 'float';
}
else {
return att.replace(reSheetStyle, function() {
return '-' + arguments[1].toLowerCase();
});
}
}
 
V

VK

function getSheetStyle(att) {
 if (att == attFloat) {
  return 'float';
 }
 else {
  return att.replace(reSheetStyle, function() {
   return '-' + arguments[1].toLowerCase();
  });
 }

}

As Asen Bozhilov suggested, getSheetStyle can be brought to a more
compact and (I guess) more effective variant:

function getSheetStyle(att) {
if (att == attFloat) {
return 'float';
}
else {
return (att.replace(reSheetStyle, '-$&')).toLowerCase();
}
}
 
T

Thomas 'PointedEars' Lahn

VK said:
/*@cc_on
/*@if (@_jscript)
var attFloat = 'styleFloat';
@else @*/
var attFloat = 'cssFloat';
/*@end
@*/

Again you have failed to understand that the scripting language has
nothing to do with the capabilities of the DOM implementation.


PointedEars
 
L

Lasse Reichstein Nielsen

Asen Bozhilov said:
Why you use reference to function for second argumenmt here? In my
Firefox 3.5.3 that is very slow. Please anybody explain that case.
Thanks.

function styleToCSS1(prop)
{
return prop.replace(/[A-Z]/g, '-$&').toLowerCase();
}

For 10000 times execution.

styleToCSS('borderTopWidth') // 350ms
styleToCSS1('borderTopWidth') // 25ms

That's a serious difference. I admit that I didn't think the
difference would be that great.

For comparison, other browsers are different too, but not all by that
much:
Opera 10: 258 vs 270
IE 8 (64-bit): 93 vs 42
Google Chrome 4: 191 vs 118 (using 100000 rounds)
Safari 4: 152 vs 115 (using 100000 rounds)

Still, if it's something to be used in a time critical part of the
code, the string-based replacement is definitly better.

/L
 
V

VK

Again you have failed to understand that the scripting language has
nothing to do with the capabilities of the DOM implementation.

Well, we need "styleFloat" were we need it and "cssFloat" were we need
it depending on the environment where the script is running. I hope
you have no objection to that on.
Also obviously we are not making a new internal test on each and every
getScriptStyle or getSheetStyle function call as it would be hugely
ineffective. I hope you have no objection to that on.

So we need to preset attFloat once in the most short, resource
effective and reliable way. Your proposals?
 
C

Csaba Gabor

 function styleToCSS(stylePropertyName) {
   return stylePropertyName.replace(/[A-Z]/g, function(m) {
     return "-" + m.toLowerCase();
     // or:
     // return String.fromCharCode(45, m.charCodeAt(0) | 0x20);
   });
 }

Why you use reference to function for second argumenmt here? In my
Firefox 3.5.3 that is very slow. Please anybody explain that case.
Thanks.

function styleToCSS1(prop)
{
    return prop.replace(/[A-Z]/g, '-$&').toLowerCase();

}

For 10000 times execution.

styleToCSS('borderTopWidth') // 350ms
styleToCSS1('borderTopWidth') // 25ms

Thanks guys. This was the point of the exercise: back
references and anonymous functions in a string's replace
method.

Lasse, the right side of the second example was indeed
a typo - sorry about that.

For the one form, I should have written all the distinct
words are separated by hyphens. As for the first example,
it could also be viewed that the first word is the empty
string (which is what you'd get if you split the string
on the hyphens).
 
T

Thomas 'PointedEars' Lahn

VK said:
Well, we need "styleFloat" were we need it and "cssFloat" were we need
it depending on the environment where the script is running. I hope
you have no objection to that on.

No, but the problem is that you are not testing the relevant environment.
You are trying to determine a language implementation by the features it
supports, which is unrelated to the DOM property to be used.
Also obviously we are not making a new internal test on each and every
getScriptStyle or getSheetStyle function call as it would be hugely
ineffective.

It would not be ineffective. It would probably be less _efficient_ than
other approaches, but it would no doubt be safer. Where and when I have to
choose between efficiency and safety, I choose safety, because efficiency
can vary greatly depending on the runtime environment, and a safer approach
makes maintenance a lot easier (less expensive) in all runtime environments.
I hope you have no objection to that on.

I have, see above.
So we need to preset attFloat once in the most short, resource
effective and reliable way.

Non sequitur. The most reliable way is not to infer anything.
Your proposals?

var jsx_object = jsx.object;
var v = jsx_object.getProperty(o, "cssFloat",
jsx_object.getProperty(o, "styleFloat", ""));
if (v)
{
// ...
}

whereas jsx.object.getProperty() would here be a method that determines with
o.hasOwnProperty() or, if unavailable, a `typeof' test, if the object
referred to by `o' has a property named "cssFloat"; if yes, return its
value, else return the value of the third argument, which here is either the
value of the `styleFloat' property of `o' or the empty string.

Of course, one could test the `float' implementation of only one object, and
infer from there to increase runtime efficiency. But since that would still
be inference, and thus not be safe enough, I would recommend against that.


PointedEarsa
 
C

Csaba Gabor

Asen Bozhilov said:
Why you use reference to function for second argumenmt here? In my
Firefox 3.5.3 that is very slow. Please anybody explain that case.
Thanks.
function styleToCSS1(prop)
{
    return prop.replace(/[A-Z]/g, '-$&').toLowerCase();
}

But then again, why use back references at all?

function styleToCSS2(prop) {
return prop.replace(/(?:(?=[A-Z]))/g, '-')
.toLowerCase(); }

That's a serious difference. I admit that I didn't think the
difference would be that great.

For comparison, other browsers are different too, but not all by that
much:
 Opera 10: 258 vs 270
 IE 8 (64-bit): 93 vs 42
 Google Chrome 4: 191 vs 118 (using 100000 rounds)
 Safari 4: 152 vs 115 (using 100000 rounds)

Testing CSS1 against CSS2 yielded inconclusive
results on my horribly aging and memory poor
Win XP Pro with FF1.5 and IE6. Ballpark results
for 100000 rounds with FF showed about 3 seconds
for FF, with advantage to CSS2, but IE came in
at shy of 2 seconds with a slight advantage to
CSS1. It may be that loading / garbage
collection / cacheing are significant factor in
my tests.
 
C

Csaba Gabor

Asen Bozhilov said:
Why you use reference to function for second argumenmt here? In my
Firefox 3.5.3 that is very slow. Please anybody explain that case.
Thanks.
function styleToCSS1(prop)
{
    return prop.replace(/[A-Z]/g, '-$&').toLowerCase();
}

But then again, why use back references at all?

function styleToCSS2(prop) {
  return prop.replace(/(?:(?=[A-Z]))/g, '-')
             .toLowerCase(); }

Sorry, but there is an artifact in my prior
post that bears some explanation. The outer group
of parens (?:...) indicates that the pattern inside
should be non capturing. However, forward lookahead,
indicated by the inner group of parens (?=...) is
already noncapturing.

Nevertheless, on my system, the above consistently
came in (slightly) faster than the version with the
outer parens omitted. This is probably most likely
a reflection of my test results being highly
suspect (ie. various effects not directly related
to the testing skewing the test), but that is why
I included the above version. It should more
properly be:

function styleToCSS2(prop) {
return prop.replace(/(?=[A-Z])/g, '-')
.toLowerCase(); }
 
J

Jorge

Here's a cute little exercise:  I needed to convert between
two css formats,
-moz-box-sizing => MozBoxSizing
borderTopWidth => border-Top-Width

Can you write the conversion in either direction compactly?

Csaba Gabor from Vienna

Note: One form is in all lowercase, with the distinct words
preceded by hyphens, while in the other form, there is no
separation between the distinct words, and all the words
following the first one are capitalized.

In Safari:

var es= document.body.style;

es['margin-top']= "234px";
alert(es.marginTop);
//alerts "234px"
 
G

Garrett Smith

[VK code]
[...]
It would not be ineffective. It would probably be less _efficient_ than
other approaches, but it would no doubt be safer. Where and when I have to
choose between efficiency and safety, I choose safety, because efficiency
can vary greatly depending on the runtime environment, and a safer approach
makes maintenance a lot easier (less expensive) in all runtime environments.
TO misleading others? Sure, I object. To having others explain the same
things you seem to not understand, again? Yep, that, too. And I can see
Thomas, for whatever reason, decided to go and explain what has been
explained to you countless times on this NG archives.

So, yeah, a little objection to that.
I have, see above.


Non sequitur. The most reliable way is not to infer anything.


var jsx_object = jsx.object;
var v = jsx_object.getProperty(o, "cssFloat",
jsx_object.getProperty(o, "styleFloat", ""));
if (v)
{
// ...
}

whereas jsx.object.getProperty() would here be a method that determines with
o.hasOwnProperty() or, if unavailable, a `typeof' test, if the object
referred to by `o' has a property named "cssFloat"; if yes, return its
value, else return the value of the third argument, which here is either the
value of the `styleFloat' property of `o' or the empty string.

Why use hasOwnProperty? Are you anticipating css properties like
"watch"? Or what?
Of course, one could test the `float' implementation of only one object, and
infer from there to increase runtime efficiency.
Yeah.

But since that would still
be inference, and thus not be safe enough, I would recommend against that.
Can you name a situation where an HTMLElement has a style object, but it
is unsafe or buggy in some way? For example, where given:

var floatProp = "cssFloat";
if(!(floatProp in document.documentElement.style)) {
floatProp = "styleFloat";
}
el.style[floatProp] == "undefined";

In the implementation, there is a cssFloat or styleFloat property on
either el.style OR on documentElement.style (but not both).
is true.

Adding runtime checks to method calls makes the methods more cluttered.

I don't see any sacrificing of safety. Maybe you can help fix that with
an example, mentioning a browser + version.
 
V

VK

Garrett said:
I don't see any sacrificing of safety. Maybe you can help fix that with
an example, mentioning a browser + version.

If you decided to play by the old c.l.j. rules then please follow them
consistently ;-) For instance your question has no sense within these
rules. First one makes a narrow set of properties and methods that
cannot fail. The set is made based on the current group consensus of
"selected regulars" based purely on abstract theoretical
considerations. Any other property or method is faillable without
relevance to any existing / ever existed environment and thus has to
be checked using unfaillable methods and properties from the above
mentioned set. You are free to follow any scripting approach but you
should not try to play football and soccer at once ;-)
 
T

Thomas 'PointedEars' Lahn

Garrett said:
var floatProp = "cssFloat";
if(!(floatProp in document.documentElement.style)) {

`in' and host objects -- when will you ever learn?

(Unless there is an easily readable version of your followup, I am going to
ignore the rest.)


PointedEars
 
T

Thomas 'PointedEars' Lahn

Csaba said:
Lasse said:
Asen Bozhilov said:
function styleToCSS1(prop)
{
return prop.replace(/[A-Z]/g, '-$&').toLowerCase();
}

But then again, why use back references at all?

function styleToCSS2(prop) {
return prop.replace(/(?:(?=[A-Z]))/g, '-')
.toLowerCase(); }

A rather dubious "optimization" that makes the whole thing less compatible.


PointedEars
 
G

Garrett Smith

Thomas said:
`in' and host objects -- when will you ever learn?

The in operator is not always reliable.

More consistently reliable *in general* is negation of typeof obj ==
"undefined".

if(typeof document.documentElement.style[floatProp] === "undefined") {
// ...
}

For style properties, I've only know one where - prop in style - returns
true and - typeof style[prop] == "undefined" - (and only in one browser,
and it is not something I would probably ever care about).
(Unless there is an easily readable version of your followup, I am going to
ignore the rest.)

You're dodging the question. Can you name a situation where an
HTMLElement has a style object, but it is unsafe or buggy in some way?

Please provide an example, mentioning a browser + version.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top