PDA

View Full Version : Element.js -- update bugs



vtswingkid
13 Nov 2006, 11:20 AM
The following bugs were found when testing with IE6, FIREFOX, OPERA, NETCSAPE
CSS doesn't dynamically load in ie6.
JS scripts would not load dynamically in all browsers.
Duplicates of CSS and JS in DOM.
Removal of whitespace broke CSS loads and JS loads in all browsers
i.e. <script>...</script><script>...</script>

I propose the following changes to update within element.js:


update:function(msg,loadScripts){
var cssFragment='(?:<style>)((\n|\r|.)*?)(?:</style>)';
var jsFragment='(?:<script>)((\n|\r|.)*?)(?:</script>)';
var docHead=document.getElementsByTagName("head")[0];
var html=msg.replace(new RegExp(cssFragment, 'img'),"");
if(html){
html=html.toString();
html=html.replace(new RegExp(jsFragment, 'img'),"");
if(html){
html=html.toString();
this.dom.innerHTML=html;
}
}
var css=msg.match(new RegExp(cssFragment, 'img'));
if(css){
css=css.toString();
var re = /(?:<style.*?id=[\"\'](.*?)[\"\'].*?>)([\S\s]*?)(?:</style>)|(?:<style>)([\S\s]*?)(?:</style>)/ig;
var match;
while(match = re.exec(css)){
if(match[2]||match[3]){
var s0 = document.createElement("style");
var t;
if(match[1]){
var e=document.getElementById(match[1]);
if(e)e.parentNode.removeChild(e);
s0.setAttribute("id", match[1]);
}
s0.setAttribute("type","text/css");
if(match[2])t=match[2];
if(match[3])t=match[3];
if(s0.styleSheet)s0.styleSheet.cssText=t;//IE
else s0.appendChild(document.createTextNode(t));//W3C
docHead.appendChild(s0);
}
}
}

if(!loadScripts)return this;
var js=msg.match(new RegExp(jsFragment, 'img'));
if(!js)return this;
js=js.toString();
var dom = this.dom;
var _parseScripts = function(){
var re = /(?:<script.*?src=[\"\'](.*?)[\"\'].*?>)[\S\s]*?(?:</script>)|(?:<script>)([\S\s]*?)(?:</script>)/ig;
var match;
while(match=re.exec(js)){
if(match[2])eval(match[2]);
else if(match[1]){
var s0=document.createElement("script");
s0.src=match[1];
docHead.appendChild(s0);
}
}
}
setTimeout(_parseScripts,10);
return this;
}[/code]

vtswingkid
13 Nov 2006, 7:54 PM
Thanks again for you hardwork on extensions, Jack.

If you or anyone has any improvements or sees any problems with this code please post your solutions. I am an unseasoned javascript user. So I imagine there are some inherent problems.
I also have not tested this in Safari, Konquer, older versions, etc... So I am very interested in such results.

By the way if this does not make it into the code what is the suggested method for overriding prototypes so as not to modify the ext code base directly?

Cheers!

Oh and please bare with my lousy coding as I come up to speed with the rest of you. :wink:

jack.slocum
13 Nov 2006, 8:20 PM
The preferred may to override functions without modifying the base code is to use prototyping. You just include it after the yui-ext.js file.

YAHOO.ext.Element.prototype.update = function(html, loadScripts){
.. insert your code..
};

jack.slocum
13 Nov 2006, 8:22 PM
BTW, I have a different solution I will be putting in. There's been so many issues with this particular functionality I've decided to address it myself.

kevinyao
15 Nov 2006, 6:23 PM
Jack,
I don't know how your new implementation is going to look, but for Safari, I had to make the following change to your function:


update : function(html, loadScripts){
this.dom.innerHTML = html;
if(!loadScripts) return this;

var dom = this.dom;
var _parseScripts = function(){
var s = dom.getElementsByTagName("script");
var docHead = document.getElementsByTagName("head")[0];

// For browsers which discard scripts when inserting innerHTML, extract the scripts using a RegExp
if(s.length == 0){
var re = /(?:<script.*(?:src=[\"\'](.*)[\"\']).*>.*</script>)|(?:<script>([\S\s]*?)</script>)/ig; // assumes HTML well formed \
and then loop through it.
var match;
while(match = re.exec(html)){
var s0 = document.createElement("script");
if (match[1])
s0.src = match[1];
else if (match[2])
s0.text = match[2];
else
continue;
docHead.appendChild(s0);
}
}else {
for(var i = 0; i < s.length; i++){
var s0 = document.createElement("script");
s0.type = s[i].type;
if (s[i].text) {
s0.text = s[i].text;
}

/**********************************
Added these lines for Safari
***********************************/
else if (s[i].innerHTML) {
s0.innerHTML = s[i].innerHTML;
}
/**********************************/

else {
s0.src = s[i].src;
}
docHead.appendChild(s0);
}
}
}
// set timeout to give DOM opportunity to catch up
setTimeout(_parseScripts, 10);
return this;
},

Animal
16 Nov 2006, 12:13 AM
The version I have says:



var s0 = document.createElement("script");
s0.type = s[i].type;
if (s[i].text) {
if (YAHOO.util.Dom.isSafari)
s0.innerHTML = s[i].text;
else
s0.text = s[i].text;
} else {
s0.src = s[i].src;
}
docHead.appendChild(s0);


so best make it:



var s0 = document.createElement("script");
s0.type = s[i].type;
if (s[i].text) {
if (YAHOO.util.Dom.isSafari)
s0.innerHTML = s[i].innerHTML;
else
s0.text = s[i].text;
} else {
s0.src = s[i].src;
}
docHead.appendChild(s0);

jack.slocum
17 Nov 2006, 4:56 AM
Actually this method can't be used at all. IE7 drops some, and allows others.

For example this:

el.update('<sc'+'ript type="text/javascript">alert("wtf1");</s'+'cript>kjhjkhkjhkjhkj lkhkjlhjkhkjhlkj <sc'+'ript language="javascript">alert("wtf2");</s'+'cript> <sc'+'ript src="test.js"></s'+'cript>', true);

test.js alerts "wtf 3"

You will see wtf 2 and then wtf 3. The first one is dropped.

If you load:
el.update('<sc'+'ript type="text/javascript">alert("wtf1");</s'+'cript><sc'+'ript src="test.js"></s'+'cript>', true);

Then IE will strip them both and you go to regex. It appears IE strips some and not others. Because of this, we are going to have to regex for them.

jack.slocum
17 Nov 2006, 5:03 AM
It appears some times the src of the old RE wasn't matching either, I changed it to this nasty regex:


var re = /(?:<script\s(?:(?:src=[\"\'](.*?)[\"\'])?|.*?).*?>(.*?)?<\/script>)/ig;