//dialog
function showError(error) {
if(Q.isString(error)){
showDialog("Error", error);
return;
}
if(error.stack){
showDialog(error.message, error.stack);
}
}
function showDialog(title, content){
if(content === undefined){
content = title;
title = null;
}
$("#q-message .modal-title").html(title || "消息");
$("#q-message .modal-body").html(content);
$('#q-message').modal("show");
}
function hideDialog(){
$('#q-message').modal("hide");
}
//PopupMenu
function showDivAt(div, x, y){
var body = document.documentElement;
var bounds = new Q.Rect(window.pageXOffset, window.pageYOffset, body.clientWidth - 2, body.clientHeight - 2);
var width = div.offsetWidth;
var height = div.offsetHeight;
if (x + width > bounds.x + bounds.width) {
x = bounds.x + bounds.width - width;
}
if (y + height > bounds.y + bounds.height) {
y = bounds.y + bounds.height - height;
}
if (x < bounds.x) {
x = bounds.x;
}
if (y < bounds.y) {
y = bounds.y;
}
div.style.left = x + 'px';
div.style.top = y + 'px';
}
function isDescendant(parent, child) {
var node = child.parentNode;
while (node != null) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
var PopupMenu = function(items){
this.items = items || [];
}
var menuClassName = 'dropdown-menu';
PopupMenu.Separator = 'divider';
PopupMenu.prototype = {
dom : null,
_invalidateFlag: true,
add : function(item){
this.items.push(item);
this._invalidateFlag = true;
},
addSeparator : function() {
this.add(PopupMenu.Separator);
},
showAt: function(x, y){
if(!this.items || !this.items.length){
return false;
}
if(this._invalidateFlag){
this.render();
}
this.dom.style.display = "block";
document.body.appendChild(this.dom);
showDivAt(this.dom, x, y);
},
hide : function(){
if(this.dom && this.dom.parentNode){
this.dom.parentNode.removeChild(this.dom);
}
},
render : function(){
this._invalidateFlag = false;
if(!this.dom){
this.dom = document.createElement('ul');
this.dom.setAttribute("role", "menu");
this.dom.className = menuClassName;
var startEventName = Q.isTouchSupport ? "touchstart" : "mousedown";
if(!this.stopEditWhenClickOnWindow){
var _this = this;
this.stopEditWhenClickOnWindow = function(evt){
if(isDescendant(_this.html, evt.target)){
_this.hide();
}
}
}
window.addEventListener("mousedown", this.stopEditWhenClickOnWindow, true);
this.dom.addEventListener(startEventName, function(evt){
Q.stopEvent(evt);
}, false);
}else{
this.dom.innerHTML = "";
}
for (var i = 0,l = this.items.length; i < l; i++) {
var item = this.renderItem(this.items[i]);
this.dom.appendChild(item);
}
},
html2Escape: function(sHtml) {
return sHtml.replace(/[<>&"]/g,function(c){return {'<':'<','>':'>','&':'&','"':'"'}[c];});
},
renderItem : function(menuItem, zIndex){
var dom = document.createElement('li');
dom.setAttribute("role", "presentation");
if(menuItem == PopupMenu.Separator){
dom.className = PopupMenu.Separator;
dom.innerHTML = " ";
return dom;
}
if(Q.isString(menuItem)){
dom.innerHTML = '' + this.html2Escape(menuItem) + '';
return dom;
}
if(menuItem.selected){
dom.style.backgroundPosition = '3px 5px';
dom.style.backgroundRepeat = 'no-repeat';
dom.style.backgroundImage = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAPklEQVQ4y2P4//8/AyWYYdQA7AYAAZuamlo7ED+H4naQGNEGQDX/R8PtpBjwHIsBz+lqAGVeoDgQR1MiaRgAnxW7Q0QEK0cAAAAASUVORK5CYII=')";
}
var a = document.createElement("a");
a.setAttribute("role", "menuitem");
a.setAttribute("tabindex", "-1");
a.setAttribute("href", "javascript:void(0)");
dom.appendChild(a);
var text = menuItem.text || menuItem.name;
if(text){
a.innerHTML = this.html2Escape(text);
}
var className = menuItem.className;
if(className){
dom.className = className;
}
var call = menuItem.action;
var self = this;
var onclick = function(evt){
if (call) {
call.call(menuItem.scope, evt, menuItem);
}
if(!Q.isIOS){
evt.target.focus();
}
setTimeout(function(){
self.hide();
}, 100);
};
if(Q.isTouchSupport){
// dom.ontouchstart = onclick;
a.ontouchstart = onclick;
}else{
dom.onclick = onclick;
}
return dom;
}
}
Object.defineProperties(PopupMenu.prototype, {
items: {
get:function(){
return this._items;
},
set: function(v){
this._items = v;
this._invalidateFlag = true;
}
}
});
function getPageXY(evt){
if (evt.touches && evt.touches.length) {
evt = evt.touches[0];
}
return {x: evt.pageX, y: evt.pageY};
}
function appendInteractionMenu(graph){
var menu = new PopupMenu();
function showMenu(evt, graph){
var xy = getPageXY(evt);
var x = xy.x, y = xy.y;
var data = graph.getElementByMouseEvent(evt);
var items = [];
// if (data) {
// var name = data instanceof Q.Node ? "Node" : "Edge";
// if (data.name) {
// name += " - " + data.name;
// }
// items.push({text: name, disabled: true});
// items.push(PopupMenu.Separator);
// }
// var currentMode = graph.interactionMode;
// var interactons = [
// {text: "平移模式", value: Q.Consts.INTERACTION_MODE_DEFAULT},
// {text: "框选模式", value: Q.Consts.INTERACTION_MODE_SELECTION}
// ];
// for (var i = 0, l = interactons.length; i < l; i++) {
// var mode = interactons[i];
// if (mode.value == currentMode) {
// mode.selected = true;
// }
// mode.action = function (evt, item) {
// graph.interactionMode = item.value;
// };
// items.push(mode)
// }
items.push(PopupMenu.Separator);
items.push({text: Q.name + ' - ' + Q.version});
menu.items = items;
menu.showAt(x, y);
if (navigator.vibrate) {
navigator.vibrate(200);
}
}
if(Q.isTouchSupport){
graph.addCustomInteraction({
onlongpress: function (evt, graph) {
showMenu(evt, graph)
},
onstart: function (evt) {
menu.hide();
}
});
}else{
graph.html.oncontextmenu = function(evt){
Q.stopEvent(evt);
showMenu(evt, graph);
}
graph.addCustomInteraction({
onstart: function (evt) {
menu.hide();
}
});
}
}
///ExportPanel
function ResizeBox(parent, onBoundsChange){
this.onBoundsChange = onBoundsChange;
this.parent = parent;
this.handleSize = Q.isTouchSupport ? 20 : 8;
this.boundsDiv = this._createDiv(this.parent);
this.boundsDiv.type = "border";
this.boundsDiv.style.position = "absolute";
this.boundsDiv.style.border = "dashed 1px #888";
var handles = "lt,t,rt,l,r,lb,b,rb";
handles = handles.split(",");
for(var i= 0,l=handles.length;i this.wholeBounds.width){
this._bounds.width = this.wholeBounds.width;
}
if(this._bounds.height > this.wholeBounds.height){
this._bounds.height = this.wholeBounds.height;
}
if(this._bounds.x < 0){
this._bounds.x = 0;
}
if(this._bounds.y < 0){
this._bounds.y = 0;
}
if(this._bounds.right > this.wholeBounds.width){
this._bounds.x -= this._bounds.right - this.wholeBounds.width;
}
if(this._bounds.bottom > this.wholeBounds.height){
this._bounds.y -= this._bounds.bottom - this.wholeBounds.height;
}
this._setBounds(this._bounds, true);
},
_createDiv: function(parent){
var div = document.createElement("div");
parent.appendChild(div);
return div;
},
_setHandleLocation: function(handle, x, y){
handle.style.left = (x - this.handleSize / 2) + "px";
handle.style.top = (y - this.handleSize / 2) + "px";
},
_setBounds: function(bounds){
if(!bounds.equals(this.wholeBounds)){
this.oldBounds = bounds;
}
this._bounds = bounds;
bounds = bounds.clone();
bounds.width += 1;
bounds.height += 1;
this.boundsDiv.style.left = bounds.x + "px";
this.boundsDiv.style.top = bounds.y + "px";
this.boundsDiv.style.width = bounds.width + "px";
this.boundsDiv.style.height = bounds.height + "px";
this._setHandleLocation(this.lt, bounds.x, bounds.y);
this._setHandleLocation(this.t, bounds.cx, bounds.y);
this._setHandleLocation(this.rt, bounds.right, bounds.y);
this._setHandleLocation(this.l, bounds.x, bounds.cy);
this._setHandleLocation(this.r, bounds.right, bounds.cy);
this._setHandleLocation(this.lb, bounds.x, bounds.bottom);
this._setHandleLocation(this.b, bounds.cx, bounds.bottom);
this._setHandleLocation(this.rb, bounds.right, bounds.bottom);
if(this.onBoundsChange){
this.onBoundsChange(this._bounds);
}
}
}
Object.defineProperties(ResizeBox.prototype, {
bounds: {
get: function(){
return this._bounds;
},
set: function(v){
this._setBounds(v);
}
}
});
function ExportPanel(){
var scope = this;
var export_panel = document.getElementById("export_panel");
export_panel.addEventListener("mousedown", function(evt){
if(evt.target == export_panel){
scope.destroy();
}
}, false);
var export_scale = document.getElementById("export_scale");
var export_scale_label = document.getElementById("export_scale_label");
export_scale.onchange = function(evt){
export_scale_label.textContent = scope.scale = export_scale.value;
scope.updateOutputSize();
}
var exportImage = function(print){
var graph = scope.graph;
if(!graph){
return;
}
var scale = export_scale.value;
var s = scope.imageInfo.scale;
var clipBounds = new Q.Rect(scope.clipBounds.x / s, scope.clipBounds.y / s, scope.clipBounds.width / s, scope.clipBounds.height / s);
clipBounds.offset(scope.bounds.x, scope.bounds.y);
var imageInfo = graph.exportImage(scale, clipBounds);
if (!imageInfo || !imageInfo.data) {
return false;
}
var win = window.open();
var doc = win.document;
doc.title = graph.name || "";
// doc.title = "export image - " + imageInfo.width + " x " + imageInfo.height;
var img = doc.createElement("img");
img.src = imageInfo.data;
doc.body.style.textAlign = "center";
doc.body.style.margin = "0px";
doc.body.appendChild(img);
if(print){
var style = doc.createElement("style");
style.setAttribute("type", "text/css");
style.setAttribute("media", "print");
var printCSS = "img {max-width: 100%; max-height: 100%;}";
if(clipBounds.width / clipBounds.height > 1.2){
printCSS += "\n @page { size: landscape; }";
}
style.appendChild(document.createTextNode(printCSS));
doc.head.appendChild(style);
img.style.maxWidth = "100%";
img.style.maxHeight = "100%";
setTimeout(function(){
win.print();
win.onfocus=function(){ win.close();}
}, 100);
}
}
var export_submit = document.getElementById("export_submit");
export_submit.onclick = function(){
exportImage();
}
var print_submit = document.getElementById("print_submit");
print_submit.onclick = function(){
exportImage(true);
}
}
ExportPanel.prototype = {
canvas: null,
initCanvas: function(){
var export_canvas = document.getElementById('export_canvas');
export_canvas.innerHTML = "";
var canvas = document.createElement("canvas");
export_canvas.appendChild(canvas);
this.canvas = canvas;
var export_bounds = document.getElementById("export_bounds");
var export_size = document.getElementById("export_size");
var scope = this;
var clipBounds;
var drawPreview = function(){
var canvas = scope.canvas;
var g = canvas.getContext("2d");
g.clearRect(0, 0, canvas.width, canvas.height);
g.drawImage(scope.imageInfo.canvas, 0, 0);
g.beginPath();
g.moveTo(0, 0);
g.lineTo(canvas.width, 0);
g.lineTo(canvas.width, canvas.height);
g.lineTo(0, canvas.height);
g.lineTo(0, 0);
var x = clipBounds.x, y = clipBounds.y, width = clipBounds.width, height = clipBounds.height;
g.moveTo(x, y);
g.lineTo(x, y + height);
g.lineTo(x + width, y + height);
g.lineTo(x + width, y);
g.closePath();
g.fillStyle = "rgba(0, 0, 0, 0.3)";
g.fill();
}
var onBoundsChange = function(bounds){
clipBounds = bounds;
scope.clipBounds = clipBounds;
drawPreview();
var w = clipBounds.width / scope.imageInfo.scale | 0;
var h = clipBounds.height / scope.imageInfo.scale | 0;
export_bounds.textContent = (clipBounds.x / scope.imageInfo.scale | 0) + ", "
+ (clipBounds.y / scope.imageInfo.scale | 0) + ", " + w + ", " + h;
scope.updateOutputSize();
}
this.updateOutputSize = function(){
var export_scale = document.getElementById("export_scale");
var scale = export_scale.value;
var w = clipBounds.width / scope.imageInfo.scale * scale | 0;
var h = clipBounds.height / scope.imageInfo.scale * scale | 0;
var info = w + " X " + h;
if(w * h > 3000 * 4000){
info += "图幅太大,导出时可能出现内存不足";
}
export_size.innerHTML = info;
}
var resizeHandler = new ResizeBox(canvas.parentNode, onBoundsChange);
this.update = function(){
this.canvas.width = this.imageInfo.width;
this.canvas.height = this.imageInfo.height;
resizeHandler.update(this.canvas.width, this.canvas.height);
}
},
destroy: function(){
this.graph = null;
this.imageInfo = null
this.clipBounds = null;
this.bounds = null;
},
show: function(graph){
$('#export_panel').modal("show");
this.graph = graph;
var bounds = graph.bounds;
this.bounds = bounds;
var canvas_size = document.getElementById("canvas_size");
canvas_size.textContent = (bounds.width | 0) + " X " + (bounds.height | 0);
var size = Math.min(500, screen.width / 1.3);
var imageScale;
if(bounds.width > bounds.height){
imageScale = Math.min(1, size / bounds.width);
}else{
imageScale = Math.min(1, size / bounds.height);
}
this.imageInfo = graph.exportImage(imageScale);
this.imageInfo.scale = imageScale;
if(!this.canvas){
this.initCanvas();
}
this.update();
}
}
var exportPanel;
function showExportPanel(graph){
if(!exportPanel){
exportPanel = new ExportPanel();
}
exportPanel.show(graph);
}
///drag and drop
var DRAGINFO_PREFIX = "draginfo";
function ondrag(evt) {
var dataTransfer = evt.dataTransfer;
var img = evt.target;
var attributes = img.attributes;
for(var i= 0,l=attributes.length; i"+title+"";
var img = document.createElement("img");
img.src = src;
img.setAttribute("draggable", "true");
img.setAttribute("title", title);
if(info){
if(Q.isString(info)){
img.setAttribute(DRAGINFO_PREFIX, info);
}else{
for(var name in info){
img.setAttribute(DRAGINFO_PREFIX + "-" + name, info[name]);
}
}
}
img.ondragstart = ondrag;
dv.appendChild(img);
$dv.append($sp);
parent.appendChild(dv);
return img;
}
function createToolBar(buttons, toolbar, scope, vertical, togglable){
for(var n in buttons){
var info = buttons[n];
if(Q.isArray(info)){
var buttonGroup = document.createElement("div");
buttonGroup.className = vertical ? "btn-group-vertical" : "btn-group";
if(togglable !== false){
buttonGroup.setAttribute("data-toggle", "buttons");
}
for(var i= 0,l=info.length;i