Tuesday, September 30, 2014

Telerik Reporting: set subreport datasource the same as main report

Telerik had some posts regarding this, however the post is not clear enough so I have to spend some time to figure out myself.

Here are the steps I have made the subreport datasource dynamically configured (Telerik Reporting Q1 2014) successfully:

Step 1. Define a static method in your factory class:
public static class YourFactoryClass{
        public static Telerik.Reporting.SqlDataSource SetConnectionString(Telerik.Reporting.SqlDataSource dataSource)
        {
 
            var dataDBConnectionString = _get_DB_connect_str();
            dataSource.ConnectionString = dataDBConnectionString;
            return dataSource;
        }
 }

Step 2. Expose your data with custom User Functions, you have to use the AssemblyReferences Element of the Telerik.Reporting configuration section to reference your custom assembly:
in your web.config or app.config:
<configuration>
      <configsections>
          <section allowdefinition="Everywhere" allowlocation="true" name="Telerik.Reporting" type="Telerik.Reporting.Configuration.ReportingConfigurationSection, Telerik.Reporting">
          </section>

      </configsections>
      …
       <telerik .reporting="">
          <assemblyreferences>
              <add culture="neutral" name="YourCustomAssembly_Name" publickeytoken="null" version="1.0.0.0"/>              
          </assemblyreferences>        
       </Telerik.Reporting>
</configuration>


Step 3. Add DataSource binding in your subreport ( not the main report) data items property. In Report Designer, right click anywhere other than those fields defined, find 'Bindings' inside Properties panel then launch the 'Edit Bindings' dialogue box,  and 'DataSource' should be in the Property path dropdown so just select it from dropdown:

Property path: DataSource
Expression: =XXXNameSpace.YourFactoryClass.SetConnectionString(ReportItem.ItemDefinition.DataSource)

Step 4. Rebuild your project/solution, have it a try. It should hit the static method defined ( to dynamically update the connection string for subreport ) in the step 1 if you run in debugging mode.

That is it.

Sunday, September 28, 2014

JavaScript: what result this will render

var data = 'mytest';
function showSomething() {
    console.log(data);    
    var data = 'newData';
    console.log(data);    
}

showSomething();
How about this:
var data = "Rafael Nadal";
(function () {    
    console.log("the guy was " + data);
    var data = "Roger Federer";

    console.log("the guy is " + data);
})();
JavaScript turns our function declaration into a function expression and hoists it to the top. JavaScript applies different rules when it comes to function hoisting depending on whether you have a function expression or a function declaration. A function declaration is fully hoisted while a function expression follows the same rules as variable hoisting.

JavaScript treats variables which will be declared later on in a function differently than variables that are not declared at all. Basically, the JavaScript interpreter "looks ahead" to find all the variable declarations and "hoists" them to the top of the function.

JavaScript: function method bind()

From 'JavaScript- The Definitive Guide' but with some of my own meat
this is a JavaScript keyword, not a variable. Unlike variables  [variables declared within a function are visible throughout the function (including within nested functions) but do not exist outside of the function.] , the this keyword does not have a scope, and nested functions do not inherit the this value of their caller. If a nested function is invoked as a method, its this value is the object it was invoked on. If a nested function is invoked as a function then its this value will be either the global object (non-strict mode) or undefined (strict mode). It is a common mistake to assume that a nested function invoked as a function can use this to obtain the invocation context of the outer function. If you want to access the this value of the outer function, you need to store that value(this) into a variable that is in scope for the inner function. It is common to use the variable self for this purpose.

For example:
var o = { // An object o.
          m: function() { // Method m of the object.
                   var self = this; // Save the this value in a variable.
                   console.log(this === o); // Prints "true": this is the object o.
                   f(); // Now call the helper function f().

                   function f() { // A nested function f
                        console.log(this === o); // "false": this is global or undefined
                        console.log(self === o); // "true": self is the outer this value.
                   }
          }
};
o.m(); // Invoke the method m on the object o.
Console:
true 
false 
true 
 // A step further
 var o = { // An object o.
          m: function() { // Method m of the object.

                   //every function invocation has a this value, and a
                   //closure cannot access the this value of its outer function 
                   //unless the outer function has saved that value into a variable
                   var self = this; // Save the this value in a variable.
                   console.log(this === o); // Prints "true": this is the object o.
                   f.bind(this)(); // Bind f to o using 'this' then call f().

                   function f() { // A nested function f
                        console.log(this === o); // "true": this is now bound to o
                        console.log(self === o); // "true": self is the outer this value.
                   }
           }
};
o.m(); // Invoke the method m on the object o
Console:
true 
true 
true  
            // Credit to Richard Of Stanley
            // This data variable is a global variable
            var data = [
                {member:"R. Nadal", age:28},
                {member:"R. Federer", age:33}
            ]

            var user = {
                // local data variable
                data    :[
                    {member:"L. James", age:28},
                    {member:"M. Jordan", age:53}
                ],
                viewData:function (event) {
                    var randomNum = ((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1

                    console.log (this.data[randomNum].member + " " + this.data[randomNum].age);
                }

            }

            // Assign the viewData method of the user object to a variable
            var viewDataVar = user.viewData;

            viewDataDataVar(); // R. Nadal 28  (from the global data array, not from the local data array)
Console:
R. Nadal 28 
When we execute the viewDataVar () function, the values printed to the console are from the global data array, not the data array in the user object. This happens because viewDataVar () is executed as a global function and use of this inside viewDataVar () is bound to the global scope, which is the window object in browsers.
            // A step further
            // This data variable is a global variable
            var data = [
                {member:"R. Nadal", age:28},
                {member:"R. Federer", age:33}
            ]

            var user = {
                // local data variable
                data    :[
                    {member:"L. James", age:28},
                    {member:"M. Jordan", age:53}
                ],
                viewData:function (event) {
                    var randomNum = ((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1

                    console.log (this.data[randomNum].member + " " + this.data[randomNum].age);
                }
              
            }
             //Invoke the method viewData on the object user
             user.viewData(); //from the local data array 
            
             var viewDataDataVar = user.viewData;  
             viewDataDataVar();//from the global data array
Console:
L. James 28
R. Nadal 28 
When we execute the user.viewData() , the values printed to the console are from the data array in the user object. This happens because user.viewData() is executed as a method of user(Yes, a method of user object, I am right about this) and use of this inside user.viewData() is bound to the user object.
            // This data variable is a global variable
            var data = [
                {member:"R. Nadal", age:28},
                {member:"R. Federer", age:33}
            ]

            var user = {
                // local data variable
                data    :[
                    {member:"L. James", age:28},
                    {member:"M. Jordan", age:53}
                ],
                viewData:function (event) {
                    var randomNum = ((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1

                    console.log (this.data[randomNum].member + " " + this.data[randomNum].age);
                }

            }

            // Bind the viewData method to the user object
            var viewDataDataVar = user.viewData.bind(user);
            //Now the we get the value from the user object because the this keyword is bound to the user object
            viewDataDataVar();
Console:
L. James 28  

var myObj = {
    _Function: function () {},
    __Function: function () {},

    getAsyncData: function (cb) {
        cb();
    },

    render: function () {
        var self = this;
        this.getAsyncData(function () {
            this._Function();//ERROR
            self.__Function();//OK
        });
    }
};

myObj.render();
Console:
Uncaught TypeError: Object [object global] has no method '_Function' 

We need to keep the context of the myObj object referenced for when the callback function is called. Calling self._Function() enables us to maintain that context and correctly execute our function.
var myObj = {
    _Function: function () { 
      console.log('_Function');
    },
    __Function: function () {
      console.log('__Function');
    },

    getAsyncData: function (cb) {
        cb();
    },

    render: function () {
        var self = this;
        this.getAsyncData(function () {
            self._Function();//No more Error
            self.__Function();
        });
    }
};

myObj.render();
However, this could be neatened somewhat by using Function.prototype.bind(). Let’s rewrite our example:
var myObj = {
    _Function: function () { 
      console.log('_Function');
    },
    __Function: function () {
      console.log('__Function');
    },

    getAsyncData: function (cb) {
        cb();
    },

    render: function () {
      this.getAsyncData(function () {
         this._Function();
         this.__Function();
    }.bind(this));

  }
};

myObj.render();
Console:
_Function
__Function

Saturday, September 27, 2014

JavaScript !! - bang, bang

The !! construct is a simple way of turning any JavaScript expression into its Boolean equivalent.

legendary Cast-to-bool 'operator',  "bang, bang you're boolean"

Coerces Object to boolean. If it was falsey (e.g. 0, nullundefined, etc.), it will be false, otherwise, true.

!oObject  //Inverted boolean
!!oObject //!! converts the value to the right of it to its equivalent boolean value

true === !!10
false === !!0
So !! is not an operator, it's just the ! operator twice. It is the double-use of ! -- which is the logical "not" operator.
The "falsey" values are:
  • false
  • NaN
  • undefined
  • null
  • "" (empty string)
  • 0

Friday, September 26, 2014

JavaScript Array.prototype:[]

Array.prototype can be shortcut as '[]'
var s= 'caatJavaScript';
var arrayStr_ = Array.prototype.slice.call(s,1);
console.log(arrayStr_);
["a", "a", "t", "J", "a", "v", "a", "S", "c", "r", "I", "p", "t"]  

var arrayStr__ = [].slice.apply(s,[]);//the same as [].slice.apply(s);
console.log(arrayStr__.join(''));
caatJavaScript 

var arrayStr__ = [].slice.call(s,0);//the same as [].slice.call(s);
console.log(arrayStr__.join(''));
caatJavaScript

var arrayStr__ = [].slice.call(s,1);
console.log(arrayStr__);
["a", "a", "t", "J", "a", "v", "a", "S", "c", "r", "i", "p", "t"]
console.log(arrayStr__.join(''));
aatJavaScript 

console.log(arrayStr__.pop());
t 

var arrayStr__ = [].slice.call(s,s.length-1);
console.log(arrayStr__);
["t"] 

Wednesday, September 24, 2014

AngularJS: evaluating when the angular loading done

Here is the code
    DCTApp.directive('caatInitialized',
    ['$rootScope',
        function($rootScope) {
            return {
                restrict: 'A',
                link: function($scope) {
                    var to;
                    var listener = $scope.$watch(function() {
                        clearTimeout(to);
                        to = setTimeout(function () {
                            console.log('detail Initialized');
                            listener();
                            $rootScope.$broadcast('detailInitialized');
                        }, 50);
                    });
                }
            };
        }]);

Listening (I am calculating the height of the Kendo UI Grid Content here)
        $scope.$on('detailInitialized', function () {
            if (typeof _navParam !== 'undefined' && _navParam !== '') {
                $('.sidebar').show();                
            }
            $('#MemberGrid .k-grid-content').height($('.contentDetail').height() - $('#MemberGrid .k-grid-pager').outerHeight() - $('#MemberGrid .k-grid-header').outerHeight());
            
        });

Then add as an attribute to the element monitoring.
And a note from original post:
It works by assuming that the application is initialised when there are no more $digest cycles. $watch is called every digest cycle and so a timer is started (setTimeout not $timeout so a new digest cycle is not triggered). If a digest cycle does not occur within the timeout then the application is assumed to have initialised.
It is obviously not as accurate as satchmoruns solution (as it is possible a digest cycle takes longer than the timeout) but my solution doesn't need you to keep track of the modules which makes it that much easier to manage (particularly for larger projects). Anyway, seems to be accurate enough for my requirements. Hope it helps.

HTML5 History API: update URL location

push state to update the URL (with Kendo UI Grid state change):
        $('#MemberGrid').on('click', " tbody > tr", function () {//delegate("tbody > tr", "click", function(){//prior to jQuery 1.7
            var $grid = $("#MemberGrid").data("kendoGrid");
            var data = $grid.dataItem(this);
            var url = location.href;
            var _privateData = $("#XXX").val();
            url = url.replace(_privateData, data.privateData);
            $("#XXX").val(data.privateData);
            $rootScope.$broadcast('detailNav-started');
            //save state as well
            var stateObj = { id: data.privateData };
            window.history.pushState(stateObj, null, url);
        });

And PopState to setup a callback to listen when the “back” or 'forth' button is pressed,(with Kendo UI Grid state change):
window.onpopstate = function(e){
            var url = location.href;
            var privateData_temp = $("#XXX").val();
            var currentState = e.state;
            $("#XXX").val(currentState.id);
            $rootScope.$broadcast('detailNav-started');
            var $grid = $("#MemberGrid").data("kendoGrid");
            $grid.table.find("tr td:last-child").filter(function () { return $.text([this]) == privateData_temp; }).closest('tr').removeClass("k-state-selected");
            $grid.table.find("tr td:last-child").filter(function () { return $.text([this]) == currentState.id; }).closest('tr').addClass("k-state-selected");
        }

Kendo UI Grid: manipulate the loaded grid rows

How To detect when the grid DOM is ready after a refresh? Or, alternatively, is there a way to detect as each TR element is created.

Actually, it can be done in dataBound as below:
(The code is trying to find from the td in the last column then filter it)
            dataBound: function () {
                var grid = this;
                grid.table.find("tr td:last-child").filter(function () { return $.text([this]) == $("#XXX").val(); }).closest('tr').addClass("k-state-selected");
                //save to history state
                var stateObj = { id: $("#XXX").val() };
                window.history.pushState(stateObj, null, null);
                return;
            }
            }

Kendo UI Grid: Send Custom request parameter or replace filter parameter

One day I need to pass the Kendo UI grid filter to server ( The idea is to re-use the filter pre-configured from the original grid for the new grid ). However KendoUI  API does not provide the way for you to override the filter request as obvious as other parameters such as pagesize, schema etc.

There are 2 ways to override the filter for KendoUI grid:

1. add filter param into transport.read.data as below
    $('#MemberGrid').on('dblclick', " tbody > tr", function () {
        var $grid = $("#MemberGrid").data("kendoGrid");
        var data = $grid.dataItem(this);        
        var datasourceFromGrid = $grid.dataSource;        
        $theForm = $("#detailswithNav");

        //..append a hidden input that holds passing val
        $("<input type='hidden' />")
        .attr("name", "myDetailReqParam")
        .val(JSON.stringify(new DetailParamModel(datasourceFromGrid._filter,datasourceFromGrid._page,datasourceFromGrid._pageSize,datasourceFromGrid._sort,datasourceFromGrid._take,datasourceFromGrid._skip,datasourceFromGrid.transport.options.read, datasourceFromGrid.options.schema, data.privateData)))
        .appendTo($theForm);

        $theForm.attr("action", $theForm.attr("action") + "/" + data.privateData).submit();           
        });       
        
        
        });

        function DetailParamModel(_filter,_page,_pageSize,_sort,_take,_skip,_read,_schema,_dataRow){
            var detailParam = this;
            detailParam.page = _page;
            detailParam.pageSize = _pageSize;
            detailParam.sort = _sort;
            detailParam.take = _take;
            detailParam.skip = _skip;
            detailParam.read = _read;
            if( typeof _filter !== 'undefined' )
                detailParam.read.data.filter = _filter;
            detailParam.schema = _schema;
            detailParam.privateData = _dataRow;
        
        }

2. add filter param into options as below:
transport: {
                    type: "json",
                    read: _navParam.read,
                    parameterMap: function (options, type) {
                        if (type === "read") {

                            //Added to options for custom request 
                            options.filter = _navParam.filter;

                            return kendo.stringify(options);
                        }
                    }
                }

Tuesday, September 23, 2014

Uncaught SyntaxError: Unexpected token; ASP.NET MVC razor in javascript

To be sure wrap ASP.NET MVC razor data in quote, otherwise you might get Uncaught SyntaxError: Unexpected token;

The code I had:

(function () {
        try{
            var __navParam = '@navParam';
            if (typeof __navParam === 'undefined' || __navParam === '') _navParam = '';
            else //use Json.parse to convert string to Json
                _navParam = JSON.parse(@Html.Raw(navParam));

            if (typeof _navParam === 'undefined' || _navParam === '') { 
            $('.sidebar').hide();
            $('#detailContent').removeClass('contentDetail');            
        }
        }catch(e) {
            $('.sidebar').hide();
            $('#detailContent').removeClass('contentDetail');       
        }
    })();

I also added this in the razor code for json serialization with json formatted data from ViewData:
    var jss = new System.Web.Script.Serialization.JavaScriptSerializer();
    var strNav = Convert.ToString(ViewData["NavParam"]);
    var navParam = string.IsNullOrEmpty(strNav)? string.Empty : jss.Serialize(strNav);

Tuesday, September 16, 2014

PHP Drupal: Blank pages or "white screen of death" (WSOD)

Change the shutdown handler And it works to show error details!

One way to force errors to display is to change the shutdown handler. Add the following code (originally from http://stackoverflow.com/questions/1475297/phps-white-screen-of-death), to the front of index.php:
<?php
// -------------------------------------------------------
// - Display Errors
// -------------------------------------------------------
ini_set('display_errors', 'On');
ini_set('html_errors', 0);
// -------------------------------------------------------
// - Error Reporting
// -------------------------------------------------------
error_reporting(-1);
// -------------------------------------------------------
// - Shutdown Handler
// -------------------------------------------------------
function ShutdownHandler()
{
    if(@is_array($error = @error_get_last()))
    {
        return(@call_user_func_array('ErrorHandler', $error));
    };
    return(TRUE);
};
register_shutdown_function('ShutdownHandler');
// -------------------------------------------------------
// - Error Handler
// -------------------------------------------------------
function ErrorHandler($type, $message, $file, $line)
{
    $_ERRORS = Array(
        0x0001 => 'E_ERROR',
        0x0002 => 'E_WARNING',
        0x0004 => 'E_PARSE',
        0x0008 => 'E_NOTICE',
        0x0010 => 'E_CORE_ERROR',
        0x0020 => 'E_CORE_WARNING',
        0x0040 => 'E_COMPILE_ERROR',
        0x0080 => 'E_COMPILE_WARNING',
        0x0100 => 'E_USER_ERROR',
        0x0200 => 'E_USER_WARNING',
        0x0400 => 'E_USER_NOTICE',
        0x0800 => 'E_STRICT',
        0x1000 => 'E_RECOVERABLE_ERROR',
        0x2000 => 'E_DEPRECATED',
        0x4000 => 'E_USER_DEPRECATED'
    );
    if(!@is_string($name = @array_search($type, @array_flip($_ERRORS))))
    {
        $name = 'E_UNKNOWN';
    };
    return(print(@sprintf("%s Error in file \xBB%s\xAB at line %d: %s\n", $name, @basename($file), $line, $message)));
};
$old_error_handler = set_error_handler("ErrorHandler");
?>

Drupal 7 on Ubuntu 14.04 [32 bit] : can't install modules with wsod

https://www.drupal.org/node/2281195

I've installed a fresh Drupal 7.28 over a clean Ubuntu 14.04. The environment is virtualbox 4.3 with 4Gb ram an 8Gb disk (hosted by win7). PHP version is 5.5.9-1ubuntu4 and db is mysql. The installed site, empty, works normally.
issue

Trying to install any module via URL leads to WSOD or downloading the module package manually the install button does nothing. After the white page i can go back and continue to navigate the site with no errors or warnings.

Fatal Error
Call to undefined function gzopen()
File: .../drupal-7.31/modules/system/system.tar.inc:716
706:           }
707:
708:           // ----- File to open if the local copy
709:           $v_filename = $this->_temp_tarname;
710:
711:         } else
712:           // ----- File to open if the normal Tar file
713:           $v_filename = $this->_tarname;
714:
715:         if ($this->_compress_type == 'gz')
716:             $this->_file = @gzopen($v_filename, "rb");
717:         else if ($this->_compress_type == 'bz2')
718:             $this->_file = @bzopen($v_filename, "r");
719:         else if ($this->_compress_type == 'none')
720:             $this->_file = @fopen($v_filename, "rb");

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
only tar.gz does not work, zip works fine for me
jabecz commented

I am facing the same problem. I use drupal on other Ubuntu 14.04 servers, but all of them are 64-bit. This server which is going to WSOD on tar.gz module installation is the only one running Ubuntu 14.04 32-bit. So I think the problem is only on 32-bit Ubuntu 14.04 server edition.

I am not able to troubleshoot this problem so I reinstall the server to Ubuntu 14.04 64-bit and see if it helps.

Have You resolved the issue?

Thanks


^^^^^^^^^^^^^^^^^^THIS WORKED FOR ME,THANKS^^^^^^^^^^^^^^^^^^
Zlib missing gzopen is PHP5 bug on 32bit systems
malc_b commented

I have the same issue. This seems to be known bug, that was fixed and has now come back. It only affects 32bit systems. In php5, with big files option, in zlib there is no gzopen only gzopen64. The latest (1.3.12) pear package archive_tar includes a test that checks if gzopen doesn't exist and gzopen64 does, and then it defines its open gzopen to call gzopen64. That fix is close but flawed too, and it doesn't help with Drupal (7, at least) which doesn't call the pear package but has its own (older) copy in core module system.tar.inc.

The fix is to edit system.tar.inc (/var/www/drupal/modules/system/system.tar.inc for my box) to add, after

define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
define ('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));

these lines to system.tar.inc
if (!function_exists('gzopen') && function_exists('gzopen64')) {
    function gzopen($filename, $mode, $use_include_path = 0)
    {
        return(gzopen64($filename, $mode, $use_include_path));
    }
}
if (!function_exists('gztell') && function_exists('gztell64')) {
    function gztell($zp)
    {
        return(gztell64($zp));
    }
}
if (!function_exists('gzseek') && function_exists('gzseek64')) {
    function gzseek($zp, $offset, $whence = SEEK_SET)
    {
        return(gzseek64($zp, $offset, $whence));
    }
}

Or I guess to some other module if you wish.

Wednesday, September 10, 2014

HTML5: output tag

From w3school.com:
Attributes
= New in HTML5.
AttributeValueDescription
forelement_idSpecifies the relationship between the result of the calculation, and the elements used in the calculation
formform_idSpecifies one or more forms the output element belongs to
namenameSpecifies a name for the output element

Sample Code ( number HTML5 input type and  range HTML5 input type ):
<form oninput="amount.value = (hours.valueAsNumber * rate.valueAsNumber) + ((hours.valueAsNumber * rate.valueAsNumber) * vat.valueAsNumber)" onsubmit="return false">
<legend>Invoice</legend>
<label for="hours">Number of hours:</label>
 <input id="hours" min="0" name="hours" type="range" value="5" />100
<label for="rate">Rate:</label>
 <input id="rate" min="0" name="rate" type="number" value="1" />($'s)
<label for="vat">VAT:</label>
 <input id="vat" min="0" name="vat" type="number" value="0.20" />
Total: <strong>$<output for="hours rate vat" name="amount"></output>
</form>
Invoice
100
($'s)

Total: $

HTML5: datalist tag - an "autocomplete" feature for input field

From w3school.com:
The <datalist> tag specifies a list of pre-defined options for an <input> element.
The <datalist> tag is used to provide an "autocomplete" feature on <input> elements. Users will see a drop-down list of pre-defined options as they input data.
Use the <input> element's list attribute to bind it together with a <datalist> element
The <datagrid> tag should be avoided in any HTML code because it has been dropped from the HTML specification (or at least deferred)


<input list="browsers" name="browser">
<datalist id="browsers">
  <option value="Internet Explorer">
  <option value="Firefox">
  <option value="Chrome">
  <option value="Opera">
  <option value="Safari">
</datalist>

Sunday, September 7, 2014

JavaScript: RegExp’s exec() function and String’s match() function

Code(credit to georg) :
re_once = /([a-z])([A-Z])/
re_global = /([a-z])([A-Z])/g

st = "aAbBcC"

console.log("match once=", st.match(re_once), "match global=", st.match(re_global))
console.log("exec once=", re_once.exec(st), "exec global=", re_global.exec(st))
console.log("exec once=", re_once.exec(st), "exec global=", re_global.exec(st))
console.log("exec once=", re_once.exec(st), "exec global=", re_global.exec(st))
Result:
match once= ["aA", "a", "A"] match global= ["aA", "bB", "cC"]

exec once= ["aA", "a", "A"] exec global= ["aA", "a", "A"]
exec once= ["aA", "a", "A"] exec global= ["bB", "b", "B"]
exec once= ["aA", "a", "A"] exec global= ["cC", "c", "C"]

Secrets of the JavaScript Ninja :
using a local regular expression (one without the global flag g) with the String object’s match() methods returns an array containing the entire matched string, along with any matched captures in the operation.

But when we supply a global regular expression (one with the g flag included),match() returns something rather different. It’s still an array of results, but in the case of a global regular expression, which matches all possibilities in the candidate string rather than just the first match,  the array returned contains the global matches; captures within each match aren’t returned in this case.

If captures are important to us, we can regain this functionality while still performing a global search by using the regular expression’s exec() method. This method can be repeatedly called against a regular expression, causing it to return the next matched set of information every time it’s called.

Here a simple example of exec() in action that Steven Levithan put...
I needed to created an array containing the indices of each tab character within a string, so I used something like the following:

var tabIndices = [];
var tabMatchInfo;
var tabRegex = /\t/g;

while (tabMatchInfo = tabRegex.exec(string)) {
     tabIndices.push(tabMatchInfo.index);
}

Nice and easy. To do this without exec, it would require something like the following:

var tabIndices = [];
var thisTabIndex;

for (var i = 0; i < string.length; i++) {
     thisTabIndex = string.indexOf(String.fromCharCode(9), i);
     if (thisTabIndex === -1) {
         break;
     } else {
                tabIndices.push(thisTabIndex);
                i = thisTabIndex;
              }
}