Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 154 lines (124 sloc) 5.159 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
/*
* CPJSONPConnection.j
* Foundation
*
* Created by Ross Boucher.
* Copyright 2008, 280 North, Inc.
*
* 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 2.1 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

@import "CPException.j"
@import "CPObject.j"
@import "CPRunLoop.j"

CPJSONPConnectionCallbacks = {};

CPJSONPCallbackReplacementString = @"${JSONP_CALLBACK}";

/*!
@ingroup foundation
@brief Allows cross domain connections using JSONP protocol.

Important note: CPJSONPConnection is <strong>only</strong> for JSONP APIs.
If aren't sure you <strong>need</strong>
<a href="http://ajaxian.com/archives/jsonp-json-with-padding">JSON<strong>P</strong></a>,
you most likely don't want to use CPJSONPConnection, but rather the more standard
CPURLConnection. CPJSONPConnection is designed for cross-domain
connections, and if you are making requests to the same domain (as most web
applications do), you do not need it.
*/
@implementation CPJSONPConnection : CPObject
{
    CPURLRequest _request;
    id _delegate;

    CPString _callbackParameter;
    DOMElement _scriptTag;
}

/*! @deprecated */
+ (CPJSONPConnection)sendRequest:(CPURLRequest)aRequest callback:(CPString)callbackParameter delegate:(id)aDelegate
{
    return [self connectionWithRequest:aRequest callback:callbackParameter delegate:aDelegate];
}

+ (CPJSONPConnection)connectionWithRequest:(CPURLRequest)aRequest callback:(CPString)callbackParameter delegate:(id)aDelegate
{
    return [[[self class] alloc] initWithRequest:aRequest callback:callbackParameter delegate:aDelegate startImmediately:YES];
}

- (id)initWithRequest:(CPURLRequest)aRequest callback:(CPString)aString delegate:(id)aDelegate
{
    return [self initWithRequest:aRequest callback:aString delegate:aDelegate startImmediately: NO];
}

- (id)initWithRequest:(CPURLRequest)aRequest callback:(CPString)aString delegate:(id)aDelegate startImmediately:(BOOL)shouldStartImmediately
{
    self = [super init];

    if (self)
    {
        _request = aRequest;
        _delegate = aDelegate;

        _callbackParameter = aString;

        if (!_callbackParameter && [[_request URL] absoluteString].indexOf(CPJSONPCallbackReplacementString) < 0)
             [CPException raise:CPInvalidArgumentException reason:@"JSONP source specified without callback parameter or CPJSONPCallbackReplacementString in URL."];

        if (shouldStartImmediately)
            [self start];
    }

    return self;
}

- (void)start
{
    try
    {
        CPJSONPConnectionCallbacks["callback" + [self UID]] = function(data)
        {
            if ([_delegate respondsToSelector:@selector(connection:didReceiveData:)])
                [_delegate connection:self didReceiveData:data];

            if ([_delegate respondsToSelector:@selector(connectionDidFinishLoading:)])
                    [_delegate connectionDidFinishLoading:self];

            [self removeScriptTag];

            [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
        };

        var head = document.getElementsByTagName("head").item(0),
            source = [[_request URL] absoluteString];

        if (_callbackParameter)
        {
            source += (source.indexOf('?') < 0) ? "?" : "&";
            source += _callbackParameter + "=CPJSONPConnectionCallbacks.callback" + [self UID];
        }
        else if (source.indexOf(CPJSONPCallbackReplacementString) >= 0)
        {
            source = [source stringByReplacingOccurrencesOfString:CPJSONPCallbackReplacementString withString:"CPJSONPConnectionCallbacks.callback" + [self UID]];
        }
        else
            return;

        _scriptTag = document.createElement("script");
        _scriptTag.setAttribute("type", "text/javascript");
        _scriptTag.setAttribute("charset", "utf-8");
        _scriptTag.setAttribute("src", source);

        head.appendChild(_scriptTag);
    }
    catch (exception)
    {
        if ([_delegate respondsToSelector:@selector(connection:didFailWithError:)])
            [_delegate connection: self didFailWithError: exception];

        [self removeScriptTag];
    }
}

- (void)removeScriptTag
{
    var head = document.getElementsByTagName("head").item(0);

    if (_scriptTag && _scriptTag.parentNode == head)
        head.removeChild(_scriptTag);

    CPJSONPConnectionCallbacks["callback" + [self UID]] = nil;
    delete CPJSONPConnectionCallbacks["callback" + [self UID]];
}

- (void)cancel
{
    [self removeScriptTag];
}

@end
Something went wrong with that request. Please try again.