/* * Released under BSD License * Copyright (c) 2014-2015 hizzgdev@163.com * * Project Home: * https://github.com/hizzgdev/jsmind/ */ (function($w){ 'use strict'; var $d = $w.document; var __name__ = 'jsMind'; var jsMind = $w[__name__]; if(!jsMind){return;} if(typeof jsMind.draggable != 'undefined'){return;} var jdom = jsMind.util.dom; var jcanvas = jsMind.util.canvas; var clear_selection = 'getSelection' in $w ? function(){ $w.getSelection().removeAllRanges(); } : function(){ $d.selection.empty(); }; var options = { line_width : 5, lookup_delay : 500, lookup_interval : 80 }; jsMind.draggable = function(jm){ this.jm = jm; this.e_canvas = null; this.canvas_ctx = null; this.shadow = null; this.shadow_w = 0; this.shadow_h = 0; this.active_node = null; this.target_node = null; this.target_direct = null; this.client_w = 0; this.client_h = 0; this.offset_x = 0; this.offset_y = 0; this.hlookup_delay = 0; this.hlookup_timer = 0; this.capture = false; this.moved = false; }; jsMind.draggable.prototype = { init:function(){ this._create_canvas(); this._create_shadow(); this._event_bind(); }, resize:function(){ this.jm.view.e_nodes.appendChild(this.shadow); this.e_canvas.width=this.jm.view.size.w; this.e_canvas.height=this.jm.view.size.h; }, _create_canvas:function(){ var c = $d.createElement('canvas'); this.jm.view.e_panel.appendChild(c); var ctx = c.getContext('2d'); this.e_canvas = c; this.canvas_ctx = ctx; }, _create_shadow:function(){ var s = $d.createElement('jmnode'); s.style.visibility = 'hidden'; s.style.zIndex = '3'; s.style.cursor = 'move'; s.style.opacity= '0.7'; this.shadow = s; }, reset_shadow:function(el){ var s = this.shadow.style; this.shadow.innerHTML = el.innerHTML; s.left = el.style.left; s.top = el.style.top; s.width = el.style.width; s.height = el.style.height; s.backgroundImage = el.style.backgroundImage; s.backgroundSize = el.style.backgroundSize; s.transform = el.style.transform; this.shadow_w = this.shadow.clientWidth; this.shadow_h = this.shadow.clientHeight; }, show_shadow:function(){ if(!this.moved){ this.shadow.style.visibility = 'visible'; } }, hide_shadow:function(){ this.shadow.style.visibility = 'hidden'; }, clear_lines:function(){ jcanvas.clear(this.canvas_ctx, 0, 0, this.jm.view.size.w, this.jm.view.size.h); }, _magnet_shadow:function(node){ if(!!node){ this.canvas_ctx.lineWidth = options.line_width; this.canvas_ctx.strokeStyle = 'rgba(0,0,0,0.3)'; this.canvas_ctx.lineCap = 'round'; this.clear_lines(); jcanvas.lineto(this.canvas_ctx, node.sp.x, node.sp.y, node.np.x, node.np.y); } }, _lookup_close_node:function(){ var root = this.jm.get_root(); var root_location = root.get_location(); var root_size = root.get_size(); var root_x = root_location.x + root_size.w/2; var sw = this.shadow_w; var sh = this.shadow_h; var sx = this.shadow.offsetLeft; var sy = this.shadow.offsetTop; var ns,nl; var direct = (sx + sw/2)>=root_x ? jsMind.direction.right : jsMind.direction.left; var nodes = this.jm.mind.nodes; var node = null; var min_distance = Number.MAX_VALUE; var distance = 0; var closest_node = null; var closest_p = null; var shadow_p = null; for(var nodeid in nodes){ var np,sp; node = nodes[nodeid]; if(node.isroot || node.direction == direct){ if(node.id == this.active_node.id){ continue; } ns = node.get_size(); nl = node.get_location(); if(direct == jsMind.direction.right){ if(sx-nl.x-ns.w<=0){continue;} distance = Math.abs(sx-nl.x-ns.w) + Math.abs(sy+sh/2-nl.y-ns.h/2); np = {x:nl.x+ns.w-options.line_width,y:nl.y+ns.h/2}; sp = {x:sx+options.line_width,y:sy+sh/2}; }else{ if(nl.x-sx-sw<=0){continue;} distance = Math.abs(sx+sw-nl.x) + Math.abs(sy+sh/2-nl.y-ns.h/2); np = {x:nl.x+options.line_width,y:nl.y+ns.h/2}; sp = {x:sx+sw-options.line_width,y:sy+sh/2}; } if(distance < min_distance){ closest_node = node; closest_p = np; shadow_p = sp; min_distance = distance; } } } var result_node = null; if(!!closest_node){ result_node = { node:closest_node, direction:direct, sp:shadow_p, np:closest_p }; } return result_node; }, lookup_close_node:function(){ var node_data = this._lookup_close_node(); if(!!node_data){ this._magnet_shadow(node_data); this.target_node = node_data.node; this.target_direct = node_data.direction; } }, _event_bind:function(){ var jd = this; var container = this.jm.view.container; jdom.add_event(container,'mousedown',function(e){ var evt = e || event; jd.dragstart.call(jd,evt); }); jdom.add_event(container,'mousemove',function(e){ var evt = e || event; jd.drag.call(jd,evt); }); jdom.add_event(container,'mouseup',function(e){ var evt = e || event; jd.dragend.call(jd,evt); }); jdom.add_event(container,'touchstart',function(e){ var evt = e || event; jd.dragstart.call(jd,evt); }); jdom.add_event(container,'touchmove',function(e){ var evt = e || event; jd.drag.call(jd,evt); }); jdom.add_event(container,'touchend',function(e){ var evt = e || event; jd.dragend.call(jd,evt); }); }, dragstart:function(e){ if(!this.jm.get_editable()){return;} if(this.capture){return;} this.active_node = null; var jview = this.jm.view; var el = e.target || event.srcElement; if(el.tagName.toLowerCase() != 'jmnode'){return;} var nodeid = jview.get_binded_nodeid(el); if(!!nodeid){ var node = this.jm.get_node(nodeid); if(!node.isroot){ this.reset_shadow(el); this.active_node = node; this.offset_x = (e.clientX || e.touches[0].clientX) - el.offsetLeft; this.offset_y = (e.clientY || e.touches[0].clientY) - el.offsetTop; this.client_hw = Math.floor(el.clientWidth/2); this.client_hh = Math.floor(el.clientHeight/2); if(this.hlookup_delay != 0){ $w.clearTimeout(this.hlookup_delay); } if(this.hlookup_timer != 0){ $w.clearInterval(this.hlookup_timer); } var jd = this; this.hlookup_delay = $w.setTimeout(function(){ jd.hlookup_delay = 0; jd.hlookup_timer = $w.setInterval(function(){ jd.lookup_close_node.call(jd); },options.lookup_interval); },options.lookup_delay); this.capture = true; } } }, drag:function(e){ if(!this.jm.get_editable()){return;} if(this.capture){ e.preventDefault(); this.show_shadow(); this.moved = true; clear_selection(); var px = (e.clientX || e.touches[0].clientX) - this.offset_x; var py = (e.clientY || e.touches[0].clientY) - this.offset_y; var cx = px + this.client_hw; var cy = py + this.client_hh; this.shadow.style.left = px + 'px'; this.shadow.style.top = py + 'px'; clear_selection(); } }, dragend:function(e){ if(!this.jm.get_editable()){return;} if(this.capture){ if(this.hlookup_delay != 0){ $w.clearTimeout(this.hlookup_delay); this.hlookup_delay = 0; this.clear_lines(); } if(this.hlookup_timer != 0){ $w.clearInterval(this.hlookup_timer); this.hlookup_timer = 0; this.clear_lines(); } if(this.moved){ var src_node = this.active_node; var target_node = this.target_node; var target_direct = this.target_direct; this.move_node(src_node,target_node,target_direct); } this.hide_shadow(); } this.moved = false; this.capture = false; }, move_node:function(src_node,target_node,target_direct){ var shadow_h = this.shadow.offsetTop; if(!!target_node && !!src_node && !jsMind.node.inherited(src_node, target_node)){ // lookup before_node var sibling_nodes = target_node.children; var sc = sibling_nodes.length; var node = null; var delta_y = Number.MAX_VALUE; var node_before = null; var beforeid = '_last_'; while(sc--){ node = sibling_nodes[sc]; if(node.direction == target_direct && node.id != src_node.id){ var dy = node.get_location().y - shadow_h; if(dy > 0 && dy < delta_y){ delta_y = dy; node_before = node; beforeid = '_first_'; } } } if(!!node_before){beforeid = node_before.id;} this.jm.move_node(src_node.id, beforeid, target_node.id, target_direct); } this.active_node = null; this.target_node = null; this.target_direct = null; }, jm_event_handle:function(type,data){ if(type === jsMind.event_type.resize){ this.resize(); } } }; var draggable_plugin = new jsMind.plugin('draggable',function(jm){ var jd = new jsMind.draggable(jm); jd.init(); jm.add_event_listener(function(type,data){ jd.jm_event_handle.call(jd,type,data); }); }); jsMind.register_plugin(draggable_plugin); })(window);