Source: number.js

(function () {
    // Dependencies
    var Base = Callpixels.Base;
    /**
     * @constructor
     * @memberOf Callpixels
     * @param {Object} attributes - Attributes
     * @property {Object} attributes
     * @property {Number} attributes.id - The CallPixels internal number ID.
     * @property {String} attributes.formatted_number - Nationally formatted phone number.
     * @property {String} attributes.number - E.164 formatted phone number.
     * @property {String} attributes.plain_number - The unformatted phone number digits.
     * @property {Boolean} attributes.target_open - Whether there is an open, available target.
     */
    Callpixels.Number = function (options) {

        var self = this;
        self.type = 'numbers';

        function initialize(data) {
            self.store(data);
            self.set('is_active', 'true');
        }

        /**
         * Add tags to a number.
         * @memberOf Callpixels.Number
         * @function add_tags
         * @instance
         * @param {Object} tags - A collection of tags {key: 'value', tag2: 'value2'}
         * @param {Function} callback - Callback that will be fired after request.
         * @throws Will throw an error if attempting to modify tags on a number that doesn't belong to a number pool
         * with per-visitor numbers enabled.
         */
        self.add_tags = function (tags, callback) {
            ensure_is_per_visitor();
            self.post_data('numbers/tag', tags_payload(tags), callback);
        };

        /**
         * Remove tags from a number.
         * @memberOf Callpixels.Number
         * @function remove_tags
         * @instance
         * @param {Object} tags - A collection of tags {key: 'value', tag2: 'value2'}
         * @param {Function} callback - Callback that will be fired after request.
         * @throws Will throw an error if attempting to modify tags on a number that doesn't belong to a number pool
         * with per-visitor numbers enabled.
         */
        self.remove_tags = function (tags, callback) {
            ensure_is_per_visitor();
            self.post_data('numbers/untag', tags_payload(tags), callback);
        };

        /**
         * Removes all tags with given keys from a number.
         * @memberOf Callpixels.Number
         * @function remove_tags_by_keys
         * @instance
         * @param {Array} keys - An array of keys to remove. eg: ['key1', 'key2']
         * @param {Function} callback - Callback that will be fired after request.
         * @throws Will throw an error if attempting to modify tags on a number that doesn't belong to a number pool
         * with per-visitor numbers enabled.
         */
        self.remove_tags_by_keys = function (keys, callback) {
            ensure_is_per_visitor();
            if (typeof(keys) === 'string') keys = keys.split(',');
            var payload = {
                tag_keys: keys,
                ids: [ get('id') ],
                campaign_key: get('campaign_key')
            };
            self.post_data('numbers/untag/keys', payload, callback);
        };

        /**
         * Clear all tags from a number.
         * @memberOf Callpixels.Number
         * @function clear_tags
         * @instance
         * @param {Function} callback - Callback that will be fired after request.
         * @throws Will throw an error if attempting to modify tags on a number that doesn't belong to a number pool
         * with per-visitor numbers enabled.
         */
        self.clear_tags = function (callback) {
            ensure_is_per_visitor();
            var payload = {
                ids: [ get('id') ],
                campaign_key: get('campaign_key'),
                all: 'true'
            };
            self.post_data('numbers/untag', payload, callback);
        };

        /**
         * Release number back to pool.
         * @memberOf Callpixels.Number
         * @function release
         * @instance
         */
        self.release = function () {
            self.set('is_active', 'false');
        };

        /**
         * Start a call immediately by having a campaign target dial the visitor.
         * @memberOf Callpixels.Number
         * @function initiate_call
         * @instance
         * @param {String} dial - The number to call.
         * @param {Object} payload - A collection of tags as key-value pairs and optional secure override properties.
         * @param {string} [payload.target_map] - A string mapping a placeholder number to a phone number.
         * @param {string} [payload.target_map_cs] - A SHA1 checksum of the target_map concatenated with your CallPixels API
         * key.
         * @param {number} [payload.timer_offset] - Number of seconds to offset the "connect" duration timers by.
         * @param {string} [payload.timer_offset_cs] - An SHA1 checksum of the timer_offset concatenated with your
         * CallPixels API key.
         * @param {(string|number)} [payload.*] - Key value pairs treated as tags.
         * @param {Function} callback - Callback that will be fired after request.
         * @example
         * number.initiate_call('4166686980', {company_name: 'CallPixels'}, function (call) {
         *     alert('Call started with UUID ' + call.uuid)
         * });
         */
        self.initiate_call = function (dial, payload, callback) {
            if (typeof(payload) === 'undefined') payload = {};
            // assign dial to payload
            payload.dial = dial;
            // merge payload into payload
            payload = Base.merge(self.get('id', 'campaign_key'), payload);
            // post the payload
            self.post_data('numbers/initiate_call', payload, callback);
        };

        function tags_payload(tags) {
            if (typeof(tags) === 'string') tags = Callpixels.Number.extract_tags_from_string(tags);
            return {
                tag_values: tags,
                ids: [ get('id') ],
                campaign_key: get('campaign_key')
            };
        }

        function get(key) {
            return self.get(key);
        }

        function ensure_is_per_visitor() {
            if (self.get('is_per_visitor') === false) {
                throw "Error: Tried to add tags to non per-visitor number.";
            }
        }

        initialize(options);
    };

    Callpixels.Number.extract_tags_from_string = function (tags) {
        var output = {};
        var tags = tags.split(",");
        for (var i = 0; i < tags.length; i++) {
            var tag = tags[i].split(":");
            output[tag[0]] = tag[1]
        }
        return output;
    };

    Callpixels.Number.prototype = new Callpixels.Base.Model();

    function ping_active_numbers(callback) {
        if (typeof(Callpixels.Base.Data._store) !== 'undefined') {
            // get numbers
            var numbers = Callpixels.Base.Data._store['numbers'];
            // for each number
            if (typeof(numbers) !== 'undefined') {
                // group number_ids by campaign_key
                var grouped = {};
                for (var primary_key in numbers) {
                    var number = numbers[primary_key];
                    if (number.is_active === 'true') {
                        if (typeof(grouped[number.campaign_key]) === 'undefined'){
                          grouped[number.campaign_key] = [];
                          grouped[number.campaign_key]['ids'] = [];
                          grouped[number.campaign_key]['hashes'] = [];
                        }
                        grouped[number.campaign_key]['ids'].push(number.id);
                        grouped[number.campaign_key]['hashes'].push(number.id_checksum);
                    }
                }
                // ping each group of number_ids
                for (var campaign_key in grouped) {
                    var payload = {
                        ids: grouped[campaign_key].ids,
                        hashes: grouped[campaign_key].hashes
                    };
                    Callpixels.Base.Request.connection().postJSON('/api/v1/numbers/ping', payload, [Callpixels.Base.Model.update, callback], this);
                }
            }
        }
        // call recursively
        setTimeout(ping_active_numbers, 15000);
    }

    // always ping active numbers
    ping_active_numbers();

})();