nlsclientscript

NLSClientScript extends the CClientScript class to be able to smart load javascript and css resources
43 followers

NLSClientScript prevents duplicated linking of js and css files when updating a view by ajax, when eg. paging or sorting a gridview, ajax-submitting a form or any custom ajax-updating a part of a view.

The issue what this extenson fights is for example when you render Jui widgets by CHtml::ajax, the js files used by the widget will be loaded as many times as you render such a widget in a view. The unnecessary bandwidth usage is the smaller problem, the bigger problem is eg. loading jquery.js again may reset some js objects set by previously loaded ui-related js files. That can cause js errors and the view may stop working.

Using NLSClientScript helps to avoid it all.

History

  • 5.0 (see the updated Usage)

    • in 4.0RC found an issue couldn't worked around: it registered also the script tags being in html/css/js comments, input field, textarea value so i had to drop the native source analysis by regexp. Fortunately found the solution in 5.0 looks like the most perfect till now. Tested successfully in IE7+, latest FF,Chrome,Opera. Reports about testing are welcome as always.
  • 4.0 RC

    • refactored, hopefully all bugs reported about 3.x have been eliminated
  • 3.6

    • fixed a typo
  • 3.5 (see the updated Usage)

    • handling special case when updating a table by tr tag
    • further IE fixes
    • added 2 new parameters: ignoredPattern and processedPattern
    • general refactoring
  • 3.4

    • fixed non-script-rendering bug in IE
  • 3.3

    • removes the occasional ...?_=3767454656434 -like timestamps from the url keys used to store/identify the loaded scripts
    • fixed the accidental naming NLSClientScript to EClientScript
  • 3.2

    • fixed accessing HEAD element for IE
    • compressed js code (full source is still there in the php source)
  • 3.1

    • the extension now prevents the duplicated loading of css files also.
  • 3.0

    • brand new approach simplifying dramatically the extension and the usage of the extension, based the great idea of Eirik Hoem
    • see the Usage below!
  • 2.1

    • dirty fix for a rendering bug of jquery.js v1.6.1 affecting binline=true mode in Yii 1.18
  • 2.0

    • brand new approach: resource hash stored at the server side, in the webuser state. All these info deleted when a non-ajax request comes
    • no $.ajax usage - better performance
    • the extension does not require jquery.js and jquery.yii.js to be linked initially any more
    • js/css files can be linked from other domain
    • new parameter: bInlineJs - if true, the scriptFile method will insert the js file content into the html instead of linking the file what can result even better performance
  • 1.3

    • added cache:true to the ajax js load
    • compressed core js code
  • 1.2

    • fixed js error when app not in YII_DEBUG mode
  • 1.1
    • hash key generated on server side
    • two hash key mode: PATH and CONTENT
    • shortened client-side code
  • 1.0
    • base version

If you interest the details, see the comments in the source.

Requirements

Yii 1.x

Limitations

  • This extension identifies the scripts by its paths so it does not prevent to load the same script content from different paths. So eg. if you published the same js file into different asset directories, NLSClientScript considers those to be different and won't prevent to load those several instances.

Usage (v5.0) - updated

1 . Simply set the class for the clientScript component in /protected/config/main.php, like

...
'components'=>array(
  ...
  'clientScript' => array(
    'class' => 'your.path.to.NLSClientScript',
    'excludePattern' => '/\.tpl/i', //js regexp, files with matching paths won't be filtered is set to other than 'null'
    //'includePattern' => '/\.php/' //js regexp, only files with matching paths will be filtered if set to other than 'null'
  )
  ...
)

Warning: in 5.0 the two optional parameters have been renamed!

2 . use Yii::app()->getClientScript() by the standard way to link js and css files/snippets

Example:

$cs = Yii::app()->getClientScript();
$systemJsPath = Yii::app()->getAssetManager()->publish( Yii::getPathOfAlias( 'system.web.js' ), false, -1, false );
$cs->registerScriptFile( $systemJsPath . '/ext/yii_ext.js');
$cs->registerScriptFile( $systemJsPath . '/ext/plugins/jquery.form.js');

3 . If you want to do a custom ajax request with "dataType"="json" and there are some fields of the response you want to update your page with, filter that part "by hand" with $.ajaxSettings.dataFilter like eg.:

echo CHtml::ajaxLink('custom update', array('/site/testupdate'), array(
 
  'dataType' => 'json',
 
  'success'=>'js:function(data){ $("#cont").html($.ajaxSettings.dataFilter(data.content)); }',
 
));

Resources

Total 20 comments

#8235 report it
le_top at 2012/05/20 04:41pm
5.0 not ok with 'dataFilter'

Hi

I just tried version 5.0 after previously using v3.6.

I use it as in the example: $("#cont").html($.ajaxSettings.dataFilter(data.content)); . Apparently this does not work anymore ad 'dataFilter' is unknown in Chrome (for example) when using 5.0. It appears that 'dataFilter' is defined only when using 'IE'.

For those that use NLSClientScript to create jQuery interface elements: I upgraded to 5.0 to see if this would fix another issue I have. When doing $("#cont").html(...) to create a jQuery dialog, I now conclude the 'div' is moved elsewhere by jQuery.dialog which is responsible in my case of creating multiple dialogs that eventually conflict with each other... That's not a NLSClientScript issue in the end ;-) and I'll have to fix this differently.

#8008 report it
marcovtwout at 2012/05/03 08:50am
Version 5

Using version 5 seems to work as expected in IE7/8 and Chrome. I upgraded after IE7/8-issues in version 3.3.

Only thing I would like to see is proper DocBlock commenting on variables and functions in your extension.

And actually, maybe a better name because "NLSClientScript" doesn't say anthing about what it does, but something like "AjaxCompatibleClientScript" or maybe just "EClientScript" or "XClientScript" is better.

#7821 report it
klaus66 at 2012/04/20 05:22am
Not really static page

You are right. My 'static' pages register a small js script. But how you said they don't need jquery.

I register now jquery for all my sites in my main layout view.

I use the google lib and I think it is no problem for performance.

#7812 report it
nlac at 2012/04/19 02:22pm
for klaus66

I guess that is not a static page where the issue occurs. On the pages where no script file registered, the component renders nothing. On your "static page" some script is registered requires no jquery, that's when the issue occurs.

So yes, for now all pages need jquery registered since the component uses it. I'll fix in the next release to register jquery automatically in every case.

#7743 report it
klaus66 at 2012/04/13 03:57pm
Script error jquery undefined

I have install version 5.0.

It works on all my pages where jquery is included.

But on my static pages (e.g. impressum page) where I need no jquery there I get the error 'jquery undefined'.

What can I do. Must I include on all my pages jquery or can I do this with exclude pattern ?

#7205 report it
nlac at 2012/03/03 04:08pm
guys please test 5.0

test results about usages of the latest version are welcome

#7167 report it
thimt8-yii at 2012/02/29 12:19pm
IE7 bug

IE7 has a problem with the "src" attribute of the script tag, as can be seen in http://bugs.jquery.com/ticket/11404

So the update of the grid returns the alert "Parser error".

The solution to the problem would be to change the script to:

(...)
selector : "script:not([src=\"\"]),link[rel=\"stylesheet\"]",
(...)
#7114 report it
vids at 2012/02/24 06:09am
Duplicate html content rendering

is #7098 fixed in 3.6 version? I am still facing the problem in filtering in the cgridview

#7098 report it
thimt8-yii at 2012/02/23 02:56pm
HTML content is being duplicated.

I have problems. When the extension is enabled the HTML content is being duplicated in ajax requests.

Detail: the problem does not occur in IE, only in Firefox and Chrome. And only occurs when the return of Ajax is the complete page, including the HTML tag.

The javascript code behaves completely different in IE vs Firefox and Chrome.

The developer needs to fix the default behavior on all browsers.

A workaround for Firefox and Chrome (but certainly not the ultimate solution):

(...)
firstTagRg : /<([a-z1-6]+)[^>]*>/i,
(...)
holders : {
    "default" : $(document.createElement("div")),
    "tr" : $(document.createElement("tbody")),
    "html" : $(document.createElement("html"))
},
#6919 report it
nlac at 2012/02/13 02:06pm
thx

hoplayann thanks for the report, i'll check and will release a new version in a few days, there's also an other issue needs to be fixed.

#6851 report it
hoplayann at 2012/02/09 06:27am
Issue with the parser in Internet Explorer 7

Hello,

I am using version 3.6 of your extension in my Yii project.

It seems that the selection of script tags in the HTML source fails for some reason in Internet Explorer 7. The looping through the SCRIPT or LINK elements is the place where we have now added an extra test case, to be sure that there is at least a not empty SRC or HREF attribute defined. (see added code below)

//fetching scripts from the DOM
                    for(i=0,res=$(document).find($._nlsc.selector); i<res.length; i++) {
                        tmp = res[i];
 
                        // CODE ADDED:
                        // ie7 sometimes gets accidental script tags and selector returns undefined objects
                        if (tmp.src == "" && typeof(tmp.href) === "undefined") {
                            continue;
                        }
 
                        ind = tmp.src ? tmp.src : tmp.href;
                        ind = ind.replace($._nlsc.urlGarbageRg,"");
                        if (tmp.href && tmp.parentNode!=$._nlsc.head)
                            $._nlsc.head.appendChild(tmp);
                        $._nlsc.resMap[ind] = 1;
                    }

And I had to add the code again in the other loop :

// iterate over new scripts and remove if source is already in DOM:
                for(i=0,res=holder.find($._nlsc.selector); i<res.length; i++) {
                    tmp = res[i];
 
                    // CODE ADDED:
                    // ie7 sometimes gets accidental script tags and selector returns undefined objects
                    if (tmp.src == "" && typeof(tmp.href) === "undefined") {
                        continue;
                    }
 
                    ind = tmp.src ? tmp.src : tmp.href;
                    ind = ind.replace($._nlsc.urlGarbageRg,"");
                    if (resMap[ind])
                        $(tmp).remove();
                    else {
                        if (tmp.href)
                            $._nlsc.head.appendChild(tmp);
                        else
                            tmp.removeAttribute("defer");
                        resMap[ind] = 1;
                    }
                }

Is it possible that the jquery selector is not precise enough ?

selector : "script[src],link[rel=\"stylesheet\"]",

Please let me know if there would be a fix for that issue in your next revision.

Yann

#6357 report it
nlac at 2012/01/02 03:26pm
thx thimt8-yii

fixed in 3.6

#6355 report it
thimt8-yii at 2012/01/02 02:49pm
Warning: "head" is not defined.

Here is returning an error warning that "head" is not defined.

This piece of code:

// iterate over new scripts and remove if source is already in DOM:
for(i=0,res=holder.find($._nlsc.selector); i<res.length; i++) {
    tmp = res[i];
    ind = tmp.src ? tmp.src : tmp.href;
    ind = ind.replace($._nlsc.urlGarbageRg,"");
    if (resMap[ind])
        $(tmp).remove();
    else {
        if (tmp.href)
            head.appendChild(tmp);
        else
            tmp.removeAttribute("defer");
        resMap[ind] = 1;
    }
}

should be?

// iterate over new scripts and remove if source is already in DOM:
for(i=0,res=holder.find($._nlsc.selector); i<res.length; i++) {
    tmp = res[i];
    ind = tmp.src ? tmp.src : tmp.href;
    ind = ind.replace($._nlsc.urlGarbageRg,"");
    if (resMap[ind])
        $(tmp).remove();
    else {
        if (tmp.href)
            $._nlsc.head.appendChild(tmp);
        else
            tmp.removeAttribute("defer");
        resMap[ind] = 1;
    }
}
#6349 report it
stereochrome at 2012/01/02 03:56am
works!

yep, 3.5 works like expected. thanks for your work and a happy new year!

#6312 report it
nlac at 2011/12/28 04:20am
for stereochrome

check 3.5 and also the demo on nlacsoft.net (it does exactly your case:))

#6166 report it
stereochrome at 2011/12/16 04:53am
problem persists

Hi,

unfortunately this new version does not fix the problem. I'm encountering this problem in chrome 15 on kubuntu and in other browsers. I've got a lot of work to do at the moment because it is the last workday for this year (yay), but i'm going to create a testcase for this problem in the new year.

#6099 report it
nlac at 2011/12/12 05:33pm
for stereochrome

Hi, i tested the bug, actually i experienced such issue only in IE (not exactly the same issue but can be related) and added a fix in 3.4. i'm curious if it has been fixed at your case

#5917 report it
nlac at 2011/11/24 11:41am
to stereochrome

Thanks, excellent catch and solution. I'll include it in the next release, also have to fight other cases (dt,dd,li,caption,thead etc)

#5916 report it
nlac at 2011/11/24 11:36am
to marcovtwout

Hi, thanks.

"NLS" points to "NLacSoft" what is my domain name actually. (i'll release some valuable content there in a few days!:))

The "EClientScript" is a kinda standard extension name format, i use my extension with that name at the company i work for.

#5914 report it
stereochrome at 2011/11/24 10:44am
table row problem

hi, i've switched from 2.1 to 3.3 today and found the following bug:

in a form which is organized in a table, the user can add new rows via ajax. the server is sending only a new table row (tr-element) with some columns in it.

the javascript-filter takes this row and puts it via innerHTML into a div-element which kills all tr- and td-elements from the original code. it then filters the dom and returns the broken code.

quick-fix:

insert above line 74:

data = jQuery.trim(data);
        if(data.slice(0,3) == "<tr") {
          $._holder = $(document.createElement("tbody"));
        } else {
          $._holder = $(document.createElement("div"));
        }
        $._holder[0].innerHTML = data; //<<< original line

Leave a comment

Please to leave your comment.

Create extension