(function(context,$) {

    "use strict";

    context.JK = context.JK || {};
    context.JK.FakeJamClient = function(app) {
        var logger = context.JK.logger;
        logger.info("*** Fake JamClient instance initialized. ***");

        // Change this to false if you want to see FTUE with fake jam client
        var ftueStatus = true;
        var eventCallbackName = '';
        var eventCallbackRate = 1000;
        var vuValue = -70;
        var vuChange = 10;
        var callbackTimer = null;
        var _mix = -30;
        var device_id = -1;
        var latencyCallback = null;
        var frameSize = 2.5;

        function dbg(msg) { logger.debug('FakeJamClient: ' + msg); }

        // Bummer that javascript doesn't have much in the way of reflection.
        // arguments.callee.name would probably do what we want, but it's deprecated
        // and not allowed when using "strict"
        // (Wishing I could write a single function which debug logs the name of the
        // current function and a JSON stringify of the arguments).

        function OpenSystemBrowser(href) {
            dbg("OpenSystemBrowser('" + href + "')");
            context.window.open(href);
        }
        function FTUEGetInputLatency() {
            dbg("FTUEGetInputLatency");
            return 2;
        }
        function FTUESetInputLatency(latency) {
            dbg("FTUESetInputLatency:" + latency);
        }
        function FTUEGetOutputLatency() {
            dbg("FTUEGetOutputLatency");
            return 2;
        }
        function FTUESetOutputLatency(latency) {
            dbg("FTUESetOutputLatency:" + latency);
        }
        function FTUEGetVolumeRanges () {
            dbg("FTUEGetVolumeRanges");
            return {
                input_maximum: 20,
                input_minimum: -80,
                output_maximum: 20,
                output_minimum: -80
            };
        }
        function FTUEHasControlPanel() {
            dbg("FTUEHasControlPanel");
            return true;
        }
        function FTUEGetFrameSize(newSize) {
            dbg("FTUEGetFrameSize");
            return frameSize;
        }
        function FTUESetFrameSize(newSize) {
            dbg("FTUESetFrameSize:" + newSize);
            // one of 2.5, 5 or 10
            frameSize = newSize;
        }
        function FTUEOpenControlPanel() {
            dbg("FTUEOpenControlPanel");
            context.alert("Simulated ASIO Dialog");
        }
        function FTUEInit() { dbg("FTUEInit"); }
        function FTUERefreshDevices() { dbg("FTUERefreshDevices()"); }
        function FTUESave(b) { dbg("FTUESave(" + b + ")"); }
        function FTUEGetStatus() { return ftueStatus; }
        function FTUESetStatus(b) { ftueStatus = b; }
        function FTUESetMusicDevice(id) { dbg("FTUESetMusicDevice"); }
        function FTUEGetDevices() {
            dbg('FTUEGetMusicDevices');
            return {
                "ASIO4ALL v2":"ASIO4ALL v2 - ASIO",
                "M-Audio FW ASIO":"M-AUDIO FW ASIO - ASIO"
            };
        }
        function FTUEGetMusicInputs() {
            dbg('FTUEGetMusicInputs');
            return {
                "i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1":
                    "Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
            };
        }
        function FTUEGetMusicOutputs() {
            dbg('FTUEGetMusicOutputs');
            return {
                "o~11~Multichannel (FW AP Multi)~0^o~11~Multichannel (FW AP Multi)~1":
                    "Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
            };
        }
        function FTUEGetChatInputs() {
            dbg('FTUEGetChatInputs');
            return {
                "i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1":
                    "Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
            };
        }
        function FTUESetMusicInput() { dbg('FTUESetMusicInput'); }
        function FTUESetChatInput() { dbg('FTUESetChatInput'); }
        function FTUESetMusicOutput() { dbg('FTUESetMusicOutput'); }
        function FTUEGetInputVolume() { dbg('FTUEGetInputVolume'); return -60; }
        function FTUESetInputVolume() { dbg('FTUESetInputVolume'); }
        function FTUEGetOutputVolume() { dbg('FTUEGetOutputVolume'); return -40; }
        function FTUESetOutputVolume() { dbg('FTUESetOutputVolume'); }
        function FTUEGetChatInputVolume() { dbg('FTUEGetChatInputVolume'); return -10; }
        function FTUESetChatInputVolume() { dbg('FTUESetChatInputVolume'); }
        function FTUERegisterVUCallbacks() {
            dbg('FTUERegisterVUCallbacks');
        }
        function FTUERegisterLatencyCallback(functionName) {
            dbg('FTUERegisterLatencyCallback');
            latencyCallback = functionName;
        }
        function FTUEStartLatency() {
            function cb() {
                // Change 4192 to modify latency MS results (in microseconds)
                eval(latencyCallback + "(10.0)");
            }
            context.setTimeout(cb, 1000);
        }

        function RegisterVolChangeCallBack(functionName) {
            dbg('RegisterVolChangeCallBack');
        }


        function GetOS() { return 100000000; }
        function GetOSAsString() {
            return "Win32";
            //return "Mac";
        }

        function LatencyUpdated(map) { dbg('LatencyUpdated:' + JSON.stringify(map)); }
        function LeaveSession(map) { dbg('LeaveSession:' + JSON.stringify(map)); }
        function P2PMessageReceived(s1,s2) { dbg('P2PMessageReceived:' + s1 + ',' + s2); }
        function JoinSession(sessionId) {dbg('JoinSession:' + sessionId);}
        function ParticipantLeft(session, participant) {
            dbg('ParticipantLeft:' + JSON.stringify(session) + ',' +
                JSON.stringify(participant));
        }
        function ParticipantJoined(session, participant) {
            dbg('ParticipantJoined:' + JSON.stringify(session) + ',' +
                JSON.stringify(participant));
        }
        function RecordTestBegin() { dbg('RecordTestBegin'); }
        function RecordTestEnd() { dbg('RecordTestBegin'); }
        function RecordTestPlayback() { dbg('RecordTestBegin'); }
        function SendP2PMessage(s1,s2) { dbg('SendP2PMessage:' + s1 + ',' + s2); }
        function SetASIOEnabled(i1, i2, i3, i4, i5, i6) {
            dbg('SetASIOEnabled(' + i1 + ',' + i2 + ',' + i3 + ',' + i4 + ',' + i5 + ',' + i6 + ')');
        }
        function SignalLatencyUpdated(client) {
            dbg('SignalLatencyUpdated:' + JSON.stringify(client));
        }
        function SignalSendP2PMessage(s1,ba) {
            dbg('SignalSendP2PMessage:' + JSON.stringify(arguments));
        }
        function StartPlayTest(s) { dbg('StartPlayTest' + JSON.stringify(arguments)); }
        function StartRecordTest(s) { dbg('StartRecordTest' + JSON.stringify(arguments)); }
        function StartRecording(map) { dbg('StartRecording' + JSON.stringify(arguments)); }
        function StopPlayTest() { dbg('StopPlayTest'); }
        function StopRecording(map) { dbg('StopRecording' + JSON.stringify(arguments)); }
        function TestASIOLatency(s) { dbg('TestASIOLatency' + JSON.stringify(arguments)); }

        function TestLatency(clientID, callbackFunctionName, timeoutCallbackName) {
            logger.debug("Fake JamClient: TestLatency called with client, " + clientID + " and callback function name: " + callbackFunctionName);
            var response = {
                clientID: clientID,
                latency: 50
            };
            var js = callbackFunctionName + "(" + JSON.stringify(response) + ");";
            eval(js);
        }

        function GetASIODevices() {
            var response =[{"device_id":0,"device_name":"Realtek High Definition Audio","device_type": 0,"interfaces":[{"interface_id":0,"interface_name":"Realtek HDA SPDIF Out","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":1,"interface_name":"Realtek HD Audio rear output","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":2,"interface_name":"Realtek HD Audio Mic input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":3,"interface_name":"Realtek HD Audio Line input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":4,"interface_name":"Realtek HD Digital input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"}]},{"interface_id":5,"interface_name":"Realtek HD Audio Stereo input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]}],"wavert_supported":false},{"device_id":1,"device_name":"M-Audio FW Audiophile","device_type": 1,"interfaces":[{"interface_id":0,"interface_name":"FW AP Multi","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":1,"interface_name":"FW AP 1/2","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":2,"interface_name":"FW AP SPDIF","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":3,"interface_name":"FW AP 3/4","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":2,"device_name":"Virtual Audio Cable","device_type": 2,"interfaces":[{"interface_id":0,"interface_name":"Virtual Cable 2","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]},{"interface_id":1,"interface_name":"Virtual Cable 1","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":3,"device_name":"WebCamDV WDM Audio Capture","device_type": 3,"interfaces":[{"interface_id":0,"interface_name":"WebCamDV Audio","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"},{"is_input":false,"pin_id":1,"pin_name":"Volume Control"}]}],"wavert_supported":false}];
            return response;
        }

        // Session Functions
        function SessionAddTrack() {}

        function SessionAudioResync() {
            logger.debug("Fake JamClient: SessionAudioResync()");
        }

        function SessionGetControlState(mixerIds) {
            dbg("SessionGetControlState");
            var groups = [0, 1, 2, 3, 7, 9];
            var names = [
                "FW AP Multi",
                "FW AP Multi",
                "FW AP Multi",
                "FW AP Multi",
                "",
                ""
            ];
            var clientIds = [
                "",
                "",
                "",
                "",
                "3933ebec-913b-43ab-a4d3-f21dc5f8955b",
                ""
            ];
            var response = [];
            for (var i=0; i<mixerIds.length; i++) {
                response.push({
                    client_id: clientIds[i],
                    group_id: groups[i],
                    id: mixerIds[i],
                    master: true,
                    monitor: true,
                    mute: false,
                    name: names[i],
                    range_high: 20,
                    range_low: -80,
                    record: true,
                    stereo: true,
                    volume_left: -40,
                    volume_right:-40,
                    instrument_id:50 // see globals.js
                });
            }
            return response;
        }
        function SessionGetIDs() {
            return [
                "FW AP Multi_0_10000",
                "FW AP Multi_1_10100",
                "FW AP Multi_2_10200",
                "FW AP Multi_3_10500",
                "User@208.191.152.98#",
                "User@208.191.152.98_*"
            ];
        }
        function SessionRegisterCallback(callbackName) {
            eventCallbackName = callbackName;
            if (callbackTimer) { context.clearInterval(callbackTimer); }
            if (eventCallbackName) {
                callbackTimer = context.setInterval(doCallbacks, eventCallbackRate);
            }
        }

        function SessionSetAlertCallback(callback) {

        }

        function SessionSetControlState(stringValue) {
            dbg('SessionSetControlState: ' + stringValue);
        }
        function SessionSetRecordingFilename(filename) {}
        function SessionSetRecordingFolder(folderName) {}
        function SessionStartPlay() {}
        function SessionStartRecording() {}
        function SessionStopPlay() {}
        function SessionStopRecording() {}

        function SessionGetDeviceLatency() { return 10.0; }

        function SessionGetMasterLocalMix() {
            logger.debug('SessionGetMasterLocalMix. Returning: ' + _mix);
            return _mix;
        }
        function SessionSetMasterLocalMix(level) {
            logger.debug('SessionSetMasterLocalMix(' + level + ')');
            _mix = level;
        }

        function SessionSetUserName(client_id,name){

        }

        function doCallbacks() {
            var names = ["left_vu", "right_vu"];
            //var ids = ["FW AP Multi_2_10200", "FW AP Multi_0_10000"];
            var ids= ["i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1",
                      "i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~2"];

            var args = [];
            for (var i=0; i<ids.length; i++) {
                for (var j=0; j<names.length; j++) {
                    args.push('"' + names[j] + '"');
                    args.push('"' + ids[i] + '"');
                    args.push(vuValue);
                }
            }
            var js = eventCallbackName + '(' + args.join(',') + ')';
            eval(js);

            vuValue += vuChange;
            if (vuValue > 10 || vuValue < -70) { vuChange = vuChange * -1; }
        }
        function SetVURefreshRate(rateMS) {
            eventCallbackRate = rateMS;
            if (callbackTimer) { context.clearInterval(callbackTimer); }
            if (eventCallbackName) {
                callbackTimer = context.setInterval(doCallbacks, eventCallbackRate);
            }
        }

        // Track Functions


        // Returns a list of objects representing all available audio devices and
        // pins on the current system. On my windows box, I get 38 objects back.
        // First couple examples included here. Note that the list tends to come
        // back with all of the left inputs, then all the right inputs, then all
        // the left outputs, then all the right outputs.
        function TrackGetChannels() {
            // Real example:
            /*
{
    device_id: "1394\\M-Audio&FW_Audiophile",
    full_id: "i~1~\\\\?\\1394#m-audio&fw_audiophile#d4eb0700036c0d00#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\\fwap_12",
    id: "\\\\?\\1394#m-audio&fw_audiophile#d4eb0700036c0d00#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\\fwap_12",
    input: true,
    left: true,
    name: "M-Audio FW Audiophile: FW AP 1/2 - Left"
},
            */

            // But we'll just build a list of names and fake it
            var devices = [
                "M-Audio FW Audiophile: FW AP 1/2"
                //"M-Audio FW Audiophile: FW Multi 1/2",
                //"M-Audio FW Audiophile: FW SPDIF 1/2",
                //"Realtek High Definition Audio: Realtek HD Digital",
                // "Realtek High Definition Audio: Realtek HD Line",
                // "Realtek High Definition Audio: Realtek HD Audio Mic",
                // "Realtek High Definition Audio: Realtek HD Audio Stereo",
                //"WebCamDV WDM Audio Capture: WebCamDV Audio"
            ];
            var suffixes = [ " - Left", " - Right", " - Left", " - Right"];
            var lefts = [true, false, true, false];
            var inputs = [true, true, false, false];
            var types = ["music", "non-music", "music", "non-music"];
            var response = [];
            var name, o, i, j;
            for (j=0; j<4; j++) {
                for (i=0; i<devices.length; i++) {
                    name = devices[i] + suffixes[j];
                    o = {
                        device_id: name,
                        full_id: name,
                        id: name,
                        input: inputs[j],
                        left: lefts[j],
                        name: name,
                        device_type: types[j]
                    };
                    response.push(o);
                }
            }
            return response;
        }

        function TrackGetCount() {
            return 1; // Real version returns whatever was set
        }

        function TrackGetInstrument(trackNumber) {
            if (trackNumber === 1) {
                return 100;
            }
            else {
                return 200;
            }
        }

        function TrackGetChatEnable() {
            return true;
        }

        function TrackSetChatEnable(chat) {

        }

        function TrackSaveAssignments() {}

        function TrackGetDevices() {
            var response = {"Built-in":"Built-in", "ASIO4ALL v2":"ASIO4ALL v2 - ASIO", "BEHRINGER USB AUDIO":"BEHRINGER USB AUDIO - ASIO", "{2B7D1F21-FA6E-11D4-0002-DDCA793267F2}":"SoundMAX HD Audio O - WDM", "{ABCC5A6C-C263-463B-A72F-A5BF64C86EBA}":"Generic USB Audio Device - WDM"};
            return response;
        }

        function TrackDeleteProfile(id) {

        }


        function TrackGetMusicDeviceID() {
            return device_id;
        }

        function TrackSetMusicDevice(id) {
            device_id = id;
        }

        function TrackGetMusicDeviceNames(input) {
            var names = [];
            names.push("Built-in");
            return names;
        }

        // mock response of this method by allowing only even device IDs to open control panel
        function TrackHasControlPanel() {
            return true;
        }

        function TrackIsMusicDeviceType(type) {
            return true;
        }

        function TrackGetChatUsesMusic() {
            return 0;
        }

        function TrackSetChatUsesMusic(usesMusic) {
        }

        function TrackOpenControlPanel() {
            return;
        }

        function TrackLoadAssignments() {}

        function TrackSave() {}

        // Set a Track Assignment.
        // assignment is an enum:
        // CHAT = -2, OUTPUT = -1, UNASSIGNED = 0
        function TrackSetAssignment(chanId, isInput, assignment) {}

        // Get a Track Assignment.
        function TrackGetAssignment(chanId, isInput) {
            // CHAT = -2, OUTPUT = -1, UNASSIGNED = 0
            return 0;
        }

        function TrackSetCount(count) {}

        function TrackSetInstrument(track, instrumentId) {}

        // Method which sets volume
        function UpdateMixer(mixerId) {}

        // Client Update Functions
        function ClientUpdateVersion() { return "Compiled 1.2.3";  }
        function ClientUpdateStartDownload(url, progressCallback, successCallback, failureCallback) {

            // simulate a bunch of download callbacks
            var count = 0;
            var max = 100;
            var bytesReceived = 0;
            var bytesTotal = 10000;

            function fire() {
                count++;
                context.setTimeout(function() {
                    bytesReceived = ( count / max ) * bytesTotal;
                    context.JK.ClientUpdate.DownloadProgressCallback(bytesReceived, bytesTotal, 0, 0);

                    if(count < max) {
                        fire();
                    }
                    else {
                        context.JK.ClientUpdate.DownloadFailureCallback("/some/path/here");
                    }
                }, 50);
            }
            fire();

        }
        function ClientUpdateStartUpdate(path, successCallback, failureCallback) {}

        // Javascript Bridge seems to camel-case
        // Set the instance functions:
        this.GetASIODevices = GetASIODevices;
        this.GetOS = GetOS;
        this.GetOSAsString = GetOSAsString;
        this.JoinSession = JoinSession;
        this.LatencyUpdated = LatencyUpdated;
        this.LeaveSession = LeaveSession;
        this.P2PMessageReceived = P2PMessageReceived;
        this.ParticipantJoined = ParticipantJoined;
        this.ParticipantLeft = ParticipantLeft;
        this.RecordTestBegin = RecordTestBegin;
        this.RecordTestEnd = RecordTestEnd;
        this.RecordTestPlayback = RecordTestPlayback;
        this.RegisterVolChangeCallBack = RegisterVolChangeCallBack;
        this.SendP2PMessage = SendP2PMessage;
        this.SetASIOEnabled = SetASIOEnabled;
        this.SignalLatencyUpdated = SignalLatencyUpdated;
        this.SignalSendP2PMessage = SignalSendP2PMessage;
        this.StartPlayTest = StartPlayTest;
        this.StartRecordTest = StartRecordTest;
        this.StartRecording = StartRecording;
        this.StopPlayTest = StopPlayTest;
        this.StopRecording = StopRecording;
        this.TestASIOLatency = TestASIOLatency;
        this.TestLatency = TestLatency;
        this.connected = true;

        // FTUE (round 3)
        this.FTUEGetChatInputVolume = FTUEGetChatInputVolume;
        this.FTUEGetChatInputs = FTUEGetChatInputs;
        this.FTUEGetDevices = FTUEGetDevices;
        this.FTUEGetFrameSize = FTUEGetFrameSize;
        this.FTUEGetInputLatency = FTUEGetInputLatency;
        this.FTUEGetInputVolume = FTUEGetInputVolume;
        this.FTUEGetMusicInputs = FTUEGetMusicInputs;
        this.FTUEGetMusicOutputs = FTUEGetMusicOutputs;
        this.FTUEGetOutputLatency = FTUEGetOutputLatency;
        this.FTUEGetOutputVolume = FTUEGetOutputVolume;
        this.FTUEGetStatus = FTUEGetStatus;
        this.FTUEGetVolumeRanges = FTUEGetVolumeRanges;
        this.FTUEHasControlPanel = FTUEHasControlPanel;
        this.FTUEInit = FTUEInit;
        this.FTUEOpenControlPanel = FTUEOpenControlPanel;
        this.FTUERegisterLatencyCallback = FTUERegisterLatencyCallback;
        this.FTUERegisterVUCallbacks = FTUERegisterVUCallbacks;
        this.FTUERefreshDevices = FTUERefreshDevices;
        this.FTUESave = FTUESave;
        this.FTUESetChatInput = FTUESetChatInput;
        this.FTUESetChatInputVolume = FTUESetChatInputVolume;
        this.FTUESetInputVolume = FTUESetInputVolume;
        this.FTUESetFrameSize = FTUESetFrameSize;
        this.FTUESetInputLatency = FTUESetInputLatency;
        this.FTUESetMusicDevice = FTUESetMusicDevice;
        this.FTUESetMusicInput = FTUESetMusicInput;
        this.FTUESetMusicOutput = FTUESetMusicOutput;
        this.FTUESetOutputLatency = FTUESetOutputLatency;
        this.FTUESetOutputVolume = FTUESetOutputVolume;
        this.FTUESetStatus = FTUESetStatus;
        this.FTUEStartLatency = FTUEStartLatency;

        // Session
        this.SessionAddTrack = SessionAddTrack;
        this.SessionGetControlState = SessionGetControlState;
        this.SessionGetIDs = SessionGetIDs;
        this.SessionRegisterCallback = SessionRegisterCallback;
        this.SessionSetAlertCallback = SessionSetAlertCallback;
        this.SessionSetControlState = SessionSetControlState;
        this.SessionSetRecordingFilename = SessionSetRecordingFilename;
        this.SessionSetRecordingFolder = SessionSetRecordingFolder;
        this.SessionStartPlay = SessionStartPlay;
        this.SessionStartRecording = SessionStartRecording;
        this.SessionStopPlay = SessionStopPlay;
        this.SessionStopRecording = SessionStopRecording;
        this.SetVURefreshRate = SetVURefreshRate;
        this.SessionGetMasterLocalMix = SessionGetMasterLocalMix;
        this.SessionSetMasterLocalMix = SessionSetMasterLocalMix;
        this.SessionGetDeviceLatency = SessionGetDeviceLatency;
        this.SessionAudioResync = SessionAudioResync;

        // Track
        this.TrackGetChannels = TrackGetChannels;
        this.TrackSetAssignment = TrackSetAssignment;
        this.TrackGetAssignment = TrackGetAssignment;
        this.TrackSetCount = TrackSetCount;
        this.TrackGetCount = TrackGetCount;
        this.TrackSave = TrackSave;
        this.TrackLoadAssignments = TrackLoadAssignments;
        this.TrackSetInstrument = TrackSetInstrument;
        this.TrackGetInstrument = TrackGetInstrument;
        this.TrackSaveAssignments = TrackSaveAssignments;

        this.TrackGetDevices = TrackGetDevices;
        this.TrackDeleteProfile = TrackDeleteProfile;
        this.TrackGetMusicDeviceID = TrackGetMusicDeviceID;
        this.TrackSetMusicDevice = TrackSetMusicDevice;
        this.TrackGetMusicDeviceNames = TrackGetMusicDeviceNames;
        this.TrackHasControlPanel = TrackHasControlPanel;
        this.TrackOpenControlPanel = TrackOpenControlPanel;
        this.TrackIsMusicDeviceType = TrackIsMusicDeviceType;

        this.TrackGetChatEnable = TrackGetChatEnable;
        this.TrackSetChatEnable = TrackSetChatEnable;

        this.TrackGetChatUsesMusic = TrackGetChatUsesMusic;
        this.TrackSetChatUsesMusic = TrackSetChatUsesMusic;

        // Client Update
        this.ClientUpdateVersion = ClientUpdateVersion;
        this.ClientUpdateStartDownload = ClientUpdateStartDownload;
        this.ClientUpdateStartUpdate = ClientUpdateStartUpdate;

        this.OpenSystemBrowser = OpenSystemBrowser;
    };

 })(window,jQuery);