Success

User tests: Successful: Unsuccessful:

avatar ghost
ghost
2 Jul 2013

http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&tracker_item_id=31791

This PR introduces a completely new approach to the email cloaking plugin.

Issues with current plugin

The current core email cloaking plugin is pretty bad and has been for years.

One issue is that it uses document.write to place a javascript encoded email address. The document.write causes issues with other scripts (like inline modal scripts).

Another issue is that it also converts email addresses inside inline scripts, causing these to completely be messed up (scripts inside scripts).

These are both fixed with this new Email Cloaking plugin.

No more javascript in output

This plugin doesn't output a javascript to generate the html, but outputs a link with a series of span elements. The email address id distributed in parts over data-content attributes in these spans.
Then by using some short css and javascript the email address is displayed correctly and the mailto is added to the link.

The result is a readable email address in the browser, but a bunch of spans with unrecognisable attributes in the html.

Testing instructions

You can test (and compare to old functionality) if the email addresses are being output correctly (together with the correct mailto link). For instance, use this as example:

<p>Email in text: my@email.com</p>
<p>Email with mailto link: <a href="mailto:my@email.com">my@email.com</a></p>
<p>Email with mailto link with an id: <a href="mailto:my@email.com" id="myid">my@email.com</a></p>
<p>Email with mailto link to other email address than text: <a href="mailto:my@email.com">someoneelse@email.com</a></p>
<p>Email with mailto link and custom text <a href="mailto:my@email.com">My Email</a></p>

Also good to test what happens when there is an email address inside a javascript block. It should be left alone.
So you can try placing this code inside an article (make sure your editor allows placing scripts):

<script language="javascript" type="text/javascript">
    myemail = 'my@email.com';
    document.write('<p>Email address in script: ' + myemail + '</p>');
</script>
avatar nonumber nonumber - open - 2 Jul 2013
avatar piotr-cz
piotr-cz - comment - 3 Jul 2013

Hi there,
I've refactored the JHtml email too: JHtmlEmail Gist

It stills produces inline javascript, but XHTML mode doesn't use document.write.
When using inline code, there's no need for jQuery

The output still looks pretty nasty though :)

XHTML output

<noscript id="noscript33732" title="This email address is being protected from spambots. You need JavaScript enabled to view it.">
 xxxxxxxxxxxxxxxxxxxx
</noscript>
<script>
//<![CDATA[
(function(d, prefix, path, addy33732){
 addy33732 += 'stylh&#101;n' + '&#46;' + 'c&#111;m';
 var el, ref = d.getElementById('noscript33732'), helper = d.createElement('div');
 helper.innerHTML = prefix + addy33732;
 el = d.createElement('a');
 el[path] = helper.firstChild.nodeValue;
 el.innerHTML = addy33732;
 ref.parentNode.insertBefore(el, ref);
}(document, '&#109;a' + 'i&#108;' + '&#116;o:', 'hr' + 'ef', 'sh&#111;wr&#111;&#111;m' + '&#64;'));
//]]>
</script>

HTML output

<noscript title="This email address is being protected from spambots. You need JavaScript enabled to view it.">
 xxxxxxxxxxxxxxxxxxxx
</noscript>
<script>
(function(d, prefix, path, addy16044){
 addy16044 += 'stylh&#101;n' + '&#46;' + 'c&#111;m';
 d.write('<a ' + path + '=' + prefix + addy16044 + '>');
 var addy_text16044 = 'Email';
 d.write(addy_text16044 + '</a>');
}(document, '&#109;a' + 'i&#108;' + '&#116;o:', 'hr' + 'ef', 'sh&#111;wr&#111;&#111;m' + '&#64;'));
</script>
avatar nonumber
nonumber - comment - 5 Jul 2013

HTML output of this PR with this input:
Email in text: my@email.com
will look something like:

<p>
   Email in text:
   <!--- This email address is being protected from spambots. You need JavaScript enabled to view it. --->
   <span class="email_address">
      <span data-content-post="&#111;m" data-content-pre="&#109;&#121;">
         <span data-content-post=".c" data-content-pre="&#64;&#101;">
            <span data-content-post="&#105;&#108;" data-content-pre="&#109;a"></span>
         </span>
      </span>
   </span>
</p>
avatar nonumber
nonumber - comment - 4 Oct 2013

So is this PR ok? What's the status?

avatar beat
beat - comment - 4 Oct 2013

I like this new approach a lot.
document.write means a re-rendering for browsers, means flickering and slower (not snappy) rendering.

document.write is real aweful javascript and a shame for Joomla!

So +1 :+1: for this from me.

avatar Fedik
Fedik - comment - 4 Oct 2013

I liked idea about change the old email cloaking plugin.
but whether realy need such difficult front end part, with relation from jQuery?
can just:

<span id="random-id-3123123" data-pref="my-email" data-suff="example.com">
This email address is being protected from spambots. You need JavaScript enabled to view it.
</span>
<script type="text/javascript">
(function (d){
    var el = d.getElementById('random-id-3123123'),
        em = el.getAttribute('data-pref') + '@' + el.getAttribute('data-suff');
    el.innerHTML = '<a href="mailto:' + em + '">' + em + '</a>';
})(document);
</script>

no? :)

avatar nonumber
nonumber - comment - 4 Oct 2013

Because that is very easy for spambots to decode. Joomla is used by millions of websites. So as soon as that is used, spambots will adapt.
The 'difficult' part is what the plugin is for: cloaking.
It makes it a lot more difficult for spambots.
In your example a simple regular expression can grab the email:
data-pref="([^"]*)" data-suff="([^"]*)"
Email = $1@$2;

avatar nonumber
nonumber - comment - 4 Oct 2013

@beat: thanks. Could you test it too and report back in the tracker if all is good?

avatar Fedik
Fedik - comment - 4 Oct 2013

yes right :)
in same way spambot can parse all data-content-post and data-content-pre of course
but, you right, your solution more difficult for the spamboots

what about make this without jQuery and addittional css files?

<span id="random-id-3123123">
    This email address is being protected from spambots. You need JavaScript enabled to view it.
      <span data-content-post="&#111;m" data-content-pre="&#109;&#121;">
         <span data-content-post=".c" data-content-pre="&#64;&#101;">
            <span data-content-post="&#105;&#108;" data-content-pre="&#109;a"></span>
         </span>
      </span>
   </span>
<script type="text/javascript">
(function (d){
    var el = d.getElementById('random-id-3123123'),
        sp = el.getElementsByTagName('span'), p = '', s = '';
    for (var i = 0, l = sp.length; i < l; i++) {
        p += sp[i].getAttribute('data-content-pre');
        s = sp[i].getAttribute('data-content-post') + s;
    }
    el.innerHTML = '<a href="mailto:' + p + s + '">' + p + s + '</a>';
})(document);
</script>

p.s. I see diference betwen media/system/js/emailcloak.js and media/system/js/emailcloak-uncompressed.js, looks like mistake or ?

avatar nonumber
nonumber - comment - 4 Oct 2013

The idea behind this is to keep the code that decodes the cloaked email outside the html output.
Also, if you have 24 email addresses on a page, you don't want 24 javascript blocks in your html output. Inline javacript sucks.

avatar beat
beat - comment - 4 Oct 2013

There are ways to make it even harder, but changing it later will be even better, as it will force spambots to adapt.
The important thing is that it's not document.write anymore.
Indeed, jquery is not required this, but as it's joomla standard, i see no issues in using it.
I will test it and comment on tracker as soon as i have fulfilled my other 3.2-commitments.

avatar Fedik
Fedik - comment - 4 Oct 2013

Also, if you have 24 email addresses on a page

right, but if I have only 1-2 emails (that in most cases) I still should do 3 additional request (jQuery,emailcloak.js, emailcloak.css) that to big price in this case

when select between 3 additional request and "Inline javacript" I would select last.
it just IMHO ;)

avatar nonumber
nonumber - comment - 4 Oct 2013

I can rewrite the js to not be jQuery dependant.

avatar Fedik
Fedik - comment - 5 Oct 2013

@nonumber for me it would be great ;)

small question between, maybe for future, what your think about random attributes?

<span id="random-id-3123123" data-randome-text-for-pref="my-email" data-randome-text-for-suff="example.com">
This email address is being protected from spambots. You need JavaScript enabled to view it.
</span>
<script type="text/javascript">
(function (d){
    var el = d.getElementById('random-id-3123123'),
        em = el.getAttribute('data-randome-text-for-pref') + '@' + el.getAttribute('data-randome-text-for-suff');
    el.innerHTML = '<a href="mailto:' + em + '">' + em + '</a>';
})(document);
</script>

also can put the javascript in to the header (for avoid inline), something like:

// generated on first JHtml::_('email.cloak') call
var randomVariableName = {};
(function (d, $){
  $(d).ready(function(){
    for(var p in randomVariableName){
        var el = d.getElementById(p),
            em = el.getAttribute('data-' + randomVariableName[p][0]) + '@' + el.getAttribute('data-' + randomVariableName[p][1]);
        el.innerHTML = '<a href="mailto:' + em + '">' + em + '</a>';
   }
});
})(document, jQuery);
randomVariableName["random-element-id"] = ["randome-text-for-pref", 'randome-text-for-suff'];
//after each new call, for other emails
randomVariableName["other-random-element-id2"] = ["other-randome-text-for-pref2", 'other-randome-text-for-suff3'];
randomVariableName["other-random-element-id3"] = ["other-randome-text-for-pref2", 'other-randome-text-for-suff2'];

jQuery here just because I not know how to simple catch "domready"

just interesting your thoughts

avatar nonumber
nonumber - comment - 5 Oct 2013

"jQuery here just because I not know how to simple catch "domready"
That's why I wanted to use jQuery to begin with.
I am reqriting it to be plain js. Problem with the way Joomla adds scripts to the header is that it will add the script (and css if using same method) multiple times.

Looking into how to solve this...

avatar Fedik
Fedik - comment - 5 Oct 2013

ok, i understand :)

for check whether added, can try use a static variable in cloak function, or where you want add it :

static $script_added;
if(!$script_added)
{
  // code for script
  $script_added = true;
}

avatar nonumber
nonumber - comment - 5 Oct 2013

Yeah, I know that. But it just is a shame the same piece of code gets added multiple times.
Will either have to use external js that hold function, or use inline script without function.

avatar nonumber
nonumber - comment - 5 Oct 2013

Ok, rewrote the whole thing. Please check it out...

avatar Fedik
Fedik - comment - 5 Oct 2013

for me works good :+1:

avatar nonumber
nonumber - comment - 9 Oct 2013

FR has 2 successful tests and PR is in sync with master.
So good to go....

avatar nonumber
nonumber - comment - 9 Oct 2013

@piotr-cz Thanks. Done.

avatar nonumber
nonumber - comment - 1 Nov 2013

I'm done waiting, discussing and wasting my time on this. This has been lying around for 4 months now.

Even if it did get merged, it would still be an issue on Joomla 2.5. And I don't see this getting in there, let alone Joomla 3.

So I have retracted the code and will just release it as a NoNumber extension. Much more rewarding than trying to 'give back to Joomla'.

Add a Comment

Login with GitHub to post a comment