##chatwork简介
Chatwork是一个结合了聊天室与任务分配的一个在线系统。

##编写插件的原因
因为Chatwork新消息到来时不会有任何提示。所以需要经常查看页面否则会使得回复速度变慢降低聊天的效率。

##技术思路
插件的功能是有新消息到来时,提醒用户。

提醒方式还没有确定。

新消息到来,聊天页面会改变。改变的内容就是新的内容。新消息可以从页面中获取或者从与服务器交互中的表单获取。

所以可以通过检测页面结构(DOM)是否改变。如果改变则执行提醒函数即可。

###第一版
先给页面挂事件。通过google查找到页面改变对应的事件名是DOMSubtreeModified,在控制带中输入$发现chatwork有使用jQuery。那么事情就简单了。直接使用jQuery添加事件即可。代码如下。

1
2
3
$(".chatTimeLine").bind("DOMSubtreeModified", function() {
alert("tree changed");
})

####测试
经过测试新消息到来时能够弹出提示框

####缺点
因为监听的是事件改动,所以对于新消息来的这个需求吻合得不够。目前有一下缺点。

  1. 页面其他变动也会有提示
  2. 一个新消息多次弹窗
  3. 提醒的方式不够友好

###第二版
第一版中使用了DOMSubtreeModified事件,这个事件范围太广,来一次消息往往会多次改变DOM子节点,所以再次Google找到了新的事件监听方案——使用MutationObserver

MutationObserver给开发者们提供了一种能在某个范围内的DOM树发生变化时作出适当反应的能力.该API设计用来替换掉在DOM3事件规范中引入的Mutation事件 ——Mozilla Develop

使用MutationObserver有几个步骤

  1. 编写回调函数
    在发生相应的事件时就会调用回调函数,代码如下

    1
    2
    3
    4
    5
    6
    observer = new (
    window.MutationObserver ||window.WebKitMutationObserver)(function(mutationRecord,observer){
    mutationRecord.forEach(function(mutation){
    console.log(mutation)
    })
    })

    其中mutationRecordobserver是调用回调函数时浏览器传入的参数。

  2. 注册事件监听
    向浏览器注册要监听的事件。

    注: childList, attributes, 或者characterData三个属性中必须至少有一个为true.否则,会抛出异常”An invalid or illegal string was specified”.
    这里选择childList作为监听事件。因为来新消息的时候,DOM必定会增加子节点,所以childList必定会改变。相应的代码如下所示

    1
    2
    3
    target = $(".chatTimeLine")[0]
    config = {childList:true}
    observer.observe(target,config);

    其中target必须是DOM对象。所以使用取数组元素的方式将jQuery对象转换为DOMObject

####测试
从控制台中看到返回两个Mutation,其中包含了每个改变的节点。

####缺点
换用了MutationObserver后,缩小了监控范围。使得事件更贴切需求。

  1. 页面其他变动也会有提示
  2. 一个新消息次弹窗
  3. 提醒的方式不够友好

###第三版
事件部分已经基本解决。接下来要解决提示部分。弹框实在是太不友好了。这里想到了HTML5的新特性之一桌面通知

桌面通知的效果是这样的。

先找到桌面通知的相关文档来学习使用桌面通知

Notification 对象使用来为用户设置和显示桌面通知。——Mozilla Develop

在使用通知前必须先申请权限,申请权限的代码如下

1
window.Notification.requestPermission()

实例代码如下

1
new window.Notification("有新消息",{body:"消息内容"})

提醒的标题可以改为说话者,消息内容既是接受到的内容。所以现在要获取消息的说话者和消息内容。

通过使用jQuery可以轻松的获取,通过观察DOM结构即可得到一下代码。

1
2
var chatName = $(".chatName").last().find("span").text();
var message = $("div.chatTimeLine div._message").last().find("pre").text();

与上面提示的代码结合,稍微修改后如下

1
2
3
4
5
6
7
show = function(){
var chatName = $(".chatName").last().find("span").text();
var message = $("div.chatTimeLine div._message").last().find("pre").text();
new window.Notification(chatName,{body:message})
}

observer = new (window.MutationObserver||window.WebKitMutationObserver)(show)

####测试
这次弹出两次提示。原因是chatwork网站消息机制。至此基本功能已经开发完毕。但还有一些问题待解决

  1. 页面其他变动也会有提示
  2. 一个新消息次弹窗
  3. 提醒的方式不够友好
  4. 提示不会自动关闭
  5. 提示信息不够完善

###第四版
接下来解决一些主要问题。

  1. 一个新消息两次弹窗
    一开始尝试了将提醒函数设定为只运行一次。之后发现只有第一次来消息时提示。后面就没有提示了。因为提醒函数运行一次就不会运行第二次了。思考良久,没有找到比较好的解决方案。在Snatic的提醒下,恍然开悟。只需要判断这两次的内容是否相同即可。代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    var avatarSpeaker = $(".avatarSpeaker").last().find("img").attr("src");//说话者
    var speakTime = $(".timeStamp").last().text();//讲话时间
    if(message === last["message"] && speakTime === last["speakTime"] && chatName === last["chatName"]){
    //什么事都不做
    }else{
    last["message"] = message
    last["speakTime"] = speakTime
    }
  2. 提示不会自动关闭
    添加计时函数,三秒后关闭对话框即可。

    1
    2
    var n = new window.Notification(chatName,{body:message})
    setTimeout(function(){n.close();},3000)
  3. 提示信息不够完善
    提示信息的左边是空白的比较难看。所以根据文档稍加修改,添加说话人的头像,提醒代码修改如下。

    1
    2
    3
    var avatarSpeaker = $(".avatarSpeaker").last().find("img").attr("src");
    var n = new window.Notification(chatName,{icon:avatarSpeaker,body:message})
    setTimeout(function(){n.close();},3000)

    至此已经解决剩余的问题。目前功能较为完善。最终代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    (function(){
    window.Notification.requestPermission()
    last = []
    last["message"] = "tuoxiedafahao"
    last["speakTime"] = "15:21"
    last["chatName"] = "tuoxiedang"

    show = function(){
    var chatNameArray = $(".chatName");
    var i = chatNameArray.length;
    while(i--){
    if(chatNameArray.eq(i).text() !== "") break;
    }
    var chatName = $(".chatName").eq(i).find("span").text();
    var message = $("div.chatTimeLine div._message").last().find("pre").text();
    var avatarSpeaker = $(".avatarSpeaker").last().find("img").attr("src");
    var speakTime = $(".timeStamp").last().text();
    var myName = $(".myStatusName").text();
    if(message === last["message"] && speakTime === last["speakTime"] && chatName === last["chatName"]){

    }else if (myName === chatName){
    console.log("you say it")
    } else{
    var n = new window.Notification(chatName,{icon:avatarSpeaker,body:message})
    setTimeout(function(){n.close();},3000)
    last["message"] = message
    last["speakTime"] = speakTime
    last["chatName"] = chatName
    }

    }

    observer = new (window.MutationObserver||window.WebKitMutationObserver)(show)

    target = $(".chatTimeLine")[0]
    config = {childList:true}
    observer.observe(target,config);
    })()

###测试
效果跟一开始想法差不多。

##后记
这篇文章在我插件完成两个星期后才写完。其中还有一些功能没有介绍就突然在最终代码里面出现了。改天有空再来填坑吧。(改天根本就不会想起来好吧!摔!)