1 /** 2 * @fileOverview dd support for kissy , dd objects central management module 3 * @author yiminghe@gmail.com 4 */ 5 KISSY.add('dd/ddm', function (S, UA, DOM, Event, Node, Base) { 6 7 var win = S.Env.host, 8 doc = win.document, 9 ie6 = UA['ie'] === 6, 10 11 // prevent collision with click , only start when move 12 PIXEL_THRESH = 3, 13 // or start when mousedown for 1 second 14 BUFFER_TIME = 1000, 15 16 MOVE_DELAY = 30, 17 _showShimMove = S.throttle(move, 18 MOVE_DELAY), 19 SHIM_ZINDEX = 999999; 20 21 /** 22 * @memberOf DD 23 * @field 24 * @namespace Manager for Drag and Drop. 25 */ 26 function DDM() { 27 var self = this; 28 DDM.superclass.constructor.apply(self, arguments); 29 } 30 31 DDM.ATTRS = 32 /** 33 * @lends DD.DDM 34 */ 35 { 36 /** 37 * cursor style when dragging,if shimmed the shim will get the cursor. 38 * @type String 39 */ 40 dragCursor:{ 41 value:'move' 42 }, 43 44 /*** 45 * the number of pixels to move to start a drag operation,default is 3. 46 * @type Number 47 */ 48 clickPixelThresh:{ 49 value:PIXEL_THRESH 50 }, 51 52 /** 53 * the number of milliseconds to start a drag operation after mousedown,default is 1000 54 * @type Number 55 */ 56 bufferTime:{ value:BUFFER_TIME }, 57 58 /** 59 * currently active draggable object 60 * @type DD.Draggable 61 */ 62 activeDrag:{}, 63 64 /** 65 * currently active droppable object 66 * @type DD.Droppable 67 */ 68 activeDrop:{}, 69 70 /** 71 * a array of drop targets 72 * @type DD.Droppable[] 73 */ 74 drops:{ 75 value:[] 76 }, 77 78 /** 79 * a array of the valid drop targets for this interaction 80 * @type DD.Droppable[] 81 */ 82 validDrops:{ 83 value:[] 84 } 85 }; 86 87 /* 88 全局鼠标移动事件通知当前拖动对象正在移动 89 注意:chrome8: click 时 mousedown-mousemove-mouseup-click 也会触发 mousemove 90 */ 91 function move(ev) { 92 var self = this, 93 __activeToDrag , 94 activeDrag; 95 // 先处理预备役,效率! 96 if (__activeToDrag = self.__activeToDrag) { 97 //防止 ie 选择到字 98 ev.preventDefault(); 99 __activeToDrag._move(ev); 100 101 } else if (activeDrag = self.get('activeDrag')) { 102 //防止 ie 选择到字 103 ev.preventDefault(); 104 activeDrag._move(ev); 105 /** 106 * 获得当前的激活drop 107 */ 108 notifyDropsMove(self, ev, activeDrag); 109 } 110 } 111 112 113 function notifyDropsMove(self, ev, activeDrag) { 114 var mode = activeDrag.get("mode"), 115 drops = self.get("validDrops"), 116 activeDrop = 0, 117 oldDrop, 118 vArea = 0, 119 dragRegion = region(activeDrag.get("node")), 120 dragArea = area(dragRegion); 121 122 S.each(drops, function (drop) { 123 var a, 124 node = drop.getNodeFromTarget(ev, 125 // node 126 activeDrag.get("dragNode")[0], 127 // proxy node 128 activeDrag.get("node")[0]); 129 130 if (!node 131 // 当前 drop 区域已经包含 activeDrag.get("node") 132 // 不要返回,可能想调整位置 133 ) { 134 return; 135 } 136 137 if (mode == "point") { 138 //取鼠标所在的 drop 区域 139 if (inNodeByPointer(node, activeDrag.mousePos)) { 140 a = area(region(node)); 141 if (!activeDrop) { 142 activeDrop = drop; 143 vArea = a; 144 } else { 145 // 当前得到的可放置元素范围更小,取范围小的那个 146 if (a < vArea) { 147 activeDrop = drop; 148 vArea = a; 149 } 150 } 151 } 152 } else if (mode == "intersect") { 153 //取一个和activeDrag交集最大的drop区域 154 a = area(intersect(dragRegion, region(node))); 155 if (a > vArea) { 156 vArea = a; 157 activeDrop = drop; 158 } 159 160 } else if (mode == "strict") { 161 //drag 全部在 drop 里面 162 a = area(intersect(dragRegion, region(node))); 163 if (a == dragArea) { 164 activeDrop = drop; 165 return false; 166 } 167 } 168 }); 169 oldDrop = self.get("activeDrop"); 170 if (oldDrop && oldDrop != activeDrop) { 171 oldDrop._handleOut(ev); 172 activeDrag._handleOut(ev); 173 } 174 self.__set("activeDrop", activeDrop); 175 if (activeDrop) { 176 if (oldDrop != activeDrop) { 177 activeDrop._handleEnter(ev); 178 } else { 179 // 注意处理代理时内部节点变化导致的 out、enter 180 activeDrop._handleOver(ev); 181 } 182 } 183 } 184 185 186 /** 187 * 垫片只需创建一次 188 */ 189 function activeShim(self) { 190 //创造垫片,防止进入iframe,外面document监听不到 mousedown/up/move 191 self._shim = new Node("<div " + 192 "style='" + 193 //red for debug 194 "background-color:red;" + 195 "position:" + (ie6 ? 'absolute' : 'fixed') + ";" + 196 "left:0;" + 197 "width:100%;" + 198 "height:100%;" + 199 "top:0;" + 200 "cursor:" + ddm.get("dragCursor") + ";" + 201 "z-index:" + 202 //覆盖iframe上面即可 203 SHIM_ZINDEX 204 + ";" + 205 "'><" + "/div>") 206 .prependTo(doc.body || doc.documentElement) 207 //0.5 for debug 208 .css("opacity", 0); 209 210 activeShim = showShim; 211 212 if (ie6) { 213 // ie6 不支持 fixed 以及 width/height 100% 214 // support dd-scroll 215 // prevent empty when scroll outside initial window 216 Event.on(win, "resize scroll", adjustShimSize, self); 217 } 218 219 showShim(self); 220 } 221 222 var adjustShimSize = S.throttle(function () { 223 var self = this, 224 activeDrag; 225 if ((activeDrag = self.get("activeDrag")) && 226 activeDrag.get("shim")) { 227 self._shim.css({ 228 width:DOM.docWidth(), 229 height:DOM.docHeight() 230 }); 231 } 232 }, MOVE_DELAY); 233 234 function showShim(self) { 235 // determine cursor according to activeHandler and dragCursor 236 var ah = self.get("activeDrag").get('activeHandler'), 237 cur = 'auto'; 238 if (ah) { 239 cur = ah.css('cursor'); 240 } 241 if (cur == 'auto') { 242 cur = self.get('dragCursor'); 243 } 244 self._shim.css({ 245 cursor:cur, 246 display:"block" 247 }); 248 if (ie6) { 249 adjustShimSize.call(self); 250 } 251 } 252 253 /** 254 * 开始时注册全局监听事件 255 */ 256 function registerEvent(self) { 257 Event.on(doc, 'mouseup', self._end, self); 258 Event.on(doc, 'mousemove', _showShimMove, self); 259 // ie6 will not response to event when cursor is out of window. 260 if (UA.ie === 6) { 261 doc.body.setCapture(); 262 } 263 } 264 265 /** 266 * 结束时需要取消掉,防止平时无谓的监听 267 */ 268 function unRegisterEvent(self) { 269 Event.remove(doc, 'mousemove', _showShimMove, self); 270 Event.remove(doc, 'mouseup', self._end, self); 271 if (UA.ie === 6) { 272 doc.body.releaseCapture(); 273 } 274 } 275 276 277 function _activeDrops(self) { 278 var drops = self.get("drops"); 279 self.__set("validDrops", []); 280 S.each(drops, function (d) { 281 d._active(); 282 }); 283 } 284 285 function _deActiveDrops(self) { 286 var drops = self.get("drops"); 287 self.__set("validDrops", []); 288 S.each(drops, function (d) { 289 d._deActive(); 290 }); 291 } 292 293 /* 294 负责拖动涉及的全局事件: 295 1.全局统一的鼠标移动监控 296 2.全局统一的鼠标弹起监控,用来通知当前拖动对象停止 297 3.为了跨越 iframe 而统一在底下的遮罩层 298 */ 299 S.extend(DDM, Base, { 300 301 /** 302 * 可能要进行拖放的对象,需要通过 buffer/pixelThresh 考验 303 */ 304 __activeToDrag:0, 305 306 _regDrop:function (d) { 307 this.get("drops").push(d); 308 }, 309 310 _unRegDrop:function (d) { 311 var self = this, 312 index = S.indexOf(d, self.get("drops")); 313 if (index != -1) { 314 self.get("drops").splice(index, 1); 315 } 316 }, 317 318 /** 319 * 注册可能将要拖放的节点 320 * @param drag 321 */ 322 _regToDrag:function (drag) { 323 var self = this; 324 // 事件先要注册好,防止点击,导致 mouseup 时还没注册事件 325 self.__activeToDrag = drag; 326 registerEvent(self); 327 328 }, 329 330 /** 331 * 真正开始 drag 332 * 当前拖动对象通知全局:我要开始啦 333 * 全局设置当前拖动对象, 334 */ 335 _start:function () { 336 var self = this, 337 drops = self.get("drops"), 338 drag = self.__activeToDrag; 339 self.__set('activeDrag', drag); 340 // 预备役清掉 341 self.__activeToDrag = 0; 342 // 真正开始移动了才激活垫片 343 if (drag.get("shim")) { 344 activeShim(self); 345 } 346 cacheWH(drag.get("node")); 347 _activeDrops(self); 348 }, 349 350 _addValidDrop:function (drop) { 351 this.get("validDrops").push(drop); 352 }, 353 354 /** 355 * 全局通知当前拖动对象:结束拖动了! 356 */ 357 _end:function () { 358 var self = this, 359 activeDrag = self.get("activeDrag"), 360 activeDrop = self.get("activeDrop"); 361 unRegisterEvent(self); 362 // 预备役清掉 , click 情况下 mousedown->mouseup 极快过渡 363 if (self.__activeToDrag) { 364 self.__activeToDrag._end(); 365 self.__activeToDrag = 0; 366 } 367 if (self._shim) { 368 self._shim.hide(); 369 } 370 if (!activeDrag) { 371 return; 372 } 373 activeDrag._end(); 374 _deActiveDrops(self); 375 if (activeDrop) { 376 activeDrop._end(); 377 } 378 self.__set("activeDrag", null); 379 self.__set("activeDrop", null); 380 } 381 }); 382 383 function region(node) { 384 var offset = node.offset(); 385 if (!node.__dd_cached_width) { 386 S.log("no cache in dd!"); 387 S.log(node[0]); 388 } 389 return { 390 left:offset.left, 391 right:offset.left + (node.__dd_cached_width || node.outerWidth()), 392 top:offset.top, 393 bottom:offset.top + (node.__dd_cached_height || node.outerHeight()) 394 }; 395 } 396 397 function inRegion(region, pointer) { 398 return region.left <= pointer.left 399 && region.right >= pointer.left 400 && region.top <= pointer.top 401 && region.bottom >= pointer.top; 402 } 403 404 function area(region) { 405 if (region.top >= region.bottom || region.left >= region.right) { 406 return 0; 407 } 408 return (region.right - region.left) * (region.bottom - region.top); 409 } 410 411 function intersect(r1, r2) { 412 var t = Math.max(r1['top'], r2.top), 413 r = Math.min(r1.right, r2.right), 414 b = Math.min(r1['bottom'], r2.bottom), 415 l = Math.max(r1.left, r2.left); 416 return { 417 left:l, 418 right:r, 419 top:t, 420 bottom:b 421 }; 422 } 423 424 function inNodeByPointer(node, point) { 425 return inRegion(region(node), point); 426 } 427 428 function cacheWH(node) { 429 if (node) { 430 node.__dd_cached_width = node.outerWidth(); 431 node.__dd_cached_height = node.outerHeight(); 432 } 433 } 434 435 var ddm = new DDM(); 436 ddm.inRegion = inRegion; 437 ddm.region = region; 438 ddm.area = area; 439 ddm.cacheWH = cacheWH; 440 441 ddm.PREFIX_CLS='ks-dd-'; 442 return ddm; 443 }, { 444 requires:["ua", "dom", "event", "node", "base"] 445 }); 446 447 /** 448 * refer 449 * - YUI3 dd 450 */ 451