How to include multiple js files using jQuery $.getScript() method
Asked Answered
K

19

139

I am trying to dynamically include javascript files into my js file. I did some research about it and find jQuery $.getScript() method would be a desired way to go.

// jQuery
$.getScript('/path/to/imported/script.js', function()
{
    // script is now loaded and executed.
    // put your dependent JS here.
    // what if the JS code is dependent on multiple JS files? 
});

But I am wondering whether this method can load multiple scripts at one time? Why I am asking this is because sometimes my javascript file is depending on more than one js files.

Thank you in advance.

Kwakiutl answered 3/8, 2012 at 20:58 Comment(0)
G
338

The answer is

You can use promises with getScript() and wait until all the scripts are loaded, something like:

$.when(
    $.getScript( "/mypath/myscript1.js" ),
    $.getScript( "/mypath/myscript2.js" ),
    $.getScript( "/mypath/myscript3.js" ),
    $.Deferred(function( deferred ){
        $( deferred.resolve );
    })
).done(function(){
    
    //place your code here, the scripts are all loaded
    
});

FIDDLE

ANOTHER FIDDLE

In the above code, adding a Deferred and resolving it inside $() is like placing any other function inside a jQuery call, like $(func), it's the same as

$(function() { func(); });

i.e. it waits for the DOM to be ready, so in the above example $.when waits for all the scripts to be loaded and for the DOM to be ready because of the $.Deferred call which resolves in the DOM ready callback.


For more generic use, a handy function

A utility function that accepts any array of scripts could be created like this :

$.getMultiScripts = function(arr, path) {
    var _arr = $.map(arr, function(scr) {
        return $.getScript( (path||"") + scr );
    });
        
    _arr.push($.Deferred(function( deferred ){
        $( deferred.resolve );
    }));
        
    return $.when.apply($, _arr);
}

which can be used like this

var script_arr = [
    'myscript1.js', 
    'myscript2.js', 
    'myscript3.js'
];

$.getMultiScripts(script_arr, '/mypath/').done(function() {
    // all scripts loaded
});

where the path will be prepended to all scripts, and is also optional, meaning that if the array contain the full URL's one could also do this, and leave out the path all together

$.getMultiScripts(script_arr).done(function() { ...

Arguments, errors etc.

As an aside, note that the done callback will contain a number of arguments matching the passed in scripts, each argument representing an array containing the response

$.getMultiScripts(script_arr).done(function(response1, response2, response3) { ...

where each array will contain something like [content_of_file_loaded, status, xhr_object]. We generally don't need to access those arguments as the scripts will be loaded automatically anyway, and most of the time the done callback is all we're really after to know that all scripts have been loaded, I'm just adding it for completeness, and for the rare occasions when the actual text from the loaded file needs to be accessed, or when one needs access to each XHR object or something similar.

Also, if any of the scripts fail to load, the fail handler will be called, and subsequent scripts will not be loaded

$.getMultiScripts(script_arr).done(function() {
     // all done
}).fail(function(error) {
     // one or more scripts failed to load
}).always(function() {
     // always called, both on success and error
});
Gannes answered 3/8, 2012 at 21:18 Comment(16)
Thanks for great answer. Would you mind explain me why adding $.Deferred(function( deferred ){$( deferred.resolve );}) here?Kwakiutl
The deferred object does'nt really have anything to do with the promise that lets you load multiple scripts and perform a function when they are all done. It justs check to see if $() is ready, and resolves if it is, in other words it checks to see that the DOM is ready before executing any code, much like document.ready would, and I found a good example of attaching promises to getScript online that had the deferred DOM ready functionality in the code, and just decided to keep it as it sounded like a good idea.Gannes
Thanks for clarifying. One more thing, I want to import javascript file from some online code host, so if I just put the link as an argument for $.getScript(). It will append some random number like this: ?_=1344033973219. So the call became for example http://pingzi.googlecode.com/svn-history/r30/branches/wangqi/web/jquery.window.min.js?_=1344033973219", any way to solve this? Thank you!Kwakiutl
That's because caching in ajax is turned off by default in jQuery, to turn it on and remove the querystring do : $.ajaxSetup({ cache: true }); but that could also affect other ajax calls that you don't want to cache, there's a lot more on this in the docs for getScript, and there's even a little howto on creating a cached getScript function called cachedScript.Gannes
Sorry for the late acceptance. Sometimes your way is still not quite working. Not sure what is the reason. I am trying to incorporate four script files at once. $.when($.getScript(scriptSrc_1), $.getScript(scriptSrc_2), $.getScript(scriptSrc_3), $.getScript(scriptSrc_4), $.Deferred(function(deferred) { $(deferred.resolve); })).done(function() { ... })Kwakiutl
@adeneo: well this is much prettier than nesting individual .getScript callbacks.Fluidize
For anyone who can't get this working, make sure there are no parse errors in your JS files i.e. test that they load properly in a <script> tag first. jQuery.getScript() will consider these errors a failed load and .fail() will be called instead of .done(). @SongtaoZ.Capillary
@Gannes awesome. I had got help from Benjamin, so I removed the previous comment. ThanksAngelynanger
@Angelynanger - No problem, I added a function to load any number of scripts from an array, and some more information about what's going on, being returned etc. as well.Gannes
@Gannes I am using this same concept in my backbonejs project. With a probability of ~0.3, the loading of files is failing when I refresh the page. I suspect it might be because of a file is getting loaded already before the dependency file has loaded (may be because of large size). The chrome is showing error Uncaught TypeError: Cannot read property 'prototype' of undefined and In firefox, it seems to be working fine. (I refreshed the page continuously and everything works fine)Angelynanger
Now, the firefox is also showing error rarely as f is undefined. both the chrome and firefox errors are in backbone.js file.Angelynanger
@Angelynanger - Not sure what the issue is, but note that this loads the files simultaneously, or at least somewhat, myscript2 doesn't wait for myscript1 to be loaded etc.Gannes
@Gannes ohh then I misunderstood the code. Is there anyway to load the myscript2 after complete load of myscript1? I have many dynamic files so I can't nest them as shown in below answer.Angelynanger
@Angelynanger - one could write some sort of conveniant function that accepts an array of scripts and queues them, executing the next when the current has loaded etc. A sort of dynamic adding of callbacks.Gannes
@Gannes thanks. Taking your suggestion, I created one :). Link.Angelynanger
I have added an improved version of getMultiScripts which loads scripts in the specified order, without chaining it. @Gannes you may want to add it to your answer.Barabbas
E
35

I implemented a simple function to load multiple scripts in parallel:

Function

function getScripts(scripts, callback) {
    var progress = 0;
    scripts.forEach(function(script) { 
        $.getScript(script, function () {
            if (++progress == scripts.length) callback();
        }); 
    });
}

Usage

getScripts(["script1.js", "script2.js"], function () {
    // do something...
});
Epidiascope answered 14/4, 2014 at 11:8 Comment(3)
For some reason, this one worked better than Emanuel's. It's a very transparent implementation too.Porterhouse
@satnam thanks :) ! top answer doesn't work for some people (including me) so I posted this solution. And it looks much simpler.Epidiascope
I'm using an older version of JQuery that doesn't have the "when" function. but this answer helped! It worked! Thank you!Kinross
F
12

Load the following up needed script in the callback of the previous one like:

$.getScript('scripta.js', function()
{
   $.getScript('scriptb.js', function()
   {
       // run script that depends on scripta.js and scriptb.js
   });
});
Fistulous answered 20/5, 2013 at 19:38 Comment(9)
Surely this method will download the scripts sequentially, slowing down the time to final execution?Sky
Correct, @Sky - to be fair; it's preferable to premature-execution. :)Statutable
Waterfalls should almost always be avoided. getScript comes with a promise... You can use $.when to listen to when promises resolve.Estivate
@Estivate and anyone else: If I DO need the scripts to load in a SPECIFIC ORDER, what, fundamentally speaking, is wrong with this approach? And what advantage, therefore, does the promise alternative bring?Violetavioletta
@IfediOkonkwo this does work when they need to be in series, but the orig op just mentioned that he would like multiples at once (in which, this would be an anti-pattern). Even then though, if I wanted them in series, I would write a looping function that I could pass an array to. Deep nesting gets messy quick.Estivate
@Roi: Let me confirm that I got you clear. Deep nesting in the scenario I painted has no performance drawback. Only issue is, it makes the code more difficult to peruse or to maintain. Right?Violetavioletta
Yeah, it's fine w/ performance.. and honestly if it were a one time situation and i was only grabbing two files (in series) i would prob just do this. If i did not need them in series this would be terrible for performance as you need to wait for one to finish & run a callback to start another. @IfediOkonkwoEstivate
pyramid of doom :)Epidiascope
@IfediOkonkwo I have added an improved version of getMultiScripts from the original answer that loads js in the specific order, without chaining it.Barabbas
G
9

Sometimes it is necessary to load scripts in a specific order. For example jQuery must be loaded before jQuery UI. Most examples on this page load scripts in parallel (asynchronously) which means order of execution is not guaranteed. Without ordering, script y that depends on x could break if both are successfully loaded but in wrong order.

I propose a hybrid approach which allows sequential loading of dependent scripts + optional parallel loading + deferred objects:

/*
 * loads scripts one-by-one using recursion
 * returns jQuery.Deferred
 */
function loadScripts(scripts) {
  var deferred = jQuery.Deferred();

  function loadScript(i) {
    if (i < scripts.length) {
      jQuery.ajax({
        url: scripts[i],
        dataType: "script",
        cache: true,
        success: function() {
          loadScript(i + 1);
        }
      });
    } else {
      deferred.resolve();
    }
  }
  loadScript(0);

  return deferred;
}

/*
 * example using serial and parallel download together
 */

// queue #1 - jquery ui and jquery ui i18n files
var d1 = loadScripts([
  "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js",
  "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/i18n/jquery-ui-i18n.min.js"
]).done(function() {
  jQuery("#datepicker1").datepicker(jQuery.datepicker.regional.fr);
});

// queue #2 - jquery cycle2 plugin and tile effect plugin
var d2 = loadScripts([
  "https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/jquery.cycle2.min.js",
  "https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/plugin/jquery.cycle2.tile.min.js"

]).done(function() {
  jQuery("#slideshow1").cycle({
    fx: "tileBlind",
    log: false
  });
});

// trigger a callback when all queues are complete
jQuery.when(d1, d2).done(function() {
  console.log("All scripts loaded");
});
@import url("https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/blitzer/jquery-ui.min.css");

#slideshow1 {
  position: relative;
  z-index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<p><input id="datepicker1"></p>

<div id="slideshow1">
  <img src="https://dummyimage.com/300x100/FC0/000">
  <img src="https://dummyimage.com/300x100/0CF/000">
  <img src="https://dummyimage.com/300x100/CF0/000">
</div>

The scripts in both queues will download in parallel, however, the scripts in each queue will download in sequence, ensuring ordered execution. Waterfall chart:

waterfall chart of scripts

Gneiss answered 23/10, 2015 at 23:3 Comment(1)
Don't we need to add the script tag in HTML once the script is loaded in memory?Linton
O
4

Use yepnope.js or Modernizr (which includes yepnope.js as Modernizr.load).

UPDATE

Just to follow up, here's a good equivalent of what you currently have using yepnope, showing dependencies on multiple scripts:

yepnope({
  load: ['script1.js', 'script2.js', 'script3.js'],
  complete: function () {
      // all the scripts have loaded, do whatever you want here
  }
});
Owlish answered 3/8, 2012 at 21:2 Comment(4)
Thanks. Can I use multiple yepnope.injectJs( scriptSource ) at the beginning of my javascript file just like including <script> tags in html file?Kwakiutl
That's new, and honestly, I don't understand why it's needed. Follow the documentation down a bit past the list of recent changes and you'll see the more conventional usage.Owlish
+1; worth noting that this properly manages dependencies (i.e. will not load the same script twice) unlike $.getScript. This is a big deal.Fluidize
yepnope.js is now deprecated.Kelwunn
J
4

I ran into a number of issues with multi script loading inculding one issue with (at least in Chrome) same domain hot loading of scripts not actually running after being successfully loaded by Ajax where as Cross Domain works perfectly fine! :(

The selected answer to original question does not work reliably.

After many many iterations here is my final answer to getScript(s) and loading asynchronously multiple scripts in a specific strict order with per script loaded callback option and overall callback on completion, Tested in jQuery 2.1+ and modern versions of Chrome, Firefox plus the forsaken Internet Explorer.

My test case was loading files for a THREE.JS webGL render then starting the render script when THREE global became available using an interval check passed to an anonymous function call to onComplete.

The Prototype function ( getScripts )

function getScripts( scripts, onScript, onComplete )
{
    this.async = true;
    this.cache = false;
    this.data = null;
    this.complete = function () { $.scriptHandler.loaded(); };
    this.scripts = scripts;
    this.onScript = onScript;
    this.onComplete = onComplete;
    this.total = scripts.length;
    this.progress = 0;
};

getScripts.prototype.fetch = function() {
    $.scriptHandler = this;
    var src = this.scripts[ this.progress ];
    console.log('%cFetching %s','color:#ffbc2e;', src);

    $.ajax({
        crossDomain:true,
        async:this.async,
        cache:this.cache,
        type:'GET',
        url: src,
        data:this.data,
        statusCode: {
            200: this.complete
        },
        dataType:'script'
    });
};

getScripts.prototype.loaded = function () {
    this.progress++;
    if( this.progress >= this.total ) {
        if(this.onComplete) this.onComplete();
    } else {
        this.fetch();
    };
    if(this.onScript) this.onScript();
};

How to use

var scripts = new getScripts(
    ['script1.js','script2.js','script.js'],
    function() {
        /* Optional - Executed each time a script has loaded (Use for Progress updates?) */
    },
    function () {
        /* Optional - Executed when the entire list of scripts has been loaded */
    }
);
scripts.fetch();

The function is as it is for I found using Deferred ( Deprecated now? ), When, Success & Complete in my trials to NOT be 100% reliable!?, Hence this function and use of statusCode for example.

You may want to add in error/fail handling behaviour if you wish.

Jenifferjenilee answered 16/2, 2014 at 22:24 Comment(2)
+1 for loading them in the correct order, often essential, and something the accepted answer will not guarantee unless I'm missing something? I've posted a similar shorter answer below but this is a more general case.Thirzi
@Thirzi I have added an improved version of getMultiScripts from the original answer that loads js in the specific order, without chaining it.Barabbas
A
2

You could make use of the $.when-method by trying the following function:

function loadScripts(scripts) {
  scripts.forEach(function (item, i) {
    item = $.getScript(item);
  });
  return $.when.apply($, scripts);
}

This function would be used like this:

loadScripts(['path/to/script-a.js', 'path/to/script-b.js']).done(function (respA, respB) {
    // both scripts are loaded; do something funny
});

That's the way to use Promises and have a minimum of overhead.

Aggiornamento answered 6/5, 2014 at 12:30 Comment(1)
Does this ensure the scripts are loaded in sequence?Cuneal
S
2

Great answer, adeneo.

It took me a little while to figure out how to make your answer more generic (so that I could load an array of code-defined scripts). Callback gets called when all scripts have loaded and executed. Here is my solution:

    function loadMultipleScripts(scripts, callback){
        var array = [];

        scripts.forEach(function(script){
            array.push($.getScript( script ))
        });

        array.push($.Deferred(function( deferred ){
                    $( deferred.resolve );
                }));

        $.when.apply($, array).done(function(){
                if (callback){
                    callback();
                }
            });
    }
Skepticism answered 16/4, 2015 at 0:19 Comment(0)
I
2

Append scripts with async=false

Here's a different, but super simple approach. To load multiple scripts you can simply append them to body.

  • Loads them asynchronously, because that's how browsers optimize the page loading
  • Executes scripts in order, because that's how browsers parse the HTML tags
  • No need for callback, because scripts are executed in order. Simply add another script, and it will be executed after the other scripts

More info here: https://www.html5rocks.com/en/tutorials/speed/script-loading/

var scriptsToLoad = [
   "script1.js", 
   "script2.js",
   "script3.js",
]; 
    
scriptsToLoad.forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.body.appendChild(script);
});
Isosteric answered 25/1, 2017 at 7:57 Comment(0)
R
2

Here is answer using Maciej Sawicki's one and implementing Promise as callback:

function loadScripts(urls, path) {
    return new Promise(function(resolve) {
        urls.forEach(function(src, i) {

            let script = document.createElement('script');        
            script.type = 'text/javascript';
            script.src = (path || "") + src;
            script.async = false;

            // If last script, bind the callback event to resolve
            if(i == urls.length-1) {                    
                // Multiple binding for browser compatibility
                script.onreadystatechange = resolve;
                script.onload = resolve;
            }

            // Fire the loading
            document.body.appendChild(script);
        });
    });
}

Use:

let JSDependencies = ["jquery.js",
                      "LibraryNeedingJquery.js",
                      "ParametersNeedingLibrary.js"];

loadScripts(JSDependencies,'JavaScript/').then(taskNeedingParameters);

All Javascript files are downloaded as soon as possible and executed in the given order. Then taskNeedingParameters is called.

Randeerandel answered 11/5, 2018 at 12:45 Comment(1)
I needed to support Internet Explorer, and was using jQuery, so have used $.Deferred instead of Promise and that did the trick, i.e. return new $.Deferred(function(resolve) {...Backspace
E
1

What you are looking for is an AMD compliant loader (like require.js).

http://requirejs.org/

http://requirejs.org/docs/whyamd.html

There are many good open source ones if you look it up. Basically this allows you to define a module of code, and if it is dependent on other modules of code, it will wait until those modules have finished downloading before proceeding to run. This way you can load 10 modules asynchronously and there should be no problems even if one depends on a few of the others to run.

Erelia answered 3/8, 2012 at 21:4 Comment(0)
A
1

This function will make sure that a file is loaded after the dependency file is loaded completely. You just need to provide the files in a sequence keeping in mind the dependencies on other files.

function loadFiles(files, fn) {
    if (!files.length) {
        files = [];
    }
    var head = document.head || document.getElementsByTagName('head')[0];

    function loadFile(index) {
        if (files.length > index) {
            var fileref = document.createElement('script');
            fileref.setAttribute("type", "text/javascript");
            fileref.setAttribute("src", files[index]);
            head.appendChild(fileref);
            index = index + 1;
            // Used to call a callback function
            fileref.onload = function () {
                loadFile(index);
            }
        } else if(fn){
            fn();
        }
    }
    loadFile(0);
}
Angelynanger answered 25/3, 2015 at 8:44 Comment(1)
Perhaps better than the accepted answer since it loads files in order. If script2 requires script1 to be loaded then ordering is essential.Gneiss
R
1

This works for me:

function getScripts(scripts) {
    var prArr = [];
    scripts.forEach(function(script) { 
        (function(script){
            prArr .push(new Promise(function(resolve){
                $.getScript(script, function () {
                    resolve();
                });
            }));
        })(script);
    });
    return Promise.all(prArr, function(){
        return true;
    });
}

And use it:

var jsarr = ['script1.js','script2.js'];
getScripts(jsarr).then(function(){
...
});
Retraction answered 27/11, 2016 at 1:48 Comment(0)
T
0

Shorter version of Andrew Marc Newton's comprehensive answer above. This one does not check the status code for success, which you should do to avoid undefined UI behaviour.

This one was for an annoying system where I could guarantee jQuery but no other includes, so I wanted a technique short enough to not be farmed off into an external script if forced into it. (You could make it even shorter by passing the index 0 to the first "recursive" call but force of style habits made me add the sugar).

I'm also assigning the dependency list to a module name, so this block can be included anywhere you need "module1" and the scripts and dependent initialization will only be included/run once (you can log index in the callback and see a single ordered set of AJAX requests runnning)

if(typeof(__loaders) == 'undefined') __loaders = {};

if(typeof(__loaders.module1) == 'undefined')
{
    __loaders.module1 = false;

    var dependencies = [];

    dependencies.push('/scripts/loadmefirst.js');
    dependencies.push('/scripts/loadmenext.js');
    dependencies.push('/scripts/loadmelast.js');

    var getScriptChain  = function(chain, index)        
    {
        if(typeof(index) == 'undefined')
            index = 0;

        $.getScript(chain[index], 
            function()
            {
                if(index == chain.length - 1)
                {
                    __loaders.module1 = true;

                    /* !!!
                        Do your initialization of dependent stuff here 
                    !!! */
                }
                else 
                    getScriptChain(chain, index + 1);
            }
        );
    };

    getScriptChain(dependencies);       
}
Thirzi answered 17/3, 2014 at 11:56 Comment(0)
A
0

There's a plugin out there that extends jQuery's getScript method. Allows for asynchronous and synchronous loading and uses jQuery's caching mechanism. Full disclosure, I wrote this. Please feel free to contribute if you find a better method.

https://github.com/hudsonfoo/jquery-getscripts

Anodic answered 24/3, 2014 at 11:41 Comment(0)
C
0

Loads n scripts one by one (useful if for example 2nd file needs the 1st one):

(function self(a,cb,i){
    i = i || 0; 
    cb = cb || function(){};    
    if(i==a.length)return cb();
    $.getScript(a[i++],self.bind(0,a,cb,i));                    
})(['list','of','script','urls'],function(){console.log('done')});
Chicory answered 15/8, 2014 at 15:1 Comment(0)
C
0

based on answer from @adeneo above: Combining both loading of css and js files

any suggestions for improvements ??

// Usage
//$.getMultiResources(['script-1.js','style-1.css'], 'assets/somePath/')
//  .done(function () {})
//  .fail(function (error) {})
//  .always(function () {});

(function ($) {
  $.getMultiResources = function (arr, pathOptional, cache) {
    cache = (typeof cache === 'undefined') ? true : cache;
    var _arr = $.map(arr, function (src) {
      var srcpath = (pathOptional || '') + src;
      if (/.css$/i.test(srcpath)) {
        return $.ajax({
          type: 'GET',
          url: srcpath,
          dataType: 'text',
          cache: cache,
          success: function () {
            $('<link>', {
              rel: 'stylesheet',
              type: 'text/css',
              'href': srcpath
            }).appendTo('head');
          }
        });

      } else {
        return $.ajax({
          type: 'GET',
          url: srcpath,
          dataType: 'script',
          cache: cache
        });
      }
    });
    //
    _arr.push($.Deferred(function (deferred) {
      $(deferred.resolve);
    }));
    //
    return $.when.apply($, _arr);
  };
})(jQuery);
Cumulous answered 15/6, 2016 at 13:15 Comment(0)
B
0

You can give this a try using recursion. This will download them in sync, one after another until it completes downloading the whole list.

var queue = ['url/links/go/here'];

ProcessScripts(function() { // All done do what ever you want

}, 0);

function ProcessScripts(cb, index) {
    getScript(queue[index], function() {
        index++;
        if (index === queue.length) { // Reached the end
            cb();
        } else {
            return ProcessScripts(cb, index);
        }
    });
}

function getScript(script, callback) {
    $.getScript(script, function() {
        callback();
    });
}
But answered 25/5, 2019 at 20:41 Comment(0)
B
0

I have improved @adeneo script so it will load all scripts in the specified order. It doesn't do chain loading, so it's very fast, but if you want even faster, change the 50 ms wait time.

$.getMultiScripts = function(arr, path) {

    function executeInOrder(scr, code, resolve) {
        // if its the first script that should be executed
        if (scr == arr[0]) {
            arr.shift();
            eval(code);
            resolve();
            console.log('executed', scr);
        } else {
            // waiting
            setTimeout(function(){
                executeInOrder(scr, code, resolve);
            }, 50);
        }
    }

    var _arr = $.map(arr, function(scr) {

        return new Promise((resolve) => {
            jQuery.ajax({
                type: "GET",
                url: (path || '') + scr,
                dataType: "text",
                success: function(code) {
                    console.log('loaded  ', scr);
                    executeInOrder(scr, code, resolve);
                },
                cache: true
            });
        });

    });
        
    _arr.push($.Deferred(function( deferred ){
        $( deferred.resolve );
    }));
        
    return $.when.apply($, _arr);
}

Usage is the same:

var script_arr = [
    'myscript1.js', 
    'myscript2.js', 
    'myscript3.js'
];

$.getMultiScripts(script_arr, '/mypath/').done(function() {
    // all scripts loaded
});
Barabbas answered 5/11, 2020 at 15:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.