在北半球这里,夏天已经过去了。孩子们回到了学校。秋天临近的迹象使小组的成员感染到了这种新入学的情绪。我们什么时候会长大呢?!我们采访了 Jay,还有他的女儿,因此他们要伴随 Web Team Talking 走进秋天。您可以期待更多的精彩内容。
除了扔湿纸团、理新发型之外,本月我们研究了 MSN® Messenger、一些奇特的 JavaScript 功能、如何更新服务器上的 XML 数据,以及多得让您看不完的短篇集。
如果您愿意学习,请将有关 Web 的问题发送给我们,我们会立即对您的问题作答。Instant Gratification
亲爱的 Web Team :
我可以将 MSN Instant Messenger 嵌入在诸如 msn.com 这样的网站上吗?
谢谢,
Joshua Carroll
Web Team 的答复:
我们越来越需要一种比 Instant Gratification 更好的功能,在这一方面,MSN Messenger 最有说服力。http://www.msn.com 中的 IM 功能是使用提供 Messenger 功能的两个 Microsoft ActiveX ® 控件以及提供 DHTML 用户界面的一些脚本来实现的。为简短起见,假定我要在 Intranet 主页中添加一个小的提要栏,以便在用户登录后列出该用户的联系人(及其连接状态),并在用户未登录时提供登录按钮。下面提供了一个示例,但是为了表明我们不是得到了神(请注意小写字母“o”)的暗示才领悟到这一信息,我们先概述一下所涉及的技术。
由于没有关于这些控件的文档,因此我们需要做一些研究工作来实现我们的目标。首先,我们可以查看 msn.com 使用的代码以获得一些提示。这不是因为缺乏勇气,而是通过艰苦地研究这些代码,我们可以从大体上了解这两个控件的功能,以及我们可以怎样编写自己的代码。我们可以使用的第二个工具(并且是有用得多的工具)是 OLE View。OLE View 是 Microsoft Visual Studio_ 附带的一个实用工具,它的功能很强大,可以分析并显示 COM 对象的类型信息。
因此,我们启动 OLE View,找到前面提到的两个控件(其友好名称分别为 MSN Messenger Application 和 MSN Messenger Object),右键单击这两个控件,然后选择 View type information 。该视图将显示对象使用的各个接口和枚举。标记为调度接口的任何接口都应该可用于基于脚本的调用方,因此我们开始着手编码了!
在确保控件正确实例化以后,代码需要做的第一件事情是确定用户是否联机。这是通过检查 Messenger Object 的 LocalState 属性来完成的。该属性从 MSTATE 枚举(其定义已部分包含在我们的代码中)返回一个值。如果用户未联机,我们将启用调用 Messenger Application 的 LaunchLogonUI 方法的登录按钮。如果用户已联机,则我们需要使用用户的所有联系人来填充 DIV。为此,我们循环访问默认列表 (List(0)) 中的每个联系人,并检索其 FriendlyName 以及描述其当前状态的 MSTATE 值,然后新建一个包含此信息的 DIV。
由于我们要允许用户通过单击列表中的任何联系人来向该联系人发送消息,因此需要以编程方式将 onclick 事件处理程序附加到新 DIV。该事件处理程序函数 ( sendMessage ) 基本上是 Messenger Application 的 LaunchIMUI 方法的包装。由于 LaunchIMUI 将目标联系人作为一个参数,因此我们需要能够确定 Messenger Object 的默认列表中的哪个联系人与被单击的 DIV 对应。这时最理想的是使用 expando 属性。我们将联系人在默认列表中的索引附加到新建的、表示该联系人的 DIV 元素。这样,onclick 处理程序就能够检索到此索引,然后从 Messenger Object 的列表中抽取正确的联系人并将其传递给 LaunchIMUI 。
本示例包含的一项额外功能是在该页加载后处理注销和登录。这是通过处理由 Messenger Object 激发的 OnLocalStateChangeResult 事件来完成的。此事件在 LocalState 属性更改时激发(并不太意外)。因此,相应地,我们仅仅查看我们已脱机还是已联机。如果已脱机,需要清空联系人 UI 列表;如果已联机,需要在短暂的 setTimeout(确保一切都正确同步)之后调用 populateContacts 。而且由于 ActiveX 控件依赖于独立 Messenger 的运行实例,因此我们的页面还将更新以响应它触发的事件。
至此,您可能已对我们的喋喋不休感到厌烦,那么,请看下面的代码。
<HEAD>
<TITLE>Embedding MSN Messenger Test</TITLE>
<OBJECT CLASSID="clsid:F3A614DC-ABE0-11d2-A441-00C04F795683"
CODEBASE="#Version=2,0,0,83"
CODETYPE="application/x-oleobject" ID="oMsgrObj" WIDTH="0"
HEIGHT="0" OnUserStateChanged="alert();">
</OBJECT>
<OBJECT CLASSID="clsid:FB7199AB-79BF-11d2-8D94-0000F875C541"
CODETYPE="application/x-oleobject"
ID="oMsgrApp" WIDTH="0" HEIGHT="0">
</OBJECT>
<STYLE>
BODY
{
FONT-FAMILY: Verdana, Arial, Helvetica;
FONT-SIZE: 8pt;
}
INPUT
{
FONT-FAMILY: Verdana, Arial, Helvetica;
FONT-SIZE: 8pt;
}
.clsHeading
{
FONT-WEIGHT: bolder;
FONT-SIZE: 10pt;
}
.clsContact
{
PADDING: 2px;
CURSOR: hand;
}
</STYLE>
</HEAD>
<BODY onLoad="body_onLoad();">
<SCRIPT LANGUAGE="JScript">
// Here are the definitions for the Messenger enumerated values we use
var MSTATE_OFFLINE = 1;
var MSTATE_ONLINE = 2;
var MSTATE_BUSY = 10;
var MSTATE_BE_RIGHT_BACK = 14;
var MSTATE_IDLE = 18;
var MSTATE_AWAY = 34;
function body_onLoad()
{
// First, we need to make sure that the Messenger controls got instantiated correctly
if ("undefined" != typeof(oMsgrObj) && null != oMsgrObj.object && "undefined"
!= typeof(oMsgrApp) && null != oMsgrApp.object)
{
// If so, let's check to see if we're online and (if so) populate our contacts UI
if (oMsgrObj.LocalState == MSTATE_ONLINE)
{
populateContacts();
}
else if (oMsgrObj.LocalState == MSTATE_OFFLINE)
{
btnLogon.disabled = false;
}
}
else
{
// Uh, oh - the controls didn't get instantiated correctly;
// the user probably needs to install Messenger
alert("You need to install the latest version of MSN Messenger!
\nGo to http://messenger.msn.com right now!");
}
}
function populateContacts()
{
var oList = oMsgrObj.List(0);
var oContact;
var i;
// To populate our contact list, we're going to iterate throught the
// default list collection, check the state of each contact,
// and add a DIV with the approppriate appearance to our UI
for (i = 0; i < oList.Count; i++)
{
oContact = oList.Item(i);
oNewElement = document.createElement("DIV");
oNewElement.innerText = oContact.FriendlyName;
switch (oContact.State)
{
case MSTATE_ONLINE:
// Don't need to do anything
break;
case MSTATE_OFFLINE:
oNewElement.innerText += " (Offline)";
oNewElement.style.color = "graytext";
break;
case MSTATE_BUSY:
oNewElement.innerText += " (Busy)";
break;
case MSTATE_BE_RIGHT_BACK:
oNewElement.innerText += " (Be Right Back)";
break;
case MSTATE_IDLE:
oNewElement.innerText += " (Idle)";
break;
case MSTATE_AWAY:
oNewElement.innerText += " (Away)";
break;
default:
oNewElement.innerText += "(Just plain not around!)";
oNewElement.style.color = "graytext";
break;
}
oNewElement.className = "clsContact";
// To enable us to respond to the onclick event,
// we're programmatically setting the event
// handler AND we're defining an expando property
// on the contact DIV whose value is set to
// the index of the contact in the default list (so we can find them later)
oNewElement.onclick = sendMessage;
oNewElement.setAttribute("CONTACTID", i.toString());
divContacts.appendChild(oNewElement);
}
}
function doLogon()
{
// To logon, we just ask Messenger to display its logon UI
if (oMsgrObj.LocalState == MSTATE_OFFLINE)
{
btnLogon.disabled = true;
oMsgrApp.LaunchLogonUI();
}
}
function sendMessage()
{
// To send a message, we likewise just ask Messenger to do the heavy
// lifting (we just have to
// pass it a contact from the default list)
var nContactID = parseInt(window.event.srcElement.getAttribute("CONTACTID"));
if (!isNaN(nContactID))
{
var oContact = oMsgrObj.List(0).Item(nContactID);
oMsgrApp.LaunchIMUI(oContact);
}
}
</SCRIPT>
<SCRIPT LANGUAGE="JScript" EVENT="onLocalStateChangeResult(hr)" FOR="oMsgrObj">
if (hr == 0)
{
if (oMsgrObj.LocalState == MSTATE_ONLINE)
{
// Now we're online
window.setTimeout("populateContacts();", 3000);
}
else if (oMsgrObj.LocalState == MSTATE_OFFLINE)
{
// Now we're offline
divContacts.innerHTML = "";
btnLogon.disabled = false;
} }
</SCRIPT>
<DIV CLASS="clsHeading">Contacts (click on a contact to send a message!)</DIV>
<DIV ID="divContacts" STYLE="MARGIN-TOP: 8px;
MARGIN-BOTTOM: 8px; BORDER: 1px solid steelblue"></DIV>
<INPUT TYPE="BUTTON" ID="btnLogon" VALUE="Logon" onClick="doLogon();" DISABLED="yes">
</BODY>
</HTML>
