├── API Documents.htm ├── Bug & Todo.txt ├── Change Log.txt ├── Feature.txt ├── LICENSE ├── README.md ├── Sample.htm ├── css ├── animate-custom.css ├── jQuery.chatbox.css └── style.css └── js ├── jQuery.chatbox.js └── jquery-1.10.2.js /API Documents.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery.chatbox API Documents 5 | 6 | 29 | 30 | 31 |
32 | 33 |

jQuery.chatbox 说明文档

34 |
35 |

特性

36 |
 37 | 1.轻量级动画特效以及友好的界面
 38 | 2.支持多窗口
 39 | 3.完善的回调函数以实现自定义功能
 40 | 4.多种调用方式
 41 | 4.良好的封装以及扩展性
 42 | 5.每个聊天窗对象实例以data属性的形式附加在聊天窗DOM对象上(如果你想获得某个特定插件的实例,可以直接从页面元素中获取:$('{boxId}').data('chatbox'))
 43 | 
44 | 45 |

配置项

46 |

配置项分为全局配置项和实例配置项

47 |
 48 | 

全局配置项:

49 | 参数 类型 默认值 说明 50 | --------------------------------------------------------------------------------------------------------------- 51 | id number null 当前用户的id,也就是发送者的id,必须是唯一值 52 | user string null 发送者的显示名称,可以是昵称用户名等,不要求唯一 53 | debug boolean false 是否打开调试功能 54 | idPrefix string 'chatbox_' 生成页面DOM元素的id值 55 | 56 |

实例配置项:

57 | 参数 类型 默认值 说明 58 | --------------------------------------------------------------------------------------------------------------- 59 | id number null 接收者的id,同时也会作为实例id,必须是唯一值 60 | user string null 接收者的显示名称,可以是昵称用户名等,不要求唯一 61 | title string 'Chat with '+{user} 聊天窗的标题 62 |
63 | 64 |

回调函数

65 |
 66 | 回调函数也分为两种,一种是全局回调函数另一种是实例回调函数。
 67 | 但是有点Javascript基础的开发者应该了解不管是哪种类型的回调函数我们都应该以传递函数引用的方式来调用,而不是对每个实例创建一个函数副本。
 68 | 这样会造成内存浪费。全局回调函数不会在实例化每个聊天窗对象时重复创建副本分配给每个对象实例,而实例回调函数会。
 69 | 因此在不需要为每个独立的聊天窗分配不同的回调功能时使用实例回调函数也应该以传递函数引用的方式来分配回调函数以节省内存空间。
 70 | 
71 |
 72 | 

回调函数参考:

73 | 函数名 参数 说明 74 | --------------------------------------------------------------------------------------------------------------- 75 | onChatboxCreate 创建聊天窗时触发 76 | onChatboxEnable 聊天窗被启用时触发 77 | onChatboxDisable 聊天窗被禁用时触发 78 | onMessageSend msg 发送消息时触发,唯一参数:消息内容msg 79 | onMessageReceive msg 收到消息时触发,唯一参数:消息内容msg 80 | onMessageSystem msg 收到系统消息时触发,唯一参数:消息内容msg 81 | onChatboxDestroy 销毁聊天窗时触发 82 | 83 | 优先级:实例回调函数优先级高于全局回调函数,也就是说实例配置项中的回调函数会覆盖全局配置项中同名的回调函数。 84 | this指针:this指针经由apply或者call方法已经指向调用该方法的实例。 85 |
86 | 87 |

API(属性和方法)

88 |
 89 | 

全局API:

90 | 属性/方法名 类型 参数 说明 91 | --------------------------------------------------------------------------------------------------------------- 92 | globalOptions 属性 无 保存所有聊天窗的全局配置项 93 | getQueue() 方法 无 返回当前聊天窗的实例队列 94 | 95 |

实例API:

96 | 属性/方法名 类型 参数 说明 97 | --------------------------------------------------------------------------------------------------------------- 98 | $elem 属性 无 保存着当前聊天窗实例的jQuery对象 99 | opts 属性 无 保存着当前聊天窗实例的初始化选项 100 | show() 方法 无 显示聊天窗 101 | hide() 方法 无 隐藏聊天窗 102 | enable() 方法 无 启用聊天窗 103 | disable() 方法 无 禁用聊天窗 104 | message() 方法 msg,type 设置接收到的消息到聊天窗,两个参数:消息内容msg、消息类型type 105 | blink() 方法 无 高亮标题栏闪烁提示 106 | destroy() 方法 无 无 107 |
108 | 109 |

调用方式

110 |
111 | 设定全局配置项:
112 | 直接定义配置对象globalOptions,未给定的配置项依旧会使用默认值并不会被该配置对象覆盖
113 | $.chatbox.globalOptions = {
114 |     id:10000,
115 |     user:'Jason',
116 |     debug:true,
117 |     onChatboxCreate:function(){
118 |         //要执行的代码
119 |     }
120 | }
121 | 
122 | 或者以附加属性的方式
123 | $.chatbox.globalOptions.id = 10000;
124 | $.chatbox.globalOptions.user = 'Jason';
125 | $.chatbox.globalOptions.onChatboxCreate = function(){
126 |     //要执行的代码
127 | };
128 | 
129 | 初始化聊天窗:
130 | $.chatbox({
131 |     id:11254,
132 |     user:'Tony',
133 |     title:'Chat with Tony',
134 |     onChatboxCreate:function(){
135 |         //要执行的代码
136 |     }
137 | });
138 | 
139 | 调用API方法:
140 | 第一种调用方式
141 | $.chatbox({instanceId}).message({message content});
142 | 
143 | 第二种调用方式
144 | $({boxId}).data('chatbox').message({message content});
145 | 
146 |
147 | 148 | -------------------------------------------------------------------------------- /Bug & Todo.txt: -------------------------------------------------------------------------------- 1 | To Do Suggestion: 2 | 3 | * -------------------------------------------------------------------------------- /Change Log.txt: -------------------------------------------------------------------------------- 1 | ChatBox 0.2.0-alpha 2 | 3 | 2014.06.06-------------------------------------------- 4 | - 修正全局默认配置项被覆盖的BUG 5 | 6 | 2014.01.08-------------------------------------------- 7 | - 用jQuery控制方式替换内嵌在HTML元素中的javascript:void(0) 8 | - 移除原型方法中的_setOptions方法,将此方法完全私有化 9 | 10 | 2013.12.30-------------------------------------------- 11 | - 让整个标题栏支持最小化响应 12 | - 增加被选中的聊天窗的焦点阴影特效 13 | - 加入animate.css框架 14 | - 全局选项增加配置项:animate,用于设定一个默认的消息接收动画 15 | - 设置文本输入区域宽高默认值以及可延伸范围 16 | 17 | 2013.12.10-------------------------------------------- 18 | - 增加API方法:blink,用于高亮聊天窗标题闪烁特效 19 | - 增加API方法:messageTo方法,用于消息发送逻辑处理 20 | - 分离init方法中的消息发送逻辑到messageTo方法中 21 | - 取消空消息回调函数onMessageEmpty 22 | - 为message方法增加系统消息输出支持 23 | - 增加onMessageSystem回调函数,在输出系统消息时触发 24 | - 修复关闭按钮重复绑定的动画事件 25 | 26 | 27 | 28 | ChatBox 0.0.1-alpha 29 | 30 | 2013.12.03-------------------------------------------- 31 | - 增加一组API方法(enable & disable),用于聊天窗的启用和禁用 32 | - 增加一组API方法(show & hide),用于聊天窗的显示和隐藏 33 | 34 | 2013.12.02-------------------------------------------- 35 | - 增加消息发送为空时的处理 36 | - 使用不同颜色区分聊天双方 37 | - 增加新消息标题栏闪烁 38 | 39 | 2013.12.01-------------------------------------------- 40 | - 增加输入消息特殊字符过滤处理 41 | - 增加debug私有方法 42 | - 增加自定义回调函数扩展模块 43 | 44 | 45 | 46 | ChatBox 0.0.1-base 2013-10-11 47 | 48 | - 创建Base版 -------------------------------------------------------------------------------- /Feature.txt: -------------------------------------------------------------------------------- 1 | 2 | * 轻量级动画特效以及友好的界面 3 | * 支持多窗口 4 | * 完善的回调函数以实现自定义功能 5 | * 多种调用方式 6 | * 良好的封装以及扩展性 7 | * 每个聊天窗对象实例以data属性的形式附加在聊天窗DOM对象上 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 haozki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 轻量级jQuery聊天窗插件 2 | -------------------------------------------------------------------------------- /Sample.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chatbox Sample 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |

开启聊天窗实例

23 |
24 |

快速添加

25 | Chat With Jason(id:10000) 26 | Chat With Kitty(id:10005) 27 | Chat With Tony(id:10009) 28 |

自定义添加

29 | 30 | 31 | 32 |
33 |

全局API

34 |
35 |
36 | 37 |
38 |
39 |

实例API

40 |
41 | 42 | 43 | 48 |

基本API

49 | 50 | 51 | 52 | 53 | 54 | 55 |

工具API

56 | 57 | 58 | 59 |

提示:打开浏览器控制台可以查看调试信息

60 |
61 |
62 | 63 | 64 | 65 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /css/animate-custom.css: -------------------------------------------------------------------------------- 1 | .animated { 2 | -webkit-animation-fill-mode:both; 3 | -moz-animation-fill-mode:both; 4 | -ms-animation-fill-mode:both; 5 | -o-animation-fill-mode:both; 6 | animation-fill-mode:both; 7 | -webkit-animation-duration:1s; 8 | -moz-animation-duration:1s; 9 | -ms-animation-duration:1s; 10 | -o-animation-duration:1s; 11 | animation-duration:1s; 12 | } 13 | 14 | .animated.hinge{ 15 | -webkit-animation-duration:1s; 16 | -moz-animation-duration:1s; 17 | -ms-animation-duration:1s; 18 | -o-animation-duration:1s; 19 | animation-duration:1s; 20 | } 21 | 22 | @-webkit-keyframes flash { 23 | 0%, 50%, 100% {opacity: 1;} 25%, 75% {opacity: 0;} 24 | } 25 | 26 | @-moz-keyframes flash { 27 | 0%, 50%, 100% {opacity: 1;} 28 | 25%, 75% {opacity: 0;} 29 | } 30 | 31 | @-o-keyframes flash { 32 | 0%, 50%, 100% {opacity: 1;} 33 | 25%, 75% {opacity: 0;} 34 | } 35 | 36 | @keyframes flash { 37 | 0%, 50%, 100% {opacity: 1;} 38 | 25%, 75% {opacity: 0;} 39 | } 40 | 41 | .flash { 42 | -webkit-animation-name: flash; 43 | -moz-animation-name: flash; 44 | -o-animation-name: flash; 45 | animation-name: flash; 46 | } 47 | @-webkit-keyframes shake { 48 | 0%, 100% {-webkit-transform: translateX(0);} 49 | 10%, 30%, 50%, 70%, 90% {-webkit-transform: translateX(-10px);} 50 | 20%, 40%, 60%, 80% {-webkit-transform: translateX(10px);} 51 | } 52 | 53 | @-moz-keyframes shake { 54 | 0%, 100% {-moz-transform: translateX(0);} 55 | 10%, 30%, 50%, 70%, 90% {-moz-transform: translateX(-10px);} 56 | 20%, 40%, 60%, 80% {-moz-transform: translateX(10px);} 57 | } 58 | 59 | @-o-keyframes shake { 60 | 0%, 100% {-o-transform: translateX(0);} 61 | 10%, 30%, 50%, 70%, 90% {-o-transform: translateX(-10px);} 62 | 20%, 40%, 60%, 80% {-o-transform: translateX(10px);} 63 | } 64 | 65 | @keyframes shake { 66 | 0%, 100% {transform: translateX(0);} 67 | 10%, 30%, 50%, 70%, 90% {transform: translateX(-10px);} 68 | 20%, 40%, 60%, 80% {transform: translateX(10px);} 69 | } 70 | 71 | .shake { 72 | -webkit-animation-name: shake; 73 | -moz-animation-name: shake; 74 | -o-animation-name: shake; 75 | animation-name: shake; 76 | } 77 | @-webkit-keyframes bounce { 78 | 0%, 20%, 50%, 80%, 100% {-webkit-transform: translateY(0);} 79 | 40% {-webkit-transform: translateY(-30px);} 80 | 60% {-webkit-transform: translateY(-15px);} 81 | } 82 | 83 | @-moz-keyframes bounce { 84 | 0%, 20%, 50%, 80%, 100% {-moz-transform: translateY(0);} 85 | 40% {-moz-transform: translateY(-30px);} 86 | 60% {-moz-transform: translateY(-15px);} 87 | } 88 | 89 | @-o-keyframes bounce { 90 | 0%, 20%, 50%, 80%, 100% {-o-transform: translateY(0);} 91 | 40% {-o-transform: translateY(-30px);} 92 | 60% {-o-transform: translateY(-15px);} 93 | } 94 | @keyframes bounce { 95 | 0%, 20%, 50%, 80%, 100% {transform: translateY(0);} 96 | 40% {transform: translateY(-30px);} 97 | 60% {transform: translateY(-15px);} 98 | } 99 | 100 | .bounce { 101 | -webkit-animation-name: bounce; 102 | -moz-animation-name: bounce; 103 | -o-animation-name: bounce; 104 | animation-name: bounce; 105 | } 106 | @-webkit-keyframes tada { 107 | 0% {-webkit-transform: scale(1);} 108 | 10%, 20% {-webkit-transform: scale(0.9) rotate(-3deg);} 109 | 30%, 50%, 70%, 90% {-webkit-transform: scale(1.1) rotate(3deg);} 110 | 40%, 60%, 80% {-webkit-transform: scale(1.1) rotate(-3deg);} 111 | 100% {-webkit-transform: scale(1) rotate(0);} 112 | } 113 | 114 | @-moz-keyframes tada { 115 | 0% {-moz-transform: scale(1);} 116 | 10%, 20% {-moz-transform: scale(0.9) rotate(-3deg);} 117 | 30%, 50%, 70%, 90% {-moz-transform: scale(1.1) rotate(3deg);} 118 | 40%, 60%, 80% {-moz-transform: scale(1.1) rotate(-3deg);} 119 | 100% {-moz-transform: scale(1) rotate(0);} 120 | } 121 | 122 | @-o-keyframes tada { 123 | 0% {-o-transform: scale(1);} 124 | 10%, 20% {-o-transform: scale(0.9) rotate(-3deg);} 125 | 30%, 50%, 70%, 90% {-o-transform: scale(1.1) rotate(3deg);} 126 | 40%, 60%, 80% {-o-transform: scale(1.1) rotate(-3deg);} 127 | 100% {-o-transform: scale(1) rotate(0);} 128 | } 129 | 130 | @keyframes tada { 131 | 0% {transform: scale(1);} 132 | 10%, 20% {transform: scale(0.9) rotate(-3deg);} 133 | 30%, 50%, 70%, 90% {transform: scale(1.1) rotate(3deg);} 134 | 40%, 60%, 80% {transform: scale(1.1) rotate(-3deg);} 135 | 100% {transform: scale(1) rotate(0);} 136 | } 137 | 138 | .tada { 139 | -webkit-animation-name: tada; 140 | -moz-animation-name: tada; 141 | -o-animation-name: tada; 142 | animation-name: tada; 143 | } 144 | @-webkit-keyframes swing { 145 | 20%, 40%, 60%, 80%, 100% { -webkit-transform-origin: top center; } 146 | 20% { -webkit-transform: rotate(15deg); } 147 | 40% { -webkit-transform: rotate(-10deg); } 148 | 60% { -webkit-transform: rotate(5deg); } 149 | 80% { -webkit-transform: rotate(-5deg); } 150 | 100% { -webkit-transform: rotate(0deg); } 151 | } 152 | 153 | @-moz-keyframes swing { 154 | 20% { -moz-transform: rotate(15deg); } 155 | 40% { -moz-transform: rotate(-10deg); } 156 | 60% { -moz-transform: rotate(5deg); } 157 | 80% { -moz-transform: rotate(-5deg); } 158 | 100% { -moz-transform: rotate(0deg); } 159 | } 160 | 161 | @-o-keyframes swing { 162 | 20% { -o-transform: rotate(15deg); } 163 | 40% { -o-transform: rotate(-10deg); } 164 | 60% { -o-transform: rotate(5deg); } 165 | 80% { -o-transform: rotate(-5deg); } 166 | 100% { -o-transform: rotate(0deg); } 167 | } 168 | 169 | @keyframes swing { 170 | 20% { transform: rotate(15deg); } 171 | 40% { transform: rotate(-10deg); } 172 | 60% { transform: rotate(5deg); } 173 | 80% { transform: rotate(-5deg); } 174 | 100% { transform: rotate(0deg); } 175 | } 176 | 177 | .swing { 178 | -webkit-transform-origin: top center; 179 | -moz-transform-origin: top center; 180 | -o-transform-origin: top center; 181 | transform-origin: top center; 182 | -webkit-animation-name: swing; 183 | -moz-animation-name: swing; 184 | -o-animation-name: swing; 185 | animation-name: swing; 186 | } 187 | /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ 188 | 189 | @-webkit-keyframes wobble { 190 | 0% { -webkit-transform: translateX(0%); } 191 | 15% { -webkit-transform: translateX(-25%) rotate(-5deg); } 192 | 30% { -webkit-transform: translateX(20%) rotate(3deg); } 193 | 45% { -webkit-transform: translateX(-15%) rotate(-3deg); } 194 | 60% { -webkit-transform: translateX(10%) rotate(2deg); } 195 | 75% { -webkit-transform: translateX(-5%) rotate(-1deg); } 196 | 100% { -webkit-transform: translateX(0%); } 197 | } 198 | 199 | @-moz-keyframes wobble { 200 | 0% { -moz-transform: translateX(0%); } 201 | 15% { -moz-transform: translateX(-25%) rotate(-5deg); } 202 | 30% { -moz-transform: translateX(20%) rotate(3deg); } 203 | 45% { -moz-transform: translateX(-15%) rotate(-3deg); } 204 | 60% { -moz-transform: translateX(10%) rotate(2deg); } 205 | 75% { -moz-transform: translateX(-5%) rotate(-1deg); } 206 | 100% { -moz-transform: translateX(0%); } 207 | } 208 | 209 | @-o-keyframes wobble { 210 | 0% { -o-transform: translateX(0%); } 211 | 15% { -o-transform: translateX(-25%) rotate(-5deg); } 212 | 30% { -o-transform: translateX(20%) rotate(3deg); } 213 | 45% { -o-transform: translateX(-15%) rotate(-3deg); } 214 | 60% { -o-transform: translateX(10%) rotate(2deg); } 215 | 75% { -o-transform: translateX(-5%) rotate(-1deg); } 216 | 100% { -o-transform: translateX(0%); } 217 | } 218 | 219 | @keyframes wobble { 220 | 0% { transform: translateX(0%); } 221 | 15% { transform: translateX(-25%) rotate(-5deg); } 222 | 30% { transform: translateX(20%) rotate(3deg); } 223 | 45% { transform: translateX(-15%) rotate(-3deg); } 224 | 60% { transform: translateX(10%) rotate(2deg); } 225 | 75% { transform: translateX(-5%) rotate(-1deg); } 226 | 100% { transform: translateX(0%); } 227 | } 228 | 229 | .wobble { 230 | -webkit-animation-name: wobble; 231 | -moz-animation-name: wobble; 232 | -o-animation-name: wobble; 233 | animation-name: wobble; 234 | } 235 | /* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ 236 | 237 | @-webkit-keyframes pulse { 238 | 0% { -webkit-transform: scale(1); } 239 | 50% { -webkit-transform: scale(1.1); } 240 | 100% { -webkit-transform: scale(1); } 241 | } 242 | @-moz-keyframes pulse { 243 | 0% { -moz-transform: scale(1); } 244 | 50% { -moz-transform: scale(1.1); } 245 | 100% { -moz-transform: scale(1); } 246 | } 247 | @-o-keyframes pulse { 248 | 0% { -o-transform: scale(1); } 249 | 50% { -o-transform: scale(1.1); } 250 | 100% { -o-transform: scale(1); } 251 | } 252 | @keyframes pulse { 253 | 0% { transform: scale(1); } 254 | 50% { transform: scale(1.1); } 255 | 100% { transform: scale(1); } 256 | } 257 | 258 | .pulse { 259 | -webkit-animation-name: pulse; 260 | -moz-animation-name: pulse; 261 | -o-animation-name: pulse; 262 | animation-name: pulse; 263 | } 264 | -------------------------------------------------------------------------------- /css/jQuery.chatbox.css: -------------------------------------------------------------------------------- 1 | .chatbox-container{ 2 | position: fixed; 3 | bottom: 0px; 4 | right: 20px; 5 | } 6 | .chatbox { 7 | position: fixed; 8 | bottom: 0px; 9 | width: 225px; 10 | display: none; 11 | bottom: 0px; 12 | right: 0px; 13 | -webkit-transition: right 0.3s; 14 | -moz-transition: right 0.3s; 15 | transition: right 0.3s; 16 | 17 | -webkit-animation-duration: .8s; 18 | -webkit-animation-iteration-count: 1; 19 | -moz-animation-duration: .8s; 20 | -moz-animation-iteration-count: 1; 21 | animation-duration: .8s; 22 | animation-iteration-count: 1; 23 | } 24 | .chatbox-selected { 25 | box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.2); 26 | } 27 | .chatbox-header { 28 | background-color: #176689; 29 | padding:7px; 30 | color: #ffffff; 31 | border-right:1px solid #176689; 32 | border-left:1px solid #176689; 33 | -webkit-transition: background-color 0.3s; 34 | -moz-transition: background-color 0.3s; 35 | transition: background-color 0.3s; 36 | } 37 | .chatbox-title { 38 | font-size: 11px; 39 | text-shadow: none; 40 | float: left; 41 | text-overflow: ellipsis; 42 | overflow: hidden; 43 | white-space: nowrap; 44 | width: 80%; 45 | cursor: pointer; 46 | } 47 | .chatbox-options { 48 | float: right; 49 | } 50 | .chatbox-options a { 51 | text-decoration: none; 52 | color: white; 53 | font-weight:bold; 54 | font-family:Verdana,Arial,"Bitstream Vera Sans",sans-serif; 55 | } 56 | .chatbox-blink { 57 | background-color: #0F820C /* #f99d39 */; 58 | border-right:1px solid #0F820C; 59 | border-left:1px solid #0F820C; 60 | } 61 | .chatbox-content { 62 | font-family: arial,sans-serif; 63 | font-size: 13px; 64 | color: #333333; 65 | height:200px; 66 | width:209px; 67 | overflow-y:auto; 68 | overflow-x:auto; 69 | padding:7px; 70 | border-left:1px solid #cccccc; 71 | border-right:1px solid #cccccc; 72 | border-bottom:1px solid #eeeeee; 73 | background-color: #ffffff; 74 | line-height: 1.3em; 75 | } 76 | .chatbox-input { 77 | padding: 5px; 78 | background-color: #ffffff; 79 | border-left:1px solid #cccccc; 80 | border-right:1px solid #cccccc; 81 | border-bottom:1px solid #cccccc; 82 | } 83 | .chatbox-textarea { 84 | width: 206px; 85 | min-height:40px; 86 | height:50px; 87 | max-height:100px; 88 | padding:3px 0pt 3px 3px; 89 | border: 1px solid #eeeeee; 90 | margin: 1px; 91 | overflow:hidden; 92 | } 93 | .chatbox-textarea-selected { 94 | border: 2px solid #176689; 95 | margin:0; 96 | } 97 | .chatbox-message { 98 | margin-left:1em; 99 | } 100 | .chatbox-info { 101 | margin-left:-1em; 102 | color:#666666; 103 | } 104 | .chatbox-message .message-by { 105 | margin-left:-1em; 106 | font-weight: bold; 107 | color: #176689; 108 | } 109 | .chatbox-message .message-from { 110 | margin-left:-1em; 111 | font-weight: bold; 112 | color: #0F820C; 113 | } 114 | .chatbox-message .message-content { 115 | } -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | html, body { 4 | margin:0px; 5 | overflow:hidden; 6 | 7 | } 8 | 9 | #main-container { 10 | width:100%; 11 | background-color:#ffffff; 12 | overflow-x: hidden; 13 | overflow-y: scroll; 14 | height:100%; 15 | position:absolute; 16 | } -------------------------------------------------------------------------------- /js/jQuery.chatbox.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Chatbox Plugin v0.2.0-alpha 3 | * https://github.com/haozki/Chatbox 4 | * 5 | * Copyright 2013 Haozki 6 | * Released under the MIT license 7 | */ 8 | 9 | (function($, window, undefined){ 10 | var Chatbox = function (options){ 11 | this.init(options); 12 | }; 13 | 14 | Chatbox.prototype = { 15 | constructor: Chatbox, 16 | 17 | // 初始化一个实例 18 | init: function (options){ 19 | var self = this; // 缓存当前实例的this指针 20 | var opts = this.opts = setOption(options); 21 | var boxFrame = '\ 22 |
\n\ 23 |
\n\ 24 |
'+opts.title+'
\n\ 25 |
\n\ 26 | -\n\ 27 | X\n\ 28 |
\n\ 29 |
\n\ 30 |
\n\ 31 |
\n\ 32 |
\n\ 33 |
\n\ 34 | \n\ 35 |
\n\ 36 |
\n\ 37 |
\n'; 38 | $('.chatbox-container').append(boxFrame); 39 | 40 | // 将插入页面的DOM对象由jQuery封装并保存在当前实例的属性中 41 | var $elem = this.$elem = $('#'+opts.boxId); 42 | 43 | // 窗口焦点动作处理 44 | /** Note: 整个窗体Div并不支持focus事件,借由click事件来触发子元素.chatbox-textarea的focus事件也就相当于触发了窗体本身的focus事件 */ 45 | $elem.on('click',function(){ 46 | $elem.addClass('chatbox-selected'); 47 | $elem.find('.chatbox-textarea').focus(); 48 | }); 49 | $elem.find('.chatbox-textarea').on('focusout',function(){ 50 | $elem.removeClass('chatbox-selected'); 51 | }); 52 | 53 | // 窗口最小化处理动作 54 | $elem.find('.minimize').on('click',function(event){ 55 | // 阻止浏览器默认按键事件 56 | event.preventDefault(); 57 | 58 | $elem.find('.chatbox-body').slideToggle(); 59 | 60 | // 阻止浏览器默认按键事件(对于IE等) 61 | return false; 62 | }); 63 | $elem.find('.chatbox-title').on('click',function(){ 64 | $elem.find('.chatbox-body').slideToggle(500); 65 | }); 66 | 67 | // 窗口关闭处理动作 68 | $elem.find('.close').on('click',function(event){ 69 | // 阻止浏览器默认按键事件 70 | event.preventDefault(); 71 | 72 | self.destroy(); 73 | 74 | // 阻止浏览器默认按键事件(对于IE等) 75 | return false; 76 | }); 77 | 78 | // 输入区域焦点处理动作 79 | $elem.find('.chatbox-textarea').on('blur',function(){ 80 | $(this).removeClass('chatbox-textarea-selected'); 81 | }).on('focus',function(){ 82 | $(this).addClass('chatbox-textarea-selected'); 83 | }); 84 | 85 | // 消息发送处理动作 86 | $elem.find('.chatbox-textarea').on('keydown',function(event){ 87 | if(event.keyCode == 13){ 88 | // 阻止浏览器默认按键事件 89 | event.preventDefault(); 90 | 91 | self.message($(this).val(),'to'); 92 | 93 | // 阻止浏览器默认按键事件(对于IE等) 94 | return false; 95 | } 96 | }); 97 | 98 | // 在页面中显示当前DOM元素 99 | $elem.slideDown(500); 100 | 101 | // 将对象实例附加到DOM对象中 102 | $elem.data('chatbox',this); 103 | 104 | /** 触发回调函数 **/ 105 | setCallback.call(this,'onChatboxCreate'); 106 | 107 | /** 输出调试 **/ 108 | debug('Chatbox create','[',this,$elem,']'); 109 | }, 110 | // API方法:弹出聊天窗 111 | show: function(){ 112 | this.$elem.find('.chatbox-body').slideDown(500); 113 | }, 114 | // API方法:隐藏聊天窗 115 | hide: function(){ 116 | this.$elem.find('.chatbox-body').slideUp(500); 117 | }, 118 | // API方法:启用聊天窗 119 | enable: function(){ 120 | this.opts.enabled = true; 121 | this.$elem.find('.chatbox-textarea').prop('disabled',false); 122 | 123 | /** 触发回调函数 **/ 124 | setCallback.call(this,'onChatboxEnable'); 125 | 126 | /** 输出调试 **/ 127 | debug('Chatbox enabled','[',this,this.$elem,']'); 128 | }, 129 | // API方法:禁用聊天窗 130 | disable: function(){ 131 | this.opts.enabled = false; 132 | this.$elem.find('.chatbox-textarea').prop('disabled',true); 133 | 134 | /** 触发回调函数 **/ 135 | setCallback.call(this,'onChatboxDisable'); 136 | 137 | /** 输出调试 **/ 138 | debug('Chatbox disabled','[',this,this.$elem,']'); 139 | }, 140 | // API方法:设置消息发送 141 | messageTo: function(msg){ 142 | if (msg == ''){ 143 | this.message('Can not send empty message','system'); 144 | }else{ 145 | msg = msg.replace(/^\s+|\s+$/g,""); // 去除首尾空字符 146 | msg = msg.replace(//g,">").replace(/\"/g,"""); // 去除HTML特殊标记 147 | var msgItem = '\ 148 |
\n\ 149 | '+globalOptions.user+'\n\ 150 | '+msg+'\n\ 151 |
\n'; 152 | this.$elem.find('.chatbox-content').append(msgItem); 153 | this.$elem.find('.chatbox-content').scrollTop(this.$elem.find('.chatbox-content').get(0).scrollHeight); 154 | this.$elem.find('.chatbox-textarea').val('').focus(); 155 | 156 | /** 触发回调函数 **/ 157 | setCallback.call(this,'onMessageSend',msg); 158 | 159 | /** 输出调试 **/ 160 | debug('Message send',this.opts.id,':',msg); 161 | } 162 | }, 163 | // API方法:设置消息接收内容(包括系统消息) 164 | message: function(msg,type){ 165 | var self = this; 166 | switch (type){ 167 | case 'to': 168 | this.messageTo(msg); 169 | break; 170 | case 'from': 171 | var msgItem = '\ 172 |
\n\ 173 | '+this.opts.user+'\n\ 174 | '+msg+'\n\ 175 |
\n'; 176 | this.$elem.find('.chatbox-content').append(msgItem); 177 | this.$elem.find('.chatbox-content').scrollTop(this.$elem.find('.chatbox-content').get(0).scrollHeight); 178 | this.blink(); 179 | this.animate(); 180 | 181 | /** 触发回调函数 **/ 182 | setCallback.call(this,'onMessageReceive',msg); 183 | 184 | /** 输出调试 **/ 185 | debug('Message receive',this.opts.id,':',msg); 186 | break; 187 | case 'system': 188 | var msgItem = '\ 189 |
\n\ 190 | '+msg+'\n\ 191 |
\n'; 192 | this.$elem.find('.chatbox-content').append(msgItem); 193 | this.$elem.find('.chatbox-content').scrollTop(this.$elem.find('.chatbox-content').get(0).scrollHeight); 194 | this.blink(); 195 | this.animate(); 196 | 197 | /** 触发回调函数 **/ 198 | setCallback.call(this,'onMessageSystem',msg); 199 | 200 | /** 输出调试 **/ 201 | debug('System message',this.opts.id,':',msg); 202 | break; 203 | } 204 | }, 205 | // API方法:高亮标题栏闪烁提示 206 | blink: function(){ 207 | var self = this; 208 | var blinkTimes = 0; 209 | do{ 210 | setTimeout(function(){ 211 | self.$elem.find('.chatbox-header').toggleClass('chatbox-blink'); 212 | },300*blinkTimes); 213 | blinkTimes++; 214 | if (blinkTimes == 6){ 215 | blinkTimes = 0; 216 | break; 217 | } 218 | }while(blinkTimes != 0); 219 | }, 220 | // API方法:消息提醒动画 221 | animate: function(){ 222 | var self = this; 223 | this.$elem 224 | .addClass('animated '+globalOptions.animate) 225 | .one('webkitAnimationEnd mozAnimationEnd oAnimationEnd animationEnd',function(){ 226 | $(this).removeClass('animated '+globalOptions.animate); 227 | }); 228 | 229 | // 当浏览器不支持以上事件时,由程序控制动画停止 230 | setTimeout(function(){ 231 | self.$elem.removeClass('animated '+globalOptions.animate); 232 | },800); 233 | 234 | return this; 235 | }, 236 | // API方法:销毁聊天窗实例 237 | destroy: function(){ 238 | var self = this; 239 | // 从页面淡出该DOM元素并从页面移除 240 | this.$elem.fadeOut(500,function(){ 241 | $(this).remove(); 242 | // 销毁对象实例 243 | boxInstance[self.opts.id] = null; 244 | delete boxInstance[self.opts.id]; 245 | 246 | /** 触发回调函数 **/ 247 | setCallback.call(self,'onChatboxDestroy'); 248 | 249 | /** 输出调试 **/ 250 | debug('Chatbox close','[',self,self.$elem,']'); 251 | 252 | // 重新布局 253 | layout(); 254 | }); 255 | } 256 | }; 257 | 258 | // 聊天窗实例对象集合 259 | var boxInstance = {}; 260 | 261 | // 聊天窗口布局 262 | function layout(){ 263 | var align = 0; 264 | $.each(boxInstance, function(i){ 265 | var ibox = $("#chatbox_"+i); 266 | var offset = align * (ibox.width()+5) + 20; 267 | 268 | /** 输出调试 **/ 269 | debug('Chatbox realignment',ibox,' offset:',offset); 270 | ibox.css('right', offset+'px'); 271 | align++; 272 | }); 273 | } 274 | 275 | // 处理选项默认值 276 | function setOption(options){ 277 | options.boxId = globalOptions.idPrefix + options.id; // 设定聊天窗Div的id值 278 | options.enabled = true; 279 | if (options.title == null) { 280 | options.title = 'Chat with '+options.user; 281 | } 282 | return options; 283 | } 284 | 285 | // 设置回调响应(执行优先级:全局回调函数 > 实例回调函数) 286 | function setCallback(callback){ 287 | if (typeof this.opts[callback] === 'function'){ 288 | // 触发实例回调函数 289 | this.opts[callback].apply(this, Array.prototype.slice.call(arguments, 1)); 290 | 291 | /** 输出调试 **/ 292 | debug(callback,this.opts[callback]); 293 | }else if (typeof globalOptions[callback] === 'function'){ 294 | // 触发全局回调函数 295 | globalOptions[callback].apply(this, Array.prototype.slice.call(arguments, 1)); 296 | 297 | /** 输出调试 **/ 298 | debug(callback,globalOptions[callback]); 299 | }else{ 300 | /** 输出调试 **/ 301 | debug(callback,'No callback function set'); 302 | return false; 303 | } 304 | } 305 | 306 | // 调试函数 307 | function debug(){ 308 | if (globalOptions.debug == true){ 309 | var logger = window.console['debug']; 310 | if (typeof logger === 'function'){ 311 | logger.apply(window.console, arguments); 312 | } 313 | } 314 | } 315 | 316 | // 定义全局选项 317 | var globalOptions = {}; 318 | 319 | // 全局选项默认值 320 | var globalOptionsDefault = { 321 | id:null, 322 | user:null, 323 | debug:false, 324 | idPrefix:'chatbox_', 325 | animate:'bounce' 326 | 327 | /* 针对全局的回调函数 328 | onChatboxCreate // 创建聊天窗时触发 329 | onChatboxEnable // 聊天窗被启用时触发 330 | onChatboxDisable // 聊天窗被禁用时触发 331 | onMessageSend // 发送消息时触发 332 | onMessageEmpty // 发送消息为空时触发 333 | onMessageReceive // 收到消息时触发 334 | onChatboxDestroy // 销毁聊天窗时触发 335 | */ 336 | } 337 | 338 | $.chatbox = function(opts){ 339 | if (!$('.chatbox-container').length){ 340 | $('body').append('
'); 341 | } 342 | // 当且仅当参数是对象,并且聊天窗实例id不存在时创建新的实例 343 | if (typeof opts === 'object' && !boxInstance[opts.id]){ 344 | // 覆盖全局选项默认值 345 | globalOptions = $.extend({}, globalOptionsDefault, $.chatbox.globalOptions || {}); 346 | 347 | // 实例选项默认值 348 | var defaults = { 349 | id:null, 350 | user:null, 351 | title:null 352 | 353 | /* 针对具体实例的回调函数 354 | onChatboxCreate // 创建聊天窗时触发 355 | onChatboxEnable // 聊天窗被启用时触发 356 | onChatboxDisable // 聊天窗被禁用时触发 357 | onMessageSend // 发送消息时触发 358 | onMessageEmpty // 发送消息为空时触发 359 | onMessageReceive // 收到消息时触发 360 | onChatboxDestroy // 销毁聊天窗时触发 361 | */ 362 | }; 363 | 364 | // 以用户的自定义选项覆盖实例默认选项 365 | var options = $.extend(defaults, opts || {}); 366 | 367 | // 创建实例 368 | boxInstance[options.id] = new Chatbox(options); 369 | 370 | /** 输出调试 **/ 371 | debug('Chatbox instance collections',boxInstance); 372 | layout(); 373 | } 374 | // 当参数是实例id时返回该实例的引用 375 | else if(typeof opts === 'number' || typeof opts === 'string'){ 376 | if (boxInstance[opts] != undefined){ 377 | return boxInstance[opts]; 378 | }else{ 379 | /** 输出调试 **/ 380 | debug('Error','Chatbox not exist') 381 | } 382 | }else{ 383 | return false; 384 | } 385 | }; 386 | 387 | // 全局API方法:返回当前所有聊天窗实例的队列 388 | $.chatbox.getQueue = function(){ 389 | return boxInstance; 390 | } 391 | 392 | // 全局选项自定义值(默认对外暴露默认值) 393 | $.chatbox.globalOptions = globalOptionsDefault; 394 | 395 | })(jQuery, window); --------------------------------------------------------------------------------