diff --git a/atms-web/src/main/webapp/Content/orgChart/jquery.orgchart.css b/atms-web/src/main/webapp/Content/orgChart/jquery.orgchart.css index f695cd1223f66fbd2a6f8fdc376dc981168d2c46..ddd3dd0db51615e131fb38fe0496b7626087b1e8 100644 --- a/atms-web/src/main/webapp/Content/orgChart/jquery.orgchart.css +++ b/atms-web/src/main/webapp/Content/orgChart/jquery.orgchart.css @@ -2,22 +2,15 @@ * jQuery OrgChart Plugin * https://github.com/dabeng/OrgChart * - * Demos of jQuery OrgChart Plugin - * http://dabeng.github.io/OrgChart/local-datasource/ - * http://dabeng.github.io/OrgChart/ajax-datasource/ - * http://dabeng.github.io/OrgChart/ondemand-loading-data/ - * http://dabeng.github.io/OrgChart/option-createNode/ - * http://dabeng.github.io/OrgChart/export-orgchart/ - * http://dabeng.github.io/OrgChart/integrate-map/ - * * Copyright 2016, dabeng - * http://dabeng.github.io/ + * https://github.com/dabeng * * Licensed under the MIT license: * http://www.opensource.org/licenses/MIT */ .orgchart { + box-sizing: border-box; display: inline-block; min-height: 202px; min-width: 202px; @@ -27,45 +20,29 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - /*background-image: linear-gradient(90deg, rgba(200, 0, 0, 0.15) 10%, rgba(0, 0, 0, 0) 10%), linear-gradient(rgba(200, 0, 0, 0.15) 10%, rgba(0, 0, 0, 0) 10%);*/ - background-color:rgba(234, 237, 238, 0.39); + background-image: linear-gradient(90deg, rgba(200, 0, 0, 0.15) 10%, rgba(0, 0, 0, 0) 10%), linear-gradient(rgba(200, 0, 0, 0.15) 10%, rgba(0, 0, 0, 0) 10%); background-size: 10px 10px; border: 1px dashed rgba(0,0,0,0); padding: 20px; } .orgchart .hidden, .orgchart~.hidden { - display: none!important; -} - -.orgchart *, .orgchart *:before, .orgchart *:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; + display: none; } .orgchart.b2t { transform: rotate(180deg); - -ms-transform: rotate(180deg); - -moz-transform: rotate(180deg); - -webkit-transform: rotate(180deg); } .orgchart.l2r { position: absolute; transform: rotate(-90deg) rotateY(180deg); - -ms-transform: rotate(-90deg) rotateY(180deg); - -moz-transform: rotate(-90deg) rotateY(180deg); - -webkit-transform: rotate(-90deg) rotateY(180deg); transform-origin: left top; - -ms-transform-origin: left top; - -moz-transform-origin: left top; - -webkit-transform-origin: left top; } .orgchart .verticalNodes ul { list-style: none; - margin:0px; + margin: 0; padding-left: 18px; text-align: left; } @@ -77,6 +54,7 @@ border: 1px solid rgba(217, 83, 79, 0.8); } .orgchart .verticalNodes>td>ul>li:first-child::before { + box-sizing: border-box; top: -4px; height: 30px; width: calc(50% - 2px); @@ -87,6 +65,7 @@ } .orgchart .verticalNodes ul>li::before, .orgchart .verticalNodes ul>li::after { + box-sizing: border-box; content: ''; position: absolute; left: -6px; @@ -104,24 +83,20 @@ height: 100%; } .orgchart .verticalNodes ul>li:first-child::after { + box-sizing: border-box; top: 24px; width: 11px; border-width: 2px 0 0 2px; } .orgchart .verticalNodes ul>li:last-child::after { + box-sizing: border-box; border-width: 2px 0 0; } .orgchart.r2l { position: absolute; transform: rotate(90deg); - -ms-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -webkit-transform: rotate(90deg); transform-origin: left top; - -ms-transform-origin: left top; - -moz-transform-origin: left top; - -webkit-transform-origin: left top; } .orgchart>.spinner { @@ -131,8 +106,8 @@ } .orgchart table { - border-spacing: 0!important; - border-collapse: separate!important; + border-spacing: 0; + border-collapse: separate; } .orgchart>table:first-child{ @@ -145,25 +120,30 @@ padding: 0; } -.orgchart tr.lines td.topLine { +.orgchart .lines:nth-child(3) td { + box-sizing: border-box; + height: 20px; +} + +.orgchart .lines .topLine { border-top: 2px solid rgba(217, 83, 79, 0.8); } -.orgchart tr.lines td.rightLine { +.orgchart .lines .rightLine { border-right: 1px solid rgba(217, 83, 79, 0.8); float: none; - border-radius: 0px; + border-radius: 0; } -.orgchart tr.lines td.leftLine { +.orgchart .lines .leftLine { border-left: 1px solid rgba(217, 83, 79, 0.8); float: none; - border-radius: 0px; + border-radius: 0; } -.orgchart tr.lines .downLine { +.orgchart .lines .downLine { background-color: rgba(217, 83, 79, 0.8); - margin: 0px auto; + margin: 0 auto; height: 20px; width: 2px; float: none; @@ -171,6 +151,7 @@ /* node styling */ .orgchart .node { + box-sizing: border-box; display: inline-block; position: relative; margin: 0; @@ -211,7 +192,7 @@ top: -10000px; } -.orgchart .ghost-node>* { +.orgchart .ghost-node rect { fill: #ffffff; stroke: #bf0000; } @@ -231,41 +212,23 @@ white-space: nowrap; background-color: rgba(217, 83, 79, 0.8); color: #fff; - border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; } .orgchart.b2t .node .title { transform: rotate(-180deg); - -ms-transform: rotate(-180deg); - -moz-transform: rotate(-180deg); - -webkit-transform: rotate(-180deg); transform-origin: center bottom; - -ms-transform-origin: center bottom; - -moz-transform-origin: center bottom; - -webkit-transform-origin: center bottom; } .orgchart.l2r .node .title { transform: rotate(-90deg) translate(-40px, -40px) rotateY(180deg); - -ms-transform: rotate(-90deg) translate(-40px, -40px) rotateY(180deg); - -moz-transform: rotate(-90deg) translate(-40px, -40px) rotateY(180deg); - -webkit-transform: rotate(-90deg) translate(-40px, -40px) rotateY(180deg); transform-origin: bottom center; - -ms-transform-origin: bottom center; - -moz-transform-origin: bottom center; - -webkit-transform-origin: bottom center; width: 120px; } .orgchart.r2l .node .title { transform: rotate(-90deg) translate(-40px, -40px); - -ms-transform: rotate(-90deg) translate(-40px, -40px); - -moz-transform: rotate(-90deg) translate(-40px, -40px); - -webkit-transform: rotate(-90deg) translate(-40px, -40px); transform-origin: bottom center; - -ms-transform-origin: bottom center; - -moz-transform-origin: bottom center; - -webkit-transform-origin: bottom center; width: 120px; } @@ -276,6 +239,7 @@ } .orgchart .node .content { + box-sizing: border-box; width: 100%; height: 20px; font-size: 11px; @@ -292,36 +256,18 @@ .orgchart.b2t .node .content { transform: rotate(180deg); - -ms-transform: rotate(180deg); - -moz-transform: rotate(180deg); - -webkit-transform: rotate(180deg); transform-origin: center top; - -ms-transform-origin: center top; - -moz-transform-origin: center top; - -webkit-transform-origin: center top; } .orgchart.l2r .node .content { transform: rotate(-90deg) translate(-40px, -40px) rotateY(180deg); - -ms-transform: rotate(-90deg) translate(-40px, -40px) rotateY(180deg); - -moz-transform: rotate(-90deg) translate(-40px, -40px) rotateY(180deg); - -webkit-transform: rotate(-90deg) translate(-40px, -40px) rotateY(180deg); transform-origin: top center; - -ms-transform-origin: top center; - -moz-transform-origin: top center; - -webkit-transform-origin: top center; width: 120px; } .orgchart.r2l .node .content { transform: rotate(-90deg) translate(-40px, -40px); - -ms-transform: rotate(-90deg) translate(-40px, -40px); - -moz-transform: rotate(-90deg) translate(-40px, -40px); - -webkit-transform: rotate(-90deg) translate(-40px, -40px); transform-origin: top center; - -ms-transform-origin: top center; - -moz-transform-origin: top center; - -webkit-transform-origin: top center; width: 120px; } @@ -331,7 +277,6 @@ color: rgba(68, 157, 68, 0.5); cursor: default; transition: .2s; - -webkit-transition: .2s; } .orgchart.noncollapsable .node .edge { @@ -377,8 +322,6 @@ .orgchart .node .horizontalEdge::before { position: absolute; top: calc(50% - 7px); - top: -webkit-calc(50% - 7px); - top: -moz-calc(50% - 7px); } .orgchart .node .rightEdge::before { @@ -413,39 +356,33 @@ text-align: center; white-space: nowrap; vertical-align: middle; - -ms-touch-action: manipulation; touch-action: manipulation; cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; color: #fff; - background-color: #ca5931; + background-color: #5cb85c; border: 1px solid transparent; - border-color:#ca5931 ; + border-color: #4cae4c; border-radius: 4px; } .oc-export-btn[disabled] { cursor: not-allowed; - filter: alpha(opacity=30); - -webkit-box-shadow: none; box-shadow: none; opacity: 0.3; } .oc-export-btn:hover,.oc-export-btn:focus,.oc-export-btn:active { - background-color: #ca5931; - border-color: #ca5931; + background-color: #449d44; + border-color: #347a34; } .orgchart~.mask { position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; + top: 0; + right: 0; + bottom: 0; + left: 0; z-index: 999; text-align: center; background-color: rgba(0,0,0,0.3); @@ -460,45 +397,41 @@ } .orgchart .node { - transition: all 0.3s; - webkit-transition: all 0.3s; - opacity: 1; - top: 0; - left: 0; + transition: transform 0.3s, opacity 0.3s; } .orgchart .slide-down { opacity: 0; - top: 40px; + transform: translateY(40px); } .orgchart.l2r .node.slide-down, .orgchart.r2l .node.slide-down { - top: 130px; + transform: translateY(130px); } .orgchart .slide-up { opacity: 0; - top: -40px; + transform: translateY(-40px); } .orgchart.l2r .node.slide-up, .orgchart.r2l .node.slide-up { - top: -130px; + transform: translateY(-130px); } .orgchart .slide-right { opacity: 0; - left: 130px; + transform: translateX(130px); } .orgchart.l2r .node.slide-right, .orgchart.r2l .node.slide-right { - left: 40px; + transform: translateX(40px); } .orgchart .slide-left { opacity: 0; - left: -130px; + transform: translateX(-130px); } .orgchart.l2r .node.slide-left, .orgchart.r2l .node.slide-left { - left: -40px; + transform: translateX(-40px); } \ No newline at end of file diff --git a/atms-web/src/main/webapp/Scripts/orgChart/jquery.orgchart.js b/atms-web/src/main/webapp/Scripts/orgChart/jquery.orgchart.js index 87e2ae9d8a206b4ce0a60e1c0ff0e352d2270c71..332f4d072fdb5b3a5b060a33c839ed7af03ff10e 100644 --- a/atms-web/src/main/webapp/Scripts/orgChart/jquery.orgchart.js +++ b/atms-web/src/main/webapp/Scripts/orgChart/jquery.orgchart.js @@ -2,11 +2,8 @@ * jQuery OrgChart Plugin * https://github.com/dabeng/OrgChart * - * Demos of jQuery OrgChart Plugin - * http://dabeng.github.io/OrgChart/ - * * Copyright 2016, dabeng - * http://dabeng.github.io/ + * https://github.com/dabeng * * Licensed under the MIT license: * http://www.opensource.org/licenses/MIT @@ -20,12 +17,14 @@ factory(jQuery, window, document); } }(function ($, window, document, undefined) { - $.fn.orgchart = function (options) { - var defaultOptions = { + var OrgChart = function (elem, opts) { + this.$chartContainer = $(elem); + this.opts = opts; + this.defaultOptions = { 'nodeTitle': 'name', 'nodeId': 'id', 'toggleSiblingsResp': false, - 'depth': 999, + 'visibleLevel': 999, 'chartClass': '', 'exportButton': false, 'exportFilename': 'OrgChart', @@ -38,1381 +37,1403 @@ 'zoominLimit': 7, 'zoomoutLimit': 0.5 }; - - switch (options) { - case 'buildHierarchy': - return buildHierarchy.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'addChildren': - return addChildren.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'addParent': - return addParent.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'addSiblings': - return addSiblings.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'removeNodes': - return removeNodes.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'getHierarchy': - return getHierarchy.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'hideParent': - return hideParent.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'showParent': - return showParent.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'hideChildren': - return hideChildren.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'showChildren': - return showChildren.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'hideSiblings': - return hideSiblings.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'showSiblings': - return showSiblings.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'getNodeState': - return getNodeState.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'getRelatedNodes': - return getRelatedNodes.apply(this, Array.prototype.splice.call(arguments, 1)); - case 'setChartScale': - return setChartScale.apply(this, Array.prototype.splice.call(arguments, 1)); - default: // initiation time - var opts = $.extend(defaultOptions, options); - } - - // build the org-chart - var $chartContainer = this; - var data = opts.data; - var $chart = $('<div>', { - 'data': { 'options': opts }, - 'class': 'orgchart' + (opts.chartClass !== '' ? ' ' + opts.chartClass : '') + (opts.direction !== 't2b' ? ' ' + opts.direction : ''), - 'click': function (event) { - if (!$(event.target).closest('.node').length) { - $chart.find('.node.focused').removeClass('focused'); - } + }; + // + OrgChart.prototype = { + // + init: function (opts) { + var that = this; + this.options = $.extend({}, this.defaultOptions, this.opts, opts); + // build the org-chart + var $chartContainer = this.$chartContainer; + if (this.$chart) { + this.$chart.remove(); } - }); - if ($.type(data) === 'object') { - if (data instanceof $) { // ul datasource - buildHierarchy($chart, buildJsonDS(data.children()), 0, opts); - } else { // local json datasource - buildHierarchy($chart, opts.ajaxURL ? data : attachRel(data, '00'), 0, opts); - } - } else { - $.ajax({ - 'url': data, - 'dataType': 'json', - 'beforeSend': function () { - $chart.append('<i class="fa fa-circle-o-notch fa-spin spinner"></i>'); + var data = this.options.data; + var $chart = this.$chart = $('<div>', { + 'data': {'options': this.options}, + 'class': 'orgchart' + (this.options.chartClass !== '' ? ' ' + this.options.chartClass : '') + (this.options.direction !== 't2b' ? ' ' + this.options.direction : ''), + 'click': function (event) { + if (!$(event.target).closest('.node').length) { + $chart.find('.node.focused').removeClass('focused'); + } } - }) - .done(function (data, textStatus, jqXHR) { - buildHierarchy($chart, opts.ajaxURL ? data : attachRel(data, '00'), 0, opts); - }) - .fail(function (jqXHR, textStatus, errorThrown) { - console.log(errorThrown); - }) - .always(function () { - $chart.children('.spinner').remove(); }); - } - $chartContainer.append($chart); + if (typeof MutationObserver !== 'undefined') { + this.triggerInitEvent(); + } + if ($.type(data) === 'object') { + if (data instanceof $) { // ul datasource + this.buildHierarchy($chart, this.buildJsonDS(data.children()), 0, this.options); + } else { // local json datasource + this.buildHierarchy($chart, this.options.ajaxURL ? data : this.attachRel(data, '00')); + } + } else { + $chart.append('<i class="fa fa-circle-o-notch fa-spin spinner"></i>'); + $.ajax({ + 'url': data, + 'dataType': 'json' + }) + .done(function (data, textStatus, jqXHR) { + that.buildHierarchy($chart, that.options.ajaxURL ? data : that.attachRel(data, '00'), 0, that.options); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + console.log(errorThrown); + }) + .always(function () { + $chart.children('.spinner').remove(); + }); + } + $chartContainer.append($chart); + + // append the export button + if (this.options.exportButton && !$chartContainer.find('.oc-export-btn').length) { + this.attachExportButton(); + } + + if (this.options.pan) { + this.bindPan(); + } + + if (this.options.zoom) { + this.bindZoom(); + } - // append the export button - if (opts.exportButton && !$chartContainer.find('.oc-export-btn').length) { + return this; + }, + // + triggerInitEvent: function () { + var that = this; + var mo = new MutationObserver(function (mutations) { + mo.disconnect(); + initTime: + for (var i = 0; i < mutations.length; i++) { + for (var j = 0; j < mutations[i].addedNodes.length; j++) { + if (mutations[i].addedNodes[j].classList.contains('orgchart')) { + if (that.options.initCompleted && typeof that.options.initCompleted === 'function') { + that.options.initCompleted(that.$chart); + var initEvent = $.Event('init.orgchart'); + that.$chart.trigger(initEvent); + break initTime; + } + } + } + } + }); + mo.observe(this.$chartContainer[0], {childList: true}); + }, + // + attachExportButton: function () { + var that = this; var $exportBtn = $('<button>', { - 'class': 'oc-export-btn' + (opts.chartClass !== '' ? ' ' + opts.chartClass : ''), + 'class': 'oc-export-btn' + (this.options.chartClass !== '' ? ' ' + this.options.chartClass : ''), 'text': 'Export', 'click': function (e) { e.preventDefault(); - if ($(this).children('.spinner').length) { - return false; + that.export(); + } + }); + this.$chartContainer.append($exportBtn); + }, + setOptions: function (opts, val) { + if (typeof opts === 'string') { + if (opts === 'pan') { + if (val) { + this.bindPan(); + } else { + this.unbindPan(); } - var $mask = $chartContainer.find('.mask'); - if (!$mask.length) { - $chartContainer.append('<div class="mask"><i class="fa fa-circle-o-notch fa-spin spinner"></i></div>'); + } + if (opts === 'zoom') { + if (val) { + this.bindZoom(); } else { - $mask.removeClass('hidden'); + this.unbindZoom(); } - var sourceChart = $chartContainer.addClass('canvasContainer').find('.orgchart:visible').get(0); - var flag = opts.direction === 'l2r' || opts.direction === 'r2l'; - html2canvas(sourceChart, { - 'width': flag ? sourceChart.clientHeight : sourceChart.clientWidth, - 'height': flag ? sourceChart.clientWidth : sourceChart.clientHeight, - 'onclone': function (cloneDoc) { - $(cloneDoc).find('.canvasContainer').css('overflow', 'visible') - .find('.orgchart:visible:first').css('transform', ''); - }, - 'onrendered': function (canvas) { - $chartContainer.find('.mask').addClass('hidden'); - if (opts.exportFileextension.toLowerCase() === 'pdf') { - var doc = {}; - var docWidth = Math.floor(canvas.width * 0.2646); - var docHeight = Math.floor(canvas.height * 0.2646); - if (docWidth > docHeight) { - doc = new jsPDF('l', 'mm', [docWidth, docHeight]); - } else { - doc = new jsPDF('p', 'mm', [docHeight, docWidth]); - } - doc.addImage(canvas.toDataURL(), 'png', 0, 0); - doc.save(opts.exportFilename + '.pdf'); - } else { - var isWebkit = 'WebkitAppearance' in document.documentElement.style; - var isFf = !!window.sidebar; - var isEdge = navigator.appName === 'Microsoft Internet Explorer' || (navigator.appName === "Netscape" && navigator.appVersion.indexOf('Edge') > -1); - - if ((!isWebkit && !isFf) || isEdge) { - window.navigator.msSaveBlob(canvas.msToBlob(), opts.exportFilename + '.png'); - } - else { - $chartContainer.find('.oc-download-btn').attr('href', canvas.toDataURL())[0].click(); - } - } + } + } + if (typeof opts === 'object') { + if (opts.data) { + this.init(opts); + } else { + if (typeof opts.pan !== 'undefined') { + if (opts.pan) { + this.bindPan(); + } else { + this.unbindPan(); } - }) - .then(function () { - $chartContainer.removeClass('canvasContainer'); - }, function () { - $chartContainer.removeClass('canvasContainer'); - }); + } + if (typeof opts.zoom !== 'undefined') { + if (opts.zoom) { + this.bindZoom(); + } else { + this.unbindZoom(); + } + } } - }); - $chartContainer.append($exportBtn); - if (opts.exportFileextension.toLowerCase() !== 'pdf') { - var downloadBtn = '<a class="oc-download-btn' + (opts.chartClass !== '' ? ' ' + opts.chartClass : '') + '"' - + ' download="' + opts.exportFilename + '.png"></a>'; - $exportBtn.after(downloadBtn); } - } - if (opts.pan) { - $chartContainer.css('overflow', 'hidden'); - $chart.on('mousedown touchstart', function (e) { - var $this = $(this); - if ($(e.target).closest('.node').length || (e.touches && e.touches.length > 1)) { - $this.data('panning', false); - return; + return this; + }, + // + panStartHandler: function (e) { + var $chart = $(e.delegateTarget); + if ($(e.target).closest('.node').length || (e.touches && e.touches.length > 1)) { + $chart.data('panning', false); + return; + } else { + $chart.css('cursor', 'move').data('panning', true); + } + var lastX = 0; + var lastY = 0; + var lastTf = $chart.css('transform'); + if (lastTf !== 'none') { + var temp = lastTf.split(','); + if (lastTf.indexOf('3d') === -1) { + lastX = parseInt(temp[4]); + lastY = parseInt(temp[5]); } else { - $this.css('cursor', 'move').data('panning', true); + lastX = parseInt(temp[12]); + lastY = parseInt(temp[13]); } - var lastX = 0; - var lastY = 0; - var lastTf = $this.css('transform'); - if (lastTf !== 'none') { - var temp = lastTf.split(','); - if (lastTf.indexOf('3d') === -1) { - lastX = parseInt(temp[4]); - lastY = parseInt(temp[5]); - } else { - lastX = parseInt(temp[12]); - lastY = parseInt(temp[13]); - } + } + var startX = 0; + var startY = 0; + if (!e.targetTouches) { // pand on desktop + startX = e.pageX - lastX; + startY = e.pageY - lastY; + } else if (e.targetTouches.length === 1) { // pan on mobile device + startX = e.targetTouches[0].pageX - lastX; + startY = e.targetTouches[0].pageY - lastY; + } else if (e.targetTouches.length > 1) { + return; + } + $chart.on('mousemove touchmove', function (e) { + if (!$chart.data('panning')) { + return; } - var startX = 0; - var startY = 0; + var newX = 0; + var newY = 0; if (!e.targetTouches) { // pand on desktop - startX = e.pageX - lastX; - startY = e.pageY - lastY; + newX = e.pageX - startX; + newY = e.pageY - startY; } else if (e.targetTouches.length === 1) { // pan on mobile device - startX = e.targetTouches[0].pageX - lastX; - startY = e.targetTouches[0].pageY - lastY; + newX = e.targetTouches[0].pageX - startX; + newY = e.targetTouches[0].pageY - startY; } else if (e.targetTouches.length > 1) { return; } - $chart.on('mousemove touchmove', function (e) { - if (!$this.data('panning')) { - return; - } - var newX = 0; - var newY = 0; - if (!e.targetTouches) { // pand on desktop - newX = e.pageX - startX; - newY = e.pageY - startY; - } else if (e.targetTouches.length === 1) { // pan on mobile device - newX = e.targetTouches[0].pageX - startX; - newY = e.targetTouches[0].pageY - startY; - } else if (e.targetTouches.length > 1) { - return; + var lastTf = $chart.css('transform'); + if (lastTf === 'none') { + if (lastTf.indexOf('3d') === -1) { + $chart.css('transform', 'matrix(1, 0, 0, 1, ' + newX + ', ' + newY + ')'); + } else { + $chart.css('transform', 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ' + newX + ', ' + newY + ', 0, 1)'); } - var lastTf = $this.css('transform'); - if (lastTf === 'none') { - if (lastTf.indexOf('3d') === -1) { - $this.css('transform', 'matrix(1, 0, 0, 1, ' + newX + ', ' + newY + ')'); - } else { - $this.css('transform', 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ' + newX + ', ' + newY + ', 0, 1)'); - } + } else { + var matrix = lastTf.split(','); + if (lastTf.indexOf('3d') === -1) { + matrix[4] = ' ' + newX; + matrix[5] = ' ' + newY + ')'; } else { - var matrix = lastTf.split(','); - if (lastTf.indexOf('3d') === -1) { - matrix[4] = ' ' + newX; - matrix[5] = ' ' + newY + ')'; - } else { - matrix[12] = ' ' + newX; - matrix[13] = ' ' + newY; - } - $this.css('transform', matrix.join(',')); + matrix[12] = ' ' + newX; + matrix[13] = ' ' + newY; } - }); + $chart.css('transform', matrix.join(',')); + } }); - $(document).on('mouseup touchend', function (e) { - if ($chart.data('panning')) { - $chart.data('panning', false).css('cursor', 'default').off('mousemove'); + }, + // + panEndHandler: function (e) { + if (e.data.chart.data('panning')) { + e.data.chart.data('panning', false).css('cursor', 'default').off('mousemove'); + } + }, + // + bindPan: function () { + this.$chartContainer.css('overflow', 'hidden'); + this.$chart.on('mousedown touchstart', this.panStartHandler); + $(document).on('mouseup touchend', {'chart': this.$chart}, this.panEndHandler); + }, + // + unbindPan: function () { + this.$chartContainer.css('overflow', 'auto'); + this.$chart.off('mousedown touchstart', this.panStartHandler); + $(document).off('mouseup touchend', this.panEndHandler); + }, + // + zoomWheelHandler: function (e) { + var oc = e.data.oc; + e.preventDefault(); + var newScale = 1 + (e.originalEvent.deltaY > 0 ? -0.2 : 0.2); + oc.setChartScale(oc.$chart, newScale); + }, + // + zoomStartHandler: function (e) { + if (e.touches && e.touches.length === 2) { + var oc = e.data.oc; + oc.$chart.data('pinching', true); + var dist = oc.getPinchDist(e); + oc.$chart.data('pinchDistStart', dist); + } + }, + zoomingHandler: function (e) { + var oc = e.data.oc; + if (oc.$chart.data('pinching')) { + var dist = oc.getPinchDist(e); + oc.$chart.data('pinchDistEnd', dist); + } + }, + zoomEndHandler: function (e) { + var oc = e.data.oc; + if (oc.$chart.data('pinching')) { + oc.$chart.data('pinching', false); + var diff = oc.$chart.data('pinchDistEnd') - oc.$chart.data('pinchDistStart'); + if (diff > 0) { + oc.setChartScale(oc.$chart, 1.2); + } else if (diff < 0) { + oc.setChartScale(oc.$chart, 0.8); + } + } + }, + // + bindZoom: function () { + this.$chartContainer.on('wheel', {'oc': this}, this.zoomWheelHandler); + this.$chartContainer.on('touchstart', {'oc': this}, this.zoomStartHandler); + $(document).on('touchmove', {'oc': this}, this.zoomingHandler); + $(document).on('touchend', {'oc': this}, this.zoomEndHandler); + }, + unbindZoom: function () { + this.$chartContainer.off('wheel', this.zoomWheelHandler); + this.$chartContainer.off('touchstart', this.zoomStartHandler); + $(document).off('touchmove', this.zoomingHandler); + $(document).off('touchend', this.zoomEndHandler); + }, + // + getPinchDist: function (e) { + return Math.sqrt((e.touches[0].clientX - e.touches[1].clientX) * (e.touches[0].clientX - e.touches[1].clientX) + + (e.touches[0].clientY - e.touches[1].clientY) * (e.touches[0].clientY - e.touches[1].clientY)); + }, + // + setChartScale: function ($chart, newScale) { + var opts = $chart.data('options'); + var lastTf = $chart.css('transform'); + var matrix = ''; + var targetScale = 1; + if (lastTf === 'none') { + $chart.css('transform', 'scale(' + newScale + ',' + newScale + ')'); + } else { + matrix = lastTf.split(','); + if (lastTf.indexOf('3d') === -1) { + targetScale = Math.abs(window.parseFloat(matrix[3]) * newScale); + if (targetScale > opts.zoomoutLimit && targetScale < opts.zoominLimit) { + $chart.css('transform', lastTf + ' scale(' + newScale + ',' + newScale + ')'); + } + } else { + targetScale = Math.abs(window.parseFloat(matrix[1]) * newScale); + if (targetScale > opts.zoomoutLimit && targetScale < opts.zoominLimit) { + $chart.css('transform', lastTf + ' scale3d(' + newScale + ',' + newScale + ', 1)'); + } } + } + }, + // + buildJsonDS: function ($li) { + var that = this; + var subObj = { + 'name': $li.contents().eq(0).text().trim(), + 'relationship': ($li.parent().parent().is('li') ? '1' : '0') + ($li.siblings('li').length ? 1 : 0) + ($li.children('ul').length ? 1 : 0) + }; + $.each($li.data(), function (key, value) { + subObj[key] = value; }); - } - - if (opts.zoom) { - $chartContainer.on('wheel', function (event) { - event.preventDefault(); - var newScale = 1 + (event.originalEvent.deltaY > 0 ? -0.2 : 0.2); - setChartScale($chart, newScale); + $li.children('ul').children().each(function () { + if (!subObj.children) { + subObj.children = []; + } + subObj.children.push(that.buildJsonDS($(this))); }); - - $chartContainer.on('touchstart', function (e) { - if (e.touches && e.touches.length === 2) { - $chart.data('pinching', true); - var dist = getPinchDist(e); - $chart.data('pinchDistStart', dist); + return subObj; + }, + // + attachRel: function (data, flags) { + var that = this; + data.relationship = flags + (data.children && data.children.length > 0 ? 1 : 0); + if (data.children) { + data.children.forEach(function (item) { + that.attachRel(item, '1' + (data.children.length > 1 ? 1 : 0)); + }); + } + return data; + }, + // + loopChart: function ($chart) { + var that = this; + var $tr = $chart.find('tr:first'); + var subObj = {'id': $tr.find('.node')[0].id}; + $tr.siblings(':last').children().each(function () { + if (!subObj.children) { + subObj.children = []; } + subObj.children.push(that.loopChart($(this))); }); - $(document).on('touchmove', function (e) { - if ($chart.data('pinching')) { - var dist = getPinchDist(e); - $chart.data('pinchDistEnd', dist); + return subObj; + }, + // + getHierarchy: function () { + if (typeof this.$chart === 'undefined') { + return 'Error: orgchart does not exist' + } else { + if (!this.$chart.find('.node').length) { + return 'Error: nodes do not exist' + } else { + var valid = true; + this.$chart.find('.node').each(function () { + if (!this.id) { + valid = false; + return false; + } + }); + if (!valid) { + return 'Error: All nodes of orghcart to be exported must have data-id attribute!'; + } } - }) - .on('touchend', function (e) { - if ($chart.data('pinching')) { - $chart.data('pinching', false); - var diff = $chart.data('pinchDistEnd') - $chart.data('pinchDistStart'); - if (diff > 0) { - setChartScale($chart, 1.2); - } else if (diff < 0) { - setChartScale($chart, 0.8); + } + return this.loopChart(this.$chart); + }, + // detect the exist/display state of related node + getNodeState: function ($node, relation) { + var $target = {}; + var relation = relation || 'self'; + if (relation === 'parent') { + $target = $node.closest('.nodes').siblings(':first'); + if ($target.length) { + if ($target.is('.hidden') || (!$target.is('.hidden') && $target.closest('.nodes').is('.hidden'))) { + return {'exist': true, 'visible': false}; } + return {'exist': true, 'visible': true}; } - }); - } - - return $chartContainer; - }; - - function getPinchDist(e) { - return Math.sqrt((e.touches[0].clientX - e.touches[1].clientX) * (e.touches[0].clientX - e.touches[1].clientX) + - (e.touches[0].clientY - e.touches[1].clientY) * (e.touches[0].clientY - e.touches[1].clientY)); - } - - function setChartScale($chart, newScale) { - var opts = $chart.data('options'); - var lastTf = $chart.css('transform'); - var matrix = ''; - var targetScale = 1; - if (lastTf === 'none') { - $chart.css('transform', 'scale(' + newScale + ',' + newScale + ')'); - } else { - matrix = lastTf.split(','); - if (lastTf.indexOf('3d') === -1) { - targetScale = window.parseFloat(matrix[3]) * newScale; - if (targetScale > opts.zoomoutLimit && targetScale < opts.zoominLimit) { - $chart.css('transform', lastTf + ' scale(' + newScale + ',' + newScale + ')'); + } else if (relation === 'children') { + $target = $node.closest('tr').siblings(':last'); + if ($target.length) { + if (!$target.is('.hidden')) { + return {'exist': true, 'visible': true}; + } + return {'exist': true, 'visible': false}; + } + } else if (relation === 'siblings') { + $target = $node.closest('table').parent().siblings(); + if ($target.length) { + if (!$target.is('.hidden') && !$target.parent().is('.hidden')) { + return {'exist': true, 'visible': true}; + } + return {'exist': true, 'visible': false}; } } else { - targetScale = window.parseFloat(matrix[1]) * newScale; - if (targetScale > opts.zoomoutLimit && targetScale < opts.zoominLimit) { - $chart.css('transform', lastTf + ' scale3d(' + newScale + ',' + newScale + ', 1)'); + $target = $node; + if ($target.length) { + if (!(($target.closest('.nodes').length && $target.closest('.nodes').is('.hidden')) || + ($target.closest('table').parent().length && $target.closest('table').parent().is('.hidden')) || + ($target.parent().is('li') && ($target.closest('ul').is('.hidden') || $target.closest('verticalNodes').is('.hidden'))) + )) { + return {'exist': true, 'visible': true}; + } + return {'exist': true, 'visible': false}; } } - } - } - - function buildJsonDS($li) { - var subObj = { - 'name': $li.contents().eq(0).text().trim(), - 'relationship': ($li.parent().parent().is('li') ? '1' : '0') + ($li.siblings('li').length ? 1 : 0) + ($li.children('ul').length ? 1 : 0) - }; - if ($li[0].id) { - subObj.id = $li[0].id; - } - $li.children('ul').children().each(function () { - if (!subObj.children) { subObj.children = []; } - subObj.children.push(buildJsonDS($(this))); - }); - return subObj; - } - - function attachRel(data, flags) { - data.relationship = flags + (data.children && data.children.length > 0 ? 1 : 0); - if (data.children) { - data.children.forEach(function (item) { - attachRel(item, '1' + (data.children.length > 1 ? 1 : 0)); - }); - } - return data; - } - - function loopChart($chart) { - var $tr = $chart.find('tr:first'); - var subObj = { 'id': $tr.find('.node')[0].id }; - $tr.siblings(':last').children().each(function () { - if (!subObj.children) { subObj.children = []; } - subObj.children.push(loopChart($(this))); - }); - return subObj; - } - - function getHierarchy($chart) { - var $chart = $chart || $(this).find('.orgchart'); - if (!$chart.find('.node:first')[0].id) { - return 'Error: Nodes of orghcart to be exported must have id attribute!'; - } - return loopChart($chart); - } - - // detect the exist/display state of related node - function getNodeState($node, relation) { - var $target = {}; - if (relation === 'parent') { - $target = $node.closest('.nodes').siblings(':first'); - } else if (relation === 'children') { - $target = $node.closest('tr').siblings(); - } else if (relation === 'siblings') { - $target = $node.closest('table').parent().siblings(); - } - if ($target.length) { - if ($target.is(':visible')) { - return { 'exist': true, 'visible': true }; + return {'exist': false, 'visible': false}; + }, + // find the related nodes + getRelatedNodes: function ($node, relation) { + if (!$node || !($node instanceof $) || !$node.is('.node')) { + return $(); } - return { 'exist': true, 'visible': false }; - } - return { 'exist': false, 'visible': false }; - } - - // find the related nodes - function getRelatedNodes($node, relation) { - if (relation === 'parent') { - return $node.closest('.nodes').parent().children(':first').find('.node'); - } else if (relation === 'children') { - return $node.closest('table').children(':last').children().find('.node:first'); - } else if (relation === 'siblings') { - return $node.closest('table').parent().siblings().find('.node:first'); - } - } - - // recursively hide the ancestor node and sibling nodes of the specified node - function hideParent($node) { - var $temp = $node.closest('table').closest('tr').siblings(); - if ($temp.eq(0).find('.spinner').length) { - $node.closest('.orgchart').data('inAjax', false); - } - // hide the sibling nodes - if (getNodeState($node, 'siblings').visible) { - hideSiblings($node); - } - // hide the lines - var $lines = $temp.slice(1); - $lines.css('visibility', 'hidden'); - // hide the superior nodes with transition - var $parent = $temp.eq(0).find('.node'); - var grandfatherVisible = getNodeState($parent, 'parent').visible; - if ($parent.length && $parent.is(':visible')) { - $parent.addClass('slide slide-down').one('transitionend', function () { - $parent.removeClass('slide'); - $lines.removeAttr('style'); - $temp.addClass('hidden'); - }); - } - // if the current node has the parent node, hide it recursively - if ($parent.length && grandfatherVisible) { - hideParent($parent); - } - } - - // show the parent node of the specified node - function showParent($node) { - // just show only one superior level - var $temp = $node.closest('table').closest('tr').siblings().removeClass('hidden'); - // just show only one line - $temp.eq(2).children().slice(1, -1).addClass('hidden'); - // show parent node with animation - var parent = $temp.eq(0).find('.node')[0]; - repaint(parent); - $(parent).addClass('slide').removeClass('slide-down').one('transitionend', function () { - $(parent).removeClass('slide'); - if (isInAction($node)) { - switchVerticalArrow($node.children('.topEdge')); - } - }); - } - - // recursively hide the descendant nodes of the specified node - function hideChildren($node) { - var $temp = $node.closest('tr').siblings(); - if ($temp.last().find('.spinner').length) { - $node.closest('.orgchart').data('inAjax', false); - } - var $visibleNodes = $temp.last().find('.node:visible'); - var isVerticalDesc = $temp.last().is('.verticalNodes') ? true : false; - if (!isVerticalDesc) { - var $lines = $visibleNodes.closest('table').closest('tr').prevAll('.lines').css('visibility', 'hidden'); - } - $visibleNodes.addClass('slide slide-up').eq(0).one('transitionend', function () { - $visibleNodes.removeClass('slide'); - if (isVerticalDesc) { - $temp.addClass('hidden'); + if (relation === 'parent') { + return $node.closest('.nodes').parent().children(':first').find('.node'); + } else if (relation === 'children') { + return $node.closest('tr').siblings('.nodes').children().find('.node:first'); + } else if (relation === 'siblings') { + return $node.closest('table').parent().siblings().find('.node:first'); } else { - $lines.removeAttr('style').addClass('hidden').siblings('.nodes').addClass('hidden'); - $temp.last().find('.verticalNodes').addClass('hidden'); + return $(); } - if (isInAction($node)) { - switchVerticalArrow($node.children('.bottomEdge')); + }, + hideParentEnd: function (event) { + $(event.target).removeClass('sliding'); + event.data.upperLevel.addClass('hidden').slice(1).removeAttr('style'); + }, + // recursively hide the ancestor node and sibling nodes of the specified node + hideParent: function ($node) { + var $upperLevel = $node.closest('.nodes').siblings(); + if ($upperLevel.eq(0).find('.spinner').length) { + $node.closest('.orgchart').data('inAjax', false); } - }); - } - - // show the children nodes of the specified node - function showChildren($node) { - var $levels = $node.closest('tr').siblings(); - var isVerticalDesc = $levels.is('.verticalNodes') ? true : false; - var $descendants = isVerticalDesc - ? $levels.removeClass('hidden').find('.node:visible') - : $levels.removeClass('hidden').eq(2).children().find('.node:first'); - // the two following statements are used to enforce browser to repaint - repaint($descendants.get(0)); - $descendants.addClass('slide').removeClass('slide-up').eq(0).one('transitionend', function () { - $descendants.removeClass('slide'); - if (isInAction($node)) { - switchVerticalArrow($node.children('.bottomEdge')); - } - }); - } - - // hide the sibling nodes of the specified node - function hideSiblings($node, direction) { - var $nodeContainer = $node.closest('table').parent(); - if ($nodeContainer.siblings().find('.spinner').length) { - $node.closest('.orgchart').data('inAjax', false); - } - if (direction) { - if (direction === 'left') { - $nodeContainer.prevAll().find('.node:visible').addClass('slide slide-right'); + // hide the sibling nodes + if (this.getNodeState($node, 'siblings').visible) { + this.hideSiblings($node); + } + // hide the lines + var $lines = $upperLevel.slice(1); + $lines.css('visibility', 'hidden'); + // hide the superior nodes with transition + var $parent = $upperLevel.eq(0).find('.node'); + if (this.getNodeState($parent).visible) { + $parent.addClass('sliding slide-down').one('transitionend', {'upperLevel': $upperLevel}, this.hideParentEnd); + } + // if the current node has the parent node, hide it recursively + if (this.getNodeState($parent, 'parent').visible) { + this.hideParent($parent); + } + }, + showParentEnd: function (event) { + var $node = event.data.node; + $(event.target).removeClass('sliding'); + if (this.isInAction($node)) { + this.switchVerticalArrow($node.children('.topEdge')); + } + }, + // show the parent node of the specified node + showParent: function ($node) { + // just show only one superior level + var $upperLevel = $node.closest('.nodes').siblings().removeClass('hidden'); + // just show only one line + $upperLevel.eq(2).children().slice(1, -1).addClass('hidden'); + // show parent node with animation + var $parent = $upperLevel.eq(0).find('.node'); + this.repaint($parent[0]); + $parent.addClass('sliding').removeClass('slide-down').one('transitionend', {'node': $node}, this.showParentEnd.bind(this)); + }, + stopAjax: function ($nodeLevel) { + if ($nodeLevel.find('.spinner').length) { + $nodeLevel.closest('.orgchart').data('inAjax', false); + } + }, + isVisibleNode: function (index, elem) { + return this.getNodeState($(elem)).visible; + }, + // + hideChildrenEnd: function (event) { + var $node = event.data.node; + event.data.animatedNodes.removeClass('sliding'); + if (event.data.isVerticalDesc) { + event.data.lowerLevel.addClass('hidden'); } else { - $nodeContainer.nextAll().find('.node:visible').addClass('slide slide-left'); + event.data.animatedNodes.closest('.nodes').prevAll('.lines').removeAttr('style').addBack().addClass('hidden'); + event.data.lowerLevel.last().find('.verticalNodes').addClass('hidden'); } - } else { - $nodeContainer.prevAll().find('.node:visible').addClass('slide slide-right'); - $nodeContainer.nextAll().find('.node:visible').addClass('slide slide-left'); - } - var $animatedNodes = $nodeContainer.siblings().find('.slide'); - var $lines = $animatedNodes.closest('.nodes').prevAll('.lines').css('visibility', 'hidden'); - $animatedNodes.eq(0).one('transitionend', function () { - $lines.removeAttr('style'); + if (this.isInAction($node)) { + this.switchVerticalArrow($node.children('.bottomEdge')); + } + }, + // recursively hide the descendant nodes of the specified node + hideChildren: function ($node) { + var $lowerLevel = $node.closest('tr').siblings(); + this.stopAjax($lowerLevel.last()); + var $animatedNodes = $lowerLevel.last().find('.node').filter(this.isVisibleNode.bind(this)); + var isVerticalDesc = $lowerLevel.last().is('.verticalNodes') ? true : false; + if (!isVerticalDesc) { + $animatedNodes.closest('table').closest('tr').prevAll('.lines').css('visibility', 'hidden'); + } + this.repaint($animatedNodes.get(0)); + $animatedNodes.addClass('sliding slide-up').eq(0).one('transitionend', { + 'animatedNodes': $animatedNodes, + 'lowerLevel': $lowerLevel, + 'isVerticalDesc': isVerticalDesc, + 'node': $node + }, this.hideChildrenEnd.bind(this)); + }, + // + showChildrenEnd: function (event) { + var $node = event.data.node; + event.data.animatedNodes.removeClass('sliding'); + if (this.isInAction($node)) { + this.switchVerticalArrow($node.children('.bottomEdge')); + } + }, + // show the children nodes of the specified node + showChildren: function ($node) { + var that = this; + var $levels = $node.closest('tr').siblings(); + var isVerticalDesc = $levels.is('.verticalNodes') ? true : false; + var $animatedNodes = isVerticalDesc + ? $levels.removeClass('hidden').find('.node').filter(this.isVisibleNode.bind(this)) + : $levels.removeClass('hidden').eq(2).children().find('.node:first').filter(this.isVisibleNode.bind(this)); + // the two following statements are used to enforce browser to repaint + this.repaint($animatedNodes.get(0)); + $animatedNodes.addClass('sliding').removeClass('slide-up').eq(0).one('transitionend', {'node': $node, 'animatedNodes': $animatedNodes}, this.showChildrenEnd.bind(this)); + }, + // + hideSiblingsEnd: function (event) { + var $node = event.data.node; + var $nodeContainer = event.data.nodeContainer; + var direction = event.data.direction; + event.data.lines.removeAttr('style'); var $siblings = direction ? (direction === 'left' ? $nodeContainer.prevAll(':not(.hidden)') : $nodeContainer.nextAll(':not(.hidden)')) : $nodeContainer.siblings(); $nodeContainer.closest('.nodes').prev().children(':not(.hidden)') - .slice(1, direction ? $siblings.length * 2 + 1 : -1).addClass('hidden'); - $animatedNodes.removeClass('slide'); - $siblings.find('.node:visible:gt(0)').removeClass('slide-left slide-right').addClass('slide-up') - .end().find('.lines, .nodes, .verticalNodes').addClass('hidden') - .end().addClass('hidden'); - - if (isInAction($node)) { - switchHorizontalArrow($node); + .slice(1, direction ? $siblings.length * 2 + 1 : -1).addClass('hidden'); + event.data.animatedNodes.removeClass('sliding'); + $siblings.find('.node:gt(0)').filter(this.isVisibleNode.bind(this)) + .removeClass('slide-left slide-right').addClass('slide-up'); + $siblings.find('.lines, .nodes, .verticalNodes').addClass('hidden') + .end().addClass('hidden'); + + if (this.isInAction($node)) { + this.switchHorizontalArrow($node); } - }); - } - - // show the sibling nodes of the specified node - function showSiblings($node, direction) { - // firstly, show the sibling td tags - var $siblings = $(); - if (direction) { - if (direction === 'left') { - $siblings = $node.closest('table').parent().prevAll().removeClass('hidden'); + }, + // hide the sibling nodes of the specified node + hideSiblings: function ($node, direction) { + var that = this; + var $nodeContainer = $node.closest('table').parent(); + if ($nodeContainer.siblings().find('.spinner').length) { + $node.closest('.orgchart').data('inAjax', false); + } + if (direction) { + if (direction === 'left') { + $nodeContainer.prevAll().find('.node').filter(this.isVisibleNode.bind(this)).addClass('sliding slide-right'); + } else { + $nodeContainer.nextAll().find('.node').filter(this.isVisibleNode.bind(this)).addClass('sliding slide-left'); + } } else { - $siblings = $node.closest('table').parent().nextAll().removeClass('hidden'); + $nodeContainer.prevAll().find('.node').filter(this.isVisibleNode.bind(this)).addClass('sliding slide-right'); + $nodeContainer.nextAll().find('.node').filter(this.isVisibleNode.bind(this)).addClass('sliding slide-left'); } - } else { - $siblings = $node.closest('table').parent().siblings().removeClass('hidden'); - } - // secondly, show the lines - var $upperLevel = $node.closest('table').closest('tr').siblings(); - if (direction) { - $upperLevel.eq(2).children('.hidden').slice(0, $siblings.length * 2).removeClass('hidden'); - } else { - $upperLevel.eq(2).children('.hidden').removeClass('hidden'); - } - // thirdly, do some cleaning stuff - if (!getNodeState($node, 'parent').visible) { - $upperLevel.removeClass('hidden'); - var parent = $upperLevel.find('.node')[0]; - repaint(parent); - $(parent).addClass('slide').removeClass('slide-down').one('transitionend', function () { - $(this).removeClass('slide'); - }); - } - // lastly, show the sibling nodes with animation - $siblings.find('.node:visible').addClass('slide').removeClass('slide-left slide-right').eq(-1).one('transitionend', function () { - $siblings.find('.node:visible').removeClass('slide'); - if (isInAction($node)) { - switchHorizontalArrow($node); + var $animatedNodes = $nodeContainer.siblings().find('.sliding'); + var $lines = $animatedNodes.closest('.nodes').prevAll('.lines').css('visibility', 'hidden'); + $animatedNodes.eq(0).one('transitionend', {'node': $node, 'nodeContainer': $nodeContainer, 'direction': direction, 'animatedNodes': $animatedNodes, 'lines': $lines}, this.hideSiblingsEnd.bind(this)); + }, + // + showSiblingsEnd: function (event) { + var $node = event.data.node; + event.data.visibleNodes.removeClass('sliding'); + if (this.isInAction($node)) { + this.switchHorizontalArrow($node); $node.children('.topEdge').removeClass('fa-chevron-up').addClass('fa-chevron-down'); } - }); - } - - // start up loading status for requesting new nodes - function startLoading($arrow, $node, options) { - var $chart = $node.closest('.orgchart'); - if (typeof $chart.data('inAjax') !== 'undefined' && $chart.data('inAjax') === true) { - return false; - } - - $arrow.addClass('hidden'); - $node.append('<i class="fa fa-circle-o-notch fa-spin spinner"></i>'); - $node.children().not('.spinner').css('opacity', 0.2); - $chart.data('inAjax', true); - $('.oc-export-btn' + (options.chartClass !== '' ? '.' + options.chartClass : '')).prop('disabled', true); - return true; - } - - // terminate loading status for requesting new nodes - function endLoading($arrow, $node, options) { - var $chart = $node.closest('div.orgchart'); - $arrow.removeClass('hidden'); - $node.find('.spinner').remove(); - $node.children().removeAttr('style'); - $chart.data('inAjax', false); - $('.oc-export-btn' + (options.chartClass !== '' ? '.' + options.chartClass : '')).prop('disabled', false); - } - - // whether the cursor is hovering over the node - function isInAction($node) { - return $node.children('.edge').attr('class').indexOf('fa-') > -1 ? true : false; - } - - function switchVerticalArrow($arrow) { - $arrow.toggleClass('fa-chevron-up').toggleClass('fa-chevron-down'); - } - - function switchHorizontalArrow($node) { - var opts = $node.closest('.orgchart').data('options'); - if (opts.toggleSiblingsResp && (typeof opts.ajaxURL === 'undefined' || $node.closest('.nodes').data('siblingsLoaded'))) { - var $prevSib = $node.closest('table').parent().prev(); - if ($prevSib.length) { - if ($prevSib.is('.hidden')) { - $node.children('.leftEdge').addClass('fa-chevron-left').removeClass('fa-chevron-right'); + }, + // + showRelatedParentEnd: function (event) { + $(event.target).removeClass('sliding'); + }, + // show the sibling nodes of the specified node + showSiblings: function ($node, direction) { + var that = this; + // firstly, show the sibling td tags + var $siblings = $(); + if (direction) { + if (direction === 'left') { + $siblings = $node.closest('table').parent().prevAll().removeClass('hidden'); } else { - $node.children('.leftEdge').addClass('fa-chevron-right').removeClass('fa-chevron-left'); + $siblings = $node.closest('table').parent().nextAll().removeClass('hidden'); } + } else { + $siblings = $node.closest('table').parent().siblings().removeClass('hidden'); } - var $nextSib = $node.closest('table').parent().next(); - if ($nextSib.length) { - if ($nextSib.is('.hidden')) { - $node.children('.rightEdge').addClass('fa-chevron-right').removeClass('fa-chevron-left'); - } else { - $node.children('.rightEdge').addClass('fa-chevron-left').removeClass('fa-chevron-right'); - } + // secondly, show the lines + var $upperLevel = $node.closest('table').closest('tr').siblings(); + if (direction) { + $upperLevel.eq(2).children('.hidden').slice(0, $siblings.length * 2).removeClass('hidden'); + } else { + $upperLevel.eq(2).children('.hidden').removeClass('hidden'); } - } else { - var $sibs = $node.closest('table').parent().siblings(); - var sibsVisible = $sibs.length ? !$sibs.is('.hidden') : false; - $node.children('.leftEdge').toggleClass('fa-chevron-right', sibsVisible).toggleClass('fa-chevron-left', !sibsVisible); - $node.children('.rightEdge').toggleClass('fa-chevron-left', sibsVisible).toggleClass('fa-chevron-right', !sibsVisible); - } - } - - function repaint(node) { - if (node) { - node.style.offsetWidth = node.offsetWidth; - } - } - - - var expandedOrgList = []; - - // create node - function createNode(nodeData, level, opts) { - - $.each(nodeData.children, function (index, child) { - child.parentId = nodeData.id; - }); - var dtd = $.Deferred(); - // construct the content of node - var $nodeDiv = $('<div' + (opts.draggable ? ' draggable="true"' : '') + (nodeData[opts.nodeId] ? ' id="' + nodeData[opts.nodeId] + '"' : '') + (nodeData.parentId ? ' data-parent="' + nodeData.parentId + '"' : '') + '>') - .addClass('node ' + (nodeData.className || '') + (level >= opts.depth ? ' slide-up' : '')) - .append('<div class="title">' + nodeData[opts.nodeTitle] + '</div>') - .append(typeof opts.nodeContent !== 'undefined' ? '<div class="content">' + (nodeData[opts.nodeContent] || '') + '</div>' : ''); - // append 4 direction arrows or expand/collapse buttons - var flags = nodeData.relationship || ''; - if (opts.verticalDepth && (level + 2) > opts.verticalDepth) { - if ((level + 1) >= opts.verticalDepth && Number(flags.substr(2, 1))) { - var icon = level + 1 >= opts.depth ? 'plus' : 'minus'; - $nodeDiv.append('<i class="toggleBtn fa fa-' + icon + '-square"></i>'); - } - } else { - if (Number(flags.substr(0, 1))) { - $nodeDiv.append('<i class="edge verticalEdge topEdge fa"></i>'); - } - if (Number(flags.substr(1, 1))) { - $nodeDiv.append('<i class="edge horizontalEdge rightEdge fa"></i>' + - '<i class="edge horizontalEdge leftEdge fa"></i>'); - } - if (Number(flags.substr(2, 1))) { - $nodeDiv.append('<i class="edge verticalEdge bottomEdge fa"></i>') - .children('.title').prepend('<i class="fa ' + opts.parentNodeSymbol + ' symbol"></i>'); + // thirdly, do some cleaning stuff + if (!this.getNodeState($node, 'parent').visible) { + $upperLevel.removeClass('hidden'); + var parent = $upperLevel.find('.node')[0]; + this.repaint(parent); + $(parent).addClass('sliding').removeClass('slide-down').one('transitionend', this.showRelatedParentEnd); + } + // lastly, show the sibling nodes with animation + var $visibleNodes = $siblings.find('.node').filter(this.isVisibleNode.bind(this)); + this.repaint($visibleNodes.get(0)); + $visibleNodes.addClass('sliding').removeClass('slide-left slide-right'); + $visibleNodes.eq(0).one('transitionend', {'node': $node, 'visibleNodes': $visibleNodes}, this.showSiblingsEnd.bind(this)); + }, + // start up loading status for requesting new nodes + startLoading: function ($edge) { + var $chart = this.$chart; + if (typeof $chart.data('inAjax') !== 'undefined' && $chart.data('inAjax') === true) { + return false; } - } - $nodeDiv.on('mouseenter mouseleave', function (event) { - var $node = $(this), flag = false; + $edge.addClass('hidden'); + $edge.parent().append('<i class="fa fa-circle-o-notch fa-spin spinner"></i>') + .children().not('.spinner').css('opacity', 0.2); + $chart.data('inAjax', true); + $('.oc-export-btn' + (this.options.chartClass !== '' ? '.' + this.options.chartClass : '')).prop('disabled', true); + return true; + }, + // terminate loading status for requesting new nodes + endLoading: function ($edge) { + var $node = $edge.parent(); + $edge.removeClass('hidden'); + $node.find('.spinner').remove(); + $node.children().removeAttr('style'); + this.$chart.data('inAjax', false); + $('.oc-export-btn' + (this.options.chartClass !== '' ? '.' + this.options.chartClass : '')).prop('disabled', false); + }, + // whether the cursor is hovering over the node + isInAction: function ($node) { + return $node.children('.edge').attr('class').indexOf('fa-') > -1 ? true : false; + }, + // + switchVerticalArrow: function ($arrow) { + $arrow.toggleClass('fa-chevron-up').toggleClass('fa-chevron-down'); + }, + // + switchHorizontalArrow: function ($node) { + var opts = this.options; + if (opts.toggleSiblingsResp && (typeof opts.ajaxURL === 'undefined' || $node.closest('.nodes').data('siblingsLoaded'))) { + var $prevSib = $node.closest('table').parent().prev(); + if ($prevSib.length) { + if ($prevSib.is('.hidden')) { + $node.children('.leftEdge').addClass('fa-chevron-left').removeClass('fa-chevron-right'); + } else { + $node.children('.leftEdge').addClass('fa-chevron-right').removeClass('fa-chevron-left'); + } + } + var $nextSib = $node.closest('table').parent().next(); + if ($nextSib.length) { + if ($nextSib.is('.hidden')) { + $node.children('.rightEdge').addClass('fa-chevron-right').removeClass('fa-chevron-left'); + } else { + $node.children('.rightEdge').addClass('fa-chevron-left').removeClass('fa-chevron-right'); + } + } + } else { + var $sibs = $node.closest('table').parent().siblings(); + var sibsVisible = $sibs.length ? !$sibs.is('.hidden') : false; + $node.children('.leftEdge').toggleClass('fa-chevron-right', sibsVisible).toggleClass('fa-chevron-left', !sibsVisible); + $node.children('.rightEdge').toggleClass('fa-chevron-left', sibsVisible).toggleClass('fa-chevron-right', !sibsVisible); + } + }, + // + repaint: function (node) { + if (node) { + node.style.offsetWidth = node.offsetWidth; + } + }, + // + nodeEnterLeaveHandler: function (event) { + var $node = $(event.delegateTarget), flag = false; var $topEdge = $node.children('.topEdge'); var $rightEdge = $node.children('.rightEdge'); var $bottomEdge = $node.children('.bottomEdge'); var $leftEdge = $node.children('.leftEdge'); if (event.type === 'mouseenter') { if ($topEdge.length) { - flag = getNodeState($node, 'parent').visible; + flag = this.getNodeState($node, 'parent').visible; $topEdge.toggleClass('fa-chevron-up', !flag).toggleClass('fa-chevron-down', flag); } if ($bottomEdge.length) { - flag = getNodeState($node, 'children').visible; + flag = this.getNodeState($node, 'children').visible; $bottomEdge.toggleClass('fa-chevron-down', !flag).toggleClass('fa-chevron-up', flag); } if ($leftEdge.length) { - switchHorizontalArrow($node); + this.switchHorizontalArrow($node); } } else { $node.children('.edge').removeClass('fa-chevron-up fa-chevron-down fa-chevron-right fa-chevron-left'); } - }); - - // define click event handler - $nodeDiv.on('click', function (event) { - $(this).closest('.orgchart').find('.focused').removeClass('focused'); - $(this).addClass('focused'); - }); - - // define click event handler for the top edge - $nodeDiv.on('click', '.topEdge', function (event) { + }, + // + nodeClickHandler: function (event) { + this.$chart.find('.focused').removeClass('focused'); + $(event.delegateTarget).addClass('focused'); + }, + // load new nodes by ajax + loadNodes: function (rel, url, $edge) { + var that = this; + var opts = this.options; + $.ajax({'url': url, 'dataType': 'json'}) + .done(function (data) { + if (that.$chart.data('inAjax')) { + if (rel === 'parent') { + if (!$.isEmptyObject(data)) { + that.addParent($edge.parent(), data); + } + } else if (rel === 'children') { + if (data.children.length) { + that.addChildren($edge.parent(), data[rel]); + } + } else { + that.addSiblings($edge.parent(), data.siblings ? data.siblings : data); + } + } + }) + .fail(function () { + console.log('Failed to get ' + rel + ' data'); + }) + .always(function () { + that.endLoading($edge); + }); + }, + // + HideFirstParentEnd: function (event) { + var $topEdge = event.data.topEdge; + var $node = $topEdge.parent(); + if (this.isInAction($node)) { + this.switchVerticalArrow($topEdge); + this.switchHorizontalArrow($node); + } + }, + // + topEdgeClickHandler: function (event) { event.stopPropagation(); - var $that = $(this); - var $node = $that.parent(); - var parentState = getNodeState($node, 'parent'); + var that = this; + var $topEdge = $(event.target); + var $node = $(event.delegateTarget); + var parentState = this.getNodeState($node, 'parent'); if (parentState.exist) { var $parent = $node.closest('table').closest('tr').siblings(':first').find('.node'); - if ($parent.is('.slide')) { return; } + if ($parent.is('.sliding')) { + return; + } // hide the ancestor nodes and sibling nodes of the specified node if (parentState.visible) { - hideParent($node); - $parent.one('transitionend', function () { - if (isInAction($node)) { - switchVerticalArrow($that); - switchHorizontalArrow($node); - } - }); + this.hideParent($node); + $parent.one('transitionend', {'topEdge': $topEdge}, this.HideFirstParentEnd.bind(this)); } else { // show the ancestors and siblings - showParent($node); + this.showParent($node); } - } else { - // load the new parent node of the specified node by ajax request - var nodeId = $that.parent()[0].id; + } else { // load the new parent node of the specified node by ajax request // start up loading status - if (startLoading($that, $node, opts)) { - // load new nodes - $.ajax({ 'url': $.isFunction(opts.ajaxURL.parent) ? opts.ajaxURL.parent(nodeData) : opts.ajaxURL.parent + nodeId, 'dataType': 'json' }) - .done(function (data) { - if ($node.closest('.orgchart').data('inAjax')) { - if (!$.isEmptyObject(data)) { - addParent.call($node.closest('.orgchart').parent(), $node, data, opts); - } - } - }) - .fail(function () { console.log('Failed to get parent node data'); }) - .always(function () { endLoading($that, $node, opts); }); + if (this.startLoading($topEdge)) { + var opts = this.options; + var url = $.isFunction(opts.ajaxURL.parent) ? opts.ajaxURL.parent($node.data('nodeData')) : opts.ajaxURL.parent + $node[0].id; + this.loadNodes('parent', url, $topEdge); } } - }); - - //get webapi token info - function getApiToken() { - var apiTokenObj = JSON.parse(getCookie('AtmsApiToken')); - var apiToken = apiTokenObj.access_token; - var tokenType = apiTokenObj.token_type; - var api_host = apiTokenObj.api_host; - - return { - apiToken: apiToken, - tokenType: tokenType, - api_host: api_host - }; - }; - - function getParameterByName(name, url) { - if (!url) url = window.location.href; - name = name.replace(/[\[\]]/g, "\\$&"); - var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), - results = regex.exec(url); - if (!results) return null; - if (!results[2]) return ''; - return decodeURIComponent(results[2].replace(/\+/g, " ")); - }; - - // bind click event handler for the bottom edge - jacob0702 - $nodeDiv.on('click', '.bottomEdge', function (event) { + }, + // + bottomEdgeClickHandler: function (event) { event.stopPropagation(); - var $that = $(this); - var $node = $that.parent(); - var childrenState = getNodeState($node, 'children'); - - var plevel = $node.find('.title:eq(0)').attr('plevel'); - + var $bottomEdge = $(event.target); + var $node = $(event.delegateTarget); + var childrenState = this.getNodeState($node, 'children'); if (childrenState.exist) { var $children = $node.closest('tr').siblings(':last'); - if ($children.find('.node:visible').is('.slide')) { + if ($children.find('.sliding').length) { return; } - - var parentOrgID = $node.find('#OrgID').text() // hide the descendant nodes of the specified node if (childrenState.visible) { - - hideChildren($node); - - expandedOrgList = jQuery.grep(expandedOrgList, function (value) { - return value != parentOrgID; - }); + this.hideChildren($node); } else { // show the descendants - - if (expandedOrgList.indexOf(parentOrgID) < 0) { - expandedOrgList.push(parentOrgID); - } - - showChildren($node); + this.showChildren($node); } } else { // load the new children nodes of the specified node by ajax request - var nodeId = $that.parent()[0].id; - if (startLoading($that, $node, opts)) { - - var apiTokenObj = getApiToken(); - var antiForgeryToken = $('input[name="__RequestVerificationToken"]').val(); - var url = $.isFunction(opts.ajaxURL.children) ? opts.ajaxURL.children(nodeData) : opts.ajaxURL.children + nodeId; - - $.ajax({ - 'type': "get", - 'url': url, - 'dataType': 'json', - beforeSend: function (xhr) { - console.log("__RequestVerificationToken", antiForgeryToken); - xhr.setRequestHeader('Authorization', apiTokenObj.tokenType + ' ' + apiTokenObj.apiToken); - } - }) - .done(function (data, textStatus, jqXHR) { - - var parentOrgID = getParameterByName('parentOrgID', url); - if (expandedOrgList.indexOf(parentOrgID) < 0) { - expandedOrgList.push(parentOrgID); - } - - if ($node.closest('.orgchart').data('inAjax')) { - - //modified by jacob on May 30 - if (data.length == 0) return; - - data = { - 'children': createNodes(data, plevel) - }; - if (data.children.length) { - addChildren($node, data, $.extend({ - }, opts, { - depth: 0 - })); - } - } - }) - .fail(function (jqXHR, textStatus, errorThrown) { - console.log('Failed to get children nodes data'); - }) - .always(function () { - endLoading($that, $node, opts); - }); + if (this.startLoading($bottomEdge)) { + var opts = this.options; + var url = $.isFunction(opts.ajaxURL.children) ? opts.ajaxURL.children($node.data('nodeData')) : opts.ajaxURL.children + $node[0].id; + this.loadNodes('children', url, $bottomEdge); } } - }); - - - function getCookie(cname) { - var name = cname + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i].trim(); - //if (c.indexOf(name) == 0) return c.substring(name.length, c.length); - if (c.indexOf(name) == 0) return ATMS_EXTRA.decodeCookieValue(c.substring(name.length, c.length)); - } - return ""; - } - - //added by jacob on May 30 - var createNodes = function (data, plevel) { - var plevel = plevel + 1; - - var childrenNode = []; - if (data !== undefined && data !== null && data.length > 0) { - data.forEach(function (row) { - - var newNode = createNewNode(row); - - newNode.pLevel = plevel; - childrenNode.push(newNode); - }); - - return childrenNode; - } - }; - - var ReplaceObj = { - //顶部节点 - topNodeName: '{{#topNodeName#}}', - //事业部å称 - dimensionValue: '{{#dimensionValue#}}', - dimensionValueID: '{{#dimensionValueID#}}', - //高亮部分统计 - //hightNodeCount: '{{#dimensionValueCount#}}', //比如 '100' - //hightNodeName: '{{#attributeName#}}', //比如'分公å¸' - //普通维度统计 - attributeID: '{{#attributeID#}}', - attributeName: '{{#attributeName#}}', //维度åå—ï¼Œå¦‚ç”¨æˆ·æ•°ï¼Œè¦†ç›–åŒºåŸŸï¼Œæ‰€å±žè¡Œä¸šç‰ - attributeValueCount: '{{#attributeValueCount#}}', //对于维度的统计 - attributeType: '{{#attributeType#}}', //维度的类型区分值,如1:事业部,2:åˆ†å…¬å¸ - firstAttrName: '{{#firstAttr}}', - secondAttrName: '{{#secondAttr}}', - firstAttrTitle: '{{#firstAttrTitle}}', - secondAttrTitle: '{{#secondAttrTitle}}' - }; - - var filterString = function (str, maxLength) { - if (str != '' && str != undefined && str != null) { - var apostrophe = '...'; - var twoChar = 'mm'; - var r = /[^\x00-\xff]/g; - if (str.replace(r, twoChar).length <= maxLength) { - return str; - } - var m = Math.floor(maxLength / 2); - for (var i = m; i < str.length; i++) { - if (str.substr(0, i).replace(r, twoChar).length >= maxLength) { - return str.substr(0, i) + apostrophe; - } - } - return str; - } - }; - - var template = { - topNodeTemplate: $("#dimension-topNode").html(), //最顶层节点 - nameNodeQuery: $("#dimension-org-node-name-query").html(), //机构å称 - nameNodeEdit: $("#dimension-org-node-name-edit").html(), //机构å称 - rowNode: $("#dimension-row-node").html() //普通行 - }; - - //创建orgchart节点 - var createNewNode = function (row) { - - var temp = []; - var relationship = '110'; - if (row.hasChildren !== undefined && row.hasChildren) { - relationship = '111'; - } - - var templateNameNode = template.nameNodeQuery; - if (opts.hasEditPermission) { - templateNameNode = template.nameNodeEdit; - } - - var node = { - 'id': row.dimensionValueID, - 'name': templateNameNode.replaceToEnd(ReplaceObj.dimensionValue, row.dimensionValue) - .replaceToEnd(ReplaceObj.dimensionValueID, row.dimensionValueID) - .replaceToEnd(ReplaceObj.dimensionValueID, row.dimensionValueID) - .replaceToEnd(ReplaceObj.firstAttrName, row.firstAttrName === null ? ' 未指定 ' : filterString(row.firstAttrName, 10)) - .replaceToEnd(ReplaceObj.secondAttrName, row.secondAttrName === null ? ' 未指定 ' : filterString(row.secondAttrName, 10)) - .replaceToEnd(ReplaceObj.firstAttrTitle, row.firstAttrName === null ? ' 未指定 ' : row.firstAttrName) - .replaceToEnd(ReplaceObj.secondAttrTitle, row.secondAttrName === null ? ' 未指定 ' : row.secondAttrName), - - 'title': (function () { - - row.orgDtoList.forEach(function (item, index) { - - temp += template.rowNode.replaceToEnd(ReplaceObj.attributeName, item.attributeName) - .replaceToEnd(ReplaceObj.attributeValueCount, item.attributeValueCount) - .replaceToEnd(ReplaceObj.dimensionValueID, row.dimensionValueID) - .replaceToEnd(ReplaceObj.dimensionValue, row.dimensionValue) - .replaceToEnd(ReplaceObj.attributeID, item.attributeID) - .replaceToEnd(ReplaceObj.attributeType, item.attributeType); - }); - return temp; - - })(), - 'children': [], - 'relationship': relationship - }; - - //特殊处ç†ï¼Œå¦‚果没有å机构,åªæ˜¾ç¤ºæœ¬èº«å³å¯ - if (row.hasChildren === undefined || !row.hasChildren) { - node.title = ''; - } - - return node; - }; - - // event handler for toggle buttons in Hybrid(horizontal + vertical) OrgChart - $nodeDiv.on('click', '.toggleBtn', function (event) { - var $this = $(this); - var $descWrapper = $this.parent().next(); - var $descendants = $descWrapper.find('.node'); - var $children = $descWrapper.children().children('.node'); - if ($children.is('.slide')) { - return; - } - $this.toggleClass('fa-plus-square fa-minus-square'); - if ($descendants.eq(0).is('.slide-up')) { - $descWrapper.removeClass('hidden'); - repaint($children.get(0)); - $children.addClass('slide').removeClass('slide-up').eq(0).one('transitionend', function () { - $children.removeClass('slide'); - }); - } else { - $descendants.addClass('slide slide-up').eq(0).one('transitionend', function () { - $descendants.removeClass('slide'); - // $descWrapper.addClass('hidden'); - $descendants.closest('ul').addClass('hidden'); - }); - $descendants.find('.toggleBtn').removeClass('fa-minus-square').addClass('fa-plus-square'); - } - }); - - // bind click event handler for the left and right edges - $nodeDiv.on('click', '.leftEdge, .rightEdge', function (event) { + }, + // + hEdgeClickHandler: function (event) { event.stopPropagation(); - var $that = $(this); - var $node = $that.parent(); - var siblingsState = getNodeState($node, 'siblings'); + var $hEdge = $(event.target); + var $node = $(event.delegateTarget); + var opts = this.options; + var siblingsState = this.getNodeState($node, 'siblings'); if (siblingsState.exist) { var $siblings = $node.closest('table').parent().siblings(); - if ($siblings.find('.node:visible').is('.slide')) { + if ($siblings.find('.sliding').length) { return; } if (opts.toggleSiblingsResp) { var $prevSib = $node.closest('table').parent().prev(); var $nextSib = $node.closest('table').parent().next(); - if ($that.is('.leftEdge')) { + if ($hEdge.is('.leftEdge')) { if ($prevSib.is('.hidden')) { - showSiblings($node, 'left'); + this.showSiblings($node, 'left'); } else { - hideSiblings($node, 'left'); + this.hideSiblings($node, 'left'); } } else { if ($nextSib.is('.hidden')) { - showSiblings($node, 'right'); + this.showSiblings($node, 'right'); } else { - hideSiblings($node, 'right'); + this.hideSiblings($node, 'right'); } } } else { if (siblingsState.visible) { - hideSiblings($node); + this.hideSiblings($node); } else { - showSiblings($node); + this.showSiblings($node); } } } else { // load the new sibling nodes of the specified node by ajax request - var nodeId = $that.parent()[0].id; - var url = (getNodeState($node, 'parent').exist) ? - ($.isFunction(opts.ajaxURL.siblings) ? opts.ajaxURL.siblings(nodeData) : opts.ajaxURL.siblings + nodeId) : - ($.isFunction(opts.ajaxURL.families) ? opts.ajaxURL.families(nodeData) : opts.ajaxURL.families + nodeId); - if (startLoading($that, $node, opts)) { - $.ajax({ - 'url': url, 'dataType': 'json' - }) - .done(function (data, textStatus, jqXHR) { - if ($node.closest('.orgchart').data('inAjax')) { - if (data.siblings || data.children) { - addSiblings($node, data, opts); - } - } - }) - .fail(function (jqXHR, textStatus, errorThrown) { - console.log('Failed to get sibling nodes data'); - }) - .always(function () { - endLoading($that, $node, opts); - }); + if (this.startLoading($hEdge)) { + var nodeId = $node[0].id; + var url = (this.getNodeState($node, 'parent').exist) ? + ($.isFunction(opts.ajaxURL.siblings) ? opts.ajaxURL.siblings($node.data('nodeData')) : opts.ajaxURL.siblings + nodeId) : + ($.isFunction(opts.ajaxURL.families) ? opts.ajaxURL.families($node.data('nodeData')) : opts.ajaxURL.families + nodeId); + this.loadNodes('siblings', url, $hEdge); } } - }); - if (opts.draggable) { - $nodeDiv.on('dragstart', function (event) { - var origEvent = event.originalEvent; - var isFirefox = /firefox/.test(window.navigator.userAgent.toLowerCase()); - if (isFirefox) { - origEvent.dataTransfer.setData('text/html', 'hack for firefox'); - } - // if users enable zoom or direction options - if ($nodeDiv.closest('.orgchart').css('transform') !== 'none') { - var ghostNode, nodeCover; - if (!document.querySelector('.ghost-node')) { - ghostNode = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - ghostNode.classList.add('ghost-node'); - nodeCover = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); - ghostNode.appendChild(nodeCover); - $nodeDiv.closest('.orgchart').append(ghostNode); - } else { - ghostNode = $nodeDiv.closest('.orgchart').children('.ghost-node').get(0); - nodeCover = $(ghostNode).children().get(0); - } - var transValues = $nodeDiv.closest('.orgchart').css('transform').split(','); - var scale = Math.abs(window.parseFloat((opts.direction === 't2b' || opts.direction === 'b2t') ? transValues[0].slice(transValues[0].indexOf('(') + 1) : transValues[1])); - ghostNode.setAttribute('width', $nodeDiv.outerWidth(false)); - ghostNode.setAttribute('height', $nodeDiv.outerHeight(false)); - nodeCover.setAttribute('x', 5 * scale); - nodeCover.setAttribute('y', 5 * scale); - nodeCover.setAttribute('width', 120 * scale); - nodeCover.setAttribute('height', 40 * scale); - nodeCover.setAttribute('rx', 4 * scale); - nodeCover.setAttribute('ry', 4 * scale); - nodeCover.setAttribute('stroke-width', 1 * scale); - var xOffset = origEvent.offsetX * scale; - var yOffset = origEvent.offsetY * scale; - if (opts.direction === 'l2r') { - xOffset = origEvent.offsetY * scale; - yOffset = origEvent.offsetX * scale; - } else if (opts.direction === 'r2l') { - xOffset = $nodeDiv.outerWidth(false) - origEvent.offsetY * scale; - yOffset = origEvent.offsetX * scale; - } else if (opts.direction === 'b2t') { - xOffset = $nodeDiv.outerWidth(false) - origEvent.offsetX * scale; - yOffset = $nodeDiv.outerHeight(false) - origEvent.offsetY * scale; - } - if (isFirefox) { // hack for old version of Firefox(< 48.0) - nodeCover.setAttribute('fill', 'rgb(255, 255, 255)'); - nodeCover.setAttribute('stroke', 'rgb(191, 0, 0)'); - var ghostNodeWrapper = document.createElement('img'); - ghostNodeWrapper.src = 'data:image/svg+xml;utf8,' + (new XMLSerializer()).serializeToString(ghostNode); - origEvent.dataTransfer.setDragImage(ghostNodeWrapper, xOffset, yOffset); + }, + // + expandVNodesEnd: function (event) { + event.data.vNodes.removeClass('sliding'); + }, + // + collapseVNodesEnd: function (event) { + event.data.vNodes.removeClass('sliding').closest('ul').addClass('hidden'); + }, + // event handler for toggle buttons in Hybrid(horizontal + vertical) OrgChart + toggleVNodes: function (event) { + var $toggleBtn = $(event.target); + var $descWrapper = $toggleBtn.parent().next(); + var $descendants = $descWrapper.find('.node'); + var $children = $descWrapper.children().children('.node'); + if ($children.is('.sliding')) { + return; + } + $toggleBtn.toggleClass('fa-plus-square fa-minus-square'); + if ($descendants.eq(0).is('.slide-up')) { + $descWrapper.removeClass('hidden'); + this.repaint($children.get(0)); + $children.addClass('sliding').removeClass('slide-up').eq(0).one('transitionend', {'vNodes': $children}, this.expandVNodesEnd); + } else { + $descendants.addClass('sliding slide-up').eq(0).one('transitionend', {'vNodes': $descendants}, this.collapseVNodesEnd); + $descendants.find('.toggleBtn').removeClass('fa-minus-square').addClass('fa-plus-square'); + } + }, + // + createGhostNode: function (event) { + var $nodeDiv = $(event.target); + var opts = this.options; + var origEvent = event.originalEvent; + var isFirefox = /firefox/.test(window.navigator.userAgent.toLowerCase()); + var ghostNode, nodeCover; + if (!document.querySelector('.ghost-node')) { + ghostNode = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + ghostNode.classList.add('ghost-node'); + nodeCover = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + ghostNode.appendChild(nodeCover); + $nodeDiv.closest('.orgchart').append(ghostNode); + } else { + ghostNode = $nodeDiv.closest('.orgchart').children('.ghost-node').get(0); + nodeCover = $(ghostNode).children().get(0); + } + var transValues = $nodeDiv.closest('.orgchart').css('transform').split(','); + var isHorizontal = opts.direction === 't2b' || opts.direction === 'b2t'; + var scale = Math.abs(window.parseFloat(isHorizontal ? transValues[0].slice(transValues[0].indexOf('(') + 1) : transValues[1])); + ghostNode.setAttribute('width', isHorizontal ? $nodeDiv.outerWidth(false) : $nodeDiv.outerHeight(false)); + ghostNode.setAttribute('height', isHorizontal ? $nodeDiv.outerHeight(false) : $nodeDiv.outerWidth(false)); + nodeCover.setAttribute('x', 5 * scale); + nodeCover.setAttribute('y', 5 * scale); + nodeCover.setAttribute('width', 120 * scale); + nodeCover.setAttribute('height', 40 * scale); + nodeCover.setAttribute('rx', 4 * scale); + nodeCover.setAttribute('ry', 4 * scale); + nodeCover.setAttribute('stroke-width', 1 * scale); + var xOffset = origEvent.offsetX * scale; + var yOffset = origEvent.offsetY * scale; + if (opts.direction === 'l2r') { + xOffset = origEvent.offsetY * scale; + yOffset = origEvent.offsetX * scale; + } else if (opts.direction === 'r2l') { + xOffset = $nodeDiv.outerWidth(false) - origEvent.offsetY * scale; + yOffset = origEvent.offsetX * scale; + } else if (opts.direction === 'b2t') { + xOffset = $nodeDiv.outerWidth(false) - origEvent.offsetX * scale; + yOffset = $nodeDiv.outerHeight(false) - origEvent.offsetY * scale; + } + if (isFirefox) { // hack for old version of Firefox(< 48.0) + nodeCover.setAttribute('fill', 'rgb(255, 255, 255)'); + nodeCover.setAttribute('stroke', 'rgb(191, 0, 0)'); + var ghostNodeWrapper = document.createElement('img'); + ghostNodeWrapper.src = 'data:image/svg+xml;utf8,' + (new XMLSerializer()).serializeToString(ghostNode); + origEvent.dataTransfer.setDragImage(ghostNodeWrapper, xOffset, yOffset); + } else { + origEvent.dataTransfer.setDragImage(ghostNode, xOffset, yOffset); + } + }, + // + filterAllowedDropNodes: function ($dragged) { + var opts = this.options; + var $dragZone = $dragged.closest('.nodes').siblings().eq(0).find('.node:first'); + var $dragHier = $dragged.closest('table').find('.node'); + this.$chart.data('dragged', $dragged) + .find('.node').each(function (index, node) { + if ($dragHier.index(node) === -1) { + if (opts.dropCriteria) { + if (opts.dropCriteria($dragged, $dragZone, $(node))) { + $(node).addClass('allowedDrop'); + } } else { - origEvent.dataTransfer.setDragImage(ghostNode, xOffset, yOffset); + $(node).addClass('allowedDrop'); } } - var $dragged = $(this); - var $dragZone = $dragged.closest('.nodes').siblings().eq(0).find('.node:first'); - var $dragHier = $dragged.closest('table').find('.node'); - $dragged.closest('.orgchart') - .data('dragged', $dragged) - .find('.node').each(function (index, node) { - if ($dragHier.index(node) === -1) { - if (opts.dropCriteria) { - if (opts.dropCriteria($dragged, $dragZone, $(node))) { - $(node).addClass('allowedDrop'); - } - } else { - $(node).addClass('allowedDrop'); - } - } - }); - }) - .on('dragover', function (event) { - event.preventDefault(); - if (!$(this).is('.allowedDrop')) { - event.originalEvent.dataTransfer.dropEffect = 'none'; - } - }) - .on('dragend', function (event) { - $(this).closest('.orgchart').find('.allowedDrop').removeClass('allowedDrop'); - }) - .on('drop', function (event) { - var $dropZone = $(this); - - var $orgchart = $dropZone.closest('.orgchart'); - var $dragged = $orgchart.data('dragged'); - $orgchart.find('.allowedDrop').removeClass('allowedDrop'); - - //jacob - rewrite to meeting our business requirements, it happened when drag and ajax loading data together - var destOrgID = $dropZone.find('#OrgID').text(); - var srcOrgID = $dragged.find('#OrgID').text(); - - //use ajax to request webapi to update database - var apiTokenObj = getApiToken(); - var webApiUrl = apiTokenObj.api_host + constant.webapi.prefix + '/org/updateHierarchy?srcOrgID=' + srcOrgID + '&destOrgID=' + destOrgID; - - $.ajax({ - 'url': webApiUrl, - 'dataType': 'json', - beforeSend: function (xhr) { - console.log("Authorization", apiTokenObj.tokenType + ' ' + apiTokenObj.apiToken); - xhr.setRequestHeader("Authorization", apiTokenObj.tokenType + ' ' + apiTokenObj.apiToken); - } - }) - .done(function (data, textStatus, jqXHR) { - - //trigger the children loading of this node - //$dropZone.find('.bottomEdge').click(); - - //we encountered many bugs when using above trigger to load children, - //then we changed the following way - - expandedOrgList.push(destOrgID); - opts.initOrgChartData('', expandedOrgList); - - //var $dragZone = $dragged.closest('.nodes').siblings().eq(0).children(); - //// firstly, deal with the hierarchy of drop zone - //if (!$dropZone.closest('tr').siblings().length) { // if the drop zone is a leaf node - // //remove dragged node - // $('#subsidiary-chart').orgchart('removeNodes', $dragged); - //} else { - // var dropColspan = parseInt($dropZone.parent().attr('colspan')) + 2; - // var horizontalEdges = '<i class="edge horizontalEdge rightEdge fa"></i><i class="edge horizontalEdge leftEdge fa"></i>'; - // $dropZone.closest('tr').next().addBack().children().attr('colspan', dropColspan); - // if (!$dragged.find('.horizontalEdge').length) { - // $dragged.append(horizontalEdges); - // } - // $dropZone.closest('tr').siblings().eq(1).children(':last').before('<td class="leftLine topLine"> </td><td class="rightLine topLine"> </td>') - // .end().next().append($dragged.closest('table').parent()); - // var $dropSibs = $dragged.closest('table').parent().siblings().find('.node:first'); - // if ($dropSibs.length === 1) { - // $dropSibs.append(horizontalEdges); - // } - //} - - //// secondly, deal with the hierarchy of dragged node - //var dragColspan = parseInt($dragZone.attr('colspan')); - //if (dragColspan > 2) { - // $dragZone.attr('colspan', dragColspan - 2) - // .parent().next().children().attr('colspan', dragColspan - 2) - // .end().next().children().slice(1, 3).remove(); - // var $dragSibs = $dragZone.parent().siblings('.nodes').children().find('.node:first'); - // if ($dragSibs.length === 1) { - // $dragSibs.find('.horizontalEdge').remove(); - // } - //} else { - // $dragZone.removeAttr('colspan') - // .find('.bottomEdge').remove() - // .end().end().siblings().remove(); - //} - //$orgchart.triggerHandler({ - // 'type': 'nodedropped.orgchart', 'draggedNode': $dragged, 'dragZone': $dragZone.children(), 'dropZone': $dropZone - //}); - }) - .fail(function (jqXHR, textStatus, errorThrown) { - console.log('Failed to update dragged hierarchy'); - }) }); - } - // allow user to append dom modification after finishing node create of orgchart - if (opts.createNode) { - opts.createNode($nodeDiv, nodeData); - } - dtd.resolve($nodeDiv); - return dtd.promise(); - } - // recursively build the tree - function buildHierarchy($appendTo, nodeData, level, opts, callback) { - var $nodeWrapper; - // Construct the node - var $childNodes = nodeData.children; - var hasChildren = $childNodes ? $childNodes.length : false; - var isVerticalNode = (opts.verticalDepth && (level + 1) >= opts.verticalDepth) ? true : false; - if (Object.keys(nodeData).length > 1) { // if nodeData has nested structure - $nodeWrapper = isVerticalNode ? $appendTo : $('<table>'); - if (!isVerticalNode) { - $appendTo.append($nodeWrapper); - } - $.when(createNode(nodeData, level, opts)) - .done(function ($nodeDiv) { - if (isVerticalNode) { - $nodeWrapper.append($nodeDiv); - } else { - $nodeWrapper.append($nodeDiv.wrap('<tr><td' + (hasChildren ? ' colspan="' + $childNodes.length * 2 + '"' : '') + '></td></tr>').closest('tr')); + }, + // + dragstartHandler: function (event) { + event.originalEvent.dataTransfer.setData('text/html', 'hack for firefox'); + // if users enable zoom or direction options + if (this.$chart.css('transform') !== 'none') { + this.createGhostNode(event); + } + this.filterAllowedDropNodes($(event.target)); + }, + // + dragoverHandler: function (event) { + event.preventDefault(); + if (!$(event.delegateTarget).is('.allowedDrop')) { + event.originalEvent.dataTransfer.dropEffect = 'none'; + } + }, + // + dragendHandler: function (event) { + this.$chart.find('.allowedDrop').removeClass('allowedDrop'); + }, + // + dropHandler: function (event) { + var $dropZone = $(event.delegateTarget); + var $dragged = this.$chart.data('dragged'); + var $dragZone = $dragged.closest('.nodes').siblings().eq(0).children(); + var dropEvent = $.Event('nodedrop.orgchart'); + this.$chart.trigger(dropEvent, {'draggedNode': $dragged, 'dragZone': $dragZone.children(), 'dropZone': $dropZone}); + if (dropEvent.isDefaultPrevented()) { + return; + } + // firstly, deal with the hierarchy of drop zone + if (!$dropZone.closest('tr').siblings().length) { // if the drop zone is a leaf node + $dropZone.append('<i class="edge verticalEdge bottomEdge fa"></i>') + .parent().attr('colspan', 2) + .parent().after('<tr class="lines"><td colspan="2"><div class="downLine"></div></td></tr>' + + '<tr class="lines"><td class="rightLine"></td><td class="leftLine"></td></tr>' + + '<tr class="nodes"></tr>') + .siblings(':last').append($dragged.find('.horizontalEdge').remove().end().closest('table').parent()); + } else { + var dropColspan = parseInt($dropZone.parent().attr('colspan')) + 2; + var horizontalEdges = '<i class="edge horizontalEdge rightEdge fa"></i><i class="edge horizontalEdge leftEdge fa"></i>'; + $dropZone.closest('tr').next().addBack().children().attr('colspan', dropColspan); + if (!$dragged.find('.horizontalEdge').length) { + $dragged.append(horizontalEdges); } - if (callback) { - callback(); + $dropZone.closest('tr').siblings().eq(1).children(':last').before('<td class="leftLine topLine"></td><td class="rightLine topLine"></td>') + .end().next().append($dragged.closest('table').parent()); + var $dropSibs = $dragged.closest('table').parent().siblings().find('.node:first'); + if ($dropSibs.length === 1) { + $dropSibs.append(horizontalEdges); } - }) - .fail(function () { - console.log('Failed to creat node') - }); - } - // Construct the inferior nodes and connectiong lines - if (hasChildren) { - if (Object.keys(nodeData).length === 1) { // if nodeData is just an array - $nodeWrapper = $appendTo; } - var isHidden = (level + 1 >= opts.depth || nodeData.collapsed) ? ' hidden' : ''; - var isVerticalLayer = (opts.verticalDepth && (level + 2) >= opts.verticalDepth) ? true : false; - - // draw the line close to parent node - if (!isVerticalLayer) { - $nodeWrapper.append('<tr class="lines' + isHidden + '"><td colspan="' + $childNodes.length * 2 + '"><div class="downLine"></div></td></tr>'); - } - // draw the lines close to children nodes - var lineLayer = '<tr class="lines' + isHidden + '"><td class="rightLine"> </td>'; - for (var i = 1; i < $childNodes.length; i++) { - lineLayer += '<td class="leftLine topLine"> </td><td class="rightLine topLine"> </td>'; - } - lineLayer += '<td class="leftLine"> </td></tr>'; - var $nodeLayer; - if (isVerticalLayer) { - $nodeLayer = $('<ul>'); - if (isHidden) { - $nodeLayer.addClass(isHidden); + // secondly, deal with the hierarchy of dragged node + var dragColspan = parseInt($dragZone.attr('colspan')); + if (dragColspan > 2) { + $dragZone.attr('colspan', dragColspan - 2) + .parent().next().children().attr('colspan', dragColspan - 2) + .end().next().children().slice(1, 3).remove(); + var $dragSibs = $dragZone.parent().siblings('.nodes').children().find('.node:first'); + if ($dragSibs.length === 1) { + $dragSibs.find('.horizontalEdge').remove(); } - if (level + 2 === opts.verticalDepth) { - $nodeWrapper.append('<tr class="verticalNodes' + isHidden + '"><td></td></tr>') - .find('.verticalNodes').children().append($nodeLayer); + } else { + $dragZone.removeAttr('colspan') + .find('.bottomEdge').remove() + .end().end().siblings().remove(); + } + }, + // + touchstartHandler: function (event) { + console.log("orgChart: touchstart 1: touchHandled=" + this.touchHandled + ", touchMoved=" + this.touchMoved + ", target=" + event.target.innerText); + if (this.touchHandled) + return; + this.touchHandled = true; + this.touchMoved = false; // this is so we can work out later if this was a 'press' or a 'drag' touch + event.preventDefault(); + }, + // + touchmoveHandler: function (event) { + if (!this.touchHandled) + return; + event.preventDefault(); + if (!this.touchMoved) { + var nodeIsSelected = $(this).hasClass('focused'); + console.log("orgChart: touchmove 1: " + event.touches.length + " touches, we have not moved, so simulate a drag start", event.touches); + // TODO: visualise the start of the drag (as would happen on desktop) + this.simulateMouseEvent(event, 'dragstart'); + } + this.touchMoved = true; + var $touching = $(document.elementFromPoint(event.touches[0].clientX, event.touches[0].clientY)); + var $touchingNode = $touching.closest('div.node'); + + if ($touchingNode.length > 0) { + var touchingNodeElement = $touchingNode[0]; + // TODO: simulate the dragover visualisation + if ($touchingNode.is('.allowedDrop')) { + console.log("orgChart: touchmove 2: this node (" + touchingNodeElement.id + ") is allowed to be a drop target"); + this.touchTargetNode = touchingNodeElement; } else { - $nodeWrapper.append($nodeLayer); + console.log("orgChart: touchmove 3: this node (" + touchingNodeElement.id + ") is NOT allowed to be a drop target"); + this.touchTargetNode = null; } } else { - $nodeLayer = $('<tr class="nodes' + isHidden + '">'); - $nodeWrapper.append(lineLayer).append($nodeLayer); - } - // recurse through children nodes - $.each($childNodes, function () { - var $nodeCell = isVerticalLayer ? $('<li>') : $('<td colspan="2">'); - $nodeLayer.append($nodeCell); - buildHierarchy($nodeCell, this, level + 1, opts, callback); - }); - } - } - - // build the child nodes of specific node - function buildChildNode($appendTo, nodeData, opts, callback) { - var opts = opts || $appendTo.closest('.orgchart').data('options'); - var data = nodeData.children || nodeData.siblings; - $appendTo.find('td:first').attr('colspan', data.length * 2); - buildHierarchy($appendTo, { 'children': data }, 0, opts, callback); - } - // exposed method - function addChildren($node, data, opts) { - var opts = opts || $node.closest('.orgchart').data('options'); - var count = 0; - buildChildNode.call($node.closest('.orgchart').parent(), $node.closest('table'), data, opts, function () { - if (++count === data.children.length) { - if (!$node.children('.bottomEdge').length) { - $node.append('<i class="edge verticalEdge bottomEdge fa"></i>'); + console.log("orgchart: touchmove 4: not touching a node"); + this.touchTargetNode = null; + } + }, + // + touchendHandler: function (event) { + console.log("orgChart: touchend 1: touchHandled=" + this.touchHandled + ", touchMoved=" + this.touchMoved + ", " + event.target.innerText + " "); + if (!this.touchHandled) { + console.log("orgChart: touchend 2: not handled by us, so aborting"); + return; + } + if (this.touchMoved) { + // we've had movement, so this was a 'drag' touch + if (this.touchTargetNode) { + console.log("orgChart: touchend 3: moved to a node, so simulating drop"); + var fakeEventForDropHandler = {delegateTarget: this.touchTargetNode}; + this.dropHandler(fakeEventForDropHandler); + this.touchTargetNode = null; + } + console.log("orgChart: touchend 4: simulating dragend"); + this.simulateMouseEvent(event, 'dragend'); + } + else { + // we did not move, so assume this was a 'press' touch + console.log("orgChart: touchend 5: moved, so simulating click"); + this.simulateMouseEvent(event, 'click'); + } + this.touchHandled = false; + }, + // simulate a mouse event (so we can fake them on a touch device) + simulateMouseEvent: function (event, simulatedType) { + // Ignore multi-touch events + if (event.originalEvent.touches.length > 1) { + return; + } + var touch = event.originalEvent.changedTouches[0]; + var simulatedEvent = document.createEvent('MouseEvents'); + simulatedEvent.initMouseEvent( + simulatedType, // type + true, // bubbles + true, // cancelable + window, // view + 1, // detail + touch.screenX, // screenX + touch.screenY, // screenY + touch.clientX, // clientX + touch.clientY, // clientY + false, // ctrlKey + false, // altKey + false, // shiftKey + false, // metaKey + 0, // button + null // relatedTarget + ); + // Dispatch the simulated event to the target element + event.target.dispatchEvent(simulatedEvent); + }, + // + bindDragDrop: function ($node) { + $node.on('dragstart', this.dragstartHandler.bind(this)) + .on('dragover', this.dragoverHandler.bind(this)) + .on('dragend', this.dragendHandler.bind(this)) + .on('drop', this.dropHandler.bind(this)) + .on('touchstart', this.touchstartHandler.bind(this)) + .on('touchmove', this.touchmoveHandler.bind(this)) + .on('touchend', this.touchendHandler.bind(this)); + }, + // create node + createNode: function (data) { + var that = this; + var opts = this.options; + var level = data.level; + if (data.children) { + $.each(data.children, function (index, child) { + child.parentId = data.id; + }); + } + // construct the content of node + var $nodeDiv = $('<div' + (opts.draggable ? ' draggable="true"' : '') + (data[opts.nodeId] ? ' id="' + data[opts.nodeId] + '"' : '') + (data.parentId ? ' data-parent="' + data.parentId + '"' : '') + '>') + .addClass('node ' + (data.className || '') + (level > opts.visibleLevel ? ' slide-up' : '')); + if (opts.nodeTemplate) { + $nodeDiv.append(opts.nodeTemplate(data)); + } else { + $nodeDiv.append('<div class="title">' + data[opts.nodeTitle] + '</div>') + .append(typeof opts.nodeContent !== 'undefined' ? '<div class="content">' + (data[opts.nodeContent] || '') + '</div>' : ''); + } + // + var nodeData = $.extend({}, data); + delete nodeData.children; + $nodeDiv.data('nodeData', nodeData); + // append 4 direction arrows or expand/collapse buttons + var flags = data.relationship || ''; + if (opts.verticalLevel && level >= opts.verticalLevel) { + if ((level + 1) > opts.verticalLevel && Number(flags.substr(2, 1))) { + var icon = level + 1 > opts.visibleLevel ? 'plus' : 'minus'; + $nodeDiv.append('<i class="toggleBtn fa fa-' + icon + '-square"></i>'); + } + } else { + if (Number(flags.substr(0, 1))) { + $nodeDiv.append('<i class="edge verticalEdge topEdge fa"></i>'); + } + if (Number(flags.substr(1, 1))) { + $nodeDiv.append('<i class="edge horizontalEdge rightEdge fa"></i>' + + '<i class="edge horizontalEdge leftEdge fa"></i>'); } - if (!$node.find('.symbol').length) { - $node.children('.title').prepend('<i class="fa ' + opts.parentNodeSymbol + ' symbol"></i>'); + if (Number(flags.substr(2, 1))) { + $nodeDiv.append('<i class="edge verticalEdge bottomEdge fa"></i>') + .children('.title').prepend('<i class="fa ' + opts.parentNodeSymbol + ' symbol"></i>'); } - showChildren($node); } - }); - } - - // build the parent node of specific node - function buildParentNode($currentRoot, nodeData, opts, callback) { - var that = this; - var $table = $('<table>'); - nodeData.relationship = nodeData.relationship || '001'; - $.when(createNode(nodeData, 0, opts || $currentRoot.closest('.orgchart').data('options'))) - .done(function ($nodeDiv) { - $table.append($nodeDiv.removeClass('slide-up').addClass('slide-down').wrap('<tr class="hidden"><td colspan="2"></td></tr>').closest('tr')); - $table.append('<tr class="lines hidden"><td colspan="2"><div class="downLine"></div></td></tr>'); - var linesRow = '<td class="rightLine"> </td><td class="leftLine"> </td>'; - $table.append('<tr class="lines hidden">' + linesRow + '</tr>'); - var $oc = that.children('.orgchart'); - $oc.prepend($table) - .children('table:first').append('<tr class="nodes"><td colspan="2"></td></tr>') - .children('tr:last').children().append($oc.children('table').last()); - callback(); - }) - .fail(function () { - console.log('Failed to create parent node'); - }); - } - // exposed method - function addParent($currentRoot, data, opts) { - buildParentNode.call(this, $currentRoot, data, opts, function () { - if (!$currentRoot.children('.topEdge').length) { - $currentRoot.children('.title').after('<i class="edge verticalEdge topEdge fa"></i>'); + $nodeDiv.on('mouseenter mouseleave', this.nodeEnterLeaveHandler.bind(this)); + $nodeDiv.on('click', this.nodeClickHandler.bind(this)); + $nodeDiv.on('click', '.topEdge', this.topEdgeClickHandler.bind(this)); + $nodeDiv.on('click', '.bottomEdge', this.bottomEdgeClickHandler.bind(this)); + $nodeDiv.on('click', '.leftEdge, .rightEdge', this.hEdgeClickHandler.bind(this)); + $nodeDiv.on('click', '.toggleBtn', this.toggleVNodes.bind(this)); + + if (opts.draggable) { + this.bindDragDrop($nodeDiv); + this.touchHandled = false; + this.touchMoved = false; + this.touchTargetNode = null; + } + // allow user to append dom modification after finishing node create of orgchart + if (opts.createNode) { + opts.createNode($nodeDiv, data); } - showParent($currentRoot); - }); - } - - // subsequent processing of build sibling nodes - function complementLine($oneSibling, siblingCount, existingSibligCount) { - var lines = ''; - for (var i = 0; i < existingSibligCount; i++) { - lines += '<td class="leftLine topLine"> </td><td class="rightLine topLine"> </td>'; - } - $oneSibling.parent().prevAll('tr:gt(0)').children().attr('colspan', siblingCount * 2) - .end().next().children(':first').after(lines); - } - // build the sibling nodes of specific node - function buildSiblingNode($nodeChart, nodeData, opts, callback) { - var opts = opts || $nodeChart.closest('.orgchart').data('options'); - var newSiblingCount = nodeData.siblings ? nodeData.siblings.length : nodeData.children.length; - var existingSibligCount = $nodeChart.parent().is('td') ? $nodeChart.closest('tr').children().length : 1; - var siblingCount = existingSibligCount + newSiblingCount; - var insertPostion = (siblingCount > 1) ? Math.floor(siblingCount / 2 - 1) : 0; - // just build the sibling nodes for the specific node - if ($nodeChart.parent().is('td')) { - var $parent = $nodeChart.closest('tr').prevAll('tr:last'); - $nodeChart.closest('tr').prevAll('tr:lt(2)').remove(); - var childCount = 0; - buildChildNode.call($nodeChart.closest('.orgchart').parent(), $nodeChart.parent().closest('table'), nodeData, opts, function () { - if (++childCount === newSiblingCount) { - var $siblingTds = $nodeChart.parent().closest('table').children('tr:last').children('td'); - if (existingSibligCount > 1) { - complementLine($siblingTds.eq(0).before($nodeChart.closest('td').siblings().addBack().unwrap()), siblingCount, existingSibligCount); - $siblingTds.addClass('hidden').find('.node').addClass('slide-left'); + return $nodeDiv; + }, + // recursively build the tree + buildHierarchy: function ($appendTo, data) { + var that = this; + var opts = this.options; + var level = 0; + if (data.level) { + level = data.level; + } else { + level = data.level = $appendTo.parentsUntil('.orgchart', '.nodes').length + 1; + } + // Construct the node + var childrenData = data.children; + var hasChildren = childrenData ? childrenData.length : false; + var $nodeWrapper; + if (Object.keys(data).length > 2) { + var $nodeDiv = this.createNode(data); + if (opts.verticalLevel && level >= opts.verticalLevel) { + $appendTo.append($nodeDiv); + } else { + $nodeWrapper = $('<table>'); + $appendTo.append($nodeWrapper.append($('<tr/>').append($('<td' + (hasChildren ? ' colspan="' + childrenData.length * 2 + '"' : '') + '></td>').append($nodeDiv)))); + } + } + // Construct the lower level(two "connectiong lines" rows and "inferior nodes" row) + if (hasChildren) { + var isHidden = (level + 1 > opts.visibleLevel || data.collapsed) ? ' hidden' : ''; + var isVerticalLayer = (opts.verticalLevel && (level + 1) >= opts.verticalLevel) ? true : false; + var $nodesLayer; + if (isVerticalLayer) { + $nodesLayer = $('<ul>'); + if (isHidden && level + 1 > opts.verticalLevel) { + $nodesLayer.addClass(isHidden); + } + if (level + 1 === opts.verticalLevel) { + $appendTo.children('table').append('<tr class="verticalNodes' + isHidden + '"><td></td></tr>') + .find('.verticalNodes').children().append($nodesLayer); + } else { + $appendTo.append($nodesLayer); + } + } else { + var $upperLines = $('<tr class="lines' + isHidden + '"><td colspan="' + childrenData.length * 2 + '"><div class="downLine"></div></td></tr>'); + var lowerLines = '<tr class="lines' + isHidden + '"><td class="rightLine"></td>'; + for (var i = 1; i < childrenData.length; i++) { + lowerLines += '<td class="leftLine topLine"></td><td class="rightLine topLine"></td>'; + } + lowerLines += '<td class="leftLine"></td></tr>'; + $nodesLayer = $('<tr class="nodes' + isHidden + '">'); + if (Object.keys(data).length === 2) { + $appendTo.append($upperLines).append(lowerLines).append($nodesLayer); } else { - complementLine($siblingTds.eq(insertPostion).after($nodeChart.closest('td').unwrap()), siblingCount, 1); - $siblingTds.not(':eq(' + insertPostion + 1 + ')').addClass('hidden') - .slice(0, insertPostion).find('.node').addClass('slide-right') - .end().end().slice(insertPostion).find('.node').addClass('slide-left'); + $nodeWrapper.append($upperLines).append(lowerLines).append($nodesLayer); } - callback(); } - }); - } else { // build the sibling nodes and parent node for the specific ndoe - var nodeCount = 0; - buildHierarchy($nodeChart.closest('.orgchart'), nodeData, 0, opts, function () { - if (++nodeCount === siblingCount) { - complementLine($nodeChart.next().children('tr:last') - .children().eq(insertPostion).after($('<td colspan="2">') - .append($nodeChart)), siblingCount, 1); - $nodeChart.closest('tr').siblings().eq(0).addClass('hidden').find('.node').addClass('slide-down'); - $nodeChart.parent().siblings().addClass('hidden') - .slice(0, insertPostion).find('.node').addClass('slide-right') - .end().end().slice(insertPostion).find('.node').addClass('slide-left'); - callback(); + // recurse through children nodes + $.each(childrenData, function () { + var $nodeCell = isVerticalLayer ? $('<li>') : $('<td colspan="2">'); + $nodesLayer.append($nodeCell); + this.level = level + 1; + that.buildHierarchy($nodeCell, this); + }); + } + }, + // build the child nodes of specific node + buildChildNode: function ($appendTo, data) { + $appendTo.find('td:first').attr('colspan', data.length * 2); + this.buildHierarchy($appendTo, {'children': data}); + }, + // exposed method + addChildren: function ($node, data) { + this.buildChildNode($node.closest('table'), data); + if (!$node.children('.bottomEdge').length) { + $node.append('<i class="edge verticalEdge bottomEdge fa"></i>'); + } + if (!$node.find('.symbol').length) { + $node.children('.title').prepend('<i class="fa ' + this.options.parentNodeSymbol + ' symbol"></i>'); + } + if (this.isInAction($node)) { + this.switchVerticalArrow($node.children('.bottomEdge')); + } + }, + // build the parent node of specific node + buildParentNode: function ($currentRoot, data) { + data.relationship = data.relationship || '001'; + var $table = $('<table>') + .append($('<tr>').append($('<td colspan="2">').append(this.createNode(data)))) + .append('<tr class="lines"><td colspan="2"><div class="downLine"></div></td></tr>') + .append('<tr class="lines"><td class="rightLine"></td><td class="leftLine"></td></tr>'); + this.$chart.prepend($table) + .children('table:first').append('<tr class="nodes"><td colspan="2"></td></tr>') + .children('tr:last').children().append(this.$chart.children('table').last()); + }, + // exposed method + addParent: function ($currentRoot, data) { + this.buildParentNode($currentRoot, data); + if (!$currentRoot.children('.topEdge').length) { + $currentRoot.children('.title').after('<i class="edge verticalEdge topEdge fa"></i>'); + } + if (this.isInAction($currentRoot)) { + this.switchVerticalArrow($currentRoot.children('.topEdge')); + } + }, + // subsequent processing of build sibling nodes + complementLine: function ($oneSibling, siblingCount, existingSibligCount) { + var lines = ''; + for (var i = 0; i < existingSibligCount; i++) { + lines += '<td class="leftLine topLine"></td><td class="rightLine topLine"></td>'; + } + $oneSibling.parent().prevAll('tr:gt(0)').children().attr('colspan', siblingCount * 2) + .end().next().children(':first').after(lines); + }, + // build the sibling nodes of specific node + buildSiblingNode: function ($nodeChart, data) { + var newSiblingCount = $.isArray(data) ? data.length : data.children.length; + var existingSibligCount = $nodeChart.parent().is('td') ? $nodeChart.closest('tr').children().length : 1; + var siblingCount = existingSibligCount + newSiblingCount; + var insertPostion = (siblingCount > 1) ? Math.floor(siblingCount / 2 - 1) : 0; + // just build the sibling nodes for the specific node + if ($nodeChart.parent().is('td')) { + var $parent = $nodeChart.closest('tr').prevAll('tr:last'); + $nodeChart.closest('tr').prevAll('tr:lt(2)').remove(); + this.buildChildNode($nodeChart.parent().closest('table'), data); + var $siblingTds = $nodeChart.parent().closest('table').children('tr:last').children('td'); + if (existingSibligCount > 1) { + this.complementLine($siblingTds.eq(0).before($nodeChart.closest('td').siblings().addBack().unwrap()), siblingCount, existingSibligCount); + } else { + this.complementLine($siblingTds.eq(insertPostion).after($nodeChart.closest('td').unwrap()), siblingCount, 1); } - }); - } - } - - function addSiblings($node, data, opts) { - buildSiblingNode.call($node.closest('.orgchart').parent(), $node.closest('table'), data, opts, function () { + } else { // build the sibling nodes and parent node for the specific ndoe + this.buildHierarchy($nodeChart.closest('.orgchart'), data); + this.complementLine($nodeChart.next().children('tr:last').children().eq(insertPostion).after($('<td colspan="2">').append($nodeChart)), + siblingCount, 1); + } + }, + // + addSiblings: function ($node, data) { + this.buildSiblingNode($node.closest('table'), data); $node.closest('.nodes').data('siblingsLoaded', true); if (!$node.children('.leftEdge').length) { $node.children('.topEdge').after('<i class="edge horizontalEdge rightEdge fa"></i><i class="edge horizontalEdge leftEdge fa"></i>'); } - showSiblings($node); - }); - } - - function removeNodes($node) { - var $parent = $node.closest('table').parent(); - var $sibs = $parent.parent().siblings(); - if ($parent.is('td')) { - if (getNodeState($node, 'siblings').exist) { - $sibs.eq(2).children('.topLine:lt(2)').remove(); - $sibs.slice(0, 2).children().attr('colspan', $sibs.eq(2).children().length); - $parent.remove(); + if (this.isInAction($node)) { + this.switchHorizontalArrow($node); + $node.children('.topEdge').removeClass('fa-chevron-up').addClass('fa-chevron-down'); + } + }, + // + removeNodes: function ($node) { + var $parent = $node.closest('table').parent(); + var $sibs = $parent.parent().siblings(); + if ($parent.is('td')) { + if (this.getNodeState($node, 'siblings').exist) { + $sibs.eq(2).children('.topLine:lt(2)').remove(); + $sibs.slice(0, 2).children().attr('colspan', $sibs.eq(2).children().length); + $parent.remove(); + } else { + $sibs.eq(0).children().removeAttr('colspan') + .find('.bottomEdge').remove() + .end().end().siblings().remove(); + } } else { - $sibs.eq(0).children().removeAttr('colspan') - .find('.bottomEdge').remove() - .end().end().siblings().remove(); + $parent.add($parent.siblings()).remove(); + } + }, + // + export: function (exportFilename, exportFileextension) { + var that = this; + exportFilename = (typeof exportFilename !== 'undefined') ? exportFilename : this.options.exportFilename; + exportFileextension = (typeof exportFileextension !== 'undefined') ? exportFileextension : this.options.exportFileextension; + if ($(this).children('.spinner').length) { + return false; } - } else { - $parent.add($parent.siblings()).remove(); + var $chartContainer = this.$chartContainer; + var $mask = $chartContainer.find('.mask'); + if (!$mask.length) { + $chartContainer.append('<div class="mask"><i class="fa fa-circle-o-notch fa-spin spinner"></i></div>'); + } else { + $mask.removeClass('hidden'); + } + var sourceChart = $chartContainer.addClass('canvasContainer').find('.orgchart:not(".hidden")').get(0); + var flag = that.options.direction === 'l2r' || that.options.direction === 'r2l'; + html2canvas(sourceChart, { + 'width': flag ? sourceChart.clientHeight : sourceChart.clientWidth, + 'height': flag ? sourceChart.clientWidth : sourceChart.clientHeight, + 'onclone': function (cloneDoc) { + $(cloneDoc).find('.canvasContainer').css('overflow', 'visible') + .find('.orgchart:not(".hidden"):first').css('transform', ''); + }, + 'onrendered': function (canvas) { + $chartContainer.find('.mask').addClass('hidden'); + if (exportFileextension.toLowerCase() === 'pdf') { + var doc = {}; + var docWidth = Math.floor(canvas.width * 0.2646); + var docHeight = Math.floor(canvas.height * 0.2646); + if (docWidth > docHeight) { + doc = new jsPDF('l', 'mm', [docWidth, docHeight]); + } else { + doc = new jsPDF('p', 'mm', [docHeight, docWidth]); + } + doc.addImage(canvas.toDataURL(), 'png', 0, 0); + doc.save(exportFilename + '.pdf'); + } else { + var isWebkit = 'WebkitAppearance' in document.documentElement.style; + var isFf = !!window.sidebar; + var isEdge = navigator.appName === 'Microsoft Internet Explorer' || (navigator.appName === "Netscape" && navigator.appVersion.indexOf('Edge') > -1); + + if ((!isWebkit && !isFf) || isEdge) { + window.navigator.msSaveBlob(canvas.msToBlob(), exportFilename + '.png'); + } else { + var selector = '.oc-download-btn' + (that.options.chartClass !== '' ? '.' + that.options.chartClass : ''); + if (!$chartContainer.find(selector).length) { + $chartContainer.append('<a class="oc-download-btn' + (that.options.chartClass !== '' ? ' ' + that.options.chartClass : '') + '"' + + ' download="' + exportFilename + '.png"></a>'); + } + $chartContainer.find(selector).attr('href', canvas.toDataURL())[0].click(); + } + } + } + }) + .then(function () { + $chartContainer.removeClass('canvasContainer'); + }, function () { + $chartContainer.removeClass('canvasContainer'); + }); } - } + }; + + $.fn.orgchart = function (opts) { + return new OrgChart(this, opts).init(); + }; })); diff --git a/atms-web/src/main/webapp/dist/index.html b/atms-web/src/main/webapp/dist/index.html index 43120a70d39d9368e7c5f599455dd9c2cfcc8cae..defe87a88b31c52f9f66cd9079dc110316814ec7 100644 --- a/atms-web/src/main/webapp/dist/index.html +++ b/atms-web/src/main/webapp/dist/index.html @@ -4,7 +4,7 @@ <meta charset="UTF-8"> <title>2016 NBA Playoff Picture </title> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'> - <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/orgchart/2.1.3/css/jquery.orgchart.min.css'> + <link rel='stylesheet' href='../Content/orgChart/jquery.orgchart.css'> <link rel="stylesheet" href="./style.css"> </head> <body> @@ -12,7 +12,7 @@ <div id="chart-eastern" class="chart-container" style="text-align: left;"></div> <!-- <a id="github-link" href="https://github.com/dabeng/OrgChart" target="_blank"><i class="fa fa-github-square"></i></a> --> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script> - <script src='https://cdnjs.cloudflare.com/ajax/libs/orgchart/2.1.3/js/jquery.orgchart.min.js'></script> + <script src='../Scripts/orgChart/jquery.orgchart.js'></script> <script src="./script.js"></script> </body> </html> \ No newline at end of file diff --git a/atms-web/src/main/webapp/dist2/index.html b/atms-web/src/main/webapp/dist2/index.html index c5d02792cce7accc03f8a77c1dce9baada33553f..dcf2906276a41a88b286b519f5cce3832415d02b 100644 --- a/atms-web/src/main/webapp/dist2/index.html +++ b/atms-web/src/main/webapp/dist2/index.html @@ -8,6 +8,15 @@ </head> <body> <div id="tree"></div> +<canvas id="canvas" width="1000px" height="600px"></canvas> +<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script> +<!-- Required to convert named colors to RGB --> +<script src="https://cdnjs.cloudflare.com/ajax/libs/canvg/1.4/rgbcolor.min.js"></script> +<!-- Optional if you want blur --> +<script src="https://cdn.jsdelivr.net/npm/stackblur-canvas@^1/dist/stackblur.min.js"></script> +<!-- Main canvg code --> +<script src="https://cdn.jsdelivr.net/npm/canvg/dist/browser/canvg.min.js"></script> +<script src="./d3.min.js"></script> <script src="./orgchart.js"></script> <script src="./script.js"></script> </body> diff --git a/atms-web/src/main/webapp/dist2/script.js b/atms-web/src/main/webapp/dist2/script.js index cf0c22e54e97747d742dec221ff1758868912855..21e886002b9423639cf6326c764ea44c16b56d98 100644 --- a/atms-web/src/main/webapp/dist2/script.js +++ b/atms-web/src/main/webapp/dist2/script.js @@ -1,19 +1,20 @@ -OrgChart.templates.myTemplate = Object.assign({}, OrgChart.templates.mery); -OrgChart.templates.myTemplate.size = [400, 400]; -OrgChart.templates.myTemplate.node = '<rect width="400" height="200" style="fill:rgb(255,119,48);stroke-width:3px;stroke:rgba(153,10,10,0.01)" />'; -OrgChart.templates.myTemplate.ripple = { - radius: 100, - color: "#0890D3", - rect: null -}; - -OrgChart.templates.myTemplate.field_0 = '<text style="font-size: 24px;" fill="#ffffff" x="100" y="90" text-anchor="middle">{val}</text>'; +OrgChart.templates.myTemplate = Object.assign({}, OrgChart.templates.ana); +OrgChart.templates.myTemplate.size = [200, 120]; +OrgChart.templates.myTemplate.node = '<rect width="200" height="100" style="fill:rgb(35,143,159);stroke-width:3px;stroke:rgba(153,10,10,0.01)" />'; +// OrgChart.templates.myTemplate.ripple = { +// radius: 100, +// color: "#0890D3", +// rect: null +// }; + +OrgChart.templates.myTemplate.field_0 = '<text style="font-size: 10px;" fill="#ffffff" x="100" y="90" text-anchor="middle">{val}</text>'; OrgChart.templates.myTemplate.field_1 = '<text style="font-size: 16px;" fill="#ffffff" x="100" y="60" text-anchor="middle">{val}</text>'; OrgChart.templates.myTemplate.img_0 = '<clipPath id="ulaImg"><circle cx="100" cy="150" r="0"></circle></clipPath>' + '<image preserveAspectRatio="xMidYMid slice" clip-path="url(#ulaImg)" xlink:href="{val}" x="60" y="110" width="0" height="0"></image>'; OrgChart.templates.myTemplate.link = '<path stroke="#686868" stroke-width="1px" fill="none" link-id="[{id}][{child-id}]" d="M{xa},{ya} L{xb},{yb} {xc},{yc} {xd},{yd}" />'; -OrgChart.templates.myTemplate.edge = '<path stroke="#686868" stroke-width="1px" fill="none" edge-id="[{id}][{child-id}]" d="M{xa},{ya} C{xb},{yb} {xc},{yc} {xd},{yd}"/>'; +OrgChart.templates.myTemplate.secondlink = '<path stroke="#686868" stroke-width="1px" fill="none" link-id="[{id}][{child-id}]" d="M{xa},{ya} L{xb},{yb} {xc},{yc} {xd},{yd}" />'; +// OrgChart.templates.myTemplate.edge = '<path stroke="#686868" stroke-width="1px" fill="none" edge-id="[{id}][{child-id}]" d="M{xa},{ya} C{xb},{yb} {xc},{yc} {xd},{yd}"/>'; OrgChart.templates.myTemplate.plus = '<rect x="0" y="0" width="36" height="36" rx="12" ry="12" fill="#2E2E2E" stroke="#aeaeae" stroke-width="1"></rect>' @@ -24,40 +25,41 @@ OrgChart.templates.myTemplate.minus = '<rect x="0" y="0" width="36" height="36" rx="12" ry="12" fill="#2E2E2E" stroke="#aeaeae" stroke-width="1"></rect>' + '<line x1="4" y1="18" x2="32" y2="18" stroke-width="1" stroke="#aeaeae"></line>'; -OrgChart.templates.myTemplate.expandCollapseSize = 36; - -OrgChart.templates.myTemplate.nodeMenuButton = '<g style="cursor:pointer;" transform="matrix(1,0,0,1,93,15)" control-node-menu-id="{id}">' + - '<rect x="-4" y="-10" fill="#000000" fill-opacity="0" width="22" height="22"></rect>' + - '<line x1="0" y1="0" x2="0" y2="10" stroke-width="2" stroke="#0890D3" />' + - '<line x1="7" y1="0" x2="7" y2="10" stroke-width="2" stroke="#0890D3" />' + - '<line x1="14" y1="0" x2="14" y2="10" stroke-width="2" stroke="#0890D3" /></g>'; - -OrgChart.templates.myTemplate.exportMenuButton = '<div style="position:absolute;right:{p}px;top:{p}px; width:40px;height:50px;cursor:pointer;" control-export-menu="">' + - '<hr style="background-color: #0890D3; height: 3px; border: none;">' + - '<hr style="background-color: #0890D3; height: 3px; border: none;">' + - '<hr style="background-color: #0890D3; height: 3px; border: none;"></div>'; - -OrgChart.templates.myTemplate.pointer = '<g data-pointer="pointer" transform="matrix(0,0,0,0,100,100)">' + - '<g transform="matrix(0.3,0,0,0.3,-17,-17)">' + - '<polygon fill="#0890D3" points="53.004,173.004 53.004,66.996 0,120"/>' + - '<polygon fill="#0890D3" points="186.996,66.996 186.996,173.004 240,120"/>' + - '<polygon fill="#0890D3" points="66.996,53.004 173.004,53.004 120,0"/>' + - '<polygon fill="#0890D3" points="120,240 173.004,186.996 66.996,186.996"/>' + - '<circle fill="#0890D3" cx="120" cy="120" r="30"/></g></g>'; -OrgChart.templates.ana.field_0 = '<text class="field_0" style="font-size: 20px;" fill="#ffffff" x="125" y="60" text-anchor="middle">{val}</text>'; +// OrgChart.templates.myTemplate.expandCollapseSize = 10; + +// OrgChart.templates.myTemplate.nodeMenuButton = '<g style="cursor:pointer;" transform="matrix(1,0,0,1,93,15)" control-node-menu-id="{id}">' + +// '<rect x="-4" y="-10" fill="#000000" fill-opacity="0" width="22" height="22"></rect>' + +// '<line x1="0" y1="0" x2="0" y2="10" stroke-width="2" stroke="#0890D3" />' + +// '<line x1="7" y1="0" x2="7" y2="10" stroke-width="2" stroke="#0890D3" />' + +// '<line x1="14" y1="0" x2="14" y2="10" stroke-width="2" stroke="#0890D3" /></g>'; + +// OrgChart.templates.myTemplate.exportMenuButton = '<div style="position:absolute;right:{p}px;top:{p}px; width:40px;height:50px;cursor:pointer;" control-export-menu="">' + +// '<hr style="background-color: #0890D3; height: 3px; border: none;">' + +// '<hr style="background-color: #0890D3; height: 3px; border: none;">' + +// '<hr style="background-color: #0890D3; height: 3px; border: none;"></div>'; + +// OrgChart.templates.myTemplate.pointer = '<g data-pointer="pointer" transform="matrix(0,0,0,0,100,100)">' + +// '<g transform="matrix(0.3,0,0,0.3,-17,-17)">' + +// '<polygon fill="#0890D3" points="53.004,173.004 53.004,66.996 0,120"/>' + +// '<polygon fill="#0890D3" points="186.996,66.996 186.996,173.004 240,120"/>' + +// '<polygon fill="#0890D3" points="66.996,53.004 173.004,53.004 120,0"/>' + +// '<polygon fill="#0890D3" points="120,240 173.004,186.996 66.996,186.996"/>' + +// '<circle fill="#0890D3" cx="120" cy="120" r="30"/></g></g>'; +// OrgChart.templates.ana.field_0 = '<text class="field_0" style="font-size: 20px;" fill="#ffffff" x="125" y="60" text-anchor="middle">{val}</text>'; var chart = new OrgChart(document.getElementById("tree"), { + template: "myTemplate", enableSearch: false, - // nodeMenu: { - // add: {text: "Add"}, - // edit: {text: "Edit"}, - // remove: {text: "Remove"} - // }, - // menu: { - // pdf: {text: "Export PDF"}, - // png: {text: "Export PNG"}, - // svg: {text: "Export SVG"}, - // csv: {text: "Export CSV"} - // }, + nodeMenu: { + add: {text: "Add"}, + edit: {text: "Edit"}, + remove: {text: "Remove"} + }, + menu: { + pdf: {text: "Export PDF"}, + png: {text: "Export PNG"}, + svg: {text: "Export SVG"}, + csv: {text: "Export CSV"} + }, linkBinding: { link_field_0: "percent" }, @@ -67,8 +69,133 @@ var chart = new OrgChart(document.getElementById("tree"), { // img_0: "img" }, nodes: [ - {id: 1, name: "Amber McKenzie", title: "CEO", img: "//balkangraph.com/js/img/1.jpg",percent:"100%"}, - {id: 2, pid: 1, name: "Ava Field", title: "IT Manager", img: "//balkangraph.com/js/img/2.jpg", mobile: "0878108255",percent:"100%"}, - {id: 3, pid: 1, name: "Peter Stevens", title: "HR Manager", img: "//balkangraph.com/js/img/3.jpg",percent:"100%"} - ] -}); \ No newline at end of file + {id: 4, name: "Amber McKenzie", title: "CEO", img: "//balkangraph.com/js/img/1.jpg", percent: "100%"}, + {id: 5, name: "Amber McKenzie", title: "CEO", img: "//balkangraph.com/js/img/1.jpg", percent: "100%"}, + {id: 6, name: "Amber McKenzie", title: "CEO", img: "//balkangraph.com/js/img/1.jpg", percent: "100%"}, + {id: 1, pid: 4, spids: [5,6], name: "Amber McKenzie", title: "CEO", img: "//balkangraph.com/js/img/1.jpg", percent: "100%"}, + {id: 2, pid: 1, name: "Ava Field", title: "IT Manager", img: "//balkangraph.com/js/img/2.jpg", mobile: "0878108255", percent: "100%"}, + {id: 3, pid: 1, name: "Peter Stevens", title: "HR Manager", img: "//balkangraph.com/js/img/3.jpg", percent: "100%"} + + ], + onExportStart: exportStart +}); + +// d3.select("svg").append("circle") +// .attr("r", "10") +// .attr("style", "fill:white;stroke:black;stroke-width:5"); +function makeSVG(tag, attrs) { + var el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (var k in attrs) + el.setAttribute(k, attrs[k]); + return el; +} + +var initialized = false; + + +// var circle = makeSVG('image', {"href": '../dist/2.png', height: 115, width: 250}); +var polygon = makeSVG('polygon', {"points": '100,0 0,100 200,100', style: "fill:lime;stroke:purple;stroke-width:1", height: 115, width: 250}); +var ellipse = makeSVG('ellipse', {"cx": '100', "cy":"50","rx":"100","ry":"50", style: "fill:blue;stroke:purple;stroke-width:2", height: 115, width: 250}); +var ellipse1 = makeSVG('ellipse', {"cx": '100', "cy":"50","rx":"100","ry":"50", style: "fill:blue;stroke:purple;stroke-width:2", height: 115, width: 250}); +var ellipse2 = makeSVG('ellipse', {"cx": '100', "cy":"50","rx":"100","ry":"50", style: "fill:blue;stroke:purple;stroke-width:2", height: 115, width: 250}); +// setTimeout(function() { +// $("svg>g[node-id=1]>rect").css("fill","transparent").css("stroke-width",0); +// $("svg>g[node-id=1]>rect").after(circle); +// },3000); +// circle.onmousedown = function () { +// //alert('hello'); +// }; + +chart.on('redraw', function () { + if (!initialized) { + //ç¬¬ä¸€æ¬¡åœ¨åŠ è½½æ—¶çš„å…ƒç´ + // $("svg>g[node-id=1]>rect").css("fill","transparent").css("stroke-width",0); + // $("svg>g[node-id=1]>rect").after(circle); + + //load '../path/to/your.svg' in the canvas with id = 'canvas' + // canvg('canvas', '../path/to/your.svg') + // + // //load a svg snippet in the canvas with id = 'drawingArea' + // canvg(document.getElementById('drawingArea'), $('svg')[0].outerHTML); + // + // //ignore mouse events and animation + // canvg('canvas', 'file.svg', { ignoreMouse: true, ignoreAnimation: true }); + initialized = true; + } + // $("svg>g[node-id=1]>rect").css("fill", "transparent").css("stroke-width", 0).after(polygon); + $("svg>g[node-id=1]>rect").after(polygon); + $("svg>g[node-id=4]>rect").after(ellipse); + $("svg>g[node-id=5]>rect").after(ellipse1); + $("svg>g[node-id=6]>rect").after(ellipse2); + + // $("svg>g[node-id=5]").attr("transform","matrix(1, 0, 0, 1, 360, 0)"); + // $("svg>g[node-id=6]").attr("transform","matrix(1, 0, 0, 1, 590, 0)"); + $("svg>g[second-link-id='[1][5]']>path").attr("d","M210,180 L210,160 L440,160 L440,102"); + $("svg>g[second-link-id='[1][6]']>path").attr("d","M210,180 L210,160 L660,160 L660,102"); + // d: path("M240 180 L240 160 L680 160 L680 120"); +}); + + +chart.on('click', function (sender, node) { + return false; +}); +// function click(sender, node) { +// /* c.innerHTML += "click(sender, node)<br />"; */ +// return false; +// }; + +function exportStart(sender, options, svg) { + // $("svg>g[node-id=1]>rect").css("fill", "transparent").css("stroke-width", 0); + // $("svg>g[node-id=1]>rect").after(circle); + // options.content = $('svg')[0].outerHTML.replace("../dist/2.png",urlData); + + // var svg = $('svg')[0].outerHTML.replace("../dist/2.png",urlData); + // var svg = $('svg')[0].outerHTML.replace("../dist/2.png",urlData); + // var svg = options.content; + var svg = $('svg')[0].outerHTML; + console.log("svg", svg); + canvg(document.getElementById('canvas'), svg); + var canvas = document.getElementById("canvas"); + // var context = canvas.getContext("2d"); + // context.fillStyle = "green"; + // context.fillRect(50, 50, 100, 100); + // no argument defaults to image/png; image/jpeg, etc also work on some + // implementations -- image/png is the only one that must be supported per spec. + // window.location = canvas.toDataURL("image/png"); + // var image = canvas.toDataURL("image/png"); + // document.write('<img src="'+image+'"/>'); + var a = document.createElement("a"); + a.download = "fallback.png"; + a.href = canvas.toDataURL("image/png"); + a.click(); + return false; +} + +function toDataURL(url, callback) { + var httpRequest = new XMLHttpRequest(); + httpRequest.onload = function () { + var fileReader = new FileReader(); + fileReader.onloadend = function () { + callback(fileReader.result); + }; + fileReader.readAsDataURL(httpRequest.response); + }; + httpRequest.open('GET', url); + httpRequest.responseType = 'blob'; + httpRequest.send(); +} + +// var urlData; +// toDataURL('../dist/2.png', function (dataUrl) { +// urlData = dataUrl; +// console.log('Result in string:', dataUrl); +// }); + + +// setTimeout(function(){ $("div#tree>svg")[0].children[2].append('<image id="img" xlink:href="http://localhost:63342/LEMS/atms-web/src/main/webapp/dist/2.png" height="115" width="250"></image>'); }, 3000); +// setTimeout(function(){ $("div#tree>svg")[0].children[2].append( "<strong>Hello</strong>" ); }, 3000); +// .setAttribute("fill","url(../dist/2.png)"); +// <image xlink:href="http://localhost:63342/LEMS/atms-web/src/main/webapp/dist/2.png" height="200" width="200" style=" +// width: 250px; +// height: 115px; +// "></image>