0

after few days to break my mind and trying to solve my issue myself, I've decided to ask some help. Here is my code :

// -----------
// Global object
// -----------
window.myObj = window.myObj || {};
function getLocation(myObj) {
    // Location test
    // -- Mobile connection test/fix
    // -- First step => Ask on client IP
    $.getJSON( "//freegeoip.net/json/?callback=?", function(data) {
        console.warn('Fetching Accurate JSON data...');
        if (data.city !== '') {
            // Log output to console
            console.info('Location found from client ip');
            console.info(JSON.stringify(data, null, 2));
            // myObj = data;
            // myObj.toString = function(data) {
            //  return JSON.stringify(data, null, 2);
            // }
        } else {
            // -- Second step => Ask on server IP using server name
            $.getJSON( "//freegeoip.net/json/" + window.location.host + "?callback=?", function(data) {
                // Log output to console
                console.info('Location found from server ip');
                console.info(JSON.stringify(data, null, 2));
                // myObj = data;
                // myObj.toString = function(data) {
                //  return JSON.stringify(data, null, 2);
                // }
            });
        }
    });

    return myObj;
}
console.info(getLocation(myObj));

What I want, is to store the result of the JSONP callback to the outer object named myObj.

I've tried to create the function as a closure this way:

window.myObj = (function () {
    // Location test
    // -- Mobile connection test/fix
    // -- First step => Ask on client IP
    $.getJSON( "//freegeoip.net/json/?callback=?", function(data) {
        console.warn('Fetching Accurate JSON data...');
        if (data.city !== '') {
            // Log output to console
            console.info('Location found from client ip');
            return data;
        } else {
            // -- Second step => Ask on server IP using server name
            $.getJSON( "//freegeoip.net/json/" + window.location.host + "?callback=?", function(data) {
                // Log output to console
                console.info('Location found from server ip');
                return data;
            });
        }
    });
}());

but it returns undefined everytime... As you may see my javascript skills are not so good, as my English too.

I thank you all in advance for your help.

Jiab77
  • 349
  • 3
  • 11

1 Answers1

0

So... I've found the solution by myself after a lot of study regarding Javascript language. Here is how I've managed my issue:

// -----------
// Global JS Object
// -----------
var myObj = myObj || {};

// Dependency
// Object size count function. Thanks to:
// http://stackoverflow.com/a/24457767
myObj.objSize = function(obj) {
    var count = 0;
    var key;

    if (typeof obj == "object") {
        if (Object.keys) {
            count = Object.keys(obj).length;
        } else if (window._) {
            count = _.keys(obj).length;
        } else if (window.$) {
            count = $.map(obj, function() { return 1; }).length;
        } else {
            for (key in obj) if (obj.hasOwnProperty(key)) count++;
        }
    }

    return count;
}

// Setter method
myObj.setUserDetails = function(data) {
    this.user = {};
    this.user.details = data;
    this.user.location = {lat: data.latitude, lng: data.longitude};
    this.user.location.toString = function() {
        // Returns data as same format as Google use...
        return '(' + this.user.location.lat + ', ' + this.user.location.lng + ')';
    }.bind(this);
}

// Getter method
myObj.getUserDetails = function() {
    var _this = this;

    // -- Mobile connection test/fix
    var valid_region_codes = ['GE', 'BE', 'FR', 'VD', 'VS', 'NE', 'JU'];

    // Checking if you we've not already some user data
    if (typeof _this.user !== 'object') {
        // -- First step => Ask on client IP
        $.getJSON( "//freegeoip.net/json/?callback=?", function(first_fetch) {
            console.warn('Fetching Accurate JSON data...');
            if (first_fetch.region_code === '') {
                // -- Empty 'region_code' => Ask on server IP using server name
                $.getJSON( "//freegeoip.net/json/" + window.location.host + "?callback=?", function(second_fetch) {
                    // Log output to console
                    console.info('Fetch: 2');
                    console.info('Location based on server ip');
                    console.info(JSON.stringify(second_fetch, null, 2));
                    console.info(this); console.info(_this);
                    _this.setUserDetails(second_fetch);
                });
            } else {
                // -- Have 'region_code', let's proceed...
                if (valid_region_codes.indexOf(first_fetch.region_code) !== -1) {
                    // Checked 'region_code'... Valid, using client IP
                    console.info('Fetch: 1');
                    console.info('Location based on client ip');
                    console.info(JSON.stringify(first_fetch, null, 2));
                    console.info(this); console.info(_this);
                    _this.setUserDetails(first_fetch);
                } else {
                    // Checked 'region_code'... not valid, falling back to server IP
                    $.getJSON( "//freegeoip.net/json/" + window.location.host + "?callback=?", function(third_fetch) {
                        // Log output to console
                        console.info('Fetch: 3');
                        console.info('Location based on server ip');
                        console.info(JSON.stringify(third_fetch, null, 2));
                        console.info(this); console.info(_this);
                        _this.setUserDetails(third_fetch);
                    });
                }
            }
        });
    } else {
        console.warn('Using previously fetched user data...');
    }
}

// The Holy Grail method (^_^)
myObj.waitForProps = function(prop, type, timer, callback) {
    var check = setInterval(function() {
        var evil = eval(prop), evilSize = masalle.objSize(evil);
        console.info('Checking [prop] as: ' + prop + '\nTo type: ' + type +
                     '\nEach: ' + timer + 'ms\nThen exec callback:\n\t\t\t' + callback);
        if (typeof evil === type) {
            console.info('[' + prop + '] found, checking size...');
            if (evilSize > 0) {
                console.info('[' + prop + '] size: ' + evilSize);
                clearInterval(check);
                if (callback && typeof callback === 'function') {
                    callback();
                }
            }
        }
    }, timer);
}

Explanations

First, I'm calling the getter method myObj.getUserDetails() which use the setter method myObj.setUserDetails() to insert the content of the JSONP request into an object named myObj.user

Then, I'm calling my magic method (which is like a waiting method) to check the existence of the object myObj.user

If it's already available, I'm using it and execute the specified callback OR I'm iterating a wait timer based on the specified duration till the requested object became available then it executes the callback

Usage

// Grab the user location
myObj.getUserDetails();

// Wait the time the required object to be created / filed
myObj.waitForProps('myObj.user', 'object', 600, function() {
    // Execute what you need there
});

I hope this could be useful for someone. Feel free to contact me if you know a better (and faster) way to do the same thing.

Known issues:

I know I've used the evil eval function in my magic method which could led to some security issues. I hope I've used it correctly, if not, please correct my code.

If you're using the method myObj.waitForProps() in a ReactJS project, you'll see that the specified duration is not respected and the method iterates as fast as possible till your requested object is available. Let me know if you have a tips to this issue, maybe my code is just working wrong...

Jiab77
  • 349
  • 3
  • 11