Source: webphone.js

/*
* Javascript API
* Ozeki Phone System XE
* Copyright (c) 2013 Ozeki Informatics Ltd. (info@ozekiphone.com)
* Visit http://ozekiphone.com/javascript-api-806.html for more information.
*/

/******************************************/
/* Enums                                   /
/******************************************/

/**
* Enum for call types
* @public
* @readonly
* @enum {string}
*/
var CallType = {
    TEXT: "TEXT",
    AUDIO: "AUDIO",
    VIDEO: "VIDEO"
};

/**
* Enum for connection states
* @public
* @readonly
* @enum {string}
*/
var ConnectionState = {
    CONNECTION_CLOSED: "CONNECTION_CLOSED",
    CONNECTION_FAILED: "CONNECTION_FAILED",
    ACCESS_GRANTED: "ACCESS_GRANTED",
    ACCESS_DENIED: "ACCESS_DENIED"
};

/**
* Enum for call states
* @public
* @readonly
* @enum {string}
*/
var CallState = {
    RINGING: "RINGING",
    NOT_FOUND: "NOT_FOUND",
    REJECTED: "REJECTED",
    HOLD: "HOLD",
    IN_CALL: "IN_CALL",
    COMPLETED: "COMPLETED",
    BUSY: "BUSY"
};

/**
* Enum for session states
* @public
* @readonly
* @enum {string}
*/
var SessionState = {
    CREATED: 'Created',
    SETUP: 'Setup',
    TRANSFERSETUP: 'TransferSetup',
    RINGING: 'Ringing',
    INCALL: 'InCall',
    CALLEEONHOLD: 'CalleeOnHold',
    CALLERONHOLD: 'CallerOnHold',
    ONHOLD: 'OnHold',
    ONHOLDINACTIVE: 'OnHoldInactive',
    TRANSFERREQUESTED: 'TransferRequested',
    TRANSFERRING: 'Transferring',
    TRANSFERCOMPLETED: 'TransferCompleted',
    TRANSFERFAILED: 'TransferFailed',
    CALLERHUNGUP: 'CallerHungUp',
    CALLEEHUNGUP: 'CalleeHungUp',
    REDIRECTED: 'Redirected',
    NOTFOUND: 'NotFound',
    BUSY: 'Busy',
    CANCELLED: 'Cancelled',
    NOTANSWERED: 'NotAnswered',
    ERROR: 'Error',
    ABORTED: 'Aborted',
};

/**
* Enum for extension types
* @public
* @readonly
* @enum {string}
*/
var ExtensionType = {
    SIPEXTENSION: "SIP extension",
    CALLQUEUE: "Call queue",
    RINGGROUP: "Ring group",
    SMSEXTENSION: "SMS extension",
    WEBPHONEEXTENSION: "Webphone extension",
    OZMLEXTENSION: "OzML extension",
    APIEXTENSION: "API extension",
    DIALEREXTENSION: "Dialer extension",
    SQLSMS: "SQL SMS",
    VOICEMAIL: "Voicemail",
    CONFERENCEROOM: "Conference room",
    SQLOZML: "SQL OzML",
    ECHOSOUNDTEST: "Echo / sound test",
    EXELAUNCHER: "Exe launcher"
};


/**
* Enum for outside line types
* @public
* @readonly
* @enum {string}
*/
var OutsideLineType = {
    VOIPPROVIDER: "VoIP provider",
    ISDNPHONELINE: "ISDN phone line",
    PSTNDEVICE: "PSTN device",
    EMAILPROVIDER: "Email provider",
    SMPPCONNECTION: "SMPP connection",
    SMSMODEM: "SMS Modem",
    WEBPHONE: "Webphone"
};

/******************************************/
/* Classes                                 /
/******************************************/

/**
* #USER
* User class
* @public
* @class
* @classdesc The User class represents a user configured in Ozeki Phone System XE. The user class contains the user's name, phone number
* , email address and other user related information.
*/
function User(user) {

    /**
    * The username of the user.
    * @public
    * @member {string}
    */
    this.username = user.username;

    /**
    * The full name of the user.
    * @public
    * @member {string}
    */
    this.fullName = user.fullName;

    /**
    * The phone number of the user.
    * @public
    * @member {string}
    */
    this.phoneNumber = user.phoneNumber;

    /**
    * The mobile number of the user.
    * @public
    * @member {string}
    */
    this.mobileNumber = user.mobileNumber;

    /**
    * The email address of the user.
    * @public
    * @member {string}
    */
    this.emailAddress = user.emailAddress;

    /**
    * The devices of the user
    * @public
    * @member {Array.<string>}
    */
    this.devices = user.devices;
}

/**
* #EXTENSION
* Extension class
* @public
* @class
* @classdesc The Extension class represents an extension (or outside line) in the system. The Extension class provides basic information
* about the extension configured in Ozeki Phone System XE.
*/
function Extension(wClient, extension) {

    /**
    * @private
    * @type {WebClient}
    */
    var webclient = wClient;
    
    /**
    * @private
    * @type {function}
    */
    var extensionStatusReceivedCallback = null;

    /**
    * The ID of the extension.
    * @public
    * @member {string}
    */
    this.extensionId = extension.extensionId;

    /**
    * The name of the extension.
    * @public
    * @member {string}
    */
    this.extensionName = extension.extensionName;

    /**
    * The type of the extension (e.g SIP Extension, ring group, etc.).
    * @public
    * @member {ExtensionType|OutsideLineType}
    */
    this.extensionType = extension.extensionType;

    /**
    * The connection type of the extension (e.g extension or outside line)
    * @public
    * @member {string}
    */
    this.extensionConnectionType = extension.extensionConnectionType;

    /**
    * The current status of the extension
    * @public
    * @member {string}
    */
    this.extensionStatus = extension.extensionStatus;

    /**
    * Information of the client connected to the extension (if there is any)
    * @public
    * @member {string}
    */
    this.extensionClientInfo = extension.extensionClientInfo;

    /**
    * The callback which handles extensionStatusChanged events.
    * @callback onExtensionStatusChangedCallback
    * @param {string} extensionId - The id of the extension whose status has changed.
    * @param {string} status - The new status of the extension
    */

    /**
    * This event occurs when the status of the extension has changed.
    * @public
    * @param {onExtensionStatusChangedCallback} callback - The callback which handles the status change.
    */
    this.onExtensionStatusChanged = function (callback) {
        extensionStatusReceivedCallback = callback;
    }

    /**
    * This function is called by the WebClient when the status of the extension changes.
    * @private
    * @param {string} status - the new status of the extension.
    */
    this.notifyExtensionStatusChanged = function (status, detailedStatus) {
        this.extensionStatus = status;

        if (extensionStatusReceivedCallback)
            extensionStatusReceivedCallback(status, detailedStatus);
    }
}

/**
* #SESSION
* Session class
* @public
* @class
* @classdesc The Session class represents a session of an ongoing call in the system. The Session class provides session modification 
* functions such as hold(), unhold(), etc. which can only be used if the connected client has the required privileges.
* @see {@link http://ozekiphone.com/voip-session-class-1162.html}
*/
function Session(wClient, session) {

    /**
    * Enum for session actions
    * @private
    * @readonly
    * @enum {string}
    */
    var sessionAction = {
        HANGUP: "HANGUP",
        CLOSEWITHHANGUP: "CLOSEWITHHANGUP",
        CLOSE: "CLOSE",
        HOLD: "HOLD",
        UNHOLD: "UNHOLD",
        BLINDTRANSFER: "BLINDTRANSFER",
        FORWARD: "FORWARD",
        SENDDTMF: "SENDDTMF",
    };

    /** 
    * @private
    * @type {WebClient}
    */
    var webClient = wClient;

    /** 
    * @private
    * @type {function}
    */
    var sessionStateChangedCallback;

    /**
    * The master session ID of the session
    * @public
    * @member {string}
    */
    this.masterSessionId = session.masterSessionId;

    /**
    * The session ID of the session
    * @public
    * @member {string}
    */
    this.sessionId = session.sessionId;

    /**
    * The current state of the session
    * @public
    * @member {string}
    */
    this.sessionState = session.sessionState;

    /**
    * The start time of the session represented by a string
    * @public
    * @member {string}
    */
    this.startTime = session.startTime;

    /**
    * The talk duration of the session represented by a string
    * @public
    * @member {string}
    */
    this.talkDuration = session.talkDuration;

    /**
    * The state duration of the session represented by a string
    * @public
    * @member {string}
    */
    this.stateDuration = session.stateDuration;

    /**
    * The ID of the caller
    * @public
    * @member {string}
    */
    this.caller = session.caller;

    /**
    * The ID of the callee
    * @public
    * @member {string}
    */
    this.callee = session.callee;

    /**
    * The display name of the caller
    * @public
    * @member {string}
    */
    this.callerDisplayName = session.callerDisplayName;

    /**
    * The display name of the callee
    * @public
    * @member {string}
    */
    this.calleeDisplayName = session.calleeDisplayName;

    /**
    * The source ID of the session
    * @public
    * @member {string}
    */
    this.source = session.source;

    /**
    * The source type of the session
    * @public
    * @member {string}
    */
    this.sourceType = session.sourceType;

    /**
    * The destination ID of the session
    * @public
    * @member {string}
    */
    this.destination = session.destination;

    /**
    * The destination type of the session
    * @public
    * @member {string}
    */
    this.destinationType = session.destinationType;

    /**
    * The dialed number of the session.
    * @public
    * @member {string}
    */
    this.dialedNumber = session.dialedNumber;

    /**
    * This function is called by the WebClient class when a notifySessionStateChanged event occurs
    * @private
    * @param {string} sessionState - the new state of the session
    */
    this.notifySessionStateChanged = function (sessionState) {
        if (sessionState) {
            this.sessionState = sessionState;
            if (sessionStateChangedCallback) {
                sessionStateChangedCallback(this, sessionState);
            }
        }
    };

    /**
    * Holds both call parties. Can only be used if the connected client has the required privilege.
    * @public
    * @see {@link http://ozekiphone.com/voip-hold-1175.html}
    */
    this.hold = function () {
        webClient.modifySessionState(this.masterSessionId, sessionAction.HOLD, {});
    };

    /**
    * Unholds both call parties. Can only be used if the connected client has the required privilege.
    * @public
    * @see {@link http://ozekiphone.com/voip-unhold-1176.html}
    */
    this.unhold = function () {
        webClient.modifySessionState(this.masterSessionId, sessionAction.UNHOLD, {});
    };

    /**
    * Calls HANGUP on the session, terminating the call. Can only be used if the connected client has the required privilege.
    * @public
    * @see {@link http://ozekiphone.com/voip-hangup-1177.html}
    */
    this.hangup = function () {
        webClient.modifySessionState(this.masterSessionId, sessionAction.HANGUP, {});
    };

    /**
    * Closes the session, terminating the call. Can only be used if the connected client has the required privilege.
    * @public
    * @see {@link http://ozekiphone.com/voip-close-1180.html}
    */
    this.close = function () {
        webClient.modifySessionState(this.masterSessionId, sessionAction.CLOSE, {});
    };

    /**
    * Closes the session by calling HANGUP. Can only be used if the connected client has the required privilege.
    * @public
    * @see {@link http://ozekiphone.com/voip-closewithhangup-1181.html}
    */
    this.closeWithHangup = function () {
        webClient.modifySessionState(this.masterSessionId, sessionAction.CLOSEWITHHANGUP, {});
    };

    /**
    * Uses blind transfer to transfer the call to another party. Can only be used if the connected client has the required privilege.
    * @public
    * @param {string} transferor - The transferor number. Must be either the caller ID or the callee ID.
    * @param {string} phoneNumber - The target party.
    * @throws Will throw an exception if the transferor is not the caller ID or the callee ID, or if the phoneNumber was not specified.
    * @see {@link http://ozekiphone.com/voip-blindtransfer-1179.html}
    */
    this.blindTransfer = function (transferor, phoneNumber) {
        if (transferor != this.callee && transferor != this.caller)
            throw "The transferor must be either the callee or the caller";

        if (!phoneNumber)
            throw "The phoneNumber has to be specified";

        webClient.modifySessionState(this.masterSessionId, sessionAction.BLINDTRANSFER, { phoneNumber: phoneNumber, transferor: transferor });
    };

    /**
    * Forwards the session to another party. Can only be used if the connected client has the required privilege.
    * @public
    * @param {string} phoneNumber - The target party.
    * @throws Will throw an exception if the phoneNumber was not specified.
    * @see {@link http://ozekiphone.com/voip-forward-1178.html}
    */
    this.forward = function (phoneNumber) {
        if (!phoneNumber)
            throw "The phoneNumber has to be specified";

        webClient.modifySessionState(this.masterSessionId, sessionAction.FORWARD, { phoneNumber: phoneNumber });
    };

    /**
    * Sends a DTMF signal to one of the call parties. Can only be used if the connected client has the required privilege.
    * @public
    * @param {char} key - The DTMF signal e.g. 0, 1, 2,..., 9, *, #.
    * @param {string} phoneNumber - The call party which will receive the DTMF signal. Must be either the caller ID or the callee ID.
    * @throws Will throw an exception if the transferor is not the caller ID or the callee ID, or if the key was not specified.
    * @see {@link http://ozekiphone.com/voip-senddtmf-1182.html}
    */
    this.sendDtmf = function (key, phoneNumber) {

        if (!key)
            throw "The key has to be specified";

        if (phoneNumber != this.callee || phoneNumber != this.caller)
            throw "The phoneNumber must be either the callee or the caller";

        webClient.modifySessionState(this.masterSessionId, sessionAction.SENDDTMF, { key: key, phoneNumber: phoneNumber });
    };

    /**
    * Starts a listening session e.g. lets you listen to the ongoing call. Can only be used if the connected client has the required privilege.
    * @public
    * @see {@link http://ozekiphone.com/voip-startlisten-1183.html}
    */
    this.startListen = function () {
        webClient.startListen(this.sessionId);
    };

    /**
    * Stops the current listening session. Can only be used if the connected client has the required privilege.
    * @public
    * @see {@link http://ozekiphone.com/voip-stoplisten-1184.html}
    */
    this.stopListen = function () {
        webClient.stopListen(this.sessionId);
    };


    /**
    * The callback which handles sessionStateChanged events.
    * @callback onSessionStateChangedCallback
    * @param {string} session - The changed session.
    * @param {string} sessionState - The new state of the session.
    */

    /**
    * This event occurs when the state of an active session has changed.
    * @public
    * @param {onSessionStateChangedCallback} callback - The callback which handles the session state change.
    * @see {@link http://ozekiphone.com/voip-onsessionstatechanged-1173.html}
    */
    this.onSessionStateChanged = function (callback) {
        sessionStateChangedCallback = callback;
    };
}

/**
* #CALL
* Call class
* @public
* @class
* @classdesc The Call class represents a call object in the system. The class contains methods for a wide range of call, 
* messaging and video stream functionalities. It also contains events to help messaging and monitor callstates.
* @see {@link http://ozekiphone.com/call-class-807.html}
*/
function Call(cId, wClient, oParty) {

    /** 
    * @private
    * @type {string}
    */
    var callId = cId;

    /** 
    * @private
    * @type {string}
    */
    var currentCallState;

    /** 
    * @private
    * @type {string}
    */
    var otherParty = oParty;

    /** 
    * @private
    * @type {string}
    */
    var remoteDisplayId;

    /** 
    * @private
    * @type {WebClient}
    */
    var webClient = wClient;

    /** 
    * @private
    * @type {Camera}
    */
    var camera;

    /** 
    * @private
    * @type {function}
    */
    var callStateChangedCallback;

    /** 
    * @private
    * @type {function}
    */
    var messageReceivedCallback;

    /**
    * Starts the call.
    * @public
    * @see {@link http://ozekiphone.com/start-810.html}
    */
    this.start = function () {
        OzWebClient.activeCalls[callId] = this;
        webClient.start(callId);
    };

    /**
    * Accepts a call coming to the webclient.
    * @public
    * @see {@link http://ozekiphone.com/accept-811.html}
    */
    this.accept = function () {
        OzWebClient.activeCalls[callId] = this;
        webClient.accept(callId);
    };

    /**
    * Hangs up the call.
    * @public
    * @see {@link http://ozekiphone.com/hangup-812.html}
    */
    this.hangUp = function () {
        delete OzWebClient.activeCalls[callId];
        webClient.hangUp(callId);
    };

    /**
    * Rejects a call coming to the webclient.
    * @public
    * @see {@link http://ozekiphone.com/reject-813.html}
    */
    this.reject = function () {
        webClient.reject(callId);
    };

    /**
    * Transfers the call after its picked up.
    * @public
    * @param {string} target - The target phone number.
    * @throws Will throw an exception if the target was not specified.
    * @see {@link http://ozekiphone.com/transfer-target-814.html}
    */
    this.transfer = function (target) {
        if (!target)
            throw 'the target has to be specified';

        webClient.transfer(callId, target);
    };

    /**
    * Forwards the call while not picked up.
    * @public
    * @param {string} target - The target phone number.
    * @throws Will throw an exception if the target was not specified.
    * @see {@link http://ozekiphone.com/forward-target-815.html}
    */
    this.forward = function (target) {
        if (!target)
            throw 'the target has to be specified';

        webClient.forward(callId, target);
    };

    /**
    * Holds the calls
    * @public
    * @see {@link http://ozekiphone.com/hold-816.html}
    */
    this.hold = function () {
        webClient.hold(callId);
    };

    /**
    * Unholds the calls
    * @public
    * @see {@link http://ozekiphone.com/unhold-817.html}
    */
    this.unHold = function () {
        webClient.unHold(callId);
    };

    /**
    * Sends a message to the other party.
    * @public
    * @param {string} message - The message to be sent.
    * @throws Will throw an exception if no message is specified.
    * @see {@link http://ozekiphone.com/sendmessage-818.html}
    */
    this.sendMessage = function (message) {
        if (!message)
            throw "The message has to be specified";

        webClient.sendMessage(callId, message);
    };

    /**
    * Returns the callstate of the current call.
    * @public
    * @returns {string} The current state of the call.
    * @see {@link http://ozekiphone.com/getcallstate-822.html}
    */
    this.getCallState = function () {
        return currentCallState;
    };

    /**
    * Returns the phone number of the other party.
    * @public
    * @returns {string} The phone number of the other party.
    * @see {@link http://ozekiphone.com/getotherparty-823.html}
    */
    this.getOtherParty = function () {
        return otherParty;
    };

    /**
    * Displays the video stream coming from the other party.
    * @public
    * @param {string} [displayId='local'] - The ID of the html element to where the camera stream will be shown.
    * @see {@link http://ozekiphone.com/setremotedisplay-937.html}
    */
    this.setRemoteDisplay = function (displayId) {
        if (!displayId)
            displayId = 'local';

        remoteDisplayId = displayId;
        webClient.setRemoteDisplay(callId, displayId);
        OzUtil.addVideoDisplay(displayId, 320, 240);
    };

    /**
    * Attaches the video stream of a camera to the call.
    * @public
    * @param {Camera} cam - The Camera object to be attached.
    * @see {@link http://ozekiphone.com/attachcamera-938.html}
    */
    this.attachCamera = function (cam) {
        if (!cam)
            throw 'The cam has to be specified';

        webClient.attachCamera(callId, cam.getId());
    };

    /**
     * This callback is used to handle message receiving.
     * @callback Call~messageReceivedCallback
     * @param {string} message - The message received.
     */

    /**
    * This event occurs when a message is received.
    * @public
    * @param {Call~messageReceivedCallback} callback - the callback function to handle the message received.
    * @see {@link http://ozekiphone.com/onmessagereceived-821.html}
    */
    this.onMessageReceived = function (callback) {
        messageReceivedCallback = callback;
    };

    /**
     * This callback is used to handle call state changes.
     * @callback Call~callStateChangedCallback
     * @param {CallState} state - The state of the call.
     */

    /**
    * This event occurs when the state of the call changes.
    * @public
    * @param {Call~callStateChangedCallback} callback - the callback function to handle the call state change event.
    * @see {@link http://ozekiphone.com/oncallstatechanged-819.html}
    */
    this.onCallStateChanged = function (callback) {
        callStateChangedCallback = callback;
    };

    /**
    * This function is called by the WebClient when a callStateChanged event occurs.
    * @private
    * @param {string} callState - The new state of the call.
    */
    this.notifyCallStateChanged = function (callState) {
        if (callStateChangedCallback)
            callStateChangedCallback(callState);

        currentCallState = callState;

        if (currentCallState == "COMPLETED")
            delete OzWebClient.activeCalls[callId];
    };

    /**
    * This function is called by the WebClient when a messageReceived event occurs.
    * @private
    * @param {string} message - The received message.
    */
    this.notifyMessageReceived = function (message) {
        if (messageReceivedCallback)
            messageReceivedCallback(message);
    };
}

/**
* #UTIL
* Util class
* @public
* @class
* @classdesc The Util class contains functions used by the WebClient, Call, Session and Camera classes.
*/
function Util() {

    /** 
    * @private
    * @type {WebClient}
    */
    var webClient;

    /** 
    * @private
    * @type {array}
    */
    var images = [];

    /** 
    * @private
    * @type {array}
    */
    var xmlHttpFactories = [
        function () { return new XMLHttpRequest() },
        function () { return new ActiveXObject("Msxml2.XMLHTTP") },
        function () { return new ActiveXObject("Msxml3.XMLHTTP") },
        function () { return new ActiveXObject("Microsoft.XMLHTTP") }
    ];

    /**
    * The function blocks until the flash client is not ready.
    * @public
    */
    this.waitForReady = function () {
        while (!OzWebClient.isReady) { }
        console.log('ready');
    };

    /**
    * The function shows the flash element in the webpage.
    * @public
    */
    this.showFlash = function () {
        var webClientWrapper = document.getElementById('WebClientWrapper');
        webClientWrapper.style.left = "50%";
    };

    /**
    * The function hides the flash element in the webpage.
    * @public
    */
    this.hideFlash = function () {
        var webClientWrapper = document.getElementById('WebClientWrapper');
        webClientWrapper.style.left = "-9999px";
    };

    /**
    * Returns the flash object in the webpage.
    * @public
    * @returns {Object} The flash object.
    */
    this.getWebClient = function () {
        if (webClient)
            return webClient;

        webClient = document.getElementById("WebClient");
        return webClient;
    };

    /**
    * The callback which handles the request callback.
    * @callback sendRequestCallback
    * @param {Object} request - The request object.
    */

    /**
    * Sends an HTTP Request to the specified url.
    * @public
    * @param {string} url - The url to send the request to.
    * @param {sendRequestCallback} callback - Function to handle the request callback.
    * @param {string} postData - The data to be send with the request.
    */
    this.sendRequest = function (url, callback, postData) {
        var req = this.createXMLHTTPObject();
        if (!req)
            return;

        var method = (postData) ? "POST" : "GET";
        req.open(method, url, true);

        if (postData)
            req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

        req.onreadystatechange = function () {
            if (req.readyState != 4)
                return;
            if (req.status != 200 && req.status != 304) {
                //alert('HTTP error ' + req.status);
                return;
            }

            callback(req);
        };

        if (req.readyState == 4)
            return;

        req.send(postData);
    };

    /**
    * Returns an XXMLHTTPObject based on the browser which runs the script.
    * @public
    * @returns {Object} An XMLHTTPObject.
    */
    this.createXMLHTTPObject = function () {
        var xmlhttp = false;
        for (var i = 0; i < xmlHttpFactories.length; i++) {
            try {
                xmlhttp = xmlHttpFactories[i]();
            } catch (e) {
                continue;
            }
            break;
        }
        return xmlhttp;
    };

    /**
    * Adds a video display to the specifind HTML element.
    * @public
    * @param {string} conatinerId - The ID of the HTML element.
    * @param {number} width - The width of the display in pixels.
    * @param {number} height - The height of the display in pixels.
    */
    this.addVideoDisplay = function (containerId, width, height) {
        if (!containerId)
            return;

        var div = document.getElementById(containerId);

        if (!div)
            return;

        if (images[containerId])
            return;

        var canvas = document.createElement('canvas');
        canvas.style.width = "100%";
        canvas.style.height = "100%";
        canvas.width = width;
        canvas.height = height;
        canvas.id = containerId + "_videoDisp";

        div.appendChild(canvas);

        var ctx = canvas.getContext('2d');

        var imageObj = new Image();

        imageObj.onload = function () {
            ctx.drawImage(imageObj, 0, 0);
        };

        images[containerId] = imageObj;
    };

    /**
    * Modifies an existing video display.
    * @public
    * @param {string} conatinerId - The ID of the HTML element.
    * @param {number} width - The width of the display in pixels.
    * @param {number} height - The height of the display in pixels.
    */
    this.modifyVideoDisplay = function (containerId, width, height) {
        var id = containerId + "_videoDisp";
        var canvas = document.getElementById(id);

        if (!canvas)
            return;

        canvas.width = width;
        canvas.height = height;
    };

    /**
    * Removes an existing video display.
    * @public
    * @param {string} conatinerId - The ID of the HTML element.
    */
    this.removeVideoDisplay = function (containerId) {
        if (!containerId)
            return;

        delete images[containerId];

        var container = document.getElementById(containerId);
        if (!container)
            return;

        var videoDisp = document.getElementById(containerId + "_videoDisp");
        if (videoDisp)
            container.removeChild(videoDisp);
    };

    /**
    * Draws an image to the specified HTML element
    * @public
    * @param {string} containerId - The ID of the HTML element.
    * @param {string} data - The base64 data of the image.
    */
    this.draw = function (containerId, data) {
        var image = images[containerId];

        if (image)
            image.src = "data:image/jpeg;base64," + data;
    };
}

/**
* #WEBCLIENT
* WebClient class
* @public
* @class
* @classdesc The OzWebClient class contains methods for connecting the webclient to Ozeki Phone System XE and disconnecting it from it.
* The WebClient class is the main communication interface between Ozeki Phone System XE and Javascript.
* @see {@link http://ozekiphone.com/ozwebclient-class-808.html}
*/
function WebClient() {

    /** 
    * @private
    * @type {function}
    */
    var incomingCallCallback;

    /** 
    * @private
    * @type {function}
    */
    var connectionStateChangedCallback;

    /** 
    * @private
    * @type {function}
    */
    var clientStateChangedCallback;

    /** 
    * @private
    * @type {function}
    */
    var webphoneStateCallback;

    /** 
    * @private
    * @type {function}
    */
    var callHistoryInfoReceivedCallback;

    /**
    * @private
    * @type {function}
    */
    var extensionInfoReceivedCallback;

    /**
    * @private
    * @type {function}
    */
    var extensionRemovedCallback;

    /** 
    * @private
    * @type {function}
    */
    var sessionCreatedCallback;

    /** 
    * @private
    * @type {function}
    */
    var sessionClosedCallback;

    /** 
    * @private
    * @type {function}
    */
    var sessionStateChangedCallback;

    /** 
    * @private
    * @type {function}
    */
    var errorOccuredCallback;

    /**
    * @private
    * @type {function}
    */
    var userInfoReceivedCallback;

    /**
    * @private
    * @type {function}
    */
    var userRemovedCallback;

    /** 
    * @private
    * @type {array}
    */
    var callbacks = [];

    /** 
    * @private
    * @type {WebClient}
    */
    var thisClass = this;

    /**
    * Tells whether the flash object and the WebClient is ready.
    * @public
    * @member {boolean}
    */
    this.isReady = false;

    /**
    * A Helper class instance.
    * @public
    * @member {Helper}
    */
    this.helper = new Helper(this);

    /**
    * Contains the active calls the WebClient currently handles.
    * @public
    * @member {Object.<string, Call>}
    */
    this.activeCalls = {};

    /**
    * Contains the active sessions in Ozeki Phone System XE.
    * @public
    * @member {Object.<string, Session>}
    */
    this.activeSessions = {};

    /**
    * Contains information about the extensions and outside lines configured in Ozeki Phone System XE.
    * @public
    * @member {Object.<string, Extension>}
    */
    this.extensions = {};

    /**
    * Contains information about the users configured in Ozeki Phone System XE.
    * @public
    * @member {Object.<string, User>}
    */
    this.users = {};

    /**
    * Connects the WebClient to Ozeki Phone System XE.
    * @public
    * @param {string} serverAddress - The IP Address of the computer running Ozeki Phone System XE.
    * @param {string} token - The securityToken generated for this WebClient.
    * @param {Object} [properties={}] - Additional connection properties.
    * @see {@link http://ozekiphone.com/ozwebclient-connect-1013.html}
    */
    this.connect = function (serverAddress, token, properties) {
        callbacks = [];
        this.activeCalls = {};
        this.activeSessions = {};

        if (serverAddress.indexOf(":") == -1)
            serverAddress += ":7779"; //default mediagateway port;

        if (this.isReady)
            OzUtil.getWebClient().connect(serverAddress, token, properties);
        else
            callbacks.push(function () {
                OzUtil.getWebClient().connect(serverAddress, token, properties);
            });
    };

    /**
    * Disconnects the WebClient from Ozeki Phone System XE
    * @public
    * @see {@link http://ozekiphone.com/ozwebclient-disconnect-1015.html}
    */
    this.disconnect = function () {
        if (this.isReady)
            OzUtil.getWebClient().disconnect();
    };

    /**
    * The callback which handles the response initated from checkWebphoneState.
    * @callback checkWebPhoneStateCallback
    * @param {string} state - The state of the webphone.
    */

    /**
    * Returns the state of the specified webphone.
    * @public
    * @param {string} serverAddress - The IP Address of the computer running Ozeki Phone System XE.
    * @param {string} webphoneId - The ID of the webphone.
    * @param {checkWebPhoneStateCallback} - The callback which handles the response initated from checkWebphoneState.
    * @see {@link http://ozekiphone.com/ozwebclient-checkwebphonestate-1016.html}
    */
    this.checkWebphoneState = function (serverAddress, webphoneId, callback) {
        if (serverAddress.indexOf(":") == -1)
            serverAddress += ":7777"; //default gui port;

        OzUtil.sendRequest("http://" + serverAddress + "/WebphoneVisitor/CheckWebphoneState?webphoneId=" + webphoneId, function (resp) { callback(JSON.parse(resp.responseText)); });
    };

    /**
    * Returns a new Call object with the specified target phone number.
    * @public
    * @param {string} dialedNumber - The target phone number.
    * @param {CallType} [callType=CallType.AUDIO] - The type of the call.
    * @returns {Call} A new Call object
    * @see {@link http://ozekiphone.com/ozwebclient-createcall-1017.html}
    */
    this.createCall = function (dialedNumber, callType) {
        if (!this.isReady)
            return null;

        if (!dialedNumber)
            throw 'The dialedNumber can not be null.';

        var callId = OzUtil.getWebClient().createCall(dialedNumber, callType);
        return new Call(callId, OzUtil.getWebClient(), dialedNumber);
    };

    /**
    * Creates a call between two extensions configured in Ozeki Phone System XE.
    * @public
    * @param {string} caller - The phone number of the caller.
    * @param {string} callee - the phone number of the callee.
    * @throws Will throw an exception if the caller of callee parameters are not defined.
    */
    this.setupCall = function (caller, callee) {
        if (caller == null || caller == "" || callee == "" || callee == null) {
            throw 'The caller and callee parameters must be specified';
        }

        OzUtil.getWebClient().setupCall(caller, callee);
    };

    /**
    * Initiates a call history query request to Ozeki Phone System XE. Can only be used if the connected client has the required privilege.
    * The WebClient will fire the event onCallHistoryInfoReceived once the query result is received.
    * @public
    * @param {Object} filters - The filter properties. Possible filter properties: 
    *                           pageNumber {number} (paginate option: retrieve the entries on the specified page)
    *                           pageRows {number} (paginate option: retrieve the specified number of entries in one page)
    *                           general {string} (general search query string)
    *                           callerId {string}
    *                           source {string}
    *                           dialedNumber {string}
    *                           destination {string}
    *                           sessionState {SessionState}
    * @see {@link http://ozekiphone.com/voip-getcallhistory-1163.html}
    */
    this.getCallHistory = function (filters) {
        if (!this.isReady)
            return;

        OzUtil.getWebClient().getCallHistory(filters);
    };

    /**
    * The callback which handles errorOccured events.
    * @callback onErrorOccuredCallback
    * @param {string} error - The error received.
    */

    /**
    * This event occurs if an error message has been received from Ozeki Phone System XE.
    * @public
    * @param {onErrorOccuredCallback} callback - The callback which handles the error message.
    * @see {@link http://ozekiphone.com/voip-onerroroccured-1169.html}
    */
    this.onErrorOccured = function (callback) {
        errorOccuredCallback = callback;
    };

    /**
    * The callback which handles sessionCreated events.
    * @callback onSessionCreatedCallback
    * @param {Session} session - The created session.
    */

    /**
    * This event occurs if a new Session has been created e.g. a new session has started in Ozeki Phone System XE.
    * @public
    * @param {onSessionCreatedCallback} callback - The callback which receives the created session.
    * @see {@link http://ozekiphone.com/voip-onsessioncreated-1170.html}
    */
    this.onSessionCreated = function (callback) {
        sessionCreatedCallback = callback;
    };

    /**
    * The callback which handles sessionClosed events.
    * @callback onSessionClosedCallback
    * @param {string} sessionId - The ID of the closed session.
    */

    /**
    * This event occurs when an active session has been closed.
    * @public
    * @param {onSessionClosedCallback} callback - The callback which handles the session closed event.
    * @see {@link http://ozekiphone.com/voip-onsessionclosed-1171.html}
    */
    this.onSessionClosed = function (callback) {
        sessionClosedCallback = callback;
    };

    /**
    * The callback which handles callHistoryInfoReceived events.
    * @callback onCallHistoryReceivedCallback
    * @param {Object.<Object>} info - Object containing call history records.
    */

    /**
    * This event occurs when the result of the query initiated from the getCallHistory function has been received.
    * @public
    * @param {onCallHistoryReceivedCallback} callback - The callback which receives the call history records.
    * @see {@link http://ozekiphone.com/voip-oncallhistoryinforeceived-1164.html}
    */
    this.onCallHistoryInfoReceived = function (callback) {
        callHistoryInfoReceivedCallback = callback;
    };

    /**
    * The callback which handles userInfoReceived events.
    * @callback onUserInfoReceivedCallback
    * @param {string} username - The username of the user.
    * @param {User} user - The User class instance.
    */

    /**
    * This event occurs when the WebClient receives information about a user.
    * @public
    * @param {onUserInfoReceivedCallback} callback - The callback which receives the created User class instance.
    */
    this.onUserInfoReceived = function (callback) {
        userInfoReceivedCallback = callback;
    };

    /**
    * The callback which handles userRemoved events.
    * @callback onUserRemovedCallback
    * @param {string} username - The username of the removed user.
    */

    /**
    * This event occurs when the WebClient receives information about a user being removed from Ozeki Phone Sytem XE.
    * @public
    * @param {onUserRemovedCallback} callback - The callback which handles the user removal.
    */
    this.onUserRemoved = function (callback) {
        userRemovedCallback = callback;
    };

    /**
    * The callback which handles extensionInfoReceived events.
    * @callback onExtensionInfoReceivedCallback
    * @param {Extension} extension - The extension class instance created from the info of the extension.
    */

    /**
    * This event occurs when the WebClient receives information about an extension.
    * @public
    * @param {onExtensionInfoReceivedCallback} callback - The callback which receives the created Extension class instance.
    */
    this.onExtensionInfoReceived = function (callback) {
        extensionInfoReceivedCallback = callback;
    };

    /**
    * The callback which handles extensionRemoved events.
    * @callback onExtensionRemovedCallback
    * @param {string} extensionId - The ID of the extension which has been removed.
    */

    /**
    * This event occurs when the WebClient receives information about an extension which has been removed.
    * @public
    * @param {onExtensionRemovedCallback} callback - The callback which receives the ID of the removed extension.
    */
    this.onExtensionRemoved = function (callback) {
        extensionRemovedCallback = callback;
    };

    /**
    * The callback which handles incomingCall events.
    * @callback onIncomingCallCallback
    * @param {Call} call - The Call object created for the incoming call.
    */

    /**
    * This event occurs when the WebClient receives an incoming call.
    * @public
    * @param {onIncomingCallCallback} callback - The callback which receives the created Call object.
    * @see {@link http://ozekiphone.com/onincomingcall-1018.html}
    */
    this.onIncomingCall = function (callback) {
        incomingCallCallback = callback;
    };

    /**
    * The callback which handles connectionStateChanged events.
    * @callback onConnectionStateChangedCallback
    * @param {ConnectionState} connectionState - The new state of the connection.
    */

    /**
    * This event occurs when the state of the connection changes.
    * @public
    * @param {onConnectionStateChangedCallback} callback - The callback which handles the connectionStateChanged event.
    * @see {@link http://ozekiphone.com/voip-onconstatechanged-1019.html}
    */
    this.onConnectionStateChanged = function (callback) {
        connectionStateChangedCallback = callback;
    };

    /**
    * The callback which handles the webphoneStateReceived events.
    * @callback onWebphoneStateReceivedCallback
    * @param {string} webphoneState - The state of the webphone.
    */

    /**
    * This event occurs when the state of the webphone is received.
    * @public
    * @param {onWebphoneStateReceivedCallback} callback - The callback which handles the webphoneStateReceived event.
    */
    this.onWebphoneStateReceived = function (callback) {
        webphoneStateCallback = callback;
    };

    /**
    * The callback which handles the clientStateChanged events.
    * @callback onClientStateChangedCallback
    * @param {string} clientState - The new state of the client.
    */

    /**
    * This event occurs when the state of the client changes.
    * @public
    * @param {onClientStateChangedCallback} callback - The callback which handles the clientStateChanged event.
    * @see {@link http://ozekiphone.com/voip-onclientstatechanged-1014.html}
    */
    this.onClientStateChanged = function (callback) {
        clientStateChangedCallback = callback;
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a callHistoryInfoReceived event.
    * @private
    * @param {Object.<Object>} info - Object containing call history records.
    */
    this.notifyCallHistoryInfoReceived = function (info) {
        if (callHistoryInfoReceivedCallback)
            callHistoryInfoReceivedCallback(info);
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a extensionInfoReceived event.
    * @private
    * @param {Object} extensionInfo - Object containing information about the extension.
    */
    this.notifyExtensionInfoReceived = function (extensionInfo) {
        var extension = new Extension(OzUtil.getWebClient(), extensionInfo);
        this.extensions[extension.extensionId] = extension;

        if (extensionInfoReceivedCallback)
            extensionInfoReceivedCallback(extension);
    };

    /**
    * This function is called by Ozeki Phone System XE when an extension has been removed from the system.
    * @private
    * @param {string} extensionId - The ID of the removed extension.
    */
    this.notifyExtensionRemoved = function (extensionId) {
        var extension = this.extensions[extensionId];

        if (extension)
            delete this.extensions[extensionId];

        if (extensionRemovedCallback)
            extensionRemovedCallback(extensionId);
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a userInfoReceived event.
    * @private
    * @param {string} username - The username of the user.
    * @param {Object} properties - Object containing information about the user.
    */
    this.notifyUserInfoReceived = function (username, properties) {
        var user = new User(properties);

        this.users[username] = user;

        if (userInfoReceivedCallback)
            userInfoReceivedCallback(username, user);
    };

    /**
    * This function is called by Ozeki Phone System XE when a user has been removed from the system.
    * @private
    * @param {string} username - The username of the removed user.
    */
    this.notifyUserRemoved = function (username) {
        var user = this.users[username];

        if (user)
            delete this.users[username];

        if (userRemovedCallback)
            userRemovedCallback(username);
    };

    /**
    * This function is called by Ozeki Phone System XE when the status of an extension changes.
    * @private
    * @param {string} extensionId - The ID of the extension.
    * @param {string} extensionStatus - The new status of the extension.
    * @param {string} extensionDetailedStatus - The detailed status of the extension.
    */
    this.notifyExtensionStatusReceived = function (extensionId, extensionStatus, extensionDetailedStatus) {
        var extension = this.extensions[extensionId];

        if (!extension)
            return;

        extension.notifyExtensionStatusChanged(extensionStatus, extensionDetailedStatus);
    };

    /**
    * This function is called by Ozeki Phone System XE to fire an errorOccured event.
    * @private
    * @param {string} error - The error message.
    */
    this.notifyErrorOccured = function (error) {
        if (errorOccuredCallback)
            errorOccuredCallback(error);
        else
            throw error;
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a sessionCreated event.
    * @private
    * @param {Object} sessionInfo - The properties of the created session.
    */
    this.notifySessionCreated = function (sessionInfo) {
        var session = new Session(OzUtil.getWebClient(), sessionInfo);

        console.log("New Session: " + session.masterSessionId);

        this.activeSessions[session.masterSessionId] = session;

        if (sessionCreatedCallback)
            sessionCreatedCallback(session);
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a sessionStateChanged event.
    * @private
    * @param {string} sessionId - The ID of the session.
    * @param {string} sessionState - The new state of the session.
    */
    this.notifySessionStateChanged = function (sessionId, sessionState) {
        var session = this.activeSessions[sessionId];

        if (session)
            session.notifySessionStateChanged(sessionState);

        if (sessionStateChangedCallback)
            sessionStateChangedCallback(sessionId, sessionState);
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a sessionClosed event.
    * @private
    * @param {string} sessionId - The ID of the closed session.
    */
    this.notifySessionClosed = function (sessionId) {
        var session = this.activeSessions[sessionId];

        if (session)
            delete this.activeSessions[sessionId];


        if (sessionClosedCallback)
            sessionClosedCallback(sessionId);
    };

    /**
    * This function is called by Ozeki Phone System XE to fire an incomingCall event.
    * @private
    * @param {string} callId - The ID of the call.
    * @param {string} otherParty - The caller ID of the other party.
    */
    this.notifyIncomingCall = function (callId, otherParty) {
        var call = new Call(callId, OzUtil.getWebClient(), otherParty);
        this.activeCalls[callId] = call;

        if (incomingCallCallback) {
            incomingCallCallback(call);
        }
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a connectionStateChanged event.
    * @private
    * @param {ConnectionState} connectionState - The new state of the connection.
    */
    this.notifyConnectionStateChanged = function (connectionState) {
        console.log('notify connection state changed: ' + connectionState.State);

        if (connectionStateChangedCallback)
            connectionStateChangedCallback(connectionState);

        if (connectionState.State == ConnectionState.ACCESS_GRANTED) {
            getActiveSessions();
            getExtensionInfos();
            getUserInfos();
        }
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a callStateChanged event.
    * @private
    * @param {string} callId - The ID of the call.
    * @param {CallState} callState - The new state of the call.
    */
    this.notifyCallStateChanged = function (callId, callState) {
        var call = this.activeCalls[callId];

        if (call)
            call.notifyCallStateChanged(callState);
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a messageReceived event.
    * @private
    * @param {string} callId - The ID of the call.
    * @param {string} message - The received message;
    */
    this.notifyMessageReceived = function (callId, message) {
        var call = this.activeCalls[callId];

        if (call)
            call.notifyMessageReceived(message);
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a clientStateChanged event.
    * @private
    * @param {string} state - The new state of the connection.
    */
    this.notifyClientStateChanged = function (state) {
        if (clientStateChangedCallback)
            clientStateChangedCallback(state);
    };

    /**
    * This function is called by Ozeki Phone System XE to notify the WebClient about its state.
    * @private
    */
    this.notifyReady = function () {
        this.isReady = true;
        console.log('ready');

        for (var i = 0; i < callbacks.length; i++)
            callbacks[i]();
    };

    /**
    * This function is called by Ozeki Phone System XE to fire a webphoneStateReceived event.
    * @private
    * @param {string} info - The state of the webphone.
    */
    this.notifyWebphoneState = function (info) {
        if (webphoneStateCallback)
            webphoneStateCallback(info);
    };

    /**
    * @private
    * @type {function}
    */
    var getActiveSessions = function () {
        OzUtil.getWebClient().getActiveSessions();
    }

    /**
    * @private
    * @type {function}
    */
    var getExtensionInfos = function () {
        OzUtil.getWebClient().getExtensionInfos();
    }

    /**
    * @private
    * @type {function}
    */
    var getUserInfos = function () {
        OzUtil.getWebClient().getUserInfos();
    }
}

/**
* #CAMERA
* Camera class
* @public
* @class
* @classdesc The Camera class represents a camera. The class contains methods for setting up your camera and checking out camera features.
* @see {@link http://ozekiphone.com/voip-ozcamera-class-809.html}
*/
function Camera(id, client) {

    /** 
    * @private
    * @type {string}
    */
    var _id = id;

    /** 
    * @private
    * @type {string}
    */
    var currentDisplayId;

    /** 
    * @private
    * @type {WebClient}
    */
    var _client = client;

    /**
    * Returns the width of the camera display in pixels.
    * @public
    * @returns {number} The width of the camera display in pixels.
    * @see {@link http://ozekiphone.com/voip-getwidth-1001.html}
    */
    this.getWidth = function () {
        return _client.getCameraWidth(_id);
    };

    /**
    * Returns the height of the camera display in pixels.
    * @public
    * @returns {number} The height of the camera display in pixels.
    * @see {@link http://ozekiphone.com/voip-getheight-1002.html}
    */
    this.getHeight = function () {
        return _client.getCameraHeight(_id);
    };

    /**
    * Returns the fps (frame per second) value of the camera display.
    * @public
    * @returns {number} The fps (frame per second) value of the camera display.
    * @see {@link http://ozekiphone.com/voip-getfps-1003.html}
    */
    this.getFps = function () {
        return _client.getCameraFps(_id);
    };

    /**
    * Returns the ID of the camera display.
    * @public
    * @returns {string} The ID of the camera display.
    * @see {@link http://ozekiphone.com/voip-getid-1168.html}
    */
    this.getId = function () {
        return _id;
    };

    /**
    * Returns the name of the camera.
    * @public
    * @returns {string} The name of the camera.
    * @see {@link http://ozekiphone.com/voip-getname-1005.html}
    */
    this.getName = function () {
        return _client.getCameraName(_id);
    };

    /**
    * Sets the width, height, and fps values of the camera display.
    * @public
    * @param {number} width - The width of the display in pixels.
    * @param {number} height - The height of the display in pixels.
    * @param {number} fps - The fps (frame per second) value of the display.
    * @see {@link http://ozekiphone.com/voip-setmode-1006.html}
    */
    this.setMode = function (width, height, fps) {
        _client.setCameraMode(_id, width, height, fps);
        OzUtil.modifyVideoDisplay(currentDisplayId, this.getWidth(), this.getHeight());
    };

    /**
    * Set up the camera display to the specified HTML element. If the displayId is empty, the existing camera display will be removed.
    * @public
    * @param {string|null} displayId - The ID of the HTML element or null. 
    * @see {@link http://ozekiphone.com/voip-setdisplay-1007.html}
    */
    this.setDisplay = function (displayId) {
        if (displayId) {
            OzUtil.removeVideoDisplay(currentDisplayId);
            currentDisplayId = displayId;
            OzUtil.addVideoDisplay(displayId, this.getWidth(), this.getHeight());
            _client.setCameraDisplay(_id, displayId);
        }
        else {
            OzUtil.removeVideoDisplay(currentDisplayId);
            _client.setCameraDisplay(_id, displayId);
        }
    };

    /**
    * Set the quality of the display.
    * @public
    * @param {number} bandwidth - The maximum amount of bandwidth that the current outgoing video feed can use, in bytes per second.
    * @param {number} [quality=0] - An integer that specifies the required level of picture quality (from 1 to 100).
    * @see {@link http://ozekiphone.com/voip-setquality-1008.html}
    */
    this.setQuality = function (bandwidth, quality) {
        _client.setCameraQuality(_id, bandwidth, quality);
    };
}

/**
* #HELPER
* Helper class
* @public
* @class
* @classdesc The helper class contains methods which lets you query out information from the OzWebclient instance.
*/
function Helper(wClient) {

    /**
    * @private
    * @type {WebClient}
    */
    var webClient = wClient;

    /**
    * Retrieves the extensions configured in Ozeki Phone System XE.
    * @public
    * @returns {Object.<string, Extension>} An object containing the retrieved extensions.
    */
    this.getExtensions = function () {
        var extensions = {};

        for (var ext in webClient.extensions) {
            if (webClient.extensions[ext].extensionConnectionType == 'Extension')
                extensions[ext] = webClient.extensions[ext];
        }

        return extensions;
    };

    /**
    * Retrieves the outside lines configured in Ozeki Phone System XE.
    * @public
    * @returns {Object.<string, Extension>} An object containing the retrieved outside lines.
    */
    this.getOutsideLines = function () {
        var outsideLines = {};

        for (var ext in webClient.extensions) {
            if (webClient.extensions[ext].extensionConnectionType == 'OutsideLine')
                outsideLines[ext] = webClient.extensions[ext];
        }

        return outsideLines;
    };

    /**
    * Retrieves the extension with the specified ID, or null if the extension does not exists.
    * @public
    * @param {string} id - The ID of the extension.
    * @returns {Extension|null} Returns an Extension class instance or null if the extension does not exists.
    */
    this.getExtensionById = function (id) {
        if (webClient.extensions[id])
            return webClient.extensions[id];

        return null;
    };

    /**
    * Retrieves the outside line with the specified ID, or null if the outside line does not exists.
    * @public
    * @param {string} id - The ID of the outside line.
    * @returns {Extension|null} Returns an Extension class instance or null if the outside line does not exists.
    */
    this.getOutsideLineById = function (id) {
        return this.getExtensionById(id);
    };

    /**
    * Retrieves the extensions whose type equals to the specified type.
    * @public
    * @param {ExtensionType} extensionType - The extension type.
    * @returns {Object.<string,Extension>} An object containing the retrieved extensions.
    */
    this.getExtensionsByType = function (extensionType) {
        var extensions = {};

        for (var ext in webClient.extensions) {
            if (webClient.extensions[ext].extensionType == extensionType)
                extensions[ext] = webClient.extensions[ext];
        }

        return extensions;
    };

    /**
    * Retrieves the outside lines whose type equals to the specified type.
    * @public
    * @param {OutsideLineType} outsideLineType - The outside line type.
    * @returns {Object.<string,Extension>} An object containing the retrieved outside lines.
    */
    this.getOutsideLinesByType = function (outsideLineType) {
        return this.getExtensionsByType(outsideLineType);
    };

    /**
    * Retrieves the user instance by the specified user ID, or null if the user does not exists.
    * @public
    * @param {string} userId - The ID of the user.
    * @returns {User|null} A User class instance or null if the user does not exists.
    */
    this.getUserById = function (userId) {
        if (webClient.users[userId])
            return webClient.users[userId];

        return null;
    };

    /**
    * Retrieves the user instance by the specified extension ID, e.g returns the user whose device list contains the specified extension.
    * Returns null if the specified extension is not related to any users.
    * @public
    * @param {string} extensionId - the ID of the specified extension.
    * @returns {User|null} A User class instance or null if the user does not exists.
    */
    this.getUserByExtension = function (extensionId) {
        for (var user in webClient.users) {
            var u = webClient.users[user];

            for (var i = 0; i < u.devices.length; i++) {
                if (u.devices[i] == extensionId)
                    return u;
            }
        }

        return null;
    }

    /**
    * Retrieves the session by the specified session ID, or null if the session does not exists.
    * @public
    * @param {string} sessionId - The ID of the session.
    * @return {Session|null} A Session class instance or null if the session does not exists.
    */
    this.getActiveSessionById = function (sessionId) {
        if (webClient.activeSessions[sessionId])
            return webClient.activeSessions[sessionId];

        return null;
    };

    /**
    * Retrieves the sessions by the specified session state.
    * @public
    * @param {SessionState} - The specified session state.
    * @returns {Object.<string, Session>} An object containing session class instances.
    */
    this.getActiveSessionsBySessionState = function (sessionState) {
        var sessions = {};

        for (var session in webClient.activeSessions) {
            var s = webClient.activeSessions[session];

            if (s.sessionState == sessionState)
                sessions[session] = s;
        }
      
        return sessions;
    };

    /**
    * Retrieves the active calls of the specified extension.
    * @public
    * @param {string} extensionId - The ID of the extension.
    * @returns {Object.<string,Session>} An object containing Session class instances.
    */
    this.getActiveCallsOfExtension = function (extensionId) {
        var sessions = {};

        for (var session in webClient.activeSessions) {
            var s = webClient.activeSessions[session];

            if (s.caller == extensionId || s.callee == extensionId)
                sessions[session] = s;
        }

        return sessions;
    };

    /**
    * Retrieves the active calls of the specified user.
    * @public
    * @param {string} userId - the ID of the user.
    * @return {Object.<string.Session>} An object containing Session class instances.
    */
    this.getActiveCallsOfUser = function (userId) {      
        var sessions = {};
        var user = this.getUserById(userId);

        if (user == null)
            return sessions;

        for (var session in webClient.activeSessions) {
            var s = webClient.activeSessions[session];

            for (var i = 0; i < user.devices.length; i++) {
                if (s.caller == user.devices[i] || s.callee == user.devices[i])
                    sessions[session] = s;
            }
        }

        return sessions;
    };

    /**
    * Initiates a getCallHistory request with the specified extension ID.
    * @public
    * @param {string} extensionId - The specified extension ID.
    * @param {Object} [properties] - The properties object containing filter parameters (same parameters apply as in OzWebClient.getCallHistory)
    * @throws Will throw an exception if the extension ID is not specified
    */
    this.getCallHistoryOfExtension = function (extensionId, properties) {
        if (!extensionId)
            throw 'The extension ID must be specified';

        if (!properties)
            properties = {};

        properties.general = extensionId;
        webClient.getCallHistory(properties);
    };
}

/******************************************/
/* Variables                               /
/******************************************/

/**
* Static Util instance
* @public
* @static
* @type {Util}
*/
var OzUtil = new Util();

/**
* Static WebClient instance
* @public
* @static
* @type {WebClient}
*/
var OzWebClient = new WebClient();

/**
* Static instance to create Camera objects
* @public
* @static
* @type {Object}
* @see {@link http://ozekiphone.com/ozcamera-class-809.html}
*/
var OzCamera = {

    /**
    * Returns the names of available cameras.
    * @public
    * @returns {Array<string>}
    */
    getNames: function () {
        OzUtil.waitForReady();
        return OzUtil.getWebClient().getCameraNames();
    },

    /**
    * Returns a new Camera object or null if the given cameraName does not exists.
    * @public
    * @param {string} cameraName - The name of the camera (use getNames to retrieve the names of available cameras).
    * @returns {?Camera} A new Camera object or null if the given cameraName does not exists.
    */
    getCameraByName: function (cameraName) {
        if (cameraName) {
            if (OzCamera.getNames().indexOf(cameraName) == -1)
                return null;
        }

        return new Camera(cameraName, OzUtil.getWebClient());
    },

    /**
    * Returns a new Camera object or null if the given index is not present in the array.
    * @public
    * @param {number} index - The index of the camera in the array returned by function getNames
    * @returns {?Camera} A new Camera object or null if the given index is not present in the array.
    */
    getCameraByIndex: function (index) {
        if (OzCamera.getNames().length - 1 < index)
            return null;

        return new Camera(index.toString(), OzUtil.getWebClient());
    },
};