SitePoint Sponsor

User Tag List

Results 1 to 5 of 5
  1. #1
    SitePoint Addict
    Join Date
    Feb 2005
    Posts
    311
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Prototype - Start of a cross site ajax plugin

    After some hard labour, here the start of a cross site ajax plugin. Currently works for IE and FF, but still need to fix the part for safari.
    Code largely based on the work at, http://trainofthoughts.org/blog/2007...plugin-xsajax/ but is written similar to the XmlHttpRequest object


    When it will be done, ill post it on my blog (http://www.mellowmorning.com)
    Right now im still having trouble:
    - getting it to work in Safari
    - implementing success and failure definitions


    First of all change the ajax.request initialize function to:
    PHP Code:
      initialize: function(urloptions) {
        
    this.setOptions(options);
        
    this.transport = (!this.options.crosssite) ? Ajax.getTransport() : new MyXajaxTransportClass;
        
    this.request(url);
      }, 
    Then in your ajax.request object specify the crossite option as such
    PHP Code:
    crosssitetrue
    The magic happens in this class:
    PHP Code:
    MyXajaxTransportClass = Class.create();
    //modeled after XmlHttpRequest http://en.wikipedia.org/wiki/XMLHttpRequest
    //functions open, send (setRequestHeader) - variable readyState, status
    //
    //    * 0 = uninitialized - open() has not yet been called.
    //    * 1 = open - send() has not yet been called.
    //    * 2 = sent - send() has been called, headers and status are available.
    //    * 3 = receiving - Downloading, responseText holds partial data.
    //    * 4 = loaded - Finished.

    //TODO:
    //Implementation for indicating Failure
    //Delayed removal of <script> nodes

    //
    //------------------------------ initialize, open and send ------------------------------------------------------
    //
    MyXajaxTransportClass.prototype.initialize = function() {
    this.readyState 0;
    }

    MyXajaxTransportClass.prototype.open = function(methodurlasynchronous) {
    if(
    method != 'GET'alert('Method should be set to GET when using cross site ajax');
    this.readyState 1;
    this.onreadystatechange();
    this.url url;
    dump('url: '+this.url);
    this.userAgent navigator.userAgent.toLowerCase();
    this.setBrowser();
    this.prepareGetScriptXS();

    }

    MyXajaxTransportClass.prototype.send = function(body) {
        
    this.readyState 2;
    this.onreadystatechange();
    this.getScriptXS(this.url);
    }

    //
    //------------------------------ actually do the request: setBrowser, prepareGetScriptXS, callback, getScriptXS ----------
    //

    // Figure out what browser is being used, is this good practise?
    MyXajaxTransportClass.prototype.setBrowser = function(body) {
        
    MyXajaxTransportClass.prototype.browser = {
        
    version: (this.userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
        
    safari: /webkit/.test(this.userAgent),
        
    opera: /opera/.test(this.userAgent),
        
    msie: /msie/.test(this.userAgent) && !/opera/.test(this.userAgent),
        
    mozilla: /mozilla/.test(this.userAgent) && !/(compatible|webkit)/.test(this.userAgent),
        
    konquerorthis.userAgent.match(/konqueror/i)
        };
    }

    MyXajaxTransportClass.prototype.prepareGetScriptXS = function () {
        if (
    this.browser.safari || this.browser.konqueror) {
            
    _xsajax$node = [];
            
    _xsajax$nodes 0;
        }
    }

    //node helper for safari needs fixing

    MyXajaxTransportClass.prototype.callback = function () {
        
    this.readyState 4;
        
    this.onreadystatechange();
        
    //TODO, timed cleanup
        //$(this).remove();



    MyXajaxTransportClass.prototype.getScriptXS = function () {
        
    /* determine arguments */
    var arg = {
        
    'url':      null
    };
    if (
    typeof arguments[0] == "string") {
        
    /* simple usage, is all we need */
        
    arg.url arguments[0];
    }

    /* generate <script> node */
    this.node document.createElement('SCRIPT');
    this.node.type 'text/javascript';
    this.node.src arg.url;

      

    /* optionally apply event handler to <script> node for
       garbage collecting <script> node after loading and/or
       calling a custom callback function */
    var node_helper null;
        
           
        if (
    this.browser.msie) {
            
               function 
    mybind(obj) {
                 
    temp = function() {
                 if (
    this.readyState == "complete" || this.readyState == "loaded") {
                 return 
    obj.callback.call(obj);
                 }
                 };
                return 
    temp;
               }
            
    /* MSIE doesn't support the "onload" event on
               <script> nodes, but it at least supports an
               "onreadystatechange" event instead. But notice:
               according to the MSDN documentation we would have
               to look for the state "complete", but in practice
               for <script> the state transitions from "loading"
               to "loaded". So, we check for both here... */
            
    this.node.onreadystatechange mybind(this);
          
    //function () {
          //    if (this.readyState == "complete" || this.readyState == "loaded") {
          //        this.callback.call(this);
          //    }        
          //};
        
    }
        else if (
    this.browser.safari || this.browser.konqueror) {
            
    /* Safari/WebKit and Konqueror/KHTML do not emit
               _any_ events at all, but we can exploit the fact
               that dynamically generated <script> DOM nodes
               are executed in sequence (although the scripts
               theirself are still loaded in parallel) */
            
    _xsajax$nodes++;
            var 
    helper =
                
    'var ctx = _xsajax$node[' _xsajax$nodes '];' +
                
    'ctx.callback.call(ctx.node);' +
                
    'setTimeout(function () {' +
                
    '    ctx.node_helper.parentNode.removeChild(ctx.node_helper);' +
                
    '}, 100);';
                
    node_helper document.createElement('SCRIPT');
                
    node_helper.type 'text/javascript';
                
    node_helper.appendChild(document.createTextNode(helper));
                
    _xsajax$node[_xsajax$nodes] = {
                
    callbackcallback,
                
    nodethis.node,
                
    node_helpernode_helper
            
    };
        }
        else {
            
    /* Firefox, Opera and other reasonable browsers can
               use the regular "onload" event... */
            
    this.node.onload this.callback.bind(this);
        }


    /* inject <script> node into <head> of document */
    this.readyState 3;
    this.onreadystatechange();
    var 
    head document.getElementsByTagName('HEAD')[0] ;
    head.appendChild(this.node);

    /* optionally inject helper <script> node into <head>
       (Notice: we have to use a strange indirection via
       setTimeout() to insert this second <script> node here or
       at least Konqueror (and perhaps also Safari) for unknown
       reasons will not execute the first <script> node at all) */
    if (node_helper !== null) {
        
    setTimeout(function () {
            var 
    head document.getElementsByTagName('HEAD')[0] ;
            
    head.appendChild(node_helper);
        }, 
    100);
    }

    }

    //
    //------------------------------ Don't complain when these are called: setRequestHeader and onreadystatechange ----------
    //

    MyXajaxTransportClass.prototype.setRequestHeader = function() {
        
    //we don't need this
    }
    MyXajaxTransportClass.prototype.onreadystatechange = function() {
        
    //we don't need this


  2. #2
    Follow Me On Twitter: @djg gold trophysilver trophybronze trophy Dan Grossman's Avatar
    Join Date
    Aug 2000
    Location
    Philadephia, PA
    Posts
    20,578
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    What's the benefit of using this plugin over just appending a <script> element whose output calls a callback function? Is it just to make it act more like an XMLHttpRequest object?

  3. #3
    SitePoint Enthusiast
    Join Date
    Aug 2006
    Posts
    41
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You might want to take a look at how other people build things on top of Prototype, there is a pretty standard way of doing things (take script.aculo.us as one example).

    Also probably worth taking a look at the new Class features in the soon-to-be-released 1.6. You can probably extend Ajax.Request and change just those bits that you need to, making sure the interface and default options are consistent for people used to the existing code.

    Prototype gives you browser sniffing, you don't need to recreate it.

  4. #4
    SitePoint Addict
    Join Date
    Feb 2005
    Posts
    311
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Regarding the advantage of this plugin. This plugin allows you to write your cross-site ajax request in exactly the same way as your regular ajax requests in prototype. This level of abstraction is really great if you want to have a great deal of more complex ajax interactions. The code to use the plugin goes like this:

    PHP Code:
    function ajaxTest() {
    dump('start');
    new 
    Ajax.Request(url, {
      
    method'GET',
      
    crosssitetrue,
      
    parameters: { theorder'1'down'1'threadid'5' },
      
    onLoading: function() {
      
    dump('onLoading');    
      },
      
    onSuccess: function(transport) {
      
    dump('onSuccess');
      },
      
    onFailure: function(transport) {
      
    dump('onFailure');
      }
    });

    As you see this plugin actually uses directly the ajax.request class. The ajax.request class just uses a different class for transport when specifying the crosssite: true.
    Indeed the extend functionality in 1.6 is very nice, but this extending was not needed here, since the ajax.request class remains largely unchanged.

    The plugin is not finished yet. Since it is a port of a jquery plugin, i still have the jquery browser detection in there. Didn't want to worry about the difference between those right now

    Hope you will like the end result

  5. #5


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •