diff --git a/client/dock.js b/client/dock.js deleted file mode 100644 index e5aa13a6cfd..00000000000 --- a/client/dock.js +++ /dev/null @@ -1,1254 +0,0 @@ -/* - TODO: - - expanded state tab dragging - - insertbefore tab reorder doesnt create a new splitter bar - - multiple columns: splitter bars don't function as expected: should be non-symmetrical - - dragging forelasttab out of expanded column leaves splitter - - after_tab/before_tab lacks splitter and tab page doesnt animate - - - single page should drag whole tab like button to section does - - anim should wait x00ms before playing - - - tweak tab animations - - menu should appear onmouseup not down - - floating sections or menus - - INTEGRATION - - refactor into seperate class - - closing a window should set the state in the windows menu - - debugger plugin doesnt need to be visible at the start anymore - - add right click menu to buttons/sections - - maintain state of sections/buttons even when closed - - save serialized state in settings.xml -*/ - -var menuCounter = 100; -var columnCounter = 0; - -var testState = { - bars : [ - { - expanded : false, - width : 300, - sections : [ - { - flex : 1, - width : 300, - height : 200, - buttons : [ - { - caption: "Test4", - ext : "" - }, - { - caption: "Test3", - ext : "" - } - ] - }, - { - flex : 1, - width : 200, - height : 300, - buttons : [ - { - caption: "Test2", - ext : "" - } - ] - }, - { - flex : 1, - width : 200, - height : 200, - buttons : [ - { - caption: "Test1", - ext : "" - } - ] - } - ] - }, - { - expanded : true, - width : 200, - sections : [ - { - flex : 1, - width : 200, - height : 200, - buttons : [ - { - caption: "Test2", - ext : "" - } - ] - }, - { - flex : 1, - width : 200, - height : 200, - buttons : [ - { - caption: "Test1", - ext : "" - } - ] - } - ] - } - ] -}; - -loadState(testState); -loadState(getState()); - -function getState(){ - var state = {bars: []}; - - var bar = hboxMain.lastChild; - while (bar) { - if (bar.localName == "bar") { - var barInfo = {sections: []}; - barInfo.expanded = bar.vbox && bar.vbox.visible; - barInfo.width = bar.vbox && bar.vbox.width - || bar.$dockData && bar.$dockData.width - || 200; - - var sections = bar.selectNodes("vbox"); - for (var i = 0; i < sections.length; i++) { - var sectionInfo = {buttons: []}; - var buttons = sections[i].selectNodes("button"); - sectionInfo.flex = buttons[0].$dockpage.parentNode.flex || 1; - - var menu = self[buttons[0].submenu]; - sectionInfo.width = menu.width; - sectionInfo.height = menu.height; - - for (var j = 0; j < buttons.length; j++) { - var buttonInfo = {}; - buttonInfo.ext = ""; - buttonInfo.caption = buttons[j].$dockpage.caption; - - sectionInfo.buttons.push(buttonInfo); - } - barInfo.sections.push(sectionInfo); - } - - state.bars.unshift(barInfo); - } - else if (!bar.bar) { - break; - } - - bar = bar.previousSibling; - } - - return state; -} - -function cleanup(){ - var bar = hboxMain.lastChild; - while (bar) { - if (bar.localName == "bar") { - var sections = bar.selectNodes("vbox"); - for (var i = 0; i < sections.length; i++) { - var buttons = sections[i].selectNodes("button"); - var menu = self[buttons[0].submenu]; - - for (var j = 0; j < buttons.length; j++) { - //Store pages - //buttons[j].$dockpage; - } - - menu.destroy(true, true); - } - } - else if (!bar.bar) { - break; - } - - var next = bar.previousSibling; - bar.destroy(true, true); - bar = next; - } -} - -function loadState(obj){ - cleanup(); - - var bars = obj.bars; - for (var i = 0; i < bars.length; i++) { - var bar = addBar(); - bar.$dockData = bars[i]; - - var sections = bars[i].sections; - for (var j = 0; j < sections.length; j++) { - var section = addSection(bar); - var menu = createMenu(section); - var info = section.$dockData = sections[j]; - menu.firstChild.setAttribute("flex", info.flex); - menu.setAttribute("width", info.width); - menu.setAttribute("height", info.height); - - var buttons = sections[j].buttons - for (var k = 0; k < buttons.length; k++) { - var button = addButton(section, menu, addPage(menu, buttons[k].caption, buttons[k].caption.toLowerCase())); - button.$dockData = buttons[k]; - } - } - - if (bars[i].expanded) - expandBar(bar); - } -} - -function addPage(menu, caption, name){ - var page = menu.firstChild.add(caption, name); - page.oDrag = page.$button; - page.dock = 1; - page.setAttribute("draggable", true); - page.addEventListener("beforedrag", dragPage); - page.addEventListener("afterclose", closePage); - return page; -} - -function closePage(e){ - var button = this.$dockbutton; - var pNode = this.lastParent; - var btnPNode = button.parentNode; - - button.destroy(true, true); - - if (!pNode.getPages().length) { - var barParent = btnPNode.parentNode; - if (pNode.parentNode.localName == "menu") - pNode.parentNode.destroy(true, true); - else { - var menu = self[button.submenu]; - menu.destroy(true, true); - pNode.destroy(true, true); - } - btnPNode.destroy(true, true); - if (!barParent.selectNodes("vbox").length) { - barParent.destroy(true, true); - if (barParent.vbox) { - barParent.vbox.destroy(true, true); - barParent.splitter.destroy(true, true); - } - } - } -} - -function dragPage(e){ //change this to beforedrag and recompile apf - var origMenu = self[this.$dockbutton.submenu]; - /*var menu = origMenu.cloneNode(false); - menu.removeAttribute("id"); - apf.document.body.appendChild(menu);*/ - - var tab = this.parentNode.cloneNode(false); - tab.removeAttribute("id"); - tab.setAttribute("buttons", "close"); //@todo bug in scale that doesnt resize - tab.removeAttribute("anchors"); - apf.document.body.appendChild(tab); - tab.setWidth(this.parentNode.$ext.offsetWidth); - tab.setHeight(this.parentNode.$ext.offsetHeight); - - var page = this.cloneNode(true); - page.removeAttribute("id"); - tab.appendChild(page); - - if (origMenu.$ext.offsetHeight) { - var pos = apf.getAbsolutePosition(origMenu.$ext); - tab.setLeft(pos[0]); - tab.setTop(pos[1]); - } - else { - var pos = apf.getAbsolutePosition(this.parentNode.$ext); - tab.setLeft(pos[0] - 1); - tab.setTop(pos[1] - 2); - } - tab.$ext.style.border = "1px solid #333"; - //menu.$ext.style.margin = "0 0 0 0" - tab.addEventListener("afterdrag", function(e){ - tab.id = tab.name = ""; //@todo fix this bug in apf - tab.destroy(true, true); - stopDrag(e.htmlEvent); - }); - - //document instead? - var clientX = e.htmlEvent.clientX; - var clientY = e.htmlEvent.clientY; - tab.setAttribute("draggable", true); - setTimeout(function(){ - //@todo Collapse menu - - tab.$dragStart({clientX:clientX,clientY:clientY}); - tab.$ext.style.zIndex = 1000000; - }); - - startDrag(tab, this); - - return false; -}; - -function isLastBar(aml) { - var last = hboxMain.lastChild; - while (last && !last.visible) - last = last.previousSibling; - - return aml == last || aml == last.vbox; -} - -var whiledrag, lastInfo; -function startDrag(dragged, original){ - var last, state = 0; - - apf.setOpacity(dragged.$ext, 0.3); - - function getLastBar(){ - var lastBar = hboxMain.lastChild; - while (lastBar && lastBar.previousSibling - && (lastBar.previousSibling.localName == "bar" - || lastBar.previousSibling.bar)) - lastBar = lastBar.previousSibling; - if (lastBar.localName != "bar") - lastBar = lastBar.bar; - - return lastBar; - } - - var lastBar = getLastBar(); - var leftEdge = apf.getAbsolutePosition(lastBar.$ext)[0]; - - apf.addListener(document, "mousemove", whiledrag = function(e){ - if (last) { - last.$ext.style.borderBottom = ""; - delete last; - } - - if (!e) return; - - var indicatorTop = indicator.style.top; - dragged.$ext.style.top = "-2000px"; - indicator.style.top = "-2000px"; - apf.plane.hide(); - - //Adding a column - if (e.clientX > leftEdge - 40 && e.clientX < leftEdge) { - var isSameColumn = dragged.localName == "vbox" - && dragged.$dockbar == lastBar - && !dragged.$dockbar.selectNodes("vbox").length; - - info = { - position : isSameColumn ? "none" : "left_of_column", - aml : aml = last = lastBar - } - } - //Rest - else { - var info = calcAction(e, original); - var aml = last = info.aml; - } - - if (lastInfo && lastInfo.position == info.position && lastInfo.aml == aml) { - indicator.style.top = indicatorTop; - return; - } - - lastInfo = info; - - if (!aml || !aml.dock && !aml.bar) { - if (!state) { - state = 1; - apf.tween.single(dragged.$ext, { - type: "fade", - from: 0.3, - to : 1, - steps : 20, - onfinish : function(){ - state = 1; - } - }); - } - return; - } - - var pos = apf.getAbsolutePosition(aml.$ext); - indicator.style.left = pos[0] + "px"; - indicator.style.top = pos[1] + "px"; - indicator.style.display = "block"; - indicator.style.backgroundColor = ""; - indicator.style.marginLeft = "0"; - indicator.innerHTML = ""; - - if (state) { - state = 0; - apf.tween.single(dragged.$ext, { - type: "fade", - from: 1, - to : 0.3, - steps : 20, - onfinish : function(){ - state = 0; - } - }); - } - - var width = aml.$ext.offsetWidth; - var height = aml.$ext.offsetHeight; - - switch(info.position) { - case "before_button": - case "after_button": - indicator.innerHTML = "
"; - indicator.style.borderWidth = "6px 1px 3px 1px"; - - var pos2 = apf.getAbsolutePosition(aml.parentNode.$ext); - indicator.style.left = pos2[0] + "px"; - indicator.style.top = pos2[1] + "px"; - width = aml.parentNode.$ext.offsetWidth; - height = aml.parentNode.$ext.offsetHeight; - - var div = indicator.firstChild; - if (aml == getOriginal("button", original)) { //@todo Checks needs to include different representations - div.style.top = (pos[1] - pos2[1] - 2) + "px"; - div.style.left = "2px"; - div.style.right = "3px"; - div.style.height = (aml.$ext.offsetHeight - 9) + "px"; - div.style.border = "2px solid #7ac7f4"; - div.style.webkitBorderRadius = "6px"; - } - else { - div.style.top = (pos[1] - pos2[1] - + (info.position == "before_button" ? 0 : aml.$ext.offsetHeight) - - 8) + "px"; - div.style.width = "100%"; - div.style.borderBottom = "3px solid #7ac7f4"; - } - - break; - case "in_section": - if (getOriginal("section", original) == aml.$dockfor) {//@todo move this - indicator.style.borderWidth = "1px 1px 1px 1px"; - height--; - } - break; - case "after_page": - case "before_page": - var pNode = aml.parentNode; - var pos2 = apf.getAbsolutePosition(pNode.$ext); - indicator.style.left = pos2[0] + "px"; - indicator.style.top = pos2[1] + "px"; - width = pNode.$ext.offsetWidth; - height = pNode.$ext.offsetHeight; - - indicator.style.borderWidth = "3px 3px 3px 3px"; - - var compareAml = info.position == "before_page" - ? aml.previousSibling - : aml.nextSibling; - var originalAml = getOriginal("page", original); - var matchAml = originalAml == aml - ? aml - : (originalAml == compareAml ? compareAml : false); - var diff = apf.getAbsolutePosition((matchAml || aml).$button, pNode.$ext); - if (matchAml) { - indicator.innerHTML = "
"; - var div1 = indicator.firstChild; - var div2 = indicator.childNodes[1]; - var div3 = indicator.childNodes[2]; - div1.style.left = diff[0] + "px"; - div1.style.width = (matchAml.$button.offsetWidth - 5) + "px"; - div1.style.height = "18px"; - div1.style.margin = "-18px 0 0 -3px"; - div1.style.border = "3px solid #7ac7f4"; - div1.style.borderWidth = "3px 3px 0 3px"; - - div2.style.left = (diff[0] + matchAml.$button.offsetWidth - 3) + "px"; - div2.style.right = "0px"; - div3.style.borderBottom = - div2.style.borderBottom = "3px solid #7ac7f4"; - - div3.style.left = "0px"; - div3.style.right = (width - diff[0] - 3) + "px"; - - indicator.style.borderTop = "0px solid #7ac7f4"; - indicator.style.top = (pos2[1] + 18) + "px"; - height -= 18; - } - else { - indicator.innerHTML = "
"; - indicator.firstChild.style.height = "16px"; - indicator.firstChild.style.width = "5px"; - indicator.firstChild.style.background = "rgba(122,199,244,0.5)"; - indicator.firstChild.style.top = "0px"; - indicator.firstChild.firstChild.style.background = "#7ac7f4"; - indicator.firstChild.firstChild.style.height = "100%"; - indicator.firstChild.firstChild.style.margin="0 2px 0 2px"; - - var left = (diff[0] + - (info.position == "before_page" ? 0 : aml.$button.offsetWidth)); - if (left) - left -= 5; - else { - indicator.firstChild.style.width = "2px"; - indicator.firstChild.firstChild.style.marginLeft = "0px"; - } - indicator.firstChild.style.left = left + "px"; - } - break; - case "before_tab": - height = 0; - case "after_tab": - indicator.style.left = pos[0] + "px"; - indicator.style.top = (pos[1] + height - (!aml.nextSibling ? 3 : 0)) + "px"; - indicator.style.height = "3px"; - indicator.style.width = width + "px"; - indicator.style.borderWidth = "0 0 0 0"; - indicator.style.backgroundColor = "rgba(122,199,244," - + (!aml.nextSibling ? 1 : 0.8) + ")"; - return; - case "before_section": - height = 0; - case "after_section": - indicator.style.left = pos[0] + "px"; - indicator.style.top = (pos[1] + height - 3) + "px"; - indicator.style.height = "5px"; - indicator.style.width = aml.$ext.offsetWidth + "px"; - indicator.style.borderWidth = "0 0 0 0"; - indicator.innerHTML = "
"; - indicator.firstChild.style.backgroundColor = "#7ac7f4"; - indicator.firstChild.style.height = "1px"; - indicator.style.backgroundColor = "rgba(122,199,244,0.5)"; - return; - case "in_column": - indicator.innerHTML = "
"; - indicator.style.borderWidth = "0 0 0 0"; - - var div = indicator.firstChild; - div.style.top = "100%"; - div.style.borderTop = "3px solid #7ac7f4" - div.style.height = (dragged.localName == "vbox" ? dragged.$ext.offsetHeight : 50) + "px"; - div.style.background = "rgba(172,172,172,0.5)"; - div.style.width = "100%"; - div.style.webkitBorderRadius = "0 0 4px 4px"; - - /*apf.tween.single(div, { - type: "height", - from: 0, - to : dragged.localName == "vbox" ? dragged.$ext.offsetHeight : 50, - anim : apf.tween.EASEOUT, - steps : 20 - });*/ - - break; - case "left_of_column": - if (aml != getLastBar()) { - indicator.style.borderWidth = "0 0 0 3px"; - indicator.style.marginLeft = "-1px"; - } - else { - indicator.innerHTML = "
"; - indicator.style.borderWidth = "0 0 0 0"; - - var div = indicator.firstChild; - div.style.right = "100%"; - div.style.width = 0; - div.style.height = "100%"; - div.style.borderRight = "3px solid #7ac7f4" - div.style.background = "rgba(172,172,172,0.5)"; - div.style.webkitBorderRadius = "4px 0 0 4px"; - - apf.tween.single(div, { - type: "width", - from: 0, - to : 40, - anim : apf.tween.EASEOUT, - steps : 20 - }); - } - break; - case "right_of_column": - indicator.style.borderWidth = "0 3px 0 0"; - if (!isLastBar(aml)) - indicator.style.marginLeft = "2px"; - break; - default: - indicator.style.display = "none"; - apf.setOpacity(dragged.$ext, 1); - break; - } - - var diff = apf.getDiff(indicator); - indicator.style.width = (width - diff[0]) + "px"; - indicator.style.height = (height - diff[1]) + "px"; - }); - - whiledrag.dragged = dragged; - whiledrag.original = original; -} - -function getOriginal(type, aml) { - if (type == "button") { - if (aml.localName == "page") - return aml.$dockbutton; - if (aml.localName == "divider") { - var buttons = aml.parentNode.selectNodes("button"); - if (buttons.length == 1) - return buttons[0]; - } - return aml; - } - else if (type == "page") { - if (aml.localName == "button") - return aml.$dockpage; - if (aml.localName == "divider") { - var buttons = aml.parentNode.selectNodes("button"); - if (buttons.length == 1) - return buttons[0].$dockpage; - } - return aml; - } - else if (type == "section") { - if (aml.localName == "page" && aml.parentNode.getPages().length == 1) - return aml.$dockbutton.parentNode; - if (aml.localName == "divider") - return aml.parentNode; - return aml; - } -} - -var indicator = document.body.appendChild(document.createElement("div")); -indicator.style.position = "absolute"; -indicator.style.display = "none"; -indicator.style.border = "3px solid #7ac7f4"; -indicator.style.zIndex = 1000000; - -var diffPixel = 3; - -function matchTab(pos, y) { - return y > pos - diffPixel && y < pos + diffPixel; -} - -function calcAction(e, original){ - var position = "none"; - - var el = document.elementFromPoint(e.clientX, e.clientY); - if (el == document.body) - return {}; - - var aml = apf.findHost(el); - if (!aml) return {}; - - if (!aml.dock || aml.localName == "page" || aml.localName == "tab") { - var node = aml; - while (node && !node.vdock) - node = node.parentNode; - - if (node && node.localName == "vbox") { - var tabs = node.selectNodes("tab"); - var pos = apf.getAbsolutePosition(node.$ext)[1]; - var doTest = original.parentNode.localName == "tab" - && original.parentNode.getPages().length == 1; - - if (matchTab(tabs[0].$ext.offsetTop + pos, e.clientY)) { - return doTest && original.parentNode == tabs[0] - ? {} : {position: "before_tab", aml: tabs[0]}; - } - - for (var i = 0; i < tabs.length; i++) { - if (matchTab(tabs[i].$ext.offsetHeight + 1 - + tabs[i].$ext.offsetTop + pos - (!aml.nextSibling ? 3 : 0), e.clientY)) { - return doTest && (original.parentNode == tabs[i] || original.parentNode == tabs[i+1]) - ? {} : {position: "after_tab", aml: tabs[i]}; - } - } - } - } - - if (aml.localName == "splitter") { - aml.$ext.style.display = "none"; - aml = apf.findHost(document.elementFromPoint(e.clientX, e.clientY)); - aml.$ext.style.display = "block"; - } - - if (!aml.dock && !aml.bar) - return {}; - - var bar = aml; - while (bar && bar.localName != "bar" && (bar.localName != "vbox" || bar.dock)) - bar = bar.parentNode; - - if (bar) { - var pos = apf.getAbsolutePosition(e.target, bar.$ext); - var l = pos[0] + e.offsetX; - var r = bar.$ext.offsetWidth - l; - } - - if (bar && l < diffPixel) { - var isSameColumn = original.localName == "divider" - && (original.parentNode.$dockbar == bar - || original.parentNode.$dockbar == bar.previousSibling) - && !original.parentNode.$dockbar.selectNodes("vbox").length; - - return { - position : isSameColumn ? "none" : "left_of_column", - aml : bar - } - } - else { - var df = (isLastBar(bar) - ? diffPixel * 2 - : diffPixel) - var isSameColumn = original.localName == "divider" - && (original.parentNode.$dockbar == bar - || original.parentNode.$dockbar == bar.nextSibling) - && !original.parentNode.$dockbar.selectNodes("vbox").length; - - if (bar && r < df) { - return { - position : isSameColumn ? "none" : "right_of_column", - aml : bar - } - } - } - - if (aml.localName == "page" || aml.localName == "tab" || aml.localName == "menu") { - position = "before_page"; - if (aml.localName == "page") { - var pos = apf.getAbsolutePosition(aml.$button); - var l = e.clientX - pos[0]; - - if (l > aml.$button.offsetWidth/2) - position = "after_page"; - } - else if (aml.localName == "menu") { - var pages = aml.firstChild.getPages(); - aml = pages[pages.length - 1]; - position = "after_page"; - } - else if (aml.localName == "tab") { - pages = aml.getPages(); - aml = pages[pages.length - 1]; - position = "after_page"; - } - - var pos2 = apf.getAbsolutePosition(aml.parentNode.$ext); - var t = e.clientY - pos2[1]; - if (t > 18) - return {}; - } - else { - if (aml.localName == "bar" || aml.skin == "dockheader") { - if (aml.skin == "dockheader") { - aml = aml.parentNode.selectNodes("vbox")[0]; - position = "before_section"; - } - else { - position = original.localName == "divider" && original.parentNode.$dockbar == aml - ? "in_section" - : "in_column"; - aml = aml.lastChild;/*selectNodes("vbox"); - aml = vboxs[vboxs.length - 1];*/ - } - } - else if (aml.localName == "button") { - position = "after_button"; - var pos = apf.getAbsolutePosition(aml.$ext); - var t = e.clientY - pos[1]; - if (t < aml.$ext.offsetHeight/2) { - if (aml.previousSibling && aml.previousSibling.localName == "button") - aml = aml.previousSibling - else { - position = "before_button"; - //aml = aml.parentNode; - } - } - } - else if (aml.localName == "divider" || aml.localName == "vbox") { - if (aml.localName == "divider") - aml = aml.parentNode; - - var buttons = aml.selectNodes("button"); - if (!buttons.length) - return {position: "in_section", aml: aml}; - - var pos = apf.getAbsolutePosition(aml.$ext); - var t = e.clientY - pos[1]; - var b = aml.$ext.offsetHeight - t; - - if (t < diffPixel) { - if (original.localName != "divider" - || original.parentNode != (aml.previousSibling - && aml.previousSibling.$dockfor)) { - position = "before_section"; - } - } - else if (b < diffPixel && aml.nextSibling) { - if (original.localName != "divider" - || original.parentNode != aml.$dockfor) { - if (!aml.nextSibling - || aml.nextSibling.$dockfor != getOriginal("section", original)) - position = "after_section"; - } - } - - if (position == "none") { - if (t < aml.$ext.offsetHeight/2) { - position = "before_button"; - aml = buttons[0]; - } - else { - position = "after_button"; - aml = buttons[buttons.length - 1]; - } - } - } - } - - return { - position : position, - aml : aml - } -} - -function stopDrag(e){ - whiledrag(); - apf.removeListener(document, "mousemove", whiledrag); - - indicator.style.display = "none"; - - var info = lastInfo;//calcAction(e); - var aml = info && info.aml; - - var original = whiledrag.dragged; - apf.setOpacity(original.$ext, 1); - - if (!aml) return; - switch(info.position) { - case "before_button": - case "after_button": - var submenu = self[aml.submenu]; - var dragAml = whiledrag.original; - - moveTo(submenu, dragAml, aml, info.position == "before_button" - ? aml - : aml.nextSibling, aml.parentNode, info.position); - break; - case "before_tab": - case "after_tab": - var bar = aml.parentNode.bar; - var childNr = apf.getChildNumber(aml); - var sections = bar.selectNodes("vbox"); - var section = addSection(bar, info.position == "before_tab" - ? sections[0] - : sections[childNr + 1]); - - //reconstruct menu - var submenu = createMenu(section); - var dragAml = whiledrag.original; - - //This block of code should move to inside moveTo somehow... - perhaps mimic before_page -// var pNode = dragAml.parentNode; -// submenu.firstChild.appendChild(dragAml); - var tab = aml.parentNode.insertBefore(submenu.firstChild, info.position == "before_tab" - ? aml - : aml.nextSibling); - tab.setAttribute("flex", 1); - - moveTo(submenu, dragAml, tab, null, section, info.position, tab);//, null, pNode); - break; - case "before_section": - case "in_column": - case "after_section": - var section = addSection(aml.parentNode, info.position == "before_section" - ? aml - : (info.position == "in_column" - ? null - : aml.nextSibling)); - - //reconstruct menu - var submenu = createMenu(section); - var dragAml = whiledrag.original; - - moveTo(submenu, dragAml, aml, null, section, info.position); - break; - case "before_page": - case "after_page": - var submenu = self[aml.$dockbutton.submenu];//aml.parentNode.parentNode; - var dragAml = whiledrag.original; - - moveTo(submenu, dragAml, aml.parentNode, info.position == "before_page" - ? aml.$dockbutton - : aml.nextSibling && aml.nextSibling.$dockbutton, submenu.ref, - info.position, aml.parentNode); - break; - case "left_of_column": - var bar = addBar(aml); - //Single Tab Case - //create new section - var section = addSection(bar); - var submenu = createMenu(section); - var dragAml = whiledrag.original; - - moveTo(submenu, dragAml, aml, null, section, info.position); - break; - case "right_of_column": - var bar = addBar(aml.nextSibling); - //Single Tab Case - //create new section - var section = addSection(bar); - - //reconstruct menu - var submenu = createMenu(section); - var dragAml = whiledrag.original; - - moveTo(submenu, dragAml, aml, null, section, info.position); - break; - default: - break; - } -} - -function moveTo(submenu, dragAml, aml, beforeButton, parentNode, position, tab, pNode){ - var beforePage = beforeButton && beforeButton.$dockpage; - - if (dragAml.localName == "page" || dragAml.localName == "button") { - if (dragAml.localName == "page") { - var page = dragAml; - var button = dragAml.$dockbutton; - } - else if (dragAml.localName == "button") { - var page = dragAml.$dockpage; - var button = dragAml; - } - if (!pNode) - pNode = page.parentNode; - var btnPNode = button.parentNode; - - var oldMenu = self[page.$dockbutton.submenu]; - - if (beforeButton && beforeButton.previousSibling == button || beforeButton == button - || !beforeButton && !button.nextSibling && button.parentNode == parentNode) - return; - - var newPNode = tab || submenu.firstChild; - if (newPNode) { - newPNode.insertBefore(page, beforePage); - - if (newPNode.getPages().length == 1) { - var totalFlex = 0, count = 0; - if (newPNode.parentNode.localName == "vbox") { - newPNode.parentNode.selectNodes("tab").each(function(tab){ - totalFlex += tab.flex || 1; - count++; - }); - } - else { - var tabs = parentNode.parentNode.selectNodes("vbox").each(function(vbox){ - var button = vbox.selectSingleNode("button"); - totalFlex += button && self[button.submenu].firstChild.flex || 1; - count++; - }); - } - newPNode.setAttribute("flex", totalFlex/count); - } - } - button.setAttribute("submenu", submenu.id); - - //add button to section - parentNode.insertBefore(button, beforeButton); - - if (!pNode.getPages().length) { - var barParent = btnPNode.parentNode; - oldMenu.destroy(true, true); - if (pNode.parentNode) - pNode.destroy(true, true); - btnPNode.destroy(true, true); - if (!barParent.selectNodes("vbox").length) { - barParent.destroy(true, true); - if (barParent.vbox) { - barParent.vbox.destroy(true, true); - barParent.splitter.destroy(true, true); - } - } - } - } - else if (dragAml.localName == "divider") { - var buttons = dragAml.parentNode.selectNodes("button"); - for (var i = buttons.length - 1; i >= 0; i--) { - var button = buttons[i]; - - moveTo(submenu, button, aml, beforeButton, parentNode, position, tab, pNode) - } - } -} - -function createMenu(section){ - var menu = new apf.menu({ - id : "submenu" + menuCounter++, - width : "200", - height : "200", - ref : section, - pinned : "true", - animate : "false", - skin : "dockwindowbasic", - dock : 1, - ondisplay : function(){ - var pos = apf.getAbsolutePosition(this.opener.$ext); - var width = apf.getWindowWidth(); - this.$ext.style.marginLeft = (-1 * Math.min((width - pos[0]), this.$ext.offsetWidth)) + "px"; - }, - childNodes : [ - new apf.tab({ - anchors : "0 0 0 0", - skin : "docktab", - buttons : "scale,close", - dock : 1, - onclose : function(e){ - var page = e.page; - page.lastParent = this; - } - - }) - ] - }); - - apf.document.body.appendChild(menu); - menu.show(); - menu.hide(); - - return menu; -} - -function addBar(before){ - columnCounter++; - - var bar = hboxMain.insertBefore(new apf.bar({ - skin : "debug-panel", - dock : 1, - onDOMNodeRemovedFromDocument : function(){ - columnCounter--; - }, - childNodes : [ - new apf.button({ - dock : 1, - skin : "dockheader", - onclick : function(){ - expandBar(this.parentNode); - } - }) - ] - }), before); - - return bar; -} - -function expandBar(bar){ - if (!bar.vbox) { - bar.vbox = bar.parentNode.insertBefore(new apf.vbox({ - padding : 3, - width : bar.$dockData && bar.$dockData.width || 260, - splitters : true, - vdock : 1, - "class" : "dockcol unselectable", - childNodes : [ - new apf.button({ - dock : 1, - skin : "dockheader", - "class" : "expanded", - nosplitter : true, - height : 11, - margin : "0 0 -3 0", - onclick : function(){ - collapseBar(bar); - } - }) - ] - }), bar); - - //style hack - bar.vbox.$ext.style.borderLeft = "1px solid #333"; - - bar.splitter = bar.parentNode.insertBefore(new apf.splitter({ - width : "0" - }), bar.vbox); - - bar.splitter.bar = - bar.vbox.bar = bar; - } - else { - bar.parentNode.insertBefore(bar.vbox, bar); - bar.parentNode.insertBefore(bar.splitter, bar.vbox); - } - - var vbox = bar.selectNodes("vbox"); - for (var i = 0; i < vbox.length; i++) { - var menu = self[vbox[i].selectSingleNode("button").submenu] - menu.hide(); - var tab = menu.firstChild; - bar.vbox.appendChild(tab); - if (!tab.flex) - tab.setAttribute("flex", 1); - } - - bar.hide(); - bar.vbox.show(); - bar.splitter.show(); - - //Hack for button - bar.vbox.firstChild.$ext.onmousemove({}); -} - -function collapseBar(bar){ - var vbox = bar.selectNodes("vbox"); - var tabs = bar.vbox.selectNodes("tab"); - for (var i = 0; i < vbox.length; i++) { - var menu = self[vbox[i].selectSingleNode("button").submenu]; - menu.appendChild(tabs[i]); - } - - bar.show(); - bar.vbox.hide(); - bar.splitter.hide(); - - bar.parentNode.removeChild(bar.vbox); - bar.parentNode.removeChild(bar.splitter); - - //Hack for button - bar.firstChild.$ext.onmousemove({}); -} - -function addSection(bar, before){ - var section = bar.insertBefore(new apf.vbox({ - padding : 0, - edge : "0 0 3 0", - "class" : "docksection", - dock : 1, - childNodes : [ - new apf.divider({ - skin : "divider-debugpanel", - margin : "3 5 2 5", - dock : 1, - draggable : "true" - }) - ] - }), before); - - var div = section.firstChild; - div.addEventListener("beforedrag", function(e){ //change this to beforedrag and recompile apf - var section = this.parentNode; - - //this.hideMenu(); - - var pNode = section.$dockbar = section.parentNode; - var placeHolder = section.cloneNode(false); - placeHolder.removeAttribute("id"); - placeHolder.$dockfor = section; - - var diff = apf.getDiff(section.$ext); - var height = section.$ext.offsetHeight; - var pos = apf.getAbsolutePosition(section.$ext); - - pNode.insertBefore(placeHolder, section); - placeHolder.$ext.style.background = "#acacac"; - placeHolder.$ext.style.height = (height - diff[1]) + "px"; - - section.setWidth(section.$ext.offsetWidth); - apf.document.body.appendChild(section); - section.setLeft(pos[0]); - section.setTop(pos[1]); - - section.addEventListener("afterdrag", function(e){ - pNode.insertBefore(section, placeHolder); - section.setAttribute("draggable", false); - - setTimeout(function(){ - section.removeAttribute("left"); - section.removeAttribute("top"); - section.removeAttribute("width"); - section.$ext.style.position = "relative"; - }); - - var buttons = this.selectNodes("button"); - if (buttons.length) - buttons[0].setValue(false); - - stopDrag(e.htmlEvent); - placeHolder.destroy(true, true); - }); - - section.setAttribute("draggable", true); - - var clientX = e.htmlEvent.clientX; - var clientY = e.htmlEvent.clientY; - setTimeout(function(){ - //@todo Collapse menu - - section.$dragStart({clientX:clientX,clientY:clientY}); - section.$ext.style.zIndex = 1000000; - }); - - startDrag(section, this); - - return false; - }); - - return section; -} - -function addButton(section, submenu, page){ - var button = section.appendChild(new apf.button({ - "class" : "dockButtonID", - skin : "dockButton", - submenu : submenu.id, - dock : 1, - draggable : "true" - })); - - button.addEventListener("beforedrag", function(e){ //change this to beforedrag and recompile apf - var _self = this; - this.hideMenu(); - this.setValue(true); - - //Upgrade to container if only 1 element - if (this.parentNode.selectNodes("button").length == 1) { - this.parentNode.firstChild.dispatchEvent("beforedrag", e); - return false; - } - - var btn = this.cloneNode(true); - btn.removeAttribute("id"); - apf.document.body.appendChild(btn); - btn.setValue(true); - - var pos = apf.getAbsolutePosition(this.$ext); - btn.setLeft(pos[0]); - btn.setTop(pos[1]); - btn.addEventListener("afterdrag", function(e){ - btn.destroy(true, true); - _self.setValue(false); - stopDrag(e.htmlEvent); - }); - - //document instead? - var clientX = e.htmlEvent.clientX; - var clientY = e.htmlEvent.clientY; - setTimeout(function(){ - btn.$dragStart({clientX:clientX,clientY:clientY}); - btn.$ext.style.zIndex = 1000000; - this.removeEventListener("mouseover", arguments.callee); - }); - - startDrag(btn, this); - - return false; - }); - - page.$dockbutton = button; - button.$dockpage = page; - - return button; -} \ No newline at end of file diff --git a/client/dock.html b/client/ext/dockpanel/dock.html similarity index 100% rename from client/dock.html rename to client/ext/dockpanel/dock.html diff --git a/client/ext/dockpanel/dock.js b/client/ext/dockpanel/dock.js new file mode 100644 index 00000000000..44abbdfb512 --- /dev/null +++ b/client/ext/dockpanel/dock.js @@ -0,0 +1,1353 @@ +/* + TODO: + - expanded state tab dragging + - insertbefore tab reorder doesnt create a new splitter bar + - multiple columns: splitter bars don't function as expected: should be non-symmetrical + - dragging forelasttab out of expanded column leaves splitter + - after_tab/before_tab lacks splitter and tab page doesnt animate + + - single page should drag whole tab like button to section does + - anim should wait x00ms before playing + + - tweak tab animations + - menu should appear onmouseup not down + - floating sections or menus + + INTEGRATION + - refactor into seperate class + - closing a window should set the state in the windows menu + - debugger plugin doesnt need to be visible at the start anymore + - add right click menu to buttons/sections + - maintain state of sections/buttons even when closed + - save serialized state in settings.xml + + - page closing should call cbStorePage +*/ + +var testState = { + bars : [ + { + expanded : false, + width : 300, + sections : [ + { + flex : 1, + width : 300, + height : 200, + buttons : [ + { + caption: "Test4", + ext : "" + }, + { + caption: "Test3", + ext : "" + } + ] + }, + { + flex : 1, + width : 200, + height : 300, + buttons : [ + { + caption: "Test2", + ext : "" + } + ] + }, + { + flex : 1, + width : 200, + height : 200, + buttons : [ + { + caption: "Test1", + ext : "" + } + ] + } + ] + }, + { + expanded : true, + width : 200, + sections : [ + { + flex : 1, + width : 200, + height : 200, + buttons : [ + { + caption: "Test2", + ext : "" + } + ] + }, + { + flex : 1, + width : 200, + height : 200, + buttons : [ + { + caption: "Test1", + ext : "" + } + ] + } + ] + } + ] +}; + +function DockableLayout(parentHBox, cbFindPage, cbStorePage) { + this.columnCounter = 0; + this.$parentHBox = parentHBox; + this.$cbStorePage = cbStorePage; + this.$cbFindPage = cbFindPage; + + var indicator = this.indicator = document.body.appendChild(document.createElement("div")); + indicator.style.position = "absolute"; + indicator.style.display = "none"; + indicator.style.border = "3px solid #7ac7f4"; + indicator.style.zIndex = 1000000; +}; + +(function(){ + var whiledrag, lastInfo, diffPixel = 3; + var menuCounter = 100; + + /** + * Retrieve the current state of the layout as a JSON object + * + */ + this.getState = function(){ + var state = {bars: []}; + + var bar = this.$parentHBox.lastChild; + while (bar) { + if (bar.localName == "bar") { + var barInfo = {sections: []}; + barInfo.expanded = bar.vbox && bar.vbox.visible; + barInfo.width = bar.vbox && bar.vbox.width + || bar.$dockData && bar.$dockData.width + || 200; + + var sections = bar.selectNodes("vbox"); + for (var i = 0; i < sections.length; i++) { + var sectionInfo = {buttons: []}; + var buttons = sections[i].selectNodes("button"); + sectionInfo.flex = buttons[0].$dockpage.parentNode.flex || 1; + + var menu = self[buttons[0].submenu]; + sectionInfo.width = menu.width; + sectionInfo.height = menu.height; + + for (var j = 0; j < buttons.length; j++) { + var buttonInfo = {}; + buttonInfo.ext = buttons[j].$dockpage.extension; + buttonInfo.caption = buttons[j].$dockpage.caption; + + sectionInfo.buttons.push(buttonInfo); + } + barInfo.sections.push(sectionInfo); + } + + state.bars.unshift(barInfo); + } + else if (!bar.bar) { + break; + } + + bar = bar.previousSibling; + } + + return state; + } + + /** + * Set the current layout via a JSON object + * @param {Object} obj JSON object with the following structure: + * { + * bars : [ + * { + * expanded : false, + * width : 300, + * sections : [ + * { + * flex : 1, + * width : 200, + * height : 200, + * buttons : [ + * { + * + * } + * ] + * } + * ] + * } + * ] + * } + * + */ + this.loadState = function(obj){ + this.$cleanup(); + + var bars = obj.bars; + for (var i = 0; i < bars.length; i++) { + var bar = this.$addBar(); + bar.$dockData = bars[i]; + + var sections = bars[i].sections; + for (var j = 0; j < sections.length; j++) { + var section = this.$addSection(bar); + var menu = this.$addMenu(section); + var info = section.$dockData = sections[j]; + + menu.firstChild.setAttribute("flex", info.flex); + menu.setAttribute("width", info.width); + menu.setAttribute("height", info.height); + + var buttons = sections[j].buttons + for (var k = 0; k < buttons.length; k++) { + var button = this.$addButton(section, menu, + this.$addPage( + this.$cbFindPage(buttons[k].ext), + menu, + buttons[k].caption, + buttons[k].caption.toLowerCase() + ) + ); + button.$dockData = buttons[k]; + } + } + + if (bars[i].expanded) + this.expandBar(bar); + } + } + + /** + * Expand a bar + */ + this.expandBar = function (bar){ + if (!bar.vbox) { + var _self = this; + bar.vbox = bar.parentNode.insertBefore(new apf.vbox({ + padding : 3, + width : bar.$dockData && bar.$dockData.width || 260, + splitters : true, + vdock : 1, + "class" : "dockcol unselectable", + childNodes : [ + new apf.button({ + dock : 1, + skin : "dockheader", + "class" : "expanded", + nosplitter : true, + height : 11, + margin : "0 0 -3 0", + onclick : function(){ + _self.collapseBar(bar); + } + }) + ] + }), bar); + + //style hack + bar.vbox.$ext.style.borderLeft = "1px solid #333"; + + bar.splitter = bar.parentNode.insertBefore(new apf.splitter({ + width : "0" + }), bar.vbox); + + bar.splitter.bar = + bar.vbox.bar = bar; + } + else { + bar.parentNode.insertBefore(bar.vbox, bar); + bar.parentNode.insertBefore(bar.splitter, bar.vbox); + } + + var vbox = bar.selectNodes("vbox"); + for (var i = 0; i < vbox.length; i++) { + var menu = self[vbox[i].selectSingleNode("button").submenu] + menu.hide(); + var tab = menu.firstChild; + bar.vbox.appendChild(tab); + if (!tab.flex) + tab.setAttribute("flex", 1); + } + + bar.hide(); + bar.vbox.show(); + bar.splitter.show(); + + //Hack for button + bar.vbox.firstChild.$ext.onmousemove({}); + } + + /** + * Collapse a bar + */ + this.collapseBar = function(bar){ + var vbox = bar.selectNodes("vbox"); + var tabs = bar.vbox.selectNodes("tab"); + for (var i = 0; i < vbox.length; i++) { + var menu = self[vbox[i].selectSingleNode("button").submenu]; + menu.appendChild(tabs[i]); + } + + bar.show(); + bar.vbox.hide(); + bar.splitter.hide(); + + bar.parentNode.removeChild(bar.vbox); + bar.parentNode.removeChild(bar.splitter); + + //Hack for button + bar.firstChild.$ext.onmousemove({}); + } + + /** + * Destroy full state + */ + this.$cleanup = function(){ + var bar = this.$parentHBox.lastChild; + while (bar) { + if (bar.localName == "bar") { + var sections = bar.selectNodes("vbox"); + for (var i = 0; i < sections.length; i++) { + var buttons = sections[i].selectNodes("button"); + var menu = self[buttons[0].submenu]; + + for (var j = 0; j < buttons.length; j++) { + //Store pages + this.$cbStorePage(buttons[j].$dockpage); + } + + menu.destroy(true, true); + } + } + else if (!bar.bar) { + break; + } + + var next = bar.previousSibling; + bar.destroy(true, true); + bar = next; + } + } + + this.$isLastBar = function(aml) { + var last = this.$parentHBox.lastChild; + while (last && !last.visible) + last = last.previousSibling; + + return aml == last || aml == last.vbox; + } + + /** + * Starts the docking detection during drag&drop + */ + this.$startDrag = function (dragged, original){ + var last, state = 0, _self = this; + + apf.setOpacity(dragged.$ext, 0.3); + + function getLastBar(){ + var lastBar = _self.$parentHBox.lastChild; + while (lastBar && lastBar.previousSibling + && (lastBar.previousSibling.localName == "bar" + || lastBar.previousSibling.bar)) + lastBar = lastBar.previousSibling; + if (lastBar.localName != "bar") + lastBar = lastBar.bar; + + return lastBar; + } + + var lastBar = getLastBar(); + var leftEdge = apf.getAbsolutePosition(lastBar.$ext)[0]; + var indicator = this.indicator; + + apf.addListener(document, "mousemove", whiledrag = function(e){ + if (last) { + last.$ext.style.borderBottom = ""; + delete last; + } + + if (!e) return; + + var indicatorTop = indicator.style.top; + dragged.$ext.style.top = "-2000px"; + indicator.style.top = "-2000px"; + apf.plane.hide(); + + //Adding a column + if (e.clientX > leftEdge - 40 && e.clientX < leftEdge) { + var isSameColumn = dragged.localName == "vbox" + && dragged.$dockbar == lastBar + && !dragged.$dockbar.selectNodes("vbox").length; + + info = { + position : isSameColumn ? "none" : "left_of_column", + aml : aml = last = lastBar + } + } + //Rest + else { + var info = _self.$calcAction(e, original); + var aml = last = info.aml; + } + + if (lastInfo && lastInfo.position == info.position && lastInfo.aml == aml) { + indicator.style.top = indicatorTop; + return; + } + + lastInfo = info; + + if (!aml || !aml.dock && !aml.bar) { + if (!state) { + state = 1; + apf.tween.single(dragged.$ext, { + type: "fade", + from: 0.3, + to : 1, + steps : 20, + onfinish : function(){ + state = 1; + } + }); + } + return; + } + + var pos = apf.getAbsolutePosition(aml.$ext); + indicator.style.left = pos[0] + "px"; + indicator.style.top = pos[1] + "px"; + indicator.style.display = "block"; + indicator.style.backgroundColor = ""; + indicator.style.marginLeft = "0"; + indicator.innerHTML = ""; + + if (state) { + state = 0; + apf.tween.single(dragged.$ext, { + type: "fade", + from: 1, + to : 0.3, + steps : 20, + onfinish : function(){ + state = 0; + } + }); + } + + var width = aml.$ext.offsetWidth; + var height = aml.$ext.offsetHeight; + + switch(info.position) { + case "before_button": + case "after_button": + indicator.innerHTML = "
"; + indicator.style.borderWidth = "6px 1px 3px 1px"; + + var pos2 = apf.getAbsolutePosition(aml.parentNode.$ext); + indicator.style.left = pos2[0] + "px"; + indicator.style.top = pos2[1] + "px"; + width = aml.parentNode.$ext.offsetWidth; + height = aml.parentNode.$ext.offsetHeight; + + var div = indicator.firstChild; + if (aml == getOriginal("button", original)) { //@todo Checks needs to include different representations + div.style.top = (pos[1] - pos2[1] - 2) + "px"; + div.style.left = "2px"; + div.style.right = "3px"; + div.style.height = (aml.$ext.offsetHeight - 9) + "px"; + div.style.border = "2px solid #7ac7f4"; + div.style.webkitBorderRadius = "6px"; + } + else { + div.style.top = (pos[1] - pos2[1] + + (info.position == "before_button" ? 0 : aml.$ext.offsetHeight) + - 8) + "px"; + div.style.width = "100%"; + div.style.borderBottom = "3px solid #7ac7f4"; + } + + break; + case "in_section": + if (getOriginal("section", original) == aml.$dockfor) {//@todo move this + indicator.style.borderWidth = "1px 1px 1px 1px"; + height--; + } + break; + case "after_page": + case "before_page": + var pNode = aml.parentNode; + var pos2 = apf.getAbsolutePosition(pNode.$ext); + indicator.style.left = pos2[0] + "px"; + indicator.style.top = pos2[1] + "px"; + width = pNode.$ext.offsetWidth; + height = pNode.$ext.offsetHeight; + + indicator.style.borderWidth = "3px 3px 3px 3px"; + + var compareAml = info.position == "before_page" + ? aml.previousSibling + : aml.nextSibling; + var originalAml = getOriginal("page", original); + var matchAml = originalAml == aml + ? aml + : (originalAml == compareAml ? compareAml : false); + var diff = apf.getAbsolutePosition((matchAml || aml).$button, pNode.$ext); + if (matchAml) { + indicator.innerHTML = "
"; + var div1 = indicator.firstChild; + var div2 = indicator.childNodes[1]; + var div3 = indicator.childNodes[2]; + div1.style.left = diff[0] + "px"; + div1.style.width = (matchAml.$button.offsetWidth - 5) + "px"; + div1.style.height = "18px"; + div1.style.margin = "-18px 0 0 -3px"; + div1.style.border = "3px solid #7ac7f4"; + div1.style.borderWidth = "3px 3px 0 3px"; + + div2.style.left = (diff[0] + matchAml.$button.offsetWidth - 3) + "px"; + div2.style.right = "0px"; + div3.style.borderBottom = + div2.style.borderBottom = "3px solid #7ac7f4"; + + div3.style.left = "0px"; + div3.style.right = (width - diff[0] - 3) + "px"; + + indicator.style.borderTop = "0px solid #7ac7f4"; + indicator.style.top = (pos2[1] + 18) + "px"; + height -= 18; + } + else { + indicator.innerHTML = "
"; + indicator.firstChild.style.height = "16px"; + indicator.firstChild.style.width = "5px"; + indicator.firstChild.style.background = "rgba(122,199,244,0.5)"; + indicator.firstChild.style.top = "0px"; + indicator.firstChild.firstChild.style.background = "#7ac7f4"; + indicator.firstChild.firstChild.style.height = "100%"; + indicator.firstChild.firstChild.style.margin="0 2px 0 2px"; + + var left = (diff[0] + + (info.position == "before_page" ? 0 : aml.$button.offsetWidth)); + if (left) + left -= 5; + else { + indicator.firstChild.style.width = "2px"; + indicator.firstChild.firstChild.style.marginLeft = "0px"; + } + indicator.firstChild.style.left = left + "px"; + } + break; + case "before_tab": + height = 0; + case "after_tab": + indicator.style.left = pos[0] + "px"; + indicator.style.top = (pos[1] + height - (!aml.nextSibling ? 3 : 0)) + "px"; + indicator.style.height = "3px"; + indicator.style.width = width + "px"; + indicator.style.borderWidth = "0 0 0 0"; + indicator.style.backgroundColor = "rgba(122,199,244," + + (!aml.nextSibling ? 1 : 0.8) + ")"; + return; + case "before_section": + height = 0; + case "after_section": + indicator.style.left = pos[0] + "px"; + indicator.style.top = (pos[1] + height - 3) + "px"; + indicator.style.height = "5px"; + indicator.style.width = aml.$ext.offsetWidth + "px"; + indicator.style.borderWidth = "0 0 0 0"; + indicator.innerHTML = "
"; + indicator.firstChild.style.backgroundColor = "#7ac7f4"; + indicator.firstChild.style.height = "1px"; + indicator.style.backgroundColor = "rgba(122,199,244,0.5)"; + return; + case "in_column": + indicator.innerHTML = "
"; + indicator.style.borderWidth = "0 0 0 0"; + + var div = indicator.firstChild; + div.style.top = "100%"; + div.style.borderTop = "3px solid #7ac7f4" + div.style.height = (dragged.localName == "vbox" ? dragged.$ext.offsetHeight : 50) + "px"; + div.style.background = "rgba(172,172,172,0.5)"; + div.style.width = "100%"; + div.style.webkitBorderRadius = "0 0 4px 4px"; + + /*apf.tween.single(div, { + type: "height", + from: 0, + to : dragged.localName == "vbox" ? dragged.$ext.offsetHeight : 50, + anim : apf.tween.EASEOUT, + steps : 20 + });*/ + + break; + case "left_of_column": + if (aml != getLastBar()) { + indicator.style.borderWidth = "0 0 0 3px"; + indicator.style.marginLeft = "-1px"; + } + else { + indicator.innerHTML = "
"; + indicator.style.borderWidth = "0 0 0 0"; + + var div = indicator.firstChild; + div.style.right = "100%"; + div.style.width = 0; + div.style.height = "100%"; + div.style.borderRight = "3px solid #7ac7f4" + div.style.background = "rgba(172,172,172,0.5)"; + div.style.webkitBorderRadius = "4px 0 0 4px"; + + apf.tween.single(div, { + type: "width", + from: 0, + to : 40, + anim : apf.tween.EASEOUT, + steps : 20 + }); + } + break; + case "right_of_column": + indicator.style.borderWidth = "0 3px 0 0"; + if (!_self.$isLastBar(aml)) + indicator.style.marginLeft = "2px"; + break; + default: + indicator.style.display = "none"; + apf.setOpacity(dragged.$ext, 1); + break; + } + + var diff = apf.getDiff(indicator); + indicator.style.width = (width - diff[0]) + "px"; + indicator.style.height = (height - diff[1]) + "px"; + }); + + whiledrag.dragged = dragged; + whiledrag.original = original; + } + + /** + * Normalize types by converting them to the requested widget type of the + * conceptual single object + */ + function getOriginal(type, aml) { + if (type == "button") { + if (aml.localName == "page") + return aml.$dockbutton; + if (aml.localName == "divider") { + var buttons = aml.parentNode.selectNodes("button"); + if (buttons.length == 1) + return buttons[0]; + } + return aml; + } + else if (type == "page") { + if (aml.localName == "button") + return aml.$dockpage; + if (aml.localName == "divider") { + var buttons = aml.parentNode.selectNodes("button"); + if (buttons.length == 1) + return buttons[0].$dockpage; + } + return aml; + } + else if (type == "section") { + if (aml.localName == "page" && aml.parentNode.getPages().length == 1) + return aml.$dockbutton.parentNode; + if (aml.localName == "divider") + return aml.parentNode; + return aml; + } + } + + function matchTab(pos, y) { + return y > pos - diffPixel && y < pos + diffPixel; + } + + /** + * Calculate what action will be performed based on the relative location + * of the mouse cursor + */ + this.$calcAction = function(e, original){ + var position = "none"; + + var el = document.elementFromPoint(e.clientX, e.clientY); + if (el == document.body) + return {}; + + var aml = apf.findHost(el); + if (!aml) return {}; + + if (!aml.dock || aml.localName == "page" || aml.localName == "tab") { + var node = aml; + while (node && !node.vdock) + node = node.parentNode; + + if (node && node.localName == "vbox") { + var tabs = node.selectNodes("tab"); + var pos = apf.getAbsolutePosition(node.$ext)[1]; + var doTest = original.parentNode.localName == "tab" + && original.parentNode.getPages().length == 1; + + if (matchTab(tabs[0].$ext.offsetTop + pos, e.clientY)) { + return doTest && original.parentNode == tabs[0] + ? {} : {position: "before_tab", aml: tabs[0]}; + } + + for (var i = 0; i < tabs.length; i++) { + if (matchTab(tabs[i].$ext.offsetHeight + 1 + + tabs[i].$ext.offsetTop + pos - (!aml.nextSibling ? 3 : 0), e.clientY)) { + return doTest && (original.parentNode == tabs[i] || original.parentNode == tabs[i+1]) + ? {} : {position: "after_tab", aml: tabs[i]}; + } + } + } + } + + if (aml.localName == "splitter") { + aml.$ext.style.display = "none"; + aml = apf.findHost(document.elementFromPoint(e.clientX, e.clientY)); + aml.$ext.style.display = "block"; + } + + if (!aml.dock && !aml.bar) + return {}; + + var bar = aml; + while (bar && bar.localName != "bar" && (bar.localName != "vbox" || bar.dock)) + bar = bar.parentNode; + + if (bar) { + var pos = apf.getAbsolutePosition(e.target, bar.$ext); + var l = pos[0] + e.offsetX; + var r = bar.$ext.offsetWidth - l; + } + + if (bar && l < diffPixel) { + var isSameColumn = original.localName == "divider" + && (original.parentNode.$dockbar == bar + || original.parentNode.$dockbar == bar.previousSibling) + && !original.parentNode.$dockbar.selectNodes("vbox").length; + + return { + position : isSameColumn ? "none" : "left_of_column", + aml : bar + } + } + else { + var df = (this.$isLastBar(bar) + ? diffPixel * 2 + : diffPixel) + var isSameColumn = original.localName == "divider" + && (original.parentNode.$dockbar == bar + || original.parentNode.$dockbar == bar.nextSibling) + && !original.parentNode.$dockbar.selectNodes("vbox").length; + + if (bar && r < df) { + return { + position : isSameColumn ? "none" : "right_of_column", + aml : bar + } + } + } + + if (aml.localName == "page" || aml.localName == "tab" || aml.localName == "menu") { + position = "before_page"; + if (aml.localName == "page") { + var pos = apf.getAbsolutePosition(aml.$button); + var l = e.clientX - pos[0]; + + if (l > aml.$button.offsetWidth/2) + position = "after_page"; + } + else if (aml.localName == "menu") { + var pages = aml.firstChild.getPages(); + aml = pages[pages.length - 1]; + position = "after_page"; + } + else if (aml.localName == "tab") { + pages = aml.getPages(); + aml = pages[pages.length - 1]; + position = "after_page"; + } + + var pos2 = apf.getAbsolutePosition(aml.parentNode.$ext); + var t = e.clientY - pos2[1]; + if (t > 18) + return {}; + } + else { + if (aml.localName == "bar" || aml.skin == "dockheader") { + if (aml.skin == "dockheader") { + aml = aml.parentNode.selectNodes("vbox")[0]; + position = "before_section"; + } + else { + position = original.localName == "divider" && original.parentNode.$dockbar == aml + ? "in_section" + : "in_column"; + aml = aml.lastChild;/*selectNodes("vbox"); + aml = vboxs[vboxs.length - 1];*/ + } + } + else if (aml.localName == "button") { + position = "after_button"; + var pos = apf.getAbsolutePosition(aml.$ext); + var t = e.clientY - pos[1]; + if (t < aml.$ext.offsetHeight/2) { + if (aml.previousSibling && aml.previousSibling.localName == "button") + aml = aml.previousSibling + else { + position = "before_button"; + //aml = aml.parentNode; + } + } + } + else if (aml.localName == "divider" || aml.localName == "vbox") { + if (aml.localName == "divider") + aml = aml.parentNode; + + var buttons = aml.selectNodes("button"); + if (!buttons.length) + return {position: "in_section", aml: aml}; + + var pos = apf.getAbsolutePosition(aml.$ext); + var t = e.clientY - pos[1]; + var b = aml.$ext.offsetHeight - t; + + if (t < diffPixel) { + if (original.localName != "divider" + || original.parentNode != (aml.previousSibling + && aml.previousSibling.$dockfor)) { + position = "before_section"; + } + } + else if (b < diffPixel && aml.nextSibling) { + if (original.localName != "divider" + || original.parentNode != aml.$dockfor) { + if (!aml.nextSibling + || aml.nextSibling.$dockfor != getOriginal("section", original)) + position = "after_section"; + } + } + + if (position == "none") { + if (t < aml.$ext.offsetHeight/2) { + position = "before_button"; + aml = buttons[0]; + } + else { + position = "after_button"; + aml = buttons[buttons.length - 1]; + } + } + } + } + + return { + position : position, + aml : aml + } + } + + /** + * Cleanup after dragging (detect dropping) + */ + this.$stopDrag = function(e){ + whiledrag(); + apf.removeListener(document, "mousemove", whiledrag); + + + var indicator = this.indicator; + var info = lastInfo;//calcAction(e); + var aml = info && info.aml; + + indicator.style.display = "none"; + + var original = whiledrag.dragged; + apf.setOpacity(original.$ext, 1); + + if (!aml) return; + switch(info.position) { + case "before_button": + case "after_button": + var submenu = self[aml.submenu]; + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml, info.position == "before_button" + ? aml + : aml.nextSibling, aml.parentNode, info.position); + break; + case "before_tab": + case "after_tab": + var bar = aml.parentNode.bar; + var childNr = apf.getChildNumber(aml); + var sections = bar.selectNodes("vbox"); + var section = this.$addSection(bar, info.position == "before_tab" + ? sections[0] + : sections[childNr + 1]); + + //reconstruct menu + var submenu = this.$addMenu(section); + var dragAml = whiledrag.original; + + var tab = aml.parentNode.insertBefore(submenu.firstChild, info.position == "before_tab" + ? aml + : aml.nextSibling); + tab.setAttribute("flex", 1); + + moveTo(submenu, dragAml, tab, null, section, info.position, tab);//, null, pNode); + break; + case "before_section": + case "in_column": + case "after_section": + var section = this.$addSection(aml.parentNode, info.position == "before_section" + ? aml + : (info.position == "in_column" + ? null + : aml.nextSibling)); + + //reconstruct menu + var submenu = this.$addMenu(section); + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml, null, section, info.position); + break; + case "before_page": + case "after_page": + var submenu = self[aml.$dockbutton.submenu];//aml.parentNode.parentNode; + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml.parentNode, info.position == "before_page" + ? aml.$dockbutton + : aml.nextSibling && aml.nextSibling.$dockbutton, submenu.ref, + info.position, aml.parentNode); + break; + case "left_of_column": + var bar = this.$addBar(aml); + //Single Tab Case + //create new section + var section = this.$addSection(bar); + var submenu = this.$addMenu(section); + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml, null, section, info.position); + break; + case "right_of_column": + var bar = this.$addBar(aml.nextSibling); + //Single Tab Case + //create new section + var section = this.$addSection(bar); + + //reconstruct menu + var submenu = this.$addMenu(section); + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml, null, section, info.position); + break; + default: + break; + } + } + + /** + * Manages the move of a conceptual single element, represented by either + * a button, page or divider and performs the move from it's current position + * to it's new position. + */ + function moveTo(submenu, dragAml, aml, beforeButton, parentNode, position, tab, pNode){ + var beforePage = beforeButton && beforeButton.$dockpage; + + if (dragAml.localName == "page" || dragAml.localName == "button") { + if (dragAml.localName == "page") { + var page = dragAml; + var button = dragAml.$dockbutton; + } + else if (dragAml.localName == "button") { + var page = dragAml.$dockpage; + var button = dragAml; + } + if (!pNode) + pNode = page.parentNode; + var btnPNode = button.parentNode; + + var oldMenu = self[page.$dockbutton.submenu]; + + if (beforeButton && beforeButton.previousSibling == button || beforeButton == button + || !beforeButton && !button.nextSibling && button.parentNode == parentNode) + return; + + var newPNode = tab || submenu.firstChild; + if (newPNode) { + newPNode.insertBefore(page, beforePage); + + if (newPNode.getPages().length == 1) { + var totalFlex = 0, count = 0; + if (newPNode.parentNode.localName == "vbox") { + newPNode.parentNode.selectNodes("tab").each(function(tab){ + totalFlex += tab.flex || 1; + count++; + }); + } + else { + var tabs = parentNode.parentNode.selectNodes("vbox").each(function(vbox){ + var button = vbox.selectSingleNode("button"); + totalFlex += button && self[button.submenu].firstChild.flex || 1; + count++; + }); + } + newPNode.setAttribute("flex", totalFlex/count); + } + } + button.setAttribute("submenu", submenu.id); + + //add button to section + parentNode.insertBefore(button, beforeButton); + + if (!pNode.getPages().length) { + var barParent = btnPNode.parentNode; + oldMenu.destroy(true, true); + if (pNode.parentNode) + pNode.destroy(true, true); + btnPNode.destroy(true, true); + if (!barParent.selectNodes("vbox").length) { + barParent.destroy(true, true); + if (barParent.vbox) { + barParent.vbox.destroy(true, true); + barParent.splitter.destroy(true, true); + } + } + } + } + else if (dragAml.localName == "divider") { + var buttons = dragAml.parentNode.selectNodes("button"); + for (var i = buttons.length - 1; i >= 0; i--) { + var button = buttons[i]; + + moveTo(submenu, button, aml, beforeButton, parentNode, position, tab, pNode) + } + } + } + + /** + * Creates a new menu + */ + this.$addMenu = function(section){ + var menu = new apf.menu({ + id : "submenu" + menuCounter++, + width : "200", + height : "200", + ref : section, + pinned : "true", + animate : "false", + skin : "dockwindowbasic", + dock : 1, + ondisplay : function(){ + var pos = apf.getAbsolutePosition(this.opener.$ext); + var width = apf.getWindowWidth(); + this.$ext.style.marginLeft = (-1 * Math.min((width - pos[0]), this.$ext.offsetWidth)) + "px"; + }, + childNodes : [ + new apf.tab({ + anchors : "0 0 0 0", + skin : "docktab", + buttons : "scale,close", + dock : 1, + onclose : function(e){ + var page = e.page; + page.lastParent = this; + } + + }) + ] + }); + + apf.document.body.appendChild(menu); + menu.show(); + menu.hide(); + + return menu; + } + + /** + * Creates a new bar + */ + this.$addBar = function(before){ + this.columnCounter++; + + var _self = this; + var bar = this.$parentHBox.insertBefore(new apf.bar({ + skin : "debug-panel", + dock : 1, + onDOMNodeRemovedFromDocument : function(){ + _self.columnCounter--; + }, + childNodes : [ + new apf.button({ + dock : 1, + skin : "dockheader", + onclick : function(){ + _self.expandBar(this.parentNode); + } + }) + ] + }), before); + + return bar; + } + + /** + * Creates a new page + */ + this.$addPage = function(page, menu, caption, name){ + var _self = this; + + if (!page) + page = menu.firstChild.add(caption, name); + else + menu.firstChild.appendChild(page); + + page.oDrag = page.$button; + page.dock = 1; + page.setAttribute("draggable", true); + + page.addEventListener("beforedrag", function (e){ //change this to beforedrag and recompile apf + var origMenu = self[this.$dockbutton.submenu]; + /*var menu = origMenu.cloneNode(false); + menu.removeAttribute("id"); + apf.document.body.appendChild(menu);*/ + + var tab = this.parentNode.cloneNode(false); + tab.removeAttribute("id"); + tab.setAttribute("buttons", "close"); //@todo bug in scale that doesnt resize + tab.removeAttribute("anchors"); + apf.document.body.appendChild(tab); + tab.setWidth(this.parentNode.$ext.offsetWidth); + tab.setHeight(this.parentNode.$ext.offsetHeight); + + var page = this.cloneNode(true); + page.removeAttribute("id"); + tab.appendChild(page); + + if (origMenu.$ext.offsetHeight) { + var pos = apf.getAbsolutePosition(origMenu.$ext); + tab.setLeft(pos[0]); + tab.setTop(pos[1]); + } + else { + var pos = apf.getAbsolutePosition(this.parentNode.$ext); + tab.setLeft(pos[0] - 1); + tab.setTop(pos[1] - 2); + } + tab.$ext.style.border = "1px solid #333"; + //menu.$ext.style.margin = "0 0 0 0" + tab.addEventListener("afterdrag", function(e){ + tab.id = tab.name = ""; //@todo fix this bug in apf + tab.destroy(true, true); + _self.$stopDrag(e.htmlEvent); + }); + + //document instead? + var clientX = e.htmlEvent.clientX; + var clientY = e.htmlEvent.clientY; + tab.setAttribute("draggable", true); + setTimeout(function(){ + //@todo Collapse menu + + tab.$dragStart({clientX:clientX,clientY:clientY}); + tab.$ext.style.zIndex = 1000000; + }); + + _self.$startDrag(tab, this); + + return false; + }); + + page.addEventListener("afterclose", function(e){ + var button = this.$dockbutton; + var pNode = this.lastParent; + var btnPNode = button.parentNode; + + button.destroy(true, true); + + if (!pNode.getPages().length) { + var barParent = btnPNode.parentNode; + if (pNode.parentNode.localName == "menu") + pNode.parentNode.destroy(true, true); + else { + var menu = self[button.submenu]; + menu.destroy(true, true); + pNode.destroy(true, true); + } + btnPNode.destroy(true, true); + if (!barParent.selectNodes("vbox").length) { + barParent.destroy(true, true); + if (barParent.vbox) { + barParent.vbox.destroy(true, true); + barParent.splitter.destroy(true, true); + } + } + } + }); + + return page; + } + + /** + * Creates a new section + */ + this.$addSection = function(bar, before){ + var _self = this; + var section = bar.insertBefore(new apf.vbox({ + padding : 0, + edge : "0 0 3 0", + "class" : "docksection", + dock : 1, + childNodes : [ + new apf.divider({ + skin : "divider-debugpanel", + margin : "3 5 2 5", + dock : 1, + draggable : "true" + }) + ] + }), before); + + var div = section.firstChild; + div.addEventListener("beforedrag", function(e){ //change this to beforedrag and recompile apf + var section = this.parentNode; + + //this.hideMenu(); + + var pNode = section.$dockbar = section.parentNode; + var placeHolder = section.cloneNode(false); + placeHolder.removeAttribute("id"); + placeHolder.$dockfor = section; + + var diff = apf.getDiff(section.$ext); + var height = section.$ext.offsetHeight; + var pos = apf.getAbsolutePosition(section.$ext); + + pNode.insertBefore(placeHolder, section); + placeHolder.$ext.style.background = "#acacac"; + placeHolder.$ext.style.height = (height - diff[1]) + "px"; + + section.setWidth(section.$ext.offsetWidth); + apf.document.body.appendChild(section); + section.setLeft(pos[0]); + section.setTop(pos[1]); + + section.addEventListener("afterdrag", function(e){ + pNode.insertBefore(section, placeHolder); + section.setAttribute("draggable", false); + + setTimeout(function(){ + section.removeAttribute("left"); + section.removeAttribute("top"); + section.removeAttribute("width"); + section.$ext.style.position = "relative"; + }); + + var buttons = this.selectNodes("button"); + if (buttons.length) + buttons[0].setValue(false); + + _self.$stopDrag(e.htmlEvent); + placeHolder.destroy(true, true); + }); + + section.setAttribute("draggable", true); + + var clientX = e.htmlEvent.clientX; + var clientY = e.htmlEvent.clientY; + setTimeout(function(){ + //@todo Collapse menu + + section.$dragStart({clientX:clientX,clientY:clientY}); + section.$ext.style.zIndex = 1000000; + }); + + _self.$startDrag(section, this); + + return false; + }); + + return section; + } + + /** + * Creates a new button + */ + this.$addButton = function(section, submenu, page){ + var _self = this; + var button = section.appendChild(new apf.button({ + "class" : "dockButtonID", + skin : "dockButton", + submenu : submenu.id, + dock : 1, + draggable : "true" + })); + + button.addEventListener("beforedrag", function(e){ //change this to beforedrag and recompile apf + var originalButton = this; + + this.hideMenu(); + this.setValue(true); + + //Upgrade to container if only 1 element + if (this.parentNode.selectNodes("button").length == 1) { + this.parentNode.firstChild.dispatchEvent("beforedrag", e); + return false; + } + + var btn = this.cloneNode(true); + btn.removeAttribute("id"); + apf.document.body.appendChild(btn); + btn.setValue(true); + + var pos = apf.getAbsolutePosition(this.$ext); + btn.setLeft(pos[0]); + btn.setTop(pos[1]); + btn.addEventListener("afterdrag", function(e){ + btn.destroy(true, true); + originalButton.setValue(false); + _self.$stopDrag(e.htmlEvent); + }); + + //document instead? + var clientX = e.htmlEvent.clientX; + var clientY = e.htmlEvent.clientY; + setTimeout(function(){ + btn.$dragStart({clientX:clientX,clientY:clientY}); + btn.$ext.style.zIndex = 1000000; + this.removeEventListener("mouseover", arguments.callee); + }); + + _self.$startDrag(btn, this); + + return false; + }); + + page.$dockbutton = button; + button.$dockpage = page; + + return button; + } +}).call(DockableLayout.prototype); + +var layout = new DockableLayout(hboxMain, function(){}, function(){}); +layout.loadState(testState); +layout.loadState(layout.getState()); \ No newline at end of file diff --git a/client/ext/dockpanel/docktest.html b/client/ext/dockpanel/docktest.html new file mode 100644 index 00000000000..2a967491369 --- /dev/null +++ b/client/ext/dockpanel/docktest.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/ext/dockpanel/docktest.js b/client/ext/dockpanel/docktest.js new file mode 100644 index 00000000000..2da0728545c --- /dev/null +++ b/client/ext/dockpanel/docktest.js @@ -0,0 +1,80 @@ + +var testState = { + bars : [ + { + expanded : false, + width : 300, + sections : [ + { + flex : 1, + width : 300, + height : 200, + buttons : [ + { + caption: "Test4", + ext : "" + }, + { + caption: "Test3", + ext : "" + } + ] + }, + { + flex : 1, + width : 200, + height : 300, + buttons : [ + { + caption: "Test2", + ext : "" + } + ] + }, + { + flex : 1, + width : 200, + height : 200, + buttons : [ + { + caption: "Test1", + ext : "" + } + ] + } + ] + }, + { + expanded : true, + width : 200, + sections : [ + { + flex : 1, + width : 200, + height : 200, + buttons : [ + { + caption: "Test2", + ext : "" + } + ] + }, + { + flex : 1, + width : 200, + height : 200, + buttons : [ + { + caption: "Test1", + ext : "" + } + ] + } + ] + } + ] +}; + +var layout = new DockableLayout(hboxMain, function(){}, function(){}); +layout.loadState(testState); +layout.loadState(layout.getState()); \ No newline at end of file diff --git a/client/ext/dockpanel/libdock.js b/client/ext/dockpanel/libdock.js new file mode 100644 index 00000000000..5967bbd864a --- /dev/null +++ b/client/ext/dockpanel/libdock.js @@ -0,0 +1,1295 @@ +/* + TODO: + - expanded state tab dragging + - insertbefore tab reorder doesnt create a new splitter bar + - multiple columns: splitter bars don't function as expected: should be non-symmetrical + - dragging forelasttab out of expanded column leaves splitter + - after_tab/before_tab lacks splitter and tab page doesnt animate + + - single page should drag whole tab like button to section does + - anim should wait x00ms before playing + + - tweak tab animations + - menu should appear onmouseup not down + - floating sections or menus + + INTEGRATION + - refactor into seperate class + - closing a window should set the state in the windows menu + - debugger plugin doesnt need to be visible at the start anymore + - add right click menu to buttons/sections + - maintain state of sections/buttons even when closed + - save serialized state in settings.xml + + - page closing should call cbStorePage +*/ + +function DockableLayout(parentHBox, cbFindPage, cbStorePage) { + this.columnCounter = 0; + this.$parentHBox = parentHBox; + this.$cbStorePage = cbStorePage; + this.$cbFindPage = cbFindPage; + + var indicator = this.indicator = document.body.appendChild(document.createElement("div")); + indicator.style.position = "absolute"; + indicator.style.display = "none"; + indicator.style.border = "3px solid #7ac7f4"; + indicator.style.zIndex = 1000000; +}; + +(function(){ + var whiledrag, lastInfo, diffPixel = 3; + var menuCounter = 100; + + /** + * Retrieve the current state of the layout as a JSON object + * + */ + this.getState = function(){ + var state = {bars: []}; + + var bar = this.$parentHBox.lastChild; + while (bar) { + if (bar.localName == "bar") { + var barInfo = {sections: []}; + barInfo.expanded = bar.vbox && bar.vbox.visible; + barInfo.width = bar.vbox && bar.vbox.width + || bar.$dockData && bar.$dockData.width + || 200; + + var sections = bar.selectNodes("vbox"); + for (var i = 0; i < sections.length; i++) { + var sectionInfo = {buttons: []}; + var buttons = sections[i].selectNodes("button"); + sectionInfo.flex = buttons[0].$dockpage.parentNode.flex || 1; + + var menu = self[buttons[0].submenu]; + sectionInfo.width = menu.width; + sectionInfo.height = menu.height; + + for (var j = 0; j < buttons.length; j++) { + var buttonInfo = {}; + buttonInfo.ext = buttons[j].$dockpage.extension; + buttonInfo.caption = buttons[j].$dockpage.caption; + + sectionInfo.buttons.push(buttonInfo); + } + barInfo.sections.push(sectionInfo); + } + + state.bars.unshift(barInfo); + } + else if (!bar.bar) { + break; + } + + bar = bar.previousSibling; + } + + return state; + } + + /** + * Set the current layout via a JSON object + * @param {Object} obj JSON object with the following structure: + * { + * bars : [ + * { + * expanded : false, + * width : 300, + * sections : [ + * { + * flex : 1, + * width : 200, + * height : 200, + * buttons : [ + * { + * + * } + * ] + * } + * ] + * } + * ] + * } + * + */ + this.loadState = function(obj){ + this.clearState(); + + var bar, bars = obj.bars; + for (var i = 0; i < bars.length; i++) { + if (bars[i].ref) + bar = bars[i]; + else { + bar = this.$addBar(); + bar.$dockData = bars[i]; + } + + var sections = bars[i].sections; + for (var j = 0; j < sections.length; j++) { + var section = this.$addSection(bar); + var menu = this.$addMenu(section); + var info = section.$dockData = sections[j]; + + menu.firstChild.setAttribute("flex", info.flex); + menu.setAttribute("width", info.width); + menu.setAttribute("height", info.height); + + var buttons = sections[j].buttons + for (var k = 0; k < buttons.length; k++) { + var button = this.$addButton(section, menu, + this.$addPage( + this.$cbFindPage(buttons[k].ext), + menu, + buttons[k].caption, + buttons[k].caption.toLowerCase() + ) + ); + button.$dockData = buttons[k]; + } + } + + if (bars[i].expanded) + this.expandBar(bar); + } + } + + /** + * Destroy full state + */ + this.clearState = function(){ + var bar = this.$parentHBox.lastChild; + while (bar) { + if (bar.localName == "bar") { + var sections = bar.selectNodes("vbox"); + for (var i = 0; i < sections.length; i++) { + var buttons = sections[i].selectNodes("button"); + var menu = self[buttons[0].submenu]; + + for (var j = 0; j < buttons.length; j++) { + //Store pages + this.$cbStorePage(buttons[j].$dockpage); + } + + menu.destroy(true, true); + } + } + else if (!bar.bar) { + break; + } + + var next = bar.previousSibling; + bar.destroy(true, true); + bar = next; + } + } + + this.addItem = function(def){ + var bar = this.$getLastBar(); + this.loadState({ + bars : [ + { + ref : bar, + sections : [ + def.buttons ? def : { + buttons : [ + def + ] + } + ] + } + ] + }); + } + + /** + * Expand a bar + */ + this.expandBar = function (bar){ + if (!bar.vbox) { + var _self = this; + bar.vbox = bar.parentNode.insertBefore(new apf.vbox({ + padding : 3, + width : bar.$dockData && bar.$dockData.width || 260, + splitters : true, + vdock : 1, + "class" : "dockcol unselectable", + childNodes : [ + new apf.button({ + dock : 1, + skin : "dockheader", + "class" : "expanded", + nosplitter : true, + height : 11, + margin : "0 0 -3 0", + onclick : function(){ + _self.collapseBar(bar); + } + }) + ] + }), bar); + + //style hack + bar.vbox.$ext.style.borderLeft = "1px solid #333"; + + bar.splitter = bar.parentNode.insertBefore(new apf.splitter({ + width : "0" + }), bar.vbox); + + bar.splitter.bar = + bar.vbox.bar = bar; + } + else { + bar.parentNode.insertBefore(bar.vbox, bar); + bar.parentNode.insertBefore(bar.splitter, bar.vbox); + } + + var vbox = bar.selectNodes("vbox"); + for (var i = 0; i < vbox.length; i++) { + var menu = self[vbox[i].selectSingleNode("button").submenu] + menu.hide(); + var tab = menu.firstChild; + bar.vbox.appendChild(tab); + if (!tab.flex) + tab.setAttribute("flex", 1); + } + + bar.hide(); + bar.vbox.show(); + bar.splitter.show(); + + //Hack for button + bar.vbox.firstChild.$ext.onmousemove({}); + } + + /** + * Collapse a bar + */ + this.collapseBar = function(bar){ + var vbox = bar.selectNodes("vbox"); + var tabs = bar.vbox.selectNodes("tab"); + for (var i = 0; i < vbox.length; i++) { + var menu = self[vbox[i].selectSingleNode("button").submenu]; + menu.appendChild(tabs[i]); + } + + bar.show(); + bar.vbox.hide(); + bar.splitter.hide(); + + bar.parentNode.removeChild(bar.vbox); + bar.parentNode.removeChild(bar.splitter); + + //Hack for button + bar.firstChild.$ext.onmousemove({}); + } + + this.$isLastBar = function(aml) { + var last = this.$parentHBox.lastChild; + while (last && !last.visible) + last = last.previousSibling; + + return aml == last || aml == last.vbox; + } + + this.$getLastBar = function(){ + var lastBar = _self.$parentHBox.lastChild; + while (lastBar && lastBar.previousSibling + && (lastBar.previousSibling.localName == "bar" + || lastBar.previousSibling.bar)) + lastBar = lastBar.previousSibling; + if (lastBar.localName != "bar") + lastBar = lastBar.bar; + + return lastBar; + } + + /** + * Starts the docking detection during drag&drop + */ + this.$startDrag = function (dragged, original){ + var last, state = 0, _self = this; + + apf.setOpacity(dragged.$ext, 0.3); + + var lastBar = this.$getLastBar(); + var leftEdge = apf.getAbsolutePosition(lastBar.$ext)[0]; + var indicator = this.indicator; + + apf.addListener(document, "mousemove", whiledrag = function(e){ + if (last) { + last.$ext.style.borderBottom = ""; + delete last; + } + + if (!e) return; + + var indicatorTop = indicator.style.top; + dragged.$ext.style.top = "-2000px"; + indicator.style.top = "-2000px"; + apf.plane.hide(); + + //Adding a column + if (e.clientX > leftEdge - 40 && e.clientX < leftEdge) { + var isSameColumn = dragged.localName == "vbox" + && dragged.$dockbar == lastBar + && !dragged.$dockbar.selectNodes("vbox").length; + + info = { + position : isSameColumn ? "none" : "left_of_column", + aml : aml = last = lastBar + } + } + //Rest + else { + var info = _self.$calcAction(e, original); + var aml = last = info.aml; + } + + if (lastInfo && lastInfo.position == info.position && lastInfo.aml == aml) { + indicator.style.top = indicatorTop; + return; + } + + lastInfo = info; + + if (!aml || !aml.dock && !aml.bar) { + if (!state) { + state = 1; + apf.tween.single(dragged.$ext, { + type: "fade", + from: 0.3, + to : 1, + steps : 20, + onfinish : function(){ + state = 1; + } + }); + } + return; + } + + var pos = apf.getAbsolutePosition(aml.$ext); + indicator.style.left = pos[0] + "px"; + indicator.style.top = pos[1] + "px"; + indicator.style.display = "block"; + indicator.style.backgroundColor = ""; + indicator.style.marginLeft = "0"; + indicator.innerHTML = ""; + + if (state) { + state = 0; + apf.tween.single(dragged.$ext, { + type: "fade", + from: 1, + to : 0.3, + steps : 20, + onfinish : function(){ + state = 0; + } + }); + } + + var width = aml.$ext.offsetWidth; + var height = aml.$ext.offsetHeight; + + switch(info.position) { + case "before_button": + case "after_button": + indicator.innerHTML = "
"; + indicator.style.borderWidth = "6px 1px 3px 1px"; + + var pos2 = apf.getAbsolutePosition(aml.parentNode.$ext); + indicator.style.left = pos2[0] + "px"; + indicator.style.top = pos2[1] + "px"; + width = aml.parentNode.$ext.offsetWidth; + height = aml.parentNode.$ext.offsetHeight; + + var div = indicator.firstChild; + if (aml == getOriginal("button", original)) { //@todo Checks needs to include different representations + div.style.top = (pos[1] - pos2[1] - 2) + "px"; + div.style.left = "2px"; + div.style.right = "3px"; + div.style.height = (aml.$ext.offsetHeight - 9) + "px"; + div.style.border = "2px solid #7ac7f4"; + div.style.webkitBorderRadius = "6px"; + } + else { + div.style.top = (pos[1] - pos2[1] + + (info.position == "before_button" ? 0 : aml.$ext.offsetHeight) + - 8) + "px"; + div.style.width = "100%"; + div.style.borderBottom = "3px solid #7ac7f4"; + } + + break; + case "in_section": + if (getOriginal("section", original) == aml.$dockfor) {//@todo move this + indicator.style.borderWidth = "1px 1px 1px 1px"; + height--; + } + break; + case "after_page": + case "before_page": + var pNode = aml.parentNode; + var pos2 = apf.getAbsolutePosition(pNode.$ext); + indicator.style.left = pos2[0] + "px"; + indicator.style.top = pos2[1] + "px"; + width = pNode.$ext.offsetWidth; + height = pNode.$ext.offsetHeight; + + indicator.style.borderWidth = "3px 3px 3px 3px"; + + var compareAml = info.position == "before_page" + ? aml.previousSibling + : aml.nextSibling; + var originalAml = getOriginal("page", original); + var matchAml = originalAml == aml + ? aml + : (originalAml == compareAml ? compareAml : false); + var diff = apf.getAbsolutePosition((matchAml || aml).$button, pNode.$ext); + if (matchAml) { + indicator.innerHTML = "
"; + var div1 = indicator.firstChild; + var div2 = indicator.childNodes[1]; + var div3 = indicator.childNodes[2]; + div1.style.left = diff[0] + "px"; + div1.style.width = (matchAml.$button.offsetWidth - 5) + "px"; + div1.style.height = "18px"; + div1.style.margin = "-18px 0 0 -3px"; + div1.style.border = "3px solid #7ac7f4"; + div1.style.borderWidth = "3px 3px 0 3px"; + + div2.style.left = (diff[0] + matchAml.$button.offsetWidth - 3) + "px"; + div2.style.right = "0px"; + div3.style.borderBottom = + div2.style.borderBottom = "3px solid #7ac7f4"; + + div3.style.left = "0px"; + div3.style.right = (width - diff[0] - 3) + "px"; + + indicator.style.borderTop = "0px solid #7ac7f4"; + indicator.style.top = (pos2[1] + 18) + "px"; + height -= 18; + } + else { + indicator.innerHTML = "
"; + indicator.firstChild.style.height = "16px"; + indicator.firstChild.style.width = "5px"; + indicator.firstChild.style.background = "rgba(122,199,244,0.5)"; + indicator.firstChild.style.top = "0px"; + indicator.firstChild.firstChild.style.background = "#7ac7f4"; + indicator.firstChild.firstChild.style.height = "100%"; + indicator.firstChild.firstChild.style.margin="0 2px 0 2px"; + + var left = (diff[0] + + (info.position == "before_page" ? 0 : aml.$button.offsetWidth)); + if (left) + left -= 5; + else { + indicator.firstChild.style.width = "2px"; + indicator.firstChild.firstChild.style.marginLeft = "0px"; + } + indicator.firstChild.style.left = left + "px"; + } + break; + case "before_tab": + height = 0; + case "after_tab": + indicator.style.left = pos[0] + "px"; + indicator.style.top = (pos[1] + height - (!aml.nextSibling ? 3 : 0)) + "px"; + indicator.style.height = "3px"; + indicator.style.width = width + "px"; + indicator.style.borderWidth = "0 0 0 0"; + indicator.style.backgroundColor = "rgba(122,199,244," + + (!aml.nextSibling ? 1 : 0.8) + ")"; + return; + case "before_section": + height = 0; + case "after_section": + indicator.style.left = pos[0] + "px"; + indicator.style.top = (pos[1] + height - 3) + "px"; + indicator.style.height = "5px"; + indicator.style.width = aml.$ext.offsetWidth + "px"; + indicator.style.borderWidth = "0 0 0 0"; + indicator.innerHTML = "
"; + indicator.firstChild.style.backgroundColor = "#7ac7f4"; + indicator.firstChild.style.height = "1px"; + indicator.style.backgroundColor = "rgba(122,199,244,0.5)"; + return; + case "in_column": + indicator.innerHTML = "
"; + indicator.style.borderWidth = "0 0 0 0"; + + var div = indicator.firstChild; + div.style.top = "100%"; + div.style.borderTop = "3px solid #7ac7f4" + div.style.height = (dragged.localName == "vbox" ? dragged.$ext.offsetHeight : 50) + "px"; + div.style.background = "rgba(172,172,172,0.5)"; + div.style.width = "100%"; + div.style.webkitBorderRadius = "0 0 4px 4px"; + + /*apf.tween.single(div, { + type: "height", + from: 0, + to : dragged.localName == "vbox" ? dragged.$ext.offsetHeight : 50, + anim : apf.tween.EASEOUT, + steps : 20 + });*/ + + break; + case "left_of_column": + if (aml != getLastBar()) { + indicator.style.borderWidth = "0 0 0 3px"; + indicator.style.marginLeft = "-1px"; + } + else { + indicator.innerHTML = "
"; + indicator.style.borderWidth = "0 0 0 0"; + + var div = indicator.firstChild; + div.style.right = "100%"; + div.style.width = 0; + div.style.height = "100%"; + div.style.borderRight = "3px solid #7ac7f4" + div.style.background = "rgba(172,172,172,0.5)"; + div.style.webkitBorderRadius = "4px 0 0 4px"; + + apf.tween.single(div, { + type: "width", + from: 0, + to : 40, + anim : apf.tween.EASEOUT, + steps : 20 + }); + } + break; + case "right_of_column": + indicator.style.borderWidth = "0 3px 0 0"; + if (!_self.$isLastBar(aml)) + indicator.style.marginLeft = "2px"; + break; + default: + indicator.style.display = "none"; + apf.setOpacity(dragged.$ext, 1); + break; + } + + var diff = apf.getDiff(indicator); + indicator.style.width = (width - diff[0]) + "px"; + indicator.style.height = (height - diff[1]) + "px"; + }); + + whiledrag.dragged = dragged; + whiledrag.original = original; + } + + /** + * Normalize types by converting them to the requested widget type of the + * conceptual single object + */ + function getOriginal(type, aml) { + if (type == "button") { + if (aml.localName == "page") + return aml.$dockbutton; + if (aml.localName == "divider") { + var buttons = aml.parentNode.selectNodes("button"); + if (buttons.length == 1) + return buttons[0]; + } + return aml; + } + else if (type == "page") { + if (aml.localName == "button") + return aml.$dockpage; + if (aml.localName == "divider") { + var buttons = aml.parentNode.selectNodes("button"); + if (buttons.length == 1) + return buttons[0].$dockpage; + } + return aml; + } + else if (type == "section") { + if (aml.localName == "page" && aml.parentNode.getPages().length == 1) + return aml.$dockbutton.parentNode; + if (aml.localName == "divider") + return aml.parentNode; + return aml; + } + } + + function matchTab(pos, y) { + return y > pos - diffPixel && y < pos + diffPixel; + } + + /** + * Calculate what action will be performed based on the relative location + * of the mouse cursor + */ + this.$calcAction = function(e, original){ + var position = "none"; + + var el = document.elementFromPoint(e.clientX, e.clientY); + if (el == document.body) + return {}; + + var aml = apf.findHost(el); + if (!aml) return {}; + + if (!aml.dock || aml.localName == "page" || aml.localName == "tab") { + var node = aml; + while (node && !node.vdock) + node = node.parentNode; + + if (node && node.localName == "vbox") { + var tabs = node.selectNodes("tab"); + var pos = apf.getAbsolutePosition(node.$ext)[1]; + var doTest = original.parentNode.localName == "tab" + && original.parentNode.getPages().length == 1; + + if (matchTab(tabs[0].$ext.offsetTop + pos, e.clientY)) { + return doTest && original.parentNode == tabs[0] + ? {} : {position: "before_tab", aml: tabs[0]}; + } + + for (var i = 0; i < tabs.length; i++) { + if (matchTab(tabs[i].$ext.offsetHeight + 1 + + tabs[i].$ext.offsetTop + pos - (!aml.nextSibling ? 3 : 0), e.clientY)) { + return doTest && (original.parentNode == tabs[i] || original.parentNode == tabs[i+1]) + ? {} : {position: "after_tab", aml: tabs[i]}; + } + } + } + } + + if (aml.localName == "splitter") { + aml.$ext.style.display = "none"; + aml = apf.findHost(document.elementFromPoint(e.clientX, e.clientY)); + aml.$ext.style.display = "block"; + } + + if (!aml.dock && !aml.bar) + return {}; + + var bar = aml; + while (bar && bar.localName != "bar" && (bar.localName != "vbox" || bar.dock)) + bar = bar.parentNode; + + if (bar) { + var pos = apf.getAbsolutePosition(e.target, bar.$ext); + var l = pos[0] + e.offsetX; + var r = bar.$ext.offsetWidth - l; + } + + if (bar && l < diffPixel) { + var isSameColumn = original.localName == "divider" + && (original.parentNode.$dockbar == bar + || original.parentNode.$dockbar == bar.previousSibling) + && !original.parentNode.$dockbar.selectNodes("vbox").length; + + return { + position : isSameColumn ? "none" : "left_of_column", + aml : bar + } + } + else { + var df = (this.$isLastBar(bar) + ? diffPixel * 2 + : diffPixel) + var isSameColumn = original.localName == "divider" + && (original.parentNode.$dockbar == bar + || original.parentNode.$dockbar == bar.nextSibling) + && !original.parentNode.$dockbar.selectNodes("vbox").length; + + if (bar && r < df) { + return { + position : isSameColumn ? "none" : "right_of_column", + aml : bar + } + } + } + + if (aml.localName == "page" || aml.localName == "tab" || aml.localName == "menu") { + position = "before_page"; + if (aml.localName == "page") { + var pos = apf.getAbsolutePosition(aml.$button); + var l = e.clientX - pos[0]; + + if (l > aml.$button.offsetWidth/2) + position = "after_page"; + } + else if (aml.localName == "menu") { + var pages = aml.firstChild.getPages(); + aml = pages[pages.length - 1]; + position = "after_page"; + } + else if (aml.localName == "tab") { + pages = aml.getPages(); + aml = pages[pages.length - 1]; + position = "after_page"; + } + + var pos2 = apf.getAbsolutePosition(aml.parentNode.$ext); + var t = e.clientY - pos2[1]; + if (t > 18) + return {}; + } + else { + if (aml.localName == "bar" || aml.skin == "dockheader") { + if (aml.skin == "dockheader") { + aml = aml.parentNode.selectNodes("vbox")[0]; + position = "before_section"; + } + else { + position = original.localName == "divider" && original.parentNode.$dockbar == aml + ? "in_section" + : "in_column"; + aml = aml.lastChild;/*selectNodes("vbox"); + aml = vboxs[vboxs.length - 1];*/ + } + } + else if (aml.localName == "button") { + position = "after_button"; + var pos = apf.getAbsolutePosition(aml.$ext); + var t = e.clientY - pos[1]; + if (t < aml.$ext.offsetHeight/2) { + if (aml.previousSibling && aml.previousSibling.localName == "button") + aml = aml.previousSibling + else { + position = "before_button"; + //aml = aml.parentNode; + } + } + } + else if (aml.localName == "divider" || aml.localName == "vbox") { + if (aml.localName == "divider") + aml = aml.parentNode; + + var buttons = aml.selectNodes("button"); + if (!buttons.length) + return {position: "in_section", aml: aml}; + + var pos = apf.getAbsolutePosition(aml.$ext); + var t = e.clientY - pos[1]; + var b = aml.$ext.offsetHeight - t; + + if (t < diffPixel) { + if (original.localName != "divider" + || original.parentNode != (aml.previousSibling + && aml.previousSibling.$dockfor)) { + position = "before_section"; + } + } + else if (b < diffPixel && aml.nextSibling) { + if (original.localName != "divider" + || original.parentNode != aml.$dockfor) { + if (!aml.nextSibling + || aml.nextSibling.$dockfor != getOriginal("section", original)) + position = "after_section"; + } + } + + if (position == "none") { + if (t < aml.$ext.offsetHeight/2) { + position = "before_button"; + aml = buttons[0]; + } + else { + position = "after_button"; + aml = buttons[buttons.length - 1]; + } + } + } + } + + return { + position : position, + aml : aml + } + } + + /** + * clearState after dragging (detect dropping) + */ + this.$stopDrag = function(e){ + whiledrag(); + apf.removeListener(document, "mousemove", whiledrag); + + + var indicator = this.indicator; + var info = lastInfo;//calcAction(e); + var aml = info && info.aml; + + indicator.style.display = "none"; + + var original = whiledrag.dragged; + apf.setOpacity(original.$ext, 1); + + if (!aml) return; + switch(info.position) { + case "before_button": + case "after_button": + var submenu = self[aml.submenu]; + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml, info.position == "before_button" + ? aml + : aml.nextSibling, aml.parentNode, info.position); + break; + case "before_tab": + case "after_tab": + var bar = aml.parentNode.bar; + var childNr = apf.getChildNumber(aml); + var sections = bar.selectNodes("vbox"); + var section = this.$addSection(bar, info.position == "before_tab" + ? sections[0] + : sections[childNr + 1]); + + //reconstruct menu + var submenu = this.$addMenu(section); + var dragAml = whiledrag.original; + + var tab = aml.parentNode.insertBefore(submenu.firstChild, info.position == "before_tab" + ? aml + : aml.nextSibling); + tab.setAttribute("flex", 1); + + moveTo(submenu, dragAml, tab, null, section, info.position, tab);//, null, pNode); + break; + case "before_section": + case "in_column": + case "after_section": + var section = this.$addSection(aml.parentNode, info.position == "before_section" + ? aml + : (info.position == "in_column" + ? null + : aml.nextSibling)); + + //reconstruct menu + var submenu = this.$addMenu(section); + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml, null, section, info.position); + break; + case "before_page": + case "after_page": + var submenu = self[aml.$dockbutton.submenu];//aml.parentNode.parentNode; + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml.parentNode, info.position == "before_page" + ? aml.$dockbutton + : aml.nextSibling && aml.nextSibling.$dockbutton, submenu.ref, + info.position, aml.parentNode); + break; + case "left_of_column": + var bar = this.$addBar(aml); + //Single Tab Case + //create new section + var section = this.$addSection(bar); + var submenu = this.$addMenu(section); + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml, null, section, info.position); + break; + case "right_of_column": + var bar = this.$addBar(aml.nextSibling); + //Single Tab Case + //create new section + var section = this.$addSection(bar); + + //reconstruct menu + var submenu = this.$addMenu(section); + var dragAml = whiledrag.original; + + moveTo(submenu, dragAml, aml, null, section, info.position); + break; + default: + break; + } + } + + /** + * Manages the move of a conceptual single element, represented by either + * a button, page or divider and performs the move from it's current position + * to it's new position. + */ + function moveTo(submenu, dragAml, aml, beforeButton, parentNode, position, tab, pNode){ + var beforePage = beforeButton && beforeButton.$dockpage; + + if (dragAml.localName == "page" || dragAml.localName == "button") { + if (dragAml.localName == "page") { + var page = dragAml; + var button = dragAml.$dockbutton; + } + else if (dragAml.localName == "button") { + var page = dragAml.$dockpage; + var button = dragAml; + } + if (!pNode) + pNode = page.parentNode; + var btnPNode = button.parentNode; + + var oldMenu = self[page.$dockbutton.submenu]; + + if (beforeButton && beforeButton.previousSibling == button || beforeButton == button + || !beforeButton && !button.nextSibling && button.parentNode == parentNode) + return; + + var newPNode = tab || submenu.firstChild; + if (newPNode) { + newPNode.insertBefore(page, beforePage); + + if (newPNode.getPages().length == 1) { + var totalFlex = 0, count = 0; + if (newPNode.parentNode.localName == "vbox") { + newPNode.parentNode.selectNodes("tab").each(function(tab){ + totalFlex += tab.flex || 1; + count++; + }); + } + else { + var tabs = parentNode.parentNode.selectNodes("vbox").each(function(vbox){ + var button = vbox.selectSingleNode("button"); + totalFlex += button && self[button.submenu].firstChild.flex || 1; + count++; + }); + } + newPNode.setAttribute("flex", totalFlex/count); + } + } + button.setAttribute("submenu", submenu.id); + + //add button to section + parentNode.insertBefore(button, beforeButton); + + if (!pNode.getPages().length) { + var barParent = btnPNode.parentNode; + oldMenu.destroy(true, true); + if (pNode.parentNode) + pNode.destroy(true, true); + btnPNode.destroy(true, true); + if (!barParent.selectNodes("vbox").length) { + barParent.destroy(true, true); + if (barParent.vbox) { + barParent.vbox.destroy(true, true); + barParent.splitter.destroy(true, true); + } + } + } + } + else if (dragAml.localName == "divider") { + var buttons = dragAml.parentNode.selectNodes("button"); + for (var i = buttons.length - 1; i >= 0; i--) { + var button = buttons[i]; + + moveTo(submenu, button, aml, beforeButton, parentNode, position, tab, pNode) + } + } + } + + /** + * Creates a new menu + */ + this.$addMenu = function(section){ + var menu = new apf.menu({ + id : "submenu" + menuCounter++, + width : "200", + height : "200", + ref : section, + pinned : "true", + animate : "false", + skin : "dockwindowbasic", + dock : 1, + ondisplay : function(){ + var pos = apf.getAbsolutePosition(this.opener.$ext); + var width = apf.getWindowWidth(); + this.$ext.style.marginLeft = (-1 * Math.min((width - pos[0]), this.$ext.offsetWidth)) + "px"; + }, + childNodes : [ + new apf.tab({ + anchors : "0 0 0 0", + skin : "docktab", + buttons : "scale,close", + dock : 1, + onclose : function(e){ + var page = e.page; + page.lastParent = this; + } + + }) + ] + }); + + apf.document.body.appendChild(menu); + menu.show(); + menu.hide(); + + return menu; + } + + /** + * Creates a new bar + */ + this.$addBar = function(before){ + this.columnCounter++; + + var _self = this; + var bar = this.$parentHBox.insertBefore(new apf.bar({ + skin : "debug-panel", + dock : 1, + onDOMNodeRemovedFromDocument : function(){ + _self.columnCounter--; + }, + childNodes : [ + new apf.button({ + dock : 1, + skin : "dockheader", + onclick : function(){ + _self.expandBar(this.parentNode); + } + }) + ] + }), before); + + return bar; + } + + /** + * Creates a new page + */ + this.$addPage = function(page, menu, caption, name){ + var _self = this; + + if (!page) + page = menu.firstChild.add(caption, name); + else + menu.firstChild.appendChild(page); + + page.oDrag = page.$button; + page.dock = 1; + page.setAttribute("draggable", true); + + page.addEventListener("beforedrag", function (e){ //change this to beforedrag and recompile apf + var origMenu = self[this.$dockbutton.submenu]; + /*var menu = origMenu.cloneNode(false); + menu.removeAttribute("id"); + apf.document.body.appendChild(menu);*/ + + var tab = this.parentNode.cloneNode(false); + tab.removeAttribute("id"); + tab.setAttribute("buttons", "close"); //@todo bug in scale that doesnt resize + tab.removeAttribute("anchors"); + apf.document.body.appendChild(tab); + tab.setWidth(this.parentNode.$ext.offsetWidth); + tab.setHeight(this.parentNode.$ext.offsetHeight); + + var page = this.cloneNode(true); + page.removeAttribute("id"); + tab.appendChild(page); + + if (origMenu.$ext.offsetHeight) { + var pos = apf.getAbsolutePosition(origMenu.$ext); + tab.setLeft(pos[0]); + tab.setTop(pos[1]); + } + else { + var pos = apf.getAbsolutePosition(this.parentNode.$ext); + tab.setLeft(pos[0] - 1); + tab.setTop(pos[1] - 2); + } + tab.$ext.style.border = "1px solid #333"; + //menu.$ext.style.margin = "0 0 0 0" + tab.addEventListener("afterdrag", function(e){ + tab.id = tab.name = ""; //@todo fix this bug in apf + tab.destroy(true, true); + _self.$stopDrag(e.htmlEvent); + }); + + //document instead? + var clientX = e.htmlEvent.clientX; + var clientY = e.htmlEvent.clientY; + tab.setAttribute("draggable", true); + setTimeout(function(){ + //@todo Collapse menu + + tab.$dragStart({clientX:clientX,clientY:clientY}); + tab.$ext.style.zIndex = 1000000; + }); + + _self.$startDrag(tab, this); + + return false; + }); + + page.addEventListener("afterclose", function(e){ + var button = this.$dockbutton; + var pNode = this.lastParent; + var btnPNode = button.parentNode; + + button.destroy(true, true); + + if (!pNode.getPages().length) { + var barParent = btnPNode.parentNode; + if (pNode.parentNode.localName == "menu") + pNode.parentNode.destroy(true, true); + else { + var menu = self[button.submenu]; + menu.destroy(true, true); + pNode.destroy(true, true); + } + btnPNode.destroy(true, true); + if (!barParent.selectNodes("vbox").length) { + barParent.destroy(true, true); + if (barParent.vbox) { + barParent.vbox.destroy(true, true); + barParent.splitter.destroy(true, true); + } + } + } + }); + + return page; + } + + /** + * Creates a new section + */ + this.$addSection = function(bar, before){ + var _self = this; + var section = bar.insertBefore(new apf.vbox({ + padding : 0, + edge : "0 0 3 0", + "class" : "docksection", + dock : 1, + childNodes : [ + new apf.divider({ + skin : "divider-debugpanel", + margin : "3 5 2 5", + dock : 1, + draggable : "true" + }) + ] + }), before); + + var div = section.firstChild; + div.addEventListener("beforedrag", function(e){ //change this to beforedrag and recompile apf + var section = this.parentNode; + + //this.hideMenu(); + + var pNode = section.$dockbar = section.parentNode; + var placeHolder = section.cloneNode(false); + placeHolder.removeAttribute("id"); + placeHolder.$dockfor = section; + + var diff = apf.getDiff(section.$ext); + var height = section.$ext.offsetHeight; + var pos = apf.getAbsolutePosition(section.$ext); + + pNode.insertBefore(placeHolder, section); + placeHolder.$ext.style.background = "#acacac"; + placeHolder.$ext.style.height = (height - diff[1]) + "px"; + + section.setWidth(section.$ext.offsetWidth); + apf.document.body.appendChild(section); + section.setLeft(pos[0]); + section.setTop(pos[1]); + + section.addEventListener("afterdrag", function(e){ + pNode.insertBefore(section, placeHolder); + section.setAttribute("draggable", false); + + setTimeout(function(){ + section.removeAttribute("left"); + section.removeAttribute("top"); + section.removeAttribute("width"); + section.$ext.style.position = "relative"; + }); + + var buttons = this.selectNodes("button"); + if (buttons.length) + buttons[0].setValue(false); + + _self.$stopDrag(e.htmlEvent); + placeHolder.destroy(true, true); + }); + + section.setAttribute("draggable", true); + + var clientX = e.htmlEvent.clientX; + var clientY = e.htmlEvent.clientY; + setTimeout(function(){ + //@todo Collapse menu + + section.$dragStart({clientX:clientX,clientY:clientY}); + section.$ext.style.zIndex = 1000000; + }); + + _self.$startDrag(section, this); + + return false; + }); + + return section; + } + + /** + * Creates a new button + */ + this.$addButton = function(section, submenu, page){ + var _self = this; + var button = section.appendChild(new apf.button({ + "class" : "dockButtonID", + skin : "dockButton", + submenu : submenu.id, + dock : 1, + draggable : "true" + })); + + button.addEventListener("beforedrag", function(e){ //change this to beforedrag and recompile apf + var originalButton = this; + + this.hideMenu(); + this.setValue(true); + + //Upgrade to container if only 1 element + if (this.parentNode.selectNodes("button").length == 1) { + this.parentNode.firstChild.dispatchEvent("beforedrag", e); + return false; + } + + var btn = this.cloneNode(true); + btn.removeAttribute("id"); + apf.document.body.appendChild(btn); + btn.setValue(true); + + var pos = apf.getAbsolutePosition(this.$ext); + btn.setLeft(pos[0]); + btn.setTop(pos[1]); + btn.addEventListener("afterdrag", function(e){ + btn.destroy(true, true); + originalButton.setValue(false); + _self.$stopDrag(e.htmlEvent); + }); + + //document instead? + var clientX = e.htmlEvent.clientX; + var clientY = e.htmlEvent.clientY; + setTimeout(function(){ + btn.$dragStart({clientX:clientX,clientY:clientY}); + btn.$ext.style.zIndex = 1000000; + this.removeEventListener("mouseover", arguments.callee); + }); + + _self.$startDrag(btn, this); + + return false; + }); + + page.$dockbutton = button; + button.$dockpage = page; + + return button; + } +}).call(DockableLayout.prototype); \ No newline at end of file diff --git a/support/paths.js b/support/paths.js index b91c6eedd98..6e34150af5f 100644 --- a/support/paths.js +++ b/support/paths.js @@ -9,6 +9,7 @@ require.paths.unshift(__dirname + "/../server"); require.paths.unshift(__dirname + "/connect/lib"); require.paths.unshift(__dirname + "/asyncjs/lib"); require.paths.unshift(__dirname + "/jsdav/lib"); +require.paths.unshift(__dirname + "/jsdav/support/node-sftp/lib"); require.paths.unshift(__dirname + "/socket.io/lib"); require.paths.unshift(__dirname + "/ace/lib"); require.paths.unshift(__dirname + "/ace/support/pilot/lib");