上一次为TipicIM增加直接拖放文件并发送的功能(参见http://www.cnblogs.com/movingboy/archive/2006/06/19/429689.html),总觉得不完善。一是一次只能发送一个文件,二是拖放区域是定死的,不能根据自己的喜好随意摆放。于是继续想办法改进一下。
先考虑第一个问题。经过上次的修改,拖放时发送文件的窗口是通过如下代码创建的:

function sendfile ( filename )
{
  external.globals( 'cfg' )( 'location' ) = filename;

  document.getElementById( 'txt' ).focus();
  external.wnd.params[0].dial_file( external.wnd.params[1], getresource() );
}

上面红色字体的代码实际上加载file_send.html并执行其中的init方法,在init方法中完成实际的显示发送文件窗口和响应相应的事件。由于file_send.html中的代码只考虑了发送一个文件的情况,并且这些代码比较复杂,所以不打算修改该文件中的代码了,而是考虑在chat.html中的IAnimation的OLEDragDrop事件中枚举拖放的所有文件,针对每个文件调用sendfile。于是尝试如下代码:

<SCRIPT FOR=IAnimation EVENT="OLEDragDrop(Data)">
if(Data.GetFormat(15)){
var e = new Enumerator(Data.Files);
while(!e.atEnd()){
sendfile( e.item() );
e.moveNext();
}

BackColor=0x80000003;}
</SCRIPT>

但执行以上代码,只有第一个文件能直接弹出发送窗口而跳过弹出文件选择对话框,其它的文件都会弹出文件选择对话框,传递给sendfile的参数均未被正确处理。实际上在上一次已经考虑过提供同时拖放并发送文件的功能,但一时没有找到解决这个问题的办法,才限制为每次只能发送一个文件。
经过跟踪分析,发现sendfile中创建窗口的语句是异步执行的,它并没有等到file_send.html中的init方法执行完毕,而是发出了创建窗口的命令后就立即返回了。这导致第二次调用sendfile时保存在变量external.globals( 'cfg' )( 'location' ) 中的值被第一次创建的窗口的init方法清空了,这样第二次创建窗口时总是得弹出文件选择对话框。
怎样才能让chat.html中的sendfile方法和file_send.html中的init方法保持同步呢?必须让sendfile在执行完毕后“沉睡”一下,等待后者执行完毕后再继续执行。JavaScript有setTimeout方法,但该方法的作用是将指定的动作设定为等待指定的时间间隔后执行,它也是一个异步的方法,无法直接使用。如果使用一个while循环并在循环中等待external.globals( 'cfg' )( 'location' ) 被清空,则会导致程序失去响应。怎么办呢?
经过一番搜索,在csdn论坛的JavaScript版块中找到了如下代码:

function sleep(numberMillis)
{
  var dialogScript = "window.setTimeout(" + " function () { window.close(); }, " + numberMillis + ");";
  var result = window.showModalDialog("javascript:document.writeln(" +" '<script>" + dialogScript + "<" + "/script>')");
}

sleep方法创建了一个模式窗体,在它被关闭之前程序无法继续运行。该窗体又被动态地加入了一段代码,使得该窗体指定的时间间隔后自动关闭。这正是我要的。把它加入到chat.html中,同时修改sendfile方法如下:

function sendfile ( filename )
{
  external.globals( 'cfg' )( 'location' ) = filename;

  document.getElementById( 'txt' ).focus();
  external.wnd.params[0].dial_file( external.wnd.params[1], getresource() );

  sleep(500);
}

经过测试,在拖放并发送多个文件的时候每个文件都能跳过文件选择框而直接弹出发送文件窗口了。
再看第二个问题。考虑在chat.html里放一个div,它是可以拖动的,再把IAnimation放到div里。又在网上搜索了一通,找到如下代码:

<SCRIPT>
var dragElement;
var elY;
var elX;
var mouseDownY;
var mouseDownX;

function getPageY (element) {
  var y = 0;
  do
    y += element.offsetTop;
  while ((element = element.offsetParent));
  return y;
}
function getPageX (element) {
  var x = 0;
  do
    x += element.offsetLeft;
  while ((element = element.offsetParent));
  return x;
}
function startDrag (element, evt) {
  dragElement = element;
  if (document.layers) {
    elY = dragElement.top;
    elX = dragelement.left;
    mouseDownY = evt.pageY;
    mouseDownX = evt.pageX;
    document.captureEvents(Event.MOUSEMOVE);
  }
  else if (document.all || document.getElementById) {
    elY = getPageY (dragElement);
    elX = getPageX (dragElement);
    mouseDownY = evt.clientY;
    mouseDownX = evt.clientX;
  }
  document.onmousemove = drag;
}
function drag (evt) {
  if (document.layers)
  {
    dragElement.top = elY + evt.pageY - mouseDownY;
    dragElement.left = elX + evt.pageX - mouseDownX;
    external.globals( 'cfg' ) ( 'user_dialog_dragareatop' ) = dragElement.top;
    external.globals( 'cfg' ) ( 'user_dialog_dragarealeft' ) = dragElement.left;
  }
  else if (document.all)
  {
    dragElement.style.pixelTop = elY + event.clientY - mouseDownY;
    dragElement.style.pixelLeft = elX + event.clientX - mouseDownX;
    external.globals( 'cfg' ) ( 'user_dialog_dragareatop' ) = dragElement.style.pixelTop;
    external.globals( 'cfg' ) ( 'user_dialog_dragarealeft' ) = dragElement.style.pixelLeft;
  }
  else if (document.getElementById)
  {
    dragElement.style.top = (elY + evt.clientY - mouseDownY) + 'px';
    dragElement.style.left = (elX + evt.clientX - mouseDownX) + 'px';
    external.globals( 'cfg' ) ( 'user_dialog_dragareatop' ) = dragElement.style.top;
    external.globals( 'cfg' ) ( 'user_dialog_dragarealeft' ) = dragElement.style.left;
  }
}
function stopDrag () {
  document.onmousemove = null;
  dragElement = null;
  if (document.layers)
    document.releaseEvents(Event.MOUSEMOVE);
}
</SCRIPT>
<div id="infoShow_t" style="z-index:2; border-style:solid;border-width:4pt; border-color:blue; position: absolute; left: 100px; top: 100px;" ONMOUSEDOWN="startDrag(this, event);" ONMOUSEUP="stopDrag();">
...</div>

以上代码是用div的OnMouseDown和OnMouseUp事件来模拟拖放效果。把它放到chat.html中,并把IAnimation放到<div></div>中,移动<div>时IAnimation也同时移动了。
仅仅实现了<div>可移动还是不够的,必须把每次移动后<div>的位置记录下来,以便下次聊天时能把<div>放到最后一次的位置上。以上代码中的红色字体部分就是将<div>的位置填写到变量user_dialog_dragareatop和user_dialog_dragarealeft中,这两个变量会自动保存到用户配置文件中。在chat.html的init方法中读取<div>的位置代码如下:

 var dragarea = document.getElementById( 'infoShow_t' ).style;

 dragarea.top = cfg( 'user_dialog_dragareatop' );
 dragarea.left = cfg( 'user_dialog_dragarealeft' );

由于以上两个变量在修改前是没有的,为了遵循TipicIM的惯例,在settngs\default.xml中加入如下两行:

<user_dialog_dragareatop>100</user_dialog_dragareatop>
<user_dialog_dragarealeft>100</user_dialog_dragarealeft>

至此本文提及的两个问题均已有效地解决,而直接拖放并发送文件的功能也比较完善了。这两次改动的实践让我进一步了解了JavaScript的魅力。附本次改动后的源码。 

posted on 2006-06-26 23:13  海蓝  阅读(643)  评论(0编辑  收藏  举报