使用 jsPlumb 绘制拓扑图 —— 异步加载与绘制的实现


        本文实现的方法可以边异步加载数据边绘制拓扑图。 有若干点需要说明一下:

        1.  一次性获取所有数据并绘制拓扑图, 请参见文章: <使用 JsPlumb 绘制拓扑图的通用方法> ; 本文实现的最终显示效果与之类似, 所使用的基本方法与之类似。

        2.  在此次实现中, 可以一边异步加载数据一边绘制拓扑图, 是动态可扩展的; 

        3.  所有影响节点位置、布局的配置均放置在最前面, 便于修改, 避免在代码中穿梭, 浪费时间;

        4.  布局算法比之前的实现更加完善;

        5.  此实现由于与业务逻辑绑得比较紧, 可复用的部分不多, 但是可以作为一个模板, 用在读者自己的场景中, 自行修改相应的节点类型、URL等。

        6.  添加了附着点的点击事件处理, 可以刷新显示关联实体;

        7.  主流程很简单:  发送 AJAX 请求获取数据 ---> 创建节点(实际上就是DIV) ---> 计算节点位置、布局 ---> 添加节点附着点 ---> 缓存节点连接 ---> 连接所有现有的缓存节点连接。  多个 AJAX 请求的处理是异步的, 顺序没有控制。

        8.  代码:   

  1 /**

  2  * 使用 jsPlumb 根据指定的拓扑数据结构绘制拓扑图

  3  * 使用 drawTopo_asyn(vmName, regionNo, parentDivId) 方法

  4  */

  5 /**

  6  * 初始化拓扑图实例及外观设置

  7  */

  8 (function() {

  9     

 10     jsPlumb.importDefaults({

 11         

 12         DragOptions : { cursor: 'pointer', zIndex:2000 },

 13     

 14         EndpointStyles : [{ fillStyle:'#225588' }, { fillStyle:'#558822' }],

 15     

 16         Endpoints : [ [ "Dot", { radius:2 } ], [ "Dot", { radius: 2 } ]],

 17     

 18         ConnectionOverlays : [

 19             [ "Label", { location:1 } ],

 20             [ "Label", { 

 21                 location:0.1,

 22                 id:"label",

 23                 cssClass:"aLabel"

 24             }]

 25         ]

 26     });

 27     

 28     var connectorPaintStyle = {

 29         lineWidth: 1,

 30         strokeStyle: "#096EBB",

 31         joinstyle:"round",

 32         outlineColor: "#096EBB",

 33         outlineWidth: 1

 34     };

 35     

 36     var connectorHoverStyle = {

 37         lineWidth: 2,

 38         strokeStyle: "#5C96BC",

 39         outlineWidth: 2,

 40         outlineColor:"white"

 41     };

 42     

 43     var endpointHoverStyle = {

 44         fillStyle:"#5C96BC"

 45     };

 46     

 47     window.topoDrawUtil = {

 48             

 49         sourceEndpoint: {

 50             endpoint:"Dot",

 51             paintStyle:{ 

 52                 strokeStyle:"#1e8151",

 53                 fillStyle:"transparent",

 54                 radius: 4,

 55                 lineWidth:2 

 56             },                

 57             isSource:true,

 58             maxConnections:-1,

 59             connector:[ "Flowchart", { stub:[40, 60], gap:1, cornerRadius:5, alwaysRespectStubs:true } ],                                                

 60             connectorStyle: connectorPaintStyle,

 61             hoverPaintStyle: endpointHoverStyle,

 62             connectorHoverStyle: connectorHoverStyle,

 63             dragOptions:{},

 64             overlays:[

 65                 [ "Label", { 

 66                     location:[0.5, 1.5], 

 67                     label:"",

 68                     cssClass:"endpointSourceLabel" 

 69                 } ]

 70             ]

 71         },

 72         

 73         targetEndpoint: {

 74             endpoint: "Dot",                    

 75             paintStyle: { fillStyle:"#1e8151",radius: 2 },

 76             hoverPaintStyle: endpointHoverStyle,

 77             maxConnections:-1,

 78             dropOptions:{ hoverClass:"hover", activeClass:"active" },

 79             isTarget:true,

 80             overlays:[

 81                 [ "Label", { location:[0.5, -0.5], label:"", cssClass:"endpointTargetLabel" } ]

 82             ]

 83         },

 84         

 85         initConnection: function(connection) {

 86             connection.getOverlay("label").setLabel(connection.sourceId + "-" + connection.targetId);

 87             connection.bind("editCompleted", function(o) {

 88                 if (typeof console != "undefined")

 89                     console.log("connection edited. path is now ", o.path);

 90             });

 91         },    

 92         

 93         removeAllEndPoints: function(nodeDivId) {

 94             jsPlumb.removeAllEndpoints($('#'+nodeDivId)); 

 95         },

 96         addEndpoints: function(toId, sourceAnchors, targetAnchors) {

 97             for (var i = 0; i < sourceAnchors.length; i++) {

 98                 var sourceUUID = toId + sourceAnchors[i];

 99                 var endPoint = jsPlumb.addEndpoint(toId, this.sourceEndpoint, { anchor:sourceAnchors[i], uuid:sourceUUID });    

100                 endPoint.bind("click", function(endpoint) {

101                     var anchorType = endpoint.anchor.type;

102                     var nodeType = toId.split('_')[0];

103                     var content = toId.split('_')[1];

104                     if (nodeType == VM_TYPE) {

105                         switch (anchorType) {

106                             case 'Right':

107                                 cacheKey = 'VM-DEVICE-'+ vmNodeData.key;

108                                 cacheConnectionData[cacheKey] = null;

109                                 linkDevices(vmNodeData, vmNodeData.key);

110                                 break;

111                             case 'Top':

112                                 cacheKey = 'VM-ACCOUNT-'+ vmNodeData.key;

113                                 cacheConnectionData[cacheKey] = null;

114                                 vmName = vmNodeData.key;

115                                 regionNo = vmNodeData.data.region_no;

116                                 linkAccount(vmNodeData, vmName, regionNo);

117                                 break;

118                             case 'Bottom':

119                                 cacheKey = 'VM-NC-'+ vmNodeData.key;

120                                 cacheConnectionData[cacheKey] = null;

121                                 ncId = vmNodeData.data.nc_id;

122                                 regionNo = vmNodeData.data.region_no;

123                                 linkNc(vmNodeData, ncId, regionNo);

124                                 break;

125                             case 'Left':

126                                 cacheKey = 'VM-VIP-'+ vmNodeData.key;

127                                 cacheConnectionData[cacheKey] = null;

128                                 vmInnerIp = vmNodeData.data.vm_inner_ip;

129                                 linkVips(vmNodeData, vmInnerIp);

130                                 break;

131                             default:

132                                 break;

133                         }

134                     }

135                     else if (nodeType == DEVICE_TYPE) {

136                         if (anchorType == 'Bottom') {

137                             cacheKey = 'DEVICE-SNAPSHOT-'+ content;

138                             cacheConnectionData[cacheKey] = null;

139                             deviceNodeData = deviceNodeDataMapping[content];

140                             linkSnapshot(deviceNodeData.data.aliUid, content, deviceNodeData);

141                         }

142                     }

143                 });

144             }

145             for (var j = 0; j < targetAnchors.length; j++) {

146                 var targetUUID = toId + targetAnchors[j];

147                 jsPlumb.addEndpoint(toId, this.targetEndpoint, { anchor:targetAnchors[j], uuid:targetUUID });                        

148             }

149         }

150     };

151 })();

152 //////////////////////////////////////////////////////////////////////////////

153 // 这里将所有用到的数据结构汇聚在这里, 避免修改时需要在代码中穿行, 浪费时间

154 /**

155  * 重新刷新VM关联实体时需要使用到VM的信息,这里进行全局缓存,避免重复查询

156  * 重新刷新VM关联实体时VM必定存在, vmNodeData 也必定是最近一次查询的结果

157  */

158 var vmNodeData = {};

159 /**

160  * 重新刷新磁盘关联快照实体时需要使用到磁盘的信息,这里进行全局缓存,避免重复查询

161  * 重新刷新磁盘关联快照实体时磁盘必定存在,且必定是最近一次查询的结果

162  * eg. {'instanceId': { "ecsInstanceId": "vmName", "houyiDiskId": "102-80012003",

163                         "aliUid": aliUidNum,  "instanceId": "d-28ilj8rsf", ... }}

164  */

165 var deviceNodeDataMapping = {};

166 /**

167  * 拓扑图中的节点类型

168  */

169 var nodeTypeArray = ['VM', 'DEVICE', 'NC', 'VIP', 'SNAPSHOT', 'CLUSTER', 'AVZ', 'ACCOUNT'];

170 var VM_TYPE = nodeTypeArray[0];

171 var DEVICE_TYPE = nodeTypeArray[1];

172 var NC_TYPE = nodeTypeArray[2];

173 var VIP_TYPE = nodeTypeArray[3];

174 var SNAPSHOT_TYPE= nodeTypeArray[4];

175 var CLUSTER_TYPE= nodeTypeArray[5];

176 var AVZ_TYPE= nodeTypeArray[6];

177 var ACCOUNT_TYPE= nodeTypeArray[7];

178 /**

179  * cacheConnectionData 节点之间的已有连接数目缓存, 在计算节点位置及布局方法 computeLayout 中用到

180  * eg. {

181  *    'VM-DEVICE-vmkey': 2,  'VM-NC-vmkey':1,  'VM-VIP-vmkey':2 , 'VM-ACCOUNT-vmkey': 1,

182  * } 

183  * 表示已经有2磁盘/1NC/2VIP/1ACCOUNT 与VM(key 为 vmkey)连接, 这些信息用于计算与VM相连接的同类型的下一个实体的相对位置

184  */

185 var cacheConnectionData = {};

186 /**

187  * 连接关系的存储, 在 cacheConnections , reconnectAll 方法中用到

188  * 由于重复节点会移动到新的位置,原有连接会出现断连现象, 因此采用"每次刷新拉取新实体时重连所有连线" 的策略, 可以保证实时性, 只要连接数不多重复连接的开销是可以接受的.

189  * connections = [[startPoint1, endPoint1], [startPoint2, endPoint2], ..., [startPointN, endPointN]];

190  */

191 var connections = [];

192 /**

193  * 实体与实体上附着点方向的设置

194  * DEVICE_TYPE: [['Right', 'Bottom'], ['Left']] 的含义是:

195  * 对于DEVICE实体: 作为起始节点时, 附着点可以在右方中点(连接CLUSTER), 下方中点(连接快照); 作为终止节点时, 附着点仅在左方中点(连接VM) 

196  */

197 var entityEndPointsMapping = {

198     "VM": [['Top', 'Bottom', 'Right', 'Left'], []],

199     "DEVICE": [['Right', 'Bottom'], ['Left']],

200     "NC": [['Bottom'], ['Top']],

201     "VIP": [[], ['Right']],

202     "SNAPSHOT": [[], ['Top']],

203     "CLUSTER": [[], ['Left', 'Top']],

204     "AVZ": [['Bottom'], ['Top']],

205     "ACCOUNT": [[], ['Bottom']]

206 };

207 /**

208  * 连接线附着点方向设置

209  * "VM-ACCOUNT": ['Top', 'Bottom'] 的含义是:

210  * VM 的上方附着点 与 ACCOUNT 的下方附着点的连接

211  */

212 var connectionDirectionMapping = {

213     "VM-ACCOUNT": ['Top', 'Bottom'],

214     "VM-NC": ['Bottom', 'Top'],

215     "NC-CLUSTER": ['Bottom', 'Top'],

216     "VM-DEVICE": ['Right', 'Left'],

217     "DEVICE-CLUSTER": ['Right', 'Left'],

218     "VM-VIP": ['Left', 'Right'],

219     "DEVICE-SNAPSHOT": ['Bottom', 'Top']

220 }

221 /**

222  * 节点之间的水平与垂直相对位置

223  */

224 var largeVerticalDistance = 270;

225 var verticalDistance = 220;

226 var horizontalDistance = 300;

227 var shortVerticalDistance = 50;

228 var shortHorizontalDistance = 220;

229 /**

230  * 节点之间的水平或垂直相对位置和距离的设置

231  * "VM-DEVICE": [largeVerticalDistance, horizontalDistance] 

232  */

233 var connectionDistanceMapping = {

234     "VM-ACCOUNT": [-verticalDistance, 0],

235     "VM-NC": [shortVerticalDistance, 0],

236     "NC-CLUSTER": [shortVerticalDistance, 0],

237     "VM-DEVICE": [largeVerticalDistance, horizontalDistance],

238     "DEVICE-CLUSTER": [-108, horizontalDistance],

239     "VM-VIP": [verticalDistance, -horizontalDistance],

240     "DEVICE-SNAPSHOT": [shortVerticalDistance, shortHorizontalDistance]

241 }

242 /**

243  * 根节点位置

244  */

245 rootPosition = [220, 360];

246 rootTop = rootPosition[0];

247 rootLeft = rootPosition[1];

248 var parentDiv = null;

249 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

250 function drawtopo_asyn(vmName, regionNo, parentDivId) {

251     

252     parentDiv = $('#'+parentDivId);

253     

254     var vmInfoReq = {

255         'url':     httpPrefix + '/controllers/vm/obtainVmData',

256         'params' : {

257             'vm_name' : vmName,

258             'region_no': regionNo

259         }

260     };

261     var vmjq = doAjaxRequest(vmInfoReq);

262     var vmInfoLoadedAfter = function(resultJson) {

263         

264         vmNodeData = resultJson.result.data;

265         if (vmNodeData == null) {

266             alert('没有找到VM的相关信息!');

267             return ;

268         }

269         vmNode = createDiv(vmNodeData);

270         

271         vmDivId = obtainNodeDivId(vmNodeData);

272         $('#'+vmDivId).css('top', rootTop + 'px');

273         $('#'+vmDivId).css('left', rootLeft + 'px');

274         

275         linkAccount(vmNodeData, vmName, regionNo);

276         

277         ncId = vmNodeData.data.nc_id;

278         linkNc(vmNodeData, ncId, regionNo);

279         

280         // vmName = 'ATX-28n2dhdq8';

281         linkDevices(vmNodeData, vmName);

282         

283         vmInnerIp = vmNodeData.data.vm_inner_ip;

284         linkVips(vmNodeData, vmInnerIp);

285         

286     };

287     vmjq.done(vmInfoLoadedAfter);

288 }

289 function linkAccount(vmNodeData, vmName, regionNo) {

290     var accountInfoReq = {

291         'url': httpPrefix + '/controllers/vm/obtainAliyunAccountInfo',

292         'params': {

293             'vm_name' : vmName,

294             'region_no': regionNo

295         }

296     };

297     var accountjq = doAjaxRequest(accountInfoReq);

298     accountjq.done(function(resultJson) {

299         

300         // for test

301         // resultJson = {"result":{"msg":"successful","code":200,"data":{"errorCode":null,"errorMsg":null,"aliyunID":"[email protected]","kp":null},"success":true}};

302         

303         if (resultJson.result.success) {

304             accountData = resultJson.result.data;

305             accountNodeData = createAccountData(accountData);

306             accountNode = createDiv(accountNodeData);

307             cacheConnections(vmNodeData, accountNodeData, obtainConnectionDirections(vmNodeData, accountNodeData));

308             reconnectAll(connections);

309         }

310         else {

311             $('#error').append('获取关联云账号信息失败!');

312         }

313     }).fail(function() {

314         $('#error').append('获取关联云账号信息失败! ');

315     });

316     

317 }

318 function linkNc(vmNodeData, ncId, regionNo) {

319     var ncInfoReq = {

320         'url':     httpPrefix + '/controllers/nc/listNc',

321         'params': {

322             'region_no': regionNo,

323             'nc_id': ncId,

324             'start': 0,

325             'page': 1,

326             'limit': 1

327         }

328     };

329     var ncjq = doAjaxRequest(ncInfoReq);

330     ncjq.done(function(resultJson) {

331         ncDataList = resultJson.data;

332         if (ncDataList.length > 0) {

333             ncData = ncDataList[0];

334             ncNodeData = createNcData(ncData);

335             ncNode = createDiv(ncNodeData);

336             cacheConnections(vmNodeData, ncNodeData, obtainConnectionDirections(vmNodeData, ncNodeData));

337             

338             ncClusterNodeData = createNcClusterData(ncData);

339             ncClusterNode = createDiv(ncClusterNodeData);

340             cacheConnections(ncNodeData, ncClusterNodeData, obtainConnectionDirections(ncNodeData, ncClusterNodeData));

341             reconnectAll(connections);

342         }

343         else {

344             $('#error').append('获取关联NC实体失败!');

345         }

346     }).fail(function() {

347         $('#error').append('获取关联NC实体失败!');

348     });

349 }

350 function linkDevices(vmNodeData, vmName) {

351     var deviceInfoReq = {

352         'url' :  httpPrefix + '/controllers/disk/search',

353         'params': {

354             'vmName': vmName

355         }

356     }

357     var regionPeNickName = vmNodeData.data.region_pe_nickname;

358     var devicejq = doAjaxRequest(deviceInfoReq);

359     devicejq.done(function(resultJson) {

360         

361         total = resultJson.data.total;

362         if (total > 0) {

363             devices = resultJson.data.list;

364             

365             for (var i=0; i<total; i++) {

366                 

367                 deviceData = devices[i];

368                 deviceData['regionPeNickName'] = regionPeNickName;

369                 deviceNodeData = createDeviceData(deviceData);

370                 deviceNodeDataMapping[deviceData.instanceId] = deviceNodeData;

371                 deviceNode = createDiv(deviceNodeData);

372                 cacheConnections(vmNodeData, deviceNodeData, obtainConnectionDirections(vmNodeData, deviceNodeData));

373                 

374                 deviceClusterNodeData = createDeviceClusterData(deviceData);

375                 deviceClusterNode = createDiv(deviceClusterNodeData);

376                 cacheConnections(deviceNodeData, deviceClusterNodeData, obtainConnectionDirections(deviceNodeData,deviceClusterNodeData));

377                 linkSnapshot(devices[i].aliUid, devices[i].instanceId, deviceNodeData);

378             }

379             reconnectAll(connections);

380         }

381         else {

382             $('#error').append('该VM没有关联的磁盘实体!');

383         }

384     }).fail(function() {

385         $('#error').append('获取关联磁盘实体失败!');

386     });

387 }

388 function linkVips(vmNodeData, vmInnerIp) {

389     

390     var vipInfoReq = {

391         'url': httpPrefix + '/controllers/slbvip/listVip',

392         'params': {

393             'realserver_param': vmInnerIp,

394             'start': 0,

395             'page': 1,

396             'limit': 100

397         }

398     };

399     var vipjq = doAjaxRequest(vipInfoReq);

400     vipjq.done(function(resultJson) {

401         

402         total = resultJson.total;

403         vips = resultJson.data;

404         if (total > 0) {

405             for (j=0; j<total; j++) {

406                 var vipInfo = vips[j];

407                 vipNodeData = createVipData(vipInfo);

408                 vipNode = createDiv(vipNodeData);

409                 cacheConnections(vmNodeData, vipNodeData, obtainConnectionDirections(vmNodeData,vipNodeData));

410             }

411             reconnectAll(connections);

412         }

413         else {

414             $('#error').append('该VM没有关联的VIP实体!');

415         }

416     }).fail(function() {

417         $('#error').append('获取关联VIP实体失败!');

418     });

419     

420 }

421 function linkSnapshot(aliUid, diskId, deviceNodeData) {

422     

423     var snapshotInfoReq = {

424         'url': httpPrefix + '/controllers/snapshot/search',

425         'params': {

426             'aliUid': aliUid,

427             'diskId': diskId

428         }

429     };

430     var snapshotjq = doAjaxRequest(snapshotInfoReq);

431     snapshotjq.done(function(resultJson) {

432         

433         total = resultJson.total;

434         if (total > 0) {

435             snapshotDataList = resultJson.list;

436             for (k=0; k<total; k++) {

437                 snapshotData = snapshotDataList[k];

438                 snapshotNodeData = createSnapshotData(snapshotData);

439                 snapshotNode = createDiv(snapshotNodeData);

440                 cacheConnections(deviceNodeData, snapshotNodeData, obtainConnectionDirections(deviceNodeData, snapshotNodeData));

441             }

442             reconnectAll(connections);

443         }

444         else {

445             $('#error').append('磁盘 ' + diskId + ' 没有关联的快照实体!');

446         }

447     }).fail(function() {

448         $('#error').append('磁盘' + diskId + ' 获取关联快照实体失败!');

449     });

450 }

451 /**

452  * createXXXData

453  * 创建拓扑图所使用的节点数据  

454  */

455 function createVmData(vmData) {

456     return {

457         'type': 'VM',

458         'key': vmData.vm_name,

459         'data': vmData

460     }

461 }

462 function createNcData(ncData) {

463     return {

464         'type': 'NC',

465         'key': ncData.ip,

466         'data': ncData

467     }

468 }

469 function createNcClusterData(ncData) {

470     return {

471         'type': 'CLUSTER',

472         'key': ncData.regionPeNickName,

473         'data': {

474             'regionNo': ncData.regionNo,

475             'regionNickName': ncData.regionNickName,

476             'regionPeNickName': ncData.regionPeNickName

477         }

478     }

479 }

480 function createDeviceData(deviceData) {

481     return {

482         'type': 'DEVICE',

483         'key': deviceData.instanceId,

484         'data': deviceData

485     }

486 }

487 function createDeviceClusterData(deviceData) {

488     return {

489         'type': 'CLUSTER',

490         'key': deviceData.regionNo,

491         'data': {

492             'regionNo': deviceData.regionNo

493         }

494     }

495 }

496 function createSnapshotData(snapshotData) {

497     return {

498         'type': 'SNAPSHOT',

499         'key': snapshotData.snapshotId,

500         'data': snapshotData 

501     }

502 }

503 function createSnapshotClusterData(snapshotData) {

504     return {

505         'type': 'CLUSTER',

506         'key': snapshotData.regionNo,

507         'data': {

508             'regionNo': snapshotData.regionNo

509         }

510     }

511 }

512 function createVipData(vipData) {

513     return {

514         'type': 'VIP',

515         'key': vipData.vipAddress,

516         'data': vipData

517     }

518 }

519 function createAccountData(accountData) {

520     return {

521         'type': 'ACCOUNT',

522         'key': accountData.aliyunID,

523         'data': accountData

524     }

525 }

526 /**

527  * 缓存起始节点 beginNode 和终止节点 endNode 的连接关系 

528  */

529 function cacheConnections(beginNode, endNode, directions) {

530     

531     computeLayout(beginNode, endNode);

532     

533     var startPoint = obtainNodeDivId(beginNode) + directions[0];

534     var endPoint = obtainNodeDivId(endNode) + directions[1];

535     connections.push([startPoint, endPoint]);

536 }

537 /**

538  * 计算节点位置及布局

539  */

540 function computeLayout(beginNode, endNode) {

541     

542     var beginDivId = obtainNodeDivId(beginNode);

543     var endDivId = obtainNodeDivId(endNode);

544     var beginNodeType = beginNode.type;

545     var endNodeType = endNode.type;

546     

547     beginNodeTop = $('#'+beginDivId).offset().top;

548     beginNodeLeft = $('#'+beginDivId).offset().left;

549     

550     var key = beginNodeType + '-' + endNodeType + '-' + beginNode.key;

551     if (cacheConnectionData[key] == null) {

552         cacheConnectionData[key] = -1;

553     }

554     else {

555         cacheConnectionData[key] = cacheConnectionData[key]+1;

556     }

557     connNum = cacheConnectionData[key];

558     

559     var typeKey = beginNodeType + '-' + endNodeType;

560     var relDistance = connectionDistanceMapping[typeKey];

561     var relVertiDistance = relDistance[0];

562     var relHoriDistance = relDistance[1];

563     

564     switch (beginNodeType) {

565         case VM_TYPE:

566             if (endNodeType == VIP_TYPE) {

567                 endNodePosition = [beginNodeTop+connNum*relVertiDistance, beginNodeLeft+relHoriDistance];

568             }

569             else if (endNodeType == DEVICE_TYPE) {

570                 endNodePosition = [beginNodeTop+connNum*relVertiDistance, beginNodeLeft+relHoriDistance];

571             }

572             else if (endNodeType == NC_TYPE) {

573                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+relHoriDistance];

574             }

575             else if (endNodeType == ACCOUNT_TYPE) {

576                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+relHoriDistance];

577             }

578             break;

579         case DEVICE_TYPE:

580             if (endNodeType == CLUSTER_TYPE) {

581                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+relHoriDistance];

582             }

583             else if (endNodeType == SNAPSHOT_TYPE) {

584                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+(connNum+1)*relHoriDistance];

585             }

586             break;

587         case VIP_TYPE:

588             break;

589         case NC_TYPE:

590             if (endNodeType == CLUSTER_TYPE) {

591                 endNodePosition = [beginNodeTop+relVertiDistance, beginNodeLeft+relHoriDistance];

592             }

593             break;

594         case SNAPSHOT_TYPE:

595         default: 

596             break;

597     }

598     

599     $('#'+endDivId).css('top', endNodePosition[0] + 'px');

600     $('#'+endDivId).css('left', endNodePosition[1] + 'px');

601     

602     addEndPoints(beginDivId, beginNodeType);

603     addEndPoints(endDivId, endNodeType);

604 }

605 /**

606  * 为节点添加用于连线的附着点

607  * @param nodeDivId  节点的 DIV ID

608  * @param type  节点类型

609  */

610 function addEndPoints(nodeDivId, type) {

611     var startAttachedPoints = entityEndPointsMapping[type][0];

612     var endAttachedPoints = entityEndPointsMapping[type][1];

613     topoDrawUtil.addEndpoints(nodeDivId, startAttachedPoints, endAttachedPoints);

614 }

615 /**

616  * 连接所有连线

617  */

618 function reconnectAll(connections) {

619     

620     var i=0;

621     for (i=0; i<connections.length; i++) {

622         jsPlumb.connect({uuids:connections[i], editable: false}); 

623     }

624     // 使所有拓扑节点均为可拉拽的                    

625     jsPlumb.draggable(jsPlumb.getSelector(".node"), { grid: [5, 5] });

626 }

627 /**

628  * div id cache , avoid duplicated div.

629  * {'divId': 'divStr'}

630  */

631 divIdCache = {};

632 /**

633  * 为节点数据创建节点\附着点并返回节点的DIV

634  */

635 function createDiv(metaNode) {

636     var clickUrl = '';

637     var display = '';

638     var type = metaNode.type;

639     var regionPeNickname = '';

640     if (metaNode.data != null) {

641         regionPeNickname = metaNode.data.regionPeNickName;

642     }

643     

644     nodeDivId = obtainNodeDivId(metaNode);

645     

646     if (divIdCache[nodeDivId] != null) {

647         // 该节点要移动到新的位置, 因此原来的附着点要去掉

648         topoDrawUtil.removeAllEndPoints(nodeDivId);

649         return divIdCache[nodeDivId];

650     }

651     

652     switch(type.toUpperCase()) {

653         case VM_TYPE:

654             clickUrl = httpPrefix + '/framehtml/vm_monitor.html?vm_name=' + metaNode.key + '&data='+JSON.stringify(metaNode.data).replace(/\"/g,"'");

655             display = metaNode.key;

656             break;

657         case DEVICE_TYPE:

658             displayDevice1 = metaNode.data.instanceId;

659             clickDeviceUrl2 = httpPrefix + '/framehtml/device_monitor.html?device_id=' + metaNode.data.houyiDiskId + '®ion_pe_nickname='+regionPeNickname;

660             displayDevice2 = metaNode.data.houyiDiskId;

661             break;

662         case NC_TYPE:

663             var regionNo = metaNode.data.regionNo;

664             clickUrl = httpPrefix + '/framehtml/nc_monitor.html?nc_ip=' + metaNode.key + '®ion_pe_nickname='+regionPeNickname + '®ion_no='+regionNo;

665             display = metaNode.key;

666             break;

667         case VIP_TYPE:

668             display = metaNode.key + ':' + metaNode.data.port;

669             clickUrl = httpPrefix + '/framehtml/vip_monitor.html?vip=' + display + '®ion_pe_nickname='+regionPeNickname + '&slbdb_configId='+metaNode.data.slbdb_configId;

670             break;

671         case SNAPSHOT_TYPE:

672             display = metaNode.key + '<br/>' + metaNode.data.houyiSnapshotId + '<br/><span style="color:green">'+ metaNode.data.regionNo + '</span>';

673             break;

674         case CLUSTER_TYPE:

675         case AVZ_TYPE:

676         case ACCOUNT_TYPE:

677             display = metaNode.key;

678             break;

679         default:

680             break;

681     } 

682     

683     if (type == VM_TYPE || type == NC_TYPE || type == VIP_TYPE || type == ACCOUNT_TYPE ) {

684         divStr =  '<div class="node biggerNode" id="' + nodeDivId + '"><strong>' 

685                 + metaNode.type + '<br/><a href="' + clickUrl + '" target="_blank">' + display + '</a><br/></strong></div>';

686     }

687     else if (type == DEVICE_TYPE){

688         divStr =  '<div class="node biggerNode" id="' + nodeDivId + '"><strong>' 

689         + metaNode.type + '<br/>' + displayDevice1 + '<br/><a href="' + clickDeviceUrl2 + '" target="_blank">' + displayDevice2 + '</a><br/></strong></div>';

690     }

691     else {

692         divStr = '<div class="node biggerNode" id="' + nodeDivId + '"><strong>' 

693                 + metaNode.type + '<br/>' + display + '<br/></strong></div>';

694     }

695     parentDiv.append(divStr);

696     

697     divIdCache[nodeDivId] = divStr;

698     return divStr;

699 }

700 function obtainConnectionDirections(srcNodeData, destNodeData) {

701     var key = srcNodeData.type + '-' + destNodeData.type;

702     var startDirection = connectionDirectionMapping[key][0];

703     var endDirection = connectionDirectionMapping[key][1];

704     return [startDirection, endDirection];

705 }

706 /**

707  * 生成节点的 DIV id

708  * divId = nodeType.toUpperCase + "_" + key

709  * key 可能为 IP , 其中的 . 将被替换成 ZZZ , 因为 jquery id 选择器中 . 属于转义字符.

710  * eg. {type: 'VM', key: '1.1.1.1' }, divId = 'VM_1ZZZ1ZZZ1ZZZ1'

711  */

712 function obtainNodeDivId(metaNode) {

713     if (metaNode.type == VIP_TYPE) {

714         return metaNode.type.toUpperCase() + '_' + transferKey(metaNode.key) + '_' + metaNode.data.port;

715     }

716     return metaNode.type.toUpperCase() + '_' + transferKey(metaNode.key);

717 }

718 function transferKey(key) {

719     return key.replace(/\./g, 'ZZZ').replace(/\@/g,'YYY');

720 }

721 function revTransferKey(value) {

722     return value.replace(/ZZZ/g, '.').replace('/YYY/g','@');

723 }  

 

你可能感兴趣的:(异步加载)