PHP Classes

File: MultiAjax.js

Recommend this page to a friend!
  Classes of V  >  MultiAjax  >  MultiAjax.js  >  Download  
File: MultiAjax.js
Role: Auxiliary data
Content type: text/plain
Description: MultiAjax JavaScript
Class: MultiAjax
Using AJAX in batch mode
Author: By
Last change: changed
Date: 4 years ago
Size: 11,380 bytes
 

Contents

Class file image Download
/**
 * MultiAjax class provides a convenient way to smart work with AJAX.
 * It supports timeout, queue, session limits and batch mode.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 */

/**
 * Serialize object.
 */
Object.prototype.serialize = function() {
    var s = '', i = 0;
    for (var key in this) {
        if (typeof this[key] != 'function') {
            s += ':' + key.serialize() + ':' +
                 (this[key] ? this[key].serialize() : 'n');
            i++;
        }
    }
    return 'o:' + i + s;
}

/**
 * Serialize array.
 */
Array.prototype.serialize = function() {
    while (this.length > 0 && typeof this[this.length - 1] == 'undefined') {
        this.length--;
    }
    var s = '', i = 0;
    for (; i < this.length; i++) {
        s += ':' + (this[i] ? this[i].serialize() : 'n');
    }
    return 'a:' + i + s;
}

/**
 * Serialize number.
 */
Number.prototype.serialize = function() {
    return 's:' + this;
}

/**
 * String serialization separator.
 */
String.prototype.SERIALIZATION_SEPARATOR = "__\t__";

/**
 * Serialize string
 */
String.prototype.serialize = function() {
    return String(this) ? 's:' + encodeURIComponent(this.replace(/:/g,
           String.prototype.SERIALIZATION_SEPARATOR)).replace(/%20/g, '+'):'n';
}

/**
 * Replace all occurrences of the search string with the replacement string.
 *
 * @param fromstr Searched string.
 * @param tostr Replaced string.
 *
 * @return Result string
 */
String.prototype.replaceAll = function(fromstr, tostr) {
    var s = String(this), i;

    while ((i = s.indexOf(fromstr)) >= 0) {
        s = s.replace(fromstr, tostr);
    }

    return s;
}

/**
 * Unserialize data.
 *
 * @param value Serialized string.
 *
 * @return Unserialized data.
 */
String.prototype.unserialize = function() {
    // Internal function for unserialization.
    function _unserialize_internal() {
        switch (arr[idx++]) {
            case 's':
                // String
                return decodeURIComponent(arr[idx++].replace(/\+/g, ' ')).replaceAll(String.prototype.SERIALIZATION_SEPARATOR, ':');
            case 'a':
                // Array
                var a = [];

                for (var i = arr[idx++]; i > 0; i--) {
                    a.push(_unserialize_internal());
                }

                return a;
            case 'o':
                // Object
                var obj = new Object();

                for (var i = arr[idx++]; i > 0; i--) {
                    obj[_unserialize_internal()] = _unserialize_internal();
                }

                return obj;
            default:
                return '';
        }
    }

    // Divide lexemes of input serialized string.
    var arr = this.split(':'), idx = 0;
    return _unserialize_internal();
}

/**
 * =============================================================================
 * XMLHttpRequest pool class
 * =============================================================================
 *
 * @param maxSize Maximal pool size.
 */
function RequestPool(maxSize) {
    this.maxSize = maxSize;
    this.pool = [];
}

/**
 * Get XMLHttpRequest from pool.
 *
 * @return Return XMLHttpRequest object or null.
 */
RequestPool.prototype.getRequest = function() {
    if (this.pool.length >= this.maxSize) {
        return null;
    }

    var rec = null;

    if (typeof XMLHttpRequest != 'undefined') {
        rec = new XMLHttpRequest();
    } else {
        var ms = ['Msxml2.XMLHTTP.6.0',
                  'Msxml2.XMLHTTP.4.0',
                  'Msxml2.XMLHTTP.3.0',
                  'Msxml2.XMLHTTP',
                  'Microsoft.XMLHTTP'];

        for (var i in ms) {
            try {
                rec = ActiveXObject(ms[i]);
                break;
            } catch(e) {
            }
        }
    }

    if (rec) {
        this.pool.push(rec);
    }

    return rec;
}

/**
 * Close XMLHttpRequest from pool.
 *
 * @param request XMLHttpRequest object.
 */
RequestPool.prototype.closeRequest = function(request) {
    for (var i in this.pool) {
        if (this.pool[i] === request) {
            this.pool.splice(i, 1);

            delete request.ontimeout;
            delete request.onreadystatechange;

            break;
        }
    }
}

/**
 * =============================================================================
 * Queue class
 * =============================================================================
 *
 * @param maxSize Maximal queue size.
 */
function Queue(maxSize) {
    this.maxSize = maxSize;
    this.queue = [];
}

/**
 * Push new element into the queue.
 *
 * @param value New element.
 *
 * @return Result 0 if the queue is overflowed, other number if the value
 *         is added.
 */
Queue.prototype.push = function(value) {
    if (this.queue.length >= this.maxSize) {
        return 0;
    }

    return this.queue.push(value);
}

/**
 * Get element from the queue without deletion.
 *
 * @param i Number of element, this parameter is not required, 0 (first element)
 *          by default.
 *
 * @return Result first element from the queue or undefined value if queue
 *         is empty.
 */
Queue.prototype.get = function(i) {
    if (typeof i != 'number') {
        i = 0;
    }

    if (i < 0 || i >= this.queue.length) {
        return null;
    }

    return this.queue[i];
}

/**
 * Shift element from the queue.
 *
 * @param i Number of element, this parameter is not required, 0 (first element)
 *          by default.
 *
 * @return Result first element from the queue or undefined value if queue
 *         is empty.
 */
Queue.prototype.shift = function(i) {
    if (typeof i != 'number') {
        i = 0;
    }

    if (i < 0 || i >= this.queue.length) {
        return null;
    }

    value = this.queue[i];
    this.queue.splice(i, 1);

    return value;
}

/**
 * =============================================================================
 * MultiAjax object
 * =============================================================================
 */
var multiajax = {
    /**
     * Default request data. It is possible methods: 'GET', 'POST' and 'AUTO'
     */
    _request: {
        url: '/multiajax',
        method: 'AUTO',
        request_parameter: 'q',
        timeout: 20,
        method_get_maxsize: 200
    },

    /**
     * AJAX sessions
     */
    _sessions: new RequestPool(5),

    /**
     * Queue of AJAX queries
     */
    _queue: new Queue(100),

    /**
     * Activity flag for _checkQueue method
     */
    _isActive: 0,

    /**
     * Check queue of AJAX requests
     */
    _checkQueue: function() {
        this._isActive = 1;

        // Get request from query
        var maxi = this._queue.queue.length;
        if (maxi == 0) {
            this._isActive = 0;
            return;
        }

        // Get HTTP request from pool
        var req = this._sessions.getRequest();
        if (req != null) {
            // Prepare request
            var sessions = this._sessions;
            var r = this._queue.shift();

            // Build batch package
            var batch = [[r.data, r.handler]];
            var callbacks = [r.callback];

            for (var i = 0; i < maxi - 1; i++) {
                var x = this._queue.get(i);
                if (r.url == x.url && r.timeout == x.timeout && r.method == x.method) {
                    this._queue.shift(i);
                    maxi--;
                    i--;
                    batch.push([x.data, x.handler]);
                    callbacks.push(x.callback);
                }
            }

            // Prepare request parameters
            if (!r.url) {
                r.url = this._request.url;
            }

            if (!r.timeout) {
                r.timeout = this._request.timeout;
            }

            if (!r.method) {
                r.method = this._request.method;
            }

            // Build query string
            var reqstr = this._request.request_parameter + '=' + batch.serialize();
            var geturl = r.url + (r.url.indexOf('?') < 0 ? '?' : '&') + reqstr;

            if (r.method == 'AUTO') {
                r.method = geturl.length > this._request.method_get_maxsize ? 'POST' : 'GET';
            }

            // Set timeout
            req.ontimeout = function() {
                req.abort();
                for (var i in callbacks) {
                    callbacks[i]({error: 'TIMEOUT'});
                }
                sessions.closeRequest(req);
            }

            var timeoutid = setTimeout(req.ontimeout, r.timeout * 1000);

            // Prepare callback method for result
            req.onreadystatechange = function() {
                if (req.readyState == 4) {
                    if (req.status) {
                        clearTimeout(timeoutid);

                        var data = req.responseText.unserialize();

                        for (var i in callbacks) {
                            callbacks[i](req.status == 200 && data instanceof Array ? {data: data[i]} : {error: 'REQUEST_ERROR'});
                        }
                    }

                    sessions.closeRequest(req);
                }
            }

            // Send Request
            if (r.method == 'POST') {
                req.open('POST', r.url, true);
                req.setRequestHeader(
                    'Content-type',
                    'application/x-www-form-urlencoded; charset=UTF-8');
                req.send(reqstr);
            } else {
                req.open('GET', geturl, true);
                req.send(null);
            }
        }

        setTimeout(function() {
            multiajax._checkQueue();
        }, 1000);
    },

    /**
     * Send AJAX request.
     * The structure of request:
     *   - url      - URL of server-side resource
     *   - handler  - request function name ("/multiajax" by default)
     *   - data     - request data
     *   - callback - JavaScript callback function name
     *   - timeout  - request timeout, seconds (30 seconds by default)
     *   - method   - HTTP request method, POST, GET or AUTO (AUTO by default)
     *
     * The structure of response:
     *   - data     - response data
     *   - error    - response error code
     *
     * @param r AJAX request.
     */
    send: function(r) {
        if (!this._queue.push(r)) {
            r.callback({error: 'QUEUE_OVERFLOW'});
            return;
        }

        if (!this._isActive) {
            this._checkQueue();
        }
    }
}
For more information send a message to info at phpclasses dot org.