Yii 1.1: nlsclientscript

NLSClientScript extends the CClientScript class smart loading javascript files and optionally merging/minifying js+css files
61 followers

Project moved to github as 7.0beta

Motivated by earlier suggestions, a lot of improvements made on the library.

  • What's new compared to 6.x?
    • huge refactor
    • one-file code splitted to parts
    • completely new css processing part: processing @import-s, url-s
    • composer support
    • demo app
    • moved to github

Visit https://github.com/nlac


NLSClientScript prevents duplicated linking of javascript 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 extension does not prevent the multiple loading of CSS files. I simply couldn't find a way to manage it clearly (too long to explain here).

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.

From 6.0, it optionally merges/caches + minifies the registered js and css files.

History

  • 6.7
    • fixed toAbsUrl() and optimized init() methods (reported + fix by le_top)
  • 6.6
    • fixed regexp in normUrl (reported by le_top)
  • 6.5
    • fixed buggy behavior when more xhr "script"-dataType requests started for the same script
    • eliminated deprecated jQuery.browser reference
  • 6.4
    • followed the change of the registerScriptFile() arguments in yii 1.1.14
    • removed/added some comments
  • 6.3

    • serious bug fixed: filtering duplications (usually) failed when js-merging applied for the response of an xhr request
    • new params: mergeIfXhr, mergeJsExcludePattern, mergeJsIncludePattern, mergeCssExcludePattern, mergeCssIncludePattern, resMap2Request (see the phpdoc comments in the source for more info)
    • appended an extra ; to the js files
    • some other small improvements
  • 6.21

    • fixed another bug: merged files have been re-generated on every request when the appVersion parameter was used
  • 6.2 (see the updated Usage)

    • fixed a serious bug broke the original functionality when merging happened (duplicates couldn't been recognized)
    • added a new parameter appVersion
  • 6.1 (see the updated Usage)

    • fixed several bugs (serverBaseUrl composing, merging css files by media correctly)
    • added parameters mergeAbove, curlTimeOut, curlConnectionTimeOut
  • 6.0 (see the updated Usage)

    • added optional merge and minify functionalities
    • to keep the simpleness of the single-file extension, embedded JSMin.php from https://github.com/eriknyk/jsmin-php
  • 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

  • The 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.

  • The extension doesn't watch wether a js/css file has been changed. If you set the merge functionality and some file changed, you need to delete the cached merged file manually, otherwise you'll get the old merged one.

Usage (v6.3+)

1 . 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'
 
    'mergeJs' => true, //def:true
    'compressMergedJs' => false, //def:false
 
    'mergeCss' => true, //def:true
    'compressMergedCss' => false, //def:false
 
    'mergeJsExcludePattern' => '/edit_area/', //won't merge js files with matching names
 
    'mergeIfXhr' => true, //def:false, if true->attempts to merge the js files even if the request was xhr (if all other merging conditions are satisfied)
 
    'serverBaseUrl' => 'http://localhost', //can be optionally set here
    'mergeAbove' => 1, //def:1, only "more than this value" files will be merged,
    'curlTimeOut' => 10, //def:10, see curl_setopt() doc
    'curlConnectionTimeOut' => 10, //def:10, see curl_setopt() doc
 
    'appVersion'=>1.0 //if set, it will be appended to the urls of the merged scripts/css
  )
  ...
)
 
For more information about the parameters, see the header comment of NLSClientScript.php.

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 . DOES NOT WORK FROM v5.0: 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

#18614 report it
samarhaider at 2014/12/02 07:01am
Very Cool

Very Good extension, Its solve my issue as well as it boost my application speed

#17951 report it
le_top at 2014/08/15 03:56am
CSS urls

Hi I suggest that you mention the limitation for the CSS at least in the notes above, and also in the comments of the source code (e.g., as a comment to the variable enabling the merge).

Also, while the comments say that Js and CSS merging are disabled by default, they are actually active by default in the extension. I recommend to keep buggy/limited options inactive - so CSS merging should really be false because relative Urls are essential (to the framework CSS for starters). If things do not work "immediately", many developers try and abandon the extension.

I understand the limitation of time completely - you'll likely add proper Css merging when you are in need of it yourself, which is fully understandable. That's why I generally fix bugs myself, the difficulty is often to get them into the official release.

#17949 report it
nlac at 2014/08/15 02:14am
@le_top

Yep, i'm aware that url issue in the merged css, that was a topic in some earlier commments (btw not sure why only the top 20 comments are shown, near 90 comments are here). It's a plan to allow configuring a 3rd pary service to do the proper merging (or handle it properly without 3rd party, still not sure how much work it is). Anyway it requires effort, i can't say will be done in some weeks, but it is in my list (as other things...:( ).

#17926 report it
le_top at 2014/08/12 01:32am
CSS merging and relative resources

Hi Now that merging is working, I'v done a few tests. Css merging has an important issue: relative resources are not rebased.

Example:

.grid-view table.items th {
    color: white;
    background: url("bg.gif") repeat-x scroll left top white;
    text-align: center
}

After merging, the navigators looks in the assets folder for 'bg.gif', but it is not there.

Other issues with combining CSS files can be expected, and it might be a good idea to rely on an existing open source to do the job.

#17925 report it
nlac at 2014/08/11 07:47pm
@moa, @le_top

@moa: when the error comes, when the page is loaded or at the first partial update? I guess the second case, i suspect the fancybox plugin has broken due to loading again a js file (maybe fancybox.pack or other). Please check with Firebug what js file is loaded twice - remember if you refer a js once xxx.js?v=2.14, later xxx.js or xxx.js?v=2.15, NLSClientScript will consider those files to be different and won't prevent to load again. Please normalize the url's, to be the same for the same js content.

@le_top: nice, i'll test it and add to the next version, thx.

#17924 report it
le_top at 2014/08/11 04:54pm
Fix for compression bug

Hi I found the solution for the compression bug. Just add the following line to the initCurlHandler method:

curl_setopt($this->ch, CURLOPT_ENCODING, "");
#17922 report it
msoa at 2014/08/11 01:31pm
problem in mergeJs to false

Thanks for your work I have this code in widget:

public function init()
    {
        $assetsPath = Yii::getPathOfAlias('application.widgets.gallery.assets');
        $assetsUrl = Yii::app()->assetManager->publish($assetsPath);
        Yii::app()->clientScript->registerCssFile($assetsUrl.'/gallery.css');
        Yii::app()->clientScript->registerScriptFile($assetsUrl.'/gallery.js');
        // fancybox
        Yii::app()->clientScript->registerCssFile($assetsUrl.'/fancybox/source/jquery.fancybox.css?v=2.1.5');
        Yii::app()->clientScript->registerScriptFile($assetsUrl.'/fancybox/lib/jquery.mousewheel-3.0.6.pack.js');
        Yii::app()->clientScript->registerScriptFile($assetsUrl.'/fancybox/source/jquery.fancybox.pack.js?v=2.1.5');
    }

But when i set mergeJs to false:

'mergeJs' => false, //def:true

I get this error:

TypeError: $(...).fancybox is not a function
$(".fancybox").fancybox();

How i can resolve it?

#17912 report it
le_top at 2014/08/10 04:32pm
Compression issue

Well, the absUrl fix is integrated ;-).

The compression issue is that the resulting file contains gzip compressed content, and is not related to content minimized (or compacted) by JsMin.

#17911 report it
nlac at 2014/08/10 04:20pm
@le_top

Ok, somehow i skipped the toAbsUrl issue last time, focused only the second comment.. the fixes seem to be fine, have been added now. About the compression issue, the compression is done by the embedded JSMin.php, that project is unfortunately abandoned, no further buxfixes provided. Possible minification issues should be work-arounded, eg. deal with originally minified files and just concat them by NLSClientScript.

#17908 report it
le_top at 2014/08/09 06:57pm
Fix(es)

Ok for dataFilter - I suppose that is why my JSON calls do not seem to give any trouble. Some of the logic is done behind the scenes so I thought it did not apply.

I also reported about the 'toAbsUrl' method which is equally important. Otherwise combining(merging) scripts/css is not functional for 'localhost/workspace/index.php' or '//blabla.google.com/blabla'. A resource like 'themes/mytheme/js/script.js' would not work without one of the fixes for toAbsUrl.

Anyway, I also had some kind of compression issue where the combined file had compressed content - but only on the production server, so I suppose it has to do with compression from the content served by the production server and not with content served from third parties.

#17907 report it
nlac at 2014/08/09 04:36pm
@le_top

Thanks for the bug hunt, i added the fix.

About the old trick you pointed ($.ajaxSettings.dataFilter(data)), there's no need for that at all. From 5.0, the extension is rewritten in a totally different way, so you can work with any json ajax response without any neccessary post-processing.

#17904 report it
le_top at 2014/08/08 06:23pm
Duplicate library load fixed...

The core mystery has been solved: my url had a trailing '&' after removing the '='. That is because I added a timestamp to the assets which postfixes the urls with '?' which becomes '?&='.

So the normUrl function had to be updated with:

return url.replace(/\?*\&?(_=\d+)?$/g,"");

Finally, any plan to provide an alternative to

$.ajaxSettings.dataFilter(data)

?

#17902 report it
le_top at 2014/08/08 04:56pm
Issues - some solutions

I was still using 5.0, but for some reason my jQuery gets loaded two times when fetching a CJuiDialog through Ajax. So I was digging into that issue and decided to upgrade to the latest version to see if things were fixed.

Actually things are worse. My page layout and javascript were completely "scrambled". First, the function to combine files is on by default and some "urls" were not found. Second, my production server serves compressed files and they remain compressed after combining - they should be combined uncompressed!

The missing urls issue could be fixed by fixing the way the absUrl is computed. The following code works for me:

protected function toAbsUrl($relUrl) {
        if(substr($relUrl,0,2)==='//')
            return (Yii::app()->getRequest()->getIsSecureConnection()?'https:':'http:').$relUrl;
        else
            return preg_match('&^(http(s?):)?//&',$relUrl) ? $relUrl : rtrim($this->serverBaseUrl,'/') . '/' . (substr($relUrl,0,1)==='/'?'':Yii::app()->getRequest()->getBaseUrl().'/').ltrim($relUrl,'/');
    }

Changes are that if the url starts with '//' that only the scheme is prefixed. Also, when the url starts with '/', the path is absolute from the host, but when the '/' is missing, the url is relative, so 'baseUrl' has to be added, which is also done above.

I prefer using Yii methods if they exist, so in 'init()', I changed a few lines:

if (!$this->serverBaseUrl) {
            $this->serverBaseUrl=Yii::app()->getRequest()->getHostInfo();

So that works on my dev machine, but not on the server where compressed content is combined. I haven't checked that issue yet. I have now disabled combining again.

I still have the dual jQuery loading, so I continue to look into that core issue for me.

#17862 report it
nlac at 2014/07/31 05:55pm
moving to github

good point, it would have several advantages. i registered it on my todo list:)

#17851 report it
marcovtwout at 2014/07/31 04:19am
GitHub

Would you consider moving the source code to GitHub and adding Composer support? :)

#17161 report it
nlac at 2014/05/08 11:28am
@yanhui

I didn't experienced yet op. system incompatibility regarding nlsclientscript. I contacted you for details through your forum profile.

#17125 report it
yanhui at 2014/05/05 11:49pm
@nlac Maybe compatibility problem work with Linux?

Your reply is appreciated, thanks for your attention, nlac.
nlsclientscript works fine in my develop pc (win8.1/xampp1.8.3),
today I found it doesn't works properly in my server environment (centos6.4/lampp1.8.3).
The error messages in browser like follow:
-Uncaught ReferenceError: $ is not defined nls1858337492.js:79
-Uncaught ReferenceError: jQuery is not defined index.php:28
After some googling, I found someone may meets the same problem.
-http://stackoverflow.com/questions/21679474/nlsclientscript-errors
-https://github.com/yiisoft/yii/issues/604

#17121 report it
nlac at 2014/05/05 01:38pm
@yanhui + @fad

yes, i'm aware of that problem, there is no url transformation in the content implemented when merging css. Since many people miss that feature I'll try to work out a solution soon. Till that time, pls do not use the css merging.

#17108 report it
yanhui at 2014/05/04 11:26am
Problem about "mergeCss" option

Thanks for your great job, nlsc!
You save my time. While I just stranded by the duplicated "jquery-ui.min.js".
It works fine with JS. There is a small problem about "mergeCss" option.
If set "mergeCss" to TRUE, some jquery-ui assets files may got a wrong uri (404 in browser).
For example:
VALID: http://localhost/app/assets/4a2e203a/jui/css/base/images/ui-bg_flat_75_ffffff_40x100.png
INVALID: http://localhost/app/assets/images/ui-bg_flat_75_ffffff_40x100.png

#17061 report it
nlac at 2014/04/29 01:11pm
@fad

first, there is no "compress" option in nlsclientscript, only mergeCss or compressMergedCss. I guess the problem caused by using the mergeCss. Just don't use the option in that specific case with mapping.

Leave a comment

Please to leave your comment.

Create extension