ArrayCollection

Has anyone gotten this error? I get it when I try to open a room or get a roster. Both of these classes extend the mx.collections.ArrayCollection class… Help!

TypeError: Error #1009: Cannot access a property or method of a null object reference.

at mx.collections::ListCollectionView$/::loadResources()

at mx.collections::ListCollectionView$cinit()

at global$init()

at global$init()

at default_fla::MainTimeline/default_fla::frame1()

Forgot to mention… This is using XIFF 3.

Message was edited by: ambelovsky

I mucked around with the Room.as code a bit to get it off the Flex garbage. Instead of ArrayCollection, I used fl.data.DataProvider. It’'s pretty much the same thing. I also fixed another issue that arose. Below is the code that I did:

See next post…

Message was edited by: ambelovsky

// BELOW IS A SLIGHTLY BETTER FORMATTED VERSION //

/*
* Copyright (C) 2003-2007
* Nick Velloff <nick.velloff@gmail.com>
* Derrick Grigg <dgrigg@rogers.com>
* Sean Voisen <sean@voisen.org>
* Sean Treadway <seant@oncotype.dk>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
*/ package org.jivesoftware.xiff.conference
{<br />
import org.jivesoftware.xiff.core.XMPPConnection;<br/>
import org.jivesoftware.xiff.data.*;<br/>
import org.jivesoftware.xiff.data.muc.*;<br/>
import org.jivesoftware.xiff.data.forms.FormExtension;<br/>
import org.jivesoftware.xiff.events.MessageEvent;<br/>
import org.jivesoftware.xiff.events.PresenceEvent;<br/>
import org.jivesoftware.xiff.events.DisconnectionEvent;<br/>
import org.jivesoftware.xiff.events.RoomEvent;<br/>
import fl.data.DataProvider;<br/>
<br/>
/**<br/>
* Dispatched when the room subject changes.<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent.SUBJECT_CHANGE<br/>
*/<br/>
[Event( name="subjectChange", type="org.jivesoftware.xiff.events.RoomEvent" )]<br/>
<br/>
/**<br/>
* Dispatched whenever a new message intented for all room occupants is received. The <br/>
* &lt;code&gt;RoomEvent&lt;/code&gt; class will contain an attribute &lt;code&gt;data&lt;/code&gt; with the <br/>
* group message as an instance of the &lt;code&gt;Message&lt;/code&gt; class.<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event( name="groupMessage", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched whenever a new private message is received. The &lt;code&gt;RoomEvent&lt;/code&gt; class<br/>
* contains an attribute &lt;code&gt;data&lt;/code&gt; with the private message as an instance of the <br/>
* &lt;code&gt;Message&lt;/code&gt; class.<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event(name="privateMessage", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched when you have entered the room and messages that are sent<br/>
* will be displayed to other users. The room''s role and affiliation will<br/>
* be visible from this point forward.<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event(name="roomJoin", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched when the server acknoledges that you have the left the room.<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event(name="roomLeave", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched when an affiliation list has been requested. The event object contains an <br/>
* array of &lt;code&gt;MUCItems&lt;/code&gt; containing the JID and affiliation properties.<br/>
*<br/>
* &lt;p&gt;To grant or revoke permissions based on this list, only send the changes you wish to <br/>
* make, calling grant/revoke with the new affiliation and existing JID.&lt;/p&gt;<br/>
* <br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event(name="affiliations", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched when an administration action failed.<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
* @see org.jivesoftware.xiff.core.XMPPConnection.error<br/>
*/<br/>
[Event(name="adminError", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched whenever an occupant joins the room. The &lt;code&gt;RoomEvent&lt;/code&gt; instance will <br/>
* contain an attribute &lt;code&gt;nickname&lt;/code&gt; with the nickname of the occupant who joined.<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event(name="userJoin", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched whenever an occpant leaves the room. The &lt;code&gt;RoomEvent&lt;/code&gt; instance will<br/>
* contain an attribute &lt;code&gt;nickname&lt;/code&gt; with the nickname of the occupant who left.<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event(name="userDeparture", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched when the user''s preferred nickname already exists in the room. The <br/>
* &lt;code&gt;RoomEvent&lt;/code&gt; will contain an attribute &lt;code&gt;nickname&lt;/code&gt; with the nickname <br/>
* already existing in the room.<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event(name="nickConflict", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched when a room configuration form is required. This can occur during the <br/>
* creation of a room, or if a room configuration is requested. The &lt;code&gt;RoomEvent&lt;/code&gt;<br/>
* instance will contain an attribute &lt;code&gt;data&lt;/code&gt; that is an instance of an object <br/>
* with the following attributes:<br/>
* <br/>
* &lt;p&gt;&lt;code&gt;instructions&lt;/code&gt;: Instructions for the use of form&lt;br /&gt;<br/>
* &lt;code&gt;title&lt;/code&gt;: Title of the configuration form&lt;br /&gt;<br/>
* &lt;code&gt;label&lt;/code&gt;: A friendly name for the field&lt;br /&gt;<br/>
* &lt;code&gt;name&lt;/code&gt;: A computer readable identifier for the field used to identify <br/>
* this field in the result passed to &lt;code&gt;configure()&lt;/code&gt;&lt;br /&gt;<br/>
* &lt;code&gt;type&lt;/code&gt;: The type of the field to be displayed. Type will be a constant<br/>
* from the &lt;code&gt;FormField&lt;/code&gt; class.&lt;/p&gt;<br/>
* <br/>
* @see org.jivesoftware.xiff.data.forms.FormExtension<br/>
* @see org.jivesoftware.xiff.data.forms.FormField<br/>
* @see #configure<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event(name="configureForm", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Dispatched when an invite to this room has been declined by the invitee. The &lt;code&gt;RoomEvent&lt;/code&gt;<br/>
* &lt;code&gt;data&lt;/code&gt; property that has the following attributes:<br/>
*<br/>
* &lt;p&gt;&lt;code&gt;from&lt;/code&gt;: The JID of the user initiating the invite&lt;br /&gt;<br/>
* &lt;code&gt;reason&lt;/code&gt;: A string containing the reason to join the room&lt;br /&gt;<br/>
* &lt;code&gt;data&lt;/code&gt;: The original message containing the decline&lt;/p&gt;<br/>
*<br/>
* @eventType org.jivesoftware.xiff.events.RoomEvent<br/>
*/<br/>
[Event(name="declined", type="org.jivesoftware.xiff.events.RoomEvent")]<br/>
<br/>
/**<br/>
* Manages incoming and outgoing data from a conference room as part of multi-user conferencing (JEP-0045).<br/>
* You will need an instance of this class for each room that the user joins.<br/>
*<br/>
* @param connection An XMPPConnection instance that is providing the primary server connection<br/>
*/<br/>
public class Room extends DataProvider<br/>
{<br/>
public static const NO_AFFILIATION:String = MUC.NO_AFFILIATION;<br/>
public static const MEMBER_AFFILIATION:String = MUC.MEMBER_AFFILIATION;<br/>
public static const ADMIN_AFFILIATION:String = MUC.ADMIN_AFFILIATION;<br/>
public static const OWNER_AFFILIATION:String = MUC.OWNER_AFFILIATION;<br/>
public static const OUTCAST_AFFILIATION:String = MUC.OUTCAST_AFFILIATION;<br/>
<br/>
public static const NO_ROLE:String = MUC.NO_ROLE;<br/>
public static const MODERATOR_ROLE:String = MUC.MODERATOR_ROLE;<br/>
public static const PARTICIPANT_ROLE:String = MUC.PARTICIPANT_ROLE;<br/>
public static const VISITOR_ROLE:String = MUC.VISITOR_ROLE;<br/>
<br/>
private var myConnection:XMPPConnection;<br/>
private var myJID:String;<br/>
private var roomShortJID:String = '''';<br/>
private var serverAddress:String = '''';<br/>
private var myNickname:String;<br/>
private var myPassword:String;<br/>
private var myRole:String;<br/>
private var myAffiliation:String;<br/>
private var myIsReserved:Boolean;<br/>
private var mySubject:String;<br/>
<br/>
private var active:Boolean;<br/>
<br/>
// Used to store nicknames in pending status, awaiting change approval from server<br/>
private var pendingNickname:String; </p>
<br/>
<p>
private static var staticConstructorDependencies:Array = [ <br/>
FormExtension,<br/>
MUC<br/>
]<br/>
<br/>
private static var roomStaticConstructed:Boolean = RoomStaticConstructor();<br/>
<br/>
public function Room( aConnection:XMPPConnection=null )<br/>
{<br/>
active = false;<br/>
if (aConnection != null){<br/>
setConnection( aConnection );<br/>
}
} private static function RoomStaticConstructor():Boolean
{<br/>
MUC.enable();<br/>
FormExtension.enable();<br/>
<br/>
return true;<br/>
} /**
* Sets a reference to the XMPPConnection being used for incoming/outgoing XMPP data.
*
* @param connection The XMPPConnection instance to use.
* @see org.jivesoftware.xiff.core.XMPPConnection
*/
public function setConnection( connection:XMPPConnection ):void
{<br/>
if (myConnection != null)<br/>
{<br/>
myConnection.removeEventListener(MessageEvent.MESSAGE, handleEvent);<br/>
myConnection.removeEventListener(PresenceEvent.PRESENCE, handleEvent);<br/>
myConnection.removeEventListener(DisconnectionEvent.DISCONNECT, handleEvent); <br/>
} myConnection = connection; myConnection.addEventListener(MessageEvent.MESSAGE, handleEvent, false, 0, true);
myConnection.addEventListener(PresenceEvent.PRESENCE, handleEvent, false, 0, true);
myConnection.addEventListener(DisconnectionEvent.DISCONNECT, handleEvent, false, 0, true);
} /**
* Gets a reference to the XMPPConnection being used for incoming/outgoing XMPP data.
*
* @returns The XMPPConnection used
* @see org.jivesoftware.xiff.core.XMPPConnection
*/
public function getConnection():XMPPConnection
{<br/>
return myConnection;<br/>
} /** * Joins a conference room based on the parameters specified by the room
* properties. This call will create an instant room based on a default
* server configuration if the room doesn''t exist. * * &lt;p&gt;To create and begin the configuration process of a reserved room, pass
* &lt;code&gt;true&lt;/code&gt; to this method to begin the configuration process. When
* The configuration is complete, the room will be unlocked for others to join.
* Listen for the &lt;code&gt;RoomEvent.CONFIGURE_ROOM&lt;/code&gt; event to handle and * either return or cancel the configuration of the room.
*
* @param createReserved Set to true if you wish to create and configure a reserved room
* @return A boolean indicating whether the join attempt was successfully sent.
*/
public function join( createReserved:Boolean = false ):Boolean
{<br/>
if( !isActive() && myConnection.isActive() ) {<br/>
myIsReserved = createReserved == true ? true : false;<br/>
<br/>
var joinPresence:Presence = new Presence( getUserJID() ); </p>
<br/>
<p>
var muc:MUCExtension = new MUCExtension();<br/>
<br/>
if( password != null ) {<br/>
muc.password = password;<br/>
} joinPresence.addExtension(muc);
myConnection.send( joinPresence );
return true;
} return false;
} /**
* Leaves the current conference room, assuming that the user has joined one.
* If the user is not currently in a room, this method does nothing.
*/
public function leave():void
{<br/>
if( isActive() ) {<br/>
var leavePresence:Presence = new Presence( getUserJID(), null, Presence.UNAVAILABLE_TYPE );<br/>
myConnection.send( leavePresence );<br/>
<br/>
// Clear out the roster items<br/>
removeAll();<br/>
myConnection.removeEventListener(MessageEvent.MESSAGE, handleEvent);<br/>
myConnection.removeEventListener(PresenceEvent.PRESENCE, handleEvent);<br/>
myConnection.removeEventListener(DisconnectionEvent.DISCONNECT, handleEvent);<br/>
}
} /**
* Gets an instance of the &lt;code&gt;Message&lt;/code&gt; class that has been pre-configured to be * sent from this room. Use this method to get a &lt;code&gt;Message&lt;/code&gt; in order to add extensions * to outgoing room messages.
* * @param body The message body
* @param htmlBody The message body with HTML formatting
* @return A &lt;code&gt;Message&lt;/code&gt; class instance
*/
public function getMessage( body:String = null, htmlBody:String = null ):Message
{<br/>
var tempMessage:Message = new Message( getRoomJID(), null, body, htmlBody, Message.GROUPCHAT_TYPE );<br/>
return tempMessage;<br/>
} /**
* Sends a message to the conference room.
*
* @param body The message body
* @param htmlBody The message body with HTML formatting
*/
public function sendMessage( body:String=null, htmlBody:String=null ):void
{<br/>
if( isActive() ) {<br/>
var tempMessage:Message = new Message( getRoomJID(), null, body, htmlBody, Message.GROUPCHAT_TYPE );<br/>
myConnection.send( tempMessage );<br/>
}
} /**
* Sends a message to the conference room with an extension attached. * Use this method in conjunction with the &lt;code&gt;getMessage&lt;/code&gt; method.
*
* @param msg The message to send
*/
public function sendMessageWithExtension( msg:Message ):void
{<br/>
if( isActive() ) {<br/>
myConnection.send( msg );<br/>
}
} /**
* Sends a private message to a specific participant in the conference room.
*
* @param recipientNickname The conference room nickname of the recipient who should * receive the private message
* @param body The message body
* @param htmlBody The message body with HTML formatting
*/
public function sendPrivateMessage( recipientNickname:String, body:String = null, htmlBody:String = null ):void
{<br/>
if( isActive() ) {<br/>
var tempMessage:Message = new Message( getRoomJID() + "/" + recipientNickname, null, body, htmlBody, Message.CHAT_TYPE );<br/>
myConnection.send( tempMessage );<br/>
}
} /**
* Changes the subject in the conference room. You must have already joined the * room before you can change the subject.
*
* @param newSubject The new subject
*/
public function changeSubject( newSubject:String ):void
{<br/>
if( isActive() ) {<br/>
var tempMessage:Message = new Message( getRoomJID(), null, null, null, Message.GROUPCHAT_TYPE, newSubject );<br/>
myConnection.send( tempMessage );<br/>
}
} /**
* Kicks an occupant out of the room, assuming that the user has necessary * permissions in order to do so. If the user does not, the server will return an error.
*
* @param occupantNick The nickname of the room occupant to kick
* @param reason The reason for the kick
*/
public function kickOccupant( occupantNick:String, reason:String ):void
{<br/>
if( isActive() ) {<br/>
var tempIQ:IQ = new IQ( getRoomJID(), IQ.SET_TYPE, XMPPStanza.generateID("kick_occupant_") );<br/>
var ext:MUCAdminExtension = new MUCAdminExtension(tempIQ.getNode());<br/>
//ext.addItem(null, MUC.NO_ROLE, null, null, null, reason);<br/>
ext.addItem(null, MUC.NO_ROLE, occupantNick, null, null, reason); <br/>
tempIQ.addExtension(ext);<br/>
myConnection.send( tempIQ );<br/>
}
} /**
* In a moderated room, sets voice status to a particular occupant, assuming the user * has the necessary permissions to do so.
*
* @param occupantNick The nickname of the occupant to give voice
* @param voice Whether to add voice (true) or remove voice (false). Having voice means
* that the user is actually able to talk. Without voice the user is effectively muted.
*/
public function setOccupantVoice( occupantNick:String, voice:Boolean ):void
{<br/>
if( isActive() ) {<br/>
var tempIQ:IQ = new IQ( getRoomJID(), IQ.SET_TYPE, XMPPStanza.generateID("voice_") );<br/>
var ext:MUCAdminExtension = new MUCAdminExtension(tempIQ.getNode());<br/>
ext.addItem(null, voice ? MUC.PARTICIPANT_ROLE : MUC.VISITOR_ROLE);<br/>
tempIQ.addExtension(ext);<br/>
myConnection.send( tempIQ );<br/>
}
} /**
* Invites a user that is not currently a member of this room to this room.
*
* &lt;p&gt;You must have joined the room and have appropriate permissions to invite * other memebers, because the server will format and send the invite message to * as if it came from the room rather that you sending the invite directly from you.&lt;/p&gt;
*
* &lt;p&gt;To listen to invite events, add an event listener on your XMPPConnection to the
* &lt;code&gt;InviteEvent.INVITED&lt;/code&gt; event.&lt;/p&gt;
*
* @param jid A string JID of the user to invite.
* @param reason A string describing why you would like to invite the user.
*/
public function invite( jid:String, reason:String ):void
{<br/>
var msg:Message = new Message(getRoomJID())<br/>
var muc:MUCUserExtension = new MUCUserExtension();<br/>
<br/>
muc.invite(jid, undefined, reason);<br/>
<br/>
msg.addExtension(muc);<br/>
myConnection.send(msg);<br/>
} /**
* Actively decline an invitation. You can optionally ignore invitations
* but if you choose to decline an invitation, you call this method on
* a room instance that represents the room the invite originated from.
*
* &lt;p&gt;You do not need to have joined this room to decline an invitation&lt;/p&gt;
*
* &lt;p&gt;Note: mu-conference-0.6 does not allow users to send decline
* messages without joining first. If using this version of conferencing
* software, it is best to ignore invites.&lt;/p&gt;
*
* @param reason A string describing why the invitiation was declined
*/
public function decline(jid:String, reason:String):void
{<br/>
var msg:Message = new Message(getRoomJID())<br/>
var muc:MUCUserExtension = new MUCUserExtension();<br/>
<br/>
muc.decline(jid, undefined, reason);<br/>
<br/>
msg.addExtension(muc);<br/>
myConnection.send(msg);<br/>
} /**
* &lt;strong&gt;DEPRECATED! Use &lt;code&gt;getRoomJID&lt;/code&gt; instead.&lt;/strong&gt;
* Gets the fully qualified room name (room@server) of the current room.
*
* @return The fully qualified room name.
*/
public function getFullRoomName():String
{<br/>
return getRoomJID();<br/>
} /**
* Get the JID of the room.
*
* @return The room''s JID.
*/
public function getRoomJID():String
{<br/>
return myJID;<br/>
} /**
* Set the JID of the room in the form "room@conference.server"
*/
public function setRoomJID():void
{<br/>
myJID = roomShortJID + ''@'' + serverAddress;<br/>
} /**
* Get the JID of the conference room user.
*
* @return your JID in the room */
public function getUserJID():String
{<br/>
return getRoomJID() + "/" + nickname;<br/>
} /**
* Gets the user''s role in the conference room. * Possible roles are "visitor", "participant", "moderator" or no defined role.
*
* @return The user''s role
*/
public function getRole():String
{<br/>
return myRole;<br/>
} /**
* Gets the user''s affiliation for this room.
* Possible affiliations are "owner", "admin", "member", and "outcast". * It is also possible to have no defined affiliation.
*
* @return The user''s affiliation
*/
public function getAffiliation():String
{<br/>
return myAffiliation;<br/>
} /**
* Determines whether the connection to the room is active - that is, the user * is connected and has joined the room.
*
* @return True if the connection is active; false otherwise.
*/
public function isActive():Boolean
{<br/>
return active;<br/>
} private function handleEvent( eventObj:Object ):void
{<br/>
switch( eventObj.type )<br/>
{<br/>
case "message":<br/>
var msg:Message = eventObj.data;<br/>
<br/>
// Check to see that the message is from this room<br/>
if( isThisRoom( msg.from ) ) {<br/>
var e:RoomEvent;<br/>
if ( msg.type == Message.GROUPCHAT_TYPE ) {<br/>
// Check for a subject change<br/>
if( msg.subject != null ) {<br/>
mySubject = msg.subject;<br/>
e = new RoomEvent(RoomEvent.SUBJECT_CHANGE);<br/>
e.subject = msg.subject;<br/>
dispatchEvent(e);<br/>
}
else {<br/>
e = new RoomEvent(RoomEvent.GROUP_MESSAGE);<br/>
e.data = msg;<br/>
dispatchEvent(e);<br/>
}
} else if ( msg.type == Message.NORMAL_TYPE ) {<br/>
try<br/>
{<br/>
var form:Array = msg.getAllExtensionsByNS(FormExtension.NS)[0];<br/>
if (form) {<br/>
e = new RoomEvent(RoomEvent.CONFIGURE_ROOM);<br/>
e.data = form;<br/>
dispatchEvent(e);<br/>
}
}
catch (e:Error)
{<br/>
trace("Error : null trapped. Resuming.");<br/>
}
}
} // It could be a private message via the conference
else if( isThisUser(msg.to) && msg.type == Message.CHAT_TYPE ) {<br/>
e = new RoomEvent(RoomEvent.PRIVATE_MESSAGE);<br/>
e.data = msg;<br/>
dispatchEvent(e);<br/>
} // Could be an decline to a previous invite
else {<br/>
try<br/>
{<br/>
var muc:MUCUserExtension = msg.getAllExtensionsByNS(MUCUserExtension.NS)[0];<br/>
if (muc && muc.type == MUCUserExtension.DECLINE_TYPE) <br/>
{<br/>
e = new RoomEvent(RoomEvent.DECLINED);<br/>
e.from = muc.reason;<br/>
e.reason = muc.reason;<br/>
e.data = msg;<br/>
dispatchEvent(e);<br/>
}
}
catch (err:Error) {<br/>
trace("Error : null trapped. Resuming.");<br/>
}
}
break; case "presence":
var presence:Presence = eventObj.data; //trace("ROOM presence: " + presence.from + " : " + nickname); if (presence.type == Presence.ERROR_TYPE) {<br/>
switch (presence.errorCode) {<br/>
case 409:<br/>
e = new RoomEvent(RoomEvent.NICK_CONFLICT);<br/>
e.nickname = nickname;<br/>
dispatchEvent(e);<br/>
break;<br/>
}
} else if( isThisRoom( presence.from ) ) {<br/>
// If the presence has our pending nickname, nickname change went through<br/>
if( presence.from.split( "/" )[1] == pendingNickname ) {<br/>
myNickname = pendingNickname;<br/>
pendingNickname = null;<br/>
}
try
{<br/>
var user:MUCUserExtension = presence.getAllExtensionsByNS(MUCUserExtension.NS)[0];<br/>
if( user.statusCode == 201 ) {<br/>
unlockRoom(myIsReserved);<br/>
}
}
catch (e:Error)
{<br/>
trace("Error : null trapped. Resuming.");<br/>
} updateRoomRoster( presence ); if (presence.type == Presence.UNAVAILABLE_TYPE && isActive() && isThisUser(presence.from)) {<br/>
//trace("Room: becoming inactive: " + presence.getNode());<br/>
active = false;<br/>
e = new RoomEvent(RoomEvent.ROOM_LEAVE);<br/>
dispatchEvent(e);<br/>
}
}
break; case "disconnection":
// The server disconnected, so we are no longer active
active = false;
removeAll();
e = new RoomEvent(RoomEvent.ROOM_LEAVE);
dispatchEvent(e);
break;
}
} /*
* Room owner (creation/configuration/destruction) methods
*/ private function unlockRoom( isReserved:Boolean ):void
{<br/>
// http://www.jabber.org/jeps/jep-0045.html#createroom<br/>
<br/>
if( isReserved ) {<br/>
requestConfiguration();<br/>
} else {<br/>
// Send an empty configuration form to open the instant room<br/>
<br/>
// The IQ.result for this request will signify that the room is<br/>
// unlocked. Sometimes there are messages that are sent before<br/>
// the request is returned. It may be smart to either block those<br/>
// messages, or provide 2 events "beginConfiguration" and "endConfiguration"<br/>
// so the application can decide to block configuration messages<br/>
<br/>
var iq:IQ = new IQ(getRoomJID(), IQ.SET_TYPE);<br/>
var owner:MUCOwnerExtension = new MUCOwnerExtension();<br/>
var form:FormExtension = new FormExtension();<br/>
<br/>
form.type = FormExtension.SUBMIT_TYPE;<br/>
<br/>
owner.addExtension(form);<br/>
iq.addExtension(owner);<br/>
myConnection.send(iq);<br/>
}
} /**
* Requests a configuration form from the room. Listen to &lt;code&gt;configureForm&lt;/code&gt;
* event to fill out the form then call either &lt;code&gt;configure&lt;/code&gt; or
* &lt;code&gt;cancelConfiguration&lt;/code&gt; to complete the configuration process
*
* You must be joined to the room and have the owner affiliation to request * a configuration form
*
* @see #configureForm
* @see #configure
* @see #cancelConfiguration
*/
public function requestConfiguration():void
{<br/>
var iq:IQ = new IQ(getRoomJID(), IQ.GET_TYPE);<br/>
var owner:MUCOwnerExtension = new MUCOwnerExtension();<br/>
<br/>
iq.callbackScope = this;<br/>
iq.callbackName = "finish_requestConfiguration";<br/>
iq.addExtension(owner);<br/>
<br/>
myConnection.send(iq);<br/>
} /**
* @private
* * IQ callback when form is ready
*/
public function finish_requestConfiguration(iq:IQ):void
{<br/>
try<br/>
{<br/>
var owner:MUCOwnerExtension = iq.getAllExtensionsByNS(MUCOwnerExtension.NS)[0];<br/>
var form:FormExtension = owner.getAllExtensionsByNS(FormExtension.NS)[0];<br/>
<br/>
if( form.type == FormExtension.REQUEST_TYPE ) {<br/>
var e:RoomEvent = new RoomEvent(RoomEvent.CONFIGURE_ROOM);<br/>
e.data = form;<br/>
dispatchEvent(e);<br/>
}
}
catch (e:Error)
{<br/>
trace("Error : null trapped. Resuming.");<br/>
} } /**
* Sends a configuration form to the room.
*
* You must be joined and have owner affiliation to configure the room
*
* @param fieldmap A hash that is an object with keys being the room configuration
* form variables and the values being arrays. For single value fields, use a single * element array.
* @see #configureForm
*/
public function configure(fieldmap:Object):void
{<br/>
var iq:IQ = new IQ(getRoomJID(), IQ.SET_TYPE);<br/>
var owner:MUCOwnerExtension = new MUCOwnerExtension();<br/>
var form:FormExtension;<br/>
<br/>
if (fieldmap is FormExtension) {<br/>
form = FormExtension(fieldmap);<br/>
} else {<br/>
form = new FormExtension();<br/>
fieldmap["FORM_TYPE"] = [MUCOwnerExtension.NS];<br/>
form.setFields(fieldmap);<br/>
}
form.type = FormExtension.SUBMIT_TYPE;
owner.addExtension(form); iq.addExtension(owner);
myConnection.send(iq);
} /**
* Cancels the configuration process. The room may still be locked if
* you cancel the configuration process when attempting to join a
* reserved room.
*
* &lt;p&gt;You must have joined the room and have the owner affiliation to * configure the room.&lt;/p&gt;
*
* @see #configureForm
* @see #join
*/
public function cancelConfiguration():void
{<br/>
var iq:IQ = new IQ(getRoomJID(), IQ.SET_TYPE);<br/>
var owner:MUCOwnerExtension = new MUCOwnerExtension();<br/>
var form:FormExtension = new FormExtension();<br/>
<br/>
form.type = FormExtension.CANCEL_TYPE;<br/>
<br/>
owner.addExtension(form);<br/>
iq.addExtension(owner);<br/>
myConnection.send(iq);<br/>
} /**
* Grants permissions on a room one or more JIDs by setting the * affiliation of a user based * on their JID.
*
* &lt;p&gt;If the JID currenly has an existing affiliation, then the existing * affiliation will be replaced with the one passed. If the process could not be * completed, the room will dispatch the event &lt;code&gt;RoomEvent.ADMIN_ERROR&lt;/code&gt;.
* * @param affiliation Use one of the * following affiliations: &lt;code&gt;Room.MEMBER_AFFILIATION&lt;/code&gt;,
* &lt;code&gt;Room.ADMIN_AFFILIATION&lt;/code&gt;,
* &lt;code&gt;Room.OWNER_AFFILIATION&lt;/code&gt;
* @param jids An array of JIDs to grant these permissions to
* @param callback The function to call once permission granting is complete
* @see #revoke
* @see #ban
*/
public function grant(affiliation:String, jids:Array, callback:Function):void
{<br/>
var iq:IQ = new IQ(getRoomJID(), IQ.SET_TYPE);<br/>
var owner:MUCOwnerExtension = new MUCOwnerExtension();<br/>
<br/>
iq.callbackScope = this;<br/>
iq.callbackName = "finish_admin";<br/>
iq.callback = callback;<br/>
<br/>
for (var i:int=0; i &lt; jids.length; i++) {<br/>
owner.addItem(affiliation, null, null, jids[i], null, null);<br/>
} iq.addExtension(owner);
getConnection().send(iq);
} /**
* Revokes all affiliations from the JIDs. This is the same as:
* &lt;code&gt;grant( Room.NO_AFFILIATION, jids )&lt;/code&gt;
* * &lt;p&gt;If the process could not be completed, the room will dispatch the event
* &lt;code&gt;RoomEvent.ADMIN_ERROR&lt;/code&gt;. Note: if the JID is banned from this room, * then this will also revoke the banned status.&lt;/p&gt;
* * @param jids An array of JIDs to revoke affiliations from
* @param callback The function to call once revocation is complete
* @see #grant
* @see #ban
* @see #allow
*/
public function revoke(jids:Array, callback:Function):void
{<br/>
grant(Room.NO_AFFILIATION, jids, callback);<br/>
} /**
* Bans an array of JIDs from entering the room. This is the same as:
* &lt;code&gt;Room.grant( OUTCAST_AFFILIATION, jid )&lt;/code&gt;
*
* &lt;p&gt;If the process could not be completed, the room will dispatch the event
* &lt;code&gt;RoomEvent.ADMIN_ERROR&lt;/code&gt;.&lt;/p&gt;
* * @param jids An arry of JIDs to ban
* @param callback The function to call once banning is complete
* @see #grant
* @see #allow
*/
public function ban(jids:Array, callback:Function):void
{<br/>
grant(Room.OUTCAST_AFFILIATION, jids, callback);<br/>
} /**
* Allow a previously banned JIDs to enter this room. This is the same as:
* Room.grant(NO_AFFILIATION, jid)
*
* &lt;p&gt;If the process could not be completed, the room will dispatch the event
* &lt;code&gt;RoomEvent.ADMIN_ERROR&lt;/code&gt;&lt;/p&gt;
* * @param jids An array of JIDs to allow
* @param callback The function to call once allowing is complete
* @see #revoke
* @see #ban
*/
public function allow( jids:Array, callback:Function ):void
{<br/>
grant(Room.NO_AFFILIATION, jids, callback);<br/>
} /*
* The default handler for admin IQ messages
* Dispatches the adminError event if anything went wrong
*/
private function finish_admin(iq:IQ):void
{<br/>
if (iq.type == IQ.ERROR_TYPE) {<br/>
var e:RoomEvent = new RoomEvent(RoomEvent.ADMIN_ERROR);<br/>
e.errorCondition = iq.errorCondition;<br/>
e.errorMessage = iq.errorMessage;<br/>
e.errorType = iq.errorType;<br/>
e.errorCode = iq.errorCode;<br/>
dispatchEvent(e);<br/>
}
} /**
* Requests an affiliation list for a given affiliation with with room.
* This will either dispatch the event &lt;code&gt;RoomEvent.AFFILIATIONS&lt;/code&gt; or * &lt;code&gt;RoomEvent.ADMIN_ERROR&lt;/code&gt; depending on the result of the request.
*
* @param affiliation Use one of the following affiliations: &lt;code&gt;Room.NO_AFFILIATION&lt;/code&gt;,
* &lt;code&gt;Room.OUTCAST_AFFILIATION&lt;/code&gt;,
* &lt;code&gt;Room.MEMBER_AFFILIATION&lt;/code&gt;,
* &lt;code&gt;Room.ADMIN_AFFILIATION&lt;/code&gt;,
* &lt;code&gt;Room.OWNER_AFFILIATION&lt;/code&gt;
* @see #revoke
* @see #grant
* @see #affiliations
*/
public function requestAffiliations( affiliation:String ):void
{<br/>
var iq:IQ = new IQ(getRoomJID(), IQ.GET_TYPE);<br/>
var owner:MUCOwnerExtension = new MUCOwnerExtension();<br/>
<br/>
iq.callbackScope = this;<br/>
iq.callbackName = "finish_requestAffiliates";<br/>
<br/>
owner.addItem(affiliation);<br/>
<br/>
iq.addExtension(owner);<br/>
getConnection().send(iq);<br/>
} private function finish_requestAffiliates(iq:IQ):void
{<br/>
finish_admin(iq);<br/>
if (iq.type == IQ.RESULT_TYPE) {<br/>
<br/>
try<br/>
{<br/>
var owner:MUCOwnerExtension = iq.getAllExtensionsByNS(MUCOwnerExtension.NS)[0];<br/>
var items:Array = owner.getAllItems();<br/>
// trace("Affiliates: " + items);<br/>
var e:RoomEvent = new RoomEvent(RoomEvent.AFFILIATIONS);<br/>
e.data = items;<br/>
dispatchEvent(e);<br/>
}
catch (e:Error)
{<br/>
trace("Error : null trapped. Resuming.");<br/>
} }
} /**
* Destroys a reserved room. If the room has been configured to be persistent,
* then it is optional that the server will permanently remove the room.
*
* @param reason A short description of why the room is being destroyed
* @param alternateJID A JID for current members to use as an alternate room to join * after the room has been destroyed. Like a postal forwarding address.
*/
public function destroy( reason:String, alternateJID:String = null, callback:Function = null ):void
{<br/>
var iq:IQ = new IQ(getRoomJID(), IQ.SET_TYPE);<br/>
var owner:MUCOwnerExtension = new MUCOwnerExtension();<br/>
<br/>
iq.callback = callback;<br/>
owner.destroy(reason, alternateJID);<br/>
<br/>
iq.addExtension(owner);<br/>
myConnection.send(iq);<br/>
} private function updateRoomRoster( aPresence:Presence ):void
{<br/>
try<br/>
{<br/>
var userNickname:String = aPresence.from.split( "/" )[1];<br/>
var userExts:Array = aPresence.getAllExtensionsByNS(MUCUserExtension.NS);<br/>
var item:MUCItem = userExts[0].getAllItems()[0];<br/>
var e:RoomEvent;<br/>
<br/>
if ( isThisUser( aPresence.from ) ) {<br/>
myAffiliation = item.affiliation;<br/>
myRole = item.role;<br/>
<br/>
if (!isActive() && aPresence.type != Presence.UNAVAILABLE_TYPE) {<br/>
//trace("Room: becoming active: " + presence.getNode());<br/>
active = true;<br/>
e = new RoomEvent(RoomEvent.ROOM_JOIN);<br/>
dispatchEvent(e);<br/>
}
} for( var i:int=0; i &lt; length; i++ ) {<br/>
//trace("Room: updateRoomRoster: checking: " + getItemAt(i).nickname);<br/>
<br/>
if( getItemAt( i ).nickname == userNickname ) {<br/>
<br/>
// If the user left, remove the item<br/>
if( aPresence.type == Presence.UNAVAILABLE_TYPE ) {<br/>
//trace("Room: updateRoomRoster: leaving room: " + userNickname);<br/>
<br/>
// Notify listeners that a user has left the room<br/>
e = new RoomEvent(RoomEvent.USER_DEPARTURE);<br/>
e.nickname = userNickname;<br/>
dispatchEvent(e);<br/>
removeItemAt (i);<br/>
<br/>
} else if (item != null) {<br/>
<br/>
var o:Object = getItemAt(i);<br/>
o.affiliation = item.affiliation;<br/>
o.role = item.role;<br/>
o.show = aPresence.show != null ? aPresence.show : Presence.SHOW_NORMAL;<br/>
}
return;
}
} // Wasn''t found, so add it
if( aPresence.type != Presence.UNAVAILABLE_TYPE ) {<br/>
addToRoomRoster( userNickname, <br/>
aPresence.show != null ? aPresence.show : Presence.SHOW_NORMAL, <br/>
item.affiliation, item.role, item.jid );<br/>
<br/>
if( userNickname != nickname ) {<br/>
e = new RoomEvent(RoomEvent.USER_JOIN);<br/>
e.nickname = userNickname;<br/>
dispatchEvent(e);<br/>
} }
}
catch (e:Error)
{<br/>
trace("Error : null trapped. Resuming.");<br/>
}
} private function addToRoomRoster( nickname:String, show:String, affiliation:String, role:String, jid:String ):void
{<br/>
addItem({nickname:nickname, show:show, affiliation:affiliation, role:role, jid:jid});
} /**
* Determines if the &lt;code&gt;sender&lt;/code&gt; parameter is the same
* as the room''s JID.
*
* @param the room JID to test
* @return true if the passed JID matches the getRoomJID
*/
public function isThisRoom( sender:String ):Boolean
{<br/>
// Checks to see that sender is this room<br/>
return sender.split( "/" )[0].toLowerCase() == getRoomJID().toLowerCase();<br/>
} /**
* Determines if the &lt;code&gt;sender&lt;/code&gt; param is the
* same as the user''s JID.
*
* @param the room JID to test
* @return true if the passed JID matches the getUserJID()
*/
public function isThisUser( sender:String ):Boolean
{<br/>
// Case insensitive check that the sender is the same as the user<br/>
return sender.toLowerCase() == getUserJID().toLowerCase();<br/>
} /**
* The conference server to use for this room. Usually, this is a subdomain of * the primary XMPP server, like conference.myserver.com.
*/
public function get conferenceServer():String
{<br/>
return serverAddress;<br/>
} /**
* @private
*/ //public var conferenceServer:String;
public function set conferenceServer( aServer:String ):void
{<br/>
serverAddress = aServer;<br/>
setRoomJID();<br/>
} /**
* The room name that should be used when joining.
*/
public function get roomName():String
{<br/>
return roomShortJID;<br/>
} /**
* @private
*/
public function set roomName( aName:String ):void
{<br/>
roomShortJID = aName;<br/>
setRoomJID();<br/>
} /**
* The nickname to use when joining.
*/
public function get nickname():String
{<br/>
return myNickname == null ? myConnection.username : myNickname;<br/>
} /**
* @private
*/
public function set nickname( theNickname:String ):void
{ <br/>
if( isActive() ) {<br/>
pendingNickname = theNickname;<br/>
// var tempPresence:Presence = new Presence( getUserJID() );<br/>
var tempPresence:Presence = new Presence( getUserJID() + "/" + pendingNickname );<br/>
myConnection.send( tempPresence );<br/>
}
else {<br/>
myNickname = theNickname;<br/>
}
} /**
* The password.
*/
public function get password():String
{<br/>
return myPassword;<br/>
} /**
* @private
*/
public function set password(aPassword:String):void
{<br/>
myPassword = aPassword;<br/>
} /**
* The subject. (Read-only)
*/
public function get subject():String
{<br/>
return mySubject;<br/>
} /**
* @private
*/ override public function toString():String
{<br/>
return ''[object Room]'';<br/>
}
}
}

I am grateful to you for figuring out a workaround to the ArrayCollection issue in AS 3. It took me a while to get this workaround to actually work as CS3 apparently does not add a path to the fl.data package. I quickly realized that I had to configure the classpath in the ActionScript 3.0 Settings from the Preferences panel myself but had a hard time locating the package. On my windows xp computer it is at C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Component Source\ActionScript 3.0\User Interface. Hopefully this will save someone a bit of time and effort.

Peter

Hey Primo, I am hitting the same arrayCollection problem when I try to compile an actionscript project in flex3. In looking for a solution I came across this post.

Are you aware of a way to deal with this when compiling the actionscript project in flexbuilder 3?

Overall a prett frustrating experience dealing with XIFF unless you are just building a flex app. Maybe someone needs to package up a nice little swf with some public endpoints for JS and other laguages to call.

No. I am not familiar with Flex or flexbuilder. Nick Velloff has an Flex based example (http://www.igniterealtime.org/community/message/165197/edit) of the AS3 XIFF library but I don’t know what version of flex is being used and whether it makes a difference.

I am able to use the AS3 XIFF library in a standard CS3 based Flash application using the workaround mentioned in this thread.

Working with XIFF is frustrating but the payoff makes it worthwhile, I think. The people behind this API have written some great code. I wouldn’t know where to begin implmenting xmpp on my own. Unfortunately there are a few gaps and missing documentation that make it difficult to use.