Git Product home page Git Product logo

strophejs's Introduction

strophejs's People

Contributors

adamjmcintyre avatar aeon avatar astro avatar benlangfeld avatar clarifiednetworks avatar dodo avatar exavolt avatar florob avatar francois2metz avatar henrikjoreteg avatar lboynton avatar loe avatar metajack avatar mwild1 avatar paulsd avatar smparkes avatar thepug avatar twonds avatar vog avatar yigit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

strophejs's Issues

attach() doesn't trigger a poll

I noticed that, when attaching to a session, if there is no activity (send stanza) Strophe never polls.. and the session expires after a while whereas I'm attached..

When using #connect, Strophe polls at least every 60 seconds. When attached, should we not have the same behavior ?

I don't know if this should be fixed but I think I have the fix, which is not hard. Actually, the _onIdle process is not triggered because we are disconnected, it shall be restarted by hand : In #attach(), #_onIdle() shall be called at the end.

 attach: function (jid, sid, rid, callback, wait, hold, wind)
    {
       //...
       //Bla bala
       //...

        this._changeConnectStatus(Strophe.Status.ATTACHED, null);
        this._onIdle();
    },

I'll do a pull request if you find this relevant. Thanks.

BOSH module not started - when using echobot.html

When using stophe.js version 1.0 echobot.html I get the xml message 'BOSH module not started'.

I have jwchat and also pidgin working.

In all cases BOSH_SERVICE (echobot.js) is pointing to http://job.myserver.com/http-bind/

Direct URL access of http://job.myserver.com/http-bind/ shows: "ejabberd mod_http_bind
An implementation of XMPP over BOSH (XEP-0206)" message.

Is there something I am doing wrong or perhaps you can point me to the right direction.

Using plugins

Strophe js has a great amount of plugins, in this case I'm trying to use the roster plugin.
I haven't found documentation related to plugin usage so I'm kind of lost and I would really appreciate your help.
Roster plugin: https://github.com/metajack/strophejs-plugins/tree/master/roster

What I've done is add both the Strophejs .js file and the Strophe.roster.js file to my .html file like this (plus the jQuery lib):

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script> <script type="text/javascript" src="strophejs/strophe.js"></script> <script type="text/javascript" src="strophejs/plugins/strophe.roster.js"></script>

I've stablished a connection with the server successfully, the user I'm connecting appears in Jabber clients such as Pidgin.

function OnConnectionStatus(nStatus)
{
if (nStatus == Strophe.Status.CONNECTING) {
} else if (nStatus == Strophe.Status.CONNFAIL) {
console.log('connection error');
} else if (nStatus == Strophe.Status.DISCONNECTING) {
} else if (nStatus == Strophe.Status.DISCONNECTED) {
} else if (nStatus == Strophe.Status.CONNECTED) {
console.log('connected');
OnConnected();
}
}

Also, I get the connected message in the terminal, so the connection is well made. But I don't know how to proceed from here, I don't know how to call the roster plugin from the OnConnected function so I get the roster (buddy list), I want to create some html/css markup with this info and show it in my html.

I would really appreciate if you could tell me how to call the plugins and in this particular case how to get the roster.
Thank you so much in advanced!

Stable release offline

Not exactly an issue with strophejs, but still: At the moment the download links at your website for both stable release packages are broken.

CDATA is not respected in serializer

While strophe does not escape CDATA nodes before sending them out, incoming CDATA nodes are treated incorrectly as text nodes and get escaped

flXHR connection error

While connecting from behind firewall I get CONNFAIL with reason code "flXHR connection error" and that triggers _onDisconnectTimeout

connection status moves like this: CONNECTING -> AUTHENTICATING -> CONNFAIL

This occurs with Firefox 7.0.1, strophejs-1.0.2 and flXHR-1.0.6

On IE 7 no such issues.

AMD modulization support

AMD modules are an increasingly popular way to prevent namespace pollution. It would be great if Strophe could be packaged with support for this.

The code for this can be as simple as:

var args = arguments;
if ( typeof define === 'function' && define.amd ) {
    // register with AMD as a module
    define( function () {
        var Strophe = args[0];
        Strophe.$build = args[1];
        Strophe.$msg = args[2];
        Strophe.$iq = args[3];
        Strophe.$pres = args[4];

        return Strophe;
    });
} else {
    window.Strophe = arguments[0];
    window.$build = arguments[1];
    window.$msg = arguments[2];
    window.$iq = arguments[3];
    window.$pres = arguments[4];
}

The only remaining problem is how to package MD5 and Base64? They can either be wrapped into their own modules, in which case they need to be distributed in their own files, or they can be compiled together using r.js.

That seems like a big change to the build system though, so I'm a little stuck on what would be appropriate here.

I cannot find strophe.js in the download

I downloaded the 1.0 version and extracted it. I do not see strophe.js in the expanded directory.

Am I doing something wrong?

I see the makefile, readme and the other directories like examples etc. but no strophe.js.

Mike

custom timer functions

Hi,

I have made a small patch to StropheJS to be able to use custom setTimeout and clearTimeout functions. The basic idea is to be able to use the Cappuccino's runloop instead of custom "parallel" loop in StropheCappuccino. I notice that is seems to work smoother on Capp app than letting strophe to eventually interrupt the Capp run loop for events.

What do you think ?

the patch is here:
https://github.com/primalmotion/strophejs/commit/8ceb6e47ac4eb2f58f32f6c7eca2faffa28b8e9b

Strophe.Connection.disconnect infinite loop

I noticed when attempting a crossdomain connection that fails, the exception handler will call an infinite loop.

Starting in _processRequest, there's this block:

try {
    req.xhr.open("POST", this.service, true);
} catch (e2) {
    Strophe.error("XHR open failed.");
    if (!this.connected) {
        this._changeConnectStatus(Strophe.Status.CONNFAIL,
                                  "bad-service");
    }
    this.disconnect();
    return;
}

So when the POST fails, it calls disconnect(), which in turns calls _sendTerminate(), which will then create another POST request that will fail, resulting in an infinite loop.

In a working setup this shouldn't ever come up, but I figured this probably shouldn't be happening.

.connect() directly (without 500ms delay) after new Strophe.Connection get stuck @ authenticating

The symptom: Strophe connection emits a 'connecting' and an 'authenticating' but never makes it to 'connected'. (Sometimes - of course)

The system setup -- server is OpenFire 3.7.1. BOSH is done via bosh-server (node.js) by Dhruv Matani.
Both running on an Amazon ec2 server.

Connection of clients is ANONYMOUS -- 'id' is 'servername' and password is "".

Clients we're using to develop on are Macs primarily. I've replicated this problem on Firefox and Chrome but use Chrome almost exclusively in test-dev. Chrome is Version 19.0.1084.56.

Strophe being used says "1.0.2" in Strophe.VERSION

Here's a snippet of console messages showing what happens in terms of the status callback and the associated callback function itself. Lines in bold are the status callback messages (I've done the bold in this message for clarity)

11:35:56 INFO fb load facebook-jssdk index.js:35
XMPP/Strophe Connecting... callcast.js:1344
11:35:56 INFO Page loaded. index.js:35
11:35:56 INFO fbAsyncInit callback index.js:35
XMPP/Strophe Authenticating... callcast.js:1342
11:35:56 INFO authResponseChange callback index.js:35
FB logged IN or got a new token. fb.js:53
11:35:56 INFO fbLoginStatus callback response.authResponse[object Object] index.js:35
11:35:56 INFO checkCredentials index.js:35
one-login-complete: Msg: checkCredentials - FB Login

    conn_callback: function(status) {
         if (status === Strophe.Status.CONNECTED) {
             console.log("Finalizing connection and then triggering connected...");
             Callcast.finalizeConnect();
             $(document).trigger('connected');
         } else if (status === Strophe.Status.AUTHENTICATING) {
             console.log("XMPP/Strophe Authenticating...");
         } else if (status === Strophe.Status.CONNECTING) {
             console.log("XMPP/Strophe Connecting...");
         } else if (status === Strophe.Status.ATTACHED) {
             console.log("Re-Attach of connection successful. Triggering re-attached...");
             $(document).trigger('re-attached');
         } else if (status === Strophe.Status.DISCONNECTED) {
             console.log("XMPP/Strophe Disconnected.");
             Callcast.disconnect();
             $(document).trigger('disconnected');
         } else if (status === Strophe.Status.DISCONNECTING) {
             console.log("XMPP/Strophe is Dis-Connecting...should we try to re-attach here? TODO:RMW");
         } else if (status === Strophe.Status.CONNFAIL) {
             console.log("XMPP/Strophe reported connection failure...attempt to re-attach...");
             Callcast.reattach(Callcast.connection.jid, Callcast.connection.sid, Callcast.connection.rid, Callcast.conn_callback);
             alert("NOTICE -- attempted to auto-re-attach after connection failure. Did we succeed?");
         } else if (status === Strophe.Status.AUTHFAIL) {
             Callcast.disconnect();
             $(document).trigger('disconnected');
             alert("Authentication failed. Bad password or username.");
         }
         else
            console.log("Strophe connection callback - unhandled status = " + status);
    },

And here's the function which setups up the connection. I can do this with or without the .reset() and it'll fail with the same general frequency. NOTE: This is an anonymous authentication: id='servername' and pw="" while url is null

    connect: function(id, pw, url) {
        var self = this;
        var boshconn = "/xmpp-httpbind";
        if (url)
            boshconn = url;

/*    Withor without this section fails...
    if (this.connection)
        {
            this.disconnect();
            this.reset();
            delete this.connection;
        }
*/
        this.connection = new Strophe.Connection(boshconn);
// with or without this it fails too     this.connection.reset();
        this.connection.connect(id, pw, self.conn_callback);
    },

Now -- to make it work reliably....put in a timer for the .connect()

        var self = this;
        this.connection = new Strophe.Connection(boshconn);
// with or without this it fails too     this.connection.reset();
        setTimeout(function() {
            self.connection.connect(id, pw, self.conn_callback);
        }, 500);

Authentication failed with Unicode Password

The Candy guys send me here: https://github.com/amiadogroup/candy/issues/59
:)

Tested with Candy 1.0.6 and OpenFire 3.7.1.
My test account is username "和製漢字" password "和製漢字".
Login into OpenFire works however with Candy the login fails for PLAIN and DIGEST-MD5.

PLAIN fix:
In libs.bundle.js line 3027:
from: auth_str = auth_str + this.pass;
to: auth_str = auth_str + unescape(encodeURIComponent(this.pass));

DIGEST-MD5 fix:
In libs.bundle.js line 3107:
from: ":" + realm + ":" + this.pass) +
to: ":" + realm + ":" + unescape(encodeURIComponent(this.pass))) +

Blank request can get sent after disconnection (depending on timing)

In the following code (core.js, inside _onIdle, line 3589):

    if (this._requests.length < 2 && this._data.length > 0 &&
        !this.paused) {
        ...
        this._requests.push(   // <--------------- line 3589
            new Strophe.Request(body.tree(),
                                this._onRequestStateChange.bind(
                                    this, this._dataRecv.bind(this)),
                                body.tree().getAttribute("rid")));
        this._processRequest(this._requests.length - 1);
    }

Depending on timing, this code seems to queue a poll request even after "terminate" message has been processed. This results in 404 being returned from server, and causes uncaught exception: TypeError: elem.getAttribute is not a function.

Perhaps, we should check for this.disconnecting variable before queuing a new request?

Here is a quick fix for this problem. However, a better fix should be possible:

        if (!this.disconnecting) {
            this._requests.push(
                new Strophe.Request(body.tree(),
                                    this._onRequestStateChange.bind(
                                        this, this._dataRecv.bind(this)),
                                    body.tree().getAttribute("rid")));
        }
        if (this._requests.length > 0) {
            this._processRequest(this._requests.length - 1);
        }

websockets

What about support of websockets? (with node-xmpp-bosh for example)

copyElement will double escape XML

If an element contains escaped XML in a text node, then copyElement will double escape the text at this point:

el = Strophe.xmlTextNode(elem.nodeValue);

A possible fix is to pass an optional flag to Strophe.xmlTextNode, such as the following:

xmlTextNode: function (text, isEscaped) {
if (! isEscaped) {
//ensure text is escaped
text = Strophe.xmlescape(text);
}
...
}

And thus the call in copyElement would be:

el = Strophe.xmlTextNode(elem.nodeValue, true);

_dataRecv has a conditionally declared variable

The variable elem is conditionally defined, and will throw an error if the following path is taken:

  1. req.getResponse() throws an error
  2. The error is caught.
  3. Disconnect happens.
  4. if (elem === null) { return; } is tried. elem is not declared, and an error is thrown.

The two solutions are:

  1. Declare elem outside the try { ... } catch { ... } block.
  2. Return inside the catch block.

FF4 Attach Generates Duplicate Requests

When using the attach() call in Strophe against Openfire 3.7.0, all works as designed in IE8/9. In FF4, duplicate requests are sent to the server with the same RID, causing a policy violation.

The sequence of events works like this:

  1. User refreshes the page and the onbeforeunload event is trapped and handled
  2. Session information is stored to cookies
  3. A presence type="unavailable" is sent to the MUC
  4. Page reloads and attempts to attach() to the session using the cookies
  5. In FF4, this results in a policy violation when two duplicate RIDs are sent on the attach.

The trace from Fiddler is attached below. In both traces, the first reqeust/response pair is the presence type="unavailable" on the onbeforeunload.

The issue starts one the page loads again and the attach is attempted with RID: 1132070775 in the FF4 sequence.

This is the Fiddler trace in FF4:

POST http://gametime.dev.com/http-bind/ HTTP/1.1
Host: gametime.dev.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://gametime.dev.com/GameTimePages/GameTimeHuddle.aspx?List=3b9551da%2D46ed%2D4357%2Db93e%2Dfa55155366b1&ID=9&Source=http%3A%2F%2Fgametime%2Edev%2Ecom%2FLists%2FGameTimeHuddles%2FAllItems%2Easpx&ContentTypeId=0x01002167C00000000000000000000000000200D799BB01255FD045A12E56621F1F57C3
Content-Length: 199
Content-Type: text/plain; charset=UTF-8
Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}; Ribbon.DocLibListForm.Edit=750500|-1|497|-1474418945
Pragma: no-cache, no-cache, no-cache
Cache-Control: no-cache, no-cache, no-cache

<body rid='1132070774' xmlns='http://jabber.org/protocol/httpbind' sid='336bd0b0'><presence type='unavailable' to='[email protected]' xmlns='jabber:client'/></body>

HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
SPRequestGuid: 877d2fc4-b1f7-40d0-9152-00699e4033a8
Set-Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}; path=/
X-SharePointHealthScore: 3
Persistent-Auth: true
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 14.0.0.5130
Date: Tue, 10 May 2011 17:16:39 GMT
Content-Length: 350

<body xmlns='http://jabber.org/protocol/httpbind'><presence xmlns="jabber:client" to="charles@scooby/charles" from="[email protected]/charles" type="unavailable"><show>away</show><x xmlns="http://jabber.org/protocol/muc#user"><item jid="charles@scooby/charles" affiliation="none" role="none"/></x></presence></body>

------------------------------------------------------------------

POST http://gametime.dev.com/http-bind/ HTTP/1.1
Host: gametime.dev.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://gametime.dev.com/GameTimePages/GameTimeHuddle.aspx?List=3b9551da%2D46ed%2D4357%2Db93e%2Dfa55155366b1&ID=9&Source=http%3A%2F%2Fgametime%2Edev%2Ecom%2FLists%2FGameTimeHuddles%2FAllItems%2Easpx&ContentTypeId=0x01002167C00000000000000000000000000200D799BB01255FD045A12E56621F1F57C3
Content-Length: 83
Content-Type: text/plain; charset=UTF-8
Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}; Ribbon.DocLibListForm.Edit=750500|-1|497|-1474418945
Pragma: no-cache, no-cache, no-cache
Cache-Control: no-cache, no-cache, no-cache

<body rid='1132070775' xmlns='http://jabber.org/protocol/httpbind' sid='336bd0b0'/>

HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
SPRequestGuid: 99afb16e-65c5-4d3f-bb1e-a61d682483b2
Set-Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}; path=/
X-SharePointHealthScore: 3
Persistent-Auth: true
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 14.0.0.5130
Date: Tue, 10 May 2011 17:17:40 GMT
Content-Length: 57

<body xmlns="http://jabber.org/protocol/httpbind"></body>

------------------------------------------------------------------

POST http://gametime.dev.com/http-bind/ HTTP/1.1
Host: gametime.dev.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://gametime.dev.com/GameTimePages/GameTimeHuddle.aspx?List=3b9551da%2D46ed%2D4357%2Db93e%2Dfa55155366b1&ID=9&Source=http%3A%2F%2Fgametime%2Edev%2Ecom%2FLists%2FGameTimeHuddles%2FAllItems%2Easpx&ContentTypeId=0x01002167C00000000000000000000000000200D799BB01255FD045A12E56621F1F57C3
Content-Length: 83
Content-Type: text/plain; charset=UTF-8
Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}; Ribbon.DocLibListForm.Edit=750500|-1|497|-1474418945
Pragma: no-cache
Cache-Control: no-cache

<body rid='1132070775' xmlns='http://jabber.org/protocol/httpbind' sid='336bd0b0'/>

HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
SPRequestGuid: ac627294-ae2e-4d1a-91a6-b756fc8a0878
Set-Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}; path=/
X-SharePointHealthScore: 3
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 14.0.0.5130
Date: Tue, 10 May 2011 17:16:41 GMT
Content-Length: 103

<body xmlns="http://jabber.org/protocol/httpbind" type="terminate" condition="policy-violation"></body>

And the Fiddler trace for the same sequence in IE9

POST http://gametime.dev.com/http-bind/ HTTP/1.1
Accept: */*
Accept-Language: en-us
Referer: http://gametime.dev.com/GameTimePages/GameTimeHuddle.aspx?List=3b9551da%2D46ed%2D4357%2Db93e%2Dfa55155366b1&ID=9&Source=http%3A%2F%2Fgametime%2Edev%2Ecom%2FLists%2FGameTimeHuddles%2FAllItems%2Easpx&ContentTypeId=0x01002167C00000000000000000000000000200D799BB01255FD045A12E56621F1F57C3
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: gametime.dev.com
Content-Length: 195
Connection: Keep-Alive
Pragma: no-cache
Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}

<body rid='103763' xmlns='http://jabber.org/protocol/httpbind' sid='7ceecff0'><presence type='unavailable' to='[email protected]' xmlns='jabber:client'/></body>

HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
SPRequestGuid: f0a999e9-876b-4815-9be9-094163cd034b
Set-Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}; path=/
X-SharePointHealthScore: 3
Persistent-Auth: true
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 14.0.0.5130
Date: Tue, 10 May 2011 17:19:52 GMT
Content-Length: 389

<body xmlns='http://jabber.org/protocol/httpbind'><presence xmlns="jabber:client" to="charles@scooby/LoginAssistant" from="[email protected]/charles" type="unavailable"><x xmlns="http://jaber.org/protocol/muc"/><x xmlns="http://jabber.org/protocol/muc#user"><item jid="charles@scooby/LoginAssistant" affiliation="none" role="none"/></x></presence></body>

------------------------------------------------------------------

POST http://gametime.dev.com/http-bind/ HTTP/1.1
Accept: */*
Accept-Language: en-us
Referer: http://gametime.dev.com/GameTimePages/GameTimeHuddle.aspx?List=3b9551da%2D46ed%2D4357%2Db93e%2Dfa55155366b1&ID=9&Source=http%3A%2F%2Fgametime%2Edev%2Ecom%2FLists%2FGameTimeHuddles%2FAllItems%2Easpx&ContentTypeId=0x01002167C00000000000000000000000000200D799BB01255FD045A12E56621F1F57C3
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: gametime.dev.com
Content-Length: 79
Connection: Keep-Alive
Pragma: no-cache
Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}

<body rid='103764' xmlns='http://jabber.org/protocol/httpbind' sid='7ceecff0'/>

HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
SPRequestGuid: a956db8f-2e5d-4a8c-a15a-264cebdfd108
Set-Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}; path=/
X-SharePointHealthScore: 3
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 14.0.0.5130
Date: Tue, 10 May 2011 17:19:55 GMT
Content-Length: 70

<body xmlns="http://jabber.org/protocol/httpbind" ack="103765"></body>

------------------------------------------------------------------

POST http://gametime.dev.com/http-bind/ HTTP/1.1
Accept: */*
Accept-Language: en-us
Referer: http://gametime.dev.com/GameTimePages/GameTimeHuddle.aspx?List=3b9551da%2D46ed%2D4357%2Db93e%2Dfa55155366b1&ID=9&Source=http%3A%2F%2Fgametime%2Edev%2Ecom%2FLists%2FGameTimeHuddles%2FAllItems%2Easpx&ContentTypeId=0x01002167C00000000000000000000000000200D799BB01255FD045A12E56621F1F57C3
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: gametime.dev.com
Content-Length: 269
Connection: Keep-Alive
Pragma: no-cache
Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}

<body rid='103765' xmlns='http://jabber.org/protocol/httpbind' sid='7ceecff0'><presence xmlns='jabber:client'/><presence to='[email protected]/charles' xmlns='jabber:client'><x xmlns='http://jaber.org/protocol/muc'/></presence></body>

HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
SPRequestGuid: 3a86922d-d09b-4b61-bb4b-05eb75b1c2f4
Set-Cookie: WSS_KeepSessionAuthenticated={4eb5a3d5-4cf9-4f1d-8fd0-42e8939c29a8}; path=/
X-SharePointHealthScore: 3
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 14.0.0.5130
Date: Tue, 10 May 2011 17:19:55 GMT
Content-Length: 377

<body xmlns='http://jabber.org/protocol/httpbind'><presence xmlns="jabber:client" to="charles@scooby/LoginAssistant" from="[email protected]/charles"><x xmlns="http://jaber.org/protocol/muc"/><x xmlns="http://jabber.org/protocol/muc#user"><item jid="charles@scooby/LoginAssistant" affiliation="none" role="participant"/></x></presence></body>

Variables named "self" in core.js should be renamed to "me"

Since "self" is a reserved word in Javascript, the "self" variables created in the Strophe._dataRecv and Strophe.isMatch functions should be renamed to "me". This was silently breaking in Internet Explorer 6,7,8 when a response had more than one element and the Strophe.forEachChild method was invoked in Strophe._dataRecv. I guess it was loosing its scope somehow...Thanks IE!

how to use strophe to dynamically create rooms for users and friends

hi,
i have an application which has users which have friends etc. the app will have chat service for the users.the problem is i can get a list of the users friends and i want to be able to create a chat room on the fly for this group of people and transmit it to all other friends of the user to be able to join
is this possible with strophe.js

thanx

strophe corrupts unicode JID node names.

(As reported at https://github.com/amiadogroup/candy/issues/24 )

The strophe js lib corrupts unicode JID nodes as a shortcoming of javascript itself.
(Unicode node names are valid per http://xmpp.org/extensions/xep-0029.html#sect-id317032 )

This can be worked around using the method described at http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html and https://developer.mozilla.org/en/DOM/window.btoa#Unicode_Strings

I've applied the same changes as done for the candy libs to strophe 1.0.2 to create the diff below.

--- strophe.js  2011-08-25 00:25:54.839392300 -0400
+++ strophe-unicode.js  2011-08-25 00:29:37.644136000 -0400
@@ -3019,9 +3019,9 @@
         } else if (do_sasl_plain) {
             // Build the plain auth string (barejid null
             // username null password) and base 64 encoded.
-            auth_str = Strophe.getBareJidFromJid(this.jid);
+            auth_str = unescape(encodeURIComponent(Strophe.getBareJidFromJid(this.jid)));
             auth_str = auth_str + "\u0000";
-            auth_str = auth_str + Strophe.getNodeFromJid(this.jid);
+            auth_str = auth_str + unescape(encodeURIComponent(Strophe.getNodeFromJid(this.jid)));
             auth_str = auth_str + "\u0000";
             auth_str = auth_str + this.pass;

@@ -3102,14 +3102,14 @@
             digest_uri = digest_uri + "/" + host;
         }

-        var A1 = MD5.hash(Strophe.getNodeFromJid(this.jid) +
+        var A1 = MD5.hash(unescape(encodeURIComponent(Strophe.getNodeFromJid(this.jid))) +
                           ":" + realm + ":" + this.pass) +
             ":" + nonce + ":" + cnonce;
         var A2 = 'AUTHENTICATE:' + digest_uri;

         var responseText = "";
         responseText += 'username=' +
-            this._quote(Strophe.getNodeFromJid(this.jid)) + ',';
+            this._quote(unescape(encodeURIComponent(Strophe.getNodeFromJid(this.jid)))) + ',';
         responseText += 'realm=' + this._quote(realm) + ',';
         responseText += 'nonce=' + this._quote(nonce) + ',';
         responseText += 'cnonce=' + this._quote(cnonce) + ',';

if server acks during authentication, strophe stops and does not error

I've run into an issue with my openfire server where sometimes (1 in 2 to 1 in 30 attempts) during the authentication flow, I get an ack from the server instead of a proper response. Because the long polls are not yet set up in the authentication flow, no handlers get called and strophe pretty much just does nothing.

I wrote a fix in the _dataRecv handler to handle this case by re-sending a request to the server

    // if server returns an ack during the auth flow, reconnect for response
    if (elem.getAttribute("ack") && !this.authenticated) {
        this.rid = elem.getAttribute("ack");
        var body = this._buildBody();
        var newReq = new Strophe.Request(body.tree(),
                this._onRequestStateChange.bind(this).prependArg(this._dataRecv.bind(this)), elem.getAttribute("ack"));
        this._requests.push(newReq);
        this._processRequest(this._requests.length - 1);
    }

I didn't handle it, but if the server sends some other unexpected response, then strophe will still stall out without any error condition. There should be some sort of error handling for unexpected server responses during the authentication flow, before the long polls are initialized.

404 Error when attaching

Hi, I use a module to communicate to XMPP in different websites and sometimes when I'm trying to attach a session I receive a 404 Not Found from the server after some successful exchange of packages to that same address. I don't know exactly what to post. But for starters I can send a log from wireshark.

POST http://ip-address/xmpp-bosh/ HTTP/1.1
Host: ip-address
Proxy-Connection: keep-alive
Content-Length: 114
Origin: http://localhost
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/17.0.963.79 Chrome/17.0.963.79 Safari/535.11
Content-Type: application/xml
Accept: */*
Referer: http://localhost/barometre/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

<body rid='755570483' xmlns='http://jabber.org/protocol/httpbind' sid='74512fe31b8830e03971f540ca241eb28c93099d'/>

HTTP/1.1 200 OK
Via: 1.1 NGRPSIN-PROXY
Connection: Keep-Alive
Proxy-Connection: Keep-Alive
Content-Length: 51
Date: Wed, 28 Mar 2012 07:51:35 GMT
Content-Type: text/xml; charset=utf-8
Server: nginx/1.0.10
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type

<body xmlns='http://jabber.org/protocol/httpbind'/>

POST http://ip-address/xmpp-bosh/ HTTP/1.1
Host: ip-address
Proxy-Connection: keep-alive
Content-Length: 153
Origin: http://localhost
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/17.0.963.79 Chrome/17.0.963.79 Safari/535.11
Content-Type: application/xml
Accept: */*
Referer: http://localhost/barometre/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

<body rid='755570485' xmlns='http://jabber.org/protocol/httpbind' sid='74512fe31b8830e03971f540ca241eb28c93099d'><presence xmlns='jabber:client'/></body>

HTTP/1.1 200 OK
Via: 1.1 NGRPSIN-PROXY
Connection: Keep-Alive
Proxy-Connection: Keep-Alive
Content-Length: 51
Date: Wed, 28 Mar 2012 07:51:36 GMT
Content-Type: text/xml; charset=utf-8
Server: nginx/1.0.10
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type

<body xmlns='http://jabber.org/protocol/httpbind'/>

POST http://ip-address/xmpp-bosh/ HTTP/1.1
Host: ip-address
Proxy-Connection: keep-alive
Content-Length: 114
Origin: http://localhost
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/17.0.963.79 Chrome/17.0.963.79 Safari/535.11
Content-Type: application/xml
Accept: */*
Referer: http://localhost/barometre/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

<body rid='755570486' xmlns='http://jabber.org/protocol/httpbind' sid='74512fe31b8830e03971f540ca241eb28c93099d'/>

HTTP/1.1 200 OK
Via: 1.1 NGRPSIN-PROXY
Connection: Keep-Alive
Proxy-Connection: Keep-Alive
Content-Length: 95
Date: Wed, 28 Mar 2012 07:51:36 GMT
Content-Type: text/xml; charset=utf-8
Server: nginx/1.0.10
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type

<body xmlns='http://jabber.org/protocol/httpbind' type='terminate' condition='item-not-found'/>

POST http://ip-address/xmpp-bosh/ HTTP/1.1
Host: ip-address
Proxy-Connection: keep-alive
Content-Length: 189
Origin: http://localhost
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/17.0.963.79 Chrome/17.0.963.79 Safari/535.11
Content-Type: application/xml
Accept: */*
Referer: http://localhost/barometre/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

<body rid='755570487' xmlns='http://jabber.org/protocol/httpbind' sid='74512fe31b8830e03971f540ca241eb28c93099d' type='terminate'><presence xmlns='jabber:client' type='unavailable'/></body>

HTTP/1.1 404 Not Found
Via: 1.1 NGRPSIN-PROXY
Connection: Keep-Alive
Proxy-Connection: Keep-Alive
Content-Length: 0
Date: Wed, 28 Mar 2012 07:51:36 GMT
Content-Type: text/xml; charset=utf-8
Server: nginx/1.0.10
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type

CDATA not sent

cdata is not sent even if the nodeType is set numerically to what should match strophe's internal definition of a cdata node.

-A

Web Worker for I/O

Hey,

Do you think it would be possible to delegate all the BOSH I/O to a dedicated pool of web workers?
If yes, do you plan to add this? I quickly look on how to do something like this, and it will need to rewrite a certain amount of stuff. But do you like the idea? and does it worth the effort in term of performances?

Internet Explorer 8 and Strophe.Builder.cnode error

Internet Explorer 8 (full version 8.0.7600.16385) reports a "wrong number of arguments" in Strophe.Builder.cnode function, on line 1361 of the built strophe.js file. That corresponds to this line:

var newElem = xmlGen.importNode ? xmlGen.importNode(elem, true) : Strophe.copyElement(elem);

Commenting out the xmlGen.importNode invocation and just going with Strophe.copyElement fixes the problem.

I'm not sure of the implications, but it doesn't work for me the way it is now, and I'm using a version with the ternary operator removed. I can send a pull request with the patch if anyone is interested.

Sincerely,
Ivan

SCRAM-SHA-1 Browser Issues

I'm using the current head of strophejs master, and I notice it now uses SCRAM-SHA-1 if the server supports it. For me at least, it causes IE7 to hang until I get a dialog asking me if I want to stop the script. In Firefox 11, the browser hangs for a couple of seconds during the auth process, but works. In Chrome 17, all is fine. I was wondering if anyone else was having the same issue?

I've tested using both the strophe basic example and my own application built using strophe and get the same result with both. I'm using ejabberd 2.1.10 for the server. Using an older version of strophe which uses DIGEST-MD5 during the auth process also works fine across all browsers I've tested.

Disconnect twice

Unfortunately I couldn't get the real issue tracker to let me login, so I have to post this here. Apologies.

I'm seeing Strophe.js post two presence unavailable stanzas to prosody's mod_bosh on disconnect (using the basic strophe.js example). This is confirmed by the prosody log as here: http://conference.prosody.im:5280/pastebin/be70c7d8-623b-4ec6-9988-b30b7cc8cfa4

In Firefox and Chrome (both on OS X, most recent stable versions), the strophe raw output looks like this: http://gist.github.com/509544, but on Safari only one presence unavailable is printed but two are still sent.

Strophe loses handler list if a handler throws an error

In the _dataRecv function, while calling handlers, hand.run is called without any exception handling.

Since Handler.run bubbles any exception that it gets from the handler function, it breaks the for loop inside the _dataRecv function and Strophe loses the list of handlers + does not call the others.

An easy fix would be encapsulating the hand.run call inside the _dataRecv function and removing the handler if it throws an exception (asif it returned false)

Conditions for stream:error stanzas not available

We're using the defined conditions for stream:errors to display detailed error messaging to users when they get disconnected. It appears that strophe isn't handling any condition nodes other than "conflict" which it checks for specifically and forwards on.

One example of the handling is around the _dataRecv function in core.js:

    var typ = elem.getAttribute("type");
    var cond, conflict;
    if (typ !== null && typ == "terminate") {
        // an error occurred
        cond = elem.getAttribute("condition");
        conflict = elem.getElementsByTagName("conflict");
        if (cond !== null) {
            if (cond == "remote-stream-error" && conflict.length > 0) {
                cond = "conflict";
            }
            this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
        } else {
            this._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");
        }
        this.disconnect();
        return;
    }

The spec for the stream:error and condition nodes that I'm referring to is at http://xmpp.org/rfcs/rfc3920.html#rfc.section.4.7.2

Basically, we send data along the lines of:

<stream:error xmlns:stream='http://etherx.jabber.org/streams'>
    <system-shutdown xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
    <text xmlns='urn:ietf:params:xml:ns:xmpp-streams'>
        The system is being shut down for an upgrade. Please wait a moment before reconnecting.
    </text>
</stream:error>

I'm trying get the "system-shutdown" and "text" nodes in order to display some more specific messaging beyond "You have been disconnected" to the user.

Implement pluggable SASL handlers in strophe

After reviewing strophe.js (within the body of _connect_cb function) I've found that vars do_sasl_digest_md5,
do_sasl_plain and do_sasl_anonymous are used to select the SASL auth
mechanism that should be used .

Since it's possible to have custom auth mechanisms, IMO it would
be nice to have methods like PyXMPP's challenge which would make
strophe more flexible & easy-to-extend . I mean , the actual code
looks like a well known anti-pattern , and it would be nice to be able
to register «auth handlers» and select the correct match considering
the value of mech var .

IOW do something like

auth_mechs = {
 'DIGEST-MD5' : handle_DIGEST_MD5,
 'PLAIN' : handle_PLAIN,
 'ANOMYNOUS' : handle_ANONYMOUS,
}

auth_handler = auth_mechs[mech] || handle_no_auth_mech;

something = auth_handler(...)

... in that case it would be possible to add custom auth without
patching strophe.js by executing auth_mechs['MY_METHOD'] = handle_MY_METHOD similar to what is possible in PyXMPP by
implementing and registering ClientAuthenticator subclasses .

Strophe.getText should use textContent?

The code:
getText: function (elem)
{
if (!elem) { return null; }

    var str = "";
    if (elem.childNodes.length === 0 && elem.nodeType ==
        Strophe.ElementType.TEXT) {
        str += elem.nodeValue;
    }

    for (var i = 0; i < elem.childNodes.length; i++) {
        if (elem.childNodes[i].nodeType == Strophe.ElementType.TEXT) {
            str += elem.childNodes[i].nodeValue;
        }
    }

    return str;
},

Why not use the standardized textContent attribute. However, we need to check for its existence, since IE doesn't know about it.

EDIT: Was mistaken previously.

IE8/9 duplicated request & inactive session

Hi,

I have a problem with Internet Explorer (8 & 9, I don't have to support IE7-). Everything works fine in other browsers.

I have found some times ago that Internet Explorer dropped my strophe connection without any reasons, just after sending two requests with the same rid. By drop connection, I mean that I'm unable to send any logs anymore. The connection is still there, sending blank request every 60s.

Here is the traces which are outputted by Strophe :

Traces

It's just after the duplicated request that IE does not send request anymore. Just blank requests.

If I comment this._data.push(null) in :


        // if no requests are in progress, poll
        if (this.authenticated && this._requests.length === 0 &&
            this._data.length === 0 && !this.disconnecting) {
            Strophe.info("no requests during idle cycle, sending " +
                         "blank request");
            this._data.push(null);
        }

There is no problems anymore. Obviously, I don't want to do that, but I can't figure out why it works when I comment this line.

I'm struggling with that for a few weeks, and I can't find why only IE has a problem. Thanks for helping me.

Context : I have an old version of Strophe with some small modifications to match our needs. These modifications shouldn't affect this part of Strophe. I've also upgraded Strophe to see if it changes anything, but the bug is still there.

v1.0.2: Strophe.serialize() doubles escaping

Hi,

In the recent version of strophe, a message sent via the muc-plugin gets escaped twice.

e.g.

<3 gets escaped to &lt;3 instead of <3 as before.

This happens also for apostrophes, quotes etc.

I think this is a bug, but don't know if the MUC-Plugin doesn't handle it correctly or if it's something inside the strophe-core (I think it's in strophe..)

Cheers,
Michael

Function.prototype.bind is not compatible with the ECMAScript 5.

Hey Jack-

The Function.prototype.bind right now in Strophe isn't compatible with browsers that use bind() natively. To ensure compatibility, it'd be nice to see Strophe's bind() take all arguments after the thisArg argument to be curried onto the function.

Thanks!

xmlhttprequest not working for cross-domain with IE8

Hi,
I found under IE8, the strophejs cannot connect the server when cross-domain. And there is a plugin to fix it by using xDomainRequest which still not working.
Is there other solutions for fix this problem?

Thanks,

Baohua

HTML escaping fails in Strophe.serialize

Very contrived example:
var aMsg = $msg({to:'[email protected]', type:'chat', text:'hello'})
con.send(aMsg);

Which results in a message that fails to send, containing:

Offending code, within the function Strophe.serialize:
result = "<" + nodeName;
for (i = 0; i < elem.attributes.length; i++) {
if(elem.attributes[i].nodeName != "_realname") {
result += " " + elem.attributes[i].nodeName.toLowerCase() +
"='" + elem.attributes[i].value
.replace("&", "&")
.replace("'", "'")
.replace("<", "<") + "'";
}
}

Proposed fix:
Change Strophe.xmlescape to handle apostrophe and quotes as well, and use it in serialize.
xmlescape: function(text)
{
text = text.replace(/&/g, "&");
text = text.replace(/</g, "<");
text = text.replace(/>/g, ">");
text = text.replace(/"/g, """);
text = text.replace(/'/g, "'");
return text;
},

Then within Strophe.serialize we'll have:
result += " " + elem.attributes[i].nodeName.toLowerCase() +
"='" + Strophe.xmlescape(elem.attributes[i].value) + "'";

EDIT: This is Strophe v1.0, on both Chrome 8 and Firefox 3.6.
EDIT2: Found the issue. See above.

Message terminate not sent

When I try to disconnect, the message "terminate" is not sent.
I call this :
connection.flush();
connection.disconnect();

Some requests still to be processed. I have done a hack for me in "_throttledRequestHandler:"

    if (this._requests.length > 0) {
    for(i=0; i<this._requests.length; i++)
            this._processRequest(i);
    }

I don't know well the strophejs framework so maybe there is a clean way to do this?

Issue appears in Google Chrome on Ubuntu

fix of pause function to prevent browser from accidental policy violation

The base problem is that on the unload event, it's possible to have
the aborted XHRs get retried (this can happen even if you've called
connection.pause()).
The fix is to fix pause() so that it won't resend requests that fail
while paused. Then in your unload handler, you call connection.pause()
before saving the RID, and that should ensure Strophe doesn't do any
more requests.

RID problem with IE7 when using attach()

Sometimes when you have a large RID, it is altered when strophe create the request. RID like : 6321769680499603 -> 6.3217696804996E+15

It causes : HTTP/1.1 400 Body missing RID (Request ID)

I modified your code with the following and it does work (in xmlElement function) :

if (typeof(arguments[a][k]) == "number"){
node.setAttribute(k, arguments[a][k].toString());
}else{
node.setAttribute(k, arguments[a][k]);
}

http://groups.google.com/group/strophe/browse_thread/thread/9f44eaf29509c327

Strophe.js needs a roadmap for websockets

There is an existing websocket version of strophe.js (see https://github.com/superfeedr/strophejs/tree/protocol-ed/src, https://github.com/superfeedr/msgboy/tree/master/lib/strophejs and http://code.google.com/p/node-xmpp-bosh/) and the author of strophe.js has recently drafted xmpp-over-websockets (http://tools.ietf.org/html/draft-moffitt-xmpp-over-websocket-00).

Still, there is no mention of websockets at all on strophe.js project. Elaborating on your plans for websockets would increase the perceived viability of this project.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.