<commit_before> // http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/
// TODO - with massive messsages from serial reading a new channel/websocket/webworker would be nice to have
// TODO - possibly use promises (although I don't see the need at the moment)
// aysynch fun
// http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/
// https://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/
// http://embed.plnkr.co/EKvjs3izaQYNCMPuur6R/preview
// http://plnkr.co/edit/EKvjs3izaQYNCMPuur6R?p=preview
// https://docs.angularjs.org/api/ng/service/$q
// http://blog.thoughtram.io/angularjs/2015/01/14/exploring-angular-1.3-speed-up-with-applyAsync.html
// https://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/

// communication begins with a synchronous /api/messages/getLocalServices
// the remote application will need to know 
// #1 what service gateway we are attached too & 
// #2 - runtime name

// TODO - gateways should make a hello() method available through their interface
// infomation returned - gateway name - mrl protokey ? & Runtime name

angular
.module('mrlapp.mrl', [])
//.service('mrl', ['$q', function($q) {
.provider('mrl', [function() {
        var _self = this;


        // The name of the gateway I am
        // currently attached to
        // tried to make these private ..
        // too much of a pain :P
        // FIXME - try again...
        this.gateway;
        this.runtime;
        this.platform;
        
        var connected = false;
        
        var instances = {};
        var registry = {};
        
        var transport = 'websocket';
        var socket = null;
        var callbacks = [];

        // framewok level callbacks
        var onOpenCallbacks = [];
        var onCloseCallbacks = [];
        var onStatus = [];
        
        var deferred = null;

        // connectivity related begins
        // required by AtmosphereJS
        // https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-atmosphere.js-API
        this.request = {
            url: document.location.origin.toString() + '/api/messages',
            transport: 'websocket',
            enableProtocol: true,
            // trackMessageLength: true,
            // maxTextMessageSize: 10000000,
            //maxBinaryMessageSize: 10000000,
            logLevel: 'info'
        };

        // connectivity related end 
        var msgCount = 0;

        // map of service names to callbacks
        var nameCallbackMap = {};
        // map of service types to callbacks
        var typeCallbackMap = {};
        // map of method names to callbacks
        var methodCallbackMap = {};
        
        if (typeof String.prototype.startsWith != 'function') {
            // see below for better implementation!
            String.prototype.startsWith = function(str) {
                return this.indexOf(str) === 0;
            };
        }
        
        function capitalize(string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        }
        
        this.getCallBackName = function(topicMethod) {
            // replacements
            if (topicMethod.startsWith("publish")) {
                return "on" + capitalize(topicMethod.substring(""))
            
            } else if (topicMethod.startsWith("get")) {
                return "get";
            }

            // no replacement - just pefix and capitalize
            // FIXME - subscribe to onMethod --- gets ---> onOnMethod :P
            return "on%s", capitalize(topicMethod);
        }

        // FIXME name would be subscribeToAllMsgs
        this.subscribeToMessages = function(callback) {
            callbacks.push(callback);
        };

        // FIXME CHECK FOR DUPLICATES
        this.subscribeToService = function(callback, serviceName) {
            if (!(serviceName in nameCallbackMap)) {
                nameCallbackMap[serviceName] = [];
            }
            nameCallbackMap[serviceName].push(callback);
        }
        
        this.subscribeToType = function(callback, typeName) {
            if (!(typeName in typeCallbackMap)) {
                typeCallbackMap[typeName] = [];
            }
            typeCallbackMap[typeName].push(callback);
        }
        
        this.subscribeToMethod = function(callback, methodName) {
            if (!(methodName in methodCallbackMap)) {
                methodCallbackMap[methodName] = [];
            }
            methodCallbackMap[methodName].push(callback);
        }
        
        this.sendMessage = function(msg) {
            var json = jQuery.stringifyJSON(msg);
            this.sendRaw(json);
        };
        
        this.sendRaw = function(msg) {
            socket.push(msg);
            console.log('sendRaw: ' + msg);
        };


        // since framework does not have a hello() onHello() defined
        // protocol - we are using Runtime.onLocalServices to do 
        // initial processing of data after a connect
        this.onLocalServices = function(msg) {
            console.log('onLocalServices:');

            // find the gateway we are talking too
            // TODO make full service?
            // _self.gateway = msg.sender;
            var gatewayName = msg.sender;
            _self.platform = msg.data[0].platform;

            // instances update
            // find the name of the runtime
            // not sure if this is "right" - but this is how
            // it is in Java .. the instance map with a null protokey
            // is the "local" instance - dunno even if javascript supports a null/undefined
            // object member name :P - perhaps it should be the protokey of the gateway instead
            // of null
            instances["null"] = msg.data[0];
            sd = msg.data[0].serviceDirectory;

            // registry update
            for (var key in sd) {
                if (sd.hasOwnProperty(key)) {
                    var service = sd[key];
                    console.log("found " + key + " of type " + service.simpleName);
                    registry[key] = {};
                    registry[key] = service;
                    if (service.simpleName == "Runtime") {
                        // the one and only runtime
                        _self.runtime = service;
                        _self.subscribeToService(_self.onRuntimeMsg, service.name);
                    }
                }
            }
            
            _self.gateway = sd[gatewayName];

            // ok now we are connected
            connected = true;
            
            deferred.resolve('connected !');
            return deferred.promise;
        }

        // keeping the registy up to date with
        // new or removed services
        this.onRuntimeMsg = function(msg) {
            if (msg.name == "registered") {
                var newService = msg.data[0];
                _self.registry[newService.name] = {};
                _self.registry[newService.name] = newService;
            } else if (msg.name == "released") {
                var name = msg.data[0];
                delete _self.registry[name];
            }
        }

        // onMessage gets all messaging from the Nettophere server
        // all asynchronous callbacks will be routhed here.  All
        // messages will be in a Message strucutre except for the 
        // Atmosphere heartbeat
        
        this.onMessage = function(response) {
            ++msgCount;
            var body = response.responseBody;
            if (body == 'X') {
                console.log("heartbeat:", body);
            } else {
                var msg;
                try {
                    msg = jQuery.parseJSON(body);

                    // process name callbacks - most common
                    if (msg.sender in nameCallbackMap) {
                        cbs = nameCallbackMap[msg.sender];
                        for (var i = 0; i < cbs.length; i++) {
                            cbs[i](msg);
                        }
                    }

                    // TODO - type based callbacks - rare, except for Runtime

                    // process method callbacks - rare - possible collisions
                    // 'onHandleError' might be worthwhile - mrl managed error
                    if (msg.method in methodCallbackMap) {
                        cbs = methodCallbackMap[msg.method];
                        for (var i = 0; i < cbs.length; i++) {
                            cbs[i](msg);
                        }
                    }
                
                } catch (e) {
                    console.log('Error onMessage: ', e, body);
                    return;
                }
            }
        };
        
        this.onTransportFailure = function(errorMsg, request) {
            jQuery.atmosphere.info(errorMsg);
            if (window.EventSource) {
                request.fallbackTransport = "sse";
            } else {
                request.fallbackTransport = 'long-polling';
            }
            transport = request.fallbackTransport;
            console.log('Error: falling back to ' + transport + ' ' + errorMsg);
        };
        
        this.onStatus = function(status) {
            console.log(status);
        }
        
        this.onClose = function(response) {
            this.connected = false;
            console.log('websocket, onclose');
            if (response.state == "unsubscribe") {
                console.log('Info: ' + transport + ' closed.');
            }
            
            for (var i = 0; i < onCloseCallbacks.length; i++) {
                onCloseCallbacks[i]();
            }
        
        };
        // --------- ws end ---------------------

        // TODO createMessage
        this.createMessage = function(inName, inMethod, inParams) {
            var msg = {
                msgID: new Date().getTime(),
                name: inName,
                method: inMethod,
                data: inParams
            };
            return msg;
        }
        
        this.isUndefinedOrNull = function(val) {
            return angular.isUndefined(val) || val === null;
        }
        
        this.getService = function(name) {
            if (this.isUndefinedOrNull(registry[name])) {
                return null;
            }
            return registry[name];
        };<commit_msg>fixing createMessage so it can handle when no args are passed to the method call.<commit_after> // http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/
// TODO - with massive messsages from serial reading a new channel/websocket/webworker would be nice to have
// TODO - possibly use promises (although I don't see the need at the moment)
// aysynch fun
// http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/
// https://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/
// http://embed.plnkr.co/EKvjs3izaQYNCMPuur6R/preview
// http://plnkr.co/edit/EKvjs3izaQYNCMPuur6R?p=preview
// https://docs.angularjs.org/api/ng/service/$q
// http://blog.thoughtram.io/angularjs/2015/01/14/exploring-angular-1.3-speed-up-with-applyAsync.html
// https://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/

// communication begins with a synchronous /api/messages/getLocalServices
// the remote application will need to know 
// #1 what service gateway we are attached too & 
// #2 - runtime name

// TODO - gateways should make a hello() method available through their interface
// infomation returned - gateway name - mrl protokey ? & Runtime name

angular
.module('mrlapp.mrl', [])
//.service('mrl', ['$q', function($q) {
.provider('mrl', [function() {
        var _self = this;


        // The name of the gateway I am
        // currently attached to
        // tried to make these private ..
        // too much of a pain :P
        // FIXME - try again...
        this.gateway;
        this.runtime;
        this.platform;
        
        var connected = false;
        
        var instances = {};
        var registry = {};
        
        var transport = 'websocket';
        var socket = null;
        var callbacks = [];

        // framewok level callbacks
        var onOpenCallbacks = [];
        var onCloseCallbacks = [];
        var onStatus = [];
        
        var deferred = null;

        // connectivity related begins
        // required by AtmosphereJS
        // https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-atmosphere.js-API
        this.request = {
            url: document.location.origin.toString() + '/api/messages',
            transport: 'websocket',
            enableProtocol: true,
            // trackMessageLength: true,
            // maxTextMessageSize: 10000000,
            //maxBinaryMessageSize: 10000000,
            logLevel: 'info'
        };

        // connectivity related end 
        var msgCount = 0;

        // map of service names to callbacks
        var nameCallbackMap = {};
        // map of service types to callbacks
        var typeCallbackMap = {};
        // map of method names to callbacks
        var methodCallbackMap = {};
        
        if (typeof String.prototype.startsWith != 'function') {
            // see below for better implementation!
            String.prototype.startsWith = function(str) {
                return this.indexOf(str) === 0;
            };
        }
        
        function capitalize(string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        }
        
        this.getCallBackName = function(topicMethod) {
            // replacements
            if (topicMethod.startsWith("publish")) {
                return "on" + capitalize(topicMethod.substring(""))
            
            } else if (topicMethod.startsWith("get")) {
                return "get";
            }

            // no replacement - just pefix and capitalize
            // FIXME - subscribe to onMethod --- gets ---> onOnMethod :P
            return "on%s", capitalize(topicMethod);
        }

        // FIXME name would be subscribeToAllMsgs
        this.subscribeToMessages = function(callback) {
            callbacks.push(callback);
        };

        // FIXME CHECK FOR DUPLICATES
        this.subscribeToService = function(callback, serviceName) {
            if (!(serviceName in nameCallbackMap)) {
                nameCallbackMap[serviceName] = [];
            }
            nameCallbackMap[serviceName].push(callback);
        }
        
        this.subscribeToType = function(callback, typeName) {
            if (!(typeName in typeCallbackMap)) {
                typeCallbackMap[typeName] = [];
            }
            typeCallbackMap[typeName].push(callback);
        }
        
        this.subscribeToMethod = function(callback, methodName) {
            if (!(methodName in methodCallbackMap)) {
                methodCallbackMap[methodName] = [];
            }
            methodCallbackMap[methodName].push(callback);
        }
        
        this.sendMessage = function(msg) {
            var json = jQuery.stringifyJSON(msg);
            this.sendRaw(json);
        };
        
        this.sendRaw = function(msg) {
            socket.push(msg);
            console.log('sendRaw: ' + msg);
        };


        // since framework does not have a hello() onHello() defined
        // protocol - we are using Runtime.onLocalServices to do 
        // initial processing of data after a connect
        this.onLocalServices = function(msg) {
            console.log('onLocalServices:');

            // find the gateway we are talking too
            // TODO make full service?
            // _self.gateway = msg.sender;
            var gatewayName = msg.sender;
            _self.platform = msg.data[0].platform;

            // instances update
            // find the name of the runtime
            // not sure if this is "right" - but this is how
            // it is in Java .. the instance map with a null protokey
            // is the "local" instance - dunno even if javascript supports a null/undefined
            // object member name :P - perhaps it should be the protokey of the gateway instead
            // of null
            instances["null"] = msg.data[0];
            sd = msg.data[0].serviceDirectory;

            // registry update
            for (var key in sd) {
                if (sd.hasOwnProperty(key)) {
                    var service = sd[key];
                    console.log("found " + key + " of type " + service.simpleName);
                    registry[key] = {};
                    registry[key] = service;
                    if (service.simpleName == "Runtime") {
                        // the one and only runtime
                        _self.runtime = service;
                        _self.subscribeToService(_self.onRuntimeMsg, service.name);
                    }
                }
            }
            
            _self.gateway = sd[gatewayName];

            // ok now we are connected
            connected = true;
            
            deferred.resolve('connected !');
            return deferred.promise;
        }

        // keeping the registy up to date with
        // new or removed services
        this.onRuntimeMsg = function(msg) {
            if (msg.name == "registered") {
                var newService = msg.data[0];
                _self.registry[newService.name] = {};
                _self.registry[newService.name] = newService;
            } else if (msg.name == "released") {
                var name = msg.data[0];
                delete _self.registry[name];
            }
        }

        // onMessage gets all messaging from the Nettophere server
        // all asynchronous callbacks will be routhed here.  All
        // messages will be in a Message strucutre except for the 
        // Atmosphere heartbeat
        
        this.onMessage = function(response) {
            ++msgCount;
            var body = response.responseBody;
            if (body == 'X') {
                console.log("heartbeat:", body);
            } else {
                var msg;
                try {
                    msg = jQuery.parseJSON(body);

                    // process name callbacks - most common
                    if (msg.sender in nameCallbackMap) {
                        cbs = nameCallbackMap[msg.sender];
                        for (var i = 0; i < cbs.length; i++) {
                            cbs[i](msg);
                        }
                    }

                    // TODO - type based callbacks - rare, except for Runtime

                    // process method callbacks - rare - possible collisions
                    // 'onHandleError' might be worthwhile - mrl managed error
                    if (msg.method in methodCallbackMap) {
                        cbs = methodCallbackMap[msg.method];
                        for (var i = 0; i < cbs.length; i++) {
                            cbs[i](msg);
                        }
                    }
                
                } catch (e) {
                    console.log('Error onMessage: ', e, body);
                    return;
                }
            }
        };
        
        this.onTransportFailure = function(errorMsg, request) {
            jQuery.atmosphere.info(errorMsg);
            if (window.EventSource) {
                request.fallbackTransport = "sse";
            } else {
                request.fallbackTransport = 'long-polling';
            }
            transport = request.fallbackTransport;
            console.log('Error: falling back to ' + transport + ' ' + errorMsg);
        };
        
        this.onStatus = function(status) {
            console.log(status);
        }
        
        this.onClose = function(response) {
            this.connected = false;
            console.log('websocket, onclose');
            if (response.state == "unsubscribe") {
                console.log('Info: ' + transport + ' closed.');
            }
            
            for (var i = 0; i < onCloseCallbacks.length; i++) {
                onCloseCallbacks[i]();
            }
        
        };
        // --------- ws end ---------------------

        // TODO createMessage
        this.createMessage = function(inName, inMethod, inParams) {
        	// TODO: consider a different way to pass inParams for a no arg method.
        	// rather than an array with a single null element.
        	if (inParams.length = 1 && inParams[0] == null) {
                var msg = {
                        msgID: new Date().getTime(),
                        name: inName,
                        method: inMethod,
                    };
                    return msg;
        	} else {
	            var msg = {
	                msgID: new Date().getTime(),
	                name: inName,
	                method: inMethod,
	                data: inParams
	            };
	            return msg;
        	}
        }
        
        this.isUndefinedOrNull = function(val) {
            return angular.isUndefined(val) || val === null;
        }
        
        this.getService = function(name) {
            if (this.isUndefinedOrNull(registry[name])) {
                return null;
            }
            return registry[name];
        };
{'fraction_non_alphanumeric': 0.10596149385293435, 'fraction_numerical': 0.0041753653444676405, 'mean_word_length': 1.4174049568240439, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '91128', 'n_tokens_mistral': 5633, 'n_tokens_neox': 5368, 'n_words': 1371}