Source: wtapi.queues.js

/**
 * Plugin provides a possibility to work with Queues events
 *
 * @version 1.0.0
 */
(function(WTAPI) {
    var NS_QUEUES = "urn:wildix:queues";

    /**
     * Constructor for Queues plugin.
     * Instance will be created each time when new WTAPI instance is created. <br />
     * Plugin could be accessible thought WTAPI with <b>queues</b> property.
     *
     * @memberof WTAPI
     * @alias WTAPI.QueuesPlugin
     * @augments WTAPI.Observer
     * @public
     * @constructor
     */
    function QueuesPlugin(wtapi) {
        this._queues = [];
        this._subscribed = false;
        this._wtapi = wtapi;
        this._wtapi.registerHandler("message", this._handleMessage, this, 10);
        this._wtapi.addListener("connected", this._onConnected, this);
        this._wtapi.addListener("disconnected", this._onDisconnected, this);

        this._messageHandlers = [
            this._handleMessageQueueAdd,
            this._handleMessageQueueRemove,
            this._handleMessageMemberAdd,
            this._handleMessageMemberUpdate,
            this._handleMessageMemberRemove,
            this._handleMessageMemberCallAdd,
            this._handleMessageMemberCallRemove,
            this._handleMessageUserJoin,
            this._handleMessageUserLeave
        ];

        // Initialize supper
        WTAPI.Observable.call(this);
    }

    QueuesPlugin.prototype = Object.create(WTAPI.Observable.prototype);

    // ============================================================
    // Public events
    // ============================================================

    // ============================================================
    // Public functions
    // ============================================================


    /**
     * @callback QueueActionCallback
     * @param {string} error An error message, null when operation is successful
     */

    /**
     * Subscribe to a queue events
     *
     * @param callback
     */
    QueuesPlugin.prototype.subscribe = function(callback) {
        if (this._subscribed) {
            return;
        }

        this._subscribed = true;

        var scope = arguments[1] || this;
        var self  = this;
        var packet = this._createSubscribePacket();
        var handlers = {
            result_handler: function(iq) {
                self._handleSubscribeResponse(iq, callback, scope);
            }
        }

        this._wtapi.getConnection().sendIQ(packet, handlers);
    }

    /**
     * Checks whether WTAPI is Subscribed to Queues events
     *
     * @returns {boolean}
     */
    QueuesPlugin.prototype.isSubscribed = function() {
        return this._subscribed;
    }

    /**
     * Removes subscription from a queues events
     *
     * @param {function} callback
     */
    QueuesPlugin.prototype.unsubscribe = function(callback) {
        var scope = arguments[1] || this;
        var self  = this;
        var packet = this._createUnsubscribePacket();
        var handlers = {
            result_handler: function(iq) {
                self._handleUnsubscribeResponse(iq, callback, scope);
            }
        }

        this._wtapi.getConnection().sendIQ(packet, handlers);
    }

    /**
     * Add member to a specified queue.
     * When member will be added he will have a Dynamic membership.
     *
     * @param {Queue} queue
     * @param {String} extension
     * @param {QueueActionCallback} callback
     */
    QueuesPlugin.prototype.add = function(queue, extension, callback) {
        var scope = arguments[3] || this;
        var packet = this._createAddPacket(queue, extension);
        var handlers = this._createIqHandler(callback, scope);
        this._wtapi.getConnection().sendIQ(packet, handlers);
    }

    /**
     * Remove a Dynamic member from a queue
     *
     * @param {Queue} queue
     * @param {QueueMember} member
     * @param {QueueActionCallback} callback
     */
    QueuesPlugin.prototype.remove = function(queue, member, callback) {
        var scope = arguments[3] || this;
        var packet = this._createRemovePacket(queue, member);
        var handlers = this._createIqHandler(callback, scope);
        this._wtapi.getConnection().sendIQ(packet, handlers);
    }

    /**
     * Pause a member in queue.
     * When user is on pause, he wouldn't receive a new calls from that queue.
     *
     * @param {Queue} queue
     * @param {QueueMember} member
     * @param {QueueActionCallback} callback
     */
    QueuesPlugin.prototype.pause = function(queue, member, callback) {
        var scope = arguments[3] || this;
        var packet = this._createPausePacket(queue, member);
        var handlers = this._createIqHandler(callback, scope);
        this._wtapi.getConnection().sendIQ(packet, handlers);
    }

    /**
     * Resume a member from pause
     *
     * @param {Queue} queue
     * @param {QueueMember} member
     * @param {QueueActionCallback} callback
     */
    QueuesPlugin.prototype.resume = function(queue, member, callback) {
        var scope = arguments[3] || this;
        var packet = this._createResumePacket(queue, member);
        var handlers = this._createIqHandler(callback, scope);
        this._wtapi.getConnection().sendIQ(packet, handlers);
    }



    /**
     * Returns a list of available queues.
     *
     * @access public
     * @returns {Array}
     */
    QueuesPlugin.prototype.getQueues = function() {
        return this._queues;
    }

    /**
     * Returns a list of available queues.
     *
     * @param {String} id A Queue ID
     * @access public
     * @returns {Queue} A Queue Object or null if queue is not found.
     */
    QueuesPlugin.prototype.getQueue = function(id) {
        for (var i=0; i < this._queues.length; i++) {
            if (this._queues[i].getId() == id) {
                return this._queues[i];
            }
        }

        return null;
    }



    // ============================================================
    // Plugin callbacks
    // ============================================================

    /**
     * @private
     */
    QueuesPlugin.prototype._onConnected = function() {
        if (this._subscribed) {
            this._subscribed = false;
            this.subscribe();
        }
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._onDisconnected = function() {
        for (var i=0; i < this._queues.length; i++) {
            this._fire("queue_removed", this._queues[i]);
        }

        this._queues = [];
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessage = function(message) {
        var event = message.getChild("event", NS_QUEUES);
        if (event !== null) {
            var operation = event.getAttribute("operation");
            for (var i=0; i < this._messageHandlers.length; i++) {
                if (this._messageHandlers[i].call(this, event, operation)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessageQueueAdd = function(event, operation) {
        if (operation != 'add') {
            return false;
        }

        var els = event.getElementsByTagName("queue");
        if (els.length == 0) {
            return false;
        }

        for (var i=0; i < els.length; i++) {
            var el = els.item(i);
            var id = el.getAttribute("id");
            var name = el.getAttribute("name");
            var queue = this.getQueue(id);

            if (queue === null) {
                queue = new Queue(id, name);

                this._queues.push(queue);
                this._fire("queue_added", queue);
            }
        }

        return true;
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessageQueueRemove = function(event, operation) {
        if (operation != 'remove') {
            return false;
        }

        var els = event.getElementsByTagName("queue");
        if (els.length == 0) {
            return false;
        }

        for (var i=0; i < els.length; i++) {
            var el = els.item(i);
            var id = el.getAttribute("id");
            var queue = this.getQueue(id);

            if (queue !== null) {
                for (var q in this._queues) {
                    if (this._queues[q] == queue) {
                        this._queues.splice(q,1);
                        this._fire("queue_removed", queue);
                        break;
                    }
                }
            }
        }

        return true;
    }


    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessageMemberAdd = function(event, operation) {
        if (operation != 'add') {
            return false;
        }

        var els = event.getElementsByTagName("member");
        if (els.length == 0) {
            return false;
        }

        for (var i=0; i < els.length; i++) {
            var el = els.item(i);
            var options = this._parseMemberItem(el);
            var queue = this.getQueue(options['queue']);
            if (!queue) {
                continue;
            }

            var member = queue.getMember(options['location']);
            if (member) {
                continue;
            }

            member = QueueMember.fromObject(options);
            queue._addMember(member);

            this._fire("member_added", queue, member);
        }

        return true;
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessageMemberUpdate = function(event, operation) {
        if (operation != 'update') {
            return false;
        }

        var els = event.getElementsByTagName("member");
        if (els.length == 0) {
            return false;
        }

        for (var i=0; i < els.length; i++) {
            var el = els.item(i);
            var options = this._parseMemberItem(el);
            var queue = this.getQueue(options['queue']);
            if (!queue) {
                continue;
            }

            var member = queue.getMember(options['location']);
            if (!member) {
                continue;
            }

            member._updateFromObject(options);
            this._fire("member_updated", queue, member);
        }

        return true;
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessageMemberRemove = function(event, operation) {
        if (operation != 'remove') {
            return false;
        }

        var els = event.getElementsByTagName("member");
        if (els.length == 0) {
            return false;
        }

        for (var i=0; i < els.length; i++) {
            var el = els.item(i);
            var options = this._parseMemberItem(el);
            var queue = this.getQueue(options['queue']);
            if (!queue) {
                continue;
            }

            var member = queue.getMember(options['location']);
            if (!member) {
                continue;
            }

            queue._removeMember(member);
            this._fire("member_removed", queue, member);
        }

        return true;
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessageMemberCallAdd = function(event, operation) {
        if (operation != 'add') {
            return false;
        }

        var els = event.getElementsByTagName("call");
        if (els.length == 0) {
            return false;
        }

        for (var i=0; i < els.length; i++) {
            var el = els.item(i);
            var options = this._parseMemberCallItem(el);
            var queue = this.getQueue(options['queue']);
            if (!queue) {
                continue;
            }

            var member = queue.getMember(options['location']);
            if (!member) {
                continue;
            }

            var call = QueueCall.fromObject(options);
            member._addCall(call);

            this._fire("call_added", queue, member, call);
        }

        return true;
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessageMemberCallRemove = function(event, operation) {
        if (operation != 'remove') {
            return false;
        }

        var els = event.getElementsByTagName("call");
        if (els.length == 0) {
            return false;
        }

        for (var i=0; i < els.length; i++) {
            var el = els.item(i);
            var options = this._parseMemberCallItem(el);
            var queue = this.getQueue(options['queue']);
            if (!queue) {
                continue;
            }

            var member = queue.getMember(options['location']);
            if (!member) {
                continue;
            }


            var call = member.getCall(options['channel']);
            if (!call) {
                continue;
            }

            member._removeCall(call);
            this._fire("call_removed", queue, member, call);
        }

        return true;
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessageUserJoin = function(event, operation) {
        if (operation != 'join') {
            return false;
        }

        var els = event.getElementsByTagName("user");
        if (els.length == 0) {
            return false;
        }

        for (var i=0; i < els.length; i++) {
            var el = els.item(i);
            var options = this._parseUserItem(el);
            var queue = this.getQueue(options['queue']);
            if (!queue) {
                continue;
            }

            var user = queue.getMember(options['channel']);
            if (user) {
                continue;
            }

            user = QueueUser.fromObject(options);
            queue._addUser(user);

            this._fire("user_join", queue, user);
        }

        return true;
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleMessageUserLeave = function(event, operation) {
        if (operation != 'leave') {
            return false;
        }

        var els = event.getElementsByTagName("user");
        if (els.length == 0) {
            return false;
        }

        for (var i=0; i < els.length; i++) {
            var el = els.item(i);
            var options = this._parseUserItem(el);
            var queue = this.getQueue(options['queue']);
            if (!queue) {
                continue;
            }

            var user = queue.getUser(options['channel']);
            if (!user) {
                continue;
            }

            queue._removeUser(user);
            this._fire("user_left", queue, user);
        }

        return true;
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleSubscribeResponse = function(iq, callback, scope) {
        this._queues = this._parseSubscribeResponse(iq);

        for (var i=0; i < this._queues.length; i++) {
            this._fire("queue_added", this._queues[i]);
        }

        if (typeof(callback) == 'function') {
            callback.call(scope, this._queues);
        }
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._handleUnsubscribeResponse = function(iq, callback, scope) {
        for (var i=0; i < this._queues.length; i++) {
            this._fire("queue_removed", this._queues[i]);
        }

        this._queues = [];
        this._subscribed = false;

        if (typeof(callback) == 'function') {
            callback.call(scope);
        }
    }

    /**
     * @private
     */
    QueuesPlugin.prototype._createIqHandler = function(callback, scope) {
        return {
            error_handler: function(iq) {
                var node = iq.getNode();
                var errors = node.getElementsByTagName("error");
                var reason = "unknown";

                if (errors.length > 0) {
                    var error = errors[0];
                    var type = error.firstChild;
                    if (type) {
                        reason = type.tagName;
                    }
                }

                callback.call(scope, reason);
            },
            result_handler: function() {
                callback.call(scope, null);
            }
        }
    }

    // ============================================================
    // Private functions
    // ============================================================

    /**
     * @private
     */
    QueuesPlugin.prototype._createSubscribePacket = function() {
	 	var packet = new JSJaCIQ().setType('set');
	 	var query = packet.setQuery(NS_QUEUES);
	 	var subscribe  = packet.getDoc().createElement('subscribe');
	 	query.appendChild(subscribe);

	 	return packet;
    };

    /**
     * @private
     */
    QueuesPlugin.prototype._createUnsubscribePacket = function() {
	 	var packet = new JSJaCIQ().setType('set');
	 	var query = packet.setQuery(NS_QUEUES);
	 	var unsubscribe  = packet.getDoc().createElement('unsubscribe');
	 	query.appendChild(unsubscribe);

	 	return packet;
    };

    /**
     * @private
     */
    QueuesPlugin.prototype._createAddPacket = function(queue, extension) {
	 	var packet = new JSJaCIQ().setType('set');
	 	var query = packet.setQuery(NS_QUEUES);
	 	var add = packet.getDoc().createElement('add');
        add.setAttribute('queue', queue.getId());
        add.setAttribute('extension', extension);
	 	query.appendChild(add);

	 	return packet;
    };

    /**
     * @private
     */
    QueuesPlugin.prototype._createRemovePacket = function(queue, member) {
	 	var packet = new JSJaCIQ().setType('set');
	 	var query = packet.setQuery(NS_QUEUES);
	 	var remove = packet.getDoc().createElement('remove');
        remove.setAttribute('queue', queue.getId());
        remove.setAttribute('location', member.getLocation());
	 	query.appendChild(remove);

	 	return packet;
    };

    /**
     * @private
     */
    QueuesPlugin.prototype._createPausePacket = function(queue, member) {
	 	var packet = new JSJaCIQ().setType('set');
	 	var query = packet.setQuery(NS_QUEUES);
	 	var pause = packet.getDoc().createElement('pause');
        pause.setAttribute('queue', queue.getId());
        pause.setAttribute('location', member.getLocation());
	 	query.appendChild(pause);

	 	return packet;
    };

    /**
     * @private
     */
    QueuesPlugin.prototype._createResumePacket = function(queue, member) {
	 	var packet = new JSJaCIQ().setType('set');
	 	var query = packet.setQuery(NS_QUEUES);
	 	var pause = packet.getDoc().createElement('unpause');
        pause.setAttribute('queue', queue.getId());
        pause.setAttribute('location', member.getLocation());
	 	query.appendChild(pause);

	 	return packet;
    };


    /**
     *
     * @param event
     * @returns {Array}
     * @private
     */
    QueuesPlugin.prototype._parseSubscribeResponse = function(iq) {
        var root = iq.getChild("queues", NS_QUEUES);
        var queueEls = root.getElementsByTagName("queue");
        var queues = [];

        for (var i=0; i < queueEls.length; i++) {
            var queueEl = queueEls.item(i);
            var queueId = queueEl.getAttribute("id");
            var queueName = queueEl.getAttribute("name");
            var queue = new Queue(queueId, queueName);
            var memberEls = queueEl.getElementsByTagName("member");
            var userEls = queueEl.getElementsByTagName("user");

            for (var c=0; c < memberEls.length; c++) {
                var memberEl = memberEls.item(c);
                var member = QueueMember.fromObject(this._parseMemberItem(memberEl));

                var callEls = memberEl.getElementsByTagName("call");
                for (var s=0; s < callEls.length; s++) {
                    var call = QueueCall.fromObject(this._parseMemberCallItem(callEls[s]));

                    member._addCall(call);
                }

                queue._addMember(member);
            }

            for (var c=0; c < userEls.length; c++) {
                var userEl = userEls.item(c);
                var user = QueueUser.fromObject(this._parseUserItem(userEl));
                queue._addUser(user);
            }

            queues.push(queue);
        }

        return queues;
    };

    /**
     * @param {DOMElement} element
     * @returns {{queue: *, location: *, extension: *, membership: *, penalty: *, status: *, pause: *}}
     * @private
     */
    QueuesPlugin.prototype._parseMemberItem = function(element) {
        return {
            queue: element.getAttribute('queue'),
            location: element.getAttribute('location'),
            extension: element.getAttribute('extension'),
            name: element.getAttribute('name'),
            membership: element.getAttribute('membership'),
            penalty: element.getAttribute('penalty'),
            status: element.getAttribute('status'),
            pause: element.getAttribute('pause'),
            sleep: element.getAttribute('sleep')
        }
    };

    /**
     * @param {DOMElement} element
     * @returns {{queue: *, location: *, channel: *, destination: *, calleeNumber: *, calleeName: *, duration: *}}
     * @private
     */
    QueuesPlugin.prototype._parseMemberCallItem = function(element) {
        return {
            queue: element.getAttribute('queue'),
            location: element.getAttribute('location'),
            channel: element.getAttribute('channel'),
            destination: element.getAttribute('destination'),
            calleeNumber: element.getAttribute('connected_line_num'),
            calleeName: element.getAttribute('connected_line_name'),
            duration: element.getAttribute('duration')
        }
    };

    /**
     * @param {DOMElement} element
     * @returns {{queue: *, channel: *, num: *, name: *, duration: *}}
     * @private
     */
    QueuesPlugin.prototype._parseUserItem = function(element) {
        return {
            queue: element.getAttribute('queue'),
            channel: element.getAttribute('channel'),
            num: element.getAttribute('num'),
            name: element.getAttribute('name'),
            duration: element.getAttribute('duration')
        }
    };

    // ============================================================
    // Extra classes
    // ============================================================

    /**
     * @memberof WTAPI.QueuesPlugin
     * @alias WTAPI.QueuesPlugin.Queue
     * @public
     * @constructor
     */
    function Queue(id, name) {
        this._members = [];
        this._users = [];

        /**
         * Returns unique id of current Queue
         *
         * @public
         * @returns {string}
         */
        this.getId = function() {
            return id;
        }

        /**
         * Returns name of current Queue
         *
         * @public
         * @returns {string}
         */
        this.getName = function() {
            return name;
        }
    }

    /**
     * Gets a queue unique identity
     *
     * @returns {String}
     */
    Queue.prototype.getId = function() {
        return this._id;
    }

    /**
     * Gets a member by location
     *
     * @param location
     * @returns {QueueMember}
     */
    Queue.prototype.getMember = function(location) {
        for (var i=0; i < this._members.length; i++) {
            if (this._members[i].getLocation() == location) {
                return this._members[i];
            }
        }

        return null;
    }

    /**
     * Gets a list of members (agents) that are in queue
     *
     * @returns {Array}
     */
    Queue.prototype.getMembers = function() {
        return this._members;
    }

    /**
     * @private
     */
    Queue.prototype._addMember = function(member) {
        this._members.push(member);
    }

    /**
     * @private
     */
    Queue.prototype._removeMember = function(member) {
        for(var i in this._members){
            if(this._members[i] == member) {
                this._members.splice(i,1);
                break;
            }
        }
    }

    /**
     * Gets User using his channel identity
     *
     * @param channel
     * @returns {QueueUser}
     */
    Queue.prototype.getUser = function(channel) {
        for (var i=0; i < this._users.length; i++) {
            if (this._users[i].getChannel() == channel) {
                return this._users[i];
            }
        }

        return null;
    }

    /**
     * Gets users that a calling to queue
     *
     * @returns {Array}
     */
    Queue.prototype.getUsers = function() {
        return this._users;
    }

    /**
     * @private
     */
    Queue.prototype._addUser = function(user) {
        this._users.push(user);
    }

    /**
     * @private
     */
    Queue.prototype._removeUser = function(user) {
        for (var i in this._users){
            if (this._users[i] == user) {
                this._users.splice(i,1);
                break;
            }
        }
    }

    /**
     * @memberof WTAPI.QueuesPlugin
     * @alias WTAPI.QueuesPlugin.QueueMember
     * @public
     * @constructor
     */
    function QueueMember(location, extension, name, membership, penalty, status, pause, sleep) {
        this._location = location;
        this._extension = extension;
        this._name = name;
        this._membership = membership;
        this._penalty = penalty;
        this._status = status;
        this._pause = pause;
        this._sleep = parseInt(sleep);
        this._sleepSetTime = Math.round(new Date().getTime() / 1000);
        this._calls = [];
    }

    /**
     * @private
     * @returns {QueueMember}
     */
    QueueMember.fromObject = function(o) {
        return new QueueMember(o.location, o.extension, o.name, o.membership, o.penalty, o.status, o.pause, o.sleep);
    }

    /**
     * Gets a unique identifier of a Member that are on Queue
     *
     * @returns {String}
     */
    QueueMember.prototype.getLocation = function() {
        return this._location;
    }

    /**
     * Gets an extension of Member (e.g. 100, 200, etc...)
     *
     * @returns {String}
     */
    QueueMember.prototype.getExtension = function() {
        return this._extension;
    }

    /**
     * Gets a Member name
     *
     * @returns {String}
     */
    QueueMember.prototype.getName = function() {
        return this._name;
    }

    /**
     * Checks whether Member is statically added to Queue (e.g. can't be removed by this API)
     *
     * @returns {boolean}
     */
    QueueMember.prototype.isStaticMember = function() {
        return (this._membership == 'static');
    }

    /**
     * Checks whether Member is dynamically added to Queue
     *
     * @returns {boolean}
     */
    QueueMember.prototype.isDynamicMember = function() {
        return (this._membership == 'dynamic');
    }

    /**
     * Gets a Member penalty in Queue.
     * If the strategy of Queue is defined as 'ringall', then only those available
     * members with the lowest priorities will ring.
     *
     * @returns {string}
     */
    QueueMember.prototype.getPenalty = function() {
        return this._penalty;
    }

    /**
     * Gets a Member status in Queue
     * Possible statuses are: not_inuse, inuse, busy, invalid, unavailable, ringing, ringinuse, onhold, unknown
     *
     * @returns {String}
     */
    QueueMember.prototype.getStatus = function() {
        return this._status;
    }

    /**
     * Checks whether Member is paused in that Queue
     *
     * @returns {boolean}
     */
    QueueMember.prototype.isPaused = function() {
        return (this._pause == "true");
    }


    /**
     * Gets a sleep time in seconds (e.g. time from a last call)
     * @returns {number}
     */
    QueueMember.prototype.getSleepTime = function() {
        var time = Math.round(new Date().getTime() / 1000);
        var offset = time - this._sleepSetTime;
        return this._sleep + offset;
    }

    /**
     * Gets a Talk time in seconds
     *
     * @returns {integer}
     */
    QueueMember.prototype.getTalkTime = function() {
        if (this._calls.length > 0) {
            var call = this._calls[0];
            return call.getDuration();
        }

        return 0;
    }

    /**
     * Gets an member active call by channel
     *
     * @param {String} channel
     * @returns {QueueCall|null}
     */
    QueueMember.prototype.getCall = function(channel) {
        for (var i=0; i < this._calls.length; i++) {
            if (this._calls[i].getChannel() == channel) {
                return this._calls[i];
            }
        }

        return null;
    }

    /**
     * Gets an array of active calls of that member in queue
     *
     * @returns {Array}
     */
    QueueMember.prototype.getCalls = function() {
        return this._calls;
    }

    /**
     * @private
     */
    QueueMember.prototype._addCall = function(call) {
        this._calls.push(call);
    }

    /**
     * @private
     */
    QueueMember.prototype._removeCall = function(call) {
        for (var i in this._calls){
            if (this._calls[i] == call) {
                this._calls.splice(i,1);
                break;
            }
        }

        if (this._calls.length == 0) {
            this._sleepSetTime = Math.round(new Date().getTime() / 1000);
        }
    }

    /**
     * @param {Object} o
     * @private
     */
    QueueMember.prototype._updateFromObject = function(o) {
        this._location = o.location;
        this._extension = o.extension;
        this._membership = o.membership;
        this._penalty = o.penalty;
        this._status = o.status;
        this._pause = o.pause;
        this._sleep = parseInt(o.sleep);
        this._sleepSetTime = Math.round(new Date().getTime() / 1000);
    }

    /**
     * @memberof WTAPI.QueuesPlugin
     * @alias WTAPI.QueuesPlugin.QueueUser
     * @public
     * @constructor
     */
    function QueueUser(channel, num, name, duration) {
        this._channel = channel;
        this._num = num;
        this._name = name;
        this._duration = parseInt(duration);
        this._durationSetTime = Math.round(new Date().getTime() / 1000);
    }

    QueueUser.fromObject = function(o) {
        return new QueueUser(o.channel, o.num, o.name, o.duration);
    }

    /**
     * Gets a unique identifier of a User that are on Queue
     *
     * @returns {String}
     */
    QueueUser.prototype.getChannel = function() {
        return this._channel;
    }

    /**
     * Gets a User number that are calling to Queue
     *
     * @returns {String}
     */
    QueueUser.prototype.getNumber = function() {
        return this._num;
    }

    /**
     * Gets a User name that are calling to Queue
     *
     * @returns {String}
     */
    QueueUser.prototype.getName = function() {
        return this._name;
    }

    /**
     * Gets a waiting for answer time in seconds.
     *
     * @returns {number}
     */
    QueueUser.prototype.getDuration = function() {
        var time = Math.round(new Date().getTime() / 1000);
        var offset = time - this._durationSetTime;
        return this._duration + offset;
    }

    /**
     * @memberof WTAPI.QueuesPlugin
     * @alias WTAPI.QueuesPlugin.QueueCall
     * @public
     * @constructor
     */
    function QueueCall(channel, destination, calleeNumber, calleeName, duration) {
        this._channel = channel;
        this._destinationChannel = destination;
        this._calleeNumber = calleeNumber;
        this._calleeName= calleeName;
        this._duration = parseInt(duration);
        this._durationSetTime = Math.round(new Date().getTime() / 1000);
    }

    /**
     * @private
     * @returns {QueueCall}
     */
    QueueCall.fromObject = function(o) {
        return new QueueCall(o.channel, o.destination, o.calleeNumber, o.calleeName, o.duration);
    }

    /**
     * Gets a call channel identity
     *
     * @returns {*}
     */
    QueueCall.prototype.getChannel = function() {
        return this._channel;
    }

    /**
     * @private
     * @returns {String}
     */
    QueueCall.prototype.getDestinationChannel = function() {
        return this._destinationChannel;
    }

    /**
     * Gets a callee number
     *
     * @returns {String}
     */
    QueueCall.prototype.getCalleeNumber = function() {
        return this._calleeNumber;
    }

    /**
     * Gets a callee name
     *
     * @returns {String}
     */
    QueueCall.prototype.getCalleeName = function() {
        return this._calleeName;
    }

    /**
     * Gets a call duration in seconds.
     *
     * @returns {number}
     */
    QueueCall.prototype.getDuration = function() {
        var time = Math.round(new Date().getTime() / 1000);
        var offset = time - this._durationSetTime;
        return this._duration + offset;
    }

    WTAPI.addPlugin("queues", QueuesPlugin);
}(WTAPI));